bintools/rcomp/src/main.cpp
author timothy.murphy@nokia.com
Sun, 28 Feb 2010 21:18:07 +0200
branchfix
changeset 279 733464eaac50
parent 0 044383f39525
child 590 360bd6b35136
permissions -rw-r--r--
fix: make sure host attribute is set rather than blank in logs on windows by using the env var 'COMPUTERNAME' instead of 'HOSTNAME'. Thus make it less difficult to order recipes in the log by time.

/*
* Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "unistd.h"

#if defined( __MSVCDOTNET__) || defined(__TOOLS2__)
#include <iostream>
using std::cout;
using std::endl;
using std::cerr;
#else //!__MSVCDOTNET__
#ifndef __LINUX__
#include <io.h>
#endif //!__LINUX__
#endif //__MSVCDOTNET__

#include "RESOURCE.H"
#include "DATATYPE.H"
#include "MEM.H"     
#include "RCBINSTR.H"
#include "NUMVAL.H"  
#include "ERRORHAN.H"
#include "FILEACC.H" 
#include "VERSION.H" 
#include "CTABLE.H"  
#include "main.h"
#include "TOKENS.H"
#include "localise.h"
#include "qualifar.h"
#include "messages.h"

extern NameIdMap* pResourceNameIds;
extern long CurrentId;

void WriteHeaderFile(FILE* aRSG, IndexTable& aIndex)
	{
	IndexTableIterator next(aIndex);
	IndexTableItem * p;
	while( ( p = next() ) != NULL)
		{
		ResourceHeader& r=p->Resource();
		if (r.iLabel.Length()>0 && !r.iLocal)
			{
			r.iLabel.Upper();
			if (r.iFormatAsHex)
				fprintf(aRSG, "#define %-41s 0x%x\n", r.iLabel.GetAssertedNonEmptyBuffer(), r.iResourceId);
			else
				fprintf(aRSG, "#define %-41s %d\n", r.iLabel.GetAssertedNonEmptyBuffer(), r.iResourceId);
			}
		}
	}

void WriteBitArrayOfResourcesContainingCompressedUnicode(RCBinaryStream& aRSC, const IndexTable& aIndex)
	{
	IndexTableIterator next(aIndex);
	unsigned char bitBuffer = 0;
	int numberOfBitsInBuffer = 0;
	for (;;)
		{
		IndexTableItem* const p = next();
		if (p == NULL)
			{
			if (numberOfBitsInBuffer > 0)
				{
				aRSC.Write(&bitBuffer, 1);
				}
			break;
			}
		if (p->Resource().ContainsCompressedUnicode())
			{
			bitBuffer |= (1 << numberOfBitsInBuffer);
			}
		++numberOfBitsInBuffer;
		if (numberOfBitsInBuffer == 8)
			{
			aRSC.Write(&bitBuffer, 1);
			bitBuffer = 0;
			numberOfBitsInBuffer = 0;
			}
		}
	}

void WriteBinaryResourceData(RCBinaryStream& aRSC, IndexTable& aIndex, int& aSizeOfLargestResourceWhenUncompressed, const char* aDumpDirectory)
	{
	IndexTableIterator next(aIndex);
	IndexTableItem * p;
	int resourceIndex=1;
	while( ( p = next() ) != NULL)
		{
		char* dumpFile=NULL;
		if (aDumpDirectory!=NULL)
			{
			dumpFile=new char[strlen(aDumpDirectory)+20];
			strcpy(dumpFile, aDumpDirectory);
			char resourceIndexAsString[20];
			sprintf(resourceIndexAsString, "%d", resourceIndex);
			strcat(dumpFile, resourceIndexAsString);
			}
		p->SetOffset(aRSC.GetPosition()); // record start of this resource in the index
		p->Resource().StreamOut(aRSC, aSizeOfLargestResourceWhenUncompressed, dumpFile); // write out binary form of resource
		delete [] dumpFile;
		++resourceIndex;
		}
	}

void WriteResourceFile(RCBinaryStream& aRSC, IndexTable& aIndex, bool aThirdUidIsOffset, const char* aDumpDirectory)
	{
	char flags=0;
	if (aThirdUidIsOffset)
		{
		flags|=0x01;
		}
	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)
	const int positionToOverWriteFrom=aRSC.GetPosition();
	NumericValue twoByteSizeOfLargestResourceWhenUncompressed(L_WORD);
	aRSC << twoByteSizeOfLargestResourceWhenUncompressed;
	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)
	int sizeOfLargestResourceWhenUncompressed=0;
	WriteBinaryResourceData(aRSC, aIndex, sizeOfLargestResourceWhenUncompressed, aDumpDirectory);
	aIndex.SetIndexOffset(aRSC.GetPosition());
	aRSC << aIndex;
	aRSC.SetPosition(positionToOverWriteFrom);
	twoByteSizeOfLargestResourceWhenUncompressed=sizeOfLargestResourceWhenUncompressed;
	aRSC << twoByteSizeOfLargestResourceWhenUncompressed;
	WriteBitArrayOfResourcesContainingCompressedUnicode(aRSC, aIndex); // overwrites the bit array with correct data

	if(verbose) 
		{
		MOFF; cout << aIndex; cout << endl; MON;
		}
	}

void CheckLabels() // checks whether the labels that are used in the input have been declared
	{
	QualifiedStringArrayIterator nextLabel(pG->UsedIdentifiers);
	QualifiedString * pLabel;
	while ((pLabel = nextLabel() ) != NULL)
		{
		bool found = false; // gets set to true if the label in question is declared
		StringArrayIterator nextDeclared(pG->AllIdentifiers);
		String * pDeclared;
		while ( ( (pDeclared = nextDeclared() ) != NULL) && ( ! found ))
			{
			StringLess stringCompare;
			if( !stringCompare(*pDeclared,(*pLabel).GetEntry()) && !stringCompare((*pLabel).GetEntry(),*pDeclared) )
				{ // this comparison returns true if the label is the same as the declared label
				found = true;
				}
			}
		if( ! found ) // if label hasn't been declared emit warning
			{
			Message * message = pG->Messages.GetEntry(LT_045);
			String fileName = (*pLabel).GetFileName();
			int lineNumber = (*pLabel).GetLineNumber();
			if(message->GetActivated())
				{
				String comment = message->GetMessageOutput();
				comment += (*pLabel).GetEntry();
				ErrorHandler::OutputWholeLine(fileName, lineNumber, comment);
				}
			}
		}
	}


/*  Tokenize expects a string in the following format:
 *	  \d{3}(,\d{3})*
 *  i.e. comma-separated three digit numbers. 
 *  The string should contain no whitespace.
 */
void Tokenize(String aString)
	{
	int length = aString.Length();

	for(int end=3; end<=length; end+=4)
		{
		String messageNumber = aString.ExtractSubString(end-3,end-1);
		if(messageNumber.IsDecNatural())
			{
			Message * message = pG->Messages.GetTextEntry(messageNumber);
			if(message != NULL)
				{
				message->SetActivationStatus(false);
				}
			}
		}
	}
				

void OutputHelp()
	{
	cerr << "Resource compiler version " << version << " (Build " << build << ") (C) 1997-2009 Nokia Corporation." << endl;
	cerr << "Usage: rcomp [-vpul] [-force] [-oRSCFile] [-{uid2,uid3}] [-hHeaderFile] [-sSourceFile] [-iBaseInputFileName]" << endl;
	cerr << "\tv" << "\tverbose" << endl;
	cerr << "\tp" << "\tParser debugging" << endl;
	cerr << "\tl" << "\tCheck localisation comments" << endl;
	cerr << "\tforce" << "\tEmit localisation warnings even if no localisation tags are present" << endl;
	cerr << "\tadd-defaults" << "\tAmend input rss/rpp file to add missing default localisation options" << endl;
	cerr << endl;
	cerr << "\tu" << "\tGenerate Unicode resource binary" << endl;
	cerr << endl;
	cerr << "If no source file is specified, the source will be read from standard input." << endl;
	cerr << "(-i is used to specify the file given to the preprocessor this " << endl;
	cerr << "    name is used in generating debug output.)" << endl;
	}



GlobalData *pG;
GlobalLocalisationData *pGL;
String InputBaseName;

int main(int argc, char * argv[])
	{
	cout << "\n"; 
	int vParam=0;
	bool lParam = false; // used as flag to specify whether checking of localisation comment tags should be performed
	bool lForce = false; // used as flag to force localisation output even if there are no localisation comments
	bool lAddDefaults = false; // used as flag to add missing default localisation data to input rss/rpp file, this is not the default behaviour
	logmemorysetting = 0;
	unsigned short	pParam = 0;
	String			DataOutputFileName;
	String			HeaderOutputFileName;
	String			MessageSuppressionList;
	String			BasePath;
	String			SourceFileName;
	FILE *			pSourceFile;
	char*			uidsParameter=NULL;
	char*			dumpDirectory=NULL;
	fpos_t			filePositionIndicator;
	int				i;
	
	char *fullcommand = argv[0];
	std::string s(fullcommand);

	if(argc<=1)
		{
		OutputHelp();
		exit(-1);
		}
	else
		{
		// Look through arguments for ones beginning with '-?'.
		for(i = 1; i < argc; i++)
			{
			if(* argv[i] == '-')
				{
				char * settings = argv[i] + 1;
			
				if(strchr(settings, '?') )
					{
					OutputHelp();
					exit(-1);
					}
				}
			}

		for(i = 1; i < argc; i++)
			{
			if(* argv[i] == '-')
				{
				char * settings = argv[i] + 1;
			
				if(* settings == 'o' || * settings == 'O')
					{
					DataOutputFileName = (settings + 1);
					continue;
					}

				if(* settings == 'm' || * settings == 'M')
					{
					MessageSuppressionList = (settings + 1);
					continue;
					}
					
				if(* settings == 'h' || * settings == 'H')
					{
					HeaderOutputFileName = (settings + 1);
					continue;
					}

				if(* settings == 'i' || * settings == 'I')
					{
					InputBaseName = (settings + 1);
					String DriveAndDirectory = FileAccess::GetDriveAndDirectory(InputBaseName);
					BasePath = FileAccess::FullPath(DriveAndDirectory);
					continue;
					}

				if(* settings == 's' || * settings == 'S')
					{
					SourceFileName = (settings + 1);
					continue;
					}

				if(* settings == '{')
					{
					uidsParameter = settings + 1;
					char* temp = strchr(uidsParameter, ',');
					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 '{'
						{
						OutputHelp();
						exit(-1);
						}
					*temp = ' ';
					temp = strchr(uidsParameter, '}');
					if ((temp == NULL) || (temp[1] != '\0'))
						{
						OutputHelp();
						exit(-1);
						}
					*temp = ' ';
					continue;
					}

				if(* settings == ':')
					{
					dumpDirectory=settings+1;
					continue;
					}

				if(strchr(settings, 'u') || strchr(settings, 'U') )
					{
					SourceCharacterSet = String::CP1252;
					TargetCharacterSet = String::Unicode;
					}

				if(strchr(settings, 'v') || strchr(settings, 'V') )
					vParam = 1;
				if(strchr(settings, 'p') || strchr(settings, 'P') )
					pParam = 1;
				if(strchr(settings, 'l') || strchr(settings, 'L') )
					lParam = true;
				if(strchr(settings, 'f') || strchr(settings, 'F') )
					lForce = true;
				if(strchr(settings, 'a') || strchr(settings, 'A') )
					lAddDefaults = true;
				}
			}
		}
	if(SourceFileName.Length() == 0)
		{
		pSourceFile = stdin;
		}
	else 
		{
		if((pSourceFile = fopen(SourceFileName.GetAssertedNonEmptyBuffer(), "r") ) == NULL)
			{
			cerr << "Failed to open " << SourceFileName << endl;
			exit(-2);
			}
		}
	//Searchig for BOM signature which if found will be ignored

	unsigned char buffer[3];
	fread( buffer, sizeof( char ), 3, pSourceFile);
	
	if((buffer[0]!=239) && (buffer[1]!=187) && (buffer[2]!=191))
		{
		// BOM not found. Set the file-position indicator to 0
		filePositionIndicator = fpos_t();
		if(fsetpos(pSourceFile, &filePositionIndicator) !=0)
			{
			perror("fsetpos error");
			} 
		}	
	verbose = vParam;

	pG = new GlobalData;
	if (pG==NULL)
		exit(-4);
	
	Tokenize(MessageSuppressionList);

	pGL = new GlobalLocalisationData;
	if(pG==NULL)
		exit(-4);

	pG->WarningMultiExplained = false;
	pG->FileLineHandler.SetPath(BasePath);
	
	#ifdef __TOOLS2__
	pG->FileLineHandler.SetBase(SourceFileName,0);
	#endif
	
	int ret=ParseSourceFile(pSourceFile, pParam);
	fclose(pSourceFile);
	
	pGL->StoreFinalComment(); // final comment not stored during running of lex and yacc
	if(lParam && (pGL->LocalisationCommentsExist() || lForce))
		{
		pGL->AnalyseLocalisationData();
		pGL->PrintLocalisationWarnings();
		if(lAddDefaults)
			{
			// only add deafult localisation values to rpp/rss file if the option has been set on the command line
			if(verbose)
				{
				cout << "* Reparsing source file and adding any missing default localisation comments" << endl;
				}
			pGL->OutputLocalisedFile(SourceFileName);
			}
		}
	if (ret != 0) 
		{
		cerr << "RCOMP failed with code " << ret << endl;
		exit(ret);
		}
	// A successful parse, now generate the output files

	CheckLabels(); // check that all labels are declared and emit suitable warnings if not

	if(DataOutputFileName.Length() != 0)
		{


#ifdef __LINUX__

		std::string totalpath(s.substr( 0, s.rfind("/")+1 ));
		const char* uidTool = "uidcrc";

#else
		std::string totalpath(s.substr( 0, s.rfind("\\")+1 ));
		const char* uidTool = "uidcrc.exe";

#endif

		// Calls the uidcrc tool with the full path to where RCOMP resides in
		std::string uidpath(uidTool);
		totalpath += uidpath;
		
		// Find and replace all occurences of \ with /
		std::string searchString( "\\" ); 
		std::string replaceString( "/" );
 		std::string::size_type pos = 0;
	    	while ( (pos = totalpath.find("\\", pos)) != std::string::npos ) {
        		totalpath.replace( pos, searchString.size(), replaceString );
		        pos++;
		    }
			
		const char *uidcrcTool = totalpath.c_str();

		bool thirdUidIsOffset=true;
		
		char uidcrcUIDs[3][100];
		strcpy (uidcrcUIDs[0], "0x101f4a6b");

		if (uidsParameter)
			{
			// Command line argument takes precedence 
			
			strcpy (uidcrcUIDs[1], strtok (uidsParameter, " "));
			strcpy (uidcrcUIDs[2], strtok (NULL, " "));
 
			char* const temp = strchr(uidcrcUIDs[2], '*');		
			if (temp == NULL)
				{
				thirdUidIsOffset=false;
				}
			}
		else
			{
			// otherwise use values supplied in source

			extern unsigned long Uid2;
			extern unsigned long Uid3;
			sprintf(uidcrcUIDs[1], "0x%08lx", Uid2);
			if (Uid3 != 0)
				{
				sprintf(uidcrcUIDs[2], "0x%08lx", Uid3);
				thirdUidIsOffset=false;
				}
			}


		if (thirdUidIsOffset)
			{
			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")
			sprintf(uidcrcUIDs[2], "0x%08x", idOfAnyResource >> 12); // use the 20 bits derived from the resource file's NAME as the 3rd UID
			}

		if (verbose)
			{
			MOFF; cout << uidcrcTool << " " << uidcrcUIDs[0] << " " << uidcrcUIDs[1] << " " << uidcrcUIDs[2] << " " << DataOutputFileName.GetAssertedNonEmptyBuffer(); cout << endl; MON;
			}

#ifndef __LINUX__
		const int error = _spawnlp (_P_WAIT,
									uidcrcTool,
									uidcrcTool,
									uidcrcUIDs[0],
									uidcrcUIDs[1],
									uidcrcUIDs[2],
									DataOutputFileName.GetAssertedNonEmptyBuffer(),
									NULL);
#else
		char uidcrc_params[256];
		const int ret = snprintf(uidcrc_params,
					 sizeof(uidcrc_params),
					 "%s %s %s %s %s",
					 uidcrcTool,
					 uidcrcUIDs[0],
					 uidcrcUIDs[1],
					 uidcrcUIDs[2],
					 DataOutputFileName.GetBuffer());
		if(ret <= 0) {
			cerr << "Failed to write UIDs to " << DataOutputFileName << endl;
			exit(ret);
		}
		const int error = system(uidcrc_params);
#endif //__LINUX__

		if(error != 0)
			{
			cerr << "Failed to write UIDs to " << DataOutputFileName << endl;
			exit(error);
			}
		RCBinaryStream RSCStream;
		RSCStream.OpenForAppend(DataOutputFileName);
		if(! RSCStream.IsOpen())
			{
			cerr << "Failed to open " << DataOutputFileName << endl;
			exit(-2);
			}
		WriteResourceFile(RSCStream, pG->Index, thirdUidIsOffset, dumpDirectory);
		}
	
	if(HeaderOutputFileName.Length() != 0)
		{
		FILE* RSG;
		RSG = fopen(HeaderOutputFileName.GetAssertedNonEmptyBuffer(), "w");
		if(RSG==NULL)
			{
			cerr << "Failed to open " << HeaderOutputFileName << endl;
			exit(-2);
			}
		WriteHeaderFile(RSG, pG->Index);
		fclose(RSG);
		}

	delete pG;
	delete pGL;

	return 0;
	}