]> git.ipfire.org Git - pakfire.git/commitdiff
log file: Try to process any ANSI escape sequences
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Feb 2025 15:55:27 +0000 (15:55 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Feb 2025 17:07:05 +0000 (17:07 +0000)
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 <michael.tremer@ipfire.org>
src/pakfire/log_file.c

index dab4a03eee92277500becca5a4ddbb2532f7c4ac..2733d94650bfc327360653643f314c4905e07cc1 100644 (file)
@@ -27,6 +27,8 @@
 #include <pakfire/util.h>
 #include <pakfire/xfopen.h>
 
+#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;
        }