#include #include #include #include #include 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; } const char *issue_level_str(enum issue_level level) { switch (level) { case SRC_INFO: return "info"; case SRC_WARN: return "warn"; case SRC_ERROR: return "error"; } return "unknown"; } static void _issue(struct src_issue issue, const char *fmt, va_list args) { /* get start and end of current line in buffer */ const char *line_start = find_lineno(issue.fctx.fbuf, issue.loc.first_line); const char *line_end = strchr(line_start, '\n'); if (!line_end) line_end = strchr(line_start, 0); const int line_len = line_end - line_start; fprintf(stderr, "%s:%i:%i: %s: ", issue.fctx.fname, issue.loc.first_line, issue.loc.first_col, issue_level_str(issue.level)); vfprintf(stderr, fmt, args); fputc('\n', stderr); int lineno_len = snprintf(NULL, 0, "%i", issue.loc.first_line); fputc(' ', stderr); fprintf(stderr, "%i | ", issue.loc.first_line); fprintf(stderr, "%.*s\n", line_len, line_start); for (int i = 0; i < lineno_len + 2; ++i) fputc(' ', stderr); fprintf(stderr, "| "); for (int i = 0; i < issue.loc.first_col - 1; ++i) fputc(line_start[i] == '\t' ? '\t' : ' ', stderr); for (int 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 *err_msg, ...) { va_list args; va_start(args, err_msg); _issue(issue, err_msg, args); va_end(args); } void internal_error(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "internal error: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); } void internal_warn(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "internal warning: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); }