From 76182c27ae8d9f791c9088f0171287443f51aff3 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Fri, 14 Feb 2025 15:55:27 +0000 Subject: [PATCH] log file: Try to process any ANSI escape sequences This is a rather basic implementation and it has the problem that it erases the entire timestamp on carriage return, too. Signed-off-by: Michael Tremer --- src/pakfire/log_file.c | 110 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 5 deletions(-) diff --git a/src/pakfire/log_file.c b/src/pakfire/log_file.c index dab4a03e..2733d946 100644 --- a/src/pakfire/log_file.c +++ b/src/pakfire/log_file.c @@ -27,6 +27,8 @@ #include #include +#define MAX_LINE_LENGTH 4096 + struct pakfire_log_file { struct pakfire_ctx* ctx; int nrefs; @@ -186,7 +188,104 @@ int pakfire_log_file_close(struct pakfire_log_file* self) { return 0; } +#define pakfire_log_file_process_line(file, line, buffer) \ + __pakfire_log_file_process_line(file, line, sizeof(line), buffer) + +static int __pakfire_log_file_process_line(struct pakfire_log_file* self, + char* line, ssize_t length, const char* p) { + char* e = NULL; + int num; + + // The cursor position + int cursor = 0; + + while (*p) { + // Match the beginning of an ESC sequence + switch (*p) { + case '\x1B': + p++; + + // Match CSI sequences + switch (*p) { + case '[': + p++; + + // Parse the number + num = strtoul(p, &e, 10); + + // Continue from after the number + p = e; + + switch (*p) { + // Move the cursor to the left + case 'D': + cursor -= (num > 0) ? num : 1; + + // Don't go too far left + if (cursor < 0) + cursor = 0; + break; + + // Move the cursor to the right + case 'C': + cursor += (num > 0) ? num : 1; + + // Don't go too far right + if (cursor >= length) + cursor = length - 1; + break; + + // Move to a certain column + case 'G': + cursor = (num > 0) ? num - 1 : 0; + + // Don't go too far right + if (cursor >= length) + cursor = length - 1; + break; + } + + // Skip the final character + p++; + break; + } + break; + + // Backspace + case '\x08': + if (cursor > 0) { + cursor--; + + // Erase the previous character + line[cursor] = ' '; + } + p++; + break; + + // Carriage Return + case '\x0d': + // Erase everything up to the cursor + for (int i = 0; i < cursor; i++) + line[i] = '\0'; + + cursor = 0; + p++; + break; + + // Normal characters + default: + if (cursor < length - 1) + line[cursor++] = *p; + p++; + break; + } + } + + return 0; +} + int pakfire_log_file_write(struct pakfire_log_file* self, const char* buffer, ssize_t length) { + char line[MAX_LINE_LENGTH] = {}; int r; // Check inputs @@ -197,13 +296,14 @@ int pakfire_log_file_write(struct pakfire_log_file* self, const char* buffer, ss if (unlikely(!self->f)) return -EBADF; - // Automatically determine the length - if (unlikely(length < 0)) - length = strlen(buffer); + // Process any ANSI Escape codes + r = pakfire_log_file_process_line(self, line, buffer); + if (r < 0) + return r; // Write the buffer to the file - r = fwrite(buffer, 1, length, self->f); - if (unlikely(r < length)) { + r = fprintf(self->f, "%s", line); + if (unlikely(r < 0)) { ERROR(self->ctx, "Could not write to log file: %m\n"); return -errno; } -- 2.39.5