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