Logo coherent WaveBurst  
Config Reference Guide
Logo
filter.cxx
Go to the documentation of this file.
1 // @(#)root/test:$name: $:$id: filter.cxx,v 1.0 exp $
2 // Author: O.Couet
3 
4 /// The ROOT doxygen filter implements ROOT's specific directives used to generate
5 /// the ROOT reference guide.
6 ///
7 /// ## In the ROOT classes
8 ///
9 /// ### `Begin_Macro` and `End_Macro`
10 /// The two tags where used the THtml version to generate images from ROOT code.
11 /// The generated picture is inlined exactly at the place where the macro is
12 /// defined. The Macro can be defined in two way:
13 /// - by direct in-lining of the the C++ code
14 /// - by a reference to a C++ file
15 /// The tag `Begin_Macro` can have the parameter `(source)`. The directive becomes:
16 /// `Begin_Macro(source)`. This parameter allows to show the macro's code in addition.
17 /// `Begin_Macro` also accept the image file type as option. "png" or "svg".
18 /// "png" is the default value. For example: `Begin_Macro(source, svg)` will show
19 /// the code of the macro and the image will be is svg format. The "width" keyword
20 /// can be added to define the width of the picture in pixel: "width=400" will
21 /// scale a picture to 400 pixel width. This allow to define large picture which
22 /// can then be scale done to have a better definition.
23 ///
24 /// ## In the ROOT tutorials
25 ///
26 /// ROOT tutorials are also included in the ROOT documentation. The tutorials'
27 /// macros headers should look like:
28 ///
29 /// ~~~ {.cpp}
30 /// \file
31 /// \ingroup tutorial_hist
32 /// \notebook
33 /// Getting Contours From TH2D.
34 ///
35 /// #### Image produced by `.x ContourList.C`
36 /// The contours values are drawn next to each contour.
37 /// \macro_image
38 ///
39 /// #### Output produced by `.x ContourList.C`
40 /// It shows that 6 contours and 12 graphs were found.
41 /// \macro_output
42 ///
43 /// #### `ContourList.C`
44 /// \macro_code
45 ///
46 /// \authors Josh de Bever, Olivier Couet
47 /// ~~~
48 ///
49 /// This example shows that four new directives have been implemented:
50 ///
51 /// 1. `\macro_image`
52 /// The images produced by this macro are shown. A caption can be added to document
53 /// the pictures: `\macro_image This is a picture`
54 ///
55 /// 2. `\macro_code`
56 /// The macro code is shown. A caption can be added: `\macro_code This is code`
57 ///
58 /// 3. `\macro_output`
59 /// The output produced by this macro is shown. A caption can be added:
60 /// `\macro_output This the macro output`
61 ///
62 /// 4. `\notebook`
63 /// To generate the corresponding jupyter notebook. In case the tutorial does
64 /// not generate any graphics output, the option `-nodraw` should be added.
65 ///
66 /// Note that the doxygen directive `\authors` or `\author` must be the last one
67 /// of the macro header.
68 
69 #include <unistd.h>
70 #include <stdio.h>
71 #include <string>
72 #include <cstring>
73 #include <iostream>
74 #include <stdlib.h>
75 #include <stdarg.h>
76 #include <memory>
77 
78 using namespace std;
79 
80 // Auxiliary functions
81 void FilterClass();
82 void FilterTutorial();
83 void GetClassName();
84 int NumberOfImages();
85 string ImagesList(string&);
86 void ExecuteMacro();
87 void ExecuteCommand(string);
88 void ReplaceAll(string&, const string&, const string&);
89 string StringFormat(const string fmt_str, ...);
90 bool EndsWith(string const &, string const &);
91 bool BeginsWith(const string&, const string&);
92 
93 // Global variables.
94 FILE *f; // Pointer to the file being parsed.
95 char gLine[255]; // Current line in the current input file
96 string gFileName; // Input file name
97 string gLineString; // Current line (as a string) in the current input file
98 string gClassName; // Current class name
99 string gImageName; // Current image name
100 string gMacroName; // Current macro name
101 string gImageType; // Type of image used to produce pictures (png, svg ...)
102 string gImageWidth; // Width of image
103 string gCwd; // Current working directory
104 string gOutDir; // Output directory
105 string gSourceDir; // Source directory
106 string gOutputName; // File containing a macro std::out
107 bool gHeader; // True if the input file is a header
108 bool gSource; // True if the input file is a source file
109 bool gPython; // True if the input file is a Python script.
110 bool gImageSource; // True the source of the current macro should be shown
111 int gInMacro; // >0 if parsing a macro in a class documentation.
112 int gImageID; // Image Identifier.
113 int gMacroID; // Macro identifier in class documentation.
114 
115 
116 ////////////////////////////////////////////////////////////////////////////////
117 /// Filter ROOT files for Doxygen.
118 
119 int main(int argc, char *argv[])
120 {
121  // Initialisation
122  gFileName = argv[1];
123  gHeader = false;
124  gSource = false;
125  gPython = false;
126  gImageSource = false;
127  gInMacro = 0;
128  gImageID = 0;
129  gMacroID = 0;
130  gOutputName = "stdout.dat";
131  gImageType = "png";
132  gImageWidth = "";
133  if (EndsWith(gFileName,".cxx")) gSource = true;
134  if (EndsWith(gFileName,".h")) gHeader = true;
135  if (EndsWith(gFileName,".py")) gPython = true;
136  GetClassName();
137 
138  // Retrieve the current working directory
139  int last = gFileName.rfind("/");
140  gCwd = gFileName.substr(0,last);
141 
142  // Retrieve the output directory
143  gOutDir = getenv("DOXYGEN_OUTPUT_DIRECTORY");
144  ReplaceAll(gOutDir,"\"","");
145 
146  // Retrieve the source directory
147  gSourceDir = getenv("DOXYGEN_SOURCE_DIRECTORY");
148  ReplaceAll(gSourceDir,"\"","");
149 
150  // Open the input file name.
151  f = fopen(gFileName.c_str(),"r");
152 
153  if (gFileName.find("tutorials") != string::npos) FilterTutorial();
154  else FilterClass();
155 }
156 
157 ////////////////////////////////////////////////////////////////////////////////
158 /// Filter ROOT class for Doxygen.
159 
160 void FilterClass()
161 {
162  // File for inline macros.
163  FILE *m = 0;
164 
165  // File header.
166  if (gHeader) {
167  while (fgets(gLine,255,f)) {
168  gLineString = gLine;
169  printf("%s",gLineString.c_str());
170  }
171  fclose(f);
172  return;
173  }
174 
175  // Source file.
176  if (gSource) {
177  size_t spos = 0;
178  while (fgets(gLine,255,f)) {
179  gLineString = gLine;
180 
181  if (gInMacro && gLineString.find("End_Macro") != string::npos) {
182  gImageSource = false;
183  gInMacro = 0;
184  spos = 0;
185  if (m) {
186  fclose(m);
187  m = 0;
188  ExecuteCommand(StringFormat("root -l -b -q \"makeimage.C(\\\"%s\\\",\\\"%s\\\",\\\"%s\\\",true,false)\""
189  , StringFormat("%s_%3.3d.C", gClassName.c_str(), gMacroID).c_str()
190  , StringFormat("%s_%3.3d.%s", gClassName.c_str(), gImageID, gImageType.c_str()).c_str()
191  , gOutDir.c_str()));
192  ExecuteCommand(StringFormat("rm %s_%3.3d.C", gClassName.c_str(), gMacroID));
193  }
194  int ImageSize = 300;
195  FILE *f = fopen("ImagesSizes.dat", "r");
196  fscanf(f, "%d", &ImageSize);
197  fclose(f);
198  remove("ImagesSizes.dat");
199  ReplaceAll(gImageWidth,"IMAGESIZE",StringFormat("%d",ImageSize));
200  ReplaceAll(gLineString,"End_Macro", StringFormat("\\image html pict1_%s_%3.3d.%s %s", gClassName.c_str(), gImageID, gImageType.c_str(), gImageWidth.c_str()));
201  ReplaceAll(gLineString,"///","");
202  }
203 
204  if (gInMacro) {
205  if (spos) gLineString = gLineString.substr(spos);
206  if (gInMacro == 1) {
207  if (EndsWith(gLineString,".C\n") || (gLineString.find(".C(") != string::npos)) {
208  ExecuteMacro();
209  gInMacro++;
210  } else {
211  gMacroID++;
212  m = fopen(StringFormat("%s_%3.3d.C", gClassName.c_str(), gMacroID).c_str(), "w");
213  if (m) fprintf(m,"%s",gLineString.c_str());
214  if (BeginsWith(gLineString,"{")) {
215  if (gImageSource) {
216  ReplaceAll(gLineString,"{"
217  , StringFormat("\\include %s_%3.3d.C"
218  , gClassName.c_str()
219  , gMacroID));
220  } else {
221  gLineString = "\n";
222  }
223  }
224  gInMacro++;
225  }
226  } else {
227  if (m) fprintf(m,"%s",gLineString.c_str());
228  if (BeginsWith(gLineString,"}")) {
229  ReplaceAll(gLineString,"}","");
230  } else {
231  gLineString = "\n";
232  }
233  gInMacro++;
234  }
235  }
236 
237  if (gLineString.find("Begin_Macro") != string::npos &&
238  gLineString.find("End_Macro") == string::npos) {
239  if (BeginsWith(gLineString, "///")) {
240  spos = gLineString.find_first_not_of(' ', 3);
241  }
242  if (gLineString.find("source") != string::npos) gImageSource = true;
243  if (gLineString.find("png") != string::npos) {
244  gImageType = "png";
245  } else if (gLineString.find("svg") != string::npos) {
246  gImageType = "svg";
247  } else {
248  gImageType = "png";
249  }
250  gImageWidth = "";
251  int wpos1 = gLineString.find("\"width=");
252  if (wpos1 != string::npos) {
253  int wpos2 = gLineString.find_first_of("\"", wpos1+1);
254  gImageWidth = gLineString.substr(wpos1+1, wpos2-wpos1-1);
255  } else {
256  gImageWidth = "width=IMAGESIZE";
257  }
258  gImageID++;
259  gInMacro++;
260  gLineString = "\n";
261  }
262 
263  size_t l = gLineString.length();
264  size_t b = 0;
265  do {
266  size_t e = gLineString.find('\n', b);
267  if (e != string::npos) e++;
268  if (spos) printf("%-*s%s", (int)spos, "///",
269  gLineString.substr(b, e - b).c_str());
270  else printf("%s", gLineString.substr(b, e - b).c_str());
271  b = e;
272  } while (b < l);
273  }
274  fclose(f);
275  return;
276  }
277 
278  // Output anything not header nor source
279  while (fgets(gLine,255,f)) {
280  gLineString = gLine;
281  printf("%s",gLineString.c_str());
282  }
283  fclose(f);
284 }
285 
286 ////////////////////////////////////////////////////////////////////////////////
287 /// Filter ROOT tutorials for Doxygen.
288 
289 void FilterTutorial()
290 {
291  // File for inline macros.
292  FILE *m = 0;
293 
294  int showTutSource = 0;
295  int incond = 0;
296 
297  // Extract the macro name
298  int i1 = gFileName.rfind('/')+1;
299  int i2;
300  if (gPython) {
301  i2 = gFileName.rfind('y');
302  } else {
303  i2 = gFileName.rfind('C');
304  }
305  gMacroName = gFileName.substr(i1,i2-i1+1);
306  gImageName = StringFormat("%s.%s", gMacroName.c_str(), gImageType.c_str()); // Image name
307  gOutputName = StringFormat("%s.out", gMacroName.c_str()); // output name
308 
309  // Parse the source and generate the image if needed
310  while (fgets(gLine,255,f)) {
311  gLineString = gLine;
312 
313  // \macro_image found
314  if (gLineString.find("\\macro_image") != string::npos) {
315  bool nobatch = (gLineString.find("(nobatch)") != string::npos);
316  ReplaceAll(gLineString,"(nobatch)","");
317  if (gPython) {
318  if (nobatch) {
319  ExecuteCommand(StringFormat("./makeimage.py %s %s %s 0 1 0",
320  gFileName.c_str(), gImageName.c_str(), gOutDir.c_str()));
321  } else {
322  ExecuteCommand(StringFormat("./makeimage.py %s %s %s 0 1 1",
323  gFileName.c_str(), gImageName.c_str(), gOutDir.c_str()));
324  }
325  } else {
326  if (nobatch) {
327  ExecuteCommand(StringFormat("root -l -q \"makeimage.C(\\\"%s\\\",\\\"%s\\\",\\\"%s\\\",false,false)\"",
328  gFileName.c_str(), gImageName.c_str(), gOutDir.c_str()));
329  } else {
330  ExecuteCommand(StringFormat("root -l -b -q \"makeimage.C(\\\"%s\\\",\\\"%s\\\",\\\"%s\\\",false,false)\"",
331  gFileName.c_str(), gImageName.c_str(), gOutDir.c_str()));
332  }
333  }
334  ReplaceAll(gLineString, "\\macro_image", ImagesList(gImageName));
335  remove(gOutputName.c_str());
336  }
337 
338  // \macro_code found
339  if (gLineString.find("\\macro_code") != string::npos) {
340  showTutSource = 1;
341  m = fopen(StringFormat("%s/macros/%s",gOutDir.c_str(),gMacroName.c_str()).c_str(), "w");
342  ReplaceAll(gLineString, "\\macro_code", StringFormat("\\include %s",gMacroName.c_str()));
343  }
344 
345  // notebook found
346  if (gLineString.find("\\notebook") != string::npos) {
347  ExecuteCommand(StringFormat("python converttonotebook.py %s %s/notebooks/",
348  gFileName.c_str(), gOutDir.c_str()));
349 
350  if (gPython){
351  gLineString = "## ";
352  }
353  else{
354  gLineString = "/// ";
355  }
356  gLineString += StringFormat( "\\htmlonly <a href=\"http://nbviewer.jupyter.org/url/root.cern.ch/doc/master/notebooks/%s.nbconvert.ipynb\" target=\"_blank\"><img src= notebook.gif alt=\"View in nbviewer\" style=\"height:1em\" ></a> <a href=\"https://cern.ch/swanserver/cgi-bin/go?projurl=https://root.cern.ch/doc/master/notebooks/%s.nbconvert.ipynb\" target=\"_blank\"><img src=\"http://swanserver.web.cern.ch/swanserver/images/badge_swan_white_150.png\" alt=\"Open in SWAN\" style=\"height:1em\" ></a> \\endhtmlonly \n", gMacroName.c_str() , gMacroName.c_str());
357 
358  }
359  // \macro_output found
360  if (gLineString.find("\\macro_output") != string::npos) {
361  if (!gPython) ExecuteCommand(StringFormat("root -l -b -q %s", gFileName.c_str()).c_str());
362  else ExecuteCommand(StringFormat("python %s", gFileName.c_str()).c_str());
363  rename(gOutputName.c_str(), StringFormat("%s/macros/%s",gOutDir.c_str(), gOutputName.c_str()).c_str());
364  ReplaceAll(gLineString, "\\macro_output", StringFormat("\\include %s",gOutputName.c_str()));
365  }
366 
367  // \author is the last comment line.
368  if (gLineString.find("\\author") != string::npos) {
369  if (gPython) printf("%s",StringFormat("%s \n## \\cond \n",gLineString.c_str()).c_str());
370  else printf("%s",StringFormat("%s \n/// \\cond \n",gLineString.c_str()).c_str());
371  if (showTutSource == 1) showTutSource = 2;
372  incond = 1;
373  } else {
374  printf("%s",gLineString.c_str());
375  if (m && showTutSource == 2) fprintf(m,"%s",gLineString.c_str());
376  }
377  }
378 
379  if (incond) {
380  if (gPython) printf("## \\endcond \n");
381  else printf("/// \\endcond \n");
382  }
383 
384  if (m) {
385  fclose(m);
386  }
387 }
388 
389 ////////////////////////////////////////////////////////////////////////////////
390 /// Retrieve the class name.
391 
392 void GetClassName()
393 {
394  int i1 = 0;
395  int i2 = 0;
396 
397  // File header.
398  if (gHeader) {
399  i1 = gFileName.find_last_of("/")+1;
400  i2 = gFileName.find(".h")-1;
401  gClassName = gFileName.substr(i1,i2-i1+1);
402  }
403 
404  // Source file.
405  if (gSource) {
406  i1 = gFileName.find_last_of("/")+1;
407  i2 = gFileName.find(".cxx")-1;
408  gClassName = gFileName.substr(i1,i2-i1+1);
409  }
410 
411  return;
412 }
413 
414 ////////////////////////////////////////////////////////////////////////////////
415 /// Execute the macro in gLineString and produce the corresponding picture.
416 
417 void ExecuteMacro()
418 {
419  // Name of the next Image to be generated
420  gImageName = StringFormat("%s_%3.3d.%s", gClassName.c_str(), gImageID, gImageType.c_str());
421 
422  // Retrieve the macro to be executed.
423  if (gLineString.find("../../..") != string::npos) {
424  ReplaceAll(gLineString,"../../..", gSourceDir.c_str());
425  } else {
426  gLineString.insert(0, StringFormat("%s/../doc/macros/",gCwd.c_str()));
427  }
428  int i1 = gLineString.rfind('/')+1;
429  int i2 = gLineString.rfind('C');
430  gMacroName = gLineString.substr(i1,i2-i1+1);
431 
432  // Build the ROOT command to be executed.
433  gLineString.insert(0, StringFormat("root -l -b -q \"makeimage.C(\\\""));
434  size_t l = gLineString.length();
435  gLineString.replace(l-1,1,StringFormat("\\\",\\\"%s\\\",\\\"%s\\\",true,false)\"", gImageName.c_str(), gOutDir.c_str()));
436 
437  // Execute the macro
438  ExecuteCommand(gLineString);
439 
440  // Inline the directives to show the code
441  if (gImageSource) gLineString = StringFormat("\\include %s\n", gMacroName.c_str());
442  else gLineString = "";
443 }
444 
445 ////////////////////////////////////////////////////////////////////////////////
446 /// Execute a command making sure stdout will not go in the doxygen file.
447 
448 void ExecuteCommand(string command)
449 {
450  int o = dup(fileno(stdout));
451  freopen(gOutputName.c_str(),"a",stdout);
452  system(command.c_str());
453  dup2(o,fileno(stdout));
454  close(o);
455 }
456 
457 ////////////////////////////////////////////////////////////////////////////////
458 /// Get the number of images in NumberOfImages.dat after makeimage.C is executed.
459 
460 int NumberOfImages()
461 {
462  int ImageNum;
463  FILE *f = fopen("NumberOfImages.dat", "r");
464  fscanf(f, "%d", &ImageNum);
465  fclose(f);
466  remove("NumberOfImages.dat");
467  return ImageNum;
468 }
469 
470 ////////////////////////////////////////////////////////////////////////////////
471 /// Replace all instances of a string with another string.
472 
473 void ReplaceAll(string& str, const string& from, const string& to) {
474  if (from.empty()) return;
475  string wsRet;
476  wsRet.reserve(str.length());
477  size_t start_pos = 0, pos;
478  while ((pos = str.find(from, start_pos)) != string::npos) {
479  wsRet += str.substr(start_pos, pos - start_pos);
480  wsRet += to;
481  pos += from.length();
482  start_pos = pos;
483  }
484  wsRet += str.substr(start_pos);
485  str.swap(wsRet);
486 }
487 
488 ////////////////////////////////////////////////////////////////////////////////
489 /// std::string formatting like sprintf.
490 
491 string StringFormat(const string fmt_str, ...) {
492  int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
493  string str;
494  unique_ptr<char[]> formatted;
495  va_list ap;
496  while (1) {
497  formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
498  strcpy(&formatted[0], fmt_str.c_str());
499  va_start(ap, fmt_str);
500  final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
501  va_end(ap);
502  if (final_n < 0 || final_n >= n) n += abs(final_n - n + 1);
503  else break;
504  }
505  return string(formatted.get());
506 }
507 
508 ////////////////////////////////////////////////////////////////////////////////
509 /// Return the image list after a tutorial macro execution.
510 
511 string ImagesList(string& name) {
512 
513  int N = NumberOfImages();
514 
515  char val[300];
516  int len = 0;
517 
518  int ImageSize = 300;
519  FILE *f = fopen("ImagesSizes.dat", "r");
520 
521  for (int i = 1; i <= N; i++){
522  fscanf(f, "%d", &ImageSize);
523  if (i>1) sprintf(&val[len]," \n/// \\image html pict%d_%s width=%d",i,name.c_str(),ImageSize);
524  else sprintf(&val[len],"\\image html pict%d_%s width=%d",i,name.c_str(),ImageSize);
525  len = (int)strlen(val);
526  }
527 
528  fclose(f);
529  remove("ImagesSizes.dat");
530 
531  return (string)val;
532 }
533 
534 ////////////////////////////////////////////////////////////////////////////////
535 /// Find if a string ends with another string.
536 
537 bool EndsWith(string const &fullString, string const &ending) {
538  if (fullString.length() >= ending.length()) {
539  return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
540  } else {
541  return false;
542  }
543 }
544 
545 ////////////////////////////////////////////////////////////////////////////////
546 /// Find if a string begins with another string.
547 
548 bool BeginsWith(const string& haystack, const string& needle) {
549  return needle.length() <= haystack.length() && equal(needle.begin(), needle.end(), haystack.begin());
550 }
strcpy(analysis,"2G")
shift breaksw case n
Definition: cwb_clchunk.csh:75
shift breaksw case o
Definition: cwb_mkchunk.csh:96
shift breaksw case m
shift breaksw case l
shift breaksw case b
Definition: cwb_obchunk.csh:92
par[0] name