#include #include #include /* find the start of line number no in buffer buf */ static const char *find_lineno(const char *buf, size_t no) { if (no == 0 || no == 1) return buf; char c; while ((c = *buf)) { buf++; if (c == '\n') no--; if (no == 1) break; } return buf; } static void print_bar(int lineno_len) { fprintf(stderr, "%*s", lineno_len + 4, "| "); } static void vsrc_multi_line(struct src_issue issue, const char *line_start, int lineno_len) { /* just dump lines as they are, adding bars to the left */ size_t line = issue.loc.first_line; for (size_t i = 0;; ++i) { char c = line_start[i]; if (c == 0) { fputc('\n', stderr); break; } fputc(c, stderr); if (c == '\n') { if (++line > issue.loc.last_line) break; print_bar(lineno_len); } } } void vsrc_issue(struct src_issue issue, const char *msg, va_list args) { /* with color support we could start coloring the actual error region at * issue.loc.first_column on issue.loc.first_line, but messing with * xterm color codes is a bit cumbersome so for now just stick to * monochrome */ fprintf(stderr, "%s:%i:%i: ", issue.fname, issue.loc.first_line, issue.loc.first_col); vfprintf(stderr, msg, args); fputc('\n', stderr); /* figure out how many spaces line number takes up */ int lineno_len = snprintf(NULL, 0, "%i", issue.loc.first_line); fprintf(stderr, " %i | ", issue.loc.first_line); const char *line_start = find_lineno(issue.buf, issue.loc.first_line); if (issue.loc.first_line != issue.loc.last_line) { /* multiline location requires slightly different handling */ return vsrc_multi_line(issue, line_start, lineno_len); } /* print the offending line */ for (size_t i = 0;; ++i) { char c = line_start[i]; if (c == 0 || c == '\n') break; fputc(c, stderr); } fputc('\n', stderr); /* the rest is just to print a little squiggly line to highlight exact location */ print_bar(lineno_len); /* print tabs on tabs, otherwise spaces to make start of squiggle line * up with start of offending location */ for (size_t i = 0; i < issue.loc.first_col - 1; ++i) fputc(line_start[i] == '\t' ? '\t' : ' ', stderr); /* highlight the offending location */ for (size_t i = issue.loc.first_col; i < issue.loc.last_col; ++i) { if (i == issue.loc.first_col) fputc('^', stderr); else fputc('~', stderr); } fputc('\n', stderr); } void src_issue(struct src_issue issue, const char *msg, ...) { va_list args; va_start(args, msg); vsrc_issue(issue, msg, args); va_end(args); }