diff --git a/src/asm_errors.h b/src/asm_errors.h index 58a2bd0..0f98e85 100644 --- a/src/asm_errors.h +++ b/src/asm_errors.h @@ -6,23 +6,23 @@ /* Enums */ typedef enum { - ET_SYNTAX, - ET_FILEIO + ET_INCLUDE } ASMErrorType; typedef enum { - ED_INCLUDE_BAD_ARG, + ED_BAD_ARG, + ED_RECURSION, ED_FILE_READ_ERR } ASMErrorDesc; /* Strings */ static const char *asm_error_types[] = { - "invalid syntax", - "file I/O" + "include directive" }; static const char *asm_error_descs[] = { - "bad argument passed to include directive", - "couldn't read from file" + "missing or invalid argument", + "infinite recursion detected", + "couldn't read included file" }; diff --git a/src/assembler.c b/src/assembler.c index 36d74f4..6e582c6 100644 --- a/src/assembler.c +++ b/src/assembler.c @@ -515,6 +515,7 @@ char* read_include_path(const ASMLine *line) if (!dup) OUT_OF_MEMORY() + // TODO: should normalize filenames in some way to prevent accidental dupes snprintf(path, maxlen, "%s/%.*s", dirname(dup), (int) (i - start), line->data + start); free(dup); @@ -526,6 +527,23 @@ char* read_include_path(const ASMLine *line) } /* + Return whether the given path has already been loaded. +*/ +static bool path_has_been_loaded( + const char *path, const LineBuffer *root, const ASMInclude *include) +{ + if (!strcmp(path, root->filename)) + return true; + + while (include) { + if (!strcmp(path, include->lines->filename)) + return true; + include = include->next; + } + return false; +} + +/* Build a LineBuffer into a ASMLines, normalizing them along the way. This function operates recursively to handle includes, but handles no other @@ -537,8 +555,8 @@ char* read_include_path(const ASMLine *line) *includes may be updated in either case. */ static ErrorInfo* build_asm_lines( - const LineBuffer *source, ASMLine **head, ASMLine **tail, - ASMInclude **includes) + const LineBuffer *root, const LineBuffer *source, ASMLine **head, + ASMLine **tail, ASMInclude **includes) { ASMLine dummy = {.next = NULL}; ASMLine *line, *prev = &dummy; @@ -559,17 +577,24 @@ static ErrorInfo* build_asm_lines( ErrorInfo *ei; char *path = read_include_path(line); if (!path) { - ei = create_error(line, ET_SYNTAX, ED_INCLUDE_BAD_ARG); + ei = create_error(line, ET_INCLUDE, ED_BAD_ARG); + free_asm_lines(line); + free_asm_lines(dummy.next); + return ei; + } + + if (path_has_been_loaded(path, root, *includes)) { + ei = create_error(line, ET_INCLUDE, ED_RECURSION); free_asm_lines(line); free_asm_lines(dummy.next); + free(path); return ei; } - // TODO: handle recursive includes properly LineBuffer *incbuffer = read_source_file(path, false); free(path); if (!incbuffer) { - ei = create_error(line, ET_FILEIO, ED_FILE_READ_ERR); + ei = create_error(line, ET_INCLUDE, ED_FILE_READ_ERR); free_asm_lines(line); free_asm_lines(dummy.next); return ei; @@ -583,16 +608,17 @@ static ErrorInfo* build_asm_lines( include->next = *includes; *includes = include; - ASMLine *ihead, *itail; - if ((ei = build_asm_lines(incbuffer, &ihead, &itail, includes))) { + ASMLine *inchead, *inctail; + if ((ei = build_asm_lines(root, incbuffer, &inchead, &inctail, + includes))) { append_to_error(ei, line); free_asm_lines(line); free_asm_lines(dummy.next); return ei; } - prev->next = ihead; - prev = itail; + prev->next = inchead; + prev = inctail; free_asm_lines(line); // Destroy only the .include line } else { @@ -634,7 +660,8 @@ static ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) ErrorInfo* ei; - if ((ei = build_asm_lines(source, &state->lines, NULL, &state->includes))) + if ((ei = build_asm_lines(source, source, &state->lines, NULL, + &state->includes))) return ei; // TODO: iterate here for all global preprocessor directives