Fix for UBSan build
[platform/upstream/doxygen.git] / addon / doxywizard / config.l
1 /******************************************************************************
2  *
3  * $Id: config_templ.l,v 1.8 2001/01/01 10:15:16 root Exp $
4  *
5  * Copyright (C) 1997-2012 by Dimitri van Heesch.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation under the terms of the GNU General Public License is hereby 
9  * granted. No representations are made about the suitability of this software 
10  * for any purpose. It is provided "as is" without express or implied warranty.
11  * See the GNU General Public License for more details.
12  *
13  */
14
15 %{
16
17 /*
18  *      includes
19  */
20 #include "config.h"
21 #include "input.h"
22 #include <QtCore>
23
24 #define MAX_INCLUDE_DEPTH 10
25
26
27 /* -----------------------------------------------------------------
28  *
29  *      static variables
30  */
31
32 struct ConfigFileState
33 {
34   int lineNr;
35   FILE *file;
36   YY_BUFFER_STATE oldState;
37   YY_BUFFER_STATE newState;
38   QString fileName;
39 };  
40
41 static const QHash<QString,Input*>   *g_options;
42 static FILE                          *g_file;
43 static QString                        g_yyFileName;
44 static QString                        g_includeName;
45 static QVariant                       g_includePathList;
46 static QStack<ConfigFileState*>       g_includeStack;  
47 static int                            g_includeDepth;
48 static QVariant                      *g_arg;
49 static Input                         *g_curOption=0;
50 static QString                        g_elemStr;
51 static QTextCodec                    *g_codec     = QTextCodec::codecForName("UTF-8");
52 static QString                        g_codecName = QString::fromAscii("UTF-8");
53 static int                            g_lastState;
54 static QByteArray                     g_tmpString;
55
56 /* -----------------------------------------------------------------
57  */
58 #undef  YY_INPUT
59 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
60
61 static int yyread(char *buf,int maxSize)
62 {
63     // no file included
64     if (g_includeStack.isEmpty()) 
65     {
66       return fread(buf,1,maxSize,g_file);
67     } 
68     else 
69     {
70       return fread(buf,1,maxSize,g_includeStack.top()->file);
71     }
72 }
73
74 void config_err(const char *fmt, ...)
75 {
76   va_list args;
77   va_start(args, fmt);
78   vfprintf(stderr, fmt, args);
79   va_end(args); 
80 }
81 void config_warn(const char *fmt, ...)
82 {
83   va_list args;
84   va_start(args, fmt);
85   vfprintf(stderr, fmt, args);
86   va_end(args);
87 }
88
89 static void substEnvVarsInStrList(QStringList &sl);
90 static void substEnvVarsInString(QString &s);
91
92 static void checkEncoding()
93 {
94   Input *option = g_options->value(QString::fromAscii("DOXYFILE_ENCODING"));
95   if (option && option->value().toString()!=g_codecName)
96   {
97     QTextCodec *newCodec = QTextCodec::codecForName(option->value().toString().toAscii());
98     if (newCodec)
99     {
100       g_codec = newCodec;
101       g_codecName = option->value().toString();
102     }
103   }
104 }
105
106 static FILE *tryPath(const QString &path,const QString &fileName)
107 {
108   QString absName=!path.isEmpty() ? path+QString::fromAscii("/")+fileName : fileName;
109   QFileInfo fi(absName);
110   if (fi.exists() && fi.isFile())
111   {
112     FILE *f = fopen(absName.toLocal8Bit(),"r");
113     if (f==NULL)
114       config_err("Error: could not open file %s for reading\n",absName.toLatin1().data());
115     else 
116       return f;
117   }
118   return NULL;
119 }
120
121 static FILE *findFile(const QString &fileName)
122 {
123   if (QFileInfo(fileName).isAbsolute()) // absolute path
124   {
125     return tryPath(QString(), fileName);
126   }
127
128   // relative path, try with include paths in the list
129   QStringList sl = g_includePathList.toStringList();
130   substEnvVarsInStrList(sl);
131   foreach (QString s, sl) 
132   {
133     FILE *f = tryPath(s,fileName);
134     if (f) return f;
135   }
136   // try cwd if g_includePathList fails
137   return tryPath(QString::fromAscii("."),fileName);
138 }
139
140 static void readIncludeFile(const QString &incName)
141 {
142   if (g_includeDepth==MAX_INCLUDE_DEPTH) 
143   {
144     config_err("Error: maximum include depth (%d) reached, %s is not included. Aborting...\n",
145         MAX_INCLUDE_DEPTH,qPrintable(incName));
146     exit(1);
147   } 
148
149   QString inc = incName;
150   substEnvVarsInString(inc);
151   inc = inc.trimmed();
152   uint incLen = inc.length();
153   if (inc.at(0)==QChar::fromAscii('"') && 
154       inc.at(incLen-1)==QChar::fromAscii('"')) // strip quotes
155   {
156     inc=inc.mid(1,incLen-2);
157   }
158
159   FILE *f = findFile(inc);
160   if (f) // see if the include file can be found
161   {
162     // For debugging
163 #if SHOW_INCLUDES
164     for (i=0;i<includeStack.count();i++) msg("  ");
165     msg("@INCLUDE = %s: parsing...\n",inc.toLatin1().data());
166 #endif
167
168     // store the state of the old file 
169     ConfigFileState *fs=new ConfigFileState;
170     fs->oldState=YY_CURRENT_BUFFER;
171     fs->fileName=g_yyFileName;
172     fs->file=f;
173     // push the state on the stack
174     g_includeStack.push(fs);
175     // set the scanner to the include file
176     yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
177     fs->newState=YY_CURRENT_BUFFER;
178     g_yyFileName=inc;
179     g_includeDepth++;
180   } 
181   else
182   {
183     config_err("Error: @INCLUDE = %s: not found!\n",inc.toLatin1().data());
184     exit(1);
185   }
186 }
187
188
189 %}
190
191 %option nounput
192 %option noyywrap
193 %option yylineno
194
195 %x      Start
196 %x      SkipComment
197 %x      SkipInvalid
198 %x      GetString
199 %x      GetStrList
200 %x      GetQuotedString
201 %x      GetEnvVar
202 %x      Include
203
204 %%
205
206 <*>\0x0d
207 <Start,GetString,GetStrList,SkipInvalid>"#"      { BEGIN(SkipComment); }
208 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"="   { QString cmd = g_codec->toUnicode(yytext);
209                                            cmd=cmd.left(cmd.length()-1).trimmed(); 
210                                            g_curOption = g_options->value(cmd);
211                                            if (g_curOption==0) // oops not known
212                                            {
213                                              config_err("Warning: ignoring unsupported tag `%s' at line %d, file %s\n",
214                                                  qPrintable(cmd),yylineno,qPrintable(g_yyFileName)); 
215                                              BEGIN(SkipInvalid);
216                                            }
217                                            else // known tag
218                                            {
219                                              //option->setEncoding(encoding);
220                                              g_arg = &g_curOption->value();
221                                              switch(g_curOption->kind())
222                                              {
223                                                case Input::StrList:
224                                                  g_elemStr = QString();
225                                                  *g_arg = QStringList();
226                                                  BEGIN(GetStrList);
227                                                  break;
228                                                case Input::String:
229                                                  BEGIN(GetString);
230                                                  break;
231                                                case Input::Int:
232                                                  BEGIN(GetString);
233                                                  break;
234                                                case Input::Bool:
235                                                  BEGIN(GetString);
236                                                  break;
237                                                case Input::Obsolete:
238                                                  config_err("Warning: Tag `%s' at line %d of file %s has become obsolete.\n"
239                                                             "To avoid this warning please update your configuration "
240                                                             "file using \"doxygen -u\"\n", qPrintable(cmd),
241                                                             yylineno,qPrintable(g_yyFileName)); 
242                                                  BEGIN(SkipInvalid);
243                                                  break;
244                                              }
245                                            }
246                                         }
247 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { QString cmd=g_codec->toUnicode(yytext);
248                                           cmd=cmd.left(cmd.length()-2).trimmed(); 
249                                           g_curOption = g_options->value(cmd);
250                                           if (g_curOption==0) // oops not known
251                                           {
252                                             config_err("Warning: ignoring unsupported tag `%s' at line %d, file %s\n",
253                                                 yytext,yylineno,qPrintable(g_yyFileName)); 
254                                             BEGIN(SkipInvalid);
255                                           }
256                                           else // known tag
257                                           {
258                                             switch(g_curOption->kind())
259                                             {
260                                               case Input::StrList:
261                                                 g_arg = &g_curOption->value();
262                                                 g_elemStr=QString();
263                                                 BEGIN(GetStrList);
264                                                 break;
265                                               case Input::String:
266                                               case Input::Int:
267                                               case Input::Bool:
268                                                 config_err("Warning: operator += not supported for `%s'. Ignoring line at line %d, file %s\n",
269                                                     yytext,yylineno,qPrintable(g_yyFileName)); 
270                                                 BEGIN(SkipInvalid);
271                                                 break;
272                                               case Input::Obsolete:
273                                                  config_err("Warning: Tag `%s' at line %d of file %s has become obsolete.\n"
274                                                             "To avoid this warning please update your configuration "
275                                                             "file using \"doxygen -u\"\n", 
276                                                             qPrintable(cmd),yylineno,qPrintable(g_yyFileName)); 
277                                                  BEGIN(SkipInvalid);
278                                                  break;
279                                              }
280                                            }
281                                         }
282 <Start>"@INCLUDE_PATH"[ \t]*"="         { BEGIN(GetStrList); g_arg=&g_includePathList; *g_arg = QStringList(); g_elemStr=QString(); }
283   /* include a config file */
284 <Start>"@INCLUDE"[ \t]*"="              { BEGIN(Include);}
285 <Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") { 
286                                           readIncludeFile(g_codec->toUnicode(yytext)); 
287                                           BEGIN(Start);
288                                         }
289 <<EOF>>                                 {
290                                           //printf("End of include file\n");
291                                           //printf("Include stack depth=%d\n",g_includeStack.count());
292                                           if (g_includeStack.isEmpty())
293                                           {
294                                             //printf("Terminating scanner!\n");
295                                             yyterminate();
296                                           }
297                                           else
298                                           {
299                                             ConfigFileState *fs = g_includeStack.pop();
300                                             fclose(fs->file);
301                                             YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
302                                             yy_switch_to_buffer( fs->oldState );
303                                             yy_delete_buffer( oldBuf );
304                                             g_yyFileName=fs->fileName;
305                                             delete fs; 
306                                             g_includeDepth--;
307                                           }
308                                         }
309
310 <Start>[a-z_A-Z0-9]+                    { config_err("Warning: ignoring unknown tag `%s' at line %d, file %s\n",yytext,yylineno,qPrintable(g_yyFileName)); }
311 <GetString,SkipInvalid>\n               { BEGIN(Start); }
312 <GetStrList>\n                          { 
313                                           if (!g_elemStr.isEmpty())
314                                           {
315                                             //printf("elemStr1=`%s'\n",elemStr.toLatin1().data());
316                                             *g_arg = QVariant(g_arg->toStringList() << g_elemStr);
317                                           }
318                                           BEGIN(Start); 
319                                         }
320 <GetStrList>[ \t]+                      {
321                                           if (!g_elemStr.isEmpty())
322                                           {
323                                             //printf("elemStr2=`%s'\n",elemStr.toLatin1().data());
324                                             *g_arg = QVariant(g_arg->toStringList() << g_elemStr);
325                                           }
326                                           g_elemStr = QString();
327                                         }
328 <GetString>[^ \"\t\r\n]+                { 
329                                           *g_arg = QVariant(g_codec->toUnicode(yytext)); 
330                                           checkEncoding();
331                                         }
332 <GetString,GetStrList,SkipInvalid>"\""  { g_lastState=YY_START;
333                                           BEGIN(GetQuotedString); 
334                                           g_tmpString="";
335                                         }
336 <GetQuotedString>"\""|"\n"              { 
337                                           // we add a bogus space to signal that the string was quoted. This space will be stripped later on.
338                                           g_tmpString+=" ";
339                                           //printf("Quoted String = `%s'\n",tmpString.toLatin1().data());
340                                           if (g_lastState==GetString)
341                                           {
342                                             *g_arg = g_codec->toUnicode(g_tmpString);
343                                             checkEncoding();
344                                           }
345                                           else
346                                           {
347                                             g_elemStr+=g_codec->toUnicode(g_tmpString);
348                                           }
349                                           if (*yytext=='\n')
350                                           {
351                                             config_err("Warning: Missing end quote (\") on line %d, file %s\n",yylineno,
352                                                 qPrintable(g_yyFileName));
353                                           }
354                                           BEGIN(g_lastState);
355                                         }
356 <GetQuotedString>"\\\""                 {
357                                           g_tmpString+='"';
358                                         }
359 <GetQuotedString>.                      { g_tmpString+=*yytext; }
360 <GetStrList>[^ \#\"\t\r\n]+             {
361                                           g_elemStr+=g_codec->toUnicode(yytext);
362                                         }
363 <SkipComment>\n                         { BEGIN(Start); }
364 <SkipComment>\\[ \r\t]*\n               { BEGIN(Start); }
365 <*>\\[ \r\t]*\n                         { }
366 <*>\n
367 <*>.                                    
368
369 %%
370
371 /*@ ----------------------------------------------------------------------------
372  */
373
374 static void substEnvVarsInString(QString &s)
375 {
376   static QRegExp re(QString::fromAscii("\\$\\([a-z_A-Z0-9]+\\)"));
377   if (s.isEmpty()) return;
378   int p=0;
379   int i,l;
380   //printf("substEnvVarInString(%s) start\n",s.toLatin1().data());
381   while ((i=re.indexIn(s,p))!=-1)
382   {
383     l = re.matchedLength();
384     //printf("Found environment var s.mid(%d,%d)=`%s'\n",i+2,l-3,s.mid(i+2,l-3).toLatin1().data());
385     QString env=g_codec->toUnicode(getenv(s.mid(i+2,l-3).toLatin1()));
386     substEnvVarsInString(env); // recursively expand variables if needed.
387     s = s.left(i)+env+s.right(s.length()-i-l);
388     p=i+env.length(); // next time start at the end of the expanded string
389   }
390   s=s.trimmed(); // to strip the bogus space that was added when an argument
391                          // has quotes
392   //printf("substEnvVarInString(%s) end\n",s.toLatin1().data());
393 }
394
395 static void substEnvVarsInStrList(QStringList &sl)
396 {
397   QStringList out;
398
399   foreach (QString result, sl)
400   {
401     // an argument with quotes will have an extra space at the end, so wasQuoted will be TRUE.
402     bool wasQuoted = (result.indexOf(QChar::fromAscii(' '))!=-1) || 
403                      (result.indexOf(QChar::fromAscii('\t'))!=-1);
404     // here we strip the quote again
405     substEnvVarsInString(result);
406
407     //printf("Result %s was quoted=%d\n",result.toLatin1().data(),wasQuoted);
408
409     if (!wasQuoted) /* as a result of the expansion, a single string
410                        may have expanded into a list, which we'll
411                        add to sl. If the orginal string already 
412                        contained multiple elements no further 
413                        splitting is done to allow quoted items with spaces! */
414     {
415       int l=result.length();
416       int i,p=0;
417       // skip spaces
418       // search for a "word"
419       for (i=0;i<l;i++)
420       {
421         QChar c=0;
422         // skip until start of new word
423         while (i<l && ((c=result.at(i))==QChar::fromAscii(' ') || c==QChar::fromAscii('\t'))) i++; 
424         p=i; // p marks the start index of the word
425         // skip until end of a word
426         while (i<l && ((c=result.at(i))!=QChar::fromAscii(' ') && 
427                       c!=QChar::fromAscii('\t') && 
428                       c!=QChar::fromAscii('"'))) i++;
429         if (i<l) // not at the end of the string
430         {
431           if (c==QChar::fromAscii('"')) // word within quotes
432           {
433             p=i+1;
434             for (i++;i<l;i++)
435             {
436               c=result.at(i);
437               if (c==QChar::fromAscii('"')) // end quote
438               {
439                 out += result.mid(p,i-p);
440                 p=i+1;
441                 break; 
442               }
443               else if (c==QChar::fromAscii('\\')) // skip escaped stuff
444               {
445                 i++;
446               }
447             }
448           }
449           else if (c==QChar::fromAscii(' ') || c==QChar::fromAscii('\t')) // separator
450           {
451             out += result.mid(p,i-p);
452             p=i+1;
453           }
454         }
455       }
456       if (p!=l) // add the leftover as a string
457       {
458         out += result.right(l-p);
459       }
460     }
461     else // just goto the next element in the list
462     {
463       out += result;
464     }
465   }
466   sl = out;
467 }
468
469 //--------------------------------------------------------------------------
470
471 bool parseConfig(
472       const QString &fileName,
473       const QHash<QString,Input *> &options
474     )
475 {
476   QHashIterator<QString, Input*> i(options);
477   g_file = fopen(fileName.toLocal8Bit(),"r");
478   if (g_file==NULL) return false;
479
480   // reset all values
481   i.toFront();
482   while (i.hasNext()) 
483   {
484     i.next();
485     if (i.value())
486     {
487       i.value()->reset();
488     }
489   }
490
491   // parse config file
492   g_options       = &options;
493   g_yyFileName    = fileName;
494   g_includeStack.clear();
495   g_includeDepth  = 0;
496   configrestart( configin );
497   BEGIN( Start );
498   configlex();
499
500   // update the values in the UI
501   i.toFront();
502   while (i.hasNext()) 
503   {
504     i.next();
505     if (i.value())
506     {
507       //printf("Updating: %s\n",qPrintable(i.key()));
508       i.value()->update();
509     }
510     else
511     {
512       printf("Invalid option: %s\n",qPrintable(i.key()));
513     }
514   } 
515   fclose(g_file);
516   return true;
517 }
518
519 void writeStringValue(QTextStream &t,QTextCodec *codec,const QString &s)
520 {
521   QChar c;
522   bool needsEscaping=FALSE;
523   // convert the string back to it original encoding
524   //QByteArray se = codec->fromUnicode(s);
525   t.setCodec(codec);
526   const QChar *p=s.data();
527   if (!s.isEmpty() && !p->isNull())
528   {
529     while (!(c=*p++).isNull() && !needsEscaping) 
530     {
531       needsEscaping = (c==QChar::fromAscii(' ')  || 
532                        c==QChar::fromAscii('\n') || 
533                        c==QChar::fromAscii('\t') || 
534                        c==QChar::fromAscii('"'));
535     }
536     if (needsEscaping)
537     { 
538       t << "\"";
539       p=s.data();
540       while (!p->isNull())
541       {
542         if (*p   ==QChar::fromAscii(' ') && 
543            *(p+1)==QChar::fromAscii('\0')) break; // skip inserted space at the end
544         if (*p   ==QChar::fromAscii('"')) t << "\\"; // escape quotes
545         t << *p++;
546       }
547       t << "\"";
548     }
549     else
550     {
551       t << s;
552     }
553   }
554 }
555