mirror of
https://github.com/frida/tinycc
synced 2024-11-24 16:49:44 +03:00
59e18aee0e
Tested under linux exec the "mk-it" shell script and you'll end up with a portable tinycc executable that doesn't depend on anything else.
616 lines
18 KiB
C
616 lines
18 KiB
C
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <assert.h>
|
|
|
|
/* Version information */
|
|
const char *versionInfo =
|
|
"bin2c version 1.0 by Fatih Aygun\n"
|
|
"\n"
|
|
"Visit sourceforge.net/projects/bin2c to for new versions.\n"
|
|
"This program is public domain, do whatever you want with it.\n"
|
|
;
|
|
|
|
/* Command line help */
|
|
const char *commandLineHelp =
|
|
"Usage: bin2c [OPTION...] FILE [FILE...]\n"
|
|
"bin2c creates a C soures and headers from binary files.\n"
|
|
"\n"
|
|
"Examples:\n"
|
|
" bin2c -o foo.h bar.bin # Create 'foo.h' from the contents of\n"
|
|
" 'bar.bin'\n"
|
|
" bin2c -o foo.h file1 file2 # Create 'foo.h' from the contents of 'file1'\n"
|
|
" and 'file2'\n"
|
|
" bin2c -d foo.h -o foo.c bar.bin # Create 'foo.c' and 'foo.h' from the\n"
|
|
" contents of 'bar.bin'\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -d, --header <file name> Name a header file (A header file will not be\n"
|
|
" created unless explicitly named)\n"
|
|
" -h, --help Print this command line help and exit immediately\n"
|
|
" -o, --output <file name> Name an output file\n"
|
|
" -m, --macro Create the size definition as a macro instead of\n"
|
|
" a const\n"
|
|
" -n, --name <symbol name> Name the symbol to be defined\n"
|
|
" -v, --version Print version information and exit immediately\n"
|
|
"\n"
|
|
"Visit sourceforge.net/projects/bin2c to for new versions.\n"
|
|
"This program is public domain, do whatever you want with it.\n"
|
|
;
|
|
|
|
/* Output formatting macros */
|
|
#define bytesPerLine 16
|
|
#define indentString " "
|
|
#define outputFormatBase "0x%.2X"
|
|
#define outputFormat outputFormatBase", "
|
|
#define outputFormatEOL outputFormatBase",\n"
|
|
#define outputFormatEOF outputFormatBase"\n"
|
|
#define sizeDefinition "const long int bin2c_%s_size = %d;\n"
|
|
#define sizeMacroDefinition "#define bin2c_%s_size %d\n"
|
|
#define typeName "const unsigned char"
|
|
|
|
/* Define bool */
|
|
#define bool int
|
|
#define false 0
|
|
#define true 1
|
|
|
|
/* Global variables */
|
|
FILE *outputFile=0; /* Current output file stream */
|
|
const char *outputFileName=0; /* Current output file name */
|
|
FILE *headerFile=0; /* Current header file */
|
|
const char *headerFileName=0; /* Current header file name */
|
|
FILE *tmpFuncFile=0; /* File to write final function that search for files */
|
|
const char *tmpFuncFileName=0; /* Current tmpFunc file name */
|
|
char *symbolName=0; /* Current symbol name */
|
|
char *headerSymbol=0; /* Symbol for multiple inclusion protection */
|
|
bool createMacro = false; /* Should we create a define instead of a const? */
|
|
|
|
/* Error messages */
|
|
const char* cantOpen = "Can't open file '%s'";
|
|
const char* cantClose = "Can't close file '%s'";
|
|
const char* cantRemove = "Can't remove file '%s'";
|
|
const char* cantWrite = "Can't write to file '%s'";
|
|
const char* noOutputFilesAreNamed = "No output files are named";
|
|
const char* noHeaderFilesAreNamed = "No header files are named";
|
|
const char* cantMalloc = "Can't allocate memory for '%s'";
|
|
const char* cantSeek = "Can't seek in the file '%s'";
|
|
const char* cantDetermine = "Can't determine the file size of '%s'";
|
|
const char* cantRead = "Can't read from file %s";
|
|
const char* unknownOption = "Unknown option '%s'";
|
|
const char* tryHelp = "Try 'bin2c --help' for more information.\n";
|
|
const char* noSymbolName = "No symbol name is given";
|
|
const char* notValidId = "'%s' is not a valid identifier";
|
|
const char* symbolNameGiven = "Symbol name is given twice";
|
|
|
|
/* Print a formatted error message */
|
|
static void vprintfError(const char *format, va_list args)
|
|
{
|
|
fprintf(stderr, "bin2c: ");
|
|
vfprintf(stderr, format, args);
|
|
if (errno) {
|
|
fprintf(stderr, ": ");
|
|
perror("");
|
|
errno = 0;
|
|
} else {
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
static void printfError(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vprintfError(format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* Panic */
|
|
static void panic()
|
|
{
|
|
/* Close and remove the output file if it's open */
|
|
if (outputFile) {
|
|
if (fclose(outputFile)) {
|
|
printfError(cantClose, outputFileName);
|
|
}
|
|
|
|
if (remove(outputFileName)) {
|
|
printfError(cantRemove, outputFileName);
|
|
}
|
|
}
|
|
|
|
/* Close and remove the header file if it's open */
|
|
if (headerFile) {
|
|
if (fclose(headerFile)) {
|
|
printfError(cantClose, headerFileName);
|
|
}
|
|
|
|
if (remove(headerFileName)) {
|
|
printfError(cantRemove, headerFileName);
|
|
}
|
|
}
|
|
|
|
/* Close and remove the header file if it's open */
|
|
if (tmpFuncFile) {
|
|
if (fclose(tmpFuncFile)) {
|
|
printfError(cantClose, tmpFuncFileName);
|
|
}
|
|
|
|
if (remove(tmpFuncFileName)) {
|
|
printfError(cantRemove, tmpFuncFileName);
|
|
}
|
|
}
|
|
|
|
/* Exit with an error code */
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Check a contidion and panic if it's false */
|
|
static void check(bool condition, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (!condition) {
|
|
va_start(args, format);
|
|
vprintfError(format, args);
|
|
va_end(args);
|
|
panic();
|
|
}
|
|
}
|
|
|
|
/* Write a formatted string to the output file and check for errors */
|
|
static void output(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
/* Try to write to the file */
|
|
va_start(args, format);
|
|
vfprintf(outputFile, format, args);
|
|
va_end(args);
|
|
|
|
/* Check for errors */
|
|
check(!ferror(outputFile), cantWrite, outputFileName);
|
|
}
|
|
|
|
/* Write a formatted string to the header file and check for errors */
|
|
static void header(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
/* Try to write to the file */
|
|
va_start(args, format);
|
|
vfprintf(headerFile, format, args);
|
|
va_end(args);
|
|
|
|
/* Check for errors */
|
|
check(!ferror(headerFile), cantWrite, headerFileName);
|
|
}
|
|
|
|
/* Write a formatted string to the tmp file and check for errors */
|
|
static void tmpFunc(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
/* Try to write to the file */
|
|
va_start(args, format);
|
|
vfprintf(tmpFuncFile, format, args);
|
|
va_end(args);
|
|
|
|
/* Check for errors */
|
|
check(!ferror(tmpFuncFile), cantWrite, tmpFuncFileName);
|
|
}
|
|
|
|
/* Allocate a valid C identifier from a file name */
|
|
static char *fileNameToSymbol(const char *fileName)
|
|
{
|
|
char *name;
|
|
int i;
|
|
|
|
name = malloc(strlen(fileName) + 1);
|
|
check(name != 0, cantMalloc, "the symbol name");
|
|
|
|
/* Copy the file name to symbolName */
|
|
strcpy(name, fileName);
|
|
|
|
/* Replace every non alphanumerical character with '_' */
|
|
for (i = 0; name[i]; ++i) {
|
|
if (!((name[i] >= 'a' && name[i] <= 'z') ||
|
|
(name[i] >= 'A' && name[i] <= 'Z') ||
|
|
(name[i] >= '0' && name[i] <= '9'))) {
|
|
name[i] = '_';
|
|
}
|
|
}
|
|
|
|
/* Replace the first character with '_' if it's a digit */
|
|
if (name[0] >= '0' && name[0] <= '9') {
|
|
name[0] = '_';
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
int isdir(const char * path)
|
|
{
|
|
int bOK = 0;
|
|
#ifndef _MSC_VER
|
|
struct stat buf;
|
|
if (path == NULL) return 0;
|
|
if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode)) bOK=1;
|
|
#else
|
|
wchar_t *wcpath = to_wide_string((char*)path);
|
|
if (wcpath == NULL) return FALSE;
|
|
bOK = isdirW(wcpath);
|
|
FREE(wcpath);
|
|
#endif
|
|
return bOK;
|
|
}
|
|
|
|
/* Process an input file */
|
|
static int processFile(const char *fileName, long int from, long int to)
|
|
{
|
|
bool symbolNameAllocated = false; /* Did we allocate the symbolName? */
|
|
long int i, j; /* Indexes for various purposes */
|
|
FILE *inputFile; /* Input file stream */
|
|
int byte; /* Byte read from the input */
|
|
long int fsize=0;
|
|
|
|
if(isdir(fileName)) return 0;
|
|
|
|
/* Be sure that the output file is open */
|
|
check(outputFile != 0, noOutputFilesAreNamed);
|
|
|
|
/* Write the comment */
|
|
output("\n/* Contents of file %s */\n", fileName);
|
|
if (headerFile) {
|
|
header("\n/* Contents of file %s */\n", fileName);
|
|
}
|
|
|
|
/* Make up a suitable symbolName if we don't have one */
|
|
if (!symbolName) {
|
|
symbolName = fileNameToSymbol(fileName);
|
|
symbolNameAllocated = true;
|
|
}
|
|
|
|
/* Open the input file */
|
|
inputFile = fopen(fileName, "rb");
|
|
check(inputFile != 0, cantOpen, fileName);
|
|
|
|
/* If to == -1, set it to the last byte of the file */
|
|
if (to == -1) {
|
|
check(!fseek(inputFile, 0, SEEK_END), cantSeek, fileName);
|
|
to = ftell(inputFile);
|
|
check(to >= 0, cantDetermine, fileName);
|
|
--to;
|
|
}
|
|
|
|
assert(from <= to - 1);
|
|
|
|
/* Position the file to "from" */
|
|
check(!fseek(inputFile, from, SEEK_SET), cantSeek, fileName);
|
|
|
|
fsize = to - from + 1;
|
|
|
|
/* Output the size definition */
|
|
if (headerFile) {
|
|
if (createMacro) {
|
|
header(sizeMacroDefinition, symbolName, fsize);
|
|
} else {
|
|
header("extern "sizeDefinition, symbolName, fsize);
|
|
output(sizeDefinition, symbolName, fsize);
|
|
}
|
|
} else {
|
|
if (createMacro) {
|
|
output(sizeMacroDefinition, symbolName, fsize);
|
|
} else {
|
|
output(sizeDefinition, symbolName, fsize);
|
|
}
|
|
}
|
|
|
|
if (tmpFuncFile) {
|
|
tmpFunc("\t{\"/_attach_/%s\", bin2c_%s, %d},\n", fileName, symbolName, fsize);
|
|
}
|
|
/* Output the definition */
|
|
output("%s bin2c_%s[%d] = {\n", typeName, symbolName, fsize);
|
|
if (headerFile) {
|
|
header("extern %s *bin2c_%s;\n", typeName, symbolName, fsize);
|
|
}
|
|
|
|
/* Output the contents */
|
|
j = 1;
|
|
for (i = from; i <= to; ++i) {
|
|
/* Read a byte from the input file */
|
|
byte = fgetc(inputFile);
|
|
check(byte != EOF, cantRead, fileName);
|
|
|
|
/* Output the indentation if it's the first byte of a line */
|
|
if (j == 1) {
|
|
output(indentString);
|
|
}
|
|
|
|
/* Output the actual byte */
|
|
if (i == to) {
|
|
/* Output the last byte */
|
|
output(outputFormatEOF, byte);
|
|
} else if (j == bytesPerLine) {
|
|
/* Output the last byte of a line */
|
|
output(outputFormatEOL, byte);
|
|
} else {
|
|
/* Output a normal byte */
|
|
output(outputFormat, byte);
|
|
}
|
|
|
|
/* Next byte */
|
|
++j;
|
|
if (j > bytesPerLine) {
|
|
j = 1;
|
|
}
|
|
}
|
|
|
|
/* Output the end of definition */
|
|
output("};\n");
|
|
|
|
/* Free the symbol name if it was allocated by us */
|
|
if (symbolNameAllocated) {
|
|
free(symbolName);
|
|
}
|
|
|
|
/* Clear the symbol name */
|
|
symbolName = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Unknown option error */
|
|
static void handleUnknownOption(const char *s)
|
|
{
|
|
printfError(unknownOption, s);
|
|
fprintf(stderr, tryHelp);
|
|
panic();
|
|
}
|
|
|
|
/* Open an output file */
|
|
static void openOutputFile(const char *fileName)
|
|
{
|
|
/* Be sure that a file name is given */
|
|
if (!fileName) {
|
|
printfError(noOutputFilesAreNamed);
|
|
fprintf(stderr, tryHelp);
|
|
panic();
|
|
}
|
|
|
|
/* Close previous output file if any */
|
|
if (outputFile) {
|
|
check(!fclose(outputFile), cantClose, outputFileName);
|
|
}
|
|
|
|
/* Open the new output file */
|
|
outputFile = fopen(fileName, "w");
|
|
check(outputFile != 0, cantOpen, outputFileName);
|
|
outputFileName = fileName;
|
|
|
|
output("/* Generated by bin2c, do not edit manually */\n");
|
|
}
|
|
|
|
/* Open a header file */
|
|
static void openHeaderFile(const char *fileName)
|
|
{
|
|
/* Be sure that a file name is given */
|
|
if (!fileName) {
|
|
printfError(noHeaderFilesAreNamed);
|
|
fprintf(stderr, tryHelp);
|
|
panic();
|
|
}
|
|
|
|
/* Close previous header file if any */
|
|
if (headerFile) {
|
|
header("\n#endif /* __%s_included */\n", headerSymbol);
|
|
free(headerSymbol);
|
|
headerSymbol = 0;
|
|
check(!fclose(headerFile), cantClose, headerFileName);
|
|
}
|
|
|
|
/* Open the new header file */
|
|
headerFile = fopen(fileName, "w");
|
|
check(headerFile != 0, cantOpen, headerFileName);
|
|
headerFileName = fileName;
|
|
headerSymbol = fileNameToSymbol(fileName);
|
|
|
|
header("/* Generated by bin2c, do not edit manually */\n");
|
|
header("#ifndef __%s_included\n", headerSymbol);
|
|
header("#define __%s_included\n", headerSymbol);
|
|
}
|
|
|
|
typedef void writeFileFunc(const char *format, ...);
|
|
|
|
void writeAttchmentStruct(writeFileFunc *writeFunc){
|
|
(*writeFunc)("\ntypedef struct {\n");
|
|
(*writeFunc)("\tconst char *file_name;\n");
|
|
(*writeFunc)("\tconst unsigned char *sym_name;\n");
|
|
(*writeFunc)("\tlong size;\n");
|
|
(*writeFunc)("} bin2c_filesAttached_st;\n");
|
|
}
|
|
|
|
/* Open a header file */
|
|
static void openFuncFile(const char *fileName)
|
|
{
|
|
static char fnBuf[1024];
|
|
/* Be sure that a file name is given */
|
|
if (!fileName) {
|
|
printfError(noHeaderFilesAreNamed);
|
|
fprintf(stderr, tryHelp);
|
|
panic();
|
|
}
|
|
|
|
/* Close previous header file if any */
|
|
if (tmpFuncFile) {
|
|
tmpFunc("\n};\n");
|
|
check(!fclose(tmpFuncFile), cantClose, tmpFuncFileName);
|
|
}
|
|
|
|
snprintf(fnBuf, sizeof(fnBuf), "%s.tmp", fileName);
|
|
|
|
/* Open the new header file */
|
|
tmpFuncFile = fopen(fnBuf, "w");
|
|
check(tmpFuncFile != 0, cantOpen, fnBuf);
|
|
tmpFuncFileName = fnBuf;
|
|
|
|
tmpFunc("\n/* Generated by bin2c, do not edit manually */\n");
|
|
writeAttchmentStruct(&tmpFunc);
|
|
tmpFunc("const bin2c_filesAttached_st bin2c_filesAttached[] = {\n");
|
|
}
|
|
|
|
/* Set the symbol name for next file */
|
|
static void setSymbolName(char *name)
|
|
{
|
|
int i;
|
|
|
|
if (symbolName) {
|
|
printfError(symbolNameGiven);
|
|
fprintf(stderr, tryHelp);
|
|
panic();
|
|
}
|
|
|
|
/* Be sure that a symbol name is given */
|
|
if (!name) {
|
|
printfError(noSymbolName);
|
|
fprintf(stderr, tryHelp);
|
|
panic();
|
|
}
|
|
|
|
/* Check if the symbol is a valid C identifier */
|
|
check((name[0] >= 'a' && name[0] <='z') ||
|
|
(name[0] >= 'A' && name[0] <='Z') ||
|
|
(name[0] == '_'), notValidId, name);
|
|
|
|
for (i = 1; name[i]; ++i) {
|
|
check((name[i] >= 'a' && name[i] <='z') ||
|
|
(name[i] >= 'A' && name[i] <='Z') ||
|
|
(name[i] >= '0' && name[i] <='9') ||
|
|
(name[i] == '_'), notValidId, name);
|
|
}
|
|
|
|
/* Set the symbol name */
|
|
symbolName = name;
|
|
}
|
|
|
|
void concatenate(const char *dest, const char *src){
|
|
int i,ch;
|
|
FILE *fpdest=fopen(dest,"a");
|
|
FILE *fpsrc=fopen(src,"r");
|
|
while((ch=getc(fpsrc))!=EOF) putc(ch,fpdest);
|
|
}
|
|
|
|
/* Main function */
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i; /* Index to process commandline arguments */
|
|
int ipf = 0; /*total files processed*/
|
|
|
|
/* Scan for a -h or --help option */
|
|
for (i = 1; i < argc; ++i) {
|
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
|
/* Print command line arguments help and exit immediately */
|
|
printf("%s", commandLineHelp);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Scan for a -v or --version option */
|
|
for (i = 1; i < argc; ++i) {
|
|
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
|
/* Print version information and exit immediately */
|
|
printf("%s", versionInfo);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Process command line arguments */
|
|
for (i = 1; i < argc; ++i) {
|
|
/* Check if it's an option or file name */
|
|
if (argv[i][0] == '-') {
|
|
/* Process the option */
|
|
switch (argv[i][1]) {
|
|
case '-':
|
|
/* Long options */
|
|
if (!strcmp(argv[i], "--output")) {
|
|
/* Name an output file */
|
|
openOutputFile(argv[i+1]);
|
|
openFuncFile(argv[i+1]);
|
|
++i;
|
|
} else if (!strcmp(argv[i], "--header")) {
|
|
/* Name a header file */
|
|
openHeaderFile(argv[i+1]);
|
|
++i;
|
|
} else if (!strcmp(argv[i], "--macro")) {
|
|
/* Create a macro for the size definition */
|
|
createMacro = true;
|
|
} else if (!strcmp(argv[i], "--name")) {
|
|
/* Name the symbol */
|
|
setSymbolName(argv[i+1]);
|
|
++i;
|
|
} else {
|
|
/* Unknown option */
|
|
handleUnknownOption(argv[i]);
|
|
}
|
|
break;
|
|
case 'o':
|
|
/* Name an output file */
|
|
openOutputFile(argv[i+1]);
|
|
openFuncFile(argv[i+1]);
|
|
++i;
|
|
break;
|
|
case 'd':
|
|
/* Name a header file */
|
|
openHeaderFile(argv[i+1]);
|
|
++i;
|
|
break;
|
|
case 'm':
|
|
/* Create a macro for the size definition */
|
|
createMacro = true;
|
|
break;
|
|
case 'n':
|
|
/* Name the symbol */
|
|
setSymbolName(argv[i+1]);
|
|
++i;
|
|
break;
|
|
default:
|
|
/* Unknown option */
|
|
handleUnknownOption(argv[i]);
|
|
}
|
|
} else {
|
|
/* Process the file */
|
|
if(processFile(argv[i], 0, -1)) ipf++;
|
|
}
|
|
}
|
|
|
|
/* Close any remaining output files */
|
|
if (headerFile) {
|
|
writeAttchmentStruct(&header);
|
|
header("extern const bin2c_filesAttached_st bin2c_filesAttached[%d];\n", ipf);
|
|
|
|
header("\n#endif /* __%s_included */\n", headerSymbol);
|
|
free(headerSymbol);
|
|
headerSymbol = 0;
|
|
check(!fclose(headerFile), cantClose, headerFileName);
|
|
}
|
|
|
|
if (tmpFuncFile) {
|
|
tmpFunc("};\n");
|
|
check(!fclose(tmpFuncFile), cantClose, tmpFuncFileName);
|
|
}
|
|
|
|
if (outputFile) {
|
|
check(!fclose(outputFile), cantClose, outputFileName);
|
|
}
|
|
|
|
if (tmpFuncFile && outputFile) {
|
|
concatenate(outputFileName, tmpFuncFileName);
|
|
remove(tmpFuncFileName);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|