|
1 /* |
|
2 * Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <assert.h> |
|
20 #include <stdio.h> |
|
21 #include <string.h> |
|
22 |
|
23 #include "unistd.h" |
|
24 |
|
25 #if defined( __MSVCDOTNET__) || defined(__TOOLS2__) |
|
26 #include <iostream> |
|
27 using std::cout; |
|
28 using std::endl; |
|
29 using std::cerr; |
|
30 #else //!__MSVCDOTNET__ |
|
31 #ifndef __LINUX__ |
|
32 #include <io.h> |
|
33 #endif //!__LINUX__ |
|
34 #endif //__MSVCDOTNET__ |
|
35 |
|
36 #include "RESOURCE.H" |
|
37 #include "DATATYPE.H" |
|
38 #include "MEM.H" |
|
39 #include "RCBINSTR.H" |
|
40 #include "NUMVAL.H" |
|
41 #include "ERRORHAN.H" |
|
42 #include "FILEACC.H" |
|
43 #include "VERSION.H" |
|
44 #include "CTABLE.H" |
|
45 #include "main.h" |
|
46 #include "TOKENS.H" |
|
47 #include "localise.h" |
|
48 #include "qualifar.h" |
|
49 #include "messages.h" |
|
50 |
|
51 extern NameIdMap* pResourceNameIds; |
|
52 extern long CurrentId; |
|
53 |
|
54 void WriteHeaderFile(FILE* aRSG, IndexTable& aIndex) |
|
55 { |
|
56 IndexTableIterator next(aIndex); |
|
57 IndexTableItem * p; |
|
58 while( ( p = next() ) != NULL) |
|
59 { |
|
60 ResourceHeader& r=p->Resource(); |
|
61 if (r.iLabel.Length()>0 && !r.iLocal) |
|
62 { |
|
63 r.iLabel.Upper(); |
|
64 if (r.iFormatAsHex) |
|
65 fprintf(aRSG, "#define %-41s 0x%x\n", r.iLabel.GetAssertedNonEmptyBuffer(), r.iResourceId); |
|
66 else |
|
67 fprintf(aRSG, "#define %-41s %d\n", r.iLabel.GetAssertedNonEmptyBuffer(), r.iResourceId); |
|
68 } |
|
69 } |
|
70 } |
|
71 |
|
72 void WriteBitArrayOfResourcesContainingCompressedUnicode(RCBinaryStream& aRSC, const IndexTable& aIndex) |
|
73 { |
|
74 IndexTableIterator next(aIndex); |
|
75 unsigned char bitBuffer = 0; |
|
76 int numberOfBitsInBuffer = 0; |
|
77 for (;;) |
|
78 { |
|
79 IndexTableItem* const p = next(); |
|
80 if (p == NULL) |
|
81 { |
|
82 if (numberOfBitsInBuffer > 0) |
|
83 { |
|
84 aRSC.Write(&bitBuffer, 1); |
|
85 } |
|
86 break; |
|
87 } |
|
88 if (p->Resource().ContainsCompressedUnicode()) |
|
89 { |
|
90 bitBuffer |= (1 << numberOfBitsInBuffer); |
|
91 } |
|
92 ++numberOfBitsInBuffer; |
|
93 if (numberOfBitsInBuffer == 8) |
|
94 { |
|
95 aRSC.Write(&bitBuffer, 1); |
|
96 bitBuffer = 0; |
|
97 numberOfBitsInBuffer = 0; |
|
98 } |
|
99 } |
|
100 } |
|
101 |
|
102 void WriteBinaryResourceData(RCBinaryStream& aRSC, IndexTable& aIndex, int& aSizeOfLargestResourceWhenUncompressed, const char* aDumpDirectory) |
|
103 { |
|
104 IndexTableIterator next(aIndex); |
|
105 IndexTableItem * p; |
|
106 int resourceIndex=1; |
|
107 while( ( p = next() ) != NULL) |
|
108 { |
|
109 char* dumpFile=NULL; |
|
110 if (aDumpDirectory!=NULL) |
|
111 { |
|
112 dumpFile=new char[strlen(aDumpDirectory)+20]; |
|
113 strcpy(dumpFile, aDumpDirectory); |
|
114 char resourceIndexAsString[20]; |
|
115 sprintf(resourceIndexAsString, "%d", resourceIndex); |
|
116 strcat(dumpFile, resourceIndexAsString); |
|
117 } |
|
118 p->SetOffset(aRSC.GetPosition()); // record start of this resource in the index |
|
119 p->Resource().StreamOut(aRSC, aSizeOfLargestResourceWhenUncompressed, dumpFile); // write out binary form of resource |
|
120 delete [] dumpFile; |
|
121 ++resourceIndex; |
|
122 } |
|
123 } |
|
124 |
|
125 void WriteResourceFile(RCBinaryStream& aRSC, IndexTable& aIndex, bool aThirdUidIsOffset, const char* aDumpDirectory) |
|
126 { |
|
127 char flags=0; |
|
128 if (aThirdUidIsOffset) |
|
129 { |
|
130 flags|=0x01; |
|
131 } |
|
132 aRSC << flags; // these flags are to be used only by a dictionary-compressing program rather than to be used directly by Bafl when reading non-dictionary-compressed resource files (as output by Rcomp) |
|
133 const int positionToOverWriteFrom=aRSC.GetPosition(); |
|
134 NumericValue twoByteSizeOfLargestResourceWhenUncompressed(L_WORD); |
|
135 aRSC << twoByteSizeOfLargestResourceWhenUncompressed; |
|
136 WriteBitArrayOfResourcesContainingCompressedUnicode(aRSC, aIndex); // simply makes space for the bit-array without writing anything sensible in it (as we don't yet know which resources will contain compressed Unicode) |
|
137 int sizeOfLargestResourceWhenUncompressed=0; |
|
138 WriteBinaryResourceData(aRSC, aIndex, sizeOfLargestResourceWhenUncompressed, aDumpDirectory); |
|
139 aIndex.SetIndexOffset(aRSC.GetPosition()); |
|
140 aRSC << aIndex; |
|
141 aRSC.SetPosition(positionToOverWriteFrom); |
|
142 twoByteSizeOfLargestResourceWhenUncompressed=sizeOfLargestResourceWhenUncompressed; |
|
143 aRSC << twoByteSizeOfLargestResourceWhenUncompressed; |
|
144 WriteBitArrayOfResourcesContainingCompressedUnicode(aRSC, aIndex); // overwrites the bit array with correct data |
|
145 |
|
146 if(verbose) |
|
147 { |
|
148 MOFF; cout << aIndex; cout << endl; MON; |
|
149 } |
|
150 } |
|
151 |
|
152 void CheckLabels() // checks whether the labels that are used in the input have been declared |
|
153 { |
|
154 QualifiedStringArrayIterator nextLabel(pG->UsedIdentifiers); |
|
155 QualifiedString * pLabel; |
|
156 while ((pLabel = nextLabel() ) != NULL) |
|
157 { |
|
158 bool found = false; // gets set to true if the label in question is declared |
|
159 StringArrayIterator nextDeclared(pG->AllIdentifiers); |
|
160 String * pDeclared; |
|
161 while ( ( (pDeclared = nextDeclared() ) != NULL) && ( ! found )) |
|
162 { |
|
163 StringLess stringCompare; |
|
164 if( !stringCompare(*pDeclared,(*pLabel).GetEntry()) && !stringCompare((*pLabel).GetEntry(),*pDeclared) ) |
|
165 { // this comparison returns true if the label is the same as the declared label |
|
166 found = true; |
|
167 } |
|
168 } |
|
169 if( ! found ) // if label hasn't been declared emit warning |
|
170 { |
|
171 Message * message = pG->Messages.GetEntry(LT_045); |
|
172 String fileName = (*pLabel).GetFileName(); |
|
173 int lineNumber = (*pLabel).GetLineNumber(); |
|
174 if(message->GetActivated()) |
|
175 { |
|
176 String comment = message->GetMessageOutput(); |
|
177 comment += (*pLabel).GetEntry(); |
|
178 ErrorHandler::OutputWholeLine(fileName, lineNumber, comment); |
|
179 } |
|
180 } |
|
181 } |
|
182 } |
|
183 |
|
184 |
|
185 /* Tokenize expects a string in the following format: |
|
186 * \d{3}(,\d{3})* |
|
187 * i.e. comma-separated three digit numbers. |
|
188 * The string should contain no whitespace. |
|
189 */ |
|
190 void Tokenize(String aString) |
|
191 { |
|
192 int length = aString.Length(); |
|
193 |
|
194 for(int end=3; end<=length; end+=4) |
|
195 { |
|
196 String messageNumber = aString.ExtractSubString(end-3,end-1); |
|
197 if(messageNumber.IsDecNatural()) |
|
198 { |
|
199 Message * message = pG->Messages.GetTextEntry(messageNumber); |
|
200 if(message != NULL) |
|
201 { |
|
202 message->SetActivationStatus(false); |
|
203 } |
|
204 } |
|
205 } |
|
206 } |
|
207 |
|
208 |
|
209 void OutputHelp() |
|
210 { |
|
211 cerr << "Resource compiler version " << version << " (Build " << build << ") (C) 1997-2009 Nokia Corporation." << endl; |
|
212 cerr << "Usage: rcomp [-vpul] [-force] [-oRSCFile] [-{uid2,uid3}] [-hHeaderFile] [-sSourceFile] [-iBaseInputFileName]" << endl; |
|
213 cerr << "\tv" << "\tverbose" << endl; |
|
214 cerr << "\tp" << "\tParser debugging" << endl; |
|
215 cerr << "\tl" << "\tCheck localisation comments" << endl; |
|
216 cerr << "\tforce" << "\tEmit localisation warnings even if no localisation tags are present" << endl; |
|
217 cerr << "\tadd-defaults" << "\tAmend input rss/rpp file to add missing default localisation options" << endl; |
|
218 cerr << endl; |
|
219 cerr << "\tu" << "\tGenerate Unicode resource binary" << endl; |
|
220 cerr << endl; |
|
221 cerr << "If no source file is specified, the source will be read from standard input." << endl; |
|
222 cerr << "(-i is used to specify the file given to the preprocessor this " << endl; |
|
223 cerr << " name is used in generating debug output.)" << endl; |
|
224 } |
|
225 |
|
226 |
|
227 |
|
228 GlobalData *pG; |
|
229 GlobalLocalisationData *pGL; |
|
230 String InputBaseName; |
|
231 |
|
232 int main(int argc, char * argv[]) |
|
233 { |
|
234 cout << "\n"; |
|
235 int vParam=0; |
|
236 bool lParam = false; // used as flag to specify whether checking of localisation comment tags should be performed |
|
237 bool lForce = false; // used as flag to force localisation output even if there are no localisation comments |
|
238 bool lAddDefaults = false; // used as flag to add missing default localisation data to input rss/rpp file, this is not the default behaviour |
|
239 logmemorysetting = 0; |
|
240 unsigned short pParam = 0; |
|
241 String DataOutputFileName; |
|
242 String HeaderOutputFileName; |
|
243 String MessageSuppressionList; |
|
244 String BasePath; |
|
245 String SourceFileName; |
|
246 FILE * pSourceFile; |
|
247 char* uidsParameter=NULL; |
|
248 char* dumpDirectory=NULL; |
|
249 fpos_t filePositionIndicator; |
|
250 int i; |
|
251 |
|
252 char *fullcommand = argv[0]; |
|
253 std::string s(fullcommand); |
|
254 |
|
255 if(argc<=1) |
|
256 { |
|
257 OutputHelp(); |
|
258 exit(-1); |
|
259 } |
|
260 else |
|
261 { |
|
262 // Look through arguments for ones beginning with '-?'. |
|
263 for(i = 1; i < argc; i++) |
|
264 { |
|
265 if(* argv[i] == '-') |
|
266 { |
|
267 char * settings = argv[i] + 1; |
|
268 |
|
269 if(strchr(settings, '?') ) |
|
270 { |
|
271 OutputHelp(); |
|
272 exit(-1); |
|
273 } |
|
274 } |
|
275 } |
|
276 |
|
277 for(i = 1; i < argc; i++) |
|
278 { |
|
279 if(* argv[i] == '-') |
|
280 { |
|
281 char * settings = argv[i] + 1; |
|
282 |
|
283 if(* settings == 'o' || * settings == 'O') |
|
284 { |
|
285 DataOutputFileName = (settings + 1); |
|
286 continue; |
|
287 } |
|
288 |
|
289 if(* settings == 'm' || * settings == 'M') |
|
290 { |
|
291 MessageSuppressionList = (settings + 1); |
|
292 continue; |
|
293 } |
|
294 |
|
295 if(* settings == 'h' || * settings == 'H') |
|
296 { |
|
297 HeaderOutputFileName = (settings + 1); |
|
298 continue; |
|
299 } |
|
300 |
|
301 if(* settings == 'i' || * settings == 'I') |
|
302 { |
|
303 InputBaseName = (settings + 1); |
|
304 String DriveAndDirectory = FileAccess::GetDriveAndDirectory(InputBaseName); |
|
305 BasePath = FileAccess::FullPath(DriveAndDirectory); |
|
306 continue; |
|
307 } |
|
308 |
|
309 if(* settings == 's' || * settings == 'S') |
|
310 { |
|
311 SourceFileName = (settings + 1); |
|
312 continue; |
|
313 } |
|
314 |
|
315 if(* settings == '{') |
|
316 { |
|
317 uidsParameter = settings + 1; |
|
318 char* temp = strchr(uidsParameter, ','); |
|
319 if ((temp == NULL) || (temp == uidsParameter) || (strchr(temp + 1, ',') != NULL)) // check that there is *one* comma in this parameter (no more and no less), and that it is not the first thing immediately after the '{' |
|
320 { |
|
321 OutputHelp(); |
|
322 exit(-1); |
|
323 } |
|
324 *temp = ' '; |
|
325 temp = strchr(uidsParameter, '}'); |
|
326 if ((temp == NULL) || (temp[1] != '\0')) |
|
327 { |
|
328 OutputHelp(); |
|
329 exit(-1); |
|
330 } |
|
331 *temp = ' '; |
|
332 continue; |
|
333 } |
|
334 |
|
335 if(* settings == ':') |
|
336 { |
|
337 dumpDirectory=settings+1; |
|
338 continue; |
|
339 } |
|
340 |
|
341 if(strchr(settings, 'u') || strchr(settings, 'U') ) |
|
342 { |
|
343 SourceCharacterSet = String::CP1252; |
|
344 TargetCharacterSet = String::Unicode; |
|
345 } |
|
346 |
|
347 if(strchr(settings, 'v') || strchr(settings, 'V') ) |
|
348 vParam = 1; |
|
349 if(strchr(settings, 'p') || strchr(settings, 'P') ) |
|
350 pParam = 1; |
|
351 if(strchr(settings, 'l') || strchr(settings, 'L') ) |
|
352 lParam = true; |
|
353 if(strchr(settings, 'f') || strchr(settings, 'F') ) |
|
354 lForce = true; |
|
355 if(strchr(settings, 'a') || strchr(settings, 'A') ) |
|
356 lAddDefaults = true; |
|
357 } |
|
358 } |
|
359 } |
|
360 if(SourceFileName.Length() == 0) |
|
361 { |
|
362 pSourceFile = stdin; |
|
363 } |
|
364 else |
|
365 { |
|
366 if((pSourceFile = fopen(SourceFileName.GetAssertedNonEmptyBuffer(), "r") ) == NULL) |
|
367 { |
|
368 cerr << "Failed to open " << SourceFileName << endl; |
|
369 exit(-2); |
|
370 } |
|
371 } |
|
372 //Searchig for BOM signature which if found will be ignored |
|
373 |
|
374 unsigned char buffer[3]; |
|
375 fread( buffer, sizeof( char ), 3, pSourceFile); |
|
376 |
|
377 if((buffer[0]!=239) && (buffer[1]!=187) && (buffer[2]!=191)) |
|
378 { |
|
379 // BOM not found. Set the file-position indicator to 0 |
|
380 filePositionIndicator = fpos_t(); |
|
381 if(fsetpos(pSourceFile, &filePositionIndicator) !=0) |
|
382 { |
|
383 perror("fsetpos error"); |
|
384 } |
|
385 } |
|
386 verbose = vParam; |
|
387 |
|
388 pG = new GlobalData; |
|
389 if (pG==NULL) |
|
390 exit(-4); |
|
391 |
|
392 Tokenize(MessageSuppressionList); |
|
393 |
|
394 pGL = new GlobalLocalisationData; |
|
395 if(pG==NULL) |
|
396 exit(-4); |
|
397 |
|
398 pG->WarningMultiExplained = false; |
|
399 pG->FileLineHandler.SetPath(BasePath); |
|
400 |
|
401 #ifdef __TOOLS2__ |
|
402 pG->FileLineHandler.SetBase(SourceFileName,0); |
|
403 #endif |
|
404 |
|
405 int ret=ParseSourceFile(pSourceFile, pParam); |
|
406 fclose(pSourceFile); |
|
407 |
|
408 pGL->StoreFinalComment(); // final comment not stored during running of lex and yacc |
|
409 if(lParam && (pGL->LocalisationCommentsExist() || lForce)) |
|
410 { |
|
411 pGL->AnalyseLocalisationData(); |
|
412 pGL->PrintLocalisationWarnings(); |
|
413 if(lAddDefaults) |
|
414 { |
|
415 // only add deafult localisation values to rpp/rss file if the option has been set on the command line |
|
416 if(verbose) |
|
417 { |
|
418 cout << "* Reparsing source file and adding any missing default localisation comments" << endl; |
|
419 } |
|
420 pGL->OutputLocalisedFile(SourceFileName); |
|
421 } |
|
422 } |
|
423 if (ret != 0) |
|
424 { |
|
425 cerr << "RCOMP failed with code " << ret << endl; |
|
426 exit(ret); |
|
427 } |
|
428 // A successful parse, now generate the output files |
|
429 |
|
430 CheckLabels(); // check that all labels are declared and emit suitable warnings if not |
|
431 |
|
432 if(DataOutputFileName.Length() != 0) |
|
433 { |
|
434 |
|
435 |
|
436 #ifdef __LINUX__ |
|
437 |
|
438 std::string totalpath(s.substr( 0, s.rfind("/")+1 )); |
|
439 const char* uidTool = "uidcrc"; |
|
440 |
|
441 #else |
|
442 std::string totalpath(s.substr( 0, s.rfind("\\")+1 )); |
|
443 const char* uidTool = "uidcrc.exe"; |
|
444 |
|
445 #endif |
|
446 |
|
447 // Calls the uidcrc tool with the full path to where RCOMP resides in |
|
448 std::string uidpath(uidTool); |
|
449 totalpath += uidpath; |
|
450 |
|
451 // Find and replace all occurences of \ with / |
|
452 std::string searchString( "\\" ); |
|
453 std::string replaceString( "/" ); |
|
454 std::string::size_type pos = 0; |
|
455 while ( (pos = totalpath.find("\\", pos)) != std::string::npos ) { |
|
456 totalpath.replace( pos, searchString.size(), replaceString ); |
|
457 pos++; |
|
458 } |
|
459 |
|
460 const char *uidcrcTool = totalpath.c_str(); |
|
461 |
|
462 bool thirdUidIsOffset=true; |
|
463 |
|
464 char uidcrcUIDs[3][100]; |
|
465 strcpy (uidcrcUIDs[0], "0x101f4a6b"); |
|
466 |
|
467 if (uidsParameter) |
|
468 { |
|
469 // Command line argument takes precedence |
|
470 |
|
471 strcpy (uidcrcUIDs[1], strtok (uidsParameter, " ")); |
|
472 strcpy (uidcrcUIDs[2], strtok (NULL, " ")); |
|
473 |
|
474 char* const temp = strchr(uidcrcUIDs[2], '*'); |
|
475 if (temp == NULL) |
|
476 { |
|
477 thirdUidIsOffset=false; |
|
478 } |
|
479 } |
|
480 else |
|
481 { |
|
482 // otherwise use values supplied in source |
|
483 |
|
484 extern unsigned long Uid2; |
|
485 extern unsigned long Uid3; |
|
486 sprintf(uidcrcUIDs[1], "0x%08lx", Uid2); |
|
487 if (Uid3 != 0) |
|
488 { |
|
489 sprintf(uidcrcUIDs[2], "0x%08lx", Uid3); |
|
490 thirdUidIsOffset=false; |
|
491 } |
|
492 } |
|
493 |
|
494 |
|
495 if (thirdUidIsOffset) |
|
496 { |
|
497 const unsigned int idOfAnyResource = CurrentId; // *must* be unsigned so that when we right-shift it, the top bit doesn't get propagated if its set (i.e. "negative") |
|
498 sprintf(uidcrcUIDs[2], "0x%08x", idOfAnyResource >> 12); // use the 20 bits derived from the resource file's NAME as the 3rd UID |
|
499 } |
|
500 |
|
501 if (verbose) |
|
502 { |
|
503 MOFF; cout << uidcrcTool << " " << uidcrcUIDs[0] << " " << uidcrcUIDs[1] << " " << uidcrcUIDs[2] << " " << DataOutputFileName.GetAssertedNonEmptyBuffer(); cout << endl; MON; |
|
504 } |
|
505 |
|
506 #ifndef __LINUX__ |
|
507 const int error = _spawnlp (_P_WAIT, |
|
508 uidcrcTool, |
|
509 uidcrcTool, |
|
510 uidcrcUIDs[0], |
|
511 uidcrcUIDs[1], |
|
512 uidcrcUIDs[2], |
|
513 DataOutputFileName.GetAssertedNonEmptyBuffer(), |
|
514 NULL); |
|
515 #else |
|
516 char uidcrc_params[256]; |
|
517 const int ret = snprintf(uidcrc_params, |
|
518 sizeof(uidcrc_params), |
|
519 "%s %s %s %s %s", |
|
520 uidcrcTool, |
|
521 uidcrcUIDs[0], |
|
522 uidcrcUIDs[1], |
|
523 uidcrcUIDs[2], |
|
524 DataOutputFileName.GetBuffer()); |
|
525 if(ret <= 0) { |
|
526 cerr << "Failed to write UIDs to " << DataOutputFileName << endl; |
|
527 exit(ret); |
|
528 } |
|
529 const int error = system(uidcrc_params); |
|
530 #endif //__LINUX__ |
|
531 |
|
532 if(error != 0) |
|
533 { |
|
534 cerr << "Failed to write UIDs to " << DataOutputFileName << endl; |
|
535 exit(error); |
|
536 } |
|
537 RCBinaryStream RSCStream; |
|
538 RSCStream.OpenForAppend(DataOutputFileName); |
|
539 if(! RSCStream.IsOpen()) |
|
540 { |
|
541 cerr << "Failed to open " << DataOutputFileName << endl; |
|
542 exit(-2); |
|
543 } |
|
544 WriteResourceFile(RSCStream, pG->Index, thirdUidIsOffset, dumpDirectory); |
|
545 } |
|
546 |
|
547 if(HeaderOutputFileName.Length() != 0) |
|
548 { |
|
549 FILE* RSG; |
|
550 RSG = fopen(HeaderOutputFileName.GetAssertedNonEmptyBuffer(), "w"); |
|
551 if(RSG==NULL) |
|
552 { |
|
553 cerr << "Failed to open " << HeaderOutputFileName << endl; |
|
554 exit(-2); |
|
555 } |
|
556 WriteHeaderFile(RSG, pG->Index); |
|
557 fclose(RSG); |
|
558 } |
|
559 |
|
560 delete pG; |
|
561 delete pGL; |
|
562 |
|
563 return 0; |
|
564 } |
|
565 |
|
566 |
|
567 |
|
568 |