/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2014.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see .
carray.c++
Convert a binary file to a C array.
*******************************************************************************/
#include
#include
#include
#include
#include
#include
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
bool get_option_variable(const char* option, char** varptr,
const char* arg, int argc, char** argv, int* ip,
const char* argv0)
{
size_t option_len = strlen(option);
if ( strncmp(option, arg, option_len) != 0 )
return false;
if ( arg[option_len] == '=' )
{
*varptr = strdup(arg + option_len + 1);
return true;
}
if ( arg[option_len] != '\0' )
return false;
if ( *ip + 1 == argc )
{
fprintf(stderr, "%s: expected operand after `%s'\n", argv0, option);
exit(1);
}
*varptr = strdup(argv[++*ip]), argv[*ip] = NULL;
return true;
}
#define GET_OPTION_VARIABLE(str, varptr) \
get_option_variable(str, varptr, arg, argc, argv, &i, argv0)
void get_short_option_variable(char c, char** varptr,
const char* arg, int argc, char** argv, int* ip,
const char* argv0)
{
free(*varptr);
if ( *(arg+1) )
{
*varptr = strdup(arg + 1);
}
else
{
if ( *ip + 1 == argc )
{
error(0, 0, "option requires an argument -- '%c'", c);
fprintf(stderr, "Try `%s --help' for more information.\n", argv0);
exit(1);
}
*varptr = strdup(argv[*ip + 1]);
argv[++(*ip)] = NULL;
}
}
#define GET_SHORT_OPTION_VARIABLE(c, varptr) \
get_short_option_variable(c, varptr, arg, argc, argv, &i, argv0)
static void help(FILE* fp, const char* argv0)
{
fprintf(fp, "Usage: %s [OPTION]... [INPUT...] -o OUTPUT\n", argv0);
fprintf(fp, "Convert a binary file to a C array.\n");
fprintf(fp, "\n");
fprintf(fp, "Mandatory arguments to long options are mandatory for short options too.\n");
fprintf(fp, " -c, --const add const keyword\n");
fprintf(fp, " -e, --extern add extern keyword\n");
fprintf(fp, " --extern-c use C linkage\n");
fprintf(fp, " -f, --forward forward declare rather than define\n");
fprintf(fp, " --guard=MACRO use include guard macro MACRO\n");
fprintf(fp, " -g, --use-guard add include guard\n");
fprintf(fp, " -i, --include include prerequisite headers\n");
fprintf(fp, " --identifier=NAME use identifier NAME\n");
fprintf(fp, " --includes=INCLUDES emit raw preprocessor INCLUDES\n");
fprintf(fp, " -o, --output=FILE write result to FILE\n");
fprintf(fp, " -r, --raw emit only raw \n");
fprintf(fp, " -s, --static add static keyword\n");
fprintf(fp, " --type=TYPE use type TYPE [unsigned char]\n");
fprintf(fp, " -v, --volatile add volatile keyword\n");
fprintf(fp, " --char use type char\n");
fprintf(fp, " --signed-char use type signed char\n");
fprintf(fp, " --unsigned-char use type unsigned char\n");
fprintf(fp, " --int8_t use type int8_t\n");
fprintf(fp, " --uint8_t use type uint8_t\n");
fprintf(fp, " --help display this help and exit\n");
fprintf(fp, " --version output version information and exit\n");
}
static void version(FILE* fp, const char* argv0)
{
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
fprintf(fp, "License GPLv3+: GNU GPL version 3 or later .\n");
fprintf(fp, "This is free software: you are free to change and redistribute it.\n");
fprintf(fp, "There is NO WARRANTY, to the extent permitted by law.\n");
}
int main(int argc, char* argv[])
{
setlocale(LC_ALL, "");
bool flag_const = false;
bool flag_extern_c = false;
bool flag_extern = false;
bool flag_forward = false;
bool flag_guard = false;
bool flag_include = false;
bool flag_raw = false;
bool flag_static = false;
bool flag_volatile = false;
char* arg_guard = NULL;
char* arg_identifier = NULL;
char* arg_includes = NULL;
char* arg_output = NULL;
char* arg_type = NULL;
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
while ( char c = *++arg ) switch ( c )
{
case 'c': flag_const = true; break;
case 'e': flag_extern = true; break;
case 'f': flag_forward = true; break;
case 'g': flag_guard = true; break;
case 'i': flag_include = true; break;
case 'o': GET_SHORT_OPTION_VARIABLE('o', &arg_output); arg = "o"; break;
case 'r': flag_raw = true; break;
case 's': flag_static = true; break;
case 'v': flag_volatile = true; break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
help(stderr, argv0);
exit(1);
}
}
else if ( !strcmp(arg, "--help") )
help(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--version") )
version(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--const") )
flag_const = true;
else if ( !strcmp(arg, "--extern") )
flag_extern = true;
else if ( !strcmp(arg, "--forward") )
flag_forward = true;
else if ( !strcmp(arg, "--use-guard") )
flag_guard = true;
else if ( !strcmp(arg, "--include") )
flag_include = true;
else if ( !strcmp(arg, "--raw") )
flag_raw = true;
else if ( !strcmp(arg, "--static") )
flag_static = true;
else if ( !strcmp(arg, "--volatile") )
flag_volatile = true;
else if ( !strcmp(arg, "--char") )
free(arg_type), arg_type = strdup("char");
else if ( !strcmp(arg, "--signed-char") )
free(arg_type), arg_type = strdup("signed char");
else if ( !strcmp(arg, "--unsigned-char") )
free(arg_type), arg_type = strdup("unsigned char");
else if ( !strcmp(arg, "--int8_t") )
free(arg_type), arg_type = strdup("int8_t");
else if ( !strcmp(arg, "--uint8_t") )
free(arg_type), arg_type = strdup("uint8_t");
else if ( GET_OPTION_VARIABLE("--guard", &arg_guard) )
flag_guard = true;
else if ( GET_OPTION_VARIABLE("--identifier", &arg_identifier) ) { }
else if ( GET_OPTION_VARIABLE("--includes", &arg_includes) )
flag_include = true;
else if ( GET_OPTION_VARIABLE("--output", &arg_output) ) { }
else if ( GET_OPTION_VARIABLE("--type", &arg_type) ) { }
else if ( !strcmp(arg, "--extern-c") )
flag_extern_c = true;
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
help(stderr, argv0);
exit(1);
}
}
compact_arguments(&argc, &argv);
const char* output_path = arg_output;
if ( flag_extern && flag_static )
error(1, 0, "the --extern and --static are mutually incompatible");
if ( flag_forward && flag_raw )
error(1, 0, "the --forward and --raw are mutually incompatible");
if ( !arg_type )
arg_type = strdup("unsigned char");
char* guard = arg_guard;
if ( !guard )
{
if ( output_path )
guard = strdup(output_path);
else if ( 2 <= argc && strcmp(argv[1], "-") != 0 )
asprintf(&guard, "%s_H", argv[1]);
else
guard = strdup("CARRAY_H");
for ( size_t i = 0; guard[i]; i++ )
{
if ( 'A' <= guard[i] && guard[i] <= 'Z' )
continue;
else if ( 'a' <= guard[i] && guard[i] <= 'z' )
guard[i] = 'A' + guard[i] - 'a';
else if ( i != 0 && '0' <= guard[i] && guard[i] <= '9' )
continue;
else if ( guard[i] == '+' )
guard[i] = 'X';
else if ( i == 0 )
guard[i] = 'X';
else
guard[i] = '_';
}
}
if ( flag_include && !arg_includes )
{
if ( !strcmp(arg_type, "int8_t") ||
!strcmp(arg_type, "uint8_t") )
arg_includes = strdup("#include ");
}
char* identifier = arg_identifier;
if ( !identifier )
{
if ( output_path )
identifier = strdup(output_path);
else if ( 2 <= argc && strcmp(argv[1], "-") != 0 )
identifier = strdup(argv[1]);
else
identifier = strdup("carray");
for ( size_t i = 0; identifier[i]; i++ )
{
if ( i && identifier[i] == '.' && !strchr(identifier + i, '/') )
identifier[i] = '\0';
else if ( 'a' <= identifier[i] && identifier[i] <= 'z' )
continue;
else if ( 'A' <= identifier[i] && identifier[i] <= 'Z' )
identifier[i] = 'a' + identifier[i] - 'A';
else if ( i != 0 && '0' <= identifier[i] && identifier[i] <= '9' )
continue;
else if ( guard[i] == '+' )
identifier[i] = 'x';
else if ( i == 0 )
identifier[i] = 'x';
else
identifier[i] = '_';
}
}
if ( output_path && !freopen(output_path, "w", stdout) )
error(1, errno, "%s", output_path);
if ( flag_guard && guard )
{
printf("#ifndef %s\n", guard);
printf("#define %s\n", guard);
printf("\n");
}
if ( flag_include && arg_includes )
{
printf("%s\n", arg_includes);
printf("\n");
}
if ( !flag_raw )
{
if ( flag_extern_c )
{
printf("#if defined(__cplusplus)\n");
printf("extern \"C\" {\n");
printf("#endif\n");
printf("\n");
}
if ( flag_extern )
printf("extern ");
if ( flag_static )
printf("static ");
if ( flag_const )
printf("const ");
if ( flag_volatile )
printf("volatile ");
printf("%s %s[]", arg_type, identifier);
if ( flag_forward )
printf(";\n");
else
printf(" = {\n");
}
if ( !flag_forward )
{
bool begun_row = false;
unsigned int position = 0;
for ( int i = 0; i < argc; i++ )
{
if ( i == 0 && 2 <= argc )
continue;
FILE* fp;
const char* arg;
if ( argc == 1 || !strcmp(argv[i], "-") )
{
arg = "";
fp = stdin;
}
else
{
arg = argv[i];
fp = fopen(arg, "r");
}
if ( !fp )
error(1, errno, "%s", arg);
int ic;
while ( (ic = fgetc(fp)) != EOF )
{
printf("%c0x%02X,", position++ ? ' ' : '\t', ic);
begun_row = true;
if ( position == (80 - 8) / 6 )
{
printf("\n");
position = 0;
begun_row = false;
}
}
if ( ferror(fp) )
error(1, errno, "fgetc: %s", arg);
if ( fp != stdin )
fclose(fp);
}
if ( begun_row )
printf("\n");
}
if ( !flag_raw )
{
if ( !flag_forward )
printf("};\n");
if ( flag_extern_c )
{
printf("\n");
printf("#if defined(__cplusplus)\n");
printf("} /* extern \"C\" */\n");
printf("#endif\n");
}
}
if ( flag_guard && guard )
{
printf("\n");
printf("#endif\n");
}
if ( ferror(stdout) || fflush(stdout) == EOF )
error(1, errno, "%s", output_path ? output_path : "stdout");
return 0;
}