// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
// Implementation of the Class DefFile for the elf2e32 tool
// @internalComponent
// @released
//
//
//
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include "pl_symbol.h"
#include "deffile.h"
#include "errorhandler.h"
#ifdef __LINUX__
#include "h_utl.h"
#define STRUPR strupr
#else
#define STRUPR _strupr
#endif
using std::cerr;
using std::cout;
using std::endl;
#define SUCCESS 1
#define FAILURE 0
/**
Destructor to release all the allocated memory
@internalComponent
@released
*/
DefFile::~DefFile()
{
if(iSymbolList && iSymbolList->size())
{
SymbolList::iterator aItr = iSymbolList->begin();
SymbolList::iterator last = iSymbolList->end();
Symbol *temp;
while(aItr != last)
{
temp = *aItr;
aItr++;
delete temp;
}
iSymbolList->clear();
}
delete iSymbolList;
}
/**
Function to Get File Size.
@param fptrDef - File pointer to DEF file
@internalComponent
@released
*/
int DefFile::GetFileSize(FILE *fptrDef)
{
int fileSize,status;
status=fseek(fptrDef, 0, SEEK_END);
if(status!=0)
{
throw FileError(FILEREADERROR,iFileName);
}
fileSize=ftell(fptrDef);
rewind(fptrDef);
return fileSize;
}
/**
Function to Open File and read it in memory.
@param defFile - DEF File name
@internalComponent
@released
*/
char* DefFile::OpenDefFile(char * defFile)
{
int fileSize;
char *defFileEntries;
FILE *fptrDef;
iFileName=defFile;
if((fptrDef=fopen(defFile,"rb"))==NULL)
{
throw FileError(FILEOPENERROR,defFile);
}
fileSize=GetFileSize(fptrDef);
if((defFileEntries= new char[fileSize+2]) ==NULL)
{
throw MemoryAllocationError(MEMORYALLOCATIONERROR,defFile);
}
//Getting whole file in memory
if(!fread(defFileEntries, fileSize, 1, fptrDef))
{
throw FileError(FILEREADERROR,defFile);
}
//Adding ENTER at end
*(defFileEntries+fileSize)='\n';
//Adding '\0' at end
*(defFileEntries+fileSize+1)='\0';
fclose(fptrDef);
return defFileEntries;
}
/**
Function to Parse Def File which has been read in buffer.
@param defFileEntries - pointer to def file entries which has been read in buffer
@internalComponent
@released
*/
void DefFile::ParseDefFile(char *defFileEntries)
{
iSymbolList = new SymbolList;
int ordinalNo = 0;
int symbolType=SymbolTypeCode;
int PreviousOrdinal=0;
char MultiLineStatement[1024]="";
bool NAMEorLIBRARYallowed=true;
int LineNum = 0;
bool isComment;
char *lineToken;
int aLineLength = 0, width = 0;
unsigned i;
char *ptrEntry,*ptrEntryType;
char entryType[256];
bool entryFlag;
lineToken=strtok(defFileEntries,"\n");
while(lineToken != NULL)
{
symbolType=SymbolTypeCode;
isComment=false;
entryType[0]='\0';
aLineLength = strlen(lineToken);
LineNum++;
if (lineToken == NULL || lineToken[0]==13)
{
lineToken=strtok(NULL,"\n");
continue;
}
// comment lines
if (lineToken[0] == ';')
{
lineToken=strtok(NULL,"\n");
continue;
}
ptrEntry=lineToken;
if((!strstr(lineToken, "NONAME") && ((ptrEntryType=strstr(lineToken, "NAME")) != NULL)) ||
((ptrEntryType=strstr(lineToken, "EXPORTS")) != NULL) ||
((ptrEntryType=strstr(lineToken, "IMPORTS")) != NULL) ||
((ptrEntryType=strstr(lineToken, "SECTIONS")) != NULL) ||
((ptrEntryType=strstr(lineToken, "LIBRARY")) != NULL) ||
((ptrEntryType=strstr(lineToken, "DESCRIPTION")) != NULL)||
((ptrEntryType=strstr(lineToken, "STACKSIZE")) != NULL)||
((ptrEntryType=strstr(lineToken, "VERSION")) != NULL)
)
{
entryFlag=true;
for(i=0; ptrEntry!=ptrEntryType; i++,ptrEntry++)
{
switch(lineToken[i])
{
case ' ':
case '\t':
continue;
default:
entryFlag=false;
break;
}
if(entryFlag==false)
break;
}
if(entryFlag==false && !strcmp(MultiLineStatement,""))
{
throw DEFFileError(UNRECOGNIZEDTOKEN,iFileName,LineNum,lineToken);
}
if(entryFlag==true)
{
switch(ptrEntryType[0])
{
case 'E':
case 'I':
case 'L':
case 'V':
width=7;
break;
case 'S':
if(ptrEntryType[1]=='E')
width=8;
else
width=9;
break;
case 'N':
width=4;
break;
case 'D':
width=11;
break;
}
}
if(entryFlag==true)
{
for(i=i+width; i<strlen(lineToken); i++)
{
switch(lineToken[i])
{
case ' ':
case '\t':
continue;
case '\r':
case '\0':
break;
default:
entryFlag=false;
break;
}
if(entryFlag==false)
break;
}
}
if(entryFlag==false && !strcmp(MultiLineStatement,""))
{
throw DEFFileError(UNRECOGNIZEDTOKEN,iFileName,LineNum,lineToken+i);
}
if(entryFlag==true)
{
strncpy(entryType, ptrEntryType, width);
entryType[width]='\0';
switch(ptrEntryType[0])
{
case 'E': // check for multi-line sections starting
strcpy(MultiLineStatement, STRUPR(entryType)); // Uppercase token
NAMEorLIBRARYallowed = false;
lineToken = strtok(NULL, "\n" ); // Get the next line
continue;
case 'N':
break;
case 'L':
break;
case 'D':
break;
case 'S':
case 'V':
if(entryType[1]!='E')
{
// set MULTI-LINE statement to OFF
strcpy(MultiLineStatement, STRUPR(entryType)); // Uppercase token
// check single-line statements are specified correctly
// check NAME or LIBRARY statements aren't supplied incorrectly
if (!strcmp(entryType, "NAME") ||
!strcmp(entryType, "LIBRARY")
)
{
if (NAMEorLIBRARYallowed == false)
{
throw DEFFileError(NAMELIBRARYNOTCORRECT,iFileName,LineNum,lineToken);
}
lineToken=strtok(NULL,"\n");
continue;
}
NAMEorLIBRARYallowed = false;
lineToken=strtok(NULL,"\n");
continue;
}
continue;
case 'I':
strcpy(MultiLineStatement, STRUPR(entryType)); // Uppercase token
lineToken = strtok(NULL, "\n" ); // Get the next line
continue;
}
}
}
else
{
if (!strcmp(MultiLineStatement,""))
{
throw DEFFileError(EXPORTSEXPECTED,iFileName,LineNum,lineToken);
}
}
// Get Export entries
if (!strcmp(MultiLineStatement,"EXPORTS"))
{
Symbol aSymbol(NULL, SymbolTypeCode);
if( Tokenize(lineToken, LineNum, aSymbol) )
{
Symbol *newSymbolEntry = new Symbol(aSymbol);
iSymbolList->push_back(newSymbolEntry);
ordinalNo = aSymbol.OrdNum();
//Check for ordinal sequence
if (ordinalNo != PreviousOrdinal+1)
{
throw DEFFileError(ORDINALSEQUENCEERROR,iFileName,LineNum,(char*)aSymbol.SymbolName());
}
PreviousOrdinal = ordinalNo;
}
lineToken=strtok(NULL,"\n");
continue;
}
else if(strcmp(MultiLineStatement,"")!=0)//For entry other than exports
lineToken = strtok(NULL, "\n" ); // Get the next line
}//End of while
}
/**
This Function calls LineToken's Tokenize function.
@param aTokens - Input string at the current line number
@param aLineNum - Current line number
@param aSymbol - Symbol to be populated while parsing the line.
Return value - It returns true if a valid def file entry is found.
@internalComponent
@released
*/
bool DefFile::Tokenize(char* aTokens, int aLineNum, Symbol& aSymbol)
{
/*
* Pattern to match is:
* START\s*(\S+)\s+@\s*(d+)\s*(NONAME)?\s*(R3UNUSED)?\s*(ABSENT)?\s*(;\s*(.*))END
*/
LineToken aLine(iFileName, aLineNum, aTokens, &aSymbol);
return aLine.Tokenize();
}
char * DefFilePatterns[] =
{
"NONAME",//index 0
"DATA",
"R3UNUSED",
"ABSENT"
};
#define DEF_NONAME 0
#define DEF_DATA 1
#define DEF_R3UNUSED 2
#define DEF_ABSENT 3
/**
This constructor creates an instance of LineToken class.
@param aFileName - Def File Name.
@param aLineNum - Current line number
@param aLn - Input string at the current line number
@param aSym - Symbol to be populated while parsing the line.
@internalComponent
@released
*/
LineToken::LineToken(char* aFileName, int aLineNum, char *aLn, Symbol* aSym) : \
iLine(aLn) , iSymbol(aSym) , iOffset(0), iState(EInitState),iFileName(aFileName), iLineNum(aLineNum)
{
}
/**
This function tokenizes the line and populates a symbol entry
if there is one.
Return Value - True, if the current line has a valid def entry.
@internalComponent
@released
*/
bool LineToken::Tokenize()
{
while (1)
{
switch( iState )
{
case EFinalState:
return true;
case EInitState:
if( *(iLine + iOffset) == '\0' ||
*(iLine + iOffset) == '\r' ||
*(iLine + iOffset) == '\n')
{
/*
* Skip empty lines.
*/
return false;
}
else
{
NextToken();
}
break;
default:
NextToken();
break;
}
}
return false;
}
/**
This function parses a line of the def file based on the current
state it is in.
@internalComponent
@released
*/
void LineToken::NextToken()
{
int aCurrentPos = 0;
char *aSymbolName;
switch( iState )
{
case EInitState:
if(IsWhiteSpace((iLine + iOffset), aCurrentPos))
{
IncrOffset(aCurrentPos);
}
if(IsWord(iLine + iOffset, aCurrentPos))
{
SetState(ESymbolName);
}
break;
case ESymbolName:
{
// Get the length of the symbol
IsWord(iLine + iOffset, aCurrentPos);
char *cmt = strchr(iLine + iOffset, ';');
char *aAlias = strchr(iLine + iOffset, '=');
if( aAlias && (!cmt || (aAlias < cmt)) )
{
int aAliasPos = aAlias - (iLine+ iOffset);
//Check if alias name is also supplied, they should be separated
// by whitespace, i.e., SymbolName=AliasName is valid while,
// SymbolName =AliasName is invalid.
if( aAliasPos > aCurrentPos)
{
char *aToken = (iLine + iOffset + aCurrentPos);
throw DEFFileError(UNRECOGNIZEDTOKEN, iFileName, iLineNum, aToken);
}
aSymbolName = new char[aAliasPos+1];
strncpy(aSymbolName, iLine + iOffset, aAliasPos);
aSymbolName[aAliasPos] = '\0';
char *aExportName = new char[aCurrentPos - aAliasPos + 1];
strncpy(aExportName, aAlias +1, (aCurrentPos - aAliasPos));
aExportName[(aCurrentPos - aAliasPos)] = '\0';
iSymbol->ExportName(aExportName);
}
else
{
aSymbolName = new char[aCurrentPos+1];
strncpy(aSymbolName, iLine+ iOffset, aCurrentPos);
aSymbolName[aCurrentPos] = '\0';
}
iSymbol->SymbolName(aSymbolName);
IncrOffset(aCurrentPos);
if(IsWhiteSpace((iLine + iOffset), aCurrentPos))
{
IncrOffset(aCurrentPos);
}
if(*(iLine+iOffset) == '@')
{
SetState(EAtSymbol);
IncrOffset(1);
}
else
{
/*
* The first non-whitespace entry in a line is assumed to be the
* symbol name and a symbol name might also have a '@' char. Hence
* there MUST be a whitespace between symbol name and '@'.
*/
throw DEFFileError(ATRATEMISSING,iFileName,iLineNum,(iLine+iOffset));
}
}
break;
case EAtSymbol:
if(IsWhiteSpace((iLine + iOffset), aCurrentPos))
{
IncrOffset(aCurrentPos);
}
SetState(EOrdinal);
break;
case EOrdinal:
{
if(!IsDigit(iLine+iOffset, aCurrentPos ) )
{
throw DEFFileError(ORDINALNOTANUMBER, iFileName, iLineNum, (iLine+iOffset));
}
char aTmp[32];
strncpy(aTmp, iLine+iOffset, aCurrentPos);
aTmp[aCurrentPos] = '\0';
int aOrdinal = atoi(aTmp);
iSymbol->SetOrdinal(aOrdinal);
IncrOffset(aCurrentPos);
if(IsWhiteSpace((iLine + iOffset), aCurrentPos))
{
IncrOffset(aCurrentPos);
}
SetState(EOptionals);
}
break;
case EOptionals:
{
int aPrevPatternIndex, aPatternIdx = 0;
aPrevPatternIndex = -1;
while (*(iLine + iOffset) != '\n' || *(iLine + iOffset) != '\r')
{
if(IsPattern(iLine+iOffset, aCurrentPos, aPatternIdx) )
{
switch(aPatternIdx)
{
case DEF_NONAME:
break;
case DEF_DATA:
iSymbol->CodeDataType(SymbolTypeData);
IncrOffset(aCurrentPos);
if(IsWhiteSpace((iLine + iOffset), aCurrentPos))
{
IncrOffset(aCurrentPos);
}
if(IsDigit(iLine+iOffset, aCurrentPos ) )
{
char aSymSz[16];
strncpy(aSymSz, (iLine + iOffset), aCurrentPos);
aSymSz[aCurrentPos] = '\0';
iSymbol->SetSymbolSize(atoi(aSymSz));
}
break;
case DEF_R3UNUSED:
iSymbol->R3Unused(true);
break;
case DEF_ABSENT:
iSymbol->SetAbsent(true);
break;
default:
break;
}
/*
* NONAME , DATA, R3UNUSED and ABSENT, all the 3 are optional. But, if more than
* one of them appear, they MUST appear in that order.
* Else, it is not accepted.
*/
if( aPrevPatternIndex >= aPatternIdx)
{
throw DEFFileError(UNRECOGNIZEDTOKEN, iFileName, iLineNum,(iLine + iOffset));
}
aPrevPatternIndex = aPatternIdx;
IncrOffset(aCurrentPos);
if(IsWhiteSpace((iLine + iOffset), aCurrentPos))
{
IncrOffset(aCurrentPos);
}
}
else
{
if( *(iLine + iOffset) == ';' )
{
SetState(EComment);
IncrOffset(1);
return;
}
else if( *(iLine + iOffset) == '\0' ||
*(iLine + iOffset) == '\r' ||
*(iLine + iOffset) == '\n')
{
SetState(EFinalState);
return;
}
else
{
throw DEFFileError(UNRECOGNIZEDTOKEN, iFileName, iLineNum,(iLine + iOffset));
}
}
}
}
break;
case EComment:
{
if(IsWhiteSpace(iLine + iOffset, aCurrentPos))
{
IncrOffset(aCurrentPos);
}
int aLen = strlen(iLine + iOffset);
if( *(iLine + iOffset + aLen - 1 ) == '\n' || *(iLine + iOffset + aLen - 1 ) == '\r')
aLen -=1;
char * aComment = new char[ aLen + 1];
strncpy( aComment, iLine + iOffset, aLen);
aComment[aLen] = '\0';
IncrOffset(aLen);
iSymbol->Comment(aComment);
SetState(EFinalState);
}
break;
case EFinalState:
return;
default:
break;
}
}
/**
This function returns true if the string starts with one
of the fixed patterns.
It also updates the length and index of this pattern.
@param aChar - Line Token
@param aTill - Length of the pattern
@param aTill - Index of the pattern
Return Value - True, if the string starts with one of the patterns.
@internalComponent
@released
*/
bool LineToken::IsPattern(char* aStr, int& aTill, int& aIndex)
{
int pos = 0;
int aLength;
int size = sizeof(DefFilePatterns)/sizeof(char*);
while(size > pos)
{
aLength = strlen(DefFilePatterns[pos]);
if(!strncmp(aStr, DefFilePatterns[pos], aLength))
{
aTill = aLength;
aIndex = pos;
return true;
}
pos++;
}
return false;
}
/**
This function returns true if the string starts with digits.
It also updates the length of this digit string.
@param aChar - Line Token
@param aTill - Length of the digit string
Return Value - True, if the string starts with digit(s)
@internalComponent
@released
*/
bool LineToken::IsDigit(char *aChar, int &aTill)
{
int pos = 0;
if( aChar[pos] - '0' >= 0 && aChar[pos] - '0' <= 9)
{
pos++;
while(aChar[pos] - '0' >= 0 && aChar[pos] - '0' <= 9)
{
pos++;
}
aTill = pos;
return true;
}
else
{
return false;
}
}
/**
This function returns true if the string starts with white space.
It also updates the length of this white string!
@param aStr - Line Token
@param aTill - Length of the white string
Return Value - True, if the string starts with whitespace
@internalComponent
@released
*/
bool LineToken::IsWhiteSpace(char *aStr, int &aTill)
{
int pos = 0;
switch( aStr[pos] )
{
case ' ':
case '\t':
break;
default:
return false;
}
pos++;
while( aStr[pos])
{
switch(aStr[pos])
{
case ' ':
case '\t':
pos++;
break;
default:
aTill = pos;
return true;
}
}
aTill = pos;
return true;
}
/**
This function returns true if the string starts with non-whitespace.
It also updates the length of this word.
@param aStr - Line Token
@param aTill - Length of the word
Return Value - True, if the string starts with non-whitespace chars.
It also updates the length of the word.
@internalComponent
@released
*/
bool LineToken::IsWord(char *aStr, int &aTill)
{
int pos = 0;
switch( aStr[pos] )
{
case '\0':
case ' ':
case '\t':
case '\r':
case '\n':
return false;
default:
break;
}
pos++;
while( aStr[pos])
{
switch(aStr[pos])
{
case ' ':
case '\t':
case '\r':
case '\n':
aTill = pos;
return true;
default:
pos++;
break;
}
}
aTill = pos;
return true;
}
/**
This function increments the current offset.
@param aOff - increment by this value
@internalComponent
@released
*/
void LineToken::IncrOffset(int aOff)
{
iOffset += aOff;
}
/**
This function sets the state of the tokenizer that is parsing
the line.
@param aState - next state
@internalComponent
@released
*/
void LineToken::SetState(DefStates aState)
{
iState = aState;
}
/**
Function to Read def file and get the internal representation in structure.
@param defFile - DEF File name
@internalComponent
@released
*/
SymbolList* DefFile::ReadDefFile(char *defFile)
{
char *defFileEntries;
defFileEntries=OpenDefFile(defFile);
ParseDefFile(defFileEntries);
delete [] defFileEntries;//Free the memory which was required to read def file
return iSymbolList;
}
/**
Function to get the internal representation of Def File.
@param defFile - DEF File name
@internalComponent
@released
*/
SymbolList* DefFile::GetSymbolEntryList(char *defFile)
{
if(iSymbolList)
{
return iSymbolList;
}
else
{
iSymbolList=ReadDefFile(defFile);
return iSymbolList;
}
}
/**
Function to write DEF file from symbol entry List.
@param fileName - Def file name
@param newSymbolList - pointer to SymbolList which we get as an input for writing in DEF File
@internalComponent
@released
*/
void DefFile::WriteDefFile(char *fileName, SymbolList * newSymbolList)
{
char ordinal[6];
int newDefEntry=0;
FILE *fptr;
if((fptr=fopen(fileName,"wb"))==NULL)
{
throw FileError(FILEOPENERROR,fileName);
}
else
{
SymbolList::iterator aItr = newSymbolList->begin();
SymbolList::iterator last = newSymbolList->end();
Symbol *aSym;
fputs("EXPORTS",fptr);
fputs("\r\n",fptr);
while( aItr != last)
{
aSym = *aItr;
//Do not write now if its a new entry
if(aSym->GetSymbolStatus()==New)
{
newDefEntry=1;
aItr++;
continue;
}
//Make it comment if its missing def entry
if(aSym->GetSymbolStatus()==Missing)
fputs("; MISSING:",fptr);
fputs("\t",fptr);
if((aSym->ExportName()) && strcmp(aSym->SymbolName(),aSym->ExportName())!=0)
{
fputs(aSym->ExportName(),fptr);
fputs("=",fptr);
}
fputs(aSym->SymbolName(),fptr);
fputs(" @ ",fptr);
sprintf(ordinal,"%d",aSym->OrdNum());
fputs(ordinal,fptr);
fputs(" NONAME",fptr);
if(aSym->CodeDataType()==SymbolTypeData) {
fputs(" DATA",fptr);
fputs(" ",fptr);
char aSymSize[16];
sprintf(aSymSize, "%d", aSym->SymbolSize());
fputs(aSymSize,fptr);
}
if(aSym->R3unused())
fputs(" R3UNUSED",fptr);
if(aSym->Absent())
fputs(" ABSENT",fptr);
if(aSym->Comment()!=NULL)
{
fputs(" ; ",fptr);
fputs(aSym->Comment(),fptr);
}
fputs("\r\n",fptr);
aItr++;
}
//This is for writing new def entry in DEF File
if(newDefEntry)
{
fputs("; NEW:",fptr);
fputs("\r\n",fptr);
aItr = newSymbolList->begin();
last = newSymbolList->end();
while( aItr != last)
{
aSym = *aItr;
if(aSym->GetSymbolStatus()!=New)
{
aItr++;
continue;
}
fputs("\t",fptr);
if((aSym->ExportName()) && strcmp(aSym->SymbolName(),aSym->ExportName())!=0)
{
fputs(aSym->ExportName(),fptr);
fputs("=",fptr);
}
fputs(aSym->SymbolName(),fptr);
fputs(" @ ",fptr);
sprintf(ordinal,"%d",aSym->OrdNum());
fputs(ordinal,fptr);
fputs(" NONAME",fptr);
if(aSym->CodeDataType()==SymbolTypeData) {
fputs(" DATA",fptr);
fputs(" ",fptr);
char aSymSize[16];
sprintf(aSymSize, "%d", aSym->SymbolSize());
fputs(aSymSize,fptr);
}
if(aSym->R3unused())
fputs(" R3UNUSED",fptr);
if(aSym->Absent())
fputs(" ABSENT",fptr);
if(aSym->Comment()!=NULL)
{
if(aSym->CodeDataType()!=SymbolTypeCode &&
aSym->CodeDataType()!=SymbolTypeData)
{
fputs(" ; ",fptr);
fputs(aSym->Comment(),fptr);
}
else
{
fputs(" ",fptr);
fputs(aSym->Comment(),fptr);
}
}
fputs("\r\n",fptr);
aItr++;
}
}
fputs("\r\n",fptr);
fclose(fptr);
}
}