Diffstat (limited to 'development/translation/opie-lupdate/fetchtr.cpp') (more/less context) (show whitespace changes)
-rw-r--r-- | development/translation/opie-lupdate/fetchtr.cpp | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/development/translation/opie-lupdate/fetchtr.cpp b/development/translation/opie-lupdate/fetchtr.cpp new file mode 100644 index 0000000..eb25555 --- a/dev/null +++ b/development/translation/opie-lupdate/fetchtr.cpp | |||
@@ -0,0 +1,784 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <metatranslator.h> | ||
22 | |||
23 | #include <qfile.h> | ||
24 | #include <qregexp.h> | ||
25 | #include <qstring.h> | ||
26 | #include <qtextstream.h> | ||
27 | #include <qvaluestack.h> | ||
28 | #include <qxml.h> | ||
29 | |||
30 | #include <ctype.h> | ||
31 | #include <errno.h> | ||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | |||
35 | /* qmake ignore Q_OBJECT */ | ||
36 | |||
37 | static const char MagicComment[] = "TRANSLATOR "; | ||
38 | |||
39 | static QMap<QCString, int> needs_Q_OBJECT; | ||
40 | static QMap<QCString, int> lacks_Q_OBJECT; | ||
41 | |||
42 | /* | ||
43 | The first part of this source file is the C++ tokenizer. We skip | ||
44 | most of C++; the only tokens that interest us are defined here. | ||
45 | Thus, the code fragment | ||
46 | |||
47 | int main() | ||
48 | { | ||
49 | printf( "Hello, world!\n" ); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | is broken down into the following tokens (Tok_ omitted): | ||
54 | |||
55 | Ident Ident LeftParen RightParen | ||
56 | LeftBrace | ||
57 | Ident LeftParen String RightParen Semicolon | ||
58 | return Semicolon | ||
59 | RightBrace. | ||
60 | |||
61 | The 0 doesn't produce any token. | ||
62 | */ | ||
63 | |||
64 | enum { Tok_Eof, Tok_class, Tok_namespace, Tok_return, Tok_tr, | ||
65 | Tok_trUtf8, Tok_translate, Tok_Q_OBJECT, Tok_Ident, | ||
66 | Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, | ||
67 | Tok_Gulbrandsen, Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen, | ||
68 | Tok_RightParen, Tok_Comma, Tok_Semicolon }; | ||
69 | |||
70 | /* | ||
71 | The tokenizer maintains the following global variables. The names | ||
72 | should be self-explanatory. | ||
73 | */ | ||
74 | static QCString yyFileName; | ||
75 | static int yyCh; | ||
76 | static char yyIdent[128]; | ||
77 | static size_t yyIdentLen; | ||
78 | static char yyComment[65536]; | ||
79 | static size_t yyCommentLen; | ||
80 | static char yyString[16384]; | ||
81 | static size_t yyStringLen; | ||
82 | static QValueStack<int> yySavedBraceDepth; | ||
83 | static int yyBraceDepth; | ||
84 | static int yyParenDepth; | ||
85 | static int yyLineNo; | ||
86 | static int yyCurLineNo; | ||
87 | |||
88 | // the file to read from (if reading from a file) | ||
89 | static FILE *yyInFile; | ||
90 | |||
91 | // the string to read from and current position in the string (otherwise) | ||
92 | static QString yyInStr; | ||
93 | static int yyInPos; | ||
94 | |||
95 | static int (*getChar)(); | ||
96 | |||
97 | static int getCharFromFile() | ||
98 | { | ||
99 | int c = getc( yyInFile ); | ||
100 | if ( c == '\n' ) | ||
101 | yyCurLineNo++; | ||
102 | return c; | ||
103 | } | ||
104 | |||
105 | static int getCharFromString() | ||
106 | { | ||
107 | if ( yyInPos == (int) yyInStr.length() ) { | ||
108 | return EOF; | ||
109 | } else { | ||
110 | return yyInStr[yyInPos++].latin1(); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static void startTokenizer( const char *fileName, int (*getCharFunc)() ) | ||
115 | { | ||
116 | yyInPos = 0; | ||
117 | getChar = getCharFunc; | ||
118 | |||
119 | yyFileName = fileName; | ||
120 | yyCh = getChar(); | ||
121 | yySavedBraceDepth.clear(); | ||
122 | yyBraceDepth = 0; | ||
123 | yyParenDepth = 0; | ||
124 | yyCurLineNo = 1; | ||
125 | } | ||
126 | |||
127 | static int getToken() | ||
128 | { | ||
129 | const char tab[] = "abfnrtv"; | ||
130 | const char backTab[] = "\a\b\f\n\r\t\v"; | ||
131 | uint n; | ||
132 | |||
133 | yyIdentLen = 0; | ||
134 | yyCommentLen = 0; | ||
135 | yyStringLen = 0; | ||
136 | |||
137 | while ( yyCh != EOF ) { | ||
138 | yyLineNo = yyCurLineNo; | ||
139 | |||
140 | if ( isalpha(yyCh) || yyCh == '_' ) { | ||
141 | do { | ||
142 | if ( yyIdentLen < sizeof(yyIdent) - 1 ) | ||
143 | yyIdent[yyIdentLen++] = (char) yyCh; | ||
144 | yyCh = getChar(); | ||
145 | } while ( isalnum(yyCh) || yyCh == '_' ); | ||
146 | yyIdent[yyIdentLen] = '\0'; | ||
147 | |||
148 | switch ( yyIdent[0] ) { | ||
149 | case 'Q': | ||
150 | if ( strcmp(yyIdent + 1, "_OBJECT") == 0 ) { | ||
151 | return Tok_Q_OBJECT; | ||
152 | } else if ( strcmp(yyIdent + 1, "T_TR_NOOP") == 0 ) { | ||
153 | return Tok_tr; | ||
154 | } else if ( strcmp(yyIdent + 1, "T_TRANSLATE_NOOP") == 0 ) { | ||
155 | return Tok_translate; | ||
156 | } | ||
157 | break; | ||
158 | case 'T': | ||
159 | // TR() for when all else fails | ||
160 | if ( qstricmp(yyIdent + 1, "R") == 0 ) | ||
161 | return Tok_tr; | ||
162 | break; | ||
163 | case 'c': | ||
164 | if ( strcmp(yyIdent + 1, "lass") == 0 ) | ||
165 | return Tok_class; | ||
166 | break; | ||
167 | case 'n': | ||
168 | if ( strcmp(yyIdent + 1, "amespace") == 0 ) | ||
169 | return Tok_namespace; | ||
170 | break; | ||
171 | case 'r': | ||
172 | if ( strcmp(yyIdent + 1, "eturn") == 0 ) | ||
173 | return Tok_return; | ||
174 | break; | ||
175 | case 's': | ||
176 | if ( strcmp(yyIdent + 1, "truct") == 0 ) | ||
177 | return Tok_class; | ||
178 | break; | ||
179 | case 't': | ||
180 | if ( strcmp(yyIdent + 1, "r") == 0 ) { | ||
181 | return Tok_tr; | ||
182 | } else if ( qstrcmp(yyIdent + 1, "rUtf8") == 0 ) { | ||
183 | return Tok_trUtf8; | ||
184 | } else if ( qstrcmp(yyIdent + 1, "ranslate") == 0 ) { | ||
185 | return Tok_translate; | ||
186 | } | ||
187 | } | ||
188 | return Tok_Ident; | ||
189 | } else { | ||
190 | switch ( yyCh ) { | ||
191 | case '#': | ||
192 | /* | ||
193 | Early versions of lupdate complained about | ||
194 | unbalanced braces in the following code: | ||
195 | |||
196 | #ifdef ALPHA | ||
197 | while ( beta ) { | ||
198 | #else | ||
199 | while ( gamma ) { | ||
200 | #endif | ||
201 | delta; | ||
202 | } | ||
203 | |||
204 | The code contains, indeed, two opening braces for | ||
205 | one closing brace; yet there's no reason to panic. | ||
206 | |||
207 | The solution is to remember yyBraceDepth as it was | ||
208 | when #if, #ifdef or #ifndef was met, and to set | ||
209 | yyBraceDepth to that value when meeting #elif or | ||
210 | #else. | ||
211 | */ | ||
212 | do { | ||
213 | yyCh = getChar(); | ||
214 | } while ( isspace(yyCh) && yyCh != '\n' ); | ||
215 | |||
216 | switch ( yyCh ) { | ||
217 | case 'i': | ||
218 | yyCh = getChar(); | ||
219 | if ( yyCh == 'f' ) { | ||
220 | // if, ifdef, ifndef | ||
221 | yySavedBraceDepth.push( yyBraceDepth ); | ||
222 | } | ||
223 | break; | ||
224 | case 'e': | ||
225 | yyCh = getChar(); | ||
226 | if ( yyCh == 'l' ) { | ||
227 | // elif, else | ||
228 | if ( !yySavedBraceDepth.isEmpty() ) | ||
229 | yyBraceDepth = yySavedBraceDepth.top(); | ||
230 | } else if ( yyCh == 'n' ) { | ||
231 | // endif | ||
232 | if ( !yySavedBraceDepth.isEmpty() ) | ||
233 | yySavedBraceDepth.pop(); | ||
234 | } | ||
235 | } | ||
236 | while ( isalnum(yyCh) || yyCh == '_' ) | ||
237 | yyCh = getChar(); | ||
238 | break; | ||
239 | case '/': | ||
240 | yyCh = getChar(); | ||
241 | if ( yyCh == '/' ) { | ||
242 | do { | ||
243 | yyCh = getChar(); | ||
244 | } while ( yyCh != EOF && yyCh != '\n' ); | ||
245 | } else if ( yyCh == '*' ) { | ||
246 | bool metAster = FALSE; | ||
247 | bool metAsterSlash = FALSE; | ||
248 | |||
249 | while ( !metAsterSlash ) { | ||
250 | yyCh = getChar(); | ||
251 | if ( yyCh == EOF ) { | ||
252 | fprintf( stderr, | ||
253 | "%s: Unterminated C++ comment starting at" | ||
254 | " line %d\n", | ||
255 | (const char *) yyFileName, yyLineNo ); | ||
256 | yyComment[yyCommentLen] = '\0'; | ||
257 | return Tok_Comment; | ||
258 | } | ||
259 | if ( yyCommentLen < sizeof(yyComment) - 1 ) | ||
260 | yyComment[yyCommentLen++] = (char) yyCh; | ||
261 | |||
262 | if ( yyCh == '*' ) | ||
263 | metAster = TRUE; | ||
264 | else if ( metAster && yyCh == '/' ) | ||
265 | metAsterSlash = TRUE; | ||
266 | else | ||
267 | metAster = FALSE; | ||
268 | } | ||
269 | yyCh = getChar(); | ||
270 | yyCommentLen -= 2; | ||
271 | yyComment[yyCommentLen] = '\0'; | ||
272 | return Tok_Comment; | ||
273 | } | ||
274 | break; | ||
275 | case '"': | ||
276 | yyCh = getChar(); | ||
277 | |||
278 | while ( yyCh != EOF && yyCh != '\n' && yyCh != '"' ) { | ||
279 | if ( yyCh == '\\' ) { | ||
280 | yyCh = getChar(); | ||
281 | |||
282 | if ( yyCh == '\n' ) { | ||
283 | yyCh = getChar(); | ||
284 | } else if ( yyCh == 'x' ) { | ||
285 | QCString hex = "0"; | ||
286 | |||
287 | yyCh = getChar(); | ||
288 | while ( isxdigit(yyCh) ) { | ||
289 | hex += (char) yyCh; | ||
290 | yyCh = getChar(); | ||
291 | } | ||
292 | sscanf( hex, "%x", &n ); | ||
293 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
294 | yyString[yyStringLen++] = (char) n; | ||
295 | } else if ( yyCh >= '0' && yyCh < '8' ) { | ||
296 | QCString oct = ""; | ||
297 | |||
298 | do { | ||
299 | oct += (char) yyCh; | ||
300 | yyCh = getChar(); | ||
301 | } while ( yyCh >= '0' && yyCh < '8' ); | ||
302 | sscanf( oct, "%o", &n ); | ||
303 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
304 | yyString[yyStringLen++] = (char) n; | ||
305 | } else { | ||
306 | const char *p = strchr( tab, yyCh ); | ||
307 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
308 | yyString[yyStringLen++] = ( p == 0 ) ? | ||
309 | (char) yyCh : backTab[p - tab]; | ||
310 | yyCh = getChar(); | ||
311 | } | ||
312 | } else { | ||
313 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
314 | yyString[yyStringLen++] = (char) yyCh; | ||
315 | yyCh = getChar(); | ||
316 | } | ||
317 | } | ||
318 | yyString[yyStringLen] = '\0'; | ||
319 | |||
320 | if ( yyCh != '"' ) | ||
321 | qWarning( "%s:%d: Unterminated C++ string", | ||
322 | (const char *) yyFileName, yyLineNo ); | ||
323 | |||
324 | if ( yyCh == EOF ) { | ||
325 | return Tok_Eof; | ||
326 | } else { | ||
327 | yyCh = getChar(); | ||
328 | return Tok_String; | ||
329 | } | ||
330 | break; | ||
331 | case '-': | ||
332 | yyCh = getChar(); | ||
333 | if ( yyCh == '>' ) { | ||
334 | yyCh = getChar(); | ||
335 | return Tok_Arrow; | ||
336 | } | ||
337 | break; | ||
338 | case ':': | ||
339 | yyCh = getChar(); | ||
340 | if ( yyCh == ':' ) { | ||
341 | yyCh = getChar(); | ||
342 | return Tok_Gulbrandsen; | ||
343 | } | ||
344 | return Tok_Colon; | ||
345 | case '\'': | ||
346 | yyCh = getChar(); | ||
347 | if ( yyCh == '\\' ) | ||
348 | yyCh = getChar(); | ||
349 | |||
350 | do { | ||
351 | yyCh = getChar(); | ||
352 | } while ( yyCh != EOF && yyCh != '\'' ); | ||
353 | yyCh = getChar(); | ||
354 | break; | ||
355 | case '{': | ||
356 | yyBraceDepth++; | ||
357 | yyCh = getChar(); | ||
358 | return Tok_LeftBrace; | ||
359 | case '}': | ||
360 | yyBraceDepth--; | ||
361 | yyCh = getChar(); | ||
362 | return Tok_RightBrace; | ||
363 | case '(': | ||
364 | yyParenDepth++; | ||
365 | yyCh = getChar(); | ||
366 | return Tok_LeftParen; | ||
367 | case ')': | ||
368 | yyParenDepth--; | ||
369 | yyCh = getChar(); | ||
370 | return Tok_RightParen; | ||
371 | case ',': | ||
372 | yyCh = getChar(); | ||
373 | return Tok_Comma; | ||
374 | case ';': | ||
375 | yyCh = getChar(); | ||
376 | return Tok_Semicolon; | ||
377 | default: | ||
378 | yyCh = getChar(); | ||
379 | } | ||
380 | } | ||
381 | } | ||
382 | return Tok_Eof; | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | The second part of this source file is the parser. It accomplishes | ||
387 | a very easy task: It finds all strings inside a tr() or translate() | ||
388 | call, and possibly finds out the context of the call. It supports | ||
389 | three cases: (1) the context is specified, as in | ||
390 | FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); | ||
391 | (2) the call appears within an inlined function; (3) the call | ||
392 | appears within a function defined outside the class definition. | ||
393 | */ | ||
394 | |||
395 | static int yyTok; | ||
396 | |||
397 | static bool match( int t ) | ||
398 | { | ||
399 | bool matches = ( yyTok == t ); | ||
400 | if ( matches ) | ||
401 | yyTok = getToken(); | ||
402 | return matches; | ||
403 | } | ||
404 | |||
405 | static bool matchString( QCString *s ) | ||
406 | { | ||
407 | bool matches = ( yyTok == Tok_String ); | ||
408 | *s = ""; | ||
409 | while ( yyTok == Tok_String ) { | ||
410 | *s += yyString; | ||
411 | yyTok = getToken(); | ||
412 | } | ||
413 | return matches; | ||
414 | } | ||
415 | |||
416 | static bool matchEncoding( bool *utf8 ) | ||
417 | { | ||
418 | if ( yyTok == Tok_Ident ) { | ||
419 | if ( strcmp(yyIdent, "QApplication") == 0 ) { | ||
420 | yyTok = getToken(); | ||
421 | if ( yyTok == Tok_Gulbrandsen ) | ||
422 | yyTok = getToken(); | ||
423 | } | ||
424 | *utf8 = QString( yyIdent ).endsWith( QString("UTF8") ); | ||
425 | yyTok = getToken(); | ||
426 | return TRUE; | ||
427 | } else { | ||
428 | return FALSE; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | static void parse( MetaTranslator *tor, const char *initialContext, | ||
433 | const char *defaultContext ) | ||
434 | { | ||
435 | QMap<QCString, QCString> qualifiedContexts; | ||
436 | QStringList namespaces; | ||
437 | QCString context; | ||
438 | QCString text; | ||
439 | QCString com; | ||
440 | QCString functionContext = initialContext; | ||
441 | QCString prefix; | ||
442 | bool utf8 = FALSE; | ||
443 | bool missing_Q_OBJECT = FALSE; | ||
444 | |||
445 | yyTok = getToken(); | ||
446 | while ( yyTok != Tok_Eof ) { | ||
447 | switch ( yyTok ) { | ||
448 | case Tok_class: | ||
449 | /* | ||
450 | Partial support for inlined functions. | ||
451 | */ | ||
452 | yyTok = getToken(); | ||
453 | if ( yyBraceDepth == (int) namespaces.count() && | ||
454 | yyParenDepth == 0 ) { | ||
455 | do { | ||
456 | /* | ||
457 | This code should execute only once, but we play | ||
458 | safe with impure definitions such as | ||
459 | 'class Q_EXPORT QMessageBox', in which case | ||
460 | 'QMessageBox' is the class name, not 'Q_EXPORT'. | ||
461 | */ | ||
462 | functionContext = yyIdent; | ||
463 | yyTok = getToken(); | ||
464 | } while ( yyTok == Tok_Ident ); | ||
465 | |||
466 | while ( yyTok == Tok_Gulbrandsen ) { | ||
467 | yyTok = getToken(); | ||
468 | functionContext += "::"; | ||
469 | functionContext += yyIdent; | ||
470 | yyTok = getToken(); | ||
471 | } | ||
472 | |||
473 | if ( yyTok == Tok_Colon ) { | ||
474 | missing_Q_OBJECT = TRUE; | ||
475 | } else { | ||
476 | functionContext = defaultContext; | ||
477 | } | ||
478 | } | ||
479 | break; | ||
480 | case Tok_namespace: | ||
481 | yyTok = getToken(); | ||
482 | if ( yyTok == Tok_Ident ) { | ||
483 | QCString ns = yyIdent; | ||
484 | yyTok = getToken(); | ||
485 | if ( yyTok == Tok_LeftBrace && | ||
486 | yyBraceDepth == (int) namespaces.count() + 1 ) | ||
487 | namespaces.append( QString(ns) ); | ||
488 | } | ||
489 | break; | ||
490 | case Tok_tr: | ||
491 | case Tok_trUtf8: | ||
492 | utf8 = ( yyTok == Tok_trUtf8 ); | ||
493 | yyTok = getToken(); | ||
494 | if ( match(Tok_LeftParen) && matchString(&text) ) { | ||
495 | com = ""; | ||
496 | if ( match(Tok_RightParen) || (match(Tok_Comma) && | ||
497 | matchString(&com) && match(Tok_RightParen)) ) { | ||
498 | if ( prefix.isNull() ) { | ||
499 | context = functionContext; | ||
500 | if ( !namespaces.isEmpty() ) | ||
501 | context.prepend( (namespaces.join(QString("::")) + | ||
502 | QString("::")).latin1() ); | ||
503 | } else { | ||
504 | context = prefix; | ||
505 | } | ||
506 | prefix = (const char *) 0; | ||
507 | |||
508 | if ( qualifiedContexts.contains(context) ) | ||
509 | context = qualifiedContexts[context]; | ||
510 | tor->insert( MetaTranslatorMessage(context, text, com, | ||
511 | QString::null, utf8) ); | ||
512 | |||
513 | if ( lacks_Q_OBJECT.contains(context) ) { | ||
514 | qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", | ||
515 | (const char *) yyFileName, yyLineNo, | ||
516 | (const char *) context ); | ||
517 | lacks_Q_OBJECT.remove( context ); | ||
518 | } else { | ||
519 | needs_Q_OBJECT.insert( context, 0 ); | ||
520 | } | ||
521 | } | ||
522 | } | ||
523 | break; | ||
524 | case Tok_translate: | ||
525 | utf8 = FALSE; | ||
526 | yyTok = getToken(); | ||
527 | if ( match(Tok_LeftParen) && | ||
528 | matchString(&context) && | ||
529 | match(Tok_Comma) && | ||
530 | matchString(&text) ) { | ||
531 | com = ""; | ||
532 | if ( match(Tok_RightParen) || | ||
533 | (match(Tok_Comma) && | ||
534 | matchString(&com) && | ||
535 | (match(Tok_RightParen) || | ||
536 | match(Tok_Comma) && | ||
537 | matchEncoding(&utf8) && | ||
538 | match(Tok_RightParen))) ) | ||
539 | tor->insert( MetaTranslatorMessage(context, text, com, | ||
540 | QString::null, utf8) ); | ||
541 | } | ||
542 | break; | ||
543 | case Tok_Q_OBJECT: | ||
544 | missing_Q_OBJECT = FALSE; | ||
545 | yyTok = getToken(); | ||
546 | break; | ||
547 | case Tok_Ident: | ||
548 | if ( !prefix.isNull() ) | ||
549 | prefix += "::"; | ||
550 | prefix += yyIdent; | ||
551 | yyTok = getToken(); | ||
552 | if ( yyTok != Tok_Gulbrandsen ) | ||
553 | prefix = (const char *) 0; | ||
554 | break; | ||
555 | case Tok_Comment: | ||
556 | com = yyComment; | ||
557 | com = com.simplifyWhiteSpace(); | ||
558 | if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) { | ||
559 | com.remove( 0, sizeof(MagicComment) - 1 ); | ||
560 | int k = com.find( ' ' ); | ||
561 | if ( k == -1 ) { | ||
562 | context = com; | ||
563 | } else { | ||
564 | context = com.left( k ); | ||
565 | com.remove( 0, k + 1 ); | ||
566 | tor->insert( MetaTranslatorMessage(context, "", com, | ||
567 | QString::null, FALSE) ); | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | Provide a backdoor for people using "using | ||
572 | namespace". See the manual for details. | ||
573 | */ | ||
574 | k = 0; | ||
575 | while ( (k = context.find("::", k)) != -1 ) { | ||
576 | qualifiedContexts.insert( context.mid(k + 2), context ); | ||
577 | k++; | ||
578 | } | ||
579 | } | ||
580 | yyTok = getToken(); | ||
581 | break; | ||
582 | case Tok_Arrow: | ||
583 | yyTok = getToken(); | ||
584 | if ( yyTok == Tok_tr || yyTok == Tok_trUtf8 ) | ||
585 | qWarning( "%s:%d: Cannot invoke tr() like this", | ||
586 | (const char *) yyFileName, yyLineNo ); | ||
587 | break; | ||
588 | case Tok_Gulbrandsen: | ||
589 | // at top level? | ||
590 | if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) | ||
591 | functionContext = prefix; | ||
592 | yyTok = getToken(); | ||
593 | break; | ||
594 | case Tok_RightBrace: | ||
595 | case Tok_Semicolon: | ||
596 | if ( yyBraceDepth >= 0 && | ||
597 | yyBraceDepth + 1 == (int) namespaces.count() ) | ||
598 | namespaces.remove( namespaces.fromLast() ); | ||
599 | if ( yyBraceDepth == (int) namespaces.count() ) { | ||
600 | if ( missing_Q_OBJECT ) { | ||
601 | if ( needs_Q_OBJECT.contains(functionContext) ) { | ||
602 | qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", | ||
603 | (const char *) yyFileName, yyLineNo, | ||
604 | (const char *) functionContext ); | ||
605 | } else { | ||
606 | lacks_Q_OBJECT.insert( functionContext, 0 ); | ||
607 | } | ||
608 | } | ||
609 | functionContext = defaultContext; | ||
610 | missing_Q_OBJECT = FALSE; | ||
611 | } | ||
612 | yyTok = getToken(); | ||
613 | break; | ||
614 | default: | ||
615 | yyTok = getToken(); | ||
616 | } | ||
617 | } | ||
618 | |||
619 | if ( yyBraceDepth != 0 ) | ||
620 | fprintf( stderr, | ||
621 | "%s: Unbalanced braces in C++ code (or abuse of the C++" | ||
622 | " preprocessor)\n", | ||
623 | (const char *) yyFileName ); | ||
624 | if ( yyParenDepth != 0 ) | ||
625 | fprintf( stderr, | ||
626 | "%s: Unbalanced parentheses in C++ code (or abuse of the C++" | ||
627 | " preprocessor)\n", | ||
628 | (const char *) yyFileName ); | ||
629 | } | ||
630 | |||
631 | void fetchtr_cpp( const char *fileName, MetaTranslator *tor, | ||
632 | const char *defaultContext, bool mustExist ) | ||
633 | { | ||
634 | yyInFile = fopen( fileName, "r" ); | ||
635 | if ( yyInFile == 0 ) { | ||
636 | if ( mustExist ) | ||
637 | fprintf( stderr, | ||
638 | "lupdate error: Cannot open C++ source file '%s': %s\n", | ||
639 | fileName, strerror(errno) ); | ||
640 | return; | ||
641 | } | ||
642 | |||
643 | startTokenizer( fileName, getCharFromFile ); | ||
644 | parse( tor, 0, defaultContext ); | ||
645 | fclose( yyInFile ); | ||
646 | } | ||
647 | |||
648 | /* | ||
649 | In addition to C++, we support Qt Designer UI files. | ||
650 | */ | ||
651 | |||
652 | /* | ||
653 | Fetches tr() calls in C++ code in UI files (inside "<function>" | ||
654 | tag). This mechanism is obsolete. | ||
655 | */ | ||
656 | void fetchtr_inlined_cpp( const char *fileName, const QString& in, | ||
657 | MetaTranslator *tor, const char *context ) | ||
658 | { | ||
659 | yyInStr = in; | ||
660 | startTokenizer( fileName, getCharFromString ); | ||
661 | parse( tor, context, 0 ); | ||
662 | yyInStr = QString::null; | ||
663 | } | ||
664 | |||
665 | class UiHandler : public QXmlDefaultHandler | ||
666 | { | ||
667 | public: | ||
668 | UiHandler( MetaTranslator *translator, const char *fileName ) | ||
669 | : tor( translator ), fname( fileName ), comment( "" ) { } | ||
670 | |||
671 | virtual bool startElement( const QString& namespaceURI, | ||
672 | const QString& localName, const QString& qName, | ||
673 | const QXmlAttributes& atts ); | ||
674 | virtual bool endElement( const QString& namespaceURI, | ||
675 | const QString& localName, const QString& qName ); | ||
676 | virtual bool characters( const QString& ch ); | ||
677 | virtual bool fatalError( const QXmlParseException& exception ); | ||
678 | |||
679 | private: | ||
680 | void flush(); | ||
681 | |||
682 | MetaTranslator *tor; | ||
683 | QCString fname; | ||
684 | QString context; | ||
685 | QString source; | ||
686 | QString comment; | ||
687 | |||
688 | QString accum; | ||
689 | }; | ||
690 | |||
691 | bool UiHandler::startElement( const QString& /* namespaceURI */, | ||
692 | const QString& /* localName */, | ||
693 | const QString& qName, | ||
694 | const QXmlAttributes& atts ) | ||
695 | { | ||
696 | if ( qName == QString("item") ) { | ||
697 | flush(); | ||
698 | if ( !atts.value(QString("text")).isEmpty() ) | ||
699 | source = atts.value( QString("text") ); | ||
700 | } else if ( qName == QString("string") ) { | ||
701 | flush(); | ||
702 | } | ||
703 | accum.truncate( 0 ); | ||
704 | return TRUE; | ||
705 | } | ||
706 | |||
707 | bool UiHandler::endElement( const QString& /* namespaceURI */, | ||
708 | const QString& /* localName */, | ||
709 | const QString& qName ) | ||
710 | { | ||
711 | accum.replace( QRegExp(QString("\r\n")), "\n" ); | ||
712 | |||
713 | if ( qName == QString("class") ) { | ||
714 | if ( context.isEmpty() ) | ||
715 | context = accum; | ||
716 | } else if ( qName == QString("string") ) { | ||
717 | source = accum; | ||
718 | } else if ( qName == QString("comment") ) { | ||
719 | comment = accum; | ||
720 | flush(); | ||
721 | } else if ( qName == QString("function") ) { | ||
722 | fetchtr_inlined_cpp( (const char *) fname, accum, tor, | ||
723 | context.latin1() ); | ||
724 | } else { | ||
725 | flush(); | ||
726 | } | ||
727 | return TRUE; | ||
728 | } | ||
729 | |||
730 | bool UiHandler::characters( const QString& ch ) | ||
731 | { | ||
732 | accum += ch; | ||
733 | return TRUE; | ||
734 | } | ||
735 | |||
736 | bool UiHandler::fatalError( const QXmlParseException& exception ) | ||
737 | { | ||
738 | QString msg; | ||
739 | msg.sprintf( "Parse error at line %d, column %d (%s).", | ||
740 | exception.lineNumber(), exception.columnNumber(), | ||
741 | exception.message().latin1() ); | ||
742 | fprintf( stderr, "XML error: %s\n", msg.latin1() ); | ||
743 | return FALSE; | ||
744 | } | ||
745 | |||
746 | void UiHandler::flush() | ||
747 | { | ||
748 | if ( !context.isEmpty() && !source.isEmpty() ) | ||
749 | tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(), | ||
750 | comment.utf8(), QString::null, | ||
751 | TRUE) ); | ||
752 | source.truncate( 0 ); | ||
753 | comment.truncate( 0 ); | ||
754 | } | ||
755 | |||
756 | void fetchtr_ui( const char *fileName, MetaTranslator *tor, | ||
757 | const char * /* defaultContext */, bool mustExist ) | ||
758 | { | ||
759 | QFile f( fileName ); | ||
760 | if ( !f.open(IO_ReadOnly) ) { | ||
761 | if ( mustExist ) | ||
762 | fprintf( stderr, "lupdate error: cannot open UI file '%s': %s\n", | ||
763 | fileName, strerror(errno) ); | ||
764 | return; | ||
765 | } | ||
766 | |||
767 | QTextStream t( &f ); | ||
768 | QXmlInputSource in( t ); | ||
769 | QXmlSimpleReader reader; | ||
770 | reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE ); | ||
771 | reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE ); | ||
772 | reader.setFeature( "http://trolltech.com/xml/features/report-whitespace" | ||
773 | "-only-CharData", FALSE ); | ||
774 | QXmlDefaultHandler *hand = new UiHandler( tor, fileName ); | ||
775 | reader.setContentHandler( hand ); | ||
776 | reader.setErrorHandler( hand ); | ||
777 | |||
778 | if ( !reader.parse(in) ) | ||
779 | fprintf( stderr, "%s: Parse error in UI file\n", fileName ); | ||
780 | reader.setContentHandler( 0 ); | ||
781 | reader.setErrorHandler( 0 ); | ||
782 | delete hand; | ||
783 | f.close(); | ||
784 | } | ||