/*
Copyright:
(C) 2022-2023 Daniele 'dzonerzy' Linguaglossa - http://libdzonerzy.so
This file is part of libdzonerzy.so.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
*/
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#define VERSION "0.0.1"
typedef enum _format
{
FORMAT_C,
FORMAT_H,
} format_t;
typedef enum _type
{
TYPE_EMBED,
TYPE_ADVISORY,
} type_t;
typedef struct _embed
{
char *input;
char *output;
format_t format;
type_t type;
int verbose;
int quiet;
int autowrap;
} embed_t;
static void usage(char **argv);
static void version(char **argv);
static int embed(embed_t *args);
static char *clean_filename(char *filename);
int main(int argc, char **argv)
{
int c = 0;
embed_t args = {0};
while ((c = getopt(argc, argv, "hvi:o:f:t:Vqw")) != -1)
{
switch (c)
{
case 'h':
usage(argv);
exit(EXIT_SUCCESS);
break;
case 'v':
version(argv);
exit(EXIT_SUCCESS);
break;
case 'i':
args.input = optarg;
break;
case 'o':
args.output = optarg;
break;
case 'f':
// check -f=c -f=h handle equal sign and skip it
if (optarg[0] == '=')
{
optarg++;
}
switch (optarg[0])
{
case 'c':
args.format = FORMAT_C;
break;
case 'h':
args.format = FORMAT_H;
break;
default:
usage(argv);
exit(EXIT_FAILURE);
break;
}
break;
case 't':
// check -t=embed -t=advisory handle equal sign and skip it
if (optarg[0] == '=')
{
optarg++;
}
switch (optarg[0])
{
case 'e':
args.type = TYPE_EMBED;
break;
case 'a':
args.type = TYPE_ADVISORY;
break;
default:
fprintf(stderr, "Unknown type: %s defaulting to embed\n", optarg);
args.type = TYPE_EMBED;
break;
}
break;
case 'V':
args.verbose = 1;
break;
case 'q':
args.quiet = 1;
break;
case 'w':
args.autowrap = 1;
break;
default:
usage(argv);
exit(EXIT_FAILURE);
break;
}
}
return embed(&args);
}
void usage(char **argv)
{
char *name = strrchr(argv[0], '/');
if (name == NULL)
{
name = argv[0];
}
else
{
name++;
}
printf("Usage: %s [OPTIONS]\n", name);
printf("Embed a file into a C source file.\n");
printf("\n");
printf("Options:\n");
printf(" -h, --help Print this help and exit\n");
printf(" -v, --version Print version and exit\n");
printf(" -i, --input=FILE Input file\n");
printf(" -o, --output=FILE Output file\n");
printf(" -f, --format=FORMAT Output format (c, h)\n");
printf(" -t, --type=TYPE Output type (embed, advisory)\n");
printf(" -w, --autowrap Autowrap output\n");
printf(" -V, --verbose Verbose output\n");
printf(" -q, --quiet Quiet output\n");
printf("\n");
}
void version(char **argv)
{
char *name = strrchr(argv[0], '/');
if (name == NULL)
{
name = argv[0];
}
else
{
name++;
}
printf("%s version %s\n", name, VERSION);
printf("\n");
}
char *clean_filename(char *filename)
{
char *p = filename;
while (*p)
{
// replace with _
// replace symbols with _
// replace - with _
// replace . with _
// all characters to uppercase
switch (*p)
{
case ' ':
case '-':
case '.':
case '/':
case '\\':
case ':':
case ';':
case ',':
case '\'':
case '"':
case '[':
case ']':
case '{':
case '}':
case '(':
case ')':
case '<':
case '>':
case '?':
case '!':
case '@':
case '#':
case '$':
case '%':
case '^':
case '&':
case '*':
case '+':
case '=':
case '|':
case '`':
case '~':
*p = '_';
break;
default:
*p = toupper(*p);
break;
}
p++;
}
return filename;
}
int embed(embed_t *args)
{
if (args->input == NULL)
{
fprintf(stderr, "Input file not specified\n");
return EXIT_FAILURE;
}
char *formats[] = {"c", "h"};
if (args->verbose)
{
printf("Input file: %s\n", args->input);
printf("Output file: %s\n", args->output);
printf("Output format: %s\n", formats[args->format]);
}
FILE *input = fopen(args->input, "rb");
if (input == NULL)
{
if (!args->quiet)
fprintf(stderr, "Unable to open input file: %s\n", args->input);
return EXIT_FAILURE;
}
FILE *output = NULL;
if (args->output != NULL)
{
output = fopen(args->output, "w");
}
else
{
output = stdout;
}
char *name_only = strdup(args->input);
name_only = strrchr(name_only, '/');
if (name_only == NULL)
{
name_only = args->input;
}
else
{
name_only++;
}
char *input_name = strdup(args->input);
input_name = strrchr(input_name, '/');
if (input_name == NULL)
{
input_name = args->input;
}
else
{
input_name++;
}
input_name = clean_filename(input_name);
fprintf(output, "/*\n");
fprintf(output, " * This file was automatically generated by embed - DO NOT EDIT\n");
fprintf(output, " * https://libdzonerzy.so\n");
fprintf(output, " */\n");
fprintf(output, "\n");
char *typ;
switch (args->type)
{
case TYPE_EMBED:
typ = "embed";
break;
case TYPE_ADVISORY:
typ = "advisory";
break;
default:
typ = "unknown";
break;
}
if (args->format == FORMAT_C)
{
fprintf(output, "__attribute__((section(\"%s/%s\"))) const unsigned char %s[] = {", typ, name_only, input_name);
if (args->autowrap)
{
fprintf(output, "\n");
}
}
else if (args->format == FORMAT_H)
{
fprintf(output, "#pragma once\n");
fprintf(output, "#ifndef %s_H\n", input_name);
fprintf(output, "__attribute__((section(\"%s/%s\"))) const unsigned char %s[] = {", typ, name_only, input_name);
if (args->autowrap)
{
fprintf(output, "\n");
}
}
int c = 0;
int i = 0;
if (args->autowrap)
{
while ((c = fgetc(input)) != EOF)
{
if (i == 0)
{
fprintf(output, " ");
}
fprintf(output, "0x%02X", c);
fprintf(output, ", ");
if (i < 15)
{
fprintf(output, ", ");
}
else
{
fprintf(output, ",\n");
i = -1;
}
i++;
}
if (i > 0)
{
fprintf(output, "\n");
}
if (args->type == TYPE_EMBED)
fprintf(output, " 0x00,\n");
fprintf(output, "};\n");
}
else
{
while ((c = fgetc(input)) != EOF)
{
fprintf(output, "0x%02X", c);
fprintf(output, ", ");
}
if (args->type == TYPE_EMBED)
fprintf(output, "0x00");
fprintf(output, "};\n\n");
}
if (args->format == FORMAT_H)
{
fprintf(output, "#endif /* %s_H */\n", input_name);
}
fclose(input);
fclose(output);
if (!args->quiet)
printf("Generated resource '%s'\n", input_name);
return EXIT_SUCCESS;
}