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