OSDN Git Service

fix compile problem
[tjqt4port/tj2qt4.git] / taskjuggler / FileInfo.cpp
1 /*
2  * FileInfo.cpp - TaskJuggler
3  *
4  * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006
5  * Chris Schlaeger <cs@kde.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of version 2 of the GNU General Public License as
9  * published by the Free Software Foundation.
10  *
11  * $Id$
12  */
13
14 #include "FileInfo.h"
15
16 #include <ctype.h>
17 #include <stdlib.h>
18
19 #include <QTextStream>
20
21 #include "TjMessageHandler.h"
22 #include "tjlib-internal.h"
23 #include "ProjectFile.h"
24 #include "debug.h"
25
26 FileInfo::FileInfo(ProjectFile* p, const QString& file, const QString& tp) :
27     FileToken(file, tp),
28     pf(p),
29     oldLineBuf(),
30     oldLine(0)
31 { }
32
33 bool
34 FileInfo::open()
35 {
36     if (m_file.right(2) == "/.")
37     {
38         m_f.reset(new QTextStream(stdin, QIODevice::ReadOnly));
39         m_fh = stdin;
40     }
41     else
42     {
43         if ((m_fh = fopen(m_file, "r")) == 0)
44             return false;
45         m_f.reset(new QTextStream(m_fh, QIODevice::ReadOnly));
46     }
47
48     if (DEBUGLEVEL > 0)
49         tjWarning(i18n("Processing file \'%1\'").arg(m_file));
50
51     m_lineBuf = oldLineBuf = QString::null;
52     m_currLine = oldLine = 1;
53     return true;
54 }
55
56 bool
57 FileInfo::close()
58 {
59     if (m_fh == stdin)
60         return true;
61
62     if (fclose(m_fh) == EOF)
63         return false;
64
65     return true;
66 }
67
68 QChar
69 FileInfo::getC(bool expandMacros)
70 {
71  BEGIN:
72     QChar c;
73     if (m_ungetBuf.isEmpty())
74     {
75         if (feof(m_fh))
76             c = QChar(EOFile);
77         else
78         {
79             *m_f >> c;
80             if (c == QChar('\r'))
81             {
82                 if (!feof(m_fh))
83                 {
84                     // Test for CR/LF Windows line breaks.
85                     QChar cb;
86                     *m_f >> cb;
87                     if (cb != QChar('\n'))
88                     {
89                         // Probably a MacOS LF only line break
90                         m_ungetBuf.append(cb);
91                     }
92                 }
93                 c = QChar('\n');
94             }
95         }
96     }
97     else
98     {
99         c = m_ungetBuf.last();
100         m_ungetBuf.pop_back();
101         if (c.unicode() == EOMacro)
102         {
103             m_macroStack.removeLast();
104             goto BEGIN;
105         }
106     }
107     oldLineBuf = m_lineBuf;
108     m_lineBuf += c;
109
110     if (expandMacros)
111     {
112         if (c == '$')
113         {
114             QChar d;
115             if ((d = getC(false)) == '{')
116             {
117                 // remove ${ from m_lineBuf;
118                 oldLineBuf = m_lineBuf;
119                 m_lineBuf = m_lineBuf.left(m_lineBuf.length() - 2);
120                 readMacroCall();
121                 goto BEGIN;
122             }
123             else if (d == '(')
124             {
125                 // remove $( from m_lineBuf;
126                 oldLineBuf = m_lineBuf;
127                 m_lineBuf = m_lineBuf.left(m_lineBuf.length() - 2);
128                 readEnvironment();
129                 goto BEGIN;
130             }
131             else if (d == '$')
132             {
133                 QChar e;
134                 if ((e = getC(false)) == '{')
135                 {
136                     // Convert "$${" into "%{"
137                     c = '%';
138                 }
139                 // $$ escapes $, so discard 2nd $
140                 ungetC(e);
141             }
142             else
143                 ungetC(d);
144         }
145     }
146
147     return c;
148 }
149
150 void
151 FileInfo::ungetC(QChar c)
152 {
153     oldLineBuf = m_lineBuf;
154     m_lineBuf = m_lineBuf.left(m_lineBuf.length() - 1);
155     m_ungetBuf.append(c);
156 }
157
158 TokenType
159 FileInfo::nextToken(QString& token)
160 {
161     if (m_tokenTypeBuf != INVALID)
162     {
163         token = m_tokenBuf;
164         TokenType tt = m_tokenTypeBuf;
165         m_tokenTypeBuf = INVALID;
166         return tt;
167     }
168
169     token = "";
170
171     // skip blanks and comments
172     for ( ; ; )
173     {
174         QChar c = getC();
175         if (c.unicode() == EOFile)
176             return EndOfFile;
177         switch (c.unicode())
178         {
179         case ' ':
180         case '\t':
181             break;
182         case '/':
183             /* This code skips c-style comments like the one you are just
184              * reading. */
185             if ((c = getC(false)) == '*')
186             {
187                 do
188                 {
189                     if (c == '\n')
190                     {
191                         oldLine = m_currLine;
192                         m_currLine++;
193                     }
194                     while ((c = getC(false)) != '*')
195                     {
196                         if (c == '\n')
197                         {
198                             oldLine = m_currLine;
199                             m_currLine++;
200                         }
201                         else if (c.unicode() == EOFile)
202                         {
203                             errorMessage(i18n("Unterminated comment"));
204                             return EndOfFile;
205                         }
206                     }
207                 } while ((c = getC(false)) != '/');
208                 break;
209             }
210             // This code skips C++-style comments like the one you are
211             // reading here.
212             else if (c != '/')
213             {
214                 ungetC(c);
215                 ungetC('/');
216                 goto BLANKS_DONE;
217             }
218             // break missing on purpose
219         case '#':   // Comments start with '#' and reach towards end of line
220             while ((c = getC(false)) != '\n' && c.unicode() != EOFile)
221                 ;
222             if (c.unicode() == EOFile)
223                 return EndOfFile;
224             // break missing on purpose
225         case '\n':
226             // Increase line counter only when not replaying a macro.
227             if (m_macroStack.isEmpty())
228             {
229                 oldLine = m_currLine;
230                 m_currLine++;
231             }
232             oldLineBuf = m_lineBuf;
233             m_lineBuf = "";
234             break;
235         default:
236             ungetC(c);
237             goto BLANKS_DONE;
238         }
239     }
240  BLANKS_DONE:
241
242     // analyse non blank characters
243     for ( ; ; )
244     {
245         QChar c = getC();
246         if (c.unicode() == EOFile)
247         {
248             errorMessage(i18n("Unexpected end of file"));
249             return EndOfFile;
250         }
251         else if (c.isLetter() || (c == '_') || (c == '!'))
252         {
253             token += c;
254             while ((c = getC()).unicode() != EOFile &&
255                    (c.isLetterOrNumber() || (c == '_') || (c == '.') || (c == '!')))
256                 token += c;
257             ungetC(c);
258             if (token[0] == '!')
259                 return RELATIVE_ID;
260             if (token.contains('.'))
261                 return ABSOLUTE_ID;
262             else
263                 return ID;
264         }
265         else if (c.isDigit())
266         {
267             // read first number (maybe a year)
268             token += c;
269             while ((c = getC()).unicode() != EOFile && c.isDigit())
270                 token += c;
271             if (c == '-')
272             {
273                 // this must be a ISO date yyyy-mm-dd[[-hh:mm:[ss]]-TZ]
274                 getDateFragment(token, c);
275                 if (c != '-')
276                 {
277                     errorMessage(i18n("Corrupted date"));
278                     return EndOfFile;
279                 }
280                 getDateFragment(token, c);
281                 if (c == '-')
282                 {
283                     getDateFragment(token, c);
284                     if (c != ':')
285                     {
286                         errorMessage(i18n("Corrupted date"));
287                         return EndOfFile;
288                     }
289                     getDateFragment(token, c);
290                     if (c == ':')
291                         getDateFragment(token, c);
292                 }
293                 int i = 0;
294                 if (c == '-')
295                 {
296                     /* Timezone can either be a name (ref.
297                      * Utility::timezone2tz) or GMT[+-]hh:mm */
298                     token += c;
299                     while ((c = getC()).unicode() != EOFile &&
300                            (c.isLetterOrNumber() || c == '+' || c == '-' || c == ':')
301                            && i++ < 9)
302                         token += c;
303                 }
304                 ungetC(c);
305                 return DATE;
306             }
307             else if (c == '.')
308             {
309                 // must be a real number
310                 token += c;
311                 while ((c = getC()).unicode() != EOFile && c.isDigit())
312                     token += c;
313                 ungetC(c);
314                 return REAL;
315             }
316             else if (c == ':')
317             {
318                 // must be a time (HH:MM)
319                 token += c;
320                 for (int i = 0; i < 2; i++)
321                 {
322                     if ((c = getC()).unicode() != EOFile && c.isDigit())
323                         token += c;
324                     else
325                     {
326                         errorMessage(i18n("2 digits minutes expected"));
327                         return EndOfFile;
328                     }
329                 }
330                 return HOUR;
331             }
332             else
333             {
334                 ungetC(c);
335                 return INTEGER;
336             }
337         }
338         else if (c == '\'' || c == '\"')
339         {
340             // single or double quoted string
341             QChar delimiter = c;
342             bool escape = false;
343             while ((c = getC()).unicode() != EOFile &&
344                    (escape || (c != delimiter)))
345             {
346                 if ((c == '\n') && m_macroStack.isEmpty())
347                 {
348                     oldLine = m_currLine;
349                     m_currLine++;
350                 }
351                 if (c == '\\' && !escape)
352                     escape = true;
353                 else
354                 {
355                     escape = false;
356                     token += c;
357                 }
358             }
359             if (c.unicode() == EOFile)
360             {
361                 errorMessage(i18n("Non terminated string"));
362                 return EndOfFile;
363             }
364             return STRING;
365         }
366         else if (c == '[')
367         {
368             token = "";
369             int nesting = 0;
370             while ((c = getC(false)).unicode() != EOFile &&
371                    (c != ']' || nesting > 0))
372             {
373                 if (c == '[')
374                     nesting++;
375                 else if (c == ']')
376                     nesting--;
377                 if (c == '\n')
378                 {
379                     oldLine = m_currLine;
380                     m_currLine++; // m_macroStack.isEmpty ??
381                 }
382                 token += c;
383             }
384             if (c.unicode() == EOFile)
385             {
386                 errorMessage(i18n("Non terminated macro definition"));
387                 return EndOfFile;
388             }
389             return MacroBody;
390         }
391         else
392         {
393             token += c;
394             switch (c.unicode())
395             {
396             case '{':
397                 return LBRACE;
398             case '}':
399                 return RBRACE;
400             case '(':
401                 return LBRACKET;
402             case ')':
403                 return RBRACKET;
404             case ',':
405                 return COMMA;
406             case '%':
407                 return PERCENT;
408             case '~':
409                 return TILDE;
410             case ':':
411                 return COLON;
412             case '?':
413                 return QUESTIONMARK;
414             case '+':
415                 return PLUS;
416             case '-':
417                 return MINUS;
418             case '&':
419                 return AND;
420             case '|':
421                 return OR;
422             case '>':
423             {
424                 if ((c = getC()) == '=')
425                 {
426                     token += c;
427                     return GREATEROREQUAL;
428                 }
429                 ungetC(c);
430                 return GREATER;
431             }
432             case '<':
433             {
434                 if ((c = getC()) == '=')
435                 {
436                     token += c;
437                     return SMALLEROREQUAL;
438                 }
439                 ungetC(c);
440                 return SMALLER;
441             }
442             case '=':
443                 return EQUAL;
444             case '*':
445                 return STAR;
446             default:
447                 errorMessage(i18n("Illegal character '%1'").arg(c));
448                 return INVALID;
449             }
450         }
451     }
452 }
453
454 void
455 FileInfo::errorMessage(const QString& msg)
456 {
457     if (m_macroStack.isEmpty())
458     {
459         if (m_tokenTypeBuf == INVALID)
460             TJMH.errorMessage(QString("%1\n%2").arg(msg)
461                               .arg(cleanupLine(m_lineBuf)),
462                               m_file, m_currLine);
463         else
464             TJMH.errorMessage(QString("%1\n%2").arg(msg)
465                               .arg(cleanupLine(oldLineBuf)),
466                               m_file, oldLine);
467     }
468     else
469     {
470         QString stackDump;
471         int i = 0;
472         QString file;
473         int line = 0;
474         for (Q3PtrListIterator<Macro> mli(m_macroStack); *mli; ++mli, ++i)
475         {
476             stackDump += "\n  ${" + (*mli)->getName() + " ... }";
477
478             file = (*mli)->getFile();
479             line = (*mli)->getLine();
480         }
481         TJMH.errorMessage(i18n("Error in expanded macro\n%1\n%2"
482                                "\nThis is the macro call stack:%3").
483                           arg(msg).arg(cleanupLine(m_lineBuf)).arg(stackDump),
484                           file, line);
485     }
486 }
487
488 void
489 FileInfo::warningMessage(const QString& msg)
490 {
491     if (m_macroStack.isEmpty())
492     {
493         if (m_tokenTypeBuf == INVALID)
494             TJMH.warningMessage(QString("%1\n%2").arg(msg)
495                                 .arg(cleanupLine(m_lineBuf)),
496                                 m_file, m_currLine);
497         else
498             TJMH.warningMessage(QString("%1\n%2").arg(msg)
499                                 .arg(cleanupLine(oldLineBuf)),
500                                 m_file, oldLine);
501     }
502     else
503     {
504         QString stackDump;
505         int i = 0;
506         QString file;
507         int line = 0;
508         for (Q3PtrListIterator<Macro> mli(m_macroStack); *mli; ++mli, ++i)
509         {
510             stackDump += "\n  ${" + (*mli)->getName() + " ... }";
511
512             file = (*mli)->getFile();
513             line = (*mli)->getLine();
514         }
515         TJMH.warningMessage(i18n("Warning in expanded macro\n%1\n%2"
516                                  "\nThis is the macro call stack:%3").
517                             arg(msg).arg(cleanupLine(m_lineBuf)).arg(stackDump),
518                             file, line);
519     }
520 }
521
522 void FileInfo::setLocation(const QString& df, int dl)
523 {
524     pf->getMacros().setLocation(df, dl);
525 }
526
527 QString FileInfo::resolve(const QStringList* argList)
528 {
529     return pf->getMacros().resolve(argList);
530 }
531
532 Macro* FileInfo::getMacro(const QString& name) const
533 {
534     return pf->getMacros().getMacro(name);
535 }