Tail newly-appended complete lines from the child log, starting at *tail_pos. * A partial (non-newline-terminated) final line is left buffered: *tail_pos is * not advanced past it, so it is re-read once completed. Returns true if any * complete line was consumed (i.e. there was progress). */
| 92 | * not advanced past it, so it is re-read once completed. Returns true if any |
| 93 | * complete line was consumed (i.e. there was progress). */ |
| 94 | static bool cbm_tail_log(const char *log_file, long *tail_pos, cbm_proc_log_cb cb, void *ud) { |
| 95 | if (!log_file) { |
| 96 | return false; |
| 97 | } |
| 98 | FILE *lf = fopen(log_file, "r"); |
| 99 | if (!lf) { |
| 100 | return false; |
| 101 | } |
| 102 | bool progressed = false; |
| 103 | if (fseek(lf, *tail_pos, SEEK_SET) == 0) { |
| 104 | char line[1024]; |
| 105 | for (;;) { |
| 106 | long before = ftell(lf); |
| 107 | if (!fgets(line, sizeof(line), lf)) { |
| 108 | break; |
| 109 | } |
| 110 | size_t l = strlen(line); |
| 111 | bool complete = (l > 0 && line[l - 1] == '\n'); |
| 112 | if (complete) { |
| 113 | line[l - 1] = '\0'; |
| 114 | *tail_pos = ftell(lf); |
| 115 | progressed = true; |
| 116 | if (line[0] && cb) { |
| 117 | cb(line, ud); |
| 118 | } |
| 119 | } else if (l == sizeof(line) - 1) { |
| 120 | /* Oversized line filled the buffer without a newline — consume it |
| 121 | * anyway (counts as progress) so we never stall on one long line. */ |
| 122 | *tail_pos = ftell(lf); |
| 123 | progressed = true; |
| 124 | if (cb) { |
| 125 | cb(line, ud); |
| 126 | } |
| 127 | } else { |
| 128 | /* Genuine partial final line — keep it buffered for next poll. */ |
| 129 | *tail_pos = before; |
| 130 | break; |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | fclose(lf); |
| 135 | return progressed; |
| 136 | } |
| 137 | |
| 138 | /* ── Windows command-line quoting (pure; unit-tested on every platform) ─────── */ |
| 139 |
no outgoing calls
no test coverage detected