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