Doxygen
pre.l
浏览该文件的文档.
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2020 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 %option never-interactive
16 %option prefix="preYY"
17 %option reentrant
18 %option extra-type="struct preYY_state *"
19 %top{
20 #include <stdint.h>
21 // forward declare yyscan_t to improve type safety
22 #define YY_TYPEDEF_YY_SCANNER_T
23 struct yyguts_t;
24 typedef yyguts_t *yyscan_t;
25 }
26 
27 %{
28 
29 /*
30  * includes
31  */
32 
33 #include "doxygen.h"
34 
35 #include <stack>
36 #include <deque>
37 #include <algorithm>
38 #include <utility>
39 #include <mutex>
40 #include <thread>
41 #include <algorithm>
42 
43 #include <stdio.h>
44 #include <assert.h>
45 #include <ctype.h>
46 #include <errno.h>
47 
48 #include "qcstring.h"
49 #include "containers.h"
50 #include "pre.h"
51 #include "constexp.h"
52 #include "define.h"
53 #include "message.h"
54 #include "util.h"
55 #include "defargs.h"
56 #include "debug.h"
57 #include "bufstr.h"
58 #include "portable.h"
59 #include "bufstr.h"
60 #include "arguments.h"
61 #include "entry.h"
62 #include "condparser.h"
63 #include "config.h"
64 #include "filedef.h"
65 #include "regex.h"
66 #include "fileinfo.h"
67 
68 #define YY_NO_UNISTD_H 1
69 
70 #define USE_STATE2STRING 0
71 
72 // Toggle for some debugging info
73 //#define DBG_CTX(x) fprintf x
74 #define DBG_CTX(x) do { } while(0)
75 
76 #if USE_STATE2STRING
77 static const char *stateToString(int state);
78 #endif
79 
80 struct preYY_CondCtx
81 {
82  preYY_CondCtx(int line,QCString id,bool b)
83  : lineNr(line),sectionId(id), skip(b) {}
84  int lineNr;
85  QCString sectionId;
86  bool skip;
87 };
88 
89 struct FileState
90 {
91  FileState(uint size) : fileBuf(size) {}
92  int lineNr = 1;
93  int curlyCount = 0;
94  BufStr fileBuf;
95  BufStr *oldFileBuf = 0;
96  yy_size_t oldFileBufPos = 0;
97  YY_BUFFER_STATE bufState = 0;
98  QCString fileName;
99 };
100 
101 struct PreIncludeInfo
102 {
103  PreIncludeInfo(const QCString &fn,FileDef *srcFd, FileDef *dstFd,const QCString &iName,bool loc, bool imp)
104  : fileName(fn), fromFileDef(srcFd), toFileDef(dstFd), includeName(iName), local(loc), imported(imp)
105  {
106  }
107  QCString fileName; // file name in which the include statement was found
108  FileDef *fromFileDef; // filedef in which the include statement was found
109  FileDef *toFileDef; // filedef to which the include is pointing
110  QCString includeName; // name used in the #include statement
111  bool local; // is it a "local" or <global> include
112  bool imported; // include via "import" keyword (Objective-C)
113 };
114 
115 /** A dictionary of managed Define objects. */
116 typedef std::map< std::string, Define > DefineMap;
117 
118 /** @brief Class that manages the defines available while
119  * preprocessing files.
120  */
121 class DefineManager
122 {
123  private:
124  /** Local class used to hold the defines for a single file */
125  class DefinesPerFile
126  {
127  public:
128  /** Creates an empty container for defines */
129  DefinesPerFile(DefineManager *parent)
130  : m_parent(parent)
131  {
132  }
133  void addInclude(std::string fileName)
134  {
135  m_includedFiles.insert(fileName);
136  }
137  void store(const DefineMap &fromMap)
138  {
139  for (auto &kv : fromMap)
140  {
141  m_defines.emplace(kv.first,kv.second);
142  }
143  //printf(" m_defines.size()=%zu\n",m_defines.size());
144  m_stored=true;
145  }
146  void retrieve(DefineMap &toMap)
147  {
148  StringSet includeStack;
149  retrieveRec(toMap,includeStack);
150  }
151  void retrieveRec(DefineMap &toMap,StringSet &includeStack)
152  {
153  //printf(" retrieveRec #includedFiles=%zu\n",m_includedFiles.size());
154  for (auto incFile : m_includedFiles)
155  {
156  DefinesPerFile *dpf = m_parent->find(incFile);
157  if (dpf && includeStack.find(incFile)==includeStack.end())
158  {
159  includeStack.insert(incFile);
160  dpf->retrieveRec(toMap,includeStack);
161  //printf(" retrieveRec: processing include %s: #toMap=%zu\n",qPrint(incFile),toMap.size());
162  }
163  }
164  for (auto &kv : m_defines)
165  {
166  toMap.emplace(kv.first,kv.second);
167  }
168  }
169  bool stored() const { return m_stored; }
170  private:
171  DefineManager *m_parent;
172  DefineMap m_defines;
173  StringSet m_includedFiles;
174  bool m_stored = false;
175  };
176 
177  friend class DefinesPerFile;
178  public:
179 
180  void addInclude(std::string fromFileName,std::string toFileName)
181  {
182  //printf("DefineManager::addInclude('%s'->'%s')\n",fromFileName.c_str(),toFileName.c_str());
183  auto it = m_fileMap.find(fromFileName);
184  if (it==m_fileMap.end())
185  {
186  it = m_fileMap.emplace(fromFileName,std::make_unique<DefinesPerFile>(this)).first;
187  }
188  auto &dpf = it->second;
189  dpf->addInclude(toFileName);
190  }
191 
192  void store(std::string fileName,const DefineMap &fromMap)
193  {
194  //printf("DefineManager::store(%s,#=%zu)\n",fileName.c_str(),fromMap.size());
195  auto it = m_fileMap.find(fileName);
196  if (it==m_fileMap.end())
197  {
198  it = m_fileMap.emplace(fileName,std::make_unique<DefinesPerFile>(this)).first;
199  }
200  it->second->store(fromMap);
201  }
202 
203  void retrieve(std::string fileName,DefineMap &toMap)
204  {
205  auto it = m_fileMap.find(fileName);
206  if (it!=m_fileMap.end())
207  {
208  auto &dpf = it->second;
209  dpf->retrieve(toMap);
210  }
211  //printf("DefineManager::retrieve(%s,#=%zu)\n",fileName.c_str(),toMap.size());
212  }
213 
214  bool alreadyProcessed(std::string fileName) const
215  {
216  auto it = m_fileMap.find(fileName);
217  if (it!=m_fileMap.end())
218  {
219  return it->second->stored();
220  }
221  return false;
222  }
223 
224  private:
225  /** Helper function to return the DefinesPerFile object for a given file name. */
226  DefinesPerFile *find(std::string fileName) const
227  {
228  auto it = m_fileMap.find(fileName);
229  return it!=m_fileMap.end() ? it->second.get() : nullptr;
230  }
231 
232  std::unordered_map< std::string, std::unique_ptr<DefinesPerFile> > m_fileMap;
233 };
234 
235 
236 /* -----------------------------------------------------------------
237  *
238  * global state
239  */
240 static std::mutex g_debugMutex;
241 static std::mutex g_globalDefineMutex;
242 static std::mutex g_updateGlobals;
243 static DefineManager g_defineManager;
244 
245 
246 /* -----------------------------------------------------------------
247  *
248  * scanner's state
249  */
250 
251 struct preYY_state
252 {
253  int yyLineNr = 1;
254  int yyMLines = 1;
255  int yyColNr = 1;
256  QCString yyFileName;
257  FileDef *yyFileDef = 0;
258  FileDef *inputFileDef = 0;
259  int ifcount = 0;
260  int defArgs = -1;
261  QCString defName;
262  QCString defText;
263  QCString defLitText;
264  QCString defArgsStr;
265  QCString defExtraSpacing;
266  bool defContinue = false;
267  bool defVarArgs = false;
268  int lastCContext = 0;
269  int lastCPPContext = 0;
270  BufStr *inputBuf = 0;
271  yy_size_t inputBufPos = 0;
272  BufStr *outputBuf = 0;
273  int roundCount = 0;
274  bool quoteArg = false;
275  bool idStart = false;
276  int findDefArgContext = 0;
277  bool expectGuard = false;
278  QCString guardName;
279  QCString lastGuardName;
280  QCString incName;
281  QCString guardExpr;
282  int curlyCount = 0;
283  bool nospaces = false; // add extra spaces during macro expansion
284  int javaBlock = 0;
285 
286  bool macroExpansion = false; // from the configuration
287  bool expandOnlyPredef = false; // from the configuration
288  QCString potentialDefine;
289  int commentCount = 0;
290  bool insideComment = false;
291  bool isImported = false;
292  QCString blockName;
293  int condCtx = 0;
294  bool skip = false;
295  bool insideCS = false; // C# has simpler preprocessor
296  bool insideFtn = false;
297  bool isSource = false;
298 
299  yy_size_t fenceSize = 0;
300  bool ccomment = false;
301  QCString delimiter;
302  bool isSpecialComment = false;
303  StringVector pathList;
304  IntMap argMap;
305  BoolStack levelGuard;
306  std::stack< std::unique_ptr<preYY_CondCtx> > condStack;
307  std::deque< std::unique_ptr<FileState> > includeStack;
308  std::unordered_map<std::string,Define*> expandedDict;
309  StringUnorderedSet expanded;
310  ConstExpressionParser constExpParser;
311  DefineMap contextDefines; // macros imported from other files
312  DefineMap localDefines; // macros defined in this file
313  DefineList macroDefinitions;
314  LinkedMap<PreIncludeInfo> includeRelations;
315 };
316 
317 // stateless functions
318 static QCString escapeAt(const QCString &text);
319 static QCString extractTrailingComment(const QCString &s);
320 static char resolveTrigraph(char c);
321 
322 // stateful functions
323 static inline void outputArray(yyscan_t yyscanner,const char *a,yy_size_t len);
324 static inline void outputString(yyscan_t yyscanner,const QCString &s);
325 static inline void outputChar(yyscan_t yyscanner,char c);
326 static inline void outputSpaces(yyscan_t yyscanner,char *s);
327 static inline void outputSpace(yyscan_t yyscanner,char c);
328 static inline void extraSpacing(yyscan_t yyscanner);
329 static QCString expandMacro(yyscan_t yyscanner,const QCString &name);
330 static void readIncludeFile(yyscan_t yyscanner,const QCString &inc);
331 static void incrLevel(yyscan_t yyscanner);
332 static void decrLevel(yyscan_t yyscanner);
333 static void setCaseDone(yyscan_t yyscanner,bool value);
334 static bool otherCaseDone(yyscan_t yyscanner);
335 static bool computeExpression(yyscan_t yyscanner,const QCString &expr);
336 static void startCondSection(yyscan_t yyscanner,const QCString &sectId);
337 static void endCondSection(yyscan_t yyscanner);
338 static void addMacroDefinition(yyscan_t yyscanner);
339 static void addDefine(yyscan_t yyscanner);
340 static void setFileName(yyscan_t yyscanner,const QCString &name);
341 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size);
342 static Define * isDefined(yyscan_t yyscanner,const QCString &name);
343 
344 /* ----------------------------------------------------------------- */
345 
346 #undef YY_INPUT
347 #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
348 
349 /* ----------------------------------------------------------------- */
350 
351 %}
352 
353 IDSTART [a-z_A-Z\x80-\xFF]
354 ID {IDSTART}[a-z_A-Z0-9\x80-\xFF]*
355 B [ \t]
356 Bopt {B}*
357 BN [ \t\r\n]
358 RAWBEGIN (u|U|L|u8)?R\"[^ \t\(\)\\]{0,16}"("
359 RAWEND ")"[^ \t\(\)\\]{0,16}\"
360 CHARLIT (("'"\\[0-7]{1,3}"'")|("'"\\."'")|("'"[^'\\\n]{1,4}"'"))
361 
362  // C start comment
363 CCS "/\*"
364  // C end comment
365 CCE "*\/"
366  // Cpp comment
367 CPPC "/\/"
368  // optional characters after import
369 ENDIMPORTopt [^\\\n]*
370  // Optional white space
371 WSopt [ \t\r]*
372 
373 %option noyywrap
374 
375 %x Start
376 %x Command
377 %x SkipCommand
378 %x SkipLine
379 %x SkipString
380 %x CopyLine
381 %x LexCopyLine
382 %x CopyString
383 %x CopyStringCs
384 %x CopyStringFtn
385 %x CopyStringFtnDouble
386 %x CopyRawString
387 %x Include
388 %x IncludeID
389 %x EndImport
390 %x DefName
391 %x DefineArg
392 %x DefineText
393 %x SkipCPPBlock
394 %x SkipCComment
395 %x ArgCopyCComment
396 %x CopyCComment
397 %x SkipVerbatim
398 %x SkipCPPComment
399 %x JavaDocVerbatimCode
400 %x RemoveCComment
401 %x RemoveCPPComment
402 %x Guard
403 %x DefinedExpr1
404 %x DefinedExpr2
405 %x SkipDoubleQuote
406 %x SkipSingleQuote
407 %x UndefName
408 %x IgnoreLine
409 %x FindDefineArgs
410 %x ReadString
411 %x CondLineC
412 %x CondLineCpp
413 %x SkipCond
414 
415 %%
416 
417 <*>\x06
418 <*>\x00
419 <*>\r
420 <*>"??"[=/'()!<>-] { // Trigraph
421  unput(resolveTrigraph(yytext[2]));
422  }
423 <Start>^{B}*"#" {
424  yyextra->yyColNr+=(int)yyleng;
425  yyextra->yyMLines=0;
426  yyextra->potentialDefine=yytext;
427  BEGIN(Command);
428  }
429 <Start>^("%top{"|"%{") {
430  if (getLanguageFromFileName(yyextra->yyFileName)!=SrcLangExt_Lex) REJECT
431  outputArray(yyscanner,yytext,yyleng);
432  BEGIN(LexCopyLine);
433  }
434 <Start>^{Bopt}/[^#] {
435  outputArray(yyscanner,yytext,yyleng);
436  BEGIN(CopyLine);
437  }
438 <Start>^{B}*[a-z_A-Z\x80-\xFF][a-z_A-Z0-9\x80-\xFF]+{B}*"("[^\)\n]*")"/{BN}{1,10}*[:{] { // constructors?
439  int i;
440  for (i=(int)yyleng-1;i>=0;i--)
441  {
442  unput(yytext[i]);
443  }
444  BEGIN(CopyLine);
445  }
446 <Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\(\)\n]*"("[^\)\n]*")"[^\)\n]*")"{B}*\n | // function list macro with one (...) argument, e.g. for K_GLOBAL_STATIC_WITH_ARGS
447 <Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\)\n]*")"{B}*\n { // function like macro
448  bool skipFuncMacros = Config_getBool(SKIP_FUNCTION_MACROS);
449  QCString name(yytext);
450  int pos = name.find('(');
451  if (pos<0) pos=0; // should never happen
452  name=name.left(pos).stripWhiteSpace();
453 
454  Define *def=0;
455  if (skipFuncMacros && !yyextra->insideFtn &&
456  name!="Q_PROPERTY" &&
457  !(
458  (yyextra->includeStack.empty() || yyextra->curlyCount>0) &&
459  yyextra->macroExpansion &&
460  (def=isDefined(yyscanner,name)) &&
461  /*macroIsAccessible(def) &&*/
462  (!yyextra->expandOnlyPredef || def->isPredefined)
463  )
464  )
465  {
466  outputChar(yyscanner,'\n');
467  yyextra->yyLineNr++;
468  }
469  else // don't skip
470  {
471  int i;
472  for (i=(int)yyleng-1;i>=0;i--)
473  {
474  unput(yytext[i]);
475  }
476  BEGIN(CopyLine);
477  }
478  }
479 <CopyLine,LexCopyLine>"extern"{BN}*"\""[^\"]+"\""{BN}*("{")? {
480  QCString text=yytext;
481  yyextra->yyLineNr+=text.contains('\n');
482  outputArray(yyscanner,yytext,yyleng);
483  }
484 <CopyLine,LexCopyLine>{RAWBEGIN} {
485  yyextra->delimiter = yytext+2;
486  yyextra->delimiter=yyextra->delimiter.left(yyextra->delimiter.length()-1);
487  outputArray(yyscanner,yytext,yyleng);
488  BEGIN(CopyRawString);
489  }
490 <CopyLine,LexCopyLine>"{" { // count brackets inside the main file
491  if (yyextra->includeStack.empty())
492  {
493  yyextra->curlyCount++;
494  }
495  outputChar(yyscanner,*yytext);
496  }
497 <LexCopyLine>^"%}" {
498  outputArray(yyscanner,yytext,yyleng);
499  }
500 <CopyLine,LexCopyLine>"}" { // count brackets inside the main file
501  if (yyextra->includeStack.empty() && yyextra->curlyCount>0)
502  {
503  yyextra->curlyCount--;
504  }
505  outputChar(yyscanner,*yytext);
506  }
507 <CopyLine,LexCopyLine>"'"\\[0-7]{1,3}"'" {
508  outputArray(yyscanner,yytext,yyleng);
509  }
510 <CopyLine,LexCopyLine>"'"\\."'" {
511  outputArray(yyscanner,yytext,yyleng);
512  }
513 <CopyLine,LexCopyLine>"'"."'" {
514  outputArray(yyscanner,yytext,yyleng);
515  }
516 <CopyLine,LexCopyLine>@\" {
517  if (getLanguageFromFileName(yyextra->yyFileName)!=SrcLangExt_CSharp) REJECT;
518  outputArray(yyscanner,yytext,yyleng);
519  BEGIN( CopyStringCs );
520  }
521 <CopyLine,LexCopyLine>\" {
522  outputChar(yyscanner,*yytext);
523  if (getLanguageFromFileName(yyextra->yyFileName)!=SrcLangExt_Fortran)
524  {
525  BEGIN( CopyString );
526  }
527  else
528  {
529  BEGIN( CopyStringFtnDouble );
530  }
531  }
532 <CopyLine,LexCopyLine>\' {
533  if (getLanguageFromFileName(yyextra->yyFileName)!=SrcLangExt_Fortran) REJECT;
534  outputChar(yyscanner,*yytext);
535  BEGIN( CopyStringFtn );
536  }
537 <CopyString>[^\"\\\r\n]+ {
538  outputArray(yyscanner,yytext,yyleng);
539  }
540 <CopyStringCs>[^\"\r\n]+ {
541  outputArray(yyscanner,yytext,yyleng);
542  }
543 <CopyString>\\. {
544  outputArray(yyscanner,yytext,yyleng);
545  }
546 <CopyString,CopyStringCs>\" {
547  outputChar(yyscanner,*yytext);
548  BEGIN( CopyLine );
549  }
550 <CopyStringFtnDouble>[^\"\\\r\n]+ {
551  outputArray(yyscanner,yytext,yyleng);
552  }
553 <CopyStringFtnDouble>\\. {
554  outputArray(yyscanner,yytext,yyleng);
555  }
556 <CopyStringFtnDouble>\" {
557  outputChar(yyscanner,*yytext);
558  BEGIN( CopyLine );
559  }
560 <CopyStringFtn>[^\'\\\r\n]+ {
561  outputArray(yyscanner,yytext,yyleng);
562  }
563 <CopyStringFtn>\\. {
564  outputArray(yyscanner,yytext,yyleng);
565  }
566 <CopyStringFtn>\' {
567  outputChar(yyscanner,*yytext);
568  BEGIN( CopyLine );
569  }
570 <CopyRawString>{RAWEND} {
571  outputArray(yyscanner,yytext,yyleng);
572  QCString delimiter = yytext+1;
573  delimiter=delimiter.left(delimiter.length()-1);
574  if (delimiter==yyextra->delimiter)
575  {
576  BEGIN( CopyLine );
577  }
578  }
579 <CopyRawString>[^)]+ {
580  outputArray(yyscanner,yytext,yyleng);
581  }
582 <CopyRawString>. {
583  outputChar(yyscanner,*yytext);
584  }
585 <CopyLine,LexCopyLine>{ID}/{BN}{0,80}"(" {
586  yyextra->expectGuard = FALSE;
587  Define *def=0;
588  //def=yyextra->globalDefineDict->find(yytext);
589  //def=isDefined(yyscanner,yytext);
590  //printf("Search for define %s found=%d yyextra->includeStack.empty()=%d "
591  // "yyextra->curlyCount=%d yyextra->macroExpansion=%d yyextra->expandOnlyPredef=%d "
592  // "isPreDefined=%d\n",yytext,def ? 1 : 0,
593  // yyextra->includeStack.empty(),yyextra->curlyCount,yyextra->macroExpansion,yyextra->expandOnlyPredef,
594  // def ? def->isPredefined : -1
595  // );
596  if ((yyextra->includeStack.empty() || yyextra->curlyCount>0) &&
597  yyextra->macroExpansion &&
598  (def=isDefined(yyscanner,yytext)) &&
599  /*(def->isPredefined || macroIsAccessible(def)) && */
600  (!yyextra->expandOnlyPredef || def->isPredefined)
601  )
602  {
603  //printf("Found it! #args=%d\n",def->nargs);
604  yyextra->roundCount=0;
605  yyextra->defArgsStr=yytext;
606  if (def->nargs==-1) // no function macro
607  {
608  QCString result = def->isPredefined ? def->definition : expandMacro(yyscanner,yyextra->defArgsStr);
609  outputString(yyscanner,result);
610  }
611  else // zero or more arguments
612  {
613  yyextra->findDefArgContext = CopyLine;
614  BEGIN(FindDefineArgs);
615  }
616  }
617  else
618  {
619  outputArray(yyscanner,yytext,yyleng);
620  }
621  }
622 <CopyLine,LexCopyLine>{ID} {
623  Define *def=0;
624  if ((yyextra->includeStack.empty() || yyextra->curlyCount>0) &&
625  yyextra->macroExpansion &&
626  (def=isDefined(yyscanner,yytext)) &&
627  def->nargs==-1 &&
628  /*(def->isPredefined || macroIsAccessible(def)) &&*/
629  (!yyextra->expandOnlyPredef || def->isPredefined)
630  )
631  {
632  QCString result=def->isPredefined ? def->definition : expandMacro(yyscanner,yytext);
633  outputString(yyscanner,result);
634  }
635  else
636  {
637  outputArray(yyscanner,yytext,yyleng);
638  }
639  }
640 <CopyLine,LexCopyLine>"\\"\r?/\n { // strip line continuation characters
641  if (getLanguageFromFileName(yyextra->yyFileName)==SrcLangExt_Fortran) outputChar(yyscanner,*yytext);
642  }
643 <CopyLine,LexCopyLine>\\. {
644  outputArray(yyscanner,yytext,(int)yyleng);
645  }
646 <CopyLine,LexCopyLine>. {
647  outputChar(yyscanner,*yytext);
648  }
649 <CopyLine,LexCopyLine>\n {
650  outputChar(yyscanner,'\n');
651  BEGIN(Start);
652  yyextra->yyLineNr++;
653  yyextra->yyColNr=1;
654  }
655 <FindDefineArgs>"(" {
656  yyextra->defArgsStr+='(';
657  yyextra->roundCount++;
658  }
659 <FindDefineArgs>")" {
660  yyextra->defArgsStr+=')';
661  yyextra->roundCount--;
662  if (yyextra->roundCount==0)
663  {
664  QCString result=expandMacro(yyscanner,yyextra->defArgsStr);
665  //printf("yyextra->defArgsStr='%s'->'%s'\n",qPrint(yyextra->defArgsStr),qPrint(result));
666  if (yyextra->findDefArgContext==CopyLine)
667  {
668  outputString(yyscanner,result);
669  BEGIN(yyextra->findDefArgContext);
670  }
671  else // yyextra->findDefArgContext==IncludeID
672  {
673  readIncludeFile(yyscanner,result);
674  yyextra->nospaces=FALSE;
675  BEGIN(Start);
676  }
677  }
678  }
679  /*
680 <FindDefineArgs>")"{B}*"(" {
681  yyextra->defArgsStr+=yytext;
682  }
683  */
684 <FindDefineArgs>{CHARLIT} {
685  yyextra->defArgsStr+=yytext;
686  }
687 <FindDefineArgs>{CCS}[*]? {
688  yyextra->defArgsStr+=yytext;
689  BEGIN(ArgCopyCComment);
690  }
691 <FindDefineArgs>\" {
692  yyextra->defArgsStr+=*yytext;
693  BEGIN(ReadString);
694  }
695 <FindDefineArgs>' {
696  if (getLanguageFromFileName(yyextra->yyFileName)!=SrcLangExt_Fortran) REJECT;
697  yyextra->defArgsStr+=*yytext;
698  BEGIN(ReadString);
699  }
700 <FindDefineArgs>\n {
701  yyextra->defArgsStr+=' ';
702  yyextra->yyLineNr++;
703  outputChar(yyscanner,'\n');
704  }
705 <FindDefineArgs>"@" {
706  yyextra->defArgsStr+="@@";
707  }
708 <FindDefineArgs>. {
709  yyextra->defArgsStr+=*yytext;
710  }
711 <ArgCopyCComment>[^*\n]+ {
712  yyextra->defArgsStr+=yytext;
713  }
714 <ArgCopyCComment>{CCE} {
715  yyextra->defArgsStr+=yytext;
716  BEGIN(FindDefineArgs);
717  }
718 <ArgCopyCComment>\n {
719  yyextra->defArgsStr+=' ';
720  yyextra->yyLineNr++;
721  outputChar(yyscanner,'\n');
722  }
723 <ArgCopyCComment>. {
724  yyextra->defArgsStr+=yytext;
725  }
726 <ReadString>"\"" {
727  yyextra->defArgsStr+=*yytext;
728  BEGIN(FindDefineArgs);
729  }
730 <ReadString>"'" {
731  if (getLanguageFromFileName(yyextra->yyFileName)!=SrcLangExt_Fortran) REJECT;
732  yyextra->defArgsStr+=*yytext;
733  BEGIN(FindDefineArgs);
734  }
735 
736 <ReadString>{CPPC}|{CCS} {
737  yyextra->defArgsStr+=yytext;
738  }
739 <ReadString>\\/\r?\n { // line continuation
740  }
741 <ReadString>\\. {
742  yyextra->defArgsStr+=yytext;
743  }
744 <ReadString>. {
745  yyextra->defArgsStr+=*yytext;
746  }
747 <Command>("include"|"import"){B}+/{ID} {
748  yyextra->isImported = yytext[1]=='m';
749  if (yyextra->macroExpansion)
750  BEGIN(IncludeID);
751  }
752 <Command>("include"|"import"){B}*[<"] {
753  yyextra->isImported = yytext[1]=='m';
754  char c[2];
755  c[0]=yytext[yyleng-1];c[1]='\0';
756  yyextra->incName=c;
757  BEGIN(Include);
758  }
759 <Command>("cmake")?"define"{B}+ {
760  yyextra->potentialDefine += substitute(yytext,"cmake"," ");
761  //printf("!!!DefName\n");
762  yyextra->yyColNr+=(int)yyleng;
763  BEGIN(DefName);
764  }
765 <Command>"ifdef"/{B}*"(" {
766  incrLevel(yyscanner);
767  yyextra->guardExpr.resize(0);
768  BEGIN(DefinedExpr2);
769  }
770 <Command>"ifdef"/{B}+ {
771  //printf("Pre.l: ifdef\n");
772  incrLevel(yyscanner);
773  yyextra->guardExpr.resize(0);
774  BEGIN(DefinedExpr1);
775  }
776 <Command>"ifndef"/{B}*"(" {
777  incrLevel(yyscanner);
778  yyextra->guardExpr="! ";
779  BEGIN(DefinedExpr2);
780  }
781 <Command>"ifndef"/{B}+ {
782  incrLevel(yyscanner);
783  yyextra->guardExpr="! ";
784  BEGIN(DefinedExpr1);
785  }
786 <Command>"if"/[ \t(!] {
787  incrLevel(yyscanner);
788  yyextra->guardExpr.resize(0);
789  BEGIN(Guard);
790  }
791 <Command>("elif"|"else"{B}*"if")/[ \t(!] {
792  if (!otherCaseDone(yyscanner))
793  {
794  yyextra->guardExpr.resize(0);
795  BEGIN(Guard);
796  }
797  else
798  {
799  yyextra->ifcount=0;
800  BEGIN(SkipCPPBlock);
801  }
802  }
803 <Command>"else"/[^a-z_A-Z0-9\x80-\xFF] {
804  if (otherCaseDone(yyscanner))
805  {
806  yyextra->ifcount=0;
807  BEGIN(SkipCPPBlock);
808  }
809  else
810  {
811  setCaseDone(yyscanner,TRUE);
812  }
813  }
814 <Command>"undef"{B}+ {
815  BEGIN(UndefName);
816  }
817 <Command>("elif"|"else"{B}*"if")/[ \t(!] {
818  if (!otherCaseDone(yyscanner))
819  {
820  yyextra->guardExpr.resize(0);
821  BEGIN(Guard);
822  }
823  }
824 <Command>"endif"/[^a-z_A-Z0-9\x80-\xFF] {
825  //printf("Pre.l: #endif\n");
826  decrLevel(yyscanner);
827  }
828 <Command,IgnoreLine>\n {
829  outputChar(yyscanner,'\n');
830  BEGIN(Start);
831  yyextra->yyLineNr++;
832  }
833 <Command>"pragma"{B}+"once" {
834  yyextra->expectGuard = FALSE;
835  }
836 <Command>{ID} { // unknown directive
837  BEGIN(IgnoreLine);
838  }
839 <IgnoreLine>\\[\r]?\n {
840  outputChar(yyscanner,'\n');
841  yyextra->yyLineNr++;
842  }
843 <IgnoreLine>.
844 <Command>. { yyextra->potentialDefine += yytext[0]=='\t' ? '\t' : ' ';
845  yyextra->yyColNr+=(int)yyleng;
846  }
847 <UndefName>{ID} {
848  Define *def;
849  if ((def=isDefined(yyscanner,yytext))
850  /*&& !def->isPredefined*/
851  && !def->nonRecursive
852  )
853  {
854  //printf("undefining %s\n",yytext);
855  def->undef=TRUE;
856  }
857  BEGIN(Start);
858  }
859 <Guard>\\[\r]?\n {
860  outputChar(yyscanner,'\n');
861  yyextra->guardExpr+=' ';
862  yyextra->yyLineNr++;
863  }
864 <Guard>"defined"/{B}*"(" {
865  BEGIN(DefinedExpr2);
866  }
867 <Guard>"defined"/{B}+ {
868  BEGIN(DefinedExpr1);
869  }
870 <Guard>{ID} { yyextra->guardExpr+=yytext; }
871 <Guard>"@" { yyextra->guardExpr+="@@"; }
872 <Guard>. { yyextra->guardExpr+=*yytext; }
873 <Guard>\n {
874  unput(*yytext);
875  //printf("Guard: '%s'\n",
876  // qPrint(yyextra->guardExpr));
877  bool guard=computeExpression(yyscanner,yyextra->guardExpr);
878  setCaseDone(yyscanner,guard);
879  if (guard)
880  {
881  BEGIN(Start);
882  }
883  else
884  {
885  yyextra->ifcount=0;
886  BEGIN(SkipCPPBlock);
887  }
888  }
889 <DefinedExpr1,DefinedExpr2>\\\n { yyextra->yyLineNr++; outputChar(yyscanner,'\n'); }
890 <DefinedExpr1>{ID} {
891  if (isDefined(yyscanner,yytext) || yyextra->guardName==yytext)
892  yyextra->guardExpr+=" 1L ";
893  else
894  yyextra->guardExpr+=" 0L ";
895  yyextra->lastGuardName=yytext;
896  BEGIN(Guard);
897  }
898 <DefinedExpr2>{ID} {
899  if (isDefined(yyscanner,yytext) || yyextra->guardName==yytext)
900  yyextra->guardExpr+=" 1L ";
901  else
902  yyextra->guardExpr+=" 0L ";
903  yyextra->lastGuardName=yytext;
904  }
905 <DefinedExpr1,DefinedExpr2>\n { // should not happen, handle anyway
906  yyextra->yyLineNr++;
907  yyextra->ifcount=0;
908  BEGIN(SkipCPPBlock);
909  }
910 <DefinedExpr2>")" {
911  BEGIN(Guard);
912  }
913 <DefinedExpr1,DefinedExpr2>.
914 <SkipCPPBlock>^{B}*"#" { BEGIN(SkipCommand); }
915 <SkipCPPBlock>^{Bopt}/[^#] { BEGIN(SkipLine); }
916 <SkipCPPBlock>\n { yyextra->yyLineNr++; outputChar(yyscanner,'\n'); }
917 <SkipCPPBlock>.
918 <SkipCommand>"if"(("n")?("def"))?/[ \t(!] {
919  incrLevel(yyscanner);
920  yyextra->ifcount++;
921  //printf("#if... depth=%d\n",yyextra->ifcount);
922  }
923 <SkipCommand>"else" {
924  //printf("Else! yyextra->ifcount=%d otherCaseDone=%d\n",yyextra->ifcount,otherCaseDone());
925  if (yyextra->ifcount==0 && !otherCaseDone(yyscanner))
926  {
927  setCaseDone(yyscanner,TRUE);
928  //outputChar(yyscanner,'\n');
929  BEGIN(Start);
930  }
931  }
932 <SkipCommand>("elif"|"else"{B}*"if")/[ \t(!] {
933  if (yyextra->ifcount==0)
934  {
935  if (!otherCaseDone(yyscanner))
936  {
937  yyextra->guardExpr.resize(0);
938  yyextra->lastGuardName.resize(0);
939  BEGIN(Guard);
940  }
941  else
942  {
943  BEGIN(SkipCPPBlock);
944  }
945  }
946  }
947 <SkipCommand>"endif" {
948  yyextra->expectGuard = FALSE;
949  decrLevel(yyscanner);
950  if (--yyextra->ifcount<0)
951  {
952  //outputChar(yyscanner,'\n');
953  BEGIN(Start);
954  }
955  }
956 <SkipCommand>\n {
957  outputChar(yyscanner,'\n');
958  yyextra->yyLineNr++;
959  BEGIN(SkipCPPBlock);
960  }
961 <SkipCommand>{ID} { // unknown directive
962  BEGIN(SkipLine);
963  }
964 <SkipCommand>.
965 <SkipLine>[^'"/\n]+
966 <SkipLine>{CHARLIT} { }
967 <SkipLine>\" {
968  BEGIN(SkipString);
969  }
970 <SkipLine>.
971 <SkipString>{CPPC}/[^\n]* {
972  }
973 <SkipLine,SkipCommand,SkipCPPBlock>{CPPC}[^\n]* {
974  yyextra->lastCPPContext=YY_START;
975  BEGIN(RemoveCPPComment);
976  }
977 <SkipString>{CCS}/[^\n]* {
978  }
979 <SkipLine,SkipCommand,SkipCPPBlock>{CCS}/[^\n]* {
980  yyextra->lastCContext=YY_START;
981  BEGIN(RemoveCComment);
982  }
983 <SkipLine>\n {
984  outputChar(yyscanner,'\n');
985  yyextra->yyLineNr++;
986  BEGIN(SkipCPPBlock);
987  }
988 <SkipString>[^"\\\n]+ { }
989 <SkipString>\\. { }
990 <SkipString>\" {
991  BEGIN(SkipLine);
992  }
993 <SkipString>. { }
994 <IncludeID>{ID}{Bopt}/"(" {
995  yyextra->nospaces=TRUE;
996  yyextra->roundCount=0;
997  yyextra->defArgsStr=yytext;
998  yyextra->findDefArgContext = IncludeID;
999  BEGIN(FindDefineArgs);
1000  }
1001 <IncludeID>{ID} {
1002  yyextra->nospaces=TRUE;
1003  readIncludeFile(yyscanner,expandMacro(yyscanner,yytext));
1004  BEGIN(Start);
1005  }
1006 <Include>[^\">\n]+[\">] {
1007  yyextra->incName+=yytext;
1008  readIncludeFile(yyscanner,yyextra->incName);
1009  if (yyextra->isImported)
1010  {
1011  BEGIN(EndImport);
1012  }
1013  else
1014  {
1015  BEGIN(Start);
1016  }
1017  }
1018 <EndImport>{ENDIMPORTopt}/\n {
1019  BEGIN(Start);
1020  }
1021 <EndImport>\\[\r]?"\n" {
1022  outputChar(yyscanner,'\n');
1023  yyextra->yyLineNr++;
1024  }
1025 <EndImport>. {
1026  }
1027 <DefName>{ID}/("\\\n")*"(" { // define with argument
1028  //printf("Define() '%s'\n",yytext);
1029  yyextra->argMap.clear();
1030  yyextra->defArgs = 0;
1031  yyextra->defArgsStr.resize(0);
1032  yyextra->defText.resize(0);
1033  yyextra->defLitText.resize(0);
1034  yyextra->defName = yytext;
1035  yyextra->defVarArgs = FALSE;
1036  yyextra->defExtraSpacing.resize(0);
1037  yyextra->defContinue = false;
1038  BEGIN(DefineArg);
1039  }
1040 <DefName>{ID}{B}+"1"/[ \r\t\n] { // special case: define with 1 -> can be "guard"
1041  //printf("Define '%s'\n",yytext);
1042  yyextra->argMap.clear();
1043  yyextra->defArgs = -1;
1044  yyextra->defArgsStr.resize(0);
1045  yyextra->defName = QCString(yytext).left(yyleng-1).stripWhiteSpace();
1046  yyextra->defVarArgs = FALSE;
1047  //printf("Guard check: %s!=%s || %d\n",
1048  // qPrint(yyextra->defName),qPrint(yyextra->lastGuardName),yyextra->expectGuard);
1049  if (yyextra->curlyCount>0 || yyextra->defName!=yyextra->lastGuardName || !yyextra->expectGuard)
1050  { // define may appear in the output
1051  QCString def = yyextra->potentialDefine +
1052  yyextra->defName ;
1053  outputString(yyscanner,def);
1054  outputSpaces(yyscanner,yytext+yyextra->defName.length());
1055  yyextra->quoteArg=FALSE;
1056  yyextra->insideComment=FALSE;
1057  yyextra->lastGuardName.resize(0);
1058  yyextra->defText="1";
1059  yyextra->defLitText="1";
1060  BEGIN(DefineText);
1061  }
1062  else // define is a guard => hide
1063  {
1064  //printf("Found a guard %s\n",yytext);
1065  yyextra->defText.resize(0);
1066  yyextra->defLitText.resize(0);
1067  BEGIN(Start);
1068  }
1069  yyextra->expectGuard=FALSE;
1070  }
1071 <DefName>{ID}/{B}*"\n" { // empty define
1072  yyextra->argMap.clear();
1073  yyextra->defArgs = -1;
1074  yyextra->defName = yytext;
1075  yyextra->defArgsStr.resize(0);
1076  yyextra->defText.resize(0);
1077  yyextra->defLitText.resize(0);
1078  yyextra->defVarArgs = FALSE;
1079  //printf("Guard check: %s!=%s || %d\n",
1080  // qPrint(yyextra->defName),qPrint(yyextra->lastGuardName),yyextra->expectGuard);
1081  if (yyextra->curlyCount>0 || yyextra->defName!=yyextra->lastGuardName || !yyextra->expectGuard)
1082  { // define may appear in the output
1083  QCString def = yyextra->potentialDefine + yyextra->defName;
1084  outputString(yyscanner,def);
1085  yyextra->quoteArg=FALSE;
1086  yyextra->insideComment=FALSE;
1087  if (yyextra->insideCS) yyextra->defText="1"; // for C#, use "1" as define text
1088  BEGIN(DefineText);
1089  }
1090  else // define is a guard => hide
1091  {
1092  //printf("Found a guard %s\n",yytext);
1093  yyextra->guardName = yytext;
1094  yyextra->lastGuardName.resize(0);
1095  BEGIN(Start);
1096  }
1097  yyextra->expectGuard=FALSE;
1098  }
1099 <DefName>{ID}/{B}* { // define with content
1100  //printf("Define '%s'\n",yytext);
1101  yyextra->argMap.clear();
1102  yyextra->defArgs = -1;
1103  yyextra->defArgsStr.resize(0);
1104  yyextra->defText.resize(0);
1105  yyextra->defLitText.resize(0);
1106  yyextra->defName = yytext;
1107  yyextra->defVarArgs = FALSE;
1108  QCString def = yyextra->potentialDefine +
1109  yyextra->defName +
1110  yyextra->defArgsStr ;
1111  outputString(yyscanner,def);
1112  yyextra->quoteArg=FALSE;
1113  yyextra->insideComment=FALSE;
1114  BEGIN(DefineText);
1115  }
1116 <DefineArg>"\\\n" {
1117  yyextra->defExtraSpacing+="\n";
1118  yyextra->defContinue = true;
1119  yyextra->yyLineNr++;
1120  }
1121 <DefineArg>{B}* { yyextra->defExtraSpacing+=yytext; }
1122 <DefineArg>","{B}* { yyextra->defArgsStr+=yytext; }
1123 <DefineArg>"("{B}* { yyextra->defArgsStr+=yytext; }
1124 <DefineArg>{B}*")"{B}* {
1125  extraSpacing(yyscanner);
1126  yyextra->defArgsStr+=yytext;
1127  QCString def = yyextra->potentialDefine +
1128  yyextra->defName +
1129  yyextra->defArgsStr +
1130  yyextra->defExtraSpacing ;
1131  outputString(yyscanner,def);
1132  yyextra->quoteArg=FALSE;
1133  yyextra->insideComment=FALSE;
1134  BEGIN(DefineText);
1135  }
1136 <DefineArg>"..." { // Variadic macro
1137  yyextra->defVarArgs = TRUE;
1138  yyextra->defArgsStr+=yytext;
1139  yyextra->argMap.emplace(std::string("__VA_ARGS__"),yyextra->defArgs);
1140  yyextra->defArgs++;
1141  }
1142 <DefineArg>{ID}{B}*("..."?) {
1143  //printf("Define addArg(%s)\n",yytext);
1144  QCString argName=yytext;
1145  yyextra->defVarArgs = yytext[yyleng-1]=='.';
1146  if (yyextra->defVarArgs) // strip ellipsis
1147  {
1148  argName=argName.left(argName.length()-3);
1149  }
1150  argName = argName.stripWhiteSpace();
1151  yyextra->defArgsStr+=yytext;
1152  yyextra->argMap.emplace(toStdString(argName),yyextra->defArgs);
1153  yyextra->defArgs++;
1154  extraSpacing(yyscanner);
1155  }
1156  /*
1157 <DefineText>"/ **"|"/ *!" {
1158  yyextra->defText+=yytext;
1159  yyextra->defLitText+=yytext;
1160  yyextra->insideComment=TRUE;
1161  }
1162 <DefineText>"* /" {
1163  yyextra->defText+=yytext;
1164  yyextra->defLitText+=yytext;
1165  yyextra->insideComment=FALSE;
1166  }
1167  */
1168 <DefineText>{CCS}[!*]? {
1169  yyextra->defText+=yytext;
1170  yyextra->defLitText+=yytext;
1171  yyextra->lastCContext=YY_START;
1172  yyextra->commentCount=1;
1173  BEGIN(CopyCComment);
1174  }
1175 <DefineText>{CPPC}[!/]? {
1176  outputArray(yyscanner,yytext,yyleng);
1177  yyextra->lastCPPContext=YY_START;
1178  yyextra->defLitText+=' ';
1179  BEGIN(SkipCPPComment);
1180  }
1181 <SkipCComment>[/]?{CCE} {
1182  if (yytext[0]=='/') outputChar(yyscanner,'/');
1183  outputChar(yyscanner,'*');outputChar(yyscanner,'/');
1184  if (--yyextra->commentCount<=0)
1185  {
1186  if (yyextra->lastCContext==Start)
1187  // small hack to make sure that ^... rule will
1188  // match when going to Start... Example: "/*...*/ some stuff..."
1189  {
1190  YY_CURRENT_BUFFER->yy_at_bol=1;
1191  }
1192  BEGIN(yyextra->lastCContext);
1193  }
1194  }
1195 <SkipCComment>{CPPC}("/")* {
1196  outputArray(yyscanner,yytext,yyleng);
1197  }
1198 <SkipCComment>{CCS} {
1199  outputChar(yyscanner,'/');outputChar(yyscanner,'*');
1200  //yyextra->commentCount++;
1201  }
1202 <SkipCComment>[\\@][\\@]("f{"|"f$"|"f[""f(") {
1203  outputArray(yyscanner,yytext,yyleng);
1204  }
1205 <SkipCComment>^({B}*"*"+)?{B}{0,3}"~~~"[~]* {
1206  bool markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
1207  if (!markdownSupport || !yyextra->isSpecialComment)
1208  {
1209  REJECT;
1210  }
1211  else
1212  {
1213  outputArray(yyscanner,yytext,yyleng);
1214  yyextra->fenceSize=(int)yyleng;
1215  BEGIN(SkipVerbatim);
1216  }
1217  }
1218 <SkipCComment>^({B}*"*"+)?{B}{0,3}"```"[`]* {
1219  bool markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
1220  if (!markdownSupport || !yyextra->isSpecialComment)
1221  {
1222  REJECT;
1223  }
1224  else
1225  {
1226  outputArray(yyscanner,yytext,yyleng);
1227  yyextra->fenceSize=(int)yyleng;
1228  BEGIN(SkipVerbatim);
1229  }
1230  }
1231 <SkipCComment>[\\@][\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"docbookonly"|"rtfonly"|"manonly"|"dot"|"code"("{"[^}]*"}")?){BN}+ {
1232  outputArray(yyscanner,yytext,yyleng);
1233  yyextra->yyLineNr+=QCString(yytext).contains('\n');
1234  }
1235 <SkipCComment>[\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"docbookonly"|"rtfonly"|"manonly"|"dot"|"code"("{"[^}]*"}")?){BN}+ {
1236  outputArray(yyscanner,yytext,yyleng);
1237  yyextra->yyLineNr+=QCString(yytext).contains('\n');
1238  yyextra->fenceSize=0;
1239  if (yytext[1]=='f')
1240  {
1241  yyextra->blockName="f";
1242  }
1243  else
1244  {
1245  QCString bn=&yytext[1];
1246  int i = bn.find('{'); // for \code{.c}
1247  if (i!=-1) bn=bn.left(i);
1248  yyextra->blockName=bn.stripWhiteSpace();
1249  }
1250  BEGIN(SkipVerbatim);
1251  }
1252 <SkipCComment>"{"[ \t]*"@code"/[ \t\n] {
1253  outputArray(yyscanner,"@code",5);
1254  yyextra->javaBlock=1;
1255  BEGIN(JavaDocVerbatimCode);
1256  }
1257 <SkipCComment,SkipCPPComment>[\\@][\\@]"cond"[ \t]+ { // escaped @cond
1258  outputArray(yyscanner,yytext,yyleng);
1259  }
1260 <SkipCPPComment>[\\@]"cond"[ \t]+ { // conditional section
1261  yyextra->ccomment=TRUE;
1262  yyextra->condCtx=YY_START;
1263  BEGIN(CondLineCpp);
1264  }
1265 <SkipCComment>[\\@]"cond"[ \t]+ { // conditional section
1266  yyextra->ccomment=FALSE;
1267  yyextra->condCtx=YY_START;
1268  BEGIN(CondLineC);
1269  }
1270 <CondLineC,CondLineCpp>[!()&| \ta-z_A-Z0-9\x80-\xFF.\-]+ {
1271  startCondSection(yyscanner,yytext);
1272  if (yyextra->skip)
1273  {
1274  if (YY_START==CondLineC)
1275  {
1276  // end C comment
1277  outputArray(yyscanner,"*/",2);
1278  yyextra->ccomment=TRUE;
1279  }
1280  else
1281  {
1282  yyextra->ccomment=FALSE;
1283  }
1284  BEGIN(SkipCond);
1285  }
1286  else
1287  {
1288  BEGIN(yyextra->condCtx);
1289  }
1290  }
1291 <CondLineC,CondLineCpp>. { // non-guard character
1292  unput(*yytext);
1293  startCondSection(yyscanner," ");
1294  if (yyextra->skip)
1295  {
1296  if (YY_START==CondLineC)
1297  {
1298  // end C comment
1299  outputArray(yyscanner,"*/",2);
1300  yyextra->ccomment=TRUE;
1301  }
1302  else
1303  {
1304  yyextra->ccomment=FALSE;
1305  }
1306  BEGIN(SkipCond);
1307  }
1308  else
1309  {
1310  BEGIN(yyextra->condCtx);
1311  }
1312  }
1313 <SkipCComment,SkipCPPComment>[\\@]"cond"{WSopt}/\n { // no guard
1314  if (YY_START==SkipCComment)
1315  {
1316  yyextra->ccomment=TRUE;
1317  // end C comment
1318  outputArray(yyscanner,"*/",2);
1319  }
1320  else
1321  {
1322  yyextra->ccomment=FALSE;
1323  }
1324  yyextra->condCtx=YY_START;
1325  startCondSection(yyscanner," ");
1326  BEGIN(SkipCond);
1327  }
1328 <SkipCond>\n { yyextra->yyLineNr++; outputChar(yyscanner,'\n'); }
1329 <SkipCond>. { }
1330 <SkipCond>[^\/\!*\\@\n]+ { }
1331 <SkipCond>{CPPC}[/!] { yyextra->ccomment=FALSE; }
1332 <SkipCond>{CCS}[*!] { yyextra->ccomment=TRUE; }
1333 <SkipCond,SkipCComment,SkipCPPComment>[\\@][\\@]"endcond"/[^a-z_A-Z0-9\x80-\xFF] {
1334  if (!yyextra->skip)
1335  {
1336  outputArray(yyscanner,yytext,yyleng);
1337  }
1338  }
1339 <SkipCond>[\\@]"endcond"/[^a-z_A-Z0-9\x80-\xFF] {
1340  bool oldSkip = yyextra->skip;
1341  endCondSection(yyscanner);
1342  if (oldSkip && !yyextra->skip)
1343  {
1344  if (yyextra->ccomment)
1345  {
1346  outputArray(yyscanner,"/** ",4);
1347  }
1348  BEGIN(yyextra->condCtx);
1349  }
1350  }
1351 <SkipCComment,SkipCPPComment>[\\@]"endcond"/[^a-z_A-Z0-9\x80-\xFF] {
1352  bool oldSkip = yyextra->skip;
1353  endCondSection(yyscanner);
1354  if (oldSkip && !yyextra->skip)
1355  {
1356  BEGIN(yyextra->condCtx);
1357  }
1358  }
1359 <SkipVerbatim>[\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"enddot"|"endcode"|"f$"|"f]"|"f}""f}") { /* end of verbatim block */
1360  outputArray(yyscanner,yytext,yyleng);
1361  if (yytext[1]=='f' && yyextra->blockName=="f")
1362  {
1363  BEGIN(SkipCComment);
1364  }
1365  else if (&yytext[4]==yyextra->blockName)
1366  {
1367  BEGIN(SkipCComment);
1368  }
1369  }
1370 <SkipVerbatim>^({B}*"*"+)?{B}{0,3}"~~~"[~]* {
1371  outputArray(yyscanner,yytext,yyleng);
1372  if (yyextra->fenceSize==(yy_size_t)yyleng)
1373  {
1374  BEGIN(SkipCComment);
1375  }
1376  }
1377 <SkipVerbatim>^({B}*"*"+)?{B}{0,3}"```"[`]* {
1378  outputArray(yyscanner,yytext,yyleng);
1379  if (yyextra->fenceSize==(yy_size_t)yyleng)
1380  {
1381  BEGIN(SkipCComment);
1382  }
1383  }
1384 <SkipVerbatim>{CCE}|{CCS} {
1385  outputArray(yyscanner,yytext,yyleng);
1386  }
1387 <JavaDocVerbatimCode>"{" {
1388  if (yyextra->javaBlock==0)
1389  {
1390  REJECT;
1391  }
1392  else
1393  {
1394  yyextra->javaBlock++;
1395  outputArray(yyscanner,yytext,(int)yyleng);
1396  }
1397  }
1398 <JavaDocVerbatimCode>"}" {
1399  if (yyextra->javaBlock==0)
1400  {
1401  REJECT;
1402  }
1403  else
1404  {
1405  yyextra->javaBlock--;
1406  if (yyextra->javaBlock==0)
1407  {
1408  outputArray(yyscanner," @endcode ",10);
1409  BEGIN(SkipCComment);
1410  }
1411  else
1412  {
1413  outputArray(yyscanner,yytext,(int)yyleng);
1414  }
1415  }
1416  }
1417 <JavaDocVerbatimCode>\n { /* new line in verbatim block */
1418  outputArray(yyscanner,yytext,(int)yyleng);
1419  }
1420 <JavaDocVerbatimCode>. { /* any other character */
1421  outputArray(yyscanner,yytext,(int)yyleng);
1422  }
1423 <SkipCComment,SkipVerbatim>[^{*\\@\x06~`\n\/]+ {
1424  outputArray(yyscanner,yytext,yyleng);
1425  }
1426 <SkipCComment,SkipVerbatim>\n {
1427  yyextra->yyLineNr++;
1428  outputChar(yyscanner,'\n');
1429  }
1430 <SkipCComment,SkipVerbatim>. {
1431  outputChar(yyscanner,*yytext);
1432  }
1433 <CopyCComment>[^*a-z_A-Z\x80-\xFF\n]*[^*a-z_A-Z\x80-\xFF\\\n] {
1434  yyextra->defLitText+=yytext;
1435  yyextra->defText+=escapeAt(yytext);
1436  }
1437 <CopyCComment>\\[\r]?\n {
1438  yyextra->defLitText+=yytext;
1439  yyextra->defText+=" ";
1440  yyextra->yyLineNr++;
1441  yyextra->yyMLines++;
1442  }
1443 <CopyCComment>{CCE} {
1444  yyextra->defLitText+=yytext;
1445  yyextra->defText+=yytext;
1446  BEGIN(yyextra->lastCContext);
1447  }
1448 <CopyCComment>\n {
1449  yyextra->yyLineNr++;
1450  yyextra->defLitText+=yytext;
1451  yyextra->defText+=' ';
1452  }
1453 <RemoveCComment>{CCE}{B}*"#" { // see bug 594021 for a usecase for this rule
1454  if (yyextra->lastCContext==SkipCPPBlock)
1455  {
1456  BEGIN(SkipCommand);
1457  }
1458  else
1459  {
1460  REJECT;
1461  }
1462  }
1463 <RemoveCComment>{CCE} { BEGIN(yyextra->lastCContext); }
1464 <RemoveCComment>{CPPC}
1465 <RemoveCComment>{CCS}
1466 <RemoveCComment>[^*\x06\n]+
1467 <RemoveCComment>\n { yyextra->yyLineNr++; outputChar(yyscanner,'\n'); }
1468 <RemoveCComment>.
1469 <SkipCPPComment>[^\n\/\\@]+ {
1470  outputArray(yyscanner,yytext,yyleng);
1471  }
1472 <SkipCPPComment,RemoveCPPComment>\n {
1473  unput(*yytext);
1474  BEGIN(yyextra->lastCPPContext);
1475  }
1476 <SkipCPPComment>{CCS} {
1477  outputChar(yyscanner,'/');outputChar(yyscanner,'*');
1478  }
1479 <SkipCPPComment>{CPPC} {
1480  outputChar(yyscanner,'/');outputChar(yyscanner,'/');
1481  }
1482 <SkipCPPComment>[^\x06\@\\\n]+ {
1483  outputArray(yyscanner,yytext,yyleng);
1484  }
1485 <SkipCPPComment>. {
1486  outputChar(yyscanner,*yytext);
1487  }
1488 <RemoveCPPComment>{CCS}
1489 <RemoveCPPComment>{CPPC}
1490 <RemoveCPPComment>[^\x06\n]+
1491 <RemoveCPPComment>.
1492 <DefineText>"#"/{IDSTART} {
1493  outputChar(yyscanner,' ');
1494  yyextra->quoteArg=TRUE;
1495  yyextra->idStart=true;
1496  yyextra->defLitText+=yytext;
1497  }
1498 <DefineText,CopyCComment>{ID} {
1499  yyextra->defLitText+=yytext;
1500  if (YY_START == DefineText) outputSpaces(yyscanner,yytext);
1501  if (yyextra->quoteArg)
1502  {
1503  yyextra->defText+="\"";
1504  }
1505  if (yyextra->defArgs>0)
1506  {
1507  auto it = yyextra->argMap.find(yytext);
1508  if (it!=yyextra->argMap.end())
1509  {
1510  int n = it->second;
1511  yyextra->defText+='@';
1512  yyextra->defText+=QCString().setNum(n);
1513  }
1514  else
1515  {
1516  if (yyextra->idStart)
1517  {
1518  warn(yyextra->yyFileName,yyextra->yyLineNr,
1519  "'#' is not followed by a macro parameter '%s': '%s'",
1520  qPrint(yyextra->defName),qPrint(yyextra->defLitText.stripWhiteSpace()));
1521  }
1522  yyextra->defText+=yytext;
1523  }
1524  }
1525  else
1526  {
1527  yyextra->defText+=yytext;
1528  }
1529  if (yyextra->quoteArg)
1530  {
1531  yyextra->defText+="\"";
1532  }
1533  yyextra->quoteArg=FALSE;
1534  yyextra->idStart=false;
1535  }
1536 <CopyCComment>. {
1537  yyextra->defLitText+=yytext;
1538  yyextra->defText+=yytext;
1539  }
1540 <DefineText>\\[\r]?\n {
1541  yyextra->defLitText+=yytext;
1542  outputChar(yyscanner,'\n');
1543  yyextra->defText += ' ';
1544  yyextra->yyLineNr++;
1545  yyextra->yyMLines++;
1546  }
1547 <DefineText>\n {
1548  QCString comment=extractTrailingComment(yyextra->defLitText);
1549  yyextra->defText = yyextra->defText.stripWhiteSpace();
1550  if (yyextra->defText.startsWith("##"))
1551  {
1552  warn(yyextra->yyFileName,yyextra->yyLineNr,
1553  "'##' cannot occur at the beginning of a macro definition '%s': '%s'",
1554  qPrint(yyextra->defName),qPrint(yyextra->defLitText.stripWhiteSpace()));
1555  }
1556  else if (yyextra->defText.endsWith("##"))
1557  {
1558  warn(yyextra->yyFileName,yyextra->yyLineNr,
1559  "'##' cannot occur at the end of a macro definition '%s': '%s'",
1560  qPrint(yyextra->defName),qPrint(yyextra->defLitText.stripWhiteSpace()));
1561  }
1562  else if (yyextra->defText.endsWith("#"))
1563  {
1564  warn(yyextra->yyFileName,yyextra->yyLineNr,
1565  "expected formal parameter after # in macro definition '%s': '%s'",
1566  qPrint(yyextra->defName),qPrint(yyextra->defLitText.stripWhiteSpace()));
1567  }
1568  yyextra->defLitText+=yytext;
1569  if (!comment.isEmpty())
1570  {
1571  outputString(yyscanner,comment);
1572  yyextra->defLitText=yyextra->defLitText.left(yyextra->defLitText.length()-comment.length()-1);
1573  }
1574  outputChar(yyscanner,'\n');
1575  Define *def=0;
1576  //printf("Define name='%s' text='%s' litTexti='%s'\n",qPrint(yyextra->defName),qPrint(yyextra->defText),qPrint(yyextra->defLitText));
1577  if (yyextra->includeStack.empty() || yyextra->curlyCount>0)
1578  {
1579  addMacroDefinition(yyscanner);
1580  }
1581  def=isDefined(yyscanner,yyextra->defName);
1582  if (def==0) // new define
1583  {
1584  //printf("new define '%s'!\n",qPrint(yyextra->defName));
1585  addDefine(yyscanner);
1586  }
1587  else if (def /*&& macroIsAccessible(def)*/)
1588  // name already exists
1589  {
1590  //printf("existing define!\n");
1591  //printf("define found\n");
1592  if (def->undef) // undefined name
1593  {
1594  def->undef = FALSE;
1595  def->name = yyextra->defName;
1596  def->definition = yyextra->defText.stripWhiteSpace();
1597  def->nargs = yyextra->defArgs;
1598  def->fileName = yyextra->yyFileName;
1599  def->lineNr = yyextra->yyLineNr-yyextra->yyMLines;
1600  def->columnNr = yyextra->yyColNr;
1601  }
1602  else
1603  {
1604  //printf("error: define %s is defined more than once!\n",qPrint(yyextra->defName));
1605  }
1606  }
1607  yyextra->argMap.clear();
1608  yyextra->yyLineNr++;
1609  yyextra->yyColNr=1;
1610  yyextra->lastGuardName.resize(0);
1611  BEGIN(Start);
1612  }
1613 <DefineText>{B}* { outputString(yyscanner,yytext);
1614  yyextra->defText += ' ';
1615  yyextra->defLitText+=yytext;
1616  }
1617 <DefineText>{B}*"##"{B}* { outputString(yyscanner,substitute(yytext,"##"," "));
1618  yyextra->defText += "##";
1619  yyextra->defLitText+=yytext;
1620  }
1621 <DefineText>"@" { outputString(yyscanner,substitute(yytext,"@@"," "));
1622  yyextra->defText += "@@";
1623  yyextra->defLitText+=yytext;
1624  }
1625 <DefineText>\" {
1626  outputChar(yyscanner,' ');
1627  yyextra->defText += *yytext;
1628  yyextra->defLitText+=yytext;
1629  if (!yyextra->insideComment)
1630  {
1631  BEGIN(SkipDoubleQuote);
1632  }
1633  }
1634 <DefineText>\' {
1635  outputChar(yyscanner,' ');
1636  yyextra->defText += *yytext;
1637  yyextra->defLitText+=yytext;
1638  if (!yyextra->insideComment)
1639  {
1640  BEGIN(SkipSingleQuote);
1641  }
1642  }
1643 <SkipDoubleQuote>{CPPC}[/]? { outputSpaces(yyscanner,yytext);
1644  yyextra->defText += yytext;
1645  yyextra->defLitText+=yytext;
1646  }
1647 <SkipDoubleQuote>{CCS}[*]? { outputSpaces(yyscanner,yytext);
1648  yyextra->defText += yytext;
1649  yyextra->defLitText+=yytext;
1650  }
1651 <SkipDoubleQuote>\" {
1652  outputChar(yyscanner,' ');
1653  yyextra->defText += *yytext;
1654  yyextra->defLitText+=yytext;
1655  BEGIN(DefineText);
1656  }
1657 <SkipSingleQuote,SkipDoubleQuote>\\. {
1658  outputSpaces(yyscanner,yytext);
1659  yyextra->defText += yytext;
1660  yyextra->defLitText+=yytext;
1661  }
1662 <SkipSingleQuote>\' {
1663  outputChar(yyscanner,' ');
1664  yyextra->defText += *yytext;
1665  yyextra->defLitText+=yytext;
1666  BEGIN(DefineText);
1667  }
1668 <SkipDoubleQuote,SkipSingleQuote>. { outputSpace(yyscanner,yytext[0]);
1669  yyextra->defText += *yytext;
1670  yyextra->defLitText += *yytext;
1671  }
1672 <DefineText>. { outputSpace(yyscanner,yytext[0]);
1673  yyextra->defText += *yytext;
1674  yyextra->defLitText += *yytext;
1675  }
1676 <<EOF>> {
1677  DBG_CTX((stderr,"End of include file\n"));
1678  //printf("Include stack depth=%d\n",yyextra->includeStack.size());
1679  if (yyextra->includeStack.empty())
1680  {
1681  DBG_CTX((stderr,"Terminating scanner!\n"));
1682  yyterminate();
1683  }
1684  else
1685  {
1686  QCString toFileName = yyextra->yyFileName;
1687  const std::unique_ptr<FileState> &fs=yyextra->includeStack.back();
1688  //fileDefineCache->merge(yyextra->yyFileName,fs->fileName);
1689  YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
1690  yy_switch_to_buffer( fs->bufState, yyscanner );
1691  yy_delete_buffer( oldBuf, yyscanner );
1692  yyextra->yyLineNr = fs->lineNr;
1693  //preYYin = fs->oldYYin;
1694  yyextra->inputBuf = fs->oldFileBuf;
1695  yyextra->inputBufPos = fs->oldFileBufPos;
1696  yyextra->curlyCount = fs->curlyCount;
1697  setFileName(yyscanner,fs->fileName);
1698  DBG_CTX((stderr,"######## FileName %s\n",qPrint(yyextra->yyFileName)));
1699 
1700  // Deal with file changes due to
1701  // #include's within { .. } blocks
1702  QCString lineStr(15+yyextra->yyFileName.length());
1703  lineStr.sprintf("# %d \"%s\" 2",yyextra->yyLineNr,qPrint(yyextra->yyFileName));
1704  outputString(yyscanner,lineStr);
1705 
1706  yyextra->includeStack.pop_back();
1707 
1708  {
1709  std::lock_guard<std::mutex> lock(g_globalDefineMutex);
1710  // to avoid deadlocks we allow multiple threads to process the same header file.
1711  // The first one to finish will store the results globally. After that the
1712  // next time the same file is encountered, the stored data is used and the file
1713  // is not processed again.
1714  if (!g_defineManager.alreadyProcessed(toFileName.str()))
1715  {
1716  // now that the file is completely processed, prevent it from processing it again
1717  g_defineManager.addInclude(yyextra->yyFileName.str(),toFileName.str());
1718  g_defineManager.store(toFileName.str(),yyextra->localDefines);
1719  }
1720  else
1721  {
1722  if (Debug::isFlagSet(Debug::Preprocessor))
1723  {
1724  Debug::print(Debug::Preprocessor,0,"#include %s: was already processed by another thread! not storing data...\n",qPrint(toFileName));
1725  }
1726  }
1727  }
1728  // move the local macros definitions for in this file to the translation unit context
1729  for (const auto &kv : yyextra->localDefines)
1730  {
1731  auto pair = yyextra->contextDefines.insert(kv);
1732  if (!pair.second) // define already in context -> replace with local version
1733  {
1734  yyextra->contextDefines.erase(pair.first);
1735  yyextra->contextDefines.insert(kv);
1736  }
1737  }
1738  yyextra->localDefines.clear();
1739  }
1740  }
1741 <*>{CCS}/{CCE} |
1742 <*>{CCS}[*!]? {
1743  if (YY_START==SkipVerbatim || YY_START==SkipCond)
1744  {
1745  REJECT;
1746  }
1747  else
1748  {
1749  outputArray(yyscanner,yytext,yyleng);
1750  yyextra->lastCContext=YY_START;
1751  yyextra->commentCount=1;
1752  if (yyleng==3)
1753  {
1754  yyextra->isSpecialComment = true;
1755  yyextra->lastGuardName.resize(0); // reset guard in case the #define is documented!
1756  }
1757  else
1758  {
1759  yyextra->isSpecialComment = false;
1760  }
1761  BEGIN(SkipCComment);
1762  }
1763  }
1764 <*>{CPPC}[/!]? {
1765  if (YY_START==SkipVerbatim || YY_START==SkipCond || getLanguageFromFileName(yyextra->yyFileName)==SrcLangExt_Fortran)
1766  {
1767  REJECT;
1768  }
1769  else
1770  {
1771  outputArray(yyscanner,yytext,yyleng);
1772  yyextra->lastCPPContext=YY_START;
1773  if (yyleng==3)
1774  {
1775  yyextra->isSpecialComment = true;
1776  yyextra->lastGuardName.resize(0); // reset guard in case the #define is documented!
1777  }
1778  else
1779  {
1780  yyextra->isSpecialComment = false;
1781  }
1782  BEGIN(SkipCPPComment);
1783  }
1784  }
1785 <*>\n {
1786  outputChar(yyscanner,'\n');
1787  yyextra->yyLineNr++;
1788  }
1789 <*>. {
1790  yyextra->expectGuard = FALSE;
1791  outputChar(yyscanner,*yytext);
1792  }
1793 
1794 %%
1795 
1796 /////////////////////////////////////////////////////////////////////////////////////
1797 
1798 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size)
1799 {
1800  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1801  yy_size_t bytesInBuf = state->inputBuf->curPos()-state->inputBufPos;
1802  yy_size_t bytesToCopy = std::min(max_size,bytesInBuf);
1803  memcpy(buf,state->inputBuf->data()+state->inputBufPos,bytesToCopy);
1804  state->inputBufPos+=bytesToCopy;
1805  return bytesToCopy;
1806 }
1807 
1808 static void setFileName(yyscan_t yyscanner,const QCString &name)
1809 {
1810  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1811  bool ambig;
1812  FileInfo fi(name.str());
1813  state->yyFileName=fi.absFilePath();
1814  state->yyFileDef=findFileDef(Doxygen::inputNameLinkedMap,state->yyFileName,ambig);
1815  if (state->yyFileDef==0) // if this is not an input file check if it is an
1816  // include file
1817  {
1818  state->yyFileDef=findFileDef(Doxygen::includeNameLinkedMap,state->yyFileName,ambig);
1819  }
1820  //printf("setFileName(%s) state->yyFileName=%s state->yyFileDef=%p\n",
1821  // name,qPrint(state->yyFileName),state->yyFileDef);
1822  if (state->yyFileDef && state->yyFileDef->isReference()) state->yyFileDef=0;
1823  state->insideCS = getLanguageFromFileName(state->yyFileName)==SrcLangExt_CSharp;
1824  state->insideFtn = getLanguageFromFileName(state->yyFileName)==SrcLangExt_Fortran;
1825  state->isSource = guessSection(state->yyFileName);
1826 }
1827 
1828 static void incrLevel(yyscan_t yyscanner)
1829 {
1830  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1831  state->levelGuard.push(false);
1832  //printf("%s line %d: incrLevel %d\n",qPrint(yyextra->yyFileName),yyextra->yyLineNr,yyextra->levelGuard.size());
1833 }
1834 
1835 static void decrLevel(yyscan_t yyscanner)
1836 {
1837  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1838  //printf("%s line %d: decrLevel %d\n",qPrint(state->yyFileName),state->yyLineNr,state->levelGuard.size());
1839  if (!state->levelGuard.empty())
1840  {
1841  state->levelGuard.pop();
1842  }
1843  else
1844  {
1845  warn(state->yyFileName,state->yyLineNr,"More #endif's than #if's found.\n");
1846  }
1847 }
1848 
1849 static bool otherCaseDone(yyscan_t yyscanner)
1850 {
1851  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1852  if (state->levelGuard.empty())
1853  {
1854  warn(state->yyFileName,state->yyLineNr,"Found an #else without a preceding #if.\n");
1855  return TRUE;
1856  }
1857  else
1858  {
1859  return state->levelGuard.top();
1860  }
1861 }
1862 
1863 static void setCaseDone(yyscan_t yyscanner,bool value)
1864 {
1865  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1866  state->levelGuard.top()=value;
1867 }
1868 
1869 
1870 static FileState *checkAndOpenFile(yyscan_t yyscanner,const QCString &fileName,bool &alreadyProcessed)
1871 {
1872  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1873  alreadyProcessed = FALSE;
1874  FileState *fs = 0;
1875  //printf("checkAndOpenFile(%s)\n",qPrint(fileName));
1876  FileInfo fi(fileName.str());
1877  if (fi.exists() && fi.isFile())
1878  {
1879  const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
1880  if (patternMatch(fi,exclPatterns)) return 0;
1881 
1882  QCString absName = fi.absFilePath();
1883 
1884  // global guard
1885  if (state->curlyCount==0) // not #include inside { ... }
1886  {
1887  std::lock_guard<std::mutex> lock(g_globalDefineMutex);
1888  if (g_defineManager.alreadyProcessed(absName.str()))
1889  {
1890  alreadyProcessed = TRUE;
1891  //printf(" already included 1\n");
1892  return 0; // already done
1893  }
1894  }
1895  // check include stack for absName
1896 
1897  alreadyProcessed = std::any_of(
1898  state->includeStack.begin(),
1899  state->includeStack.end(),
1900  [absName](const std::unique_ptr<FileState> &lfs)
1901  { return lfs->fileName==absName; }
1902  );
1903 
1904  if (alreadyProcessed)
1905  {
1906  //printf(" already included 2\n");
1907  return 0;
1908  }
1909  //printf("#include %s\n",qPrint(absName));
1910 
1911  fs = new FileState(static_cast<uint>(fi.size())+4096);
1912  if (!readInputFile(absName,fs->fileBuf))
1913  { // error
1914  //printf(" error reading\n");
1915  delete fs;
1916  fs=0;
1917  }
1918  else
1919  {
1920  fs->oldFileBuf = state->inputBuf;
1921  fs->oldFileBufPos = state->inputBufPos;
1922  }
1923  }
1924  return fs;
1925 }
1926 
1927 static FileState *findFile(yyscan_t yyscanner, const QCString &fileName,bool localInclude,bool &alreadyProcessed)
1928 {
1929  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
1930  //printf("** findFile(%s,%d) state->yyFileName=%s\n",qPrint(fileName),localInclude,qPrint(state->yyFileName));
1931  if (Portable::isAbsolutePath(fileName))
1932  {
1933  FileState *fs = checkAndOpenFile(yyscanner,fileName,alreadyProcessed);
1934  if (fs)
1935  {
1936  setFileName(yyscanner,fileName);
1937  state->yyLineNr=1;
1938  return fs;
1939  }
1940  else if (alreadyProcessed)
1941  {
1942  return 0;
1943  }
1944  }
1945  if (localInclude && !state->yyFileName.isEmpty())
1946  {
1947  FileInfo fi(state->yyFileName.str());
1948  if (fi.exists())
1949  {
1950  QCString absName = QCString(fi.dirPath(TRUE))+"/"+fileName;
1951  FileState *fs = checkAndOpenFile(yyscanner,absName,alreadyProcessed);
1952  if (fs)
1953  {
1954  setFileName(yyscanner,absName);
1955  state->yyLineNr=1;
1956  return fs;
1957  }
1958  else if (alreadyProcessed)
1959  {
1960  return 0;
1961  }
1962  }
1963  }
1964  if (state->pathList.empty())
1965  {
1966  return 0;
1967  }
1968  for (auto path : state->pathList)
1969  {
1970  std::string absName = (path+"/"+fileName).str();
1971  //printf(" Looking for %s in %s\n",fileName,path.c_str());
1972  FileState *fs = checkAndOpenFile(yyscanner,absName.c_str(),alreadyProcessed);
1973  if (fs)
1974  {
1975  setFileName(yyscanner,absName.c_str());
1976  state->yyLineNr=1;
1977  //printf(" -> found it\n");
1978  return fs;
1979  }
1980  else if (alreadyProcessed)
1981  {
1982  return 0;
1983  }
1984  }
1985  return 0;
1986 }
1987 
1988 static QCString extractTrailingComment(const QCString &s)
1989 {
1990  if (s.isEmpty()) return "";
1991  int i=(int)s.length()-1;
1992  while (i>=0)
1993  {
1994  char c=s[i];
1995  switch (c)
1996  {
1997  case '/':
1998  {
1999  i--;
2000  if (i>=0 && s[i]=='*') // end of a comment block
2001  {
2002  i--;
2003  while (i>0 && !(s[i-1]=='/' && s[i]=='*')) i--;
2004  if (i==0)
2005  {
2006  i++;
2007  }
2008  // only /*!< or /**< are treated as a comment for the macro name,
2009  // otherwise the comment is treated as part of the macro definition
2010  return ((s[i+1]=='*' || s[i+1]=='!') && s[i+2]=='<') ? &s[i-1] : "";
2011  }
2012  else
2013  {
2014  return "";
2015  }
2016  }
2017  break;
2018  // whitespace or line-continuation
2019  case ' ':
2020  case '\t':
2021  case '\r':
2022  case '\n':
2023  case '\\':
2024  break;
2025  default:
2026  return "";
2027  }
2028  i--;
2029  }
2030  return "";
2031 }
2032 
2033 static int getNextChar(yyscan_t yyscanner,const QCString &expr,QCString *rest,uint &pos);
2034 static int getCurrentChar(yyscan_t yyscanner,const QCString &expr,QCString *rest,uint pos);
2035 static void unputChar(yyscan_t yyscanner,const QCString &expr,QCString *rest,uint &pos,char c);
2036 static bool expandExpression(yyscan_t yyscanner,QCString &expr,QCString *rest,int pos,int level);
2037 
2038 static QCString stringize(const QCString &s)
2039 {
2040  QCString result;
2041  uint i=0;
2042  bool inString=FALSE;
2043  bool inChar=FALSE;
2044  char c,pc;
2045  while (i<s.length())
2046  {
2047  if (!inString && !inChar)
2048  {
2049  while (i<s.length() && !inString && !inChar)
2050  {
2051  c=s.at(i++);
2052  if (c=='"')
2053  {
2054  result+="\\\"";
2055  inString=TRUE;
2056  }
2057  else if (c=='\'')
2058  {
2059  result+=c;
2060  inChar=TRUE;
2061  }
2062  else
2063  {
2064  result+=c;
2065  }
2066  }
2067  }
2068  else if (inChar)
2069  {
2070  while (i<s.length() && inChar)
2071  {
2072  c=s.at(i++);
2073  if (c=='\'')
2074  {
2075  result+='\'';
2076  inChar=FALSE;
2077  }
2078  else if (c=='\\')
2079  {
2080  result+="\\\\";
2081  }
2082  else
2083  {
2084  result+=c;
2085  }
2086  }
2087  }
2088  else
2089  {
2090  pc=0;
2091  while (i<s.length() && inString)
2092  {
2093  c=s.at(i++);
2094  if (c=='"')
2095  {
2096  result+="\\\"";
2097  inString= pc=='\\';
2098  }
2099  else if (c=='\\')
2100  result+="\\\\";
2101  else
2102  result+=c;
2103  pc=c;
2104  }
2105  }
2106  }
2107  //printf("stringize '%s'->'%s'\n",qPrint(s),qPrint(result));
2108  return result;
2109 }
2110 
2111 /*! Execute all ## operators in expr.
2112  * If the macro name before or after the operator contains a no-rescan
2113  * marker (@-) then this is removed (before the concatenated macro name
2114  * may be expanded again.
2115  */
2116 static void processConcatOperators(QCString &expr)
2117 {
2118  if (expr.isEmpty()) return;
2119  //printf("processConcatOperators: in='%s'\n",qPrint(expr));
2120  std::string e = expr.str();
2121  static const reg::Ex r(R"(\s*##\s*)");
2122  reg::Iterator end;
2123 
2124  size_t i=0;
2125  for (;;)
2126  {
2127  reg::Iterator it(e,r,i);
2128  if (it!=end)
2129  {
2130  const auto &match = *it;
2131  size_t n = match.position();
2132  size_t l = match.length();
2133  //printf("Match: '%s'\n",qPrint(expr.mid(i)));
2134  if (n+l+1<e.length() && e[static_cast<int>(n+l)]=='@' && expr[static_cast<int>(n+l+1)]=='-')
2135  {
2136  // remove no-rescan marker after ID
2137  l+=2;
2138  }
2139  //printf("found '%s'\n",qPrint(expr.mid(n,l)));
2140  // remove the ## operator and the surrounding whitespace
2141  e=e.substr(0,n)+e.substr(n+l);
2142  int k=static_cast<int>(n)-1;
2143  while (k>=0 && isId(e[k])) k--;
2144  if (k>0 && e[k]=='-' && e[k-1]=='@')
2145  {
2146  // remove no-rescan marker before ID
2147  e=e.substr(0,k-1)+e.substr(k+1);
2148  n-=2;
2149  }
2150  i=n;
2151  }
2152  else
2153  {
2154  break;
2155  }
2156  }
2157 
2158  expr = e;
2159 
2160  //printf("processConcatOperators: out='%s'\n",qPrint(expr));
2161 }
2162 
2163 static void returnCharToStream(yyscan_t yyscanner,char c)
2164 {
2165  struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
2166  unput(c);
2167 }
2168 
2169 static inline void addTillEndOfString(yyscan_t yyscanner,const QCString &expr,QCString *rest,
2170  uint &pos,char term,QCString &arg)
2171 {
2172  int cc;
2173  while ((cc=getNextChar(yyscanner,expr,rest,pos))!=EOF && cc!=0)
2174  {
2175  if (cc=='\\') arg+=(char)cc,cc=getNextChar(yyscanner,expr,rest,pos);
2176  else if (cc==term) return;
2177  arg+=(char)cc;
2178  }
2179 }
2180 
2181 /*! replaces the function macro \a def whose argument list starts at
2182  * \a pos in expression \a expr.
2183  * Notice that this routine may scan beyond the \a expr string if needed.
2184  * In that case the characters will be read from the input file.
2185  * The replacement string will be returned in \a result and the
2186  * length of the (unexpanded) argument list is stored in \a len.
2187  */
2188 static bool replaceFunctionMacro(yyscan_t yyscanner,const QCString &expr,QCString *rest,int pos,int &len,const Define *def,QCString &result,int level)
2189 {
2190  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2191  //printf(">replaceFunctionMacro(expr='%s',rest='%s',pos=%d,def='%s') level=%d\n",qPrint(expr),rest ? qPrint(*rest) : 0,pos,qPrint(def->name),state->levelGuard.size());
2192  uint j=pos;
2193  len=0;
2194  result.resize(0);
2195  int cc;
2196  while ((cc=getCurrentChar(yyscanner,expr,rest,j))!=EOF && isspace(cc))
2197  {
2198  len++;
2199  getNextChar(yyscanner,expr,rest,j);
2200  }
2201  if (cc!='(')
2202  {
2203  unputChar(yyscanner,expr,rest,j,' ');
2204  return FALSE;
2205  }
2206  getNextChar(yyscanner,expr,rest,j); // eat the '(' character
2207 
2208  std::map<std::string,std::string> argTable; // list of arguments
2209  QCString arg;
2210  int argCount=0;
2211  bool done=FALSE;
2212 
2213  // PHASE 1: read the macro arguments
2214  if (def->nargs==0)
2215  {
2216  while ((cc=getNextChar(yyscanner,expr,rest,j))!=EOF && cc!=0)
2217  {
2218  char c = (char)cc;
2219  if (c==')') break;
2220  }
2221  }
2222  else
2223  {
2224  while (!done && (argCount<def->nargs || def->varArgs) &&
2225  ((cc=getNextChar(yyscanner,expr,rest,j))!=EOF && cc!=0)
2226  )
2227  {
2228  char c=(char)cc;
2229  if (c=='(') // argument is a function => search for matching )
2230  {
2231  int lvl=1;
2232  arg+=c;
2233  //char term='\0';
2234  while ((cc=getNextChar(yyscanner,expr,rest,j))!=EOF && cc!=0)
2235  {
2236  c=(char)cc;
2237  //printf("processing %c: term=%c (%d)\n",c,term,term);
2238  if (c=='\'' || c=='\"') // skip ('s and )'s inside strings
2239  {
2240  arg+=c;
2241  addTillEndOfString(yyscanner,expr,rest,j,c,arg);
2242  }
2243  if (c==')')
2244  {
2245  lvl--;
2246  arg+=c;
2247  if (lvl==0) break;
2248  }
2249  else if (c=='(')
2250  {
2251  lvl++;
2252  arg+=c;
2253  }
2254  else
2255  arg+=c;
2256  }
2257  }
2258  else if (c==')' || c==',') // last or next argument found
2259  {
2260  if (c==',' && argCount==def->nargs-1 && def->varArgs)
2261  {
2262  arg=arg.stripWhiteSpace();
2263  arg+=',';
2264  }
2265  else
2266  {
2267  QCString argKey;
2268  argKey.sprintf("@%d",argCount++); // key name
2269  arg=arg.stripWhiteSpace();
2270  // add argument to the lookup table
2271  argTable.emplace(toStdString(argKey), toStdString(arg));
2272  arg.resize(0);
2273  if (c==')') // end of the argument list
2274  {
2275  done=TRUE;
2276  }
2277  }
2278  }
2279  else if (c=='\"') // append literal strings
2280  {
2281  arg+=c;
2282  bool found=FALSE;
2283  while (!found && (cc=getNextChar(yyscanner,expr,rest,j))!=EOF && cc!=0)
2284  {
2285  found = cc=='"';
2286  if (cc=='\\')
2287  {
2288  c=(char)cc;
2289  arg+=c;
2290  if ((cc=getNextChar(yyscanner,expr,rest,j))==EOF || cc==0) break;
2291  }
2292  c=(char)cc;
2293  arg+=c;
2294  }
2295  }
2296  else if (c=='\'') // append literal characters
2297  {
2298  arg+=c;
2299  bool found=FALSE;
2300  while (!found && (cc=getNextChar(yyscanner,expr,rest,j))!=EOF && cc!=0)
2301  {
2302  found = cc=='\'';
2303  if (cc=='\\')
2304  {
2305  c=(char)cc;
2306  arg+=c;
2307  if ((cc=getNextChar(yyscanner,expr,rest,j))==EOF || cc==0) break;
2308  }
2309  c=(char)cc;
2310  arg+=c;
2311  }
2312  }
2313  else if (c=='/') // possible start of a comment
2314  {
2315  char prevChar = '\0';
2316  arg+=c;
2317  if ((cc=getCurrentChar(yyscanner,expr,rest,j)) == '*') // we have a comment
2318  {
2319  while ((cc=getNextChar(yyscanner,expr,rest,j))!=EOF && cc!=0)
2320  {
2321  c=(char)cc;
2322  arg+=c;
2323  if (c == '/' && prevChar == '*') break; // we have an end of comment
2324  prevChar = c;
2325  }
2326  }
2327  }
2328  else // append other characters
2329  {
2330  arg+=c;
2331  }
2332  }
2333  }
2334 
2335  // PHASE 2: apply the macro function
2336  if (argCount==def->nargs || // same number of arguments
2337  (argCount>=def->nargs-1 && def->varArgs)) // variadic macro with at least as many
2338  // params as the non-variadic part (see bug731985)
2339  {
2340  uint k=0;
2341  // substitution of all formal arguments
2342  QCString resExpr;
2343  const QCString d=def->definition.stripWhiteSpace();
2344  //printf("Macro definition: '%s'\n",qPrint(d));
2345  bool inString=FALSE;
2346  while (k<d.length())
2347  {
2348  if (d.at(k)=='@') // maybe a marker, otherwise an escaped @
2349  {
2350  if (d.at(k+1)=='@') // escaped @ => copy it (is unescaped later)
2351  {
2352  k+=2;
2353  resExpr+="@@"; // we unescape these later
2354  }
2355  else if (d.at(k+1)=='-') // no-rescan marker
2356  {
2357  k+=2;
2358  resExpr+="@-";
2359  }
2360  else // argument marker => read the argument number
2361  {
2362  QCString key="@";
2363  bool hash=FALSE;
2364  int l=k-1;
2365  // search for ## backward
2366  if (l>=0 && d.at(l)=='"') l--;
2367  while (l>=0 && d.at(l)==' ') l--;
2368  if (l>0 && d.at(l)=='#' && d.at(l-1)=='#') hash=TRUE;
2369  k++;
2370  // scan the number
2371  while (k<d.length() && d.at(k)>='0' && d.at(k)<='9') key+=d.at(k++);
2372  if (!hash)
2373  {
2374  // search for ## forward
2375  l=k;
2376  if (l<(int)d.length() && d.at(l)=='"') l++;
2377  while (l<(int)d.length() && d.at(l)==' ') l++;
2378  if (l<(int)d.length()-1 && d.at(l)=='#' && d.at(l+1)=='#') hash=TRUE;
2379  }
2380  //printf("request key %s result %s\n",qPrint(key),argTable[key]->data());
2381  auto it = argTable.find(key.str());
2382  if (it!=argTable.end())
2383  {
2384  QCString substArg = it->second.c_str();
2385  //printf("substArg='%s'\n",qPrint(substArg));
2386  // only if no ## operator is before or after the argument
2387  // marker we do macro expansion.
2388  if (!hash)
2389  {
2390  expandExpression(yyscanner,substArg,0,0,level+1);
2391  }
2392  if (inString)
2393  {
2394  //printf("'%s'=stringize('%s')\n",qPrint(stringize(*subst)),subst->data());
2395 
2396  // if the marker is inside a string (because a # was put
2397  // before the macro name) we must escape " and \ characters
2398  resExpr+=stringize(substArg);
2399  }
2400  else
2401  {
2402  if (hash && substArg.isEmpty())
2403  {
2404  resExpr+="@E"; // empty argument will be remove later on
2405  }
2406  else if (state->nospaces)
2407  {
2408  resExpr+=substArg;
2409  }
2410  else
2411  {
2412  resExpr+=" "+substArg+" ";
2413  }
2414  }
2415  }
2416  }
2417  }
2418  else // no marker, just copy
2419  {
2420  if (!inString && d.at(k)=='\"')
2421  {
2422  inString=TRUE; // entering a literal string
2423  }
2424  else if (inString && d.at(k)=='\"' && (d.at(k-1)!='\\' || d.at(k-2)=='\\'))
2425  {
2426  inString=FALSE; // leaving a literal string
2427  }
2428  resExpr+=d.at(k++);
2429  }
2430  }
2431  len=j-pos;
2432  result=resExpr;
2433  //printf("<replaceFunctionMacro(expr='%s',rest='%s',pos=%d,def='%s',result='%s') level=%d return=TRUE\n",qPrint(expr),rest ? qPrint(*rest) : 0,pos,qPrint(def->name),qPrint(result),state->levelGuard.size());
2434  return TRUE;
2435  }
2436  //printf("<replaceFunctionMacro(expr='%s',rest='%s',pos=%d,def='%s',result='%s') level=%d return=FALSE\n",qPrint(expr),rest ? qPrint(*rest) : 0,pos,qPrint(def->name),qPrint(result),state->levelGuard.size());
2437  return FALSE;
2438 }
2439 
2440 
2441 /*! returns the next identifier in string \a expr by starting at position \a p.
2442  * The position of the identifier is returned (or -1 if nothing is found)
2443  * and \a l is its length. Any quoted strings are skipping during the search.
2444  */
2445 static int getNextId(const QCString &expr,int p,int *l)
2446 {
2447  int n;
2448  while (p<(int)expr.length())
2449  {
2450  char c=expr.at(p++);
2451  if (isdigit(c)) // skip number
2452  {
2453  while (p<(int)expr.length() && isId(expr.at(p))) p++;
2454  }
2455  else if (isalpha(c) || c=='_') // read id
2456  {
2457  n=p-1;
2458  while (p<(int)expr.length() && isId(expr.at(p))) p++;
2459  *l=p-n;
2460  return n;
2461  }
2462  else if (c=='"') // skip string
2463  {
2464  char ppc=0,pc=c;
2465  if (p<(int)expr.length()) c=expr.at(p);
2466  while (p<(int)expr.length() && (c!='"' || (pc=='\\' && ppc!='\\')))
2467  // continue as long as no " is found, but ignoring \", but not \\"
2468  {
2469  ppc=pc;
2470  pc=c;
2471  c=expr.at(p);
2472  p++;
2473  }
2474  if (p<(int)expr.length()) ++p; // skip closing quote
2475  }
2476  else if (c=='/') // skip C Comment
2477  {
2478  //printf("Found C comment at p=%d\n",p);
2479  char pc=c;
2480  if (p<(int)expr.length())
2481  {
2482  c=expr.at(p);
2483  if (c=='*') // Start of C comment
2484  {
2485  p++;
2486  while (p<(int)expr.length() && !(pc=='*' && c=='/'))
2487  {
2488  pc=c;
2489  c=expr.at(p++);
2490  }
2491  }
2492  }
2493  //printf("Found end of C comment at p=%d\n",p);
2494  }
2495  }
2496  return -1;
2497 }
2498 
2499 #define MAX_EXPANSION_DEPTH 50
2500 
2501 /*! performs recursive macro expansion on the string \a expr
2502  * starting at position \a pos.
2503  * May read additional characters from the input while re-scanning!
2504  */
2505 static bool expandExpression(yyscan_t yyscanner,QCString &expr,QCString *rest,int pos,int level)
2506 {
2507  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2508  //printf(">expandExpression(expr='%s',rest='%s',pos=%d,level=%d)\n",qPrint(expr),rest ? qPrint(*rest) : "", pos, level);
2509  if (expr.isEmpty())
2510  {
2511  //printf("<expandExpression: empty\n");
2512  return TRUE;
2513  }
2514  if (state->expanded.find(expr.str())!=state->expanded.end() &&
2515  level>MAX_EXPANSION_DEPTH) // check for too deep recursive expansions
2516  {
2517  //printf("<expandExpression: already expanded expr='%s'\n",qPrint(expr));
2518  return FALSE;
2519  }
2520  else
2521  {
2522  state->expanded.insert(expr.str());
2523  }
2524  QCString macroName;
2525  QCString expMacro;
2526  bool definedTest=FALSE;
2527  int i=pos,l,p,len;
2528  int startPos = pos;
2529  int samePosCount=0;
2530  while ((p=getNextId(expr,i,&l))!=-1) // search for an macro name
2531  {
2532  bool replaced=FALSE;
2533  macroName=expr.mid(p,l);
2534  //printf(" p=%d macroName=%s\n",p,qPrint(macroName));
2535  if (p<2 || !(expr.at(p-2)=='@' && expr.at(p-1)=='-')) // no-rescan marker?
2536  {
2537  if (state->expandedDict.find(macroName.str())==state->expandedDict.end()) // expand macro
2538  {
2539  Define *def=isDefined(yyscanner,macroName);
2540  if (macroName=="defined")
2541  {
2542  //printf("found defined inside macro definition '%s'\n",qPrint(expr.right(expr.length()-p)));
2543  definedTest=TRUE;
2544  }
2545  else if (definedTest) // macro name was found after defined
2546  {
2547  if (def) expMacro = " 1 "; else expMacro = " 0 ";
2548  replaced=TRUE;
2549  len=l;
2550  definedTest=FALSE;
2551  }
2552  else if (def && def->nargs==-1) // simple macro
2553  {
2554  // substitute the definition of the macro
2555  //printf("macro '%s'->'%s'\n",qPrint(macroName),qPrint(def->definition));
2556  if (state->nospaces)
2557  {
2558  expMacro=def->definition.stripWhiteSpace();
2559  }
2560  else
2561  {
2562  expMacro=" "+def->definition.stripWhiteSpace()+" ";
2563  }
2564  //expMacro=def->definition.stripWhiteSpace();
2565  replaced=TRUE;
2566  len=l;
2567  //printf("simple macro expansion='%s'->'%s'\n",qPrint(macroName),qPrint(expMacro));
2568  }
2569  else if (def && def->nargs>=0) // function macro
2570  {
2571  //printf(" >>>> call replaceFunctionMacro expr='%s'\n",qPrint(expr));
2572  replaced=replaceFunctionMacro(yyscanner,expr,rest,p+l,len,def,expMacro,level);
2573  //printf(" <<<< call replaceFunctionMacro: replaced=%d\n",replaced);
2574  len+=l;
2575  }
2576  //printf(" macroName='%s' expMacro='%s' replaced=%d\n",qPrint(macroName),qPrint(expMacro),replaced);
2577 
2578  if (replaced) // expand the macro and rescan the expression
2579  {
2580  //printf(" replacing '%s'->'%s'\n",expr.mid(p,qPrint(len)),qPrint(expMacro));
2581  QCString resultExpr=expMacro;
2582  QCString restExpr=expr.right(expr.length()-len-p);
2583  processConcatOperators(resultExpr);
2584  //printf(" macroName=%s restExpr='%s' def->nonRecursive=%d\n",qPrint(macroName),qPrint(restExpr),def->nonRecursive);
2585  bool expanded=false;
2586  if (def && !def->nonRecursive)
2587  {
2588  state->expandedDict.emplace(toStdString(macroName),def);
2589  expanded = expandExpression(yyscanner,resultExpr,&restExpr,0,level+1);
2590  state->expandedDict.erase(toStdString(macroName));
2591  }
2592  else if (def && def->nonRecursive)
2593  {
2594  expanded = true;
2595  }
2596  if (expanded)
2597  {
2598  expr=expr.left(p)+resultExpr+restExpr;
2599  //printf(" new expression: '%s' old i=%d new i=%d\n",qPrint(expr),i,p);
2600  i=p;
2601  }
2602  else
2603  {
2604  expr=expr.left(p)+"@-"+expr.right(expr.length()-p);
2605  i=p+l+2;
2606  }
2607  }
2608  else // move to the next macro name
2609  {
2610  //printf(" moving to the next macro old i=%d new i=%d\n",i,p+l);
2611  i=p+l;
2612  }
2613  }
2614  else // move to the next macro name
2615  {
2616  expr=expr.left(p)+"@-"+expr.right(expr.length()-p);
2617  //printf("macro already expanded, moving to the next macro expr=%s\n",qPrint(expr));
2618  i=p+l+2;
2619  //i=p+l;
2620  }
2621  // check for too many inplace expansions without making progress
2622  if (i==startPos)
2623  {
2624  samePosCount++;
2625  }
2626  else
2627  {
2628  startPos=i;
2629  samePosCount=0;
2630  }
2631  if (samePosCount>MAX_EXPANSION_DEPTH)
2632  {
2633  break;
2634  }
2635  }
2636  else // no re-scan marker found, skip the macro name
2637  {
2638  //printf("skipping marked macro\n");
2639  i=p+l;
2640  }
2641  }
2642  //printf("<expandExpression(expr='%s',rest='%s',pos=%d,level=%d)\n",qPrint(expr),rest ? qPrint(*rest) : "", pos,level);
2643  return TRUE;
2644 }
2645 
2646 /*! @brief Process string or character literal.
2647  *
2648  * \a inputStr should point to the start of a string or character literal.
2649  * the routine will return a pointer to just after the end of the literal
2650  * the character making up the literal will be added to \a result.
2651  */
2652 static const char *processUntilMatchingTerminator(const char *inputStr,QCString &result)
2653 {
2654  if (inputStr==0) return inputStr;
2655  char term = *inputStr; // capture start character of the literal
2656  if (term!='\'' && term!='"') return inputStr; // not a valid literal
2657  char c=term;
2658  // output start character
2659  result+=c;
2660  inputStr++;
2661  while ((c=*inputStr)) // while inside the literal
2662  {
2663  if (c==term) // found end marker of the literal
2664  {
2665  // output end character and stop
2666  result+=c;
2667  inputStr++;
2668  break;
2669  }
2670  else if (c=='\\') // escaped character, process next character
2671  // as well without checking for end marker.
2672  {
2673  result+=c;
2674  inputStr++;
2675  c=*inputStr;
2676  if (c==0) break; // unexpected end of string after escape character
2677  }
2678  result+=c;
2679  inputStr++;
2680  }
2681  return inputStr;
2682 }
2683 
2684 /*! replaces all occurrences of @@@@ in \a s by @@
2685  * and removes all occurrences of @@E.
2686  * All identifiers found are replaced by 0L
2687  */
2688 static QCString removeIdsAndMarkers(const QCString &s)
2689 {
2690  //printf("removeIdsAndMarkers(%s)\n",s);
2691  if (s.isEmpty()) return s;
2692  const char *p=s.data();
2693  char c;
2694  bool inNum=FALSE;
2695  QCString result;
2696  if (p)
2697  {
2698  while ((c=*p))
2699  {
2700  if (c=='@') // replace @@ with @ and remove @E
2701  {
2702  if (*(p+1)=='@')
2703  {
2704  result+=c;
2705  }
2706  else if (*(p+1)=='E')
2707  {
2708  // skip
2709  }
2710  p+=2;
2711  }
2712  else if (isdigit(c)) // number
2713  {
2714  result+=c;
2715  p++;
2716  inNum=TRUE;
2717  }
2718  else if (c=='\'') // quoted character
2719  {
2720  p = processUntilMatchingTerminator(p,result);
2721  }
2722  else if (c=='d' && !inNum) // identifier starting with a 'd'
2723  {
2724  if (qstrncmp(p,"defined ",8)==0 || qstrncmp(p,"defined(",8)==0)
2725  // defined keyword
2726  {
2727  p+=7; // skip defined
2728  }
2729  else
2730  {
2731  result+="0L";
2732  p++;
2733  while ((c=*p) && isId(c)) p++;
2734  }
2735  }
2736  else if ((isalpha(c) || c=='_') && !inNum) // replace identifier with 0L
2737  {
2738  result+="0L";
2739  p++;
2740  while ((c=*p) && isId(c)) p++;
2741  while ((c=*p) && isspace((uchar)c)) p++;
2742  if (*p=='(') // undefined function macro
2743  {
2744  p++;
2745  int count=1;
2746  while ((c=*p++))
2747  {
2748  if (c=='(') count++;
2749  else if (c==')')
2750  {
2751  count--;
2752  if (count==0) break;
2753  }
2754  else if (c=='/')
2755  {
2756  char pc=c;
2757  c=*++p;
2758  if (c=='*') // start of C comment
2759  {
2760  while (*p && !(pc=='*' && c=='/')) // search end of comment
2761  {
2762  pc=c;
2763  c=*++p;
2764  }
2765  p++;
2766  }
2767  }
2768  }
2769  }
2770  }
2771  else if (c=='/') // skip C comments
2772  {
2773  char pc=c;
2774  c=*++p;
2775  if (c=='*') // start of C comment
2776  {
2777  while (*p && !(pc=='*' && c=='/')) // search end of comment
2778  {
2779  pc=c;
2780  c=*++p;
2781  }
2782  p++;
2783  }
2784  else // oops, not comment but division
2785  {
2786  result+=pc;
2787  goto nextChar;
2788  }
2789  }
2790  else
2791  {
2792 nextChar:
2793  result+=c;
2794  char lc=(char)tolower(c);
2795  if (!isId(lc) && lc!='.' /*&& lc!='-' && lc!='+'*/) inNum=FALSE;
2796  p++;
2797  }
2798  }
2799  }
2800  //printf("removeIdsAndMarkers(%s)=%s\n",s,qPrint(result));
2801  return result;
2802 }
2803 
2804 /*! replaces all occurrences of @@ in \a s by @
2805  * \par assumption:
2806  * \a s only contains pairs of @@'s
2807  */
2808 static QCString removeMarkers(const QCString &s)
2809 {
2810  if (s.isEmpty()) return s;
2811  const char *p=s.data();
2812  char c;
2813  QCString result;
2814  if (p)
2815  {
2816  while ((c=*p))
2817  {
2818  switch(c)
2819  {
2820  case '@': // replace @@ with @
2821  {
2822  if (*(p+1)=='@')
2823  {
2824  result+=c;
2825  }
2826  p+=2;
2827  }
2828  break;
2829  case '/': // skip C comments
2830  {
2831  result+=c;
2832  char pc=c;
2833  c=*++p;
2834  if (c=='*') // start of C comment
2835  {
2836  while (*p && !(pc=='*' && c=='/')) // search end of comment
2837  {
2838  if (*p=='@' && *(p+1)=='@')
2839  result+=c,p++;
2840  else
2841  result+=c;
2842  pc=c;
2843  c=*++p;
2844  }
2845  if (*p) result+=c,p++;
2846  }
2847  }
2848  break;
2849  case '"': // skip string literals
2850  case '\'': // skip char literals
2851  p = processUntilMatchingTerminator(p,result);
2852  break;
2853  default:
2854  {
2855  result+=c;
2856  p++;
2857  }
2858  break;
2859  }
2860  }
2861  }
2862  //printf("RemoveMarkers(%s)=%s\n",s,qPrint(result));
2863  return result;
2864 }
2865 
2866 /*! compute the value of the expression in string \a expr.
2867  * If needed the function may read additional characters from the input.
2868  */
2869 
2870 static bool computeExpression(yyscan_t yyscanner,const QCString &expr)
2871 {
2872  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2873  QCString e=expr;
2874  state->expanded.clear();
2875  expandExpression(yyscanner,e,0,0,0);
2876  //printf("after expansion '%s'\n",qPrint(e));
2877  e = removeIdsAndMarkers(e);
2878  if (e.isEmpty()) return FALSE;
2879  //printf("parsing '%s'\n",qPrint(e));
2880  return state->constExpParser.parse(state->yyFileName.data(),state->yyLineNr,e.str());
2881 }
2882 
2883 /*! expands the macro definition in \a name
2884  * If needed the function may read additional characters from the input
2885  */
2886 
2887 static QCString expandMacro(yyscan_t yyscanner,const QCString &name)
2888 {
2889  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2890  QCString n=name;
2891  state->expanded.clear();
2892  expandExpression(yyscanner,n,0,0,0);
2893  n=removeMarkers(n);
2894  //printf("expandMacro '%s'->'%s'\n",qPrint(name),qPrint(n));
2895  return n;
2896 }
2897 
2898 static void addDefine(yyscan_t yyscanner)
2899 {
2900  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2901  Define def;
2902  def.name = state->defName;
2903  def.definition = state->defText.stripWhiteSpace();
2904  def.nargs = state->defArgs;
2905  def.fileName = state->yyFileName;
2906  def.fileDef = state->yyFileDef;
2907  def.lineNr = state->yyLineNr-state->yyMLines;
2908  def.columnNr = state->yyColNr;
2909  def.varArgs = state->defVarArgs;
2910  //printf("newDefine: %s %s file: %s\n",qPrint(def.name),qPrint(def.definition),
2911  // def.fileDef ? qPrint(def.fileDef->name()) : qPrint(def.fileName));
2912  //printf("newDefine: '%s'->'%s'\n",qPrint(def.name),qPrint(def.definition));
2913  if (!def.name.isEmpty() &&
2914  Doxygen::expandAsDefinedSet.find(def.name.str())!=Doxygen::expandAsDefinedSet.end())
2915  {
2916  def.isPredefined=TRUE;
2917  }
2918  auto it = state->localDefines.find(def.name.str());
2919  if (it!=state->localDefines.end()) // redefine
2920  {
2921  state->localDefines.erase(it);
2922  }
2923  state->localDefines.insert(std::make_pair(def.name.str(),def));
2924 }
2925 
2926 static void addMacroDefinition(yyscan_t yyscanner)
2927 {
2928  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2929  if (state->skip) return; // do not add this define as it is inside a
2930  // conditional section (cond command) that is disabled.
2931 
2932  Define define;
2933  define.fileName = state->yyFileName;
2934  define.lineNr = state->yyLineNr - state->yyMLines;
2935  define.columnNr = state->yyColNr;
2936  define.name = state->defName;
2937  define.args = state->defArgsStr;
2938  define.fileDef = state->inputFileDef;
2939 
2940  QCString litText = state->defLitText;
2941  int l=litText.find('\n');
2942  if (l>0 && litText.left(l).stripWhiteSpace()=="\\")
2943  {
2944  // strip first line if it only contains a slash
2945  litText = litText.right(litText.length()-l-1);
2946  }
2947  else if (l>0)
2948  {
2949  // align the items on the first line with the items on the second line
2950  int k=l+1;
2951  const char *p=litText.data()+k;
2952  char c;
2953  while ((c=*p++) && (c==' ' || c=='\t')) k++;
2954  litText=litText.mid(l+1,k-l-1)+litText.stripWhiteSpace();
2955  }
2956  QCString litTextStripped = state->defLitText.stripWhiteSpace();
2957  if (litTextStripped.contains('\n')>=1)
2958  {
2959  define.definition = litText;
2960  }
2961  else
2962  {
2963  define.definition = litTextStripped;
2964  }
2965  {
2966  state->macroDefinitions.push_back(define);
2967  }
2968 }
2969 
2970 static inline void outputChar(yyscan_t yyscanner,char c)
2971 {
2972  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2973  if (state->includeStack.empty() || state->curlyCount>0) state->outputBuf->addChar(c);
2974 }
2975 
2976 static inline void outputArray(yyscan_t yyscanner,const char *a,yy_size_t len)
2977 {
2978  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2979  if (state->includeStack.empty() || state->curlyCount>0) state->outputBuf->addArray(a,static_cast<uint>(len));
2980 }
2981 
2982 static inline void outputString(yyscan_t yyscanner,const QCString &a)
2983 {
2984  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
2985  if (state->includeStack.empty() || state->curlyCount>0) state->outputBuf->addArray(a.data(),a.length());
2986 }
2987 
2988 static inline void outputSpace(yyscan_t yyscanner,char c)
2989 {
2990  if (c=='\t') outputChar(yyscanner,'\t');
2991  else outputChar(yyscanner,' ');
2992 }
2993 
2994 static inline void outputSpaces(yyscan_t yyscanner,char *s)
2995 {
2996  const char *p=s;
2997  char c;
2998  while ((c=*p++))
2999  {
3000  if (c=='\t') outputChar(yyscanner,'\t');
3001  else outputChar(yyscanner,' ');
3002  }
3003 }
3004 
3005 static inline void extraSpacing(yyscan_t yyscanner)
3006 {
3007  struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
3008  if (!yyextra->defContinue) return;
3009  for (int i=0; i< (int)yyleng; i++)
3010  {
3011  if (yytext[i] == '\t')
3012  yyextra->defExtraSpacing+='\t';
3013  else
3014  yyextra->defExtraSpacing+=' ';
3015  }
3016 }
3017 
3018 static QCString determineAbsoluteIncludeName(const QCString &curFile,const QCString &incFileName)
3019 {
3020  bool searchIncludes = Config_getBool(SEARCH_INCLUDES);
3021  QCString absIncFileName = incFileName;
3022  FileInfo fi(curFile.str());
3023  if (fi.exists())
3024  {
3025  QCString absName = QCString(fi.dirPath(TRUE))+"/"+incFileName;
3026  FileInfo fi2(absName.str());
3027  if (fi2.exists())
3028  {
3029  absIncFileName=fi2.absFilePath();
3030  }
3031  else if (searchIncludes) // search in INCLUDE_PATH as well
3032  {
3033  const StringVector &includePath = Config_getList(INCLUDE_PATH);
3034  for (const auto &incPath : includePath)
3035  {
3036  FileInfo fi3(incPath);
3037  if (fi3.exists() && fi3.isDir())
3038  {
3039  absName = QCString(fi3.absFilePath())+"/"+incFileName;
3040  //printf("trying absName=%s\n",qPrint(absName));
3041  FileInfo fi4(absName.str());
3042  if (fi4.exists())
3043  {
3044  absIncFileName=fi4.absFilePath();
3045  break;
3046  }
3047  //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
3048  }
3049  }
3050  }
3051  //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
3052  }
3053  return absIncFileName;
3054 }
3055 
3056 static void readIncludeFile(yyscan_t yyscanner,const QCString &inc)
3057 {
3058  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
3059  uint i=0;
3060 
3061  // find the start of the include file name
3062  while (i<inc.length() &&
3063  (inc.at(i)==' ' || inc.at(i)=='"' || inc.at(i)=='<')
3064  ) i++;
3065  uint s=i;
3066 
3067  // was it a local include?
3068  bool localInclude = s>0 && inc.at(s-1)=='"';
3069 
3070  // find the end of the include file name
3071  while (i<inc.length() && inc.at(i)!='"' && inc.at(i)!='>') i++;
3072 
3073  if (s<inc.length() && i>s) // valid include file name found
3074  {
3075  // extract include path+name
3076  QCString incFileName=inc.mid(s,i-s).stripWhiteSpace();
3077 
3078  QCString dosExt = incFileName.right(4);
3079  if (dosExt==".exe" || dosExt==".dll" || dosExt==".tlb")
3080  {
3081  // skip imported binary files (e.g. M$ type libraries)
3082  return;
3083  }
3084 
3085  QCString oldFileName = state->yyFileName;
3086  FileDef *oldFileDef = state->yyFileDef;
3087  int oldLineNr = state->yyLineNr;
3088  //printf("Searching for '%s'\n",qPrint(incFileName));
3089 
3090  QCString absIncFileName = determineAbsoluteIncludeName(state->yyFileName,incFileName);
3091 
3092  // findFile will overwrite state->yyFileDef if found
3093  FileState *fs;
3094  bool alreadyProcessed = FALSE;
3095  //printf("calling findFile(%s)\n",qPrint(incFileName));
3096  if ((fs=findFile(yyscanner,incFileName,localInclude,alreadyProcessed))) // see if the include file can be found
3097  {
3098  {
3099  std::lock_guard<std::mutex> lock(g_globalDefineMutex);
3100  g_defineManager.addInclude(oldFileName.str(),absIncFileName.str());
3101  }
3102 
3103  //printf("Found include file!\n");
3104  if (Debug::isFlagSet(Debug::Preprocessor))
3105  {
3106  for (i=0;i<state->includeStack.size();i++)
3107  {
3108  Debug::print(Debug::Preprocessor,0," ");
3109  }
3110  Debug::print(Debug::Preprocessor,0,"#include %s: parsing...\n",qPrint(incFileName));
3111  }
3112 
3113  if (state->includeStack.empty() && oldFileDef)
3114  {
3115  PreIncludeInfo *ii = state->includeRelations.find(absIncFileName);
3116  if (ii==0)
3117  {
3118  bool ambig;
3119  FileDef *incFd = findFileDef(Doxygen::inputNameLinkedMap,absIncFileName,ambig);
3120  state->includeRelations.add(
3121  absIncFileName,
3122  oldFileDef,
3123  ambig?nullptr:incFd,
3124  incFileName,
3125  localInclude,
3126  state->isImported
3127  );
3128  }
3129  }
3130 
3131  struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
3132  fs->bufState = YY_CURRENT_BUFFER;
3133  fs->lineNr = oldLineNr;
3134  fs->fileName = oldFileName;
3135  fs->curlyCount = state->curlyCount;
3136  state->curlyCount = 0;
3137  // push the state on the stack
3138  state->includeStack.emplace_back(fs);
3139  // set the scanner to the include file
3140 
3141  // Deal with file changes due to
3142  // #include's within { .. } blocks
3143  QCString lineStr(state->yyFileName.length()+20);
3144  lineStr.sprintf("# 1 \"%s\" 1\n",qPrint(state->yyFileName));
3145  outputString(yyscanner,lineStr);
3146 
3147  DBG_CTX((stderr,"Switching to include file %s\n",qPrint(incFileName)));
3148  state->expectGuard=TRUE;
3149  state->inputBuf = &fs->fileBuf;
3150  state->inputBufPos=0;
3151  yy_switch_to_buffer(yy_create_buffer(0, YY_BUF_SIZE, yyscanner),yyscanner);
3152  }
3153  else
3154  {
3155  if (alreadyProcessed) // if this header was already process we can just copy the stored macros
3156  // in the local context
3157  {
3158  std::lock_guard<std::mutex> lock(g_globalDefineMutex);
3159  g_defineManager.addInclude(state->yyFileName.str(),absIncFileName.str());
3160  g_defineManager.retrieve(absIncFileName.str(),state->contextDefines);
3161  }
3162 
3163  if (state->includeStack.empty() && oldFileDef)
3164  {
3165  PreIncludeInfo *ii = state->includeRelations.find(absIncFileName);
3166  if (ii==0)
3167  {
3168  bool ambig;
3169  FileDef *incFd = findFileDef(Doxygen::inputNameLinkedMap,absIncFileName,ambig);
3170  ii = state->includeRelations.add(absIncFileName,
3171  oldFileDef,
3172  ambig?0:incFd,
3173  incFileName,
3174  localInclude,
3175  state->isImported
3176  );
3177  }
3178  }
3179 
3180  if (Debug::isFlagSet(Debug::Preprocessor))
3181  {
3182  for (i=0;i<state->includeStack.size();i++)
3183  {
3184  Debug::print(Debug::Preprocessor,0," ");
3185  }
3186  if (alreadyProcessed)
3187  {
3188  Debug::print(Debug::Preprocessor,0,"#include %s: already processed! skipping...\n",qPrint(incFileName));
3189  }
3190  else
3191  {
3192  Debug::print(Debug::Preprocessor,0,"#include %s: not found! skipping...\n",qPrint(incFileName));
3193  }
3194  //printf("error: include file %s not found\n",yytext);
3195  }
3196  if (state->curlyCount>0 && !alreadyProcessed) // failed to find #include inside { ... }
3197  {
3198  warn(state->yyFileName,state->yyLineNr,"include file %s not found, perhaps you forgot to add its directory to INCLUDE_PATH?",qPrint(incFileName));
3199  }
3200  }
3201  }
3202 }
3203 
3204 /* ----------------------------------------------------------------- */
3205 
3206 static void startCondSection(yyscan_t yyscanner,const QCString &sectId)
3207 {
3208  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
3209  //printf("startCondSection: skip=%d stack=%d\n",state->skip,state->condStack.size());
3210  CondParser prs;
3211  bool expResult = prs.parse(state->yyFileName.data(),state->yyLineNr,sectId.data());
3212  state->condStack.emplace(std::make_unique<preYY_CondCtx>(state->yyLineNr,sectId,state->skip));
3213  if (!expResult)
3214  {
3215  state->skip=TRUE;
3216  }
3217  //printf(" expResult=%d skip=%d\n",expResult,state->skip);
3218 }
3219 
3220 static void endCondSection(yyscan_t yyscanner)
3221 {
3222  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
3223  if (state->condStack.empty())
3224  {
3225  state->skip=FALSE;
3226  }
3227  else
3228  {
3229  const std::unique_ptr<preYY_CondCtx> &ctx = state->condStack.top();
3230  state->skip=ctx->skip;
3231  state->condStack.pop();
3232  }
3233  //printf("endCondSection: skip=%d stack=%d\n",state->skip,state->condStack.count());
3234 }
3235 
3236 static void forceEndCondSection(yyscan_t yyscanner)
3237 {
3238  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
3239  while (!state->condStack.empty())
3240  {
3241  state->condStack.pop();
3242  }
3243  state->skip=FALSE;
3244 }
3245 
3246 static QCString escapeAt(const QCString &text)
3247 {
3248  QCString result;
3249  if (!text.isEmpty())
3250  {
3251  char c;
3252  const char *p=text.data();
3253  while ((c=*p++))
3254  {
3255  if (c=='@') result+="@@"; else result+=c;
3256  }
3257  }
3258  return result;
3259 }
3260 
3261 static char resolveTrigraph(char c)
3262 {
3263  switch (c)
3264  {
3265  case '=': return '#';
3266  case '/': return '\\';
3267  case '\'': return '^';
3268  case '(': return '[';
3269  case ')': return ']';
3270  case '!': return '|';
3271  case '<': return '{';
3272  case '>': return '}';
3273  case '-': return '~';
3274  }
3275  return '?';
3276 }
3277 
3278 /*@ ----------------------------------------------------------------------------
3279  */
3280 
3281 static int getNextChar(yyscan_t yyscanner,const QCString &expr,QCString *rest,uint &pos)
3282 {
3283  //printf("getNextChar(%s,%s,%d)\n",qPrint(expr),rest ? rest->data() : 0,pos);
3284  if (pos<expr.length())
3285  {
3286  //printf("%c=expr()\n",expr.at(pos));
3287  return expr.at(pos++);
3288  }
3289  else if (rest && !rest->isEmpty())
3290  {
3291  int cc=rest->at(0);
3292  *rest=rest->right(rest->length()-1);
3293  //printf("%c=rest\n",cc);
3294  return cc;
3295  }
3296  else
3297  {
3298  int cc=yyinput(yyscanner);
3299  //printf("%d=yyinput() %d\n",cc,EOF);
3300  return cc;
3301  }
3302 }
3303 
3304 static int getCurrentChar(yyscan_t yyscanner,const QCString &expr,QCString *rest,uint pos)
3305 {
3306  //printf("getCurrentChar(%s,%s,%d)\n",qPrint(expr),rest ? rest->data() : 0,pos);
3307  if (pos<expr.length())
3308  {
3309  //printf("%c=expr()\n",expr.at(pos));
3310  return expr.at(pos);
3311  }
3312  else if (rest && !rest->isEmpty())
3313  {
3314  int cc=rest->at(0);
3315  //printf("%c=rest\n",cc);
3316  return cc;
3317  }
3318  else
3319  {
3320  int cc=yyinput(yyscanner);
3321  returnCharToStream(yyscanner,(char)cc);
3322  //printf("%c=yyinput()\n",cc);
3323  return cc;
3324  }
3325 }
3326 
3327 static void unputChar(yyscan_t yyscanner,const QCString &expr,QCString *rest,uint &pos,char c)
3328 {
3329  //printf("unputChar(%s,%s,%d,%c)\n",qPrint(expr),rest ? rest->data() : 0,pos,c);
3330  if (pos<expr.length())
3331  {
3332  pos++;
3333  }
3334  else if (rest)
3335  {
3336  //printf("Prepending to rest!\n");
3337  char cs[2];cs[0]=c;cs[1]='\0';
3338  rest->prepend(cs);
3339  }
3340  else
3341  {
3342  //unput(c);
3343  returnCharToStream(yyscanner,c);
3344  }
3345  //printf("result: unputChar(%s,%s,%d,%c)\n",qPrint(expr),rest ? rest->data() : 0,pos,c);
3346 }
3347 
3348 /** Returns a reference to a Define object given its name or 0 if the Define does
3349  * not exist.
3350  */
3351 static Define *isDefined(yyscan_t yyscanner,const QCString &name)
3352 {
3353  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
3354 
3355  bool undef = false;
3356  auto findDefine = [&undef,&name](DefineMap &map)
3357  {
3358  Define *d=0;
3359  auto it = map.find(name.str());
3360  if (it!=map.end())
3361  {
3362  d = &it->second;
3363  if (d->undef)
3364  {
3365  undef=true;
3366  d=0;
3367  }
3368  }
3369  return d;
3370  };
3371 
3372  Define *def = findDefine(state->localDefines);
3373  if (def==0 && !undef)
3374  {
3375  def = findDefine(state->contextDefines);
3376  }
3377  return def;
3378 }
3379 
3380 static void initPredefined(yyscan_t yyscanner,const QCString &fileName)
3381 {
3382  YY_EXTRA_TYPE state = preYYget_extra(yyscanner);
3383 
3384  // add predefined macros
3385  const StringVector &predefList = Config_getList(PREDEFINED);
3386  for (const auto &ds : predefList)
3387  {
3388  size_t i_equals=ds.find('=');
3389  size_t i_obrace=ds.find('(');
3390  size_t i_cbrace=ds.find(')');
3391  bool nonRecursive = i_equals!=std::string::npos && i_equals>0 && ds[i_equals-1]==':';
3392 
3393  if ((i_obrace==0) || (i_equals==0) || (i_equals==1 && ds[i_equals-1]==':'))
3394  {
3395  continue; // no define name
3396  }
3397 
3398  if (i_obrace<i_equals && i_cbrace<i_equals &&
3399  i_obrace!=std::string::npos && i_cbrace!=std::string::npos &&
3400  i_obrace<i_cbrace
3401  ) // predefined function macro definition
3402  {
3403  static const reg::Ex reId(R"(\a\w*)");
3404  std::map<std::string,int> argMap;
3405  std::string args = ds.substr(i_obrace+1,i_cbrace-i_obrace-1); // part between ( and )
3406  bool hasVarArgs = args.find("...")!=std::string::npos;
3407  //printf("predefined function macro '%s'\n",ds.c_str());
3408  int count = 0;
3409  reg::Iterator arg_it(args,reId,0);
3410  reg::Iterator arg_end;
3411  // gather the formal arguments in a dictionary
3412  for (; arg_it!=arg_end; ++arg_it)
3413  {
3414  argMap.emplace(arg_it->str(),count++);
3415  }
3416  if (hasVarArgs) // add the variable argument if present
3417  {
3418  argMap.emplace("__VA_ARGS__",count++);
3419  }
3420 
3421  // strip definition part
3422  std::string definition;
3423  std::string in=ds.substr(i_equals+1);
3424  reg::Iterator re_it(in,reId);
3425  reg::Iterator re_end;
3426  size_t i=0;
3427  // substitute all occurrences of formal arguments by their
3428  // corresponding markers
3429  for (; re_it!=re_end; ++re_it)
3430  {
3431  const auto &match = *re_it;
3432  size_t pi = match.position();
3433  size_t l = match.length();
3434  if (pi>i) definition+=in.substr(i,pi-i);
3435 
3436  auto it = argMap.find(match.str());
3437  if (it!=argMap.end())
3438  {
3439  int argIndex = it->second;
3440  QCString marker;
3441  marker.sprintf(" @%d ",argIndex);
3442  definition+=marker.str();
3443  }
3444  else
3445  {
3446  definition+=match.str();
3447  }
3448  i=pi+l;
3449  }
3450  definition+=in.substr(i);
3451 
3452  // add define definition to the dictionary of defines for this file
3453  std::string dname = ds.substr(0,i_obrace);
3454  if (!dname.empty())
3455  {
3456  Define def;
3457  def.name = dname;
3458  def.definition = definition;
3459  def.nargs = count;
3460  def.isPredefined = TRUE;
3461  def.nonRecursive = nonRecursive;
3462  def.fileDef = state->yyFileDef;
3463  def.fileName = fileName;
3464  def.varArgs = hasVarArgs;
3465  state->contextDefines.insert(std::make_pair(def.name.str(),def));
3466 
3467  //printf("#define '%s' '%s' #nargs=%d hasVarArgs=%d\n",
3468  // qPrint(def.name),qPrint(def.definition),def.nargs,def.varArgs);
3469  }
3470  }
3471  else if (!ds.empty()) // predefined non-function macro definition
3472  {
3473  //printf("predefined normal macro '%s'\n",ds.c_str());
3474  Define def;
3475  if (i_equals==std::string::npos) // simple define without argument
3476  {
3477  def.name = ds;
3478  def.definition = "1"; // substitute occurrences by 1 (true)
3479  }
3480  else // simple define with argument
3481  {
3482  int ine=static_cast<int>(i_equals) - (nonRecursive ? 1 : 0);
3483  def.name = ds.substr(0,ine);
3484  def.definition = ds.substr(i_equals+1);
3485  }
3486  if (!def.name.isEmpty())
3487  {
3488  def.nargs = -1;
3489  def.isPredefined = TRUE;
3490  def.nonRecursive = nonRecursive;
3491  def.fileDef = state->yyFileDef;
3492  def.fileName = fileName;
3493  state->contextDefines.insert(std::make_pair(def.name.str(),def));
3494  }
3495  }
3496  }
3497 }
3498 
3499 ///////////////////////////////////////////////////////////////////////////////////////////////
3500 
3501 struct Preprocessor::Private
3502 {
3503  yyscan_t yyscanner;
3504  preYY_state state;
3505 };
3506 
3507 void Preprocessor::addSearchDir(const QCString &dir)
3508 {
3509  YY_EXTRA_TYPE state = preYYget_extra(p->yyscanner);
3510  FileInfo fi(dir.str());
3511  if (fi.isDir()) state->pathList.push_back(fi.absFilePath());
3512 }
3513 
3514 Preprocessor::Preprocessor() : p(std::make_unique<Private>())
3515 {
3516  preYYlex_init_extra(&p->state,&p->yyscanner);
3517  addSearchDir(".");
3518 }
3519 
3520 Preprocessor::~Preprocessor()
3521 {
3522  preYYlex_destroy(p->yyscanner);
3523 }
3524 
3525 void Preprocessor::processFile(const QCString &fileName,BufStr &input,BufStr &output)
3526 {
3527 // printf("Preprocessor::processFile(%s)\n",fileName);
3528  yyscan_t yyscanner = p->yyscanner;
3529  YY_EXTRA_TYPE state = preYYget_extra(p->yyscanner);
3530  struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
3531 
3532 #ifdef FLEX_DEBUG
3533  preYYset_debug(1,yyscanner);
3534 #endif
3535 
3536  printlex(yy_flex_debug, TRUE, __FILE__, qPrint(fileName));
3537  uint orgOffset=output.curPos();
3538  //printf("##########################\n%s\n####################\n",
3539  // qPrint(input));
3540 
3541  state->macroExpansion = Config_getBool(MACRO_EXPANSION);
3542  state->expandOnlyPredef = Config_getBool(EXPAND_ONLY_PREDEF);
3543  state->skip=FALSE;
3544  state->curlyCount=0;
3545  state->nospaces=FALSE;
3546  state->inputBuf=&input;
3547  state->inputBufPos=0;
3548  state->outputBuf=&output;
3549  state->includeStack.clear();
3550  state->expandedDict.clear();
3551  state->contextDefines.clear();
3552  while (!state->condStack.empty()) state->condStack.pop();
3553 
3554  setFileName(yyscanner,fileName);
3555 
3556  state->inputFileDef = state->yyFileDef;
3557  //yyextra->defineManager.startContext(state->yyFileName);
3558 
3559  initPredefined(yyscanner,fileName);
3560 
3561  state->yyLineNr = 1;
3562  state->yyColNr = 1;
3563  state->ifcount = 0;
3564 
3565  BEGIN( Start );
3566 
3567  state->expectGuard = guessSection(fileName)==Entry::HEADER_SEC;
3568  state->guardName.resize(0);
3569  state->lastGuardName.resize(0);
3570  state->guardExpr.resize(0);
3571 
3572  preYYlex(yyscanner);
3573 
3574  while (!state->condStack.empty())
3575  {
3576  const std::unique_ptr<preYY_CondCtx> &ctx = state->condStack.top();
3577  QCString sectionInfo = " ";
3578  if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label '%s' ",qPrint(ctx->sectionId.stripWhiteSpace()));
3579  warn(fileName,ctx->lineNr,"Conditional section%sdoes not have "
3580  "a corresponding \\endcond command within this file.",qPrint(sectionInfo));
3581  state->condStack.pop();
3582  }
3583  // make sure we don't extend a \cond with missing \endcond over multiple files (see bug 624829)
3584  forceEndCondSection(yyscanner);
3585 
3586  if (Debug::isFlagSet(Debug::Preprocessor))
3587  {
3588  std::lock_guard<std::mutex> lock(g_debugMutex);
3589  char *orgPos=output.data()+orgOffset;
3590  char *newPos=output.data()+output.curPos();
3591  Debug::print(Debug::Preprocessor,0,"Preprocessor output of %s (size: %d bytes):\n",qPrint(fileName),newPos-orgPos);
3592  int line=1;
3593  Debug::print(Debug::Preprocessor,0,"---------\n");
3594  if (!Debug::isFlagSet(Debug::NoLineNo)) Debug::print(Debug::Preprocessor,0,"00001 ");
3595  while (orgPos<newPos)
3596  {
3597  putchar(*orgPos);
3598  if (*orgPos=='\n' && !Debug::isFlagSet(Debug::NoLineNo)) Debug::print(Debug::Preprocessor,0,"%05d ",++line);
3599  orgPos++;
3600  }
3601  Debug::print(Debug::Preprocessor,0,"\n---------\n");
3602  if (yyextra->contextDefines.size()>0)
3603  {
3604  Debug::print(Debug::Preprocessor,0,"Macros accessible in this file (%s):\n", qPrint(fileName));
3605  Debug::print(Debug::Preprocessor,0,"---------\n");
3606  for (auto &kv : yyextra->contextDefines)
3607  {
3608  Debug::print(Debug::Preprocessor,0,"%s ",qPrint(kv.second.name));
3609  }
3610  for (auto &kv : yyextra->localDefines)
3611  {
3612  Debug::print(Debug::Preprocessor,0,"%s ",qPrint(kv.second.name));
3613  }
3614  Debug::print(Debug::Preprocessor,0,"\n---------\n");
3615  }
3616  else
3617  {
3618  Debug::print(Debug::Preprocessor,0,"No macros accessible in this file (%s).\n", qPrint(fileName));
3619  }
3620  }
3621 
3622  {
3623  std::lock_guard<std::mutex> lock(g_updateGlobals);
3624  for (const auto &inc : state->includeRelations)
3625  {
3626  if (inc->fromFileDef)
3627  {
3628  inc->fromFileDef->addIncludeDependency(inc->toFileDef,inc->includeName,inc->local,inc->imported);
3629  }
3630  if (inc->toFileDef && inc->fromFileDef)
3631  {
3632  inc->toFileDef->addIncludedByDependency(inc->fromFileDef,inc->fromFileDef->docName(),inc->local,inc->imported);
3633  }
3634  }
3635  // add the macro definition for this file to the global map
3636  Doxygen::macroDefinitions.emplace(std::make_pair(state->yyFileName.str(),std::move(state->macroDefinitions)));
3637  }
3638 
3639  //yyextra->defineManager.endContext();
3640  printlex(yy_flex_debug, FALSE, __FILE__, qPrint(fileName));
3641 // printf("Preprocessor::processFile(%s) finished\n",fileName);
3642 }
3643 
3644 #if USE_STATE2STRING
3645 #include "pre.l.h"
3646 #endif