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