Files
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

1679 lines
48 KiB
C

// ==++==
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
// ===========================================================================
// File: rc.c
// Purpose: Compiles .rc files to .satellite files
// ===========================================================================
#include "rc.h"
#include "palstartup.h"
#define BUF_SIZE (4097*2) //Number comes from resource compiler mini spec
#define PATH_BUF_SIZE _MAX_PATH
#define UNKNOWN_LINE_NUMBER -1
#if PLATFORM_UNIX
/* By default, cc treats .rc files as something to be linked in and
* therefore won't run the preprocessor on them. The '-x c' option
* tells cc to treat the following file as a C file.
* On Darwin, we don't want to use the cpp-precomp preprocessor.
*/
#if __APPLE_CC__
#define COMPILER_PREPROCESSOR_FLAGS "-E -no-cpp-precomp -DRC_INVOKED=1 -DFEATURE_PAL=1 -x c"
#else // __APPLE_CC__
#define COMPILER_PREPROCESSOR_FLAGS "-E -DRC_INVOKED=1 -DFEATURE_PAL=1 -x c"
#endif // __APPLE_CC__
#define COMPILER_NAME "/usr/bin/gcc"
#define PATH_SEPARATOR_CHAR '/'
#define LINEMARKER_PREFIX '#'
#define DEFINE_PREFIX "-D"
#define INCLUDE_PREFIX "-I"
#define TARGET_PREFIX "-o"
#else
#define COMPILER_PREPROCESSOR_FLAGS "/nologo /E /P /DRC_INVOKED=1 /DFEATURE_PAL=1"
#define COMPILER_NAME "cl.exe"
#define PATH_SEPARATOR_CHAR '\\'
#define LINEMARKER_PREFIX "#line"
#define DEFINE_PREFIX "/D"
#define INCLUDE_PREFIX "/I"
#endif
int getEscapeCharacter (char * input, char * output);
LPSTR FormatString(LPSTR inputString);
LPSTR convertToUTF8(LPSTR inputString);
int __cdecl steCompare(const void *first, const void *second);
void ProcessParameters(int argc, LPSTR argv[], LPSTR * compilerOptions);
void printUsage(LPSTR message);
void crashAndBurn(LPSTR message, int lineNumber, LPSTR file, LPSTR badline);
void updateLineNumber(LPSTR line, int * lineNumber, LPSTR * lineFile);
void preProcessFile(LPSTR compilerOptions);
void clearBlanksAndLineUpdates(FILE * file, int * lineNumber,
LPSTR * lineFile,
LPSTR * textline, BOOL checkCurrent);
BOOL startsWithLineMarker(const char *line);
BOOL __stdcall ConsoleControlHandler(DWORD dwCtrlType);
LPSTR changeExtension(LPSTR filename, LPSTR old, LPSTR new);
void removeDefinition(FILE * file,
LPSTR textline,
BOOL linesBeforeBegin,
int * lineNumber,
LPSTR * lineFile);
int LexOneLine(char *p);
ULONG Expr_Eval(void);
BOOL verbose = FALSE;
BOOL warn = FALSE;
BOOL nulls = FALSE;
int codepage = 1252;
LPSTR inFilename;
LPSTR outFilename = NULL;
LPSTR strippedFilename;
FILE * strippedrc;
FILE * outfile;
//MAIN
int __cdecl main(int argc, LPSTR argv[])
{
int lineNumber = 0;
LPSTR filenameStart;
LPSTR lineFile = NULL;
LPSTR textline;
LPSTR textlinetotok;
LPSTR firstword;
LPSTR stringID;
UINT stringIDint;
LPSTR stringValue;
LPSTR stringValueFormatted;
LPSTR stringValueOut;
STRING_TABLE_ENTRY * stringtable = NULL;
int stringTableCounter = 0;
LPSTR compilerOptions;
int i;
int lastValue = -1;
BOOL OutFileWritten = FALSE;
textline = malloc(BUF_SIZE);
SetConsoleCtrlHandler(ConsoleControlHandler, TRUE);
argv++;
argc--;
if (argc == 0){
printUsage("");
}
compilerOptions = malloc(sizeof(" " COMPILER_PREPROCESSOR_FLAGS " "));
if (compilerOptions == NULL)
crashAndBurn("Out of memory - compilerOptions",
UNKNOWN_LINE_NUMBER, "", "");
strcpy(compilerOptions, " " COMPILER_PREPROCESSOR_FLAGS " ");
ProcessParameters(argc, argv, &compilerOptions);
if (inFilename == NULL){
printUsage("A filename must be specified\n");
}
else{
printf("\n\n%s\n", inFilename);
}
if (verbose){
printf("Preprocessing resource file...\n");
}
filenameStart = strrchr(inFilename, PATH_SEPARATOR_CHAR);
if (filenameStart == NULL) {
filenameStart = inFilename;
} else {
filenameStart++;
}
lineFile = malloc(strlen(filenameStart) + 1);
if (lineFile == NULL) {
crashAndBurn("Out of memory - string allocation",
UNKNOWN_LINE_NUMBER, "", "");
}
strcpy(lineFile, filenameStart);
strippedFilename = changeExtension(filenameStart, ".rc", ".i");
#if PLATFORM_UNIX
{
LPSTR tempString;
tempString = (LPSTR) malloc(strlen(compilerOptions) +
strlen(" " TARGET_PREFIX " ") +
strlen(strippedFilename) +
strlen(" ") + 1);
if (tempString == NULL) {
crashAndBurn("Out of memory - realloc'd compilerOptions",
UNKNOWN_LINE_NUMBER, "", "");
}
strcpy(tempString, compilerOptions);
strcat(tempString, " " TARGET_PREFIX " ");
strcat(tempString, strippedFilename);
strcat(tempString, " ");
compilerOptions = tempString;
}
#endif
preProcessFile(compilerOptions);
free(compilerOptions);
free(inFilename);
if (verbose)
printf("Reading Preprocessed File %s\n", strippedFilename);
//Open stripped input file
strippedrc = fopen(strippedFilename, "r");
if (strippedrc == NULL){
crashAndBurn("Preprocessor failure", UNKNOWN_LINE_NUMBER, "", "");
}
while (fgets(textline, BUF_SIZE, strippedrc) != NULL){
lineNumber++;
clearBlanksAndLineUpdates(strippedrc, &lineNumber, &lineFile,
&textline, TRUE);
if (textline == NULL){
break;
}
textline[(strlen(textline)-1)] = '\0'; //Remove trailing newline
textlinetotok = malloc(strlen(textline) + 1);
if(textlinetotok == NULL){
crashAndBurn("Out of memory - textlinetotok",
UNKNOWN_LINE_NUMBER, "", "");
}
strcpy(textlinetotok, textline);
firstword = strtok(textlinetotok, " \t\n");
assert(firstword != NULL); //BLANK LINES WERE REMOVED
if (!strcmp(firstword, "STRINGTABLE")){
if (verbose)
printf("StringTable resource found on line %i in \n\t%s, processing\n",
lineNumber, lineFile);
clearBlanksAndLineUpdates(strippedrc,&lineNumber,
&lineFile, &textline, FALSE);
if (textline == NULL){
crashAndBurn("Unexpected end of file in Stringtable",
lineNumber, lineFile, "");
}
//MAKE SURE BEGIN EXISTS
textlinetotok = malloc(strlen(textline) + 1);
if (textlinetotok == NULL)
crashAndBurn("Out of memory -textlinetotok",
UNKNOWN_LINE_NUMBER,"","");
textlinetotok = strcpy(textlinetotok, textline);
firstword = strtok(textlinetotok, " \t\n");
assert(firstword != NULL); //BLANK LINES WERE REMOVED
if ( (strcmp(firstword,"BEGIN")) ){ //NO BEGIN, CHECK FOR {
textline = strchr(textline, '{');
if (textline == NULL){
crashAndBurn("No BEGIN or { following STRINGTABLE",
lineNumber, lineFile, textline);
}
textline++;
}
else {
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("end of file before END in stringtable",
lineNumber, lineFile, "");
}
else{
lineNumber++;
}
}
//process entries
while(1){
clearBlanksAndLineUpdates(strippedrc,
&lineNumber, &lineFile, &textline, TRUE);
if (textline == NULL){
crashAndBurn("end of file before END in stringtable",
lineNumber, lineFile, "");
}
textlinetotok = malloc(strlen(textline) + 1);
if (textlinetotok == NULL)
crashAndBurn("Out of memory -textlinetotok",
UNKNOWN_LINE_NUMBER,"","");
textlinetotok = strcpy(textlinetotok, textline);
firstword = strtok(textlinetotok, " \t\n");
assert(firstword != NULL); //BLANK LINES WERE REMOVED
if (!strcmp(firstword,"END")|| !strcmp(firstword, "}")){
break;
}
if (stringtable == NULL){ //First time through loop, no memory yet.
stringtable = malloc(sizeof(STRING_TABLE_ENTRY));
if (stringtable == NULL){
crashAndBurn("Out of memory - stringtable",
UNKNOWN_LINE_NUMBER, "", "");
}
}
else { //Make room for additional entry
stringtable = realloc(stringtable,
(stringTableCounter + 1) *
sizeof(STRING_TABLE_ENTRY));
if (stringtable == NULL){
crashAndBurn("Out of memory - stringtable",
UNKNOWN_LINE_NUMBER, "", "");
}
}
//GET FRESH COPY OF TEXTLINE TO TOKENIZE WITH DIFFERENT SPACERS
textlinetotok = malloc(strlen(textline) + 1);
if (textlinetotok == NULL)
crashAndBurn("Out of memory -textlinetotok",
UNKNOWN_LINE_NUMBER,"","");
textlinetotok = strcpy(textlinetotok, textline);
stringID = strtok(textlinetotok, "\"");
if (stringID == NULL)
crashAndBurn("Bad Format in StringTable",
lineNumber, lineFile, textline);
if (strstr(stringID, "L") != NULL)
*(strstr(stringID, "L")) = '\0';
if(!LexOneLine(stringID))
crashAndBurn("StringID in bad form",
lineNumber, lineFile, textline);
// Note: we are indexing only using the low 16 bits
stringIDint = (UINT16) Expr_Eval();
if (stringIDint == 0){
crashAndBurn("STRINGTABLE in bad form or missing END",
lineNumber, lineFile, textline);
}
stringValue = strtok(NULL, "\"");
if (stringValue == NULL){
//STRING VALUE WAS NOT ON SAME LINE AS STRING ID
//SEARCH UPCOMING LINES TO SEE IF WE CAN FIND IT
int oldLineNumber = lineNumber;
clearBlanksAndLineUpdates(strippedrc,
&lineNumber, &lineFile, &textline, FALSE);
if (textline == NULL){
crashAndBurn("Unexpected end of file in stringtable",
lineNumber, lineFile, "");
}
textlinetotok = malloc(strlen(textline) + 1);
if (textlinetotok == NULL)
crashAndBurn("Out of memory -textlinetotok",
UNKNOWN_LINE_NUMBER,"","");
textlinetotok = strcpy(textlinetotok, textline);
if (strchr(textline, '\"') != NULL){
stringValue = strtok(strchr(textline, '\"'), "\"");
}
else{
/// Found a line which is not blank, but does not have
/// a string surrounded by quotes in it
crashAndBurn("StringID with no StringValue in STRINGTABLE",
oldLineNumber, lineFile, "");
}
}
stringValueFormatted = FormatString(stringValue);
if (stringValueFormatted == NULL){
crashAndBurn("Unknown escape sequence in String Value",
lineNumber,lineFile, textline);
}
stringValueOut = convertToUTF8(stringValueFormatted);
free(stringValueFormatted);
stringtable[stringTableCounter].StringID = stringIDint;
stringtable[stringTableCounter].StringValue = stringValueOut;
stringTableCounter++;
if ((firstword = strtok(NULL, "\n")) != NULL){
if((strchr(firstword, '}')) != NULL){
break;
}
}
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("end of file before END in stringtable",
lineNumber, lineFile, "");
}
else{
lineNumber++;
}
}
}
else if (!strcmp(firstword, "#pragma")){
LPSTR pragma;
pragma = strtok(NULL, " (");
if(!strcmp(pragma, "code_page")){
LPSTR cpString;
cpString = strtok(NULL, ")");
codepage = strtoul(cpString, NULL, 0);
if(codepage == 0){ //couldn't conver to int
crashAndBurn("Bad Format in code_page pragma",
lineNumber, lineFile, textline);
}
if (!IsValidCodePage(codepage)){
if (warn)
printf("WARNING, codepage %i is invalid\n", codepage);
else
crashAndBurn("Invalid Code Page Value",
lineNumber, lineFile, textline);
}
if(verbose)
printf("Codepage pragma found, updating codepage to %i\n", codepage);
}
else if(!strcmp(pragma, "once")){
//ignore
}
else{
crashAndBurn("unknown pragma", lineNumber, lineFile, textline);
}
}
else if(!strcmp(firstword, "typedef")){
int i;
////SKIP ALL LINES UNTIL FIRST {, THEN STOP WHEN FIND } THAT MATCHES
printf("WARNING...TYPEDEF in %s:\n %s\n", lineFile, textline);
fgets(textline, BUF_SIZE, strippedrc);
lineNumber++;
while (strstr(textline, "{") == NULL){ //Skipping to first {
if (fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file", ++lineNumber, lineFile, "");
}
else{
lineNumber++;
}
}
textline = strstr(textline, "{");
textline++;
for (i = 1; i != 0;){
//i is the number of { without }
lineNumber++;
if (strstr(textline, "{") != NULL){
textline = strstr(textline, "{");
textline++;
i++;
}
else if (strstr(textline, "}") != NULL){
textline = strstr(textline, "}");
textline++;
i--;
}
else{
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file", ++lineNumber, lineFile, "");
}
lineNumber++;
i = i;
}
}
}
else { ///SOME OTHER RESOURCE
LPSTR secondword = strtok(NULL, " \t\n");
if (secondword == NULL)
secondword = "";
if(!strcmp(firstword, "FONT")
|| !strcmp(firstword, "AUTO3STATE")
|| !strcmp(firstword, "AUTORADIOBUTTON")
|| !strcmp(firstword, "COMBOBOX")
|| !strcmp(firstword, "CONTROL")
|| !strcmp(firstword, "CTEXT")
|| !strcmp(firstword, "DEFPUSHBUTTON")
|| !strcmp(firstword, "EDITTEXT")
|| !strcmp(firstword, "PUSHBOX")
|| !strcmp(firstword, "PUSHBUTTON")
|| !strcmp(firstword, "RADIOBUTTON")
|| !strcmp(firstword, "RTEXT")
|| !strcmp(firstword, "SCROLLBAR")
|| !strcmp(firstword, "STATE3")
|| !strcmp(firstword, "CAPTION")
|| !strcmp(firstword, "LANGUAGE")
|| !strcmp(firstword, "MENUITEM")
|| !strcmp(firstword, "STYLE")
){
if (verbose) //These resources are one line of text in resource file
printf("Resource %s found on line % i in \n\t%s, ignoring\n",
firstword, lineNumber, lineFile);
continue;
}
else if(!strcmp(secondword, "TYPELIB")
|| !strcmp(secondword, "typelib")
|| !strcmp(secondword, "ICON")
|| !strcmp(secondword, "BITMAP")
|| !strcmp(secondword, "CURSOR")
|| !strcmp(secondword, "REGISTRY")
|| !strcmp(secondword, "FONT")
|| !strcmp(secondword, "MESSAGETABLE")){
if (verbose) //These resources are one line of text in resource file
printf("Resource %s found on line % i in \n\t%s, ignoring\n",
secondword, lineNumber, lineFile);
continue;
}
else if (!strcmp(secondword, "VERSIONINFO")
|| !strcmp(secondword, "TOOLBAR")
|| !strcmp(secondword, "MENU")
|| !strcmp(secondword, "ACCELERATORS")
|| !strcmp(secondword, "DIALOG")
|| !strcmp(secondword, "TEXTINCLUDE")
|| !strcmp(secondword, "DIALOGEX")
|| !strcmp(secondword, "MENUEX")
|| !strcmp(secondword, "RCDATA")
|| !strcmp(firstword, "POPUP")){
// THESE RESOURCES TAKE UP MORE THEN ONE LINE, DELIMITED BY
// BEGIN/END TAGS OR BRACES { }
// int i;
if (!strcmp(firstword, "POPUP")){
secondword = firstword;
}
if (verbose){
printf("Resource %s found on line % i in \n\t%s, ignoring\n",
secondword, lineNumber, lineFile);
}
removeDefinition(strippedrc, textline, TRUE, &lineNumber, &lineFile);
}
else { //USER DEFINED RESOURCE
// OF TWO FORMS, EITHER ONE LINE WITH FILENAME SPECIFYING RESOURCE
// OR MULTIPLE LINES, WITH RESOURCE IN SCRIPT SURROUNDED BY BEGIN/END
// TAGS OR BRACES { }
LPSTR thirdword;
printf("UNKNOWN resource %s found on line % i in \n\t%s, ignoring\n",
secondword, lineNumber, lineFile);
thirdword = strtok(NULL, " \t\n");
if (thirdword != NULL){ // RESOURCE DEFINED IN OTHER FILE, ONE LINE
continue;
}
else { // RESOURCE IN RESOURCE SCRIPT, SKIP DEFINITION
fgets(textline, BUF_SIZE, strippedrc);
lineNumber++;
removeDefinition(strippedrc, textline, FALSE, &lineNumber, &lineFile);
}
}
}
}
qsort((void *)stringtable, stringTableCounter, //sort array by id
sizeof(STRING_TABLE_ENTRY), steCompare);
//Open output file
if (verbose){
printf("Opening %s for writing\n",outFilename);
}
outfile = fopen(outFilename,"w");
if (outfile == NULL){
crashAndBurn("Couldn't open file for output",UNKNOWN_LINE_NUMBER, "", "");
}
for(i=0; i<stringTableCounter; i++){
if (stringtable[i].StringID == lastValue){ //CHECK FOR DOUBLES
LPSTR errorMessage;
errorMessage = malloc(sizeof("Duplicate StringID for Values: ") +
strlen(stringtable[i-1].StringValue) +
sizeof(", ") +
strlen(stringtable[i].StringValue) - 1);
if (errorMessage == NULL){
crashAndBurn("Out of memory",
UNKNOWN_LINE_NUMBER, "", "");
}
errorMessage = strcpy(errorMessage, "Duplicate StringID for Values: ");
errorMessage = strcat(errorMessage, stringtable[i-1].StringValue);
errorMessage = strcat(errorMessage, ", ");
errorMessage = strcat(errorMessage, stringtable[i].StringValue);
crashAndBurn(errorMessage, UNKNOWN_LINE_NUMBER, "", "");
//ERROR MESSAGE SHOWS VALUES WHICH SHARE AN ID
}
//PRINT OUT TO FILE
OutFileWritten = TRUE;
fprintf(outfile,"%u ",stringtable[i].StringID);
fprintf(outfile,"\"");
fprintf(outfile,stringtable[i].StringValue);
fprintf(outfile,"\"");
fprintf(outfile,"\n");
lastValue = stringtable[i].StringID;
}
fprintf(outfile,"%c", EOF);
if (verbose){
printf("Output file is: %s\n\n", outFilename);
}
// CLOSE BOTH OUTPUT FILE AND PREPROCESSOR (.i) FILE
if (strippedrc != NULL){
fclose(strippedrc);
}
if (outfile != NULL){
fclose(outfile);
}
if (!OutFileWritten) {
if (verbose) {
printf("Removing output file - no STRINGTABLEs were found\n");
}
DeleteFileA(outFilename);
}
//REMOVE .i FILE
if(DeleteFileA(strippedFilename)){
if(verbose){
printf("Removing %s\n", strippedFilename);
}
}
if (lineFile != NULL) {
free(lineFile);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// ConsoleControlHandler
//
// Parameters:
// dwCtrlType: the type of signal to be handled
//
// Returns:
// false to indicate the program should exit
//
///////////////////////////////////////////////////////////////////////////////
BOOL
__stdcall
ConsoleControlHandler(DWORD dwCtrlType)
{
if (strippedrc != NULL){
fclose(strippedrc);
}
if (outfile != NULL){
fclose(outfile);
}
DeleteFileA(strippedFilename);
DeleteFileA(outFilename);
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
//
// removeDefinition
//
///////////////////////////////////////////////////////////////////////////////
void removeDefinition(FILE * file,
LPSTR textline,
BOOL linesBeforeBegin,
int * lineNumber,
LPSTR * lineFile)
{
// LPSTR textline = malloc(BUF_SIZE);
int i;
if(linesBeforeBegin){
////SKIP ALL LINES UNTIL FIRST BEGIN OR {
////THEN STOP WHEN FIND END or }THAT MATCHES
while ((strstr(textline, "BEGIN") == NULL) &&
(strstr(textline, "{") == NULL)){
if (fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file",
++(*lineNumber), (*lineFile), "");
}
else {
(*lineNumber)++;
}
}
}
else if ((strstr(textline, "BEGIN") == NULL) &&
(strstr(textline, "{") == NULL)){
crashAndBurn("Bad Format in User Defined Resource",
*lineNumber, *lineFile, textline);
}
if(strstr(textline, "{") != NULL){ //Brace broke the loop
textline = strstr(textline, "{");
textline++;
}
else{ //BEGIN broke the loop
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file",
++(*lineNumber), (*lineFile), "");
}
else{
(*lineNumber)++;
}
}
for (i = 1; i != 0;){
LPSTR word;
LPSTR textlinetotok;
textlinetotok = malloc(strlen(textline) + 1);
if (textlinetotok == NULL)
crashAndBurn("Out of memory -textlinetotok",
UNKNOWN_LINE_NUMBER,"","");
textlinetotok = strcpy(textlinetotok, textline);
word = strtok(textlinetotok, " \t\n");
if (word == NULL)
word = "";
if (textline[0] == '\0')
break;
//i is the number of opens not yet closed
if (!strcmp(word, "BEGIN")){
i++;
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file",
++(*lineNumber), (*lineFile), "");
}
else{
(*lineNumber)++;
}
}
else if (strstr(textline, "{") != NULL){
i++;
textline = strstr(textline, "{");
textline++;
}
else if (!strcmp(word, "END")){
i--;
if(i != 0){
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file",
++(*lineNumber), (*lineFile), "");
}
}
else{
(*lineNumber)++;
}
}
else if (strstr(textline, "}") != NULL){
i--;
textline = strstr(textline, "}");
textline++;
}
else{
if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
crashAndBurn("Unexpected end of file",
++(*lineNumber), (*lineFile), "");
}
else{
(*lineNumber)++;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
//
// crashAndBurn
//
// Parameters:
// message: Message to be displayed to stderr
// lineNumber: Line number in .rc file in which error occured, or
// UNKNOWN_LINE_NUMBER if not line number specific or line
// number unknown.
// file: File in which the error occured, if using known line number
// badline: The line of text which caused the error, if using known line
// number.
//
///////////////////////////////////////////////////////////////////////////////
void crashAndBurn(LPSTR message, int lineNumber, LPSTR file, LPSTR badline){
if (strippedrc != NULL){
fclose(strippedrc);
}
if (outfile != NULL){
fclose(outfile);
}
if (strippedFilename)
{
DeleteFileA(strippedFilename);
}
if (outFilename)
{
DeleteFileA(outFilename);
}
fprintf(stderr, "resourcecompiler: fatal error RC0001 : ");
if(lineNumber != UNKNOWN_LINE_NUMBER){
fprintf(stderr, "on line %d in %s: \n", lineNumber, file);
if (badline != NULL && badline[0] != '\0') {
fprintf(stderr, "resourcecompiler: %s\n", badline);
}
}
fprintf(stderr, "resourcecompiler: %s\n", message);
exit(1);
}
///////////////////////////////////////////////////////////////////////////////
//
// ProcessParameters
//
// Parameters:
// argc: Number of arguments to be processed
// argv[]: Array of parameters
// compilerOptions: Options to be passed to the compiler for preprocessing
//
///////////////////////////////////////////////////////////////////////////////
void ProcessParameters(int argc, LPSTR argv[], LPSTR * compilerOptions)
{
char * p;
while(argc){
if (argc == 1){
if (!strcmp(argv[0], "/?") ||
!strcmp(argv[0], "-?") ||
!strcmp(argv[0], "/h") ||
!strcmp(argv[0], "-h")){
printUsage("");
}
#if PLATFORM_UNIX
// Allow absolute paths
else if (**argv == '-') {
#else // PLATFORM_UNIX
else if(**argv == '/' || **argv == '-'){
#endif // PLATFORM_UNIX
//LAST ARGUMENT SHOULD NOT BE OPTION, SHOULD BE FILENAME
printUsage("A filename must be specified\n");
}
else{ //One arguement is filename
FILE * inFile;
FILE * inFile2;
LPSTR inFileAppend;
inFilename = malloc(strlen(argv[0]) + 1);
if (inFilename == NULL){
crashAndBurn("Out of memory - inFilename",
UNKNOWN_LINE_NUMBER, "", "");
}
strcpy(inFilename, argv[0]);
inFileAppend = malloc(strlen(argv[0]) + sizeof(".rc"));
if (inFileAppend == NULL){
crashAndBurn("Out of memory - inFileAppend",
UNKNOWN_LINE_NUMBER, "", "");
}
inFileAppend = strcpy(inFileAppend, argv[0]);
inFileAppend = strcat(inFileAppend, ".rc");
inFile = fopen(inFilename, "r");
inFile2 = fopen(inFileAppend, "r");
if(inFile == NULL && inFile2 == NULL){
LPSTR errorMessage;
errorMessage = malloc(sizeof("Cannot Open File: ") +
+ strlen(inFilename));
if (errorMessage == NULL)
crashAndBurn("Out of memory - errorMessage",
UNKNOWN_LINE_NUMBER, "", "");
errorMessage = strcpy(errorMessage, "Cannot Open File: ");
errorMessage = strcat(errorMessage, inFilename);
crashAndBurn(errorMessage, UNKNOWN_LINE_NUMBER, "", "");
}
else{
//success!
if (inFile2 != NULL){
fclose(inFile2);
if (inFile == NULL){
inFilename = inFileAppend;
}
}
if (inFile != NULL){
fclose(inFile);
}
if (outFilename == NULL){
outFilename = changeExtension(inFilename, ".rc", ".satellite");
}
}
}
}
else if ( (**argv == '/') || (**argv == '-')){
for(p = *argv + 1; *p != '\0'; p++){
switch(tolower(*p)){
case '/':
case '-':
break;
case '?':
case 'h':
printUsage("");
break;
case 'c':
p++;
if(*p == '\0')
{
argv++;
argc--;
p = *argv;
}
codepage = strtoul(p, NULL, 16);
if (!IsValidCodePage(codepage)){
if (warn)
printf("WARNING, codepage %i is invalid\n", codepage);
else
crashAndBurn("Invalid Code Page Value",
UNKNOWN_LINE_NUMBER, "", "");
}
p += strlen(p) - 1;
break;
case 'd':
p++;
if(*p == '\0')
{
argv++;
argc--;
p = *argv;
}
*compilerOptions = realloc(*compilerOptions,
strlen(*compilerOptions) +
sizeof(DEFINE_PREFIX) +
strlen(p) +
sizeof(" ") - 1);
if (*compilerOptions == NULL){
crashAndBurn("Out of memory - compilerOptions",
UNKNOWN_LINE_NUMBER, "", "");
}
*compilerOptions = strcat(*compilerOptions, DEFINE_PREFIX);
*compilerOptions = strcat(*compilerOptions, p);
*compilerOptions = strcat(*compilerOptions, " ");
p += strlen(p) - 1;
break;
case 'f':
if (*(++p) != 'o'){
printUsage("'f' must be followed by 'o' for renaming output\n");
}
else{
p++;
outFilename = p;
if(!strcmp(outFilename,""))
{
argv++;
argc--;
p = *argv;
}
outFilename = p;
p += strlen(outFilename) - 1;
if (verbose)
printf("Set output file name to %s\n", outFilename);
break;
}
case 'i':
p++;
if(*p == '\0')
{
argv++;
argc--;
p = *argv;
}
*compilerOptions = realloc(*compilerOptions,
strlen(*compilerOptions) +
sizeof(INCLUDE_PREFIX) +
strlen(p) +
strlen(" "));
if (*compilerOptions == NULL){
crashAndBurn("Out of memory - compilerOptions",
UNKNOWN_LINE_NUMBER, "", "");
}
*compilerOptions = strcat(*compilerOptions, INCLUDE_PREFIX);
*compilerOptions = strcat(*compilerOptions, p);
*compilerOptions = strcat(*compilerOptions, " ");
p += strlen(p) - 1;
break;
case 'l':
p++;
if(*p == '\0')
{
argv++;
argc--;
p = *argv;
}
//THIS ARGUMENT IS NOT USED BY RC!!
//Codepage is set by #pragma(codepage) or \c
p += strlen(p) - 1;
break;
case 'n':
break;
case 'r':
break;
case 'u':
p++;
if(*p == '\0'){
argv++;
argc--;
p = *argv;
}
*compilerOptions = realloc(*compilerOptions,
strlen(*compilerOptions) +
sizeof("/U") +
strlen(p) +
strlen(" "));
if (*compilerOptions == NULL){
crashAndBurn("Out of memory - compilerOptions",
UNKNOWN_LINE_NUMBER, "", "");
}
*compilerOptions = strcat(*compilerOptions, "/U");
*compilerOptions = strcat(*compilerOptions, p);
*compilerOptions = strcat(*compilerOptions, " ");
p += strlen(p) - 1;
break;
case 'v':
verbose = TRUE;
printf("Processing Arguments...\n");
break;
case 'w':
warn = TRUE;
break;
case 'x':
*compilerOptions = realloc(*compilerOptions,
strlen(*compilerOptions) +
sizeof("/X "));
if (*compilerOptions == NULL){
crashAndBurn("Out of memory - compilerOptions",
UNKNOWN_LINE_NUMBER, "", "");
}
*compilerOptions = strcat(*compilerOptions, "/X ");
break;
case 'z':
//ARGUMENT IGNORED, PROVIDED FOR COMPATIBILITY
p++;
if(*p == '\0')
{
argv++;
argc--;
p = *argv;
}
p += strlen(p) - 1;
break;
default:
printUsage("Unknown argument\n");
break;
}
}
}
else{ //ARGUMENT THAT DOESN'T START WITH / OR - BUT IS NOT LAST
printUsage("Unknown argument, no - or /\n");
}
argc--;
argv++;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// printUsage
//
// Prints the Usage Statement for rc
//
// Parameters:
// message: A message to be printed before the usage statemnt, often
// indicating what caused the usage statement to be printed
//
///////////////////////////////////////////////////////////////////////////////
void printUsage(LPSTR message){
printf(message);
printf("Usage: resourcecompiler [options] inputfile\n");
printf("\nOptions: \n\n");
printf("\t/? Displays a list of resource compiler command-line options.\n");
printf("\t/d Defines a symbol for the preprocessor that you can test\n");
printf("\t with the ifdef directive\n");
printf("\t/fo Rename output file\n");
printf("\t/h Displays a list of command-line options\n");
printf("\t/i Searches the specified directory before searching the\n");
printf("\t directories specified by the INCLUDE enviornment variable\n");
printf("\t/c specifies the codepage to be used for compilation\n");
printf("\t/n Null terminates all strings in the string table\n");
printf("\t/r Ignored. Provided for compatibility with existing\n");
printf("\t makefiles\n");
printf("\t/l Ignored. Provided for compatibility with existing\n");
printf("\t makefiles\n");
printf("\t/z Ignored. Provided for compatibility with existing\n");
printf("\t makefiles\n");
printf("\t/u Undefines a symbol for the preprocessor\n");
printf("\t/v Displays messages that report on the progress of the\n");
printf("\t compiler\n");
printf("\t/w Warns if invalid codepage (default is error)\n");
printf("\t/x Prevents RC from checking INCLUDE enviornment variable\n");
printf("\t when searching for header files or resource files\n\n");
exit(1);
}
///////////////////////////////////////////////////////////////////////////////
//
// preProcessFile
//
// Calls the C preprocessor on the input file to produce the stripped file.
//
// Parameters:
// compilerOptions: Command Line arguments to be passed to the compiler
//
///////////////////////////////////////////////////////////////////////////////
void preProcessFile(LPSTR compilerOptions)
{
LPSTR commandLine;
PROCESS_INFORMATION procInfo;
STARTUPINFOA startInfo;
DWORD exitCode;
commandLine = malloc(strlen(COMPILER_NAME) +
strlen(compilerOptions) +
strlen(inFilename) +
strlen("\"\"") + 1);
if (commandLine == NULL){
crashAndBurn("Out of memory - commandLine", UNKNOWN_LINE_NUMBER, "", "");
}
commandLine = strcpy(commandLine, COMPILER_NAME);
strcat(commandLine, compilerOptions);
strcat(commandLine, "\"");
strcat(commandLine, inFilename);
strcat(commandLine, "\"");
memset(&startInfo, 0, sizeof(startInfo));
startInfo.cb = sizeof(startInfo);
if (verbose){
printf("\n\n\nC compiler command line is: %s\n\n\n", commandLine);
}
if(!CreateProcessA(NULL,
commandLine,
NULL,
NULL,
0,
0,
NULL,
NULL,
&startInfo,
&procInfo)){
crashAndBurn("Failed to spawn preprocessor", UNKNOWN_LINE_NUMBER,"","");
}
WaitForSingleObject( procInfo.hProcess, INFINITE );
GetExitCodeProcess(procInfo.hProcess, &exitCode);
if (exitCode != 0){
crashAndBurn("Preprocessor Failed", UNKNOWN_LINE_NUMBER, "", "");
}
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
free(commandLine);
}
///////////////////////////////////////////////////////////////////////////////
//
// convertToUTF8
//
// Parameters:
// inputString: pointer to string of characters to be converted
//
// Returns:
// Pointer to a string converted into UTF8
//
///////////////////////////////////////////////////////////////////////////////
LPSTR convertToUTF8(LPSTR inputString)
{
int i;
int error;
int numOfWideChars;
int numOfOutChars;
LPWSTR wideString;
LPSTR outputString;
numOfWideChars = MultiByteToWideChar(codepage, MB_PRECOMPOSED,
inputString,
strlen(inputString),
NULL, 0);
wideString = malloc(numOfWideChars*sizeof(WCHAR));
if (wideString == NULL){
crashAndBurn("Out of memory - wideString", UNKNOWN_LINE_NUMBER, "", "");
}
i = MultiByteToWideChar(codepage,MB_PRECOMPOSED,inputString,
strlen(inputString),wideString,
numOfWideChars);
if (i == 0){
error = GetLastError();
if (error == ERROR_INSUFFICIENT_BUFFER)
crashAndBurn("Converting to UTF8 - INSUFFICIENT_BUFFER\n",
UNKNOWN_LINE_NUMBER, "", "");
else if (error == ERROR_INVALID_FLAGS)
crashAndBurn("Converting to UTF8 - INVALID_FLAGS\n",
UNKNOWN_LINE_NUMBER, "", "");
else if (error == ERROR_INVALID_PARAMETER)
crashAndBurn("Converting to UTF8 - INVALID_PARAMETER 1\n",
UNKNOWN_LINE_NUMBER, "", "");
else if (error == ERROR_NO_UNICODE_TRANSLATION )
crashAndBurn("Converting to UTF8 - NO_UNICODE_TRANSLATION\n",
UNKNOWN_LINE_NUMBER, "", "");
else
crashAndBurn("Error converting to UTF8\n",
UNKNOWN_LINE_NUMBER, "", "");
}
numOfOutChars = WideCharToMultiByte(CP_UTF8, 0, wideString,
numOfWideChars, NULL, 0, NULL, NULL);
outputString = malloc(numOfOutChars + 1);
if (outputString == NULL){
crashAndBurn("Out of memory - outputString", UNKNOWN_LINE_NUMBER, "", "");
}
i = WideCharToMultiByte(CP_UTF8, 0, wideString,
numOfWideChars, outputString,
numOfOutChars, NULL, NULL);
if (i == 0){
error = GetLastError();
if (error == ERROR_INSUFFICIENT_BUFFER)
crashAndBurn("Converting to UTF8 - INSUFFICIENT_BUFFER\n",
UNKNOWN_LINE_NUMBER, "", "");
else if (error == ERROR_INVALID_FLAGS)
crashAndBurn("Converting to UTF8 - INVALID_FLAGS\n",
UNKNOWN_LINE_NUMBER, "", "");
else if (error == ERROR_INVALID_PARAMETER)
crashAndBurn("Converting to UTF8 - INVALID_PARAMETER 1\n",
UNKNOWN_LINE_NUMBER, "", "");
else
crashAndBurn("Error converting to UTF8\n",
UNKNOWN_LINE_NUMBER, "", "");
}
outputString[numOfOutChars] = '\0';
free(wideString);
return outputString;
}
///////////////////////////////////////////////////////////////////////////////
//
// FormatString
//
// Parameters:
// inputString: pointer to string of characters to be formatted
//
// Returns:
// Pointer to a string in which % is replaced by %% and all character
// sequences interpretable as escape characters are replaced by the
// corresponding character
//
///////////////////////////////////////////////////////////////////////////////
LPSTR FormatString(LPSTR inputString)
{
LPSTR formattedString;
int i,j, stringLength;
int numOfCharsUsed;
char escchar;
// Gets longer only when % is present
// Max length is when all %s...becomes twice as long
formattedString = malloc(2*strlen(inputString) + 1);
if (formattedString == NULL){
crashAndBurn("Out of memory - formattedString",
UNKNOWN_LINE_NUMBER, "", "");
}
stringLength = (int)strlen(inputString);
for (i = 0, j = 0; i < stringLength; i++, j++){
if ((int) inputString[i] == '\\'){
//replace with escape character
numOfCharsUsed = getEscapeCharacter(&inputString[i+1], &escchar);
if (numOfCharsUsed == 0){
return NULL;
}
i += numOfCharsUsed;
inputString[i] = escchar;
}
if (inputString[i] == '%'){
//replace % with %%
formattedString[j] = inputString[i];
formattedString[++j] = inputString[i];
}
else {
//use character given
formattedString[j] = inputString[i];
}
}
formattedString[j] = '\0';
return formattedString;
}
///////////////////////////////////////////////////////////////////////////////
//
// changeExtension
//
// Changes the extension from old to new, or appends new filename didn't
// have extension old
//
// Parameters:
// filename: filename on which to change extension
// old: old extension
// new: new extension
//
// Returns:
// Pointer to a string containing the new filename
//
///////////////////////////////////////////////////////////////////////////////
LPSTR changeExtension(LPSTR filename, LPSTR old, LPSTR new)
{
LPSTR returnValue;
LPSTR tempString;
returnValue = malloc(strlen(filename) + strlen(new));
if (returnValue == NULL)
crashAndBurn("Out of memory - returnValue", UNKNOWN_LINE_NUMBER, "", "");
returnValue = strcpy(returnValue, filename);
tempString = strstr(returnValue, old);
if (tempString != NULL)
tempString[0] = '\0';
returnValue = strcat(returnValue, new);
return(returnValue);
}
///////////////////////////////////////////////////////////////////////////////
//
// steCompare
// (for use with qsort)
//
// Parameters:
// first: pointer to first STRING_TABLE_ENTRY to be compared
// second: pointer to second STRING_TABLE_ENTRY to be compared
//
// Returns:
// negative value if first->StringID < second->StringID
// zero if first.StringID = second.StringID
// positive value if first->StringID > second->StringID
//
///////////////////////////////////////////////////////////////////////////////
int __cdecl steCompare(const void *first, const void *second){
int result = 0;
if (((STRING_TABLE_ENTRY * )first)->StringID >
((STRING_TABLE_ENTRY * )second)->StringID)
result = 1;
else if (((STRING_TABLE_ENTRY * )first)->StringID ==
((STRING_TABLE_ENTRY * )second)->StringID)
result = 0;
else
result = -1;
return result;
}
///////////////////////////////////////////////////////////////////////////////
//
// updateLineNumber
//
// Parameters:
// line: line of the format #line linenumber filename. To be used to
// determine new values for lineNumber and lineFile.
// lineNumber: pointer to the int which holds current line number
// lineFile: pointer to the LPSTR which holds the current file
// corresponding to lineNumber
//
///////////////////////////////////////////////////////////////////////////////
void updateLineNumber(LPSTR line, int * lineNumber, LPSTR * lineFile){
LPSTR lineNumberString;
LPSTR linetotok;
LPSTR fileToken;
LPSTR fileTokenCopy;
linetotok = malloc(strlen(line) + 1);
if (linetotok == NULL)
crashAndBurn("Out of memory - linetotok", UNKNOWN_LINE_NUMBER, "", "");
strcpy(linetotok, line);
strtok(linetotok, " "); ///removes #line
lineNumberString = strtok(NULL, "\"");
*lineNumber = strtoul(lineNumberString, NULL, 0) - 1;
fileToken = strtok(NULL, "\"");
fileTokenCopy = malloc (strlen(fileToken) + 1);
if (fileTokenCopy == NULL)
crashAndBurn("Out of memory - fileTokenCopy", UNKNOWN_LINE_NUMBER, "", "");
strcpy(fileTokenCopy, fileToken);
if (*lineFile != NULL) {
free(*lineFile);
}
*lineFile = FormatString(fileTokenCopy);
if (*lineFile == NULL){
printf(line);
crashAndBurn("Error in format of preprocessed file",
UNKNOWN_LINE_NUMBER, "", "");
}
if (*lineFile == NULL){
crashAndBurn("Out of memory - lineFile", UNKNOWN_LINE_NUMBER, "", "");
}
free(linetotok);
}
///////////////////////////////////////////////////////////////////////////////
//
// clearBlanksAndLineUpdates
//
// Parameters:
// file: pointer to file from which the lines are to be taken
// lineNumber: pointer to the int which holds current line number
// lineFile: pointer to the LPSTR which holds the current file
// corresponding to lineNumber
// textline: buffer for lines read from file. if checkCurrent is true,
// will be read as first line to be examined, at return,
// will hold the value of the first non blank or
// line updating line.
// checkCurrent: if true, the value of textline when the function is called
// will be checked for blankness or line update. otherwise the
// initial string in textline will be ignored
//
///////////////////////////////////////////////////////////////////////////////
void clearBlanksAndLineUpdates(FILE * file,
int * lineNumber,
LPSTR * lineFile,
LPSTR * textline,
BOOL checkCurrent)
{
//LOOP TO REMOVE EFFECTIVELY BLANK LINES, AND #line LINES
do{
//IF checkCurrent WAS SET TO TRUE, DON'T READ LINE FIRST TIME
if(!checkCurrent){
if(fgets(*textline, BUF_SIZE, file) == NULL){
*textline = NULL;
break;
}
else{
(*lineNumber)++;
}
}
else{
checkCurrent = FALSE;
}
// Only recognize the prefix at the start of the line
if(startsWithLineMarker(*textline))
updateLineNumber(*textline, lineNumber, lineFile);
}while((strspn(*textline, " \t\n") == strlen(*textline))
|| startsWithLineMarker(*textline));
}
///////////////////////////////////////////////////////////////////////////////
//
// startsWithLineMarker
//
// Parameters:
// line: a line in a preprocessed file
//
// Returns:
// Whether the line starts with a preprocessor line directive
//
///////////////////////////////////////////////////////////////////////////////
BOOL startsWithLineMarker(const char *line)
{
#if PLATFORM_UNIX
return (line[0] == LINEMARKER_PREFIX && (line[1] == ' ' || isdigit(line[1])));
#else
return (strstr(line, LINEMARKER_PREFIX) == line);
#endif
}
///////////////////////////////////////////////////////////////////////////////
//
// getEscapeCharacter
//
// Parameters:
// input: pointer to string of characters to be interpreted as escape
// sequence
// output: pointer to location in which to store escape character
//
// Returns:
// Number of characters used to form escape sequence if successfull,
// 0 if string could not be interpreted
//
///////////////////////////////////////////////////////////////////////////////
int getEscapeCharacter (char * input, char * output){
int i;
int value;
int c;
switch(*input){
case 'x':
for (i = 0, value = 0; isxdigit(input[i]); i++){
c = input[i];
if (isupper(c)){
c -= ('A' - 10);
}
else if (islower(c)){
c -= ('a' - 10);
}
else{
c -= '0';
}
value = (16 * value) + c;
}
if(!i){
fprintf(stderr, "Bad Hex value while converting escape character\n");
return 0;
}
else if (value < 255){
fprintf(stderr,
"Out of range Hex value while converting escape character\n");
return 0;
}
*output = (char) value;
return i + 1;
case '0':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
for ( i = 0, value = 0;
input[i] <= '7' && input[i] >= '0' && i <= 3;
i++){
value = (8 * value) + (input[i] - '0');
}
if (!i){
printf("Bad Octal value while converting escape character\n");
return 0;
}
else if (value > 255){
printf("Out of range Octal value while converting escape character\n");
return 0;
}
*output = (char) value;
return i + 1;
case 'a':
*output = '\a';
return 1;
case 'b':
*output = '\b';
return 1;
case 'f':
*output = '\f';
return 1;
case 'n':
*output = '\n';
return 1;
case 'r':
*output = '\r';
return 1;
case 't':
*output = '\t';
return 1;
case 'v':
*output = '\v';
return 1;
case '?':
case '\\':
case '\'':
*output = *input;
return 1;
default:
return 0;
}
}