df032a6ce89279fcbf1dedce57f61cc50ffd5f9a
[platform/upstream/doxygen.git] / src / configimpl.l
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2015 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  */
12 %option never-interactive
13 %option prefix="configimplYY"
14
15 %{
16
17 /*
18  *      includes
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <errno.h>
26
27 #include <qfileinfo.h>
28 #include <qdir.h>
29 #include <qtextstream.h>
30 #include <qregexp.h>
31 #include <qstack.h>
32 #include <qglobal.h>
33   
34 #include "configimpl.h"
35 #include "version.h"
36 #include "portable.h"
37 #include "util.h"
38 #include "message.h"
39
40 #include "lang_cfg.h"
41 #include "configoptions.h"
42
43 #define YY_NO_INPUT 1
44 #define YY_NO_UNISTD_H 1
45
46 static const char *warning_str = "warning: ";
47 static const char *error_str = "error: ";
48
49 void config_err(const char *fmt, ...)
50 {
51   va_list args;
52   va_start(args, fmt);
53   vfprintf(stderr, (QCString(error_str) + fmt).data(), args);
54   va_end(args); 
55 }
56 void config_warn(const char *fmt, ...)
57 {
58   va_list args;
59   va_start(args, fmt);
60   vfprintf(stderr, (QCString(warning_str) + fmt).data(), args);
61   va_end(args);
62 }
63
64 static QCString configStringRecode(
65     const QCString &str,
66     const char *fromEncoding,
67     const char *toEncoding);
68
69 #define MAX_INCLUDE_DEPTH 10
70 #define YY_NEVER_INTERACTIVE 1
71
72 /* -----------------------------------------------------------------
73  */
74 static QCString convertToComment(const QCString &s, const QCString &u)
75 {
76   //printf("convertToComment(%s)=%s\n",s.data(),u.data());
77   QCString result;
78   if (!s.isEmpty())
79   {
80     QCString tmp=s.stripWhiteSpace();
81     const char *p=tmp.data();
82     char c;
83     result+="#";
84     if (*p && *p!='\n')
85       result+=" ";
86     while ((c=*p++))
87     {
88       if (c=='\n')
89       {
90         result+="\n#";
91         if (*p && *p!='\n')
92           result+=" ";
93       }
94       else result+=c;
95     }
96     result+='\n';
97   }
98   if (!u.isEmpty())
99   {
100     if (!result.isEmpty()) result+='\n';
101     result+= u;
102   }
103   return result;
104 }
105
106 void ConfigOption::writeBoolValue(FTextStream &t,bool v)
107 {
108   t << " ";
109   if (v) t << "YES"; else t << "NO";
110 }
111
112 void ConfigOption::writeIntValue(FTextStream &t,int i)
113 {
114   t << " " << i;
115 }
116
117 void ConfigOption::writeStringValue(FTextStream &t,QCString &s)
118 {
119   char c;
120   bool needsEscaping=FALSE;
121   // convert the string back to it original encoding
122   QCString se = configStringRecode(s,"UTF-8",m_encoding);
123   const char *p=se.data();
124   if (p)
125   {
126     t << " ";
127     while ((c=*p++)!=0 && !needsEscaping) 
128       needsEscaping = (c==' ' || c=='\n' || c=='\t' || c=='"' || c=='#');
129     if (needsEscaping)
130     { 
131       t << "\"";
132       p=se.data();
133       while (*p)
134       {
135         if (*p==' ' && *(p+1)=='\0') break; // skip inserted space at the end
136         if (*p=='"') t << "\\"; // escape quotes
137         t << *p++;
138       }
139       t << "\"";
140     }
141     else
142     {
143       t << se;
144     }
145   }
146 }
147
148 void ConfigOption::writeStringList(FTextStream &t,QStrList &l)
149 {
150   const char *p = l.first();
151   bool first=TRUE;
152   while (p)
153   {
154     QCString s=p;
155     if (!first)
156       t << "                        ";
157     first=FALSE;
158     writeStringValue(t,s);
159     p = l.next();
160     if (p) t << " \\" << endl;
161   }
162 }
163
164 /* -----------------------------------------------------------------
165  */
166
167 ConfigImpl *ConfigImpl::m_instance = 0;
168
169 void ConfigInt::convertStrToVal() 
170 {
171   if (!m_valueString.isEmpty())
172   {
173     bool ok;
174     int val = m_valueString.toInt(&ok);
175     if (!ok || val<m_minVal || val>m_maxVal)
176     {
177       config_warn("argument `%s' for option %s is not a valid number in the range [%d..%d]!\n"
178                 "Using the default: %d!\n",m_valueString.data(),m_name.data(),m_minVal,m_maxVal,m_value);
179     }
180     else
181     {
182       m_value=val;
183     }
184   }
185 }
186
187 void ConfigBool::convertStrToVal()
188 {
189   QCString val = m_valueString.stripWhiteSpace().lower();
190   if (!val.isEmpty())
191   {
192     if (val=="yes" || val=="true" || val=="1" || val=="all") 
193     {
194       m_value=TRUE;
195     }
196     else if (val=="no" || val=="false" || val=="0" || val=="none")
197     {
198       m_value=FALSE;
199     }
200     else
201     {
202       config_warn("argument `%s' for option %s is not a valid boolean value\n"
203                 "Using the default: %s!\n",m_valueString.data(),m_name.data(),m_value?"YES":"NO");
204     }
205   }
206 }
207
208 QCString &ConfigImpl::getString(const char *fileName,int num,const char *name) const
209 {
210   ConfigOption *opt = m_dict->find(name);
211   if (opt==0) 
212   {
213     config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
214     exit(1);
215   }
216   else if (opt->kind()!=ConfigOption::O_String)
217   {
218     config_err("%s<%d>: Internal error: Requested option %s not of string type!\n",fileName,num,name);
219     exit(1);
220   }
221   return *((ConfigString *)opt)->valueRef();
222 }
223
224 QStrList &ConfigImpl::getList(const char *fileName,int num,const char *name) const
225 {
226   ConfigOption *opt = m_dict->find(name);
227   if (opt==0) 
228   {
229     config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
230     exit(1);
231   }
232   else if (opt->kind()!=ConfigOption::O_List)
233   {
234     config_err("%s<%d>: Internal error: Requested option %s not of list type!\n",fileName,num,name);
235     exit(1);
236   }
237   return *((ConfigList *)opt)->valueRef();
238 }
239
240 QCString &ConfigImpl::getEnum(const char *fileName,int num,const char *name) const
241 {
242   ConfigOption *opt = m_dict->find(name);
243   if (opt==0) 
244   {
245     config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
246     exit(1);
247   }
248   else if (opt->kind()!=ConfigOption::O_Enum)
249   {
250     config_err("%s<%d>: Internal error: Requested option %s not of enum type!\n",fileName,num,name);
251     exit(1);
252   }
253   return *((ConfigEnum *)opt)->valueRef();
254 }
255
256 int &ConfigImpl::getInt(const char *fileName,int num,const char *name) const
257 {
258   ConfigOption *opt = m_dict->find(name);
259   if (opt==0) 
260   {
261     config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
262     exit(1);
263   }
264   else if (opt->kind()!=ConfigOption::O_Int)
265   {
266     config_err("%s<%d>: Internal error: Requested option %s not of integer type!\n",fileName,num,name);
267     exit(1);
268   }
269   return *((ConfigInt *)opt)->valueRef();
270 }
271
272 bool &ConfigImpl::getBool(const char *fileName,int num,const char *name) const
273 {
274   ConfigOption *opt = m_dict->find(name);
275   if (opt==0) 
276   {
277     config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
278     exit(1);
279   }
280   else if (opt->kind()!=ConfigOption::O_Bool)
281   {
282     config_err("%s<%d>: Internal error: Requested option %s not of boolean type!\n",fileName,num,name);
283     exit(1);
284   }
285   return *((ConfigBool *)opt)->valueRef();
286 }
287
288 /* ------------------------------------------ */
289
290 void ConfigInfo::writeTemplate(FTextStream &t, bool sl,bool)
291 {
292   if (!sl)
293   {
294     t << "\n";
295   }
296   t << "#---------------------------------------------------------------------------\n";
297   t << "# " << m_doc << endl;
298   t << "#---------------------------------------------------------------------------\n";
299 }
300
301 void ConfigList::writeTemplate(FTextStream &t,bool sl,bool)
302 {
303   if (!sl)
304   {
305     t << endl;
306     t << convertToComment(m_doc, m_userComment);
307     t << endl;
308   }
309   else if (!m_userComment.isEmpty())
310   {
311     t << convertToComment("", m_userComment);
312   }
313   t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
314   writeStringList(t,m_value);
315   t << "\n";
316 }
317
318 void ConfigEnum::writeTemplate(FTextStream &t,bool sl,bool)
319 {
320   if (!sl)
321   {
322     t << endl;
323     t << convertToComment(m_doc, m_userComment);
324     t << endl;
325   }
326   else if (!m_userComment.isEmpty())
327   {
328     t << convertToComment("", m_userComment);
329   }
330   t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
331   writeStringValue(t,m_value);
332   t << "\n";
333 }
334
335 void ConfigString::writeTemplate(FTextStream &t,bool sl,bool)
336 {
337   if (!sl)
338   {
339     t << endl;
340     t << convertToComment(m_doc, m_userComment);
341     t << endl;
342   }
343   else if (!m_userComment.isEmpty())
344   {
345     t << convertToComment("", m_userComment);
346   }
347   t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
348   writeStringValue(t,m_value);
349   t << "\n";
350 }
351
352 void ConfigInt::writeTemplate(FTextStream &t,bool sl,bool upd)
353 {
354   if (!sl)
355   {
356     t << endl;
357     t << convertToComment(m_doc, m_userComment);
358     t << endl;
359   }
360   else if (!m_userComment.isEmpty())
361   {
362     t << convertToComment("", m_userComment);
363   }
364   t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
365   if (upd && !m_valueString.isEmpty())
366   {
367     writeStringValue(t,m_valueString);
368   }
369   else
370   {
371     writeIntValue(t,m_value);
372   }
373   t << "\n";
374 }
375
376 void ConfigBool::writeTemplate(FTextStream &t,bool sl,bool upd)
377 {
378   if (!sl)
379   {
380     t << endl;
381     t << convertToComment(m_doc, m_userComment);
382     t << endl;
383   }
384   else if (!m_userComment.isEmpty())
385   {
386     t << convertToComment("", m_userComment);
387   }
388   t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
389   if (upd && !m_valueString.isEmpty())
390   {
391     writeStringValue(t,m_valueString);
392   }
393   else
394   {
395     writeBoolValue(t,m_value);
396   }
397   t << "\n";
398 }
399
400 void ConfigObsolete::writeTemplate(FTextStream &,bool,bool) {}
401 void ConfigDisabled::writeTemplate(FTextStream &,bool,bool) {}
402
403 /* -----------------------------------------------------------------
404  *
405  *      static variables
406  */
407
408 struct ConfigFileState
409 {
410   int lineNr;
411   FILE *filePtr;
412   YY_BUFFER_STATE oldState;
413   YY_BUFFER_STATE newState;
414   QCString fileName;
415 };  
416
417 static const char       *inputString;
418 static int               inputPosition;
419 static int               yyLineNr;
420 static QCString          yyFileName;
421 static QCString          tmpString;
422 static QCString         *s=0;
423 static bool             *b=0;
424 static QStrList         *l=0;
425 static int               lastState;
426 static QCString          elemStr;
427 static QStrList          includePathList;
428 static QStack<ConfigFileState> includeStack;  
429 static int               includeDepth;
430 static bool              config_upd = FALSE;
431 static QCString          encoding;
432 static ConfigImpl       *config;
433
434 /* -----------------------------------------------------------------
435  */
436 #undef  YY_INPUT
437 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
438
439 static int yyread(char *buf,int max_size)
440 {
441     // no file included
442     if (includeStack.isEmpty()) 
443     {
444         int c=0;
445         if (inputString==0) return c;
446         while( c < max_size && inputString[inputPosition] )
447         {
448               *buf = inputString[inputPosition++] ;
449               c++; buf++;
450         }
451         return c;
452     } 
453     else 
454     {
455         //assert(includeStack.current()->newState==YY_CURRENT_BUFFER);
456         return (int)fread(buf,1,max_size,includeStack.current()->filePtr);
457     }
458 }
459
460
461 static QCString configStringRecode(
462     const QCString &str,
463     const char *fromEncoding,
464     const char *toEncoding)
465 {
466   QCString inputEncoding = fromEncoding;
467   QCString outputEncoding = toEncoding;
468   if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || inputEncoding==outputEncoding) return str;
469   int inputSize=str.length();
470   int outputSize=inputSize*4+1;
471   QCString output(outputSize);
472   void *cd = portable_iconv_open(outputEncoding,inputEncoding);
473   if (cd==(void *)(-1)) 
474   {
475     fprintf(stderr,"Error: unsupported character conversion: '%s'->'%s'\n",
476         inputEncoding.data(),outputEncoding.data());
477     exit(1);
478   }
479   size_t iLeft=(size_t)inputSize;
480   size_t oLeft=(size_t)outputSize;
481   char *inputPtr  = str.rawData();
482   char *outputPtr = output.rawData();
483   if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
484   {
485     outputSize-=(int)oLeft;
486     output.resize(outputSize+1);
487     output.at(outputSize)='\0';
488     //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
489   }
490   else
491   {
492     fprintf(stderr,"Error: failed to translate characters from %s to %s: %s\n",
493         inputEncoding.data(),outputEncoding.data(),strerror(errno));
494     exit(1);
495   }
496   portable_iconv_close(cd);
497   return output;
498 }
499
500 static void checkEncoding()
501 {
502   ConfigString *option = (ConfigString*)config->get("DOXYFILE_ENCODING");
503   encoding = *option->valueRef();
504 }
505
506 static FILE *tryPath(const char *path,const char *fileName)
507 {
508   QCString absName=(path ? (QCString)path+"/"+fileName : (QCString)fileName);
509   QFileInfo fi(absName);
510   if (fi.exists() && fi.isFile())
511   {
512     FILE *f=portable_fopen(absName,"r");
513     if (!f) config_err("could not open file %s for reading\n",absName.data());
514     return f;
515   }
516   return 0;
517 }
518
519 static void substEnvVarsInStrList(QStrList &sl);
520 static void substEnvVarsInString(QCString &s);
521
522 static FILE *findFile(const char *fileName)
523 {
524   if (fileName==0)
525   {
526     return 0;
527   }
528   if (portable_isAbsolutePath(fileName))
529   {
530     return tryPath(NULL, fileName);
531   }
532   substEnvVarsInStrList(includePathList);
533   char *s=includePathList.first();
534   while (s) // try each of the include paths
535   {
536     FILE *f = tryPath(s,fileName);
537     if (f) return f;
538     s=includePathList.next();
539   } 
540   // try cwd if includePathList fails
541   return tryPath(".",fileName);
542 }
543
544 static void readIncludeFile(const char *incName)
545 {
546   if (includeDepth==MAX_INCLUDE_DEPTH) {
547     config_err("maximum include depth (%d) reached, %s is not included. Aborting...\n",
548         MAX_INCLUDE_DEPTH,incName);
549     exit(1);
550   } 
551
552   QCString inc = incName;
553   substEnvVarsInString(inc);
554   inc = inc.stripWhiteSpace();
555   uint incLen = inc.length();
556   if (incLen>0 && inc.at(0)=='"' && inc.at(incLen-1)=='"') // strip quotes
557   {
558     inc=inc.mid(1,incLen-2);
559   }
560
561   FILE *f;
562
563   if ((f=findFile(inc))) // see if the include file can be found
564   {
565     // For debugging
566 #if SHOW_INCLUDES
567     for (i=0;i<includeStack.count();i++) msg("  ");
568     msg("@INCLUDE = %s: parsing...\n",inc.data());
569 #endif
570
571     // store the state of the old file 
572     ConfigFileState *fs=new ConfigFileState;
573     fs->oldState=YY_CURRENT_BUFFER;
574     fs->lineNr=yyLineNr;
575     fs->fileName=yyFileName;
576     fs->filePtr=f;
577     // push the state on the stack
578     includeStack.push(fs);
579     // set the scanner to the include file
580     yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
581     fs->newState=YY_CURRENT_BUFFER;
582     yyFileName=inc;
583     includeDepth++;
584   } 
585   else
586   {
587     config_err("@INCLUDE = %s: not found!\n",inc.data());
588     exit(1);
589   }
590 }
591
592
593 %}
594
595 %option noyywrap
596
597 %x      PreStart
598 %x      Start
599 %x      SkipComment
600 %x      SkipInvalid
601 %x      GetString
602 %x      GetBool
603 %x      GetStrList
604 %x      GetQuotedString
605 %x      GetEnvVar
606 %x      Include
607
608 %%
609
610 <*>\0x0d
611 <PreStart>"##".*"\n" { config->appendStartComment(yytext);}
612 <PreStart>. {
613               BEGIN(Start);
614               unput(*yytext);
615             }
616 <Start,GetString,GetStrList,GetBool,SkipInvalid>"##".*"\n" { config->appendUserComment(yytext);}
617 <Start,GetString,GetStrList,GetBool,SkipInvalid>"#"        { BEGIN(SkipComment); }
618 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"="   { QCString cmd=yytext;
619                                            cmd=cmd.left(cmd.length()-1).stripWhiteSpace(); 
620                                            ConfigOption *option = config->get(cmd);
621                                            if (option==0) // oops not known
622                                            {
623                                              config_warn("ignoring unsupported tag `%s' at line %d, file %s\n",
624                                                  yytext,yyLineNr,yyFileName.data()); 
625                                              BEGIN(SkipInvalid);
626                                            }
627                                            else // known tag
628                                            {
629                                              option->setUserComment(config->takeUserComment());
630                                              option->setEncoding(encoding);
631                                              switch(option->kind())
632                                              {
633                                                case ConfigOption::O_Info:
634                                                  // shouldn't get here!
635                                                  BEGIN(SkipInvalid);
636                                                  break;
637                                                case ConfigOption::O_List:
638                                                  l = ((ConfigList *)option)->valueRef();
639                                                  l->clear();
640                                                  elemStr="";
641                                                  BEGIN(GetStrList);
642                                                  break;
643                                                case ConfigOption::O_Enum:
644                                                  s = ((ConfigEnum *)option)->valueRef();
645                                                  s->resize(0);
646                                                  BEGIN(GetString);
647                                                  break;
648                                                case ConfigOption::O_String:
649                                                  s = ((ConfigString *)option)->valueRef();
650                                                  s->resize(0);
651                                                  BEGIN(GetString);
652                                                  break;
653                                                case ConfigOption::O_Int:
654                                                  s = ((ConfigInt *)option)->valueStringRef();
655                                                  s->resize(0);
656                                                  BEGIN(GetString);
657                                                  break;
658                                                case ConfigOption::O_Bool:
659                                                  s = ((ConfigBool *)option)->valueStringRef();
660                                                  s->resize(0);
661                                                  BEGIN(GetString);
662                                                  break;
663                                                case ConfigOption::O_Obsolete:
664                                                  if (config_upd)
665                                                  {
666                                                    config_warn("Tag `%s' at line %d of file `%s' has become obsolete.\n"
667                                                               "         This tag has been removed.\n", cmd.data(),yyLineNr,yyFileName.data());
668                                                  }
669                                                  else
670                                                  {
671                                                    config_warn("Tag `%s' at line %d of file `%s' has become obsolete.\n"
672                                                               "         To avoid this warning please remove this line from your configuration "
673                                                               "file or upgrade it using \"doxygen -u\"\n", cmd.data(),yyLineNr,yyFileName.data()); 
674                                                  }
675                                                  BEGIN(SkipInvalid);
676                                                  break;
677                                                case ConfigOption::O_Disabled:
678                                                  if (config_upd)
679                                                  {
680                                                    config_warn("Tag `%s' at line %d of file `%s' belongs to an option that was not enabled at compile time.\n"
681                                                               "         This tag has been removed.\n", cmd.data(),yyLineNr,yyFileName.data());
682                                                  }
683                                                  else
684                                                  {
685                                                    config_warn("Tag `%s' at line %d of file `%s' belongs to an option that was not enabled at compile time.\n"
686                                                               "         To avoid this warning please remove this line from your configuration "
687                                                             "file or upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", cmd.data(),yyLineNr,yyFileName.data()); 
688                                                  }
689                                                  BEGIN(SkipInvalid);
690                                                  break;
691                                              }
692                                            }
693                                         }
694 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { QCString cmd=yytext;
695                                           cmd=cmd.left(cmd.length()-2).stripWhiteSpace(); 
696                                           ConfigOption *option = config->get(cmd);
697                                           if (option==0) // oops not known
698                                           {
699                                             config_warn("ignoring unsupported tag `%s' at line %d, file %s\n",
700                                                 yytext,yyLineNr,yyFileName.data()); 
701                                             BEGIN(SkipInvalid);
702                                           }
703                                           else // known tag
704                                           {
705                                             option->setUserComment(config->takeUserComment());
706                                             switch(option->kind())
707                                             {
708                                               case ConfigOption::O_Info:
709                                                 // shouldn't get here!
710                                                 BEGIN(SkipInvalid);
711                                                 break;
712                                               case ConfigOption::O_List:
713                                                 l = ((ConfigList *)option)->valueRef();
714                                                 elemStr="";
715                                                 BEGIN(GetStrList);
716                                                 break;
717                                               case ConfigOption::O_Enum:
718                                               case ConfigOption::O_String:
719                                               case ConfigOption::O_Int:
720                                               case ConfigOption::O_Bool:
721                                                 config_warn("operator += not supported for `%s'. Ignoring line at line %d, file %s\n",
722                                                     yytext,yyLineNr,yyFileName.data()); 
723                                                 BEGIN(SkipInvalid);
724                                                 break;
725                                                case ConfigOption::O_Obsolete:
726                                                  config_warn("Tag `%s' at line %d of file %s has become obsolete.\n"
727                                                             "To avoid this warning please update your configuration "
728                                                             "file using \"doxygen -u\"\n", cmd.data(),yyLineNr,yyFileName.data()); 
729                                                  BEGIN(SkipInvalid);
730                                                  break;
731                                                case ConfigOption::O_Disabled:
732                                                  config_warn("Tag `%s' at line %d of file %s belongs to an option that was not enabled at compile time.\n"
733                                                             "To avoid this warning please remove this line from your configuration "
734                                                             "file, upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", cmd.data(),yyLineNr,yyFileName.data()); 
735                                                  BEGIN(SkipInvalid);
736                                                  break;
737                                              }
738                                            }
739                                         }
740 <Start>"@INCLUDE_PATH"[ \t]*"="         { BEGIN(GetStrList); l=&includePathList; l->clear(); elemStr=""; }
741   /* include a config file */
742 <Start>"@INCLUDE"[ \t]*"="              { BEGIN(Include);}
743 <Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") { 
744                                           readIncludeFile(configStringRecode(yytext,encoding,"UTF-8")); 
745                                           BEGIN(Start);
746                                         }
747 <<EOF>>                                 {
748                                           //printf("End of include file\n");
749                                           //printf("Include stack depth=%d\n",g_includeStack.count());
750                                           if (includeStack.isEmpty())
751                                           {
752                                             //printf("Terminating scanner!\n");
753                                             yyterminate();
754                                           }
755                                           else
756                                           {
757                                             ConfigFileState *fs=includeStack.pop();
758                                             fclose(fs->filePtr);
759                                             YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
760                                             yy_switch_to_buffer( fs->oldState );
761                                             yy_delete_buffer( oldBuf );
762                                             yyLineNr=fs->lineNr;
763                                             yyFileName=fs->fileName;
764                                             delete fs; fs=0;
765                                             includeDepth--;
766                                           }
767                                         }
768
769 <Start>[a-z_A-Z0-9]+                    { config_warn("ignoring unknown tag `%s' at line %d, file %s\n",yytext,yyLineNr,yyFileName.data()); }
770 <GetString,GetBool,SkipInvalid>\n       { yyLineNr++; BEGIN(Start); }
771 <GetStrList>\n                          { 
772                                           yyLineNr++; 
773                                           if (!elemStr.isEmpty())
774                                           {
775                                             //printf("elemStr1=`%s'\n",elemStr.data());
776                                             l->append(elemStr);
777                                           }
778                                           BEGIN(Start); 
779                                         }
780 <GetStrList>[ \t]+                      {
781                                           if (!elemStr.isEmpty())
782                                           {
783                                             //printf("elemStr2=`%s'\n",elemStr.data());
784                                             l->append(elemStr);
785                                           }
786                                           elemStr.resize(0);
787                                         }
788 <GetString>[^ \"\t\r\n]+                { (*s)+=configStringRecode(yytext,encoding,"UTF-8"); 
789                                           checkEncoding();
790                                         }
791 <GetString,GetStrList,SkipInvalid>"\""  { lastState=YY_START;
792                                           BEGIN(GetQuotedString); 
793                                           tmpString.resize(0); 
794                                         }
795 <GetQuotedString>"\""|"\n"              { 
796                                           // we add a bogus space to signal that the string was quoted. This space will be stripped later on.
797                                           tmpString+=" ";
798                                           //printf("Quoted String = `%s'\n",tmpString.data());
799                                           if (lastState==GetString)
800                                           {
801                                             (*s)+=configStringRecode(tmpString,encoding,"UTF-8");
802                                             checkEncoding();
803                                           }
804                                           else
805                                           {
806                                             elemStr+=configStringRecode(tmpString,encoding,"UTF-8");
807                                           }
808                                           if (*yytext=='\n')
809                                           {
810                                             config_warn("Missing end quote (\") on line %d, file %s\n",yyLineNr,yyFileName.data());
811                                             yyLineNr++;
812                                           }
813                                           BEGIN(lastState);
814                                         }
815 <GetQuotedString>"\\\""                 {
816                                           tmpString+='"';
817                                         }
818 <GetQuotedString>.                      { tmpString+=*yytext; }
819 <GetBool>[a-zA-Z]+                      { 
820                                           QCString bs=yytext; 
821                                           bs=bs.upper();
822                                           if (bs=="YES" || bs=="1")
823                                             *b=TRUE;
824                                           else if (bs=="NO" || bs=="0")
825                                             *b=FALSE;
826                                           else 
827                                           {
828                                             *b=FALSE; 
829                                             config_warn("Invalid value `%s' for "
830                                                  "boolean tag in line %d, file %s; use YES or NO\n",
831                                                  bs.data(),yyLineNr,yyFileName.data());
832                                           }
833                                         }
834 <GetStrList>[^ \#\"\t\r\n]+             {
835                                           elemStr+=configStringRecode(yytext,encoding,"UTF-8");
836                                         }
837 <SkipComment>\n                         { yyLineNr++; BEGIN(Start); }
838 <SkipComment>\\[ \r\t]*\n               { yyLineNr++; BEGIN(Start); }
839 <*>\\[ \r\t]*\n                         { yyLineNr++; }
840 <*>.                                    
841 <*>\n                                   { yyLineNr++ ; }
842
843 %%
844
845 /*@ ----------------------------------------------------------------------------
846  */
847
848 void ConfigImpl::writeTemplate(FTextStream &t,bool sl,bool upd)
849 {
850   /* print first lines of user comment that were at the beginning of the file, might have special meaning for editors */
851   if (m_startComment)
852   {
853     t << takeStartComment() << endl; 
854   }
855   t << "# Doxyfile " << versionString << endl << endl;
856   if (!sl)
857   {
858     t << convertToComment(m_header,"");
859   }
860   QListIterator<ConfigOption> it = iterator();
861   ConfigOption *option;
862   for (;(option=it.current());++it)
863   {
864     option->writeTemplate(t,sl,upd);
865   }
866   /* print last lines of user comment that were at the end of the file */
867   if (m_userComment)
868   {
869     t << "\n";
870     t << takeUserComment();
871   }
872 }
873
874 void ConfigImpl::convertStrToVal()
875 {
876   QListIterator<ConfigOption> it = iterator();
877   ConfigOption *option;
878   for (;(option=it.current());++it)
879   {
880     option->convertStrToVal();
881   }
882 }
883
884 static void substEnvVarsInString(QCString &s)
885 {
886   static QRegExp re("\\$\\([a-z_A-Z0-9.-]+\\)");
887   static QRegExp re2("\\$\\([a-z_A-Z0-9.-]+\\([a-z_A-Z0-9.-]+\\)\\)"); // For e.g. PROGRAMFILES(X86)
888   if (s.isEmpty()) return;
889   int p=0;
890   int i,l;
891   //printf("substEnvVarInString(%s) start\n",s.data());
892   while ((i=re.match(s,p,&l))!=-1 || (i=re2.match(s,p,&l))!=-1)
893   {
894     //printf("Found environment var s.mid(%d,%d)=`%s'\n",i+2,l-3,s.mid(i+2,l-3).data());
895     QCString env=portable_getenv(s.mid(i+2,l-3));
896     substEnvVarsInString(env); // recursively expand variables if needed.
897     s = s.left(i)+env+s.right(s.length()-i-l);
898     p=i+env.length(); // next time start at the end of the expanded string
899   }
900   s=s.stripWhiteSpace(); // to strip the bogus space that was added when an argument
901                          // has quotes
902   //printf("substEnvVarInString(%s) end\n",s.data());
903 }
904
905 static void substEnvVarsInStrList(QStrList &sl)
906 {
907   char *s = sl.first();
908   while (s)
909   {
910     QCString result(s);
911     // an argument with quotes will have an extra space at the end, so wasQuoted will be TRUE.
912     bool wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1);
913     // here we strip the quote again
914     substEnvVarsInString(result);
915
916     //printf("Result %s was quoted=%d\n",result.data(),wasQuoted);
917
918     if (!wasQuoted) /* as a result of the expansion, a single string
919                        may have expanded into a list, which we'll
920                        add to sl. If the original string already 
921                        contained multiple elements no further 
922                        splitting is done to allow quoted items with spaces! */
923     {
924       int l=result.length();
925       int i,p=0;
926       // skip spaces
927       // search for a "word"
928       for (i=0;i<l;i++)
929       {
930         char c=0;
931         // skip until start of new word
932         while (i<l && ((c=result.at(i))==' ' || c=='\t')) i++; 
933         p=i; // p marks the start index of the word
934         // skip until end of a word
935         while (i<l && ((c=result.at(i))!=' ' && c!='\t' && c!='"')) i++;
936         if (i<l) // not at the end of the string
937         {
938           if (c=='"') // word within quotes
939           {
940             p=i+1;
941             for (i++;i<l;i++)
942             {
943               c=result.at(i);
944               if (c=='"') // end quote
945               {
946                 // replace the string in the list and go to the next item.
947                 sl.insert(sl.at(),result.mid(p,i-p)); // insert new item before current item.
948                 sl.next();                 // current item is now the old item
949                 p=i+1;
950                 break; 
951               }
952               else if (c=='\\') // skip escaped stuff
953               {
954                 i++;
955               }
956             }
957           }
958           else if (c==' ' || c=='\t') // separator
959           {
960             // replace the string in the list and go to the next item.
961             sl.insert(sl.at(),result.mid(p,i-p)); // insert new item before current item.
962             sl.next();                 // current item is now the old item
963             p=i+1;
964           }
965         }
966       }
967       if (p!=l) // add the leftover as a string
968       {
969         // replace the string in the list and go to the next item.
970         sl.insert(sl.at(),result.right(l-p)); // insert new item before current item.
971         sl.next();                 // current item is now the old item
972       }
973     }
974     else // just goto the next element in the list
975     {
976       sl.insert(sl.at(),result);
977       sl.next();
978     }
979     // remove the old unexpanded string from the list
980     int i=sl.at();
981     sl.remove(); // current item index changes if the last element is removed.
982     if (sl.at()==i)     // not last item
983         s = sl.current();
984     else                // just removed last item
985         s = 0;
986   }
987 }
988
989 void ConfigString::substEnvVars()
990 {
991   substEnvVarsInString(m_value);
992 }
993
994 void ConfigList::substEnvVars()
995 {
996   substEnvVarsInStrList(m_value);
997 }
998
999 void ConfigBool::substEnvVars()
1000 {
1001   substEnvVarsInString(m_valueString);
1002 }
1003
1004 void ConfigInt::substEnvVars()
1005 {
1006   substEnvVarsInString(m_valueString);
1007 }
1008
1009 void ConfigEnum::substEnvVars()
1010 {
1011   substEnvVarsInString(m_value);
1012 }
1013
1014 //---------------------------------------------
1015
1016 void ConfigImpl::substituteEnvironmentVars()
1017 {
1018   QListIterator<ConfigOption> it = iterator();
1019   ConfigOption *option;
1020   for (;(option=it.current());++it)
1021   {
1022     option->substEnvVars();
1023   }
1024 }
1025
1026 void ConfigImpl::init()
1027 {
1028   QListIterator<ConfigOption> it = iterator();
1029   ConfigOption *option;
1030   for (;(option=it.current());++it)
1031   {
1032     option->init();
1033   }
1034
1035   // sanity check if all depends relations are valid
1036   for (it.toFirst();(option=it.current());++it)
1037   {
1038     QCString depName = option->dependsOn();
1039     if (!depName.isEmpty())
1040     {
1041       ConfigOption * opt = ConfigImpl::instance()->get(depName);
1042       if (opt==0)
1043       {
1044         config_warn("Config option '%s' has invalid depends relation on unknown option '%s'\n",
1045             option->name().data(),depName.data());
1046         exit(1);
1047       }
1048     }
1049   }
1050 }
1051
1052 void ConfigImpl::create()
1053 {
1054   if (m_initialized) return; 
1055   m_initialized = TRUE;
1056   addConfigOptions(this);
1057 }
1058
1059 static QCString configFileToString(const char *name)
1060 {
1061   if (name==0 || name[0]==0) return 0;
1062   QFile f;
1063
1064   bool fileOpened=FALSE;
1065   if (name[0]=='-' && name[1]==0) // read from stdin
1066   {
1067     fileOpened=f.open(IO_ReadOnly,stdin);
1068     if (fileOpened)
1069     {
1070       const int bSize=4096;
1071       QCString contents(bSize);
1072       int totalSize=0;
1073       int size;
1074       while ((size=f.readBlock(contents.rawData()+totalSize,bSize))==bSize)
1075       {
1076         totalSize+=bSize;
1077         contents.resize(totalSize+bSize); 
1078       }
1079       totalSize+=size+2;
1080       contents.resize(totalSize);
1081       contents.at(totalSize-2)='\n'; // to help the scanner
1082       contents.at(totalSize-1)='\0';
1083       return contents;
1084     }
1085   }
1086   else // read from file
1087   {
1088     QFileInfo fi(name);
1089     if (!fi.exists() || !fi.isFile())
1090     {
1091       config_err("file `%s' not found\n",name);
1092       return "";
1093     }
1094     f.setName(name);
1095     fileOpened=f.open(IO_ReadOnly);
1096     if (fileOpened)
1097     {
1098       int fsize=f.size();
1099       QCString contents(fsize+2);
1100       f.readBlock(contents.rawData(),fsize);
1101       f.close();
1102       if (fsize==0 || contents[fsize-1]=='\n') 
1103         contents[fsize]='\0';
1104       else
1105         contents[fsize]='\n'; // to help the scanner
1106       contents[fsize+1]='\0';
1107       return contents;
1108     }
1109   }
1110   if (!fileOpened)  
1111   {
1112     config_err("cannot open file `%s' for reading\n",name);
1113     exit(1);
1114   }
1115   return "";
1116 }
1117
1118 bool ConfigImpl::parseString(const char *fn,const char *str,bool update)
1119 {
1120   config = ConfigImpl::instance();
1121   inputString   = str;
1122   inputPosition = 0;
1123   yyFileName    = fn;
1124   yyLineNr      = 1;
1125   includeStack.setAutoDelete(TRUE);
1126   includeStack.clear();
1127   includeDepth  = 0;
1128   configimplYYrestart( configimplYYin );
1129   BEGIN( PreStart );
1130   config_upd = update;
1131   configimplYYlex();
1132   config_upd = FALSE;
1133   inputString = 0;
1134   return TRUE;
1135 }
1136
1137 bool ConfigImpl::parse(const char *fn,bool update)
1138 {
1139   int retval;
1140   encoding = "UTF-8";
1141   printlex(yy_flex_debug, TRUE, __FILE__, fn);
1142   retval =  parseString(fn,configFileToString(fn), update); 
1143   printlex(yy_flex_debug, FALSE, __FILE__, fn);
1144   return retval;
1145 }
1146
1147 //----------------------------------------------------------------------
1148
1149 static void cleanUpPaths(QStrList &str)
1150 {
1151   char *sfp = str.first();
1152   while (sfp)
1153   {
1154     register char *p = sfp;
1155     if (p)
1156     {
1157       char c;
1158       while ((c=*p))
1159       {
1160         if (c=='\\') *p='/';
1161         p++;
1162       }
1163     }
1164     QCString path = sfp;
1165     if ((path.at(0)!='/' && (path.length()<=2 || path.at(1)!=':')) ||
1166          path.at(path.length()-1)!='/'
1167        )
1168     {
1169       QFileInfo fi(path);
1170       if (fi.exists() && fi.isDir())
1171       {
1172         int i = str.at();
1173         QString p = fi.absFilePath();
1174         if (p.at(p.length()-1)!='/')
1175           p.append('/');
1176         str.remove();
1177         if (str.at()==i) // did not remove last item
1178           str.insert(i,p.utf8());
1179         else
1180           str.append(p.utf8());
1181       }
1182     }
1183     sfp = str.next();
1184   }
1185 }
1186
1187 static void checkFileName(QCString &s,const char *optionName)
1188 {
1189   QCString val = s.stripWhiteSpace().lower();
1190   if ((val=="yes" || val=="true"  || val=="1" || val=="all") ||
1191       (val=="no"  || val=="false" || val=="0" || val=="none"))
1192   {
1193     err("file name expected for option %s, got %s instead. Ignoring...\n",optionName,s.data());
1194     s=""; // note the use of &s above: this will change the option value!
1195   }
1196 }
1197
1198 #include "config.h"
1199
1200 void Config::init()
1201 {
1202   ConfigImpl::instance()->init();
1203 }
1204
1205 void Config::checkAndCorrect()
1206 {
1207   QCString &warnFormat = ConfigImpl_getString("WARN_FORMAT");
1208   if (warnFormat.stripWhiteSpace().isEmpty())
1209   {
1210     warnFormat="$file:$line $text";
1211   }
1212   else
1213   {
1214     if (warnFormat.find("$file")==-1)
1215     {
1216       warn_uncond("warning format does not contain a $file tag!\n");
1217
1218     }
1219     if (warnFormat.find("$line")==-1)
1220     {
1221       warn_uncond("warning format does not contain a $line tag!\n");
1222     }
1223     if (warnFormat.find("$text")==-1)
1224     {
1225       warn_uncond("warning format foes not contain a $text tag!\n");
1226     }
1227   }
1228
1229   QCString &manExtension = ConfigImpl_getString("MAN_EXTENSION");
1230
1231   // set default man page extension if non is given by the user
1232   if (manExtension.isEmpty())
1233   {
1234     manExtension=".3";
1235   }
1236
1237   QCString &paperType = ConfigImpl_getEnum("PAPER_TYPE");
1238   paperType=paperType.lower().stripWhiteSpace();
1239   if (paperType.isEmpty() || paperType=="a4wide")
1240   {
1241     paperType = "a4";
1242   }
1243   if (paperType!="a4" && paperType!="letter" &&
1244       paperType!="legal" && paperType!="executive")
1245   {
1246     err("Unknown page type specified\n");
1247     paperType="a4";
1248   }
1249
1250   QCString &outputLanguage=ConfigImpl_getEnum("OUTPUT_LANGUAGE");
1251   outputLanguage=outputLanguage.stripWhiteSpace();
1252   if (outputLanguage.isEmpty())
1253   {
1254     outputLanguage = "English";
1255   }
1256
1257   QCString &htmlFileExtension=ConfigImpl_getString("HTML_FILE_EXTENSION");
1258   htmlFileExtension=htmlFileExtension.stripWhiteSpace();
1259   if (htmlFileExtension.isEmpty())
1260   {
1261     htmlFileExtension = ".html";
1262   }
1263
1264   // expand the relative stripFromPath values
1265   QStrList &stripFromPath = ConfigImpl_getList("STRIP_FROM_PATH");
1266   char *sfp = stripFromPath.first();
1267   if (sfp==0) // by default use the current path
1268   {
1269     QString p = QDir::currentDirPath();
1270     if (p.at(p.length()-1)!='/')
1271         p.append('/');
1272     stripFromPath.append(p.utf8());
1273   }
1274   else
1275   {
1276     cleanUpPaths(stripFromPath);
1277   }
1278
1279   // expand the relative stripFromPath values
1280   QStrList &stripFromIncPath = ConfigImpl_getList("STRIP_FROM_INC_PATH");
1281   cleanUpPaths(stripFromIncPath);
1282
1283   // Test to see if HTML header is valid
1284   QCString &headerFile = ConfigImpl_getString("HTML_HEADER");
1285   if (!headerFile.isEmpty())
1286   {
1287     QFileInfo fi(headerFile);
1288     if (!fi.exists())
1289     {
1290       err("tag HTML_HEADER: header file `%s' "
1291           "does not exist\n",headerFile.data());
1292       exit(1);
1293     }
1294   }
1295   // Test to see if HTML footer is valid
1296   QCString &footerFile = ConfigImpl_getString("HTML_FOOTER");
1297   if (!footerFile.isEmpty())
1298   {
1299     QFileInfo fi(footerFile);
1300     if (!fi.exists())
1301     {
1302       err("tag HTML_FOOTER: footer file `%s' "
1303           "does not exist\n",footerFile.data());
1304       exit(1);
1305     }
1306   }
1307
1308   // Test to see if MathJax code file is valid
1309   if (ConfigImpl_getBool("USE_MATHJAX"))
1310   {
1311     QCString &MathJaxCodefile = ConfigImpl_getString("MATHJAX_CODEFILE");
1312     if (!MathJaxCodefile.isEmpty())
1313     {
1314       QFileInfo fi(MathJaxCodefile);
1315       if (!fi.exists())
1316       {
1317         err("tag MATHJAX_CODEFILE file `%s' "
1318             "does not exist\n",MathJaxCodefile.data());
1319         exit(1);
1320       }
1321     }
1322     QCString &path = ConfigImpl_getString("MATHJAX_RELPATH");
1323     if (!path.isEmpty() && path.at(path.length()-1)!='/')
1324     {
1325       path+="/";
1326     }
1327
1328   }
1329
1330   // Test to see if LaTeX header is valid
1331   QCString &latexHeaderFile = ConfigImpl_getString("LATEX_HEADER");
1332   if (!latexHeaderFile.isEmpty())
1333   {
1334     QFileInfo fi(latexHeaderFile);
1335     if (!fi.exists())
1336     {
1337       err("tag LATEX_HEADER: header file `%s' "
1338           "does not exist\n",latexHeaderFile.data());
1339       exit(1);
1340     }
1341   }
1342   // Test to see if LaTeX footer is valid
1343   QCString &latexFooterFile = ConfigImpl_getString("LATEX_FOOTER");
1344   if (!latexFooterFile.isEmpty())
1345   {
1346     QFileInfo fi(latexFooterFile);
1347     if (!fi.exists())
1348     {
1349       err("tag LATEX_FOOTER: footer file `%s' "
1350           "does not exist\n",latexFooterFile.data());
1351       exit(1);
1352     }
1353   }
1354
1355   // check include path
1356   QStrList &includePath = ConfigImpl_getList("INCLUDE_PATH");
1357   char *s=includePath.first();
1358   while (s)
1359   {
1360     QFileInfo fi(s);
1361     if (!fi.exists()) warn_uncond("tag INCLUDE_PATH: include path `%s' "
1362                           "does not exist\n",s);
1363     s=includePath.next();
1364   }
1365
1366   // check aliases
1367   QStrList &aliasList = ConfigImpl_getList("ALIASES");
1368   s=aliasList.first();
1369   while (s)
1370   {
1371     QRegExp re1("[a-z_A-Z][a-z_A-Z0-9]*[ \t]*=");         // alias without argument
1372     QRegExp re2("[a-z_A-Z][a-z_A-Z0-9]*{[0-9]*}[ \t]*="); // alias with argument
1373     QCString alias=s;
1374     alias=alias.stripWhiteSpace();
1375     if (alias.find(re1)!=0 && alias.find(re2)!=0)
1376     {
1377       err("Illegal alias format `%s'. Use \"name=value\" or \"name(n)=value\", where n is the number of arguments\n",
1378           alias.data());
1379     }
1380     s=aliasList.next();
1381   }
1382
1383   // check if GENERATE_TREEVIEW and GENERATE_HTMLHELP are both enabled
1384   if (ConfigImpl_getBool("GENERATE_TREEVIEW") && ConfigImpl_getBool("GENERATE_HTMLHELP"))
1385   {
1386     err("When enabling GENERATE_HTMLHELP the tree view (GENERATE_TREEVIEW) should be disabled. I'll do it for you.\n");
1387     ConfigImpl_getBool("GENERATE_TREEVIEW")=FALSE;
1388   }
1389   if (ConfigImpl_getBool("SEARCHENGINE") && ConfigImpl_getBool("GENERATE_HTMLHELP"))
1390   {
1391     err("When enabling GENERATE_HTMLHELP the search engine (SEARCHENGINE) should be disabled. I'll do it for you.\n");
1392     ConfigImpl_getBool("SEARCHENGINE")=FALSE;
1393   }
1394
1395   // check if SEPARATE_MEMBER_PAGES and INLINE_GROUPED_CLASSES are both enabled
1396   if (ConfigImpl_getBool("SEPARATE_MEMBER_PAGES") && ConfigImpl_getBool("INLINE_GROUPED_CLASSES"))
1397   {
1398     err("When enabling INLINE_GROUPED_CLASSES the SEPARATE_MEMBER_PAGES option should be disabled. I'll do it for you.\n");
1399     ConfigImpl_getBool("SEPARATE_MEMBER_PAGES")=FALSE;
1400   }
1401
1402   // check dot image format
1403   QCString &dotImageFormat=ConfigImpl_getEnum("DOT_IMAGE_FORMAT");
1404   dotImageFormat=dotImageFormat.stripWhiteSpace();
1405   if (dotImageFormat.isEmpty())
1406   {
1407     dotImageFormat = "png";
1408   }
1409   //else if (dotImageFormat!="gif" && dotImageFormat!="png" && dotImageFormat!="jpg")
1410   //{
1411   //  err("Invalid value for DOT_IMAGE_FORMAT: `%s'. Using the default.\n",dotImageFormat.data());
1412   //  dotImageFormat = "png";
1413   //}
1414
1415   QCString &dotFontName=ConfigImpl_getString("DOT_FONTNAME");
1416   if (dotFontName=="FreeSans" || dotFontName=="FreeSans.ttf")
1417   {
1418     warn_uncond("doxygen no longer ships with the FreeSans font.\n"
1419                "You may want to clear or change DOT_FONTNAME.\n"
1420                "Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n");
1421   }
1422
1423
1424   // check dot path
1425   QCString &dotPath = ConfigImpl_getString("DOT_PATH");
1426   if (!dotPath.isEmpty())
1427   {
1428     QFileInfo fi(dotPath);
1429     if (fi.exists() && fi.isFile()) // user specified path + exec
1430     {
1431       dotPath=fi.dirPath(TRUE).utf8()+"/";
1432     }
1433     else
1434     {
1435       QFileInfo dp(dotPath+"/dot"+portable_commandExtension());
1436       if (!dp.exists() || !dp.isFile())
1437       {
1438         warn_uncond("the dot tool could not be found at %s\n",dotPath.data());
1439         dotPath="";
1440       }
1441       else
1442       {
1443         dotPath=dp.dirPath(TRUE).utf8()+"/";
1444       }
1445     }
1446 #if defined(_WIN32) // convert slashes
1447     uint i=0,l=dotPath.length();
1448     for (i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
1449 #endif
1450   }
1451   else // make sure the string is empty but not null!
1452   {
1453     dotPath="";
1454   }
1455
1456   // check mscgen path
1457   QCString &mscgenPath = ConfigImpl_getString("MSCGEN_PATH");
1458   if (!mscgenPath.isEmpty())
1459   {
1460     QFileInfo dp(mscgenPath+"/mscgen"+portable_commandExtension());
1461     if (!dp.exists() || !dp.isFile())
1462     {
1463       warn_uncond("the mscgen tool could not be found at %s\n",mscgenPath.data());
1464       mscgenPath="";
1465     }
1466     else
1467     {
1468       mscgenPath=dp.dirPath(TRUE).utf8()+"/";
1469 #if defined(_WIN32) // convert slashes
1470       uint i=0,l=mscgenPath.length();
1471       for (i=0;i<l;i++) if (mscgenPath.at(i)=='/') mscgenPath.at(i)='\\';
1472 #endif
1473     }
1474   }
1475   else // make sure the string is empty but not null!
1476   {
1477     mscgenPath="";
1478   }
1479
1480   // check plantuml path
1481   QCString &plantumlJarPath = ConfigImpl_getString("PLANTUML_JAR_PATH");
1482   if (!plantumlJarPath.isEmpty())
1483   {
1484     QFileInfo pu(plantumlJarPath);
1485     if (pu.exists() && pu.isDir()) // PLANTUML_JAR_PATH is directory
1486     {
1487       QFileInfo jar(plantumlJarPath+portable_pathSeparator()+"plantuml.jar");
1488       if (jar.exists() && jar.isFile())
1489       {
1490         plantumlJarPath = jar.dirPath(TRUE).utf8()+portable_pathSeparator();
1491       }
1492       else
1493       {
1494         err("Jar file plantuml.jar not found at location "
1495                    "specified via PLANTUML_JAR_PATH: '%s'\n",plantumlJarPath.data());
1496         plantumlJarPath="";
1497       }
1498     }
1499     else if (pu.exists() && pu.isFile() && plantumlJarPath.right(4)==".jar") // PLANTUML_JAR_PATH is file
1500     {
1501       plantumlJarPath = pu.dirPath(TRUE).utf8()+portable_pathSeparator();
1502     }
1503     else
1504     {
1505       err("path specified via PLANTUML_JAR_PATH does not exist or not a directory: %s\n",
1506                  plantumlJarPath.data());
1507       plantumlJarPath="";
1508     }
1509   }
1510
1511   // check dia path
1512   QCString &diaPath = ConfigImpl_getString("DIA_PATH");
1513   if (!diaPath.isEmpty())
1514   {
1515     QFileInfo dp(diaPath+"/dia"+portable_commandExtension());
1516     if (!dp.exists() || !dp.isFile())
1517     {
1518       warn_uncond("dia could not be found at %s\n",diaPath.data());
1519       diaPath="";
1520     }
1521     else
1522     {
1523       diaPath=dp.dirPath(TRUE).utf8()+"/";
1524 #if defined(_WIN32) // convert slashes
1525       uint i=0,l=diaPath.length();
1526       for (i=0;i<l;i++) if (diaPath.at(i)=='/') diaPath.at(i)='\\';
1527 #endif
1528     }
1529   }
1530   else // make sure the string is empty but not null!
1531   {
1532     diaPath="";
1533   }
1534
1535   // check input
1536   QStrList &inputSources=ConfigImpl_getList("INPUT");
1537   if (inputSources.count()==0)
1538   {
1539     // use current dir as the default
1540     inputSources.append(QDir::currentDirPath().utf8());
1541   }
1542   else
1543   {
1544     s=inputSources.first();
1545     while (s)
1546     {
1547       QFileInfo fi(s);
1548       if (!fi.exists())
1549       {
1550         warn_uncond("tag INPUT: input source `%s' does not exist\n",s);
1551       }
1552       s=inputSources.next();
1553     }
1554   }
1555
1556   // add default file patterns if needed
1557   QStrList &filePatternList = ConfigImpl_getList("FILE_PATTERNS");
1558   if (filePatternList.isEmpty())
1559   {
1560       ConfigOption * opt = ConfigImpl::instance()->get("FILE_PATTERNS");
1561       if (opt->kind()==ConfigOption::O_List)
1562       {
1563         ((ConfigList*)opt)->init();
1564       }
1565   }
1566
1567   // add default pattern if needed
1568   QStrList &examplePatternList = ConfigImpl_getList("EXAMPLE_PATTERNS");
1569   if (examplePatternList.isEmpty())
1570   {
1571     examplePatternList.append("*");
1572   }
1573
1574   // if no output format is enabled, warn the user
1575   if (!ConfigImpl_getBool("GENERATE_HTML")    &&
1576       !ConfigImpl_getBool("GENERATE_LATEX")   &&
1577       !ConfigImpl_getBool("GENERATE_MAN")     &&
1578       !ConfigImpl_getBool("GENERATE_RTF")     &&
1579       !ConfigImpl_getBool("GENERATE_XML")     &&
1580       !ConfigImpl_getBool("GENERATE_PERLMOD") &&
1581       !ConfigImpl_getBool("GENERATE_RTF")     &&
1582       !ConfigImpl_getBool("GENERATE_DOCBOOK") &&
1583       !ConfigImpl_getBool("GENERATE_AUTOGEN_DEF") &&
1584       ConfigImpl_getString("GENERATE_TAGFILE").isEmpty()
1585      )
1586   {
1587     warn_uncond("No output formats selected! Set at least one of the main GENERATE_* options to YES.\n");
1588   }
1589
1590   // check HTMLHELP creation requirements
1591   if (!ConfigImpl_getBool("GENERATE_HTML") &&
1592       ConfigImpl_getBool("GENERATE_HTMLHELP"))
1593   {
1594     warn_uncond("GENERATE_HTMLHELP=YES requires GENERATE_HTML=YES.\n");
1595   }
1596
1597   // check QHP creation requirements
1598   if (ConfigImpl_getBool("GENERATE_QHP"))
1599   {
1600     if (ConfigImpl_getString("QHP_NAMESPACE").isEmpty())
1601     {
1602       err("GENERATE_QHP=YES requires QHP_NAMESPACE to be set. Using 'org.doxygen.doc' as default!.\n");
1603       ConfigImpl_getString("QHP_NAMESPACE")="org.doxygen.doc";
1604     }
1605
1606     if (ConfigImpl_getString("QHP_VIRTUAL_FOLDER").isEmpty())
1607     {
1608       err("GENERATE_QHP=YES requires QHP_VIRTUAL_FOLDER to be set. Using 'doc' as default!\n");
1609       ConfigImpl_getString("QHP_VIRTUAL_FOLDER")="doc";
1610     }
1611   }
1612
1613   if (ConfigImpl_getBool("OPTIMIZE_OUTPUT_JAVA") && ConfigImpl_getBool("INLINE_INFO"))
1614   {
1615     // don't show inline info for Java output, since Java has no inline
1616     // concept.
1617     ConfigImpl_getBool("INLINE_INFO")=FALSE;
1618   }
1619
1620   int &depth = ConfigImpl_getInt("MAX_DOT_GRAPH_DEPTH");
1621   if (depth==0)
1622   {
1623     depth=1000;
1624   }
1625
1626   int &hue = ConfigImpl_getInt("HTML_COLORSTYLE_HUE");
1627   if (hue<0)
1628   {
1629     hue=0;
1630   }
1631   else if (hue>=360)
1632   {
1633     hue=hue%360;
1634   }
1635
1636   int &sat = ConfigImpl_getInt("HTML_COLORSTYLE_SAT");
1637   if (sat<0)
1638   {
1639     sat=0;
1640   }
1641   else if (sat>255)
1642   {
1643     sat=255;
1644   }
1645   int &gamma = ConfigImpl_getInt("HTML_COLORSTYLE_GAMMA");
1646   if (gamma<40)
1647   {
1648     gamma=40;
1649   }
1650   else if (gamma>240)
1651   {
1652     gamma=240;
1653   }
1654
1655   QCString mathJaxFormat = ConfigImpl_getEnum("MATHJAX_FORMAT");
1656   if (!mathJaxFormat.isEmpty() && mathJaxFormat!="HTML-CSS" &&
1657        mathJaxFormat!="NativeMML" && mathJaxFormat!="SVG")
1658   {
1659     err("Unsupported value for MATHJAX_FORMAT: Should be one of HTML-CSS, NativeMML, or SVG\n");
1660     ConfigImpl_getEnum("MATHJAX_FORMAT")="HTML-CSS";
1661   }
1662
1663   // add default words if needed
1664   QStrList &annotationFromBrief = ConfigImpl_getList("ABBREVIATE_BRIEF");
1665   if (annotationFromBrief.isEmpty())
1666   {
1667     annotationFromBrief.append("The $name class");
1668     annotationFromBrief.append("The $name widget");
1669     annotationFromBrief.append("The $name file");
1670     annotationFromBrief.append("is");
1671     annotationFromBrief.append("provides");
1672     annotationFromBrief.append("specifies");
1673     annotationFromBrief.append("contains");
1674     annotationFromBrief.append("represents");
1675     annotationFromBrief.append("a");
1676     annotationFromBrief.append("an");
1677     annotationFromBrief.append("the");
1678   }
1679
1680   // some default settings for vhdl
1681   if (ConfigImpl_getBool("OPTIMIZE_OUTPUT_VHDL") &&
1682       (ConfigImpl_getBool("INLINE_INHERITED_MEMB") ||
1683        ConfigImpl_getBool("INHERIT_DOCS") ||
1684        !ConfigImpl_getBool("HIDE_SCOPE_NAMES") ||
1685        !ConfigImpl_getBool("EXTRACT_PRIVATE") ||
1686        !ConfigImpl_getBool("EXTRACT_PACKAGE")
1687       )
1688      )
1689   {
1690     bool b1 = ConfigImpl_getBool("INLINE_INHERITED_MEMB");
1691     bool b2 = ConfigImpl_getBool("INHERIT_DOCS");
1692     bool b3 = ConfigImpl_getBool("HIDE_SCOPE_NAMES");
1693     bool b4 = ConfigImpl_getBool("EXTRACT_PRIVATE");
1694     bool b5 = ConfigImpl_getBool("SKIP_FUNCTION_MACROS");
1695     bool b6 = ConfigImpl_getBool("EXTRACT_PACKAGE");
1696     const char *s1,*s2,*s3,*s4,*s5,*s6;
1697     if (b1)  s1="  INLINE_INHERITED_MEMB  = NO (was YES)\n"; else s1="";
1698     if (b2)  s2="  INHERIT_DOCS           = NO (was YES)\n"; else s2="";
1699     if (!b3) s3="  HIDE_SCOPE_NAMES       = YES (was NO)\n"; else s3="";
1700     if (!b4) s4="  EXTRACT_PRIVATE        = YES (was NO)\n"; else s4="";
1701     if (b5)  s5="  ENABLE_PREPROCESSING   = NO (was YES)\n"; else s5="";
1702     if (!b6) s6="  EXTRACT_PACKAGE        = YES (was NO)\n"; else s6="";
1703
1704
1705     warn_uncond("enabling OPTIMIZE_OUTPUT_VHDL assumes the following settings:\n"
1706                "%s%s%s%s%s%s",s1,s2,s3,s4,s5,s6
1707               );
1708
1709     ConfigImpl_getBool("INLINE_INHERITED_MEMB") = FALSE;
1710     ConfigImpl_getBool("INHERIT_DOCS")          = FALSE;
1711     ConfigImpl_getBool("HIDE_SCOPE_NAMES")      = TRUE;
1712     ConfigImpl_getBool("EXTRACT_PRIVATE")       = TRUE;
1713     ConfigImpl_getBool("ENABLE_PREPROCESSING")  = FALSE;
1714     ConfigImpl_getBool("EXTRACT_PACKAGE")       = TRUE;
1715   }
1716
1717   checkFileName(ConfigImpl_getString("GENERATE_TAGFILE"),"GENERATE_TAGFILE");
1718
1719 #if 0 // TODO: this breaks test 25; SOURCEBROWSER = NO and SOURCE_TOOLTIPS = YES.
1720       // So this and other regressions should be analysed and fixed before this can be enabled
1721   // disable any boolean options that depend on disabled options
1722   QListIterator<ConfigOption> it = iterator();
1723   ConfigOption *option;
1724   for (it.toFirst();(option=it.current());++it)
1725   {
1726     QCString depName = option->dependsOn(); // option has a dependency
1727     if (!depName.isEmpty())
1728     {
1729       ConfigOption * dep = Config::instance()->get(depName);
1730       if (dep->kind()==ConfigOption::O_Bool &&
1731           ConfigImpl_getBool("depName")==FALSE) // dependent option is disabled
1732       {
1733         if (option->kind()==ConfigOption::O_Bool)
1734         {
1735           printf("disabling option %s\n",option->name().data());
1736           ConfigImpl_getBool("option->name("))=FALSE; // also disable this option
1737         }
1738       }
1739     }
1740   }
1741 #endif
1742
1743   ConfigValues::instance().init();
1744
1745 }
1746
1747 void Config::writeTemplate(FTextStream &t,bool shortList,bool update)
1748 {
1749   ConfigImpl::instance()->writeTemplate(t,shortList,update);
1750 }
1751
1752 bool Config::parse(const char *fileName,bool update)
1753 {
1754   return ConfigImpl::instance()->parse(fileName,update);
1755 }
1756
1757 void Config::postProcess(bool clearHeaderAndFooter)
1758 {
1759   ConfigImpl::instance()->substituteEnvironmentVars();
1760   ConfigImpl::instance()->convertStrToVal();
1761
1762   // avoid bootstrapping issues when the config file already
1763   // refers to the files that we are supposed to parse.
1764   if (clearHeaderAndFooter)
1765   {
1766     ConfigImpl_getString("HTML_HEADER")="";
1767     ConfigImpl_getString("HTML_FOOTER")="";
1768     ConfigImpl_getString("LATEX_HEADER")="";
1769     ConfigImpl_getString("LATEX_FOOTER")="";
1770   }
1771 }
1772
1773 void Config::deinit()
1774 {
1775   ConfigImpl::instance()->deleteInstance();
1776 }
1777