---
Obtaining line contents in a very stupid way, but also probably the best
we can do given preprocessor shenanigans. Apart from the lack of special
handling for stdin / fake file names. _That said_,
$ gcc -xc -
x
<stdin>:1:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ at end of input
$ echo x > '<stdin>'
$ gcc -xc -
x
<stdin>:1:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ at end of input
1 | x
| ^
Oh, and NUL bytes throw off the line counter, because fgets.
It would make sense to have an option for disabling this. The equivalent
flags for GCC and Clang respectively are -fdiagnostics-plain-output and
-fno-caret-diagnostics. Neither is understood by the other, and both are
rather long.
token.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/token.c b/token.c
index b67baa6..223f6a9 100644
--- a/token.c+++ b/token.c
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>#include "util.h"
#include "cc.h"
@@ -193,11 +194,42 @@ tokencheck(const struct token *t, enum tokenkind kind, const char *msg)
void error(const struct location *loc, const char *fmt, ...)
{
va_list ap;
+ FILE *file;+ static char line[1024];+ size_t len, i;+ int w; fprintf(stderr, "%s:%zu:%zu: error: ", loc->file, loc->line, loc->col);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
++ file = fopen(loc->file, "r");+ if (!file)+ goto exit;+ for (i = 1;;) {+ if (!fgets(line, sizeof(line), file))+ goto exit;+ len = strlen(line);+ if (i == loc->line)+ break;+ if (line[len - 1] == '\n')+ ++i;+ }+ if (line[len - 1] == '\n')+ line[len - 1] = '\0', --len;++ w = snprintf(NULL, 0, "%zu", loc->line);+ if (w < 4)+ w = 4;+ fprintf(stderr, " %*s |\n", w, "");+ fprintf(stderr, " %*zu |\t%s\n", w, loc->line, line);+ fprintf(stderr, " %*s |\t", w, "");+ for (i = 0; i <= len && i + 1 < loc->col; ++i)+ putc(isspace(line[i]) ? line[i] : ' ', stderr);+ fputs("^\n", stderr);++exit: exit(1);
}
--
2.45.0