]> Trent Huber's Code - xpmview.git/commitdiff
Implemented ignoring comments in XPM files
authorTrent Huber <trentmhuber@gmail.com>
Fri, 8 Nov 2024 22:05:54 +0000 (17:05 -0500)
committerTrent Huber <trentmhuber@gmail.com>
Fri, 8 Nov 2024 22:05:54 +0000 (17:05 -0500)
 - Also started reworking error messages
 - Including line number in relavent errors

cbs.c
reference/SourceCodePro.ttf [moved from fonts/SourceCodePro.ttf with 100% similarity]
src/main.c
src/parser.c
src/tokenizer.c
src/tokenizer.h
src/utils.c
src/utils.h

diff --git a/cbs.c b/cbs.c
index 808d326ec137bf9b3481f3f7423ef7e5d1a227f9..9e8cb63e0dba4c0b1dc2b14eab5be82c699e191b 100644 (file)
--- 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);
    }
 
index 13fe015ce0f3493f85be3366126ac8b1b17fad12..620313d82dc56df14b0cac6fc11dcd3f77b700b7 100644 (file)
 #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();
    }
index 831df1bf4ebf2bcc4cf7ec3161f78db61a8ed77c..8eccddeb5c04b4f2a99bd28c845f4e46bb6559ba 100644 (file)
@@ -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, ";");
index 3df6491ce5f66f680a0a44f00be081f2f40ef86d..06c2caa7fe4a46c56c94f58fbfc3438d4d02ff2b 100644 (file)
@@ -1,3 +1,4 @@
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -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 {
index cf9e5b3759d9e53206fa25cf0e4f3240ad7d503e..bce078ecaf05d8b454cb8eaa13e464af0b11d181 100644 (file)
@@ -4,8 +4,10 @@
 #include <stdbool.h>
 #include <stdio.h>
 
-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);
index 96611e4edf2e8af406d85dbce73dfec213e33f77..4df72e7e248a626ba560fd0df8d688b962a27dd2 100644 (file)
@@ -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);
index ce0a3564aebe15662a85d2ac1065b5540d12fbb6..d247be64181fe37725c0b5743873f6817e1a1089 100644 (file)
@@ -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)