fix: replace unsafe fixed-size buffers with dynamic formatting helpers; add util format helpers; centralize log_on_line cleanup

This commit is contained in:
2026-04-26 22:39:55 +02:00
parent 70998643fb
commit 05dfb3725b
9 changed files with 156 additions and 45 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
#ifndef LOCATION_H
#define LOCATION_H
#include "string.h"
#include "str.h"
#include <stddef.h>
+26 -15
View File
@@ -3,6 +3,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "util.h"
static LogError* s_logError = NULL;
@@ -19,8 +20,13 @@ void log_error(const char* msg) {
}
void log_on_line(Location* loc, int to_column, const char* msg, ...) {
char line_prefix[32];
int prefix_len = snprintf(line_prefix, sizeof(line_prefix), "%d| ", loc->line);
char* line_prefix = NULL;
char* formatted_msg = NULL;
char* header = NULL;
char* buffer = NULL;
line_prefix = format_string("%d| ", loc->line);
if (!line_prefix) goto cleanup;
int caret_len = to_column - loc->column_start + 1;
if (caret_len < 1) caret_len = 1;
@@ -28,42 +34,47 @@ void log_on_line(Location* loc, int to_column, const char* msg, ...) {
// Format the message
va_list args;
va_start(args, msg);
char formatted_msg[256];
vsnprintf(formatted_msg, sizeof(formatted_msg), msg, args);
formatted_msg = format_string_va(msg, args);
va_end(args);
if (!formatted_msg) goto cleanup;
// Header logic
char header[512];
if (loc->filename && loc->filename[0] != '\0') {
sprintf(header, "--- %s ---\n", loc->filename);
header = format_string("--- %s ---\n", loc->filename);
} else {
sprintf(header, "--- \n");
header = format_string("--- \n");
}
if (!header) goto cleanup;
size_t total_size = strlen(header) + 20 +
prefix_len + loc->line_text.length + 2 + // line| text\n
prefix_len + loc->column_start - 1 + caret_len + 2 + // indent + ^^\n
prefix_len + 3 + strlen(formatted_msg) + 2 + // indent + msg\n
100;
strlen(line_prefix) + loc->line_text.length + 2 + // line| text\n
strlen(line_prefix) + loc->column_start - 1 + caret_len + 2 + // indent + ^^\n
strlen(line_prefix) + 3 + strlen(formatted_msg) + 2 + // indent + msg\n
10;
char* buffer = (char*)malloc(total_size);
if (!buffer) return;
buffer = (char*)malloc(total_size);
if (!buffer) goto cleanup;
char* p = buffer;
p += sprintf(p, "%s", header);
p += sprintf(p, "%s%.*s\n", line_prefix, (int)loc->line_text.length, loc->line_text.data);
// Caret line
for (int i = 0; i < prefix_len + loc->column_start - 1; i++) *p++ = ' ';
for (int i = 0; i < (int)(strlen(line_prefix) + loc->column_start - 1); i++) *p++ = ' ';
for (int i = 0; i < caret_len; i++) *p++ = '^';
*p++ = '\n';
// Message line
for (int i = 0; i < prefix_len; i++) *p++ = ' ';
for (size_t i = 0; i < strlen(line_prefix); i++) *p++ = ' ';
p += sprintf(p, "%s\n", formatted_msg);
*p = '\0';
log_error(buffer);
cleanup:
free(line_prefix);
free(formatted_msg);
free(header);
free(buffer);
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Contains the definition of the String structure, which is a simple representation of a string in C.
*/
#ifndef STR_H
#define STR_H
#include <stddef.h>
/**
* A simple string structure that holds a pointer to the character data and its length.
*/
typedef struct {
char* data;
size_t length;
} String;
#endif
+24 -9
View File
@@ -3,6 +3,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"
static jmp_buf s_testJmp;
static const char* s_failMsg;
@@ -49,8 +50,11 @@ char* read_file_content(const char* filepath) {
}
void assert_log_file(const char* msg) {
char filepath[256];
snprintf(filepath, sizeof(filepath), "v0/tests/%s.log", s_currentTestName);
char* filepath = format_string("v0/tests/%s.log", s_currentTestName);
if (!filepath) {
fail("out of memory");
return;
}
const char* generate = getenv("GENERATE_GOLDEN");
if (generate && strcmp(generate, "1") == 0) {
@@ -67,18 +71,24 @@ void assert_log_file(const char* msg) {
char* content = read_file_content(filepath);
if (!content) {
fail("could not open golden file for reading");
free(filepath);
return;
}
assert_str(content, s_logOutput, msg);
free(content);
free(filepath);
}
void assert_int(int expected, int actual, const char* msg) {
if (expected != actual) {
char buf[64];
snprintf(buf, sizeof(buf), "%s (expected %d, got %d)", msg, expected, actual);
fail(buf);
char* buf = format_string("%s (expected %d, got %d)", msg, expected, actual);
if (buf) {
fail(buf);
free(buf);
} else {
fail("out of memory");
}
}
}
@@ -95,17 +105,22 @@ void assert_false(bool condition, const char* msg) {
}
TokenStream* tokenstream_get_test(void) {
char filepath[256];
snprintf(filepath, sizeof(filepath), "v0/tests/%s.c2", s_currentTestName);
char* filepath = format_string("v0/tests/%s.c2", s_currentTestName);
if (!filepath) {
fail("out of memory");
return NULL;
}
if (s_testSource) free(s_testSource);
s_testSource = read_file_content(filepath);
if (!s_testSource) {
fail("could not read test source file");
free(filepath);
return NULL;
}
return tokenstream_open(filepath, s_testSource);
TokenStream* ts = tokenstream_open(filepath, s_testSource);
free(filepath);
return ts;
}
static void log_append(const char* msg) {
+9 -4
View File
@@ -1,23 +1,28 @@
#include "test.h"
#include "log.h"
#include <string.h>
#include <stdlib.h>
#include "util.h"
static char s_lastLoggedError[256];
static char* s_lastLoggedError = NULL;
static void mock_log(const char* msg) {
strncpy(s_lastLoggedError, msg, sizeof(s_lastLoggedError) - 1);
s_lastLoggedError[sizeof(s_lastLoggedError) - 1] = '\0';
free(s_lastLoggedError);
s_lastLoggedError = format_string("%s", msg ? msg : "");
}
static void test_log_error(void) {
log_set_output(mock_log);
memset(s_lastLoggedError, 0, sizeof(s_lastLoggedError));
free(s_lastLoggedError);
s_lastLoggedError = NULL;
log_error("test error message");
assert_str("test error message", s_lastLoggedError, "expected 'test error message'");
log_set_output(NULL); // Reset to default
free(s_lastLoggedError);
s_lastLoggedError = NULL;
}
static void test_log_on_line(void) {
+14 -9
View File
@@ -1,6 +1,7 @@
#include "test.h"
#include "token.h"
#include <string.h>
#include <stdlib.h>
static void test_tokenstream_open_fail(void) {
TokenStream* ts = tokenstream_open(NULL, NULL);
@@ -97,22 +98,26 @@ static void test_tokenstream_info(void) {
Token t1 = tokenstream_next(ts);
if (t1.token != TOKEN_MODULE) fail("expected TOKEN_MODULE");
char buf1[32];
memcpy(buf1, t1.text.data, t1.text.length);
buf1[t1.text.length] = '\0';
assert_str("module", buf1, "info: expected 'module'");
char* buf1 = malloc((size_t)t1.text.length + 1);
if (!buf1) fail("out of memory");
memcpy(buf1, t1.text.data, t1.text.length);
buf1[t1.text.length] = '\0';
assert_str("module", buf1, "info: expected 'module'");
if (t1.location.line != 1) fail("expected line 1");
if (t1.location.column_start != 1) fail("expected column 1");
Token t2 = tokenstream_next(ts);
if (t2.token != TOKEN_IDENTIFIER) fail("expected TOKEN_IDENTIFIER");
char buf2[32];
memcpy(buf2, t2.text.data, t2.text.length);
buf2[t2.text.length] = '\0';
assert_str("main", buf2, "info: expected 'main'");
char* buf2 = malloc((size_t)t2.text.length + 1);
if (!buf2) { free(buf1); fail("out of memory"); }
memcpy(buf2, t2.text.data, t2.text.length);
buf2[t2.text.length] = '\0';
assert_str("main", buf2, "info: expected 'main'");
if (t2.location.line != 1) fail("expected line 1");
if (t2.location.column_start != 8) fail("expected column 8");
tokenstream_close(ts);
free(buf1);
free(buf2);
tokenstream_close(ts);
}
+5 -1
View File
@@ -124,7 +124,11 @@ TokenStream* tokenstream_open(const char* filename, const char* code) {
return NULL;
}
ts->filename = strdup(filename ? filename : "unknown");
const char* name_src = filename ? filename : "unknown";
ts->filename = malloc(strlen(name_src) + 1);
if (ts->filename) {
memcpy(ts->filename, name_src, strlen(name_src) + 1);
}
ts->code = code;
ts->pos = 0;
ts->line = 1;
+27
View File
@@ -0,0 +1,27 @@
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* format_string_va(const char* fmt, va_list args) {
if (!fmt) return NULL;
va_list args_copy;
va_copy(args_copy, args);
int needed = vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
if (needed < 0) return NULL;
char* buf = (char*)malloc((size_t)needed + 1);
if (!buf) return NULL;
vsnprintf(buf, (size_t)needed + 1, fmt, args);
return buf;
}
char* format_string(const char* fmt, ...) {
if (!fmt) return NULL;
va_list args;
va_start(args, fmt);
char* s = format_string_va(fmt, args);
va_end(args);
return s;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef UTIL_H
#define UTIL_H
#include <stdarg.h>
#include <stddef.h>
/**
* Formats a string using printf-style formatting and returns a newly allocated string.
* The caller is responsible for freeing the returned string.
*
* @param fmt The format string.
* @param ... The values to format.
* @return A newly allocated string containing the formatted output.
*/
char* format_string(const char* fmt, ...);
/**
* Formats a string using printf-style formatting with a va_list and returns a newly allocated string.
* The caller is responsible for freeing the returned string.
*
* @param fmt The format string.
* @param args The va_list of values to format.
* @return A newly allocated string containing the formatted output.
*/
char* format_string_va(const char* fmt, va_list args);
#endif