libdzonerzy.so/tools/embed/embed.c
2024-10-09 17:36:23 +02:00

540 lines
12 KiB
C

/*
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. <https://fsf.org/>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <ctype.h>
#define VERSION "0.0.1"
typedef enum _format
{
FORMAT_C,
FORMAT_H,
} format_t;
typedef enum _type
{
TYPE_EMBED,
TYPE_ADVISORY,
TYPE_ARTICLE,
TYPE_PAGE,
} 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;
case 'p':
args.type = TYPE_PAGE;
break;
case 'A':
args.type = TYPE_ARTICLE;
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 (e,a,p,A - embed, advisory, page, article)\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 <space> 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;
}
unsigned char *read_file_data(FILE *f, size_t *size)
{
fseek(f, 0, SEEK_END);
*size = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char *data = malloc(*size);
if (data == NULL)
{
return NULL;
}
fread(data, 1, *size, f);
return data;
}
unsigned char *image_add_extension(unsigned char *buff, const char *add_ext)
{
if (buff == NULL || add_ext == NULL)
{
return NULL;
}
// Find the size of the buffer
size_t len = strlen((char *)buff);
// Allocate a new buffer for the modified content
unsigned char *new_buff = malloc(len * 2); // Allocate double the size for safety
if (new_buff == NULL)
{
return NULL;
}
unsigned char *src = buff;
unsigned char *dst = new_buff;
// Loop through the buffer to find markdown image patterns
while (*src)
{
if (strncmp((char *)src, "![", 2) == 0)
{
// Copy the "!["
*dst++ = *src++;
*dst++ = *src++;
// Skip everything inside the square brackets until the closing bracket ']'
while (*src && *src != ']')
{
*dst++ = *src++;
}
// Copy the closing bracket if it's there
if (*src == ']')
{
*dst++ = *src++;
}
// Now check for the '(' indicating the start of the image URL
if (*src == '(')
{
*dst++ = *src++;
unsigned char *start = src;
// Find the end of the image URL which is marked by ')'
while (*src && *src != ')')
{
src++;
}
// Copy the image URL
strncpy((char *)dst, (char *)start, src - start);
dst += src - start;
// Add the extension
strcpy((char *)dst, add_ext);
dst += strlen(add_ext);
// Copy the closing parenthesis
if (*src == ')')
{
*dst++ = *src++;
}
}
}
else
{
// Copy any other character
*dst++ = *src++;
}
}
// Null-terminate the new buffer
*dst = '\0';
return new_buff;
}
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;
case TYPE_PAGE:
typ = "pages";
break;
case TYPE_ARTICLE:
typ = "articles";
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");
}
}
size_t i = 0;
size_t size = 0;
unsigned char *data = read_file_data(input, &size);
// update the image URL with the extension
if (args->type == TYPE_ARTICLE || args->type == TYPE_PAGE)
{
unsigned char *data_updated = image_add_extension(data, ".webp");
// free the original data
free(data);
size = strlen((const char *)data_updated);
data = data_updated;
}
if (args->autowrap)
{
while (i < size)
{
if (i == 0)
{
fprintf(output, " ");
}
fprintf(output, "0x%02X", data[i]);
if (i % 16 == 15)
{
fprintf(output, ",\n");
fprintf(output, " ");
}
else
{
fprintf(output, ", ");
}
i++;
}
if (i > 0)
{
fprintf(output, "\n");
}
if (args->type == TYPE_EMBED)
fprintf(output, " 0x00,\n");
fprintf(output, "};\n");
}
else
{
while (i < size)
{
// no wrap
if (i == 0)
{
fprintf(output, "0x%02X", data[i]);
}
else
{
fprintf(output, ", 0x%02X", data[i]);
}
i++;
}
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;
}