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