source: trunk/tools/linguist/lupdate/fetchtr.cpp@ 90

Last change on this file since 90 was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

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