tdaq-develop-2025-02-12
convert_comments_to_doxygen.cpp
1 
18 #include <string.h> //for strstr (not the same as <string>)
19 #include <iostream>
20 #include <map>
21 #include <set>
22 #include <sstream>
23 #include <string> //for string
24 #include <vector>
25 
26 #define __COUT_HDR__ ""
27 
28 #define Q(X) #X
29 #define QUOTE(X) Q(X)
30 
31 #define __FILENAME__ \
32  (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
33 #define __MF_SUBJECT__ __FILENAME__
34 #define __MF_DECOR__ (__MF_SUBJECT__)
35 
36 #define __COUT_HDR_L__ ":" << std::dec << __LINE__ << " |\t"
37 #define __COUT_HDR_FL__ __FILENAME__ << "" << __COUT_HDR_L__
38 #define TLOG(X) std::cout << QUOTE(X) << ": " << __COUT_HDR_FL__ //__LINE__ << ": "
39 #define __COUT_ERR__ TLOG(TLVL_ERROR) << __COUT_HDR__
40 #define __COUT_WARN__ TLOG(TLVL_WARN) << __COUT_HDR__
41 #define __COUT_INFO__ TLOG(TLVL_INFO) << __COUT_HDR__
42 #define __COUT__ \
43  if(1) \
44  TLOG(TLVL_DEBUG) << __COUT_HDR__
45 #define __COUTT__ \
46  if(1) \
47  TLOG(TLVL_TRACE) << __COUT_HDR__
48 
49 #define __SS__ \
50  std::stringstream ss; \
51  ss << "|" << __MF_DECOR__ << ": " << __COUT_HDR_FL__ << __COUT_HDR__
52 #define __SS_THROW__ \
53  { \
54  __COUT_ERR__ << "\n" << ss.str(); \
55  throw std::runtime_error(ss.str()); \
56  } //put in {}'s to prevent surprises, e.g. if ... else __SS_THROW__;
57 #define __SS_THROW_ONLY__ \
58  { \
59  throw std::runtime_error(ss.str()); \
60  } //put in {}'s to prevent surprises, e.g. if ... else __SS_THROW__;
61 #define __E__ std::endl
62 
63 #define __COUTV__(X) __COUT__ << QUOTE(X) << " = " << X << __E__
64 #define __COUTTV__(X) __COUTT__ << QUOTE(X) << " = " << X << __E__
65 #define __COUTVS__(LVL, X) \
66  TLOG(TLVL_DEBUG + LVL) << __COUT_HDR__ << QUOTE(X) << " = " << X << __E__
67 
68 
69 //==============================================================================
71 bool isTypeToFix(const std::string& extension)
72 {
73  __COUTTV__(extension);
74  if(extension == ".cc" ||
75  extension == ".icc" ||
76  extension == ".cpp" ||
77  extension == ".CC" ||
78  extension == ".h" ||
79  extension == ".hh" ||
80  extension == ".H")
81  return true;
82  __COUT_INFO__ << "===========================> skipping " << extension << __E__;
83  return false;
84 } //end isTypeToFix()
85 
86 //==============================================================================
89 void fixFile(const std::string& path)
90 {
91  __COUTV__(path);
92 
93  std::FILE* fp = std::fopen(path.c_str(), "rb");
94  if(!fp)
95  {
96  __SS__ << "Could not open file at " << path << ". Error: " << errno << " - "
97  << strerror(errno) << __E__;
98  __SS_THROW__;
99  }
100 
101  std::string newContents = "";
102 
103  bool isHeaderType = path[path.size()-1] == 'h';
104  __COUTV__(isHeaderType);
105 
106  //For Doxygen parsing:
107  // - for header files, convert end of line // to ///<
108  // - for source files, convert first character // to ///
109 
110  char line[200];
111  bool isNewLine = true;
112  bool inCommentBlock = false;
113  bool inHeaderCommentBlock = false;
114  bool inFunctionBlock = false;
115  bool skippedLastLine = false; //prevent comment block start with empty comment if previous line was skipped
116  size_t lineNum = 0;
117  while(fgets(line,200,fp))
118  {
119  size_t len = strlen(line);
120  ++lineNum;
121 
122  if(isHeaderType)
123  {
124  //only look for end of line comments
125  if(!isNewLine || (len > 10 && line[0] != '/' && line[0] != '#' &&
126  !(line[0] == '\t' && line[1] == '/') ))
127  {
128  bool found = false;
129  bool inQuote = false;
130  for(size_t c = 0; c < len; ++c)
131  {
132  newContents += line[c];
133 
134  if(!found && !inQuote && c > 10 && c+2 < len &&
135  line[c] == '/' &&
136  line[c+1] == '/' && line[c-1] != ':')
137  {
138  //not-: and !inQuote protects against lines like this
139  // const std::string link = "http://xdaq.web.cern.ch";
140 
141  found = true;
142  if(line[c+2] != '/') //only apply if needed
143  {
144  newContents += "//<";
145  c += 1; //skip ahead
146  }
147  }
148  else if(!found && !inQuote && line[c] == '"')
149  inQuote = true;
150  else if(!found && inQuote && line[c] == '"')
151  inQuote = false;
152  }
153 
154  inHeaderCommentBlock = false;
155  inCommentBlock = false;
156  inFunctionBlock = false;
157  skippedLastLine = false;
158 
159  continue;
160  }
161  }
162 
163  //do source type behavior for headers as well
164 
165  if(1 && path.find("RootFileExplorer.h") != std::string::npos)
166  __COUT__ << lineNum << ". " << len << ": " << inFunctionBlock << " " <<
167  inCommentBlock << " " <<
168  line << __E__;
169 
170  // else //source type
171  // {
172  if(isNewLine && ((len > 2 &&
173  line[0] == '/' &&
174  line[1] == '/' &&
175  line[2] != '=') ||
176  (isHeaderType && len > 3 &&
177  line[0] == '\t' && //header might tab in comments
178  line[1] == '/' &&
179  line[2] == '/' &&
180  line[3] != '='
181  )))
182  {
183  bool headerTab = (isHeaderType && line[0] == '\t');
184 
185  if(path.find("RootFileExplorer.h") != std::string::npos)
186  __COUT__ << lineNum << ". " << len << ": " << inFunctionBlock << " " <<
187  inCommentBlock << " " << inHeaderCommentBlock << " " << headerTab << " " <<
188  line << __E__;
189 
190  if(isHeaderType && !inHeaderCommentBlock && !inCommentBlock)
191  {
192  //ignore commented functions in header files
193  std::string testStr(line);
194  size_t i0a = testStr.find(' ');
195  size_t i0b = testStr.find('\t');
196  size_t i1 = testStr.find('(');
197  size_t i2 = testStr.find(')');
198  size_t i3 = testStr.find(';');
199 
200  if(path.find("RootFileExplorer.h") != std::string::npos)
201  {
202  __COUTV__(i0a);
203  __COUTV__(i0b);
204  }
205 
206  if(i0a != std::string::npos || i0b != std::string::npos) //must have a space or tab in function declaration
207  {
208  size_t i0 = (i0b < i0a)?i0b:i0a;
209 
210  if(path.find("RootFileExplorer.h") != std::string::npos)
211  {
212  __COUTV__(i0);
213  __COUTV__(i3);
214  }
215 
216  if(i1 != std::string::npos && i2 != std::string::npos && i3 != std::string::npos) // ( ); style exists
217  {
218 
219  if(i0 < i1 && i1 < i2 && i2 < i3)
220  {
221  __COUT__ << "skipping header commented function delcaration: " << line;
222  newContents += line;
223  isNewLine = true;
224  skippedLastLine = true;
225  continue;
226  }
227  }
228  //ignore commented member variable declaraionts in header files
229  if(i3 != std::string::npos && i3 == len - 2) //ends with ;
230  {
231  __COUT__ << "skipping header commented member variable delcaration: " << line;
232  newContents += line;
233  isNewLine = true;
234  skippedLastLine = true;
235  continue;
236  }
237  }
238  }
239 
240  if(path.find("RootFileExplorer.h") != std::string::npos)
241  __COUT__ << lineNum << ". " << len << ": " << inFunctionBlock << " " <<
242  inCommentBlock << " " << inHeaderCommentBlock << " " << headerTab << " " <<
243  line << __E__;
244 
245  if(line[2] == '{')
246  inFunctionBlock = true;
247  if(len > 3 && line[2] == ' ' && line[3] == '{')
248  inFunctionBlock = true;
249 
250  if(inFunctionBlock)
251  {
252  __COUTT__ << "skipping comment block: " << line << __E__;
253  inCommentBlock = false;
254  newContents += line;
255  isNewLine = true;
256 
257  if(line[2] == '}')
258  inFunctionBlock = false;
259  if(len > 3 && line[2] == ' ' && line[3] == '}')
260  inFunctionBlock = false;
261  if(len > 3 && line[2] == '{' && line[3] == '}')
262  inFunctionBlock = false;
263  if(len > 4 && line[2] == '{' && line[4] == '}')
264  inFunctionBlock = false;
265  if(len > 4 && line[3] == '{' && line[4] == '}')
266  inFunctionBlock = false;
267 
268  if(!inFunctionBlock)
269  skippedLastLine = true; //mark in case of gratuitous empty // to follow
270 
271  continue;
272  }
273 
274  if(len > 5) //check for clang-format
275  {
276  std::string testStr(line);
277  if(len < 30 && testStr.find("clang-format") != std::string::npos)
278  {
279  __COUTT__ << "skipping clang: " << line << __E__;
280  newContents += line;
281  isNewLine = true;
282  skippedLastLine = true;
283  continue;
284  }
285  else if(!inCommentBlock && !inHeaderCommentBlock &&
286  testStr.find("#") < 5)
287  {
288  __COUTT__ << "skipping # pragma: " << line << __E__;
289  newContents += line;
290  isNewLine = true;
291  skippedLastLine = true;
292  continue;
293  }
294  else if(!inCommentBlock && !inHeaderCommentBlock &&
295  testStr.find("BOOST_") < 5)
296  {
297  __COUTT__ << "skipping BOOST: " << line << __E__;
298  newContents += line;
299  isNewLine = true;
300  skippedLastLine = true;
301  continue;
302  }
303  else if(!inCommentBlock && !inHeaderCommentBlock &&
304  testStr.find("XDAQ_") < 5)
305  {
306  __COUTT__ << "skipping XDAQ: " << line << __E__;
307  newContents += line;
308  isNewLine = true;
309  skippedLastLine = true;
310  continue;
311  }
312  else if(!inCommentBlock && !inHeaderCommentBlock &&
313  testStr.find("static ") < 5)
314  {
315  __COUTT__ << "skipping static: " << line << __E__;
316  newContents += line;
317  isNewLine = true;
318  skippedLastLine = true;
319  continue;
320  }
321  }
322 
323  if(0 && path.find("JSONDispatcher_module.cc") != std::string::npos)
324  __COUT__ << len << ": " << inFunctionBlock << " " <<
325  inCommentBlock << " " << headerTab << " " <<
326  line << __E__;
327 
328  if(len == 3 && skippedLastLine)
329  {
330  //prevent comment block start with empty comment if previous line was skipped
331  __COUTT__ << "skipping gratuitous comment in skipzone: " << line << __E__;
332  newContents += line;
333  isNewLine = true;
334  continue;
335  }
336  else if(headerTab && len == 4 && skippedLastLine)
337  {
338  //prevent comment block start with empty comment if previous line was skipped
339  __COUTT__ << "skipping gratuitous header comment in skipzone: " << line << __E__;
340  newContents += line;
341  isNewLine = true;
342  continue;
343  }
344  else
345  skippedLastLine = false;
346 
347 
348  if(0 && path.find("JSONDispatcher_module.cc") != std::string::npos)
349  __COUT__ << len << ": " << inFunctionBlock << " " <<
350  inCommentBlock << " " << headerTab << " " <<
351  line << __E__;
352 
353  if(!headerTab && line[2] != '/') //dont add if already 3 (but stay in comment block)
354  newContents += '/'; //add extra '/'
355  else if(headerTab && line[3] != '/')
356  {
357  inHeaderCommentBlock = true;
358  newContents += "\t//";
359  newContents += '/'; //add extra '/'
360  newContents += (&line[3]);
361 
362  if(len && line[len-1] == '\n')
363  isNewLine = true;
364  else
365  {
366  __COUT__ << "No new line at size " << len << __E__;
367  isNewLine = false;
368  }
369  continue;
370  }
371  else if(!headerTab && len > 4 && line[2] == '/' && line[3] == '/' && line[4] == '/')
372  {
373  //prevent comment block start with empty comment if previous line was skipped
374  __COUTT__ << "Ending comment block with /////: " << line << __E__;
375  inCommentBlock = false;
376  newContents += line;
377  if(len && line[len-1] == '\n')
378  isNewLine = true;
379  else
380  {
381  __COUT__ << "No new line at size " << len << __E__;
382  isNewLine = false;
383  }
384  continue;
385  }
386 
387  if(headerTab)
388  inHeaderCommentBlock = true;
389  else
390  inCommentBlock = true;
391  }
392  else if(isNewLine && inCommentBlock && len == 1) //keep comment block going for empty newlines
393  {
394  __COUTT__ << "Continue comment block" << __E__;
395  newContents += "///"; //add extra '///' to keep comment block going
396  }
397  else if(isNewLine && inHeaderCommentBlock && len == 1) //keep comment block going for empty newlines
398  {
399  __COUTT__ << "Continue header comment block" << __E__;
400  newContents += "\t///"; //add extra '\t///' to keep comment block going
401  }
402  else
403  {
404  inHeaderCommentBlock = false;
405  inCommentBlock = false;
406  inFunctionBlock = false;
407  skippedLastLine = false;
408  }
409 
410  newContents += line;
411  //}
412 
413  if(len && line[len-1] == '\n')
414  isNewLine = true;
415  else
416  {
417  __COUT__ << "No new line at size " << len << __E__;
418  isNewLine = false;
419  }
420 
421  } //end main loop
422  std::fclose(fp);
423 
424 
425  fp = std::fopen(path.c_str(), "wb");
426  if(!fp)
427  {
428  __SS__ << "Could not open file to write at " << path << ". Error: " << errno << " - "
429  << strerror(errno) << __E__;
430  __SS_THROW__;
431  }
432  std::fwrite(&newContents[0], 1, newContents.size(), fp);
433  std::fclose(fp);
434 
435  // std::string contents;
436 
437  // //else standard text read
438  // std::fseek(fp, 0, SEEK_END);
439  // contents.resize(std::ftell(fp));
440  // std::rewind(fp);
441  // std::fread(&contents[0], 1, contents.size(), fp);
442  // std::fclose(fp);
443 
444  //now fix!
445  // __COUTV__(contents.size());
446 } // end fixFile
447 
448 #include <dirent.h> //DIR and dirent
449 //==============================================================================
452 void recursiveFixPathContent(const std::string& path)
453 {
454  __COUTV__(path);
455 
456  DIR* pDIR;
457  struct dirent* entry;
458  bool isDir;
459  std::string name;
460  int type;
461 
462  if(!(pDIR = opendir((path).c_str())))
463  {
464  __COUT__ << "Path '" << path << "' could not be opened!" << __E__;
465 
466  if(isTypeToFix(path.substr(path.rfind('.'))))
467  {
468  __COUT__ << "Interpreting as file: '" << path << "'" << __E__;
469  fixFile(path);
470  }
471 
472  return;
473  }
474 
475 
476  // else directory good, get all folders, .h, .cc, .txt files
477  while((entry = readdir(pDIR)))
478  {
479  name = std::string(entry->d_name);
480  type = int(entry->d_type);
481 
482  __COUT__ << "\t" << type << " " << name << std::endl;
483 
484  if(name[0] != '.' &&
485  (type == 0 || // 0 == UNKNOWN (which can happen - seen in SL7 VM)
486  type == 4 || // directory type
487  type == 8 || // file type
488  type == 10 // 10 == link (could be directory or file, treat as unknown)
489  ))
490  {
491  isDir = false;
492 
493  if(type == 0 || type == 10)
494  {
495  // unknown type .. determine if directory
496  DIR* pTmpDIR = opendir((path + "/" + name).c_str());
497  if(pTmpDIR)
498  {
499  isDir = true;
500  closedir(pTmpDIR);
501  }
502  else //assume file
503  __COUT__ << "Unable to open path as directory: "
504  << (path + "/" + name) << __E__;
505  }
506 
507  if(type == 4)
508  isDir = true; // flag directory types
509 
510  // handle directories
511 
512  if(isDir)
513  {
514  __COUT__ << "Directory: " << type << " " << name << __E__;
515 
516  recursiveFixPathContent(path + "/" + name);
517  }
518  else // type 8 or 0 is file
519  {
520  __COUT__ << "File: " << type << " " << name << std::endl;
521 
522  try
523  {
524  if(isTypeToFix(name.substr(name.rfind('.'))))
525  {
526  __COUTT__ << "Fixing file: " << name << __E__;
527  fixFile(path + "/" + name);
528  }
529  }
530  catch(...)
531  {
532  __COUT_WARN__ << "Invalid file extension, skipping '" << name << "' ..."
533  << __E__;
534  }
535  }
536  }
537  } // end directory traversal
538 
539  closedir(pDIR);
540 
541 } // end recursiveFixPathContent()
542 
543 //==============================================================================
544 int main(int argc, char* argv[])
545 try
546 {
547  if(argc < 2)
548  {
549  fprintf(stderr,
550  "Usage: $0 <path>.\n");
551  exit(1);
552  }
553 
554  std::string path = argv[1];
555 
556  __COUTV__(path);
557 
558  recursiveFixPathContent(path);
559 
560  __COUT_INFO__ << "Done." << __E__;
561  return 0;
562 }
563 catch(const std::runtime_error& e)
564 {
565  __COUT_ERR__ << "Error caught during test execution: \n" << e.what() << __E__;
566  return 1;
567 }