dc5136d4a6b29a408618bbe6cce2557a983130e6
[profile/ivi/qtbase.git] / src / tools / moc / preprocessor.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "preprocessor.h"
43 #include "utils.h"
44 #include <qstringlist.h>
45 #include <qfile.h>
46 #include <qdir.h>
47 #include <qfileinfo.h>
48
49 QT_BEGIN_NAMESPACE
50
51 #include "ppkeywords.cpp"
52 #include "keywords.cpp"
53
54 // transform \r\n into \n
55 // \r into \n (os9 style)
56 // backslash-newlines into newlines
57 static QByteArray cleaned(const QByteArray &input)
58 {
59     QByteArray result;
60     result.reserve(input.size());
61     const char *data = input.constData();
62     char *output = result.data();
63
64     int newlines = 0;
65     while (*data) {
66         while (*data && is_space(*data))
67             ++data;
68         bool takeLine = (*data == '#');
69         if (*data == '%' && *(data+1) == ':') {
70             takeLine = true;
71             ++data;
72         }
73         if (takeLine) {
74             *output = '#';
75             ++output;
76             do ++data; while (*data && is_space(*data));
77         }
78         while (*data) {
79             // handle \\\n, \\\r\n and \\\r
80             if (*data == '\\') {
81                 if (*(data + 1) == '\r') {
82                     ++data;
83                 }
84                 if (*data && (*(data + 1) == '\n' || (*data) == '\r')) {
85                     ++newlines;
86                     data += 1;
87                     if (*data != '\r')
88                         data += 1;
89                     continue;
90                 }
91             } else if (*data == '\r' && *(data + 1) == '\n') { // reduce \r\n to \n
92                 ++data;
93             }
94
95             char ch = *data;
96             if (ch == '\r') // os9: replace \r with \n
97                 ch = '\n';
98             *output = ch;
99             ++output;
100
101             if (*data == '\n') {
102                 // output additional newlines to keep the correct line-numbering
103                 // for the lines following the backslash-newline sequence(s)
104                 while (newlines) {
105                     *output = '\n';
106                     ++output;
107                     --newlines;
108                 }
109                 ++data;
110                 break;
111             }
112             ++data;
113         }
114     }
115     result.resize(output - result.constData());
116     return result;
117 }
118
119 bool Preprocessor::preprocessOnly = false;
120 void Preprocessor::skipUntilEndif()
121 {
122     while(index < symbols.size() - 1 && symbols.at(index).token != PP_ENDIF){
123         switch (symbols.at(index).token) {
124         case PP_IF:
125         case PP_IFDEF:
126         case PP_IFNDEF:
127             ++index;
128             skipUntilEndif();
129             break;
130         default:
131             ;
132         }
133         ++index;
134     }
135 }
136
137 bool Preprocessor::skipBranch()
138 {
139     while (index < symbols.size() - 1
140           && (symbols.at(index).token != PP_ENDIF
141                && symbols.at(index).token != PP_ELIF
142                && symbols.at(index).token != PP_ELSE)
143        ){
144         switch (symbols.at(index).token) {
145         case PP_IF:
146         case PP_IFDEF:
147         case PP_IFNDEF:
148             ++index;
149             skipUntilEndif();
150             break;
151         default:
152             ;
153         }
154         ++index;
155     }
156     return (index < symbols.size() - 1);
157 }
158
159
160 enum TokenizeMode { TokenizeCpp, TokenizePreprocessor, PreparePreprocessorStatement, TokenizePreprocessorStatement, TokenizeInclude };
161 static Symbols tokenize(const QByteArray &input, int lineNum = 1, TokenizeMode mode = TokenizeCpp)
162 {
163     Symbols symbols;
164     const char *begin = input.constData();
165     const char *data = begin;
166     while (*data) {
167         if (mode == TokenizeCpp) {
168             int column = 0;
169
170             const char *lexem = data;
171             int state = 0;
172             Token token = NOTOKEN;
173             for (;;) {
174                 if (static_cast<signed char>(*data) < 0) {
175                     ++data;
176                     continue;
177                 }
178                 int nextindex = keywords[state].next;
179                 int next = 0;
180                 if (*data == keywords[state].defchar)
181                     next = keywords[state].defnext;
182                 else if (!state || nextindex)
183                     next = keyword_trans[nextindex][(int)*data];
184                 if (!next)
185                     break;
186                 state = next;
187                 token = keywords[state].token;
188                 ++data;
189             }
190
191             // suboptimal, is_ident_char  should use a table
192             if (keywords[state].ident && is_ident_char(*data))
193                 token = keywords[state].ident;
194
195             if (token == NOTOKEN) {
196                 // an error really
197                 ++data;
198                 continue;
199             }
200
201             ++column;
202
203             if (token > SPECIAL_TREATMENT_MARK) {
204                 switch (token) {
205                 case QUOTE:
206                     data = skipQuote(data);
207                     token = STRING_LITERAL;
208                     // concatenate multi-line strings for easier
209                     // STRING_LITERAAL handling in moc
210                     if (!Preprocessor::preprocessOnly
211                         && !symbols.isEmpty()
212                         && symbols.last().token == STRING_LITERAL) {
213
214                         QByteArray newString = symbols.last().unquotedLexem();
215                         newString += input.mid(lexem - begin + 1, data - lexem - 2);
216                         newString.prepend('\"');
217                         newString.append('\"');
218                         symbols.last() = Symbol(symbols.last().lineNum,
219                                                 STRING_LITERAL,
220                                                 newString);
221                         continue;
222                     }
223                     break;
224                 case SINGLEQUOTE:
225                     while (*data && (*data != '\''
226                                      || (*(data-1)=='\\'
227                                          && *(data-2)!='\\')))
228                         ++data;
229                     if (*data)
230                         ++data;
231                     token = CHARACTER_LITERAL;
232                     break;
233                 case LANGLE_SCOPE:
234                     // split <:: into two tokens, < and ::
235                     token = LANGLE;
236                     data -= 2;
237                     break;
238                 case DIGIT:
239                     while (is_digit_char(*data))
240                         ++data;
241                     if (!*data || *data != '.') {
242                         token = INTEGER_LITERAL;
243                         if (data - lexem == 1 &&
244                             (*data == 'x' || *data == 'X')
245                             && *lexem == '0') {
246                             ++data;
247                             while (is_hex_char(*data))
248                                 ++data;
249                         }
250                         break;
251                     }
252                     token = FLOATING_LITERAL;
253                     ++data;
254                     // fall through
255                 case FLOATING_LITERAL:
256                     while (is_digit_char(*data))
257                         ++data;
258                     if (*data == '+' || *data == '-')
259                         ++data;
260                     if (*data == 'e' || *data == 'E') {
261                         ++data;
262                         while (is_digit_char(*data))
263                             ++data;
264                     }
265                     if (*data == 'f' || *data == 'F'
266                         || *data == 'l' || *data == 'L')
267                         ++data;
268                     break;
269                 case HASH:
270                     if (column == 1) {
271                         mode = PreparePreprocessorStatement;
272                         while (*data && (*data == ' ' || *data == '\t'))
273                             ++data;
274                         if (is_ident_char(*data))
275                             mode = TokenizePreprocessorStatement;
276                         continue;
277                     }
278                     break;
279                 case NEWLINE:
280                     ++lineNum;
281                     continue;
282                 case BACKSLASH:
283                 {
284                     const char *rewind = data;
285                     while (*data && (*data == ' ' || *data == '\t'))
286                         ++data;
287                     if (*data && *data == '\n') {
288                         ++data;
289                         continue;
290                     }
291                     data = rewind;
292                 } break;
293                 case CHARACTER:
294                     while (is_ident_char(*data))
295                         ++data;
296                     token = IDENTIFIER;
297                     break;
298                 case C_COMMENT:
299                     if (*data) {
300                         if (*data == '\n')
301                             ++lineNum;
302                         ++data;
303                         if (*data) {
304                             if (*data == '\n')
305                                 ++lineNum;
306                             ++data;
307                         }
308                     }
309                     while (*data && (*(data-1) != '/' || *(data-2) != '*')) {
310                         if (*data == '\n')
311                             ++lineNum;
312                         ++data;
313                     }
314                     token = WHITESPACE; // one comment, one whitespace
315                     // fall through;
316                 case WHITESPACE:
317                     if (column == 1)
318                         column = 0;
319                     while (*data && (*data == ' ' || *data == '\t'))
320                         ++data;
321                     if (Preprocessor::preprocessOnly) // tokenize whitespace
322                         break;
323                     continue;
324                 case CPP_COMMENT:
325                     while (*data && *data != '\n')
326                         ++data;
327                     continue; // ignore safely, the newline is a separator
328                 default:
329                     continue; //ignore
330                 }
331             }
332 #ifdef USE_LEXEM_STORE
333             if (!Preprocessor::preprocessOnly
334                 && token != IDENTIFIER
335                 && token != STRING_LITERAL
336                 && token != FLOATING_LITERAL
337                 && token != INTEGER_LITERAL)
338                 symbols += Symbol(lineNum, token);
339             else
340 #endif
341                 symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
342
343         } else { //   Preprocessor
344
345             const char *lexem = data;
346             int state = 0;
347             Token token = NOTOKEN;
348             if (mode == TokenizePreprocessorStatement) {
349                 state = pp_keyword_trans[0][(int)'#'];
350                 mode = TokenizePreprocessor;
351             }
352             for (;;) {
353                 if (static_cast<signed char>(*data) < 0) {
354                     ++data;
355                     continue;
356                 }
357
358                 int nextindex = pp_keywords[state].next;
359                 int next = 0;
360                 if (*data == pp_keywords[state].defchar)
361                     next = pp_keywords[state].defnext;
362                 else if (!state || nextindex)
363                     next = pp_keyword_trans[nextindex][(int)*data];
364                 if (!next)
365                     break;
366                 state = next;
367                 token = pp_keywords[state].token;
368                 ++data;
369             }
370             // suboptimal, is_ident_char  should use a table
371             if (pp_keywords[state].ident && is_ident_char(*data))
372                 token = pp_keywords[state].ident;
373
374             switch (token) {
375             case NOTOKEN:
376                 ++data;
377                 break;
378             case PP_IFDEF:
379                 symbols += Symbol(lineNum, PP_IF);
380                 symbols += Symbol(lineNum, PP_DEFINED);
381                 continue;
382             case PP_IFNDEF:
383                 symbols += Symbol(lineNum, PP_IF);
384                 symbols += Symbol(lineNum, PP_NOT);
385                 symbols += Symbol(lineNum, PP_DEFINED);
386                 continue;
387             case PP_INCLUDE:
388                 mode = TokenizeInclude;
389                 break;
390             case PP_QUOTE:
391                 data = skipQuote(data);
392                 token = PP_STRING_LITERAL;
393                 break;
394             case PP_SINGLEQUOTE:
395                 while (*data && (*data != '\''
396                                  || (*(data-1)=='\\'
397                                      && *(data-2)!='\\')))
398                     ++data;
399                 if (*data)
400                     ++data;
401                 token = PP_CHARACTER_LITERAL;
402                 break;
403             case PP_DIGIT:
404                 while (is_digit_char(*data))
405                     ++data;
406                 if (!*data || *data != '.') {
407                     token = PP_INTEGER_LITERAL;
408                     if (data - lexem == 1 &&
409                         (*data == 'x' || *data == 'X')
410                         && *lexem == '0') {
411                         ++data;
412                         while (is_hex_char(*data))
413                             ++data;
414                     }
415                     break;
416                 }
417                 token = PP_FLOATING_LITERAL;
418                 ++data;
419                 // fall through
420             case PP_FLOATING_LITERAL:
421                 while (is_digit_char(*data))
422                     ++data;
423                 if (*data == '+' || *data == '-')
424                     ++data;
425                 if (*data == 'e' || *data == 'E') {
426                     ++data;
427                     while (is_digit_char(*data))
428                         ++data;
429                 }
430                 if (*data == 'f' || *data == 'F'
431                     || *data == 'l' || *data == 'L')
432                     ++data;
433                 break;
434             case PP_CHARACTER:
435                 if (mode == PreparePreprocessorStatement) {
436                     // rewind entire token to begin
437                     data = lexem;
438                     mode = TokenizePreprocessorStatement;
439                     continue;
440                 }
441                 while (is_ident_char(*data))
442                     ++data;
443                 token = PP_IDENTIFIER;
444                 break;
445             case PP_C_COMMENT:
446                 if (*data) {
447                     if (*data == '\n')
448                         ++lineNum;
449                     ++data;
450                     if (*data) {
451                         if (*data == '\n')
452                             ++lineNum;
453                         ++data;
454                     }
455                 }
456                 while (*data && (*(data-1) != '/' || *(data-2) != '*')) {
457                     if (*data == '\n')
458                         ++lineNum;
459                     ++data;
460                 }
461                 token = PP_WHITESPACE; // one comment, one whitespace
462                 // fall through;
463             case PP_WHITESPACE:
464                 while (*data && (*data == ' ' || *data == '\t'))
465                     ++data;
466                 continue; // the preprocessor needs no whitespace
467             case PP_CPP_COMMENT:
468                 while (*data && *data != '\n')
469                     ++data;
470                 continue; // ignore safely, the newline is a separator
471             case PP_NEWLINE:
472                 ++lineNum;
473                 mode = TokenizeCpp;
474                 break;
475             case PP_BACKSLASH:
476             {
477                 const char *rewind = data;
478                 while (*data && (*data == ' ' || *data == '\t'))
479                     ++data;
480                 if (*data && *data == '\n') {
481                     ++data;
482                     continue;
483                 }
484                 data = rewind;
485             } break;
486             case PP_LANGLE:
487                 if (mode != TokenizeInclude)
488                     break;
489                 token = PP_STRING_LITERAL;
490                 while (*data && *data != '\n' && *(data-1) != '>')
491                     ++data;
492                 break;
493             default:
494                 break;
495             }
496             if (mode == PreparePreprocessorStatement)
497                 continue;
498 #ifdef USE_LEXEM_STORE
499             if (token != PP_IDENTIFIER
500                 && token != PP_STRING_LITERAL
501                 && token != PP_FLOATING_LITERAL
502                 && token != PP_INTEGER_LITERAL)
503                 symbols += Symbol(lineNum, token);
504             else
505 #endif
506                 symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
507         }
508     }
509     symbols += Symbol(); // eof symbol
510     return symbols;
511 }
512
513 void Preprocessor::substituteMacro(const MacroName &macro, Symbols &substituted, MacroSafeSet safeset)
514 {
515     Symbols saveSymbols = symbols;
516     int saveIndex = index;
517
518     symbols = macros.value(macro).symbols;
519     index = 0;
520
521     safeset += macro;
522     substituteUntilNewline(substituted, safeset);
523
524     symbols = saveSymbols;
525     index = saveIndex;
526 }
527
528
529
530 void Preprocessor::substituteUntilNewline(Symbols &substituted, MacroSafeSet safeset)
531 {
532     while (hasNext()) {
533         Token token = next();
534         if (token == PP_IDENTIFIER) {
535             MacroName macro = symbol();
536             if (macros.contains(macro) && !safeset.contains(macro)) {
537                 substituteMacro(macro, substituted, safeset);
538                 continue;
539             }
540         } else if (token == PP_DEFINED) {
541             bool braces = test(PP_LPAREN);
542             next(PP_IDENTIFIER);
543             Symbol definedOrNotDefined = symbol();
544             definedOrNotDefined.token = macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
545             substituted += definedOrNotDefined;
546             if (braces)
547                 test(PP_RPAREN);
548             continue;
549         } else if (token == PP_NEWLINE) {
550             substituted += symbol();
551             break;
552         }
553         substituted += symbol();
554     }
555 }
556
557
558 class PP_Expression : public Parser
559 {
560 public:
561     int value() { index = 0; return unary_expression_lookup() ?  conditional_expression() : 0; }
562
563     int conditional_expression();
564     int logical_OR_expression();
565     int logical_AND_expression();
566     int inclusive_OR_expression();
567     int exclusive_OR_expression();
568     int AND_expression();
569     int equality_expression();
570     int relational_expression();
571     int shift_expression();
572     int additive_expression();
573     int multiplicative_expression();
574     int unary_expression();
575     bool unary_expression_lookup();
576     int primary_expression();
577     bool primary_expression_lookup();
578 };
579
580 int PP_Expression::conditional_expression()
581 {
582     int value = logical_OR_expression();
583     if (test(PP_QUESTION)) {
584         int alt1 = conditional_expression();
585         int alt2 = test(PP_COLON) ? conditional_expression() : 0;
586         return value ? alt1 : alt2;
587     }
588     return value;
589 }
590
591 int PP_Expression::logical_OR_expression()
592 {
593     int value = logical_AND_expression();
594     if (test(PP_OROR))
595         return logical_OR_expression() || value;
596     return value;
597 }
598
599 int PP_Expression::logical_AND_expression()
600 {
601     int value = inclusive_OR_expression();
602     if (test(PP_ANDAND))
603         return logical_AND_expression() && value;
604     return value;
605 }
606
607 int PP_Expression::inclusive_OR_expression()
608 {
609     int value = exclusive_OR_expression();
610     if (test(PP_OR))
611         return value | inclusive_OR_expression();
612     return value;
613 }
614
615 int PP_Expression::exclusive_OR_expression()
616 {
617     int value = AND_expression();
618     if (test(PP_HAT))
619         return value ^ exclusive_OR_expression();
620     return value;
621 }
622
623 int PP_Expression::AND_expression()
624 {
625     int value = equality_expression();
626     if (test(PP_AND))
627         return value & AND_expression();
628     return value;
629 }
630
631 int PP_Expression::equality_expression()
632 {
633     int value = relational_expression();
634     switch (next()) {
635     case PP_EQEQ:
636         return value == equality_expression();
637     case PP_NE:
638         return value != equality_expression();
639     default:
640         prev();
641         return value;
642     }
643 }
644
645 int PP_Expression::relational_expression()
646 {
647     int value = shift_expression();
648     switch (next()) {
649     case PP_LANGLE:
650         return value < relational_expression();
651     case PP_RANGLE:
652         return value > relational_expression();
653     case PP_LE:
654         return value <= relational_expression();
655     case PP_GE:
656         return value >= relational_expression();
657     default:
658         prev();
659         return value;
660     }
661 }
662
663 int PP_Expression::shift_expression()
664 {
665     int value = additive_expression();
666     switch (next()) {
667     case PP_LTLT:
668         return value << shift_expression();
669     case PP_GTGT:
670         return value >> shift_expression();
671     default:
672         prev();
673         return value;
674     }
675 }
676
677 int PP_Expression::additive_expression()
678 {
679     int value = multiplicative_expression();
680     switch (next()) {
681     case PP_PLUS:
682         return value + additive_expression();
683     case PP_MINUS:
684         return value - additive_expression();
685     default:
686         prev();
687         return value;
688     }
689 }
690
691 int PP_Expression::multiplicative_expression()
692 {
693     int value = unary_expression();
694     switch (next()) {
695     case PP_STAR:
696         return value * multiplicative_expression();
697     case PP_PERCENT:
698     {
699         int remainder = multiplicative_expression();
700         return remainder ? value % remainder : 0;
701     }
702     case PP_SLASH:
703     {
704         int div = multiplicative_expression();
705         return div ? value / div : 0;
706     }
707     default:
708         prev();
709         return value;
710     };
711 }
712
713 int PP_Expression::unary_expression()
714 {
715     switch (next()) {
716     case PP_PLUS:
717         return unary_expression();
718     case PP_MINUS:
719         return -unary_expression();
720     case PP_NOT:
721         return !unary_expression();
722     case PP_TILDE:
723         return ~unary_expression();
724     case PP_MOC_TRUE:
725         return 1;
726     case PP_MOC_FALSE:
727         return 0;
728     default:
729         prev();
730         return primary_expression();
731     }
732 }
733
734 bool PP_Expression::unary_expression_lookup()
735 {
736     Token t = lookup();
737     return (primary_expression_lookup()
738             || t == PP_PLUS
739             || t == PP_MINUS
740             || t == PP_NOT
741             || t == PP_TILDE
742             || t == PP_DEFINED);
743 }
744
745 int PP_Expression::primary_expression()
746 {
747     int value;
748     if (test(PP_LPAREN)) {
749         value = conditional_expression();
750         test(PP_RPAREN);
751     } else {
752         next();
753         value = lexem().toInt(0, 0);
754     }
755     return value;
756 }
757
758 bool PP_Expression::primary_expression_lookup()
759 {
760     Token t = lookup();
761     return (t == PP_IDENTIFIER
762             || t == PP_INTEGER_LITERAL
763             || t == PP_FLOATING_LITERAL
764             || t == PP_MOC_TRUE
765             || t == PP_MOC_FALSE
766             || t == PP_LPAREN);
767 }
768
769 int Preprocessor::evaluateCondition()
770 {
771     PP_Expression expression;
772     expression.currentFilenames = currentFilenames;
773
774     substituteUntilNewline(expression.symbols);
775
776     return expression.value();
777 }
778
779 void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
780 {
781     currentFilenames.push(filename);
782     preprocessed.reserve(preprocessed.size() + symbols.size());
783     while (hasNext()) {
784         Token token = next();
785
786         switch (token) {
787         case PP_INCLUDE:
788         {
789             int lineNum = symbol().lineNum;
790             QByteArray include;
791             bool local = false;
792             if (test(PP_STRING_LITERAL)) {
793                 local = lexem().startsWith('\"');
794                 include = unquotedLexem();
795             } else
796                 continue;
797             until(PP_NEWLINE);
798
799             // #### stringery
800             QFileInfo fi;
801             if (local)
802                 fi.setFile(QFileInfo(QString::fromLocal8Bit(filename.constData())).dir(), QString::fromLocal8Bit(include.constData()));
803             for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) {
804                 const IncludePath &p = Preprocessor::includes.at(j);
805                 if (p.isFrameworkPath) {
806                     const int slashPos = include.indexOf('/');
807                     if (slashPos == -1)
808                         continue;
809                     QByteArray frameworkCandidate = include.left(slashPos);
810                     frameworkCandidate.append(".framework/Headers/");
811                     fi.setFile(QString::fromLocal8Bit(QByteArray(p.path + '/' + frameworkCandidate).constData()), QString::fromLocal8Bit(include.mid(slashPos + 1).constData()));
812                 } else {
813                     fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(include.constData()));
814                 }
815                 // try again, maybe there's a file later in the include paths with the same name
816                 // (186067)
817                 if (fi.isDir()) {
818                     fi = QFileInfo();
819                     continue;
820                 }
821             }
822
823             if (!fi.exists() || fi.isDir())
824                 continue;
825             include = fi.canonicalFilePath().toLocal8Bit();
826
827             if (Preprocessor::preprocessedIncludes.contains(include))
828                 continue;
829             Preprocessor::preprocessedIncludes.insert(include);
830
831             QFile file(QString::fromLocal8Bit(include.constData()));
832             if (!file.open(QFile::ReadOnly))
833                 continue;
834
835             QByteArray input = file.readAll();
836             file.close();
837             if (input.isEmpty())
838                 continue;
839
840             Symbols saveSymbols = symbols;
841             int saveIndex = index;
842
843             // phase 1: get rid of backslash-newlines
844             input = cleaned(input);
845
846             // phase 2: tokenize for the preprocessor
847             symbols = tokenize(input);
848             input.clear();
849
850             index = 0;
851
852             // phase 3: preprocess conditions and substitute macros
853             preprocessed += Symbol(0, MOC_INCLUDE_BEGIN, include);
854             preprocess(include, preprocessed);
855             preprocessed += Symbol(lineNum, MOC_INCLUDE_END, include);
856
857             symbols = saveSymbols;
858             index = saveIndex;
859             continue;
860         }
861         case PP_DEFINE:
862         {
863             next(IDENTIFIER);
864             QByteArray name = lexem();
865             int start = index;
866             until(PP_NEWLINE);
867             Macro macro;
868             macro.symbols.reserve(index - start - 1);
869             for (int i = start; i < index - 1; ++i)
870                 macro.symbols += symbols.at(i);
871             macros.insert(name, macro);
872             continue;
873         }
874         case PP_UNDEF: {
875             next(IDENTIFIER);
876             QByteArray name = lexem();
877             until(PP_NEWLINE);
878             macros.remove(name);
879             continue;
880         }
881         case PP_IDENTIFIER:
882         {
883 //             if (macros.contains(symbol()))
884 //                 ;
885         }
886             // we _could_ easily substitute macros by the following
887             // four lines, but we choose not to.
888             /*
889             if (macros.contains(sym.lexem())) {
890                 preprocessed += substitute(macros, symbols, i);
891                 continue;
892             }
893             */
894             break;
895         case PP_HASH:
896             until(PP_NEWLINE);
897             continue; // skip unknown preprocessor statement
898         case PP_IFDEF:
899         case PP_IFNDEF:
900         case PP_IF:
901             while (!evaluateCondition()) {
902                 if (!skipBranch())
903                     break;
904                 if (test(PP_ELIF)) {
905                 } else {
906                     until(PP_NEWLINE);
907                     break;
908                 }
909             }
910             continue;
911         case PP_ELIF:
912         case PP_ELSE:
913             skipUntilEndif();
914             // fall through
915         case PP_ENDIF:
916             until(PP_NEWLINE);
917             continue;
918         case PP_NEWLINE:
919             continue;
920         case SIGNALS:
921         case SLOTS: {
922             Symbol sym = symbol();
923             if (macros.contains("QT_NO_KEYWORDS"))
924                 sym.token = IDENTIFIER;
925             else
926                 sym.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN);
927             preprocessed += sym;
928         } continue;
929         default:
930             break;
931         }
932         preprocessed += symbol();
933     }
934
935     currentFilenames.pop();
936 }
937
938 Symbols Preprocessor::preprocessed(const QByteArray &filename, FILE *file)
939 {
940     QFile qfile;
941     qfile.open(file, QFile::ReadOnly);
942     return preprocessed(filename, &qfile);
943 }
944
945 Symbols Preprocessor::preprocessed(const QByteArray &filename, QIODevice *file)
946 {
947     QByteArray input = file->readAll();
948     if (input.isEmpty())
949         return symbols;
950
951     // phase 1: get rid of backslash-newlines
952     input = cleaned(input);
953
954     // phase 2: tokenize for the preprocessor
955     symbols = tokenize(input);
956
957 #if 0
958     for (int j = 0; j < symbols.size(); ++j)
959         fprintf(stderr, "line %d: %s(%s)\n",
960                symbols[j].lineNum,
961                symbols[j].lexem().constData(),
962                tokenTypeName(symbols[j].token));
963 #endif
964
965     // phase 3: preprocess conditions and substitute macros
966     Symbols result;
967     preprocess(filename, result);
968
969 #if 0
970     for (int j = 0; j < result.size(); ++j)
971         fprintf(stderr, "line %d: %s(%s)\n",
972                result[j].lineNum,
973                result[j].lexem().constData(),
974                tokenTypeName(result[j].token));
975 #endif
976
977     return result;
978 }
979
980 void Preprocessor::until(Token t)
981 {
982     while(hasNext() && next() != t)
983         ;
984 }
985
986 QT_END_NAMESPACE