From 3a20924d9262b318e1bab951347aad0c97498c2b Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Fri, 8 Nov 2024 17:05:54 -0500 Subject: [PATCH] Implemented ignoring comments in XPM files - Also started reworking error messages - Including line number in relavent errors --- cbs.c | 4 +- {fonts => reference}/SourceCodePro.ttf | Bin src/main.c | 48 ++++++--------- src/parser.c | 17 +++--- src/tokenizer.c | 81 ++++++++++++++++++++++--- src/tokenizer.h | 4 +- src/utils.c | 2 +- src/utils.h | 9 +-- 8 files changed, 111 insertions(+), 54 deletions(-) rename {fonts => reference}/SourceCodePro.ttf (100%) diff --git a/cbs.c b/cbs.c index 808d326..9e8cb63 100644 --- a/cbs.c +++ b/cbs.c @@ -2,6 +2,8 @@ #define CBS_LIBRARY_PATH "./external/cbs.d/cbs.h" #include CBS_LIBRARY_PATH +// TODO: Support for Linux + #define CC "cc" #define CFLAGS "-Wall", "-Wextra", "-Wpedantic", "-I./external/raylib/src", "-I./fonts" #ifdef __MACH__ @@ -43,7 +45,7 @@ int main(int argc, char **argv) { cbs_file_paths_build_file_ext(&src_files, "./src", ".c"); cbs_file_paths_for_each (src_file, src_files) { const char *obj_file = cbs_string_build(cbs_strip_file_ext(src_file), ".o"); - if (cbs_needs_rebuild(obj_file, src_file)) + if (cbs_needs_rebuild(obj_file, src_file, "./src/utils.h")) cbs_run(CC, CFLAGS, "-c", "-o", obj_file, src_file); } diff --git a/fonts/SourceCodePro.ttf b/reference/SourceCodePro.ttf similarity index 100% rename from fonts/SourceCodePro.ttf rename to reference/SourceCodePro.ttf diff --git a/src/main.c b/src/main.c index 13fe015..620313d 100644 --- a/src/main.c +++ b/src/main.c @@ -12,27 +12,7 @@ #define DEFAULT_SCREEN_HEIGHT 600 #define FILE_PATH_CAP 2048 -// TODO: Improve error messages - -static Image image; -static Texture2D texture; -static bool have_texture; - -static void create_texture_from_xpm_file(const char *xpm_file_path) { - if ((have_texture = parse_xpm_file(&image, xpm_file_path))) { - UnloadTexture(texture); - texture = LoadTextureFromImage(image); - } -} - -int main(int argc, char **argv) { - char xpm_file_path[FILE_PATH_CAP] = {0}; - - if (argc >= 2) { - strncpy(xpm_file_path, argv[1], FILE_PATH_CAP); - create_texture_from_xpm_file(xpm_file_path); - } - +int main(void) { SetTraceLogLevel(LOG_WARNING); InitWindow(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, "simplexpm"); SetWindowState(FLAG_WINDOW_RESIZABLE); @@ -40,16 +20,25 @@ int main(int argc, char **argv) { Font font = LoadSourceCodeProFont(); + char xpm_file_path[FILE_PATH_CAP] = {0}; + Image image; + Texture2D texture; + bool have_texture = false, startup = true; while (!WindowShouldClose()) { - if (IsFileDropped()) { + bool isFileDropped = IsFileDropped(); + if (isFileDropped) { FilePathList file_paths = LoadDroppedFiles(); strncpy(xpm_file_path, file_paths.paths[0], FILE_PATH_CAP); UnloadDroppedFiles(file_paths); - create_texture_from_xpm_file(xpm_file_path); } - if (IsKeyDown(KEY_R) && have_texture) - create_texture_from_xpm_file(xpm_file_path); - if (IsKeyDown(KEY_S) && have_texture) { + if ((IsKeyPressed(KEY_R) && !startup) || isFileDropped) { + if ((have_texture = parse_xpm_file(&image, xpm_file_path))) { + UnloadTexture(texture); + texture = LoadTextureFromImage(image); + } + startup = false; + } + if (IsKeyPressed(KEY_S) && have_texture) { char png_file_path[FILE_PATH_CAP] = {0}; strncpy(png_file_path, xpm_file_path, strlen(xpm_file_path) - strlen(".xpm")); strncat(png_file_path, ".png", strlen(".png")); @@ -69,12 +58,15 @@ int main(int argc, char **argv) { (screen_height - (texture.height * scale)) / 2}; DrawTextureEx(texture, position, 0, scale, WHITE); } else { - Vector2 message_dimensions = MeasureTextEx(font, error_message, FONT_SIZE, 0), + const char *message = startup ? "Drag and drop an XPM file here" + : "Unable to parse XPM file\n" + "(see console for detail)"; + Vector2 message_dimensions = MeasureTextEx(font, message, FONT_SIZE, 0), message_placement = { .x = (screen_width - message_dimensions.x) / 2, .y = (screen_height - message_dimensions.y) / 2, }; - DrawTextEx(font, error_message, message_placement, FONT_SIZE, 0, BLACK); + DrawTextEx(font, message, message_placement, FONT_SIZE, 0, BLACK); } EndDrawing(); } diff --git a/src/parser.c b/src/parser.c index 831df1b..8eccdde 100644 --- a/src/parser.c +++ b/src/parser.c @@ -16,6 +16,7 @@ unsigned int *color_table; unsigned int *pixels; bool parse_xpm_file(Image *image, const char *file_path) { + line_number = 0; switch (sigsetjmp(env, 0)) { case 1: return false; @@ -24,12 +25,10 @@ bool parse_xpm_file(Image *image, const char *file_path) { if ((file = fopen(file_path, "r")) == NULL) SIMPLE_XPM_ERROR("Unable to open provided file"); - get_next_line(&line_buffer, file); - char *line_buffer_p = line_buffer; - check_next_token(&line_buffer_p, "/* XPM */"); + check_xpm_header(file); - get_next_line(&line_buffer, file); - line_buffer_p = line_buffer; + get_next_line_check_eof(&line_buffer, file); + char *line_buffer_p = line_buffer; check_next_token(&line_buffer_p, "static"); if (!isspace(*line_buffer_p)) SIMPLE_XPM_ERROR("Expected token \"static\""); @@ -46,7 +45,7 @@ bool parse_xpm_file(Image *image, const char *file_path) { check_next_token(&line_buffer_p, "{"); // Parse values - get_next_line(&line_buffer, file); + get_next_line_check_eof(&line_buffer, file); line_buffer_p = line_buffer; check_next_token(&line_buffer_p, "\""); size_t width = convert_token_to_num(get_next_token(&line_buffer_p), 10), @@ -90,7 +89,7 @@ bool parse_xpm_file(Image *image, const char *file_path) { SIMPLE_XPM_MALLOC(color_table, NUM_XPM_MODES * num_colors * sizeof(*color_table)); bool possible_modes[NUM_XPM_MODES] = {false}; for (size_t i = 0; i < num_colors; ++i) { - get_next_line(&line_buffer, file); + get_next_line_check_eof(&line_buffer, file); line_buffer_p = line_buffer; check_next_token(&line_buffer_p, "\""); @@ -144,7 +143,7 @@ bool parse_xpm_file(Image *image, const char *file_path) { char *key_buffer; SIMPLE_XPM_MALLOC(key_buffer, (chars_per_pixel + 1) * sizeof(*key_buffer)); for (size_t i = 0; i < height; ++i) { - get_next_line(&line_buffer, file); + get_next_line_check_eof(&line_buffer, file); line_buffer_p = line_buffer; check_next_token(&line_buffer_p, "\""); if (strlen(line_buffer_p) < width * chars_per_pixel + strlen("\",")) @@ -172,7 +171,7 @@ bool parse_xpm_file(Image *image, const char *file_path) { printf("Parsing XPM extensions\n"); } - get_next_line(&line_buffer, file); + get_next_line_check_eof(&line_buffer, file); line_buffer_p = line_buffer; check_next_token(&line_buffer_p, "}"); check_next_token(&line_buffer_p, ";"); diff --git a/src/tokenizer.c b/src/tokenizer.c index 3df6491..06c2caa 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,25 +6,85 @@ #include "utils.h" -// TODO: Ignore comments -int get_next_line(char **buffer, FILE *file) { - static size_t line_cap = 0; +static bool is_multiline; +static unsigned int multiline_num; + +static bool line_is_comment(char *line) { + + // Remove comments from line + char *multiline_start = NULL; + do { + if (is_multiline) { + char *multiline_end; + if (multiline_start) multiline_end = strstr(multiline_start + 2, "*/"); + else multiline_end = strstr(line, "*/"); + if (!multiline_end) { + if (!multiline_start) return true; + *multiline_start = '\0'; + break; + } + is_multiline = false; + if (multiline_start) *(multiline_start++) = ' '; + else multiline_start = line; + multiline_end += strlen("*/"); + memmove(multiline_start, multiline_end, strlen(multiline_end) + 1); + } + char *singleline_start = strstr(line, "//"); + if (singleline_start) + *singleline_start = '\0'; + + multiline_start = strstr(line, "/*"); + } while (multiline_start && (multiline_num = line_number, is_multiline = true)); + + // Return true if the line is only whitespace at this point + return *strstrip(&line) == '\0'; +} + +bool get_next_line(char **buffer, FILE *file) { + static size_t buffer_cap = 0; errno = 0; - if (getline(buffer, &line_cap, file) == -1) { - if (errno != 0) - SIMPLE_XPM_ERROR("Unable to parse provided file: expected a new line"); - return 0; - } - return 1; + do { + if (getline(buffer, &buffer_cap, file) == -1) { + if (errno != 0) + SIMPLE_XPM_ERROR("Error occured while getting line %d", line_number + 1); + if (is_multiline) { + is_multiline = false; + SIMPLE_XPM_ERROR("Failed to close multiline comment from line %d", + multiline_num); + } + return false; + } + ++line_number; + } while (line_is_comment(*buffer)); + return true; +} + +void get_next_line_check_eof(char **buffer, FILE *file) { + if (!get_next_line(buffer, file)) + SIMPLE_XPM_ERROR("Expected another line after line %d", line_number); } void check_next_token(char **string, const char *token) { size_t token_len = strlen(token); if (strncmp(strstrip(string), token, token_len) != 0) - SIMPLE_XPM_ERROR("Unable to parse provided file: expected token \"%s\"", token); + SIMPLE_XPM_ERROR("Expected \"%s\" at line %d", token, line_number); *string += token_len; } +void check_xpm_header(FILE *file) { + char *buffer = NULL; + size_t buffer_cap; + if (getline(&buffer, &buffer_cap, file) == -1) { + if (errno != 0) + SIMPLE_XPM_ERROR("Error occured while getting line %d", line_number + 1); + SIMPLE_XPM_ERROR("The provided file is empty"); + } + ++line_number; + check_next_token(&buffer, "/* XPM */"); + if (*strstrip(&buffer) != '\0') + SIMPLE_XPM_ERROR("Trailing text after XPM file header; incorrect format"); +} + char *get_next_token(char **string) { char *result; do { diff --git a/src/tokenizer.h b/src/tokenizer.h index cf9e5b3..bce078e 100644 --- a/src/tokenizer.h +++ b/src/tokenizer.h @@ -4,8 +4,10 @@ #include #include -int get_next_line(char **buffer, FILE *file); +bool get_next_line(char **buffer, FILE *file); +void get_next_line_check_eof(char **buffer, FILE *file); void check_next_token(char **string, const char *token); +void check_xpm_header(FILE *file); char *get_next_token(char **string); bool get_terminal_token(char **string, char **token); size_t convert_token_to_num(const char *token, int base); diff --git a/src/utils.c b/src/utils.c index 96611e4..4df72e7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -5,8 +5,8 @@ #include "utils.h" -char error_message[ERROR_MESSAGE_CAP] = "Drag and drop an XPM file here"; jmp_buf env; +unsigned int line_number; char *strstrip(char **string) { for (; isspace(**string) && **string != '\0'; ++*string); diff --git a/src/utils.h b/src/utils.h index ce0a356..d247be6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -11,7 +11,7 @@ do { \ p = malloc(size); \ if (p == NULL) { \ - fprintf(stderr, "simplexpm: OUT OF MEMORY (buy more RAM?)"); \ + fprintf(stderr, "simplexpm: OUT OF MEMORY: Terminating\n"); \ exit(EXIT_FAILURE); \ } \ memset(p, 0, size); \ @@ -23,9 +23,8 @@ p = NULL; \ } while (0) -#define ERROR_MESSAGE_CAP 2048 -extern char error_message[ERROR_MESSAGE_CAP]; extern jmp_buf env; +extern unsigned int line_number; #define SIMPLE_XPM_ERROR(...) \ do { \ @@ -34,7 +33,9 @@ extern jmp_buf env; SIMPLE_XPM_FREE(keys); \ SIMPLE_XPM_FREE(color_table); \ SIMPLE_XPM_FREE(pixels); \ - snprintf(error_message, ERROR_MESSAGE_CAP, __VA_ARGS__); \ + fprintf(stderr, "simplexpm: ERROR: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ siglongjmp(env, 1); \ } while (0) -- 2.51.0