Refactor
Move json_value and obj_t to same file - Rename json_obj.* -> json_value.* - Move `print_json_value(...)` to json_value.* Move `err_ctx(...)` to util.* parse.h now only exposes `parse_json_value(...)`
This commit is contained in:
8
Makefile
8
Makefile
@@ -10,22 +10,24 @@ COMPILER ?= gcc
|
||||
CC := gcc
|
||||
|
||||
# -fsanitize={address,undefined}
|
||||
CFLAGS.gcc.debug := -Og -ggdb -fanalyzer -DBACKTRACE -rdynamic
|
||||
CFLAGS.gcc.debug := -Og -ggdb -fanalyzer -DBACKTRACE -rdynamic -fsanitize=address
|
||||
CFLAGS.gcc.release := -O3 -march=native -DNDEBUG
|
||||
CFLAGS.gcc := ${CFLAGS.gcc.${BUILD}} -Iinclude -W{all,extra,error} -fstack-protector-all -std=gnu11
|
||||
|
||||
CFLAGS.clang.debug=-O0 -ggdb -DBACKTRACE
|
||||
CFLAGS.clang.debug=-O0 -g3 -DBACKTRACE -rdynamic
|
||||
CFLAGS.clang.release=-O3 -march=native -DNDEBUG
|
||||
CFLAGS.clang=-Wextra -Wall -Wpedantic -fstack-protector-all ${CFLAGS.clang.${BUILD}}
|
||||
|
||||
CFLAGS := ${CFLAGS.${COMPILER}}
|
||||
|
||||
LD_PRELOAD:=
|
||||
|
||||
# ==== end set compiler flags ====
|
||||
|
||||
BUILD_DIR := bin/${BUILD}
|
||||
|
||||
OBJ_DIR := .obj/${BUILD}
|
||||
_OBJS := main.o parse.o json_obj.o util.o
|
||||
_OBJS := main.o parse.o json_value.o util.o
|
||||
OBJS := $(patsubst %,$(OBJ_DIR)/%,$(_OBJS))
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
#ifndef _obj_H
|
||||
#define _obj_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define OBJ_SIZE 1024
|
||||
|
||||
typedef struct obj_entry {
|
||||
char const* key;
|
||||
struct json_value* val;
|
||||
struct obj_entry* next;
|
||||
} * __p_obj_entry;
|
||||
|
||||
typedef __p_obj_entry obj_t[OBJ_SIZE];
|
||||
|
||||
void* obj_at(obj_t m, char* const key);
|
||||
bool obj_insert(obj_t m, char* const key, struct json_value* value);
|
||||
void obj_delete(obj_t m);
|
||||
|
||||
#endif
|
||||
41
include/json_value.h
Normal file
41
include/json_value.h
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
#ifndef _JSON_VALUE_H
|
||||
#define _JSON_VALUE_H
|
||||
|
||||
#include <stdbool.h> // bool
|
||||
|
||||
#define OBJ_SIZE 64
|
||||
|
||||
typedef struct obj_entry {
|
||||
char const* key;
|
||||
struct json_value* val;
|
||||
struct obj_entry* next;
|
||||
} * __p_obj_entry;
|
||||
|
||||
typedef __p_obj_entry obj_t[OBJ_SIZE];
|
||||
|
||||
enum json_type { object,
|
||||
array,
|
||||
string,
|
||||
number,
|
||||
boolean,
|
||||
null };
|
||||
|
||||
struct json_value {
|
||||
enum json_type type;
|
||||
union {
|
||||
obj_t* object;
|
||||
struct json_value** array;
|
||||
char* string;
|
||||
bool boolean;
|
||||
double number;
|
||||
};
|
||||
};
|
||||
|
||||
void* obj_at(obj_t m, char* const key);
|
||||
bool obj_insert(obj_t m, char* const key, struct json_value* value);
|
||||
void obj_delete(obj_t m);
|
||||
|
||||
void print_json(struct json_value val, int indent);
|
||||
|
||||
#endif
|
||||
@@ -2,31 +2,10 @@
|
||||
#ifndef _PARSE_H
|
||||
#define _PARSE_H
|
||||
|
||||
#include "json_obj.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "json_value.h"
|
||||
|
||||
enum json_type { object,
|
||||
array,
|
||||
string,
|
||||
number,
|
||||
boolean,
|
||||
null };
|
||||
|
||||
struct json_value {
|
||||
enum json_type type;
|
||||
union {
|
||||
obj_t* object;
|
||||
struct json_value** array;
|
||||
char* string;
|
||||
bool boolean;
|
||||
double number;
|
||||
};
|
||||
};
|
||||
#include <stdio.h> // FILE*
|
||||
|
||||
struct json_value parse_json_value(FILE* fp);
|
||||
|
||||
void print_json(struct json_value val, int indent);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#define _UTIL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((__noreturn__)) void err_ctx(int exit_code, FILE* fp, const char* format, ...);
|
||||
|
||||
void* malloc_or_die(size_t size);
|
||||
void* realloc_or_die(void* ptr, size_t size);
|
||||
|
||||
@@ -5,9 +5,17 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "json_obj.h"
|
||||
#include "json_value.h"
|
||||
#include "util.h"
|
||||
|
||||
bool obj_insert(obj_t m, char* const key, struct json_value* value);
|
||||
size_t obj_hash(char const* str);
|
||||
void obj_delete(obj_t m);
|
||||
void print_array(struct json_value** arr, int cur_indent, int indent_amount);
|
||||
void print_json_value(struct json_value val, int cur_indent, int indent_amount);
|
||||
void print_object(obj_t obj, int cur_indent, int indent_amount);
|
||||
void* obj_at(obj_t m, char* const key);
|
||||
|
||||
/*
|
||||
djb2 string hash
|
||||
credits: Daniel J. Bernstein
|
||||
@@ -128,3 +136,93 @@ void obj_delete(obj_t m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_indent(int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
void print_object(obj_t obj, int cur_indent, int indent_amount)
|
||||
{
|
||||
putchar('{');
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (size_t i = 0; i < OBJ_SIZE; i++) {
|
||||
struct obj_entry* e = obj[i];
|
||||
|
||||
while (e != NULL) {
|
||||
if (!first)
|
||||
putchar(',');
|
||||
|
||||
first = false;
|
||||
|
||||
putchar('\n');
|
||||
add_indent(cur_indent);
|
||||
printf("\"%s\": ", e->key);
|
||||
print_json_value(*(e->val), cur_indent + indent_amount, indent_amount);
|
||||
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
add_indent(cur_indent - indent_amount * 2);
|
||||
putchar('}');
|
||||
}
|
||||
|
||||
void print_array(struct json_value** arr, int cur_indent, int indent_amount)
|
||||
{
|
||||
putchar('[');
|
||||
|
||||
if (arr[0] == NULL) {
|
||||
putchar(']');
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; arr[i+1] != NULL; i++) {
|
||||
putchar('\n');
|
||||
add_indent(cur_indent);
|
||||
print_json_value(*arr[i], cur_indent + indent_amount, indent_amount);
|
||||
|
||||
if (arr[i + 1] != NULL)
|
||||
putchar(',');
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
add_indent(cur_indent - indent_amount * 2);
|
||||
putchar(']');
|
||||
}
|
||||
|
||||
void print_json_value(struct json_value val, int cur_indent,
|
||||
int indent_amount)
|
||||
{
|
||||
switch (val.type) {
|
||||
case string:
|
||||
printf("\"%s\"", val.string);
|
||||
break;
|
||||
case number:
|
||||
printf("%lf", val.number);
|
||||
break;
|
||||
case boolean:
|
||||
printf("%s", val.boolean ? "true" : "false");
|
||||
break;
|
||||
case null:
|
||||
printf("null");
|
||||
break;
|
||||
case object:
|
||||
print_object(*val.object, cur_indent + indent_amount, indent_amount);
|
||||
break;
|
||||
case array:
|
||||
print_array(val.array, cur_indent + indent_amount, indent_amount);
|
||||
break;
|
||||
default:
|
||||
printf("<unknown>");
|
||||
}
|
||||
}
|
||||
|
||||
void print_json(struct json_value val, int indent)
|
||||
{
|
||||
print_json_value(val, 0, indent);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
#include "json_obj.h"
|
||||
#include "json_value.h"
|
||||
#include "parse.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
300
src/parse.c
300
src/parse.c
@@ -1,22 +1,19 @@
|
||||
|
||||
#include "parse.h"
|
||||
|
||||
#include "json_value.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <ctype.h> // isspace, isdigit
|
||||
#include <err.h> // err, errx
|
||||
#include <stdarg.h> // va_list
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // exit, EXIT_SUCCESS, EXIT_FAILURE
|
||||
#include <string.h> // strcmp
|
||||
|
||||
#include "json_obj.h"
|
||||
#include "util.h"
|
||||
|
||||
#define EARLY_EOF 202
|
||||
#define MALLOC_DIE 201
|
||||
#define UNEXPECTED_CHAR 200
|
||||
|
||||
#define ERROR_CONTEXT_LEN 80
|
||||
|
||||
char* read_string(FILE* fp);
|
||||
obj_t* read_object(FILE* fp);
|
||||
void discard_whitespace(FILE* fp);
|
||||
@@ -24,94 +21,84 @@ bool read_boolean(FILE* fp);
|
||||
void read_null(FILE* fp);
|
||||
double read_number(FILE* fp);
|
||||
struct json_value** read_array(FILE* fp);
|
||||
void err_ctx(int exit_code, FILE* fp, const char* format, ...);
|
||||
|
||||
void print_object(obj_t obj, int cur_indent, int indent_amount);
|
||||
void print_json_value(struct json_value val, int cur_indent, int indent_amount);
|
||||
void print_array(struct json_value** arr, int cur_indent, int indent_amount);
|
||||
|
||||
// define as a macro to make debugging smoother
|
||||
#define discard_whitespace(fp) \
|
||||
do { \
|
||||
int c; \
|
||||
while (isspace(c = fgetc(fp))) { \
|
||||
if (c == EOF) \
|
||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__); \
|
||||
} \
|
||||
ungetc(c, fp); \
|
||||
} while (0);
|
||||
void discard_whitespace(FILE* fp);
|
||||
|
||||
/*
|
||||
Prints parser errors with surrounding context and
|
||||
terminates the program.
|
||||
|
||||
exit_code - code to exit with
|
||||
fp - file causing parser errors
|
||||
format - error message format string
|
||||
... - format string arguments
|
||||
Consumes the next whitespace character in a file stream
|
||||
and discards them.
|
||||
*/
|
||||
__attribute__((__noreturn__)) void err_ctx(int exit_code, FILE* fp, const char* format, ...)
|
||||
void discard_whitespace(FILE* fp)
|
||||
{
|
||||
va_list args;
|
||||
static char context[ERROR_CONTEXT_LEN];
|
||||
int c;
|
||||
|
||||
va_start(args, format);
|
||||
fputc('\n', stderr);
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, " (at index %zu)\n", ftell(fp));
|
||||
va_end(args);
|
||||
while (isspace(c = fgetc(fp))) {
|
||||
if (c == EOF)
|
||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||
}
|
||||
|
||||
if (fseek(fp, -(ERROR_CONTEXT_LEN / 2), SEEK_CUR) == 0) {
|
||||
size_t n_read = fread(context, sizeof(char), ERROR_CONTEXT_LEN, fp);
|
||||
ungetc(c, fp);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\ncontext:\n");
|
||||
/*
|
||||
Consumes the next JSON value in a file stream and returns a
|
||||
corresponding json_value. See `json_value.h` for details.
|
||||
|
||||
int arrow_offset = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < ERROR_CONTEXT_LEN / 2 && i < n_read; i++) {
|
||||
switch (context[i]) {
|
||||
case '\n':
|
||||
fprintf(stderr, "\\n");
|
||||
arrow_offset += 2;
|
||||
A JSON value can be a JSON string in double quotes, or a JSON number,
|
||||
or true or false or null, or a JOSN object or a JSON array.
|
||||
|
||||
These structures can be nested.
|
||||
*/
|
||||
struct json_value parse_json_value(FILE* fp)
|
||||
{
|
||||
discard_whitespace(fp);
|
||||
int c = fgetc(fp);
|
||||
|
||||
struct json_value result = { 0 };
|
||||
|
||||
switch (c) {
|
||||
case EOF:
|
||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||
|
||||
case '{':
|
||||
result.type = object;
|
||||
result.object = read_object(fp);
|
||||
break;
|
||||
case '\r':
|
||||
fprintf(stderr, "\\r");
|
||||
arrow_offset += 2;
|
||||
|
||||
case '"':
|
||||
result.type = string;
|
||||
result.string = read_string(fp);
|
||||
break;
|
||||
case '\t':
|
||||
fprintf(stderr, "\\t");
|
||||
arrow_offset += 2;
|
||||
|
||||
case '[':
|
||||
result.type = array;
|
||||
result.array = read_array(fp);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
case 'f':
|
||||
ungetc(c, fp);
|
||||
result.type = boolean;
|
||||
result.boolean = read_boolean(fp);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
ungetc(c, fp);
|
||||
read_null(fp);
|
||||
result.type = null;
|
||||
result.number = 0L;
|
||||
break;
|
||||
|
||||
default:
|
||||
fputc(context[i], stderr);
|
||||
arrow_offset += 1;
|
||||
break;
|
||||
if (isdigit(c)) {
|
||||
result.type = number;
|
||||
result.number = read_number(fp);
|
||||
} else {
|
||||
ungetc(c, fp);
|
||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) unexpected symbol %c", __func__, c);
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < ERROR_CONTEXT_LEN && i < n_read; i++) {
|
||||
switch (context[i]) {
|
||||
case '\n':
|
||||
fprintf(stderr, "\\n");
|
||||
break;
|
||||
case '\t':
|
||||
fprintf(stderr, "\\t");
|
||||
break;
|
||||
default:
|
||||
fputc(context[i], stderr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fputc('\n', stderr);
|
||||
for (int i = 0; i < arrow_offset - 2; i++)
|
||||
fputc(' ', stderr);
|
||||
|
||||
fputc('^', stderr);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
exit(exit_code);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -269,7 +256,7 @@ struct json_value** read_array(FILE* fp)
|
||||
break;
|
||||
|
||||
case ']':
|
||||
output[i] = NULL;
|
||||
output[i++] = NULL;
|
||||
return realloc_or_die(output, i * sizeof(struct json_value*));
|
||||
|
||||
case ',':
|
||||
@@ -336,7 +323,6 @@ bool read_boolean(FILE* fp)
|
||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) unexpected symbol", __func__);
|
||||
}
|
||||
|
||||
// TODO: fix int overflow
|
||||
/*
|
||||
A JSON number is very much like a C or Java number,
|
||||
except that the octal and hexadecimal formats are not used.
|
||||
@@ -351,155 +337,3 @@ double read_number(FILE* fp)
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
Consumes the next JSON value in a file stream and returns a
|
||||
corresponding json_value
|
||||
|
||||
A JSON value can be a JSON string in double quotes, or a JSON number,
|
||||
or true or false or null, or a JOSN object or a JSON array.
|
||||
These structures can be nested.
|
||||
*/
|
||||
struct json_value parse_json_value(FILE* fp)
|
||||
{
|
||||
discard_whitespace(fp);
|
||||
int c = fgetc(fp);
|
||||
|
||||
struct json_value result = { 0 };
|
||||
|
||||
switch (c) {
|
||||
case EOF:
|
||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||
|
||||
case '{':
|
||||
result.type = object;
|
||||
result.object = read_object(fp);
|
||||
break;
|
||||
|
||||
case '"':
|
||||
result.type = string;
|
||||
result.string = read_string(fp);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
result.type = array;
|
||||
result.array = read_array(fp);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
case 'f':
|
||||
ungetc(c, fp);
|
||||
result.type = boolean;
|
||||
result.boolean = read_boolean(fp);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
ungetc(c, fp);
|
||||
read_null(fp);
|
||||
result.type = null;
|
||||
result.number = 0L;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isdigit(c)) {
|
||||
result.type = number;
|
||||
result.number = read_number(fp);
|
||||
} else {
|
||||
ungetc(c, fp);
|
||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) unexpected symbol %c", __func__, c);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_indent(int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
void print_object(obj_t obj, int cur_indent, int indent_amount)
|
||||
{
|
||||
putchar('{');
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (size_t i = 0; i < OBJ_SIZE; i++) {
|
||||
struct obj_entry* e = obj[i];
|
||||
|
||||
while (e != NULL) {
|
||||
if (!first)
|
||||
putchar(',');
|
||||
|
||||
first = false;
|
||||
|
||||
putchar('\n');
|
||||
add_indent(cur_indent);
|
||||
printf("\"%s\": ", e->key);
|
||||
print_json_value(*(e->val), cur_indent + indent_amount, indent_amount);
|
||||
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
add_indent(cur_indent - indent_amount * 2);
|
||||
putchar('}');
|
||||
}
|
||||
|
||||
void print_array(struct json_value** arr, int cur_indent, int indent_amount)
|
||||
{
|
||||
putchar('[');
|
||||
|
||||
if (arr[0] == NULL) {
|
||||
putchar(']');
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; arr[i + 1] != NULL; i++) {
|
||||
putchar('\n');
|
||||
add_indent(cur_indent);
|
||||
print_json_value(*arr[i], cur_indent + indent_amount, indent_amount);
|
||||
if (arr[i + 1] != NULL)
|
||||
putchar(',');
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
add_indent(cur_indent - indent_amount * 2);
|
||||
putchar(']');
|
||||
}
|
||||
|
||||
void print_json_value(struct json_value val, int cur_indent,
|
||||
int indent_amount)
|
||||
{
|
||||
switch (val.type) {
|
||||
case string:
|
||||
printf("\"%s\"", val.string);
|
||||
break;
|
||||
case number:
|
||||
printf("%lf", val.number);
|
||||
break;
|
||||
case boolean:
|
||||
printf("%s", val.boolean ? "true" : "false");
|
||||
break;
|
||||
case null:
|
||||
printf("null");
|
||||
break;
|
||||
case object:
|
||||
print_object(*val.object, cur_indent + indent_amount, indent_amount);
|
||||
break;
|
||||
case array:
|
||||
print_array(val.array, cur_indent + indent_amount, indent_amount);
|
||||
break;
|
||||
default:
|
||||
printf("<unknown>");
|
||||
}
|
||||
}
|
||||
|
||||
void print_json(struct json_value val, int indent)
|
||||
{
|
||||
print_json_value(val, 0, indent);
|
||||
}
|
||||
|
||||
88
src/util.c
88
src/util.c
@@ -1,11 +1,14 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <execinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <err.h> // err
|
||||
#include <errno.h> // errno
|
||||
#include <execinfo.h> // backtrace
|
||||
#include <stdarg.h> // va_list
|
||||
#include <stdio.h> // fprintf
|
||||
#include <stdlib.h> // malloc, realloc, calloc
|
||||
|
||||
#define ERROR_CONTEXT_LEN 80
|
||||
|
||||
void* malloc_or_die(size_t size)
|
||||
{
|
||||
@@ -27,7 +30,7 @@ void* realloc_or_die(void* ptr, size_t size)
|
||||
err(errno, "realloc_or_die failed");
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nrealloc_or_die returned NULL, ptr: %p size: %zu\n", ptr, size);
|
||||
fprintf(stderr, "\nrealloc_or_die returned NULL but errno is 0, ptr: %p size: %zu\n", ptr, size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -65,3 +68,76 @@ void print_trace()
|
||||
free(strings);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Prints parser errors with surrounding context and
|
||||
terminates the program.
|
||||
|
||||
exit_code - code to exit with
|
||||
fp - file causing parser errors
|
||||
format - error message format string
|
||||
... - format string arguments
|
||||
*/
|
||||
__attribute__((__noreturn__)) void err_ctx(int exit_code, FILE* fp, const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
static char context[ERROR_CONTEXT_LEN];
|
||||
|
||||
va_start(args, format);
|
||||
fputc('\n', stderr);
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, " (at index %zu)\n", ftell(fp));
|
||||
va_end(args);
|
||||
|
||||
if (fseek(fp, -(ERROR_CONTEXT_LEN / 2), SEEK_CUR) == 0) {
|
||||
size_t n_read = fread(context, sizeof(char), ERROR_CONTEXT_LEN, fp);
|
||||
|
||||
fprintf(stderr, "\ncontext:\n");
|
||||
|
||||
int arrow_offset = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < ERROR_CONTEXT_LEN / 2 && i < n_read; i++) {
|
||||
switch (context[i]) {
|
||||
case '\n':
|
||||
fprintf(stderr, "\\n");
|
||||
arrow_offset += 2;
|
||||
break;
|
||||
case '\r':
|
||||
fprintf(stderr, "\\r");
|
||||
arrow_offset += 2;
|
||||
break;
|
||||
case '\t':
|
||||
fprintf(stderr, "\\t");
|
||||
arrow_offset += 2;
|
||||
break;
|
||||
default:
|
||||
fputc(context[i], stderr);
|
||||
arrow_offset += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < ERROR_CONTEXT_LEN && i < n_read; i++) {
|
||||
switch (context[i]) {
|
||||
case '\n':
|
||||
fprintf(stderr, "\\n");
|
||||
break;
|
||||
case '\t':
|
||||
fprintf(stderr, "\\t");
|
||||
break;
|
||||
default:
|
||||
fputc(context[i], stderr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fputc('\n', stderr);
|
||||
for (int i = 0; i < arrow_offset - 2; i++)
|
||||
fputc(' ', stderr);
|
||||
|
||||
fputc('^', stderr);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user