table

Помоћни програм из командне линије за форматирање и приказ CSV-а
git clone https://git.sr.ht/~strahinja/table
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 42c74d764d10f39de4c349c3fc2696c036a044ff
родитељ d5f1aa604fd9ff516d8dc61ed732a50f6c90bc49
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Thu, 12 Mar 2026 22:13:46 +0100

Implement more robust and organized error checking; wchar_t -> u32

Diffstat:
MTODO | 2--
ATODO.done | 9+++++++++
Mdefs.h | 149++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mtable.1.in | 20++++++++++++++++++--
Mtable.c | 373+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
измењених датотека: 5, додавања: 334(+), брисања: 219(-)

diff --git a/TODO b/TODO @@ -1,8 +1,6 @@ TODO ==== -[x] Detect if the output is a terminal and turn off ANSI codes by default if it - isn't Legend diff --git a/TODO.done b/TODO.done @@ -0,0 +1,9 @@ +Done TODOs +========== + +[x] Detect if the output is a terminal and turn off ANSI codes by default if it + isn't + +[x] Check data types + +[x] Implement more robust error checking for -c nn diff --git a/defs.h b/defs.h @@ -12,10 +12,16 @@ #define ANSI_SGR_BOLD_ON "\033[1m" #define ANSI_SGR_BOLD_OFF "\033[0m" -#define LEN(x) (sizeof(x) / sizeof(x[0])) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define UNUSED(x) ((void)(x)) +#define CUSTOM_ERROR_START 200 + +#define HASH(a, b) ((unsigned int)((a) << 8) | (b)) +#define LEN(x) (sizeof(x) / sizeof(x[0])) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define U32(c) ((u32)(0x00000000 | (c))) +#define UNUSED(x) ((void)(x)) + +#define TAB ((u32)0x00000009) #define ENSURE_SIZE(to, temp, tosize, testsize, fromsize, type) \ do \ @@ -27,7 +33,7 @@ if (!temp) \ { \ perror(PROGRAMNAME ": realloc"); \ - exit(1); \ + exit(errno); \ } \ to = temp; \ } \ @@ -40,7 +46,18 @@ if (!ptr) \ { \ perror(PROGRAMNAME ": calloc"); \ - exit(1); \ + exit(errno); \ + } \ + } while (0) + +#define MALLOC(ptr, size) \ + do \ + { \ + ptr = malloc(size); \ + if (!ptr) \ + { \ + perror(PROGRAMNAME ": malloc"); \ + exit(errno); \ } \ } while (0) @@ -51,7 +68,7 @@ if (!newptr) \ { \ perror(PROGRAMNAME ": realloc"); \ - exit(1); \ + exit(errno); \ } \ ptr = newptr; \ } while (0) @@ -84,13 +101,19 @@ *pformat++ = num; \ } while (0) -#define TABLE_ERR_CODE(num) (200 + (num)) - enum { - ERR_NONE = 0, - ERR_BAD_ALIGN = TABLE_ERR_CODE(1), - ERR_BAD_CL_ARG = TABLE_ERR_CODE(2), - ERR_UNMATCHED_QUOTE = TABLE_ERR_CODE(3), + ERR_NONE, + ERR_NO_CL_ARG_PARAM, + ERR_BAD_CL_ARG, + ERR_BAD_ALIGN, + ERR_BAD_NUMBER, + ERR_BAD_FORMAT_SPEC, + ERR_BAD_QUOTES_ARG, + ERR_BAD_SET_SPEC, + ERR_ISDIR, + ERR_NOTREG_FILE, + ERR_UNMATCHED_QUOTE, + ERR_STREAM_ERROR, }; enum { Q_PROCESS, Q_IGNORE }; @@ -131,118 +154,118 @@ typedef enum { typedef enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT } Alignment; -static const u32* table_symbols[][9] = +static const u32 table_symbols[][9] = { [TABLE_SYMBOLS_ASCII] = { // ascii - (u32*)L"+", (u32*)L"-", (u32*)L"+", - (u32*)L"|", (u32*)L" ", (u32*)L"|", - (u32*)L"+", (u32*)L"-", (u32*)L"+", + U32('+'), U32('-'), U32('+'), + U32('|'), U32(' '), U32('|'), + U32('+'), U32('-'), U32('+'), }, [TABLE_SYMBOLS_SINGLE] = { // single - (u32*)L"\u250c", (u32*)L"\u2500", (u32*)L"\u2510", - (u32*)L"\u2502", (u32*)L" ", (u32*)L"\u2502", - (u32*)L"\u2514", (u32*)L"\u2500", (u32*)L"\u2518", + U32(0x250c), U32(0x2500), U32(0x2510), + U32(0x2502), U32(' '), U32(0x2502), + U32(0x2514), U32(0x2500), U32(0x2518), }, [TABLE_SYMBOLS_DOUBLE] = { // double - (u32*)L"\u2554", (u32*)L"\u2550", (u32*)L"\u2557", - (u32*)L"\u2551", (u32*)L" ", (u32*)L"\u2551", - (u32*)L"\u255a", (u32*)L"\u2550", (u32*)L"\u255d", + U32(0x2554), U32(0x2550), U32(0x2557), + U32(0x2551), U32(' '), U32(0x2551), + U32(0x255a), U32(0x2550), U32(0x255d), }, [TABLE_SYMBOLS_EMPTY] = { // empty - (u32*)L" ", (u32*)L" ", (u32*)L" ", - (u32*)L" ", (u32*)L" ", (u32*)L" ", - (u32*)L" ", (u32*)L" ", (u32*)L" ", + U32(' '), U32(' '), U32(' '), + U32(' '), U32(' '), U32(' '), + U32(' '), U32(' '), U32(' '), } }; -static const u32* table_inner_symbols[][3] = +static const u32 table_inner_symbols[][3] = { [TABLE_INNER_ASCII_ASCII] = { // ascii -> ascii - (u32*)L"+", - (u32*)L"|", - (u32*)L"+", + U32('+'), + U32('|'), + U32('+'), }, [TABLE_INNER_SINGLE_SINGLE] = { // single -> single - (u32*)L"\u252c", - (u32*)L"\u2502", - (u32*)L"\u2534", + U32(0x252c), + U32(0x2502), + U32(0x2534), }, [TABLE_INNER_SINGLE_DOUBLE] = { // single -> double - (u32*)L"\u2565", - (u32*)L"\u2551", - (u32*)L"\u2568", + U32(0x2565), + U32(0x2551), + U32(0x2568), }, [TABLE_INNER_DOUBLE_SINGLE] = { // double -> single - (u32*)L"\u2564", - (u32*)L"\u2502", - (u32*)L"\u2567", + U32(0x2564), + U32(0x2502), + U32(0x2567), }, [TABLE_INNER_DOUBLE_DOUBLE] = { // double -> double - (u32*)L"\u2566", - (u32*)L"\u2551", - (u32*)L"\u2569", + U32(0x2566), + U32(0x2551), + U32(0x2569), }, [TABLE_INNER_EMPTY_EMPTY] = { // empty -> empty - (u32*)L" ", - (u32*)L" ", - (u32*)L" ", + U32(' '), + U32(' '), + U32(' '), }, [TABLE_INNER_ASCII_EMPTY] = { // ascii -> empty - (u32*)L"-", - (u32*)L" ", - (u32*)L"-", + U32('-'), + U32(' '), + U32('-'), }, [TABLE_INNER_SINGLE_EMPTY] = { // single -> empty - (u32*)L"\u2500", - (u32*)L" ", - (u32*)L"\u2500", + U32(0x2500), + U32(' '), + U32(0x2500), }, [TABLE_INNER_DOUBLE_EMPTY] = { // double -> empty - (u32*)L"\u2550", - (u32*)L" ", - (u32*)L"\u2550", + U32(0x2550), + U32(' '), + U32(0x2550), }, [TABLE_INNER_EMPTY_ASCII] = { // empty -> ascii - (u32*)L" ", - (u32*)L"|", - (u32*)L" ", + U32(' '), + U32('|'), + U32(' '), }, [TABLE_INNER_EMPTY_SINGLE] = { // empty -> single - (u32*)L" ", - (u32*)L"\u2502", - (u32*)L" ", + U32(' '), + U32(0x2502), + U32(' '), }, [TABLE_INNER_EMPTY_DOUBLE] = { // empty -> double - (u32*)L" ", - (u32*)L"\u2551", - (u32*)L" ", + U32(' '), + U32(0x2551), + U32(' '), } }; diff --git a/table.1.in b/table.1.in @@ -239,11 +239,27 @@ that value is returned as the exit status. Otherwise, exit status is one of the following: .Bl -tag -width Ds -offset indent -compact .It Li 201 -Alignment specification is not valid. +Command line option is missing a required argument. .It Li 202 -Command line argument or its parameter are not recognized. +Command line argument is invalid/unsupported. .It Li 203 +Alignment specification is not valid. +.It Li 204 +Numeric argument is not a valid integer. +.It Li 205 +Format specification is not valid. +.It Li 206 +Specified quotes literal is invalid. +.It Li 207 +Set specification is invalid. +.It Li 208 +File is a directory. +.It Li 209 +File is not a regular file. +.It Li 210 A quote is present without its corresponding closing quote. +.It Li 211 +There has been an input stream error. .El . .Sh EXAMPLES diff --git a/table.c b/table.c @@ -23,25 +23,26 @@ " See the file LICENSE for exact copyright and license " \ "details.") -static int* format = NULL; -static Alignment* align = NULL; -static ssize_t line_size; -static ssize_t outbuf_size; +static int* format = NULL; +static Alignment* align = NULL; static size_t align_size = 0; static size_t colno = 0; static size_t current_rune_column = 0; static size_t current_table_column = 0; static size_t format_size = 0; static size_t lineno = 0; -static size_t rune_columns = 80; static size_t tab_length = 8; static size_t table_columns = 0; -static u32 delimiter = L','; +static ssize_t line_size; +static ssize_t outbuf_size; +static ssize_t rune_columns = 80; +static u32 delimiter = U32(','); static long line_max; static int align_header = 0; static int border_mode = 0; static int current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; static int current_symbol_set = TABLE_SYMBOLS_DOUBLE; +static int error_code = ERR_NONE; static int expand_tabs = 0; static int format_value = 0; static int in_sgr = 0; @@ -57,7 +58,6 @@ static const int alignments[] = { ['c'] = ALIGN_CENTER, ['r'] = ALIGN_RIGHT }; -#define HASH(a,b) ((unsigned int)((a) << 8) | (b)) static const struct { int border; int inner; @@ -87,6 +87,20 @@ static const struct { [HASH('s','s')] = { .border = TABLE_SYMBOLS_SINGLE, .inner = TABLE_INNER_SINGLE_SINGLE } }; +const char* errors[] = { + [ERR_NONE] = "", + [ERR_NO_CL_ARG_PARAM] = "Option requires an argument: %s", + [ERR_BAD_CL_ARG] = "Invalid argument: %s", + [ERR_BAD_ALIGN] = "Invalid alignment specification: %s", + [ERR_BAD_NUMBER] = "Invalid numeric argument: %s", + [ERR_BAD_FORMAT_SPEC] = "Invalid format specification: %s", + [ERR_BAD_QUOTES_ARG] = "Invalid quotes specification: %s", + [ERR_BAD_SET_SPEC] = "Invalid set specification: %s", + [ERR_ISDIR] = "'%s' is a directory", + [ERR_NOTREG_FILE] = "'%s' is not a regular file", + [ERR_UNMATCHED_QUOTE] = "Unmatched quote", + [ERR_STREAM_ERROR] = "Input stream error", +}; /* clang-format on */ static int skip_while_sgr(const u8* s, size_t* char_delta, int also_print, @@ -103,13 +117,15 @@ static int error(const int code, const char* fmt, ...) { va_list args; - char buf[line_max]; + char* buf = NULL; assert(fmt != NULL); + MALLOC(buf, line_max); va_start(args, fmt); - if (vsnprintf(buf, LEN(buf), fmt, args) >= (ssize_t)LEN(buf)) - buf[LEN(buf) - 1] = 0; + if (vsnprintf(buf, line_max, fmt, args) >= line_max) + buf[line_max - 1] = 0; va_end(args); fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); + free(buf); return code; } @@ -124,7 +140,7 @@ number_of_columns(const u8* input, u32 delimiter) assert(input != NULL); pinput = input; - while (pinput && *pinput) + while (*pinput) { if (u8_rune_to_u32(&uch, pinput, &ch_len)) warning("Malformed UTF-8 at position %td: " @@ -156,13 +172,13 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (align == ALIGN_CENTER) for (i = 0; 2 * i + rune_length < to_size; i++) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); (*current_rune_column)++; } else if (align == ALIGN_RIGHT) for (i = 0; i + rune_length < to_size; i++) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); (*current_rune_column)++; } if (align == ALIGN_LEFT) @@ -174,13 +190,13 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, ps += skipped_chars; if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); + fputs(ANSI_SGR_BOLD_ON, stdout); u8_rune_to_u32(&uch, ps, &ch_len); output_chars = 0; while (output_chars != ch_len) { - printf("%c", (char)*ps++); + fputc((char)*ps++, stdout); output_chars++; } output_runes++; @@ -207,13 +223,13 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, ps += skipped_chars; if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); + fputs(ANSI_SGR_BOLD_ON, stdout); u8_rune_to_u32(&uch, ps, &ch_len); output_chars = 0; while (output_chars != ch_len) { - printf("%c", (char)*ps++); + fputc((char)*ps++, stdout); output_chars++; } output_runes++; @@ -239,13 +255,13 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, ps += skipped_chars; if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); + fputs(ANSI_SGR_BOLD_ON, stdout); u8_rune_to_u32(&uch, ps, &ch_len); output_chars = 0; while (output_chars != ch_len) { - printf("%c", (char)*ps++); + fputc((char)*ps++, stdout); output_chars++; } output_runes++; @@ -289,29 +305,35 @@ set_align(const char* arg, Alignment** align, size_t* align_size) } static int -set_columns(const char* arg, size_t* cols) +set_columns(const char* arg, ssize_t* cols) { - size_t c; + ssize_t c; + int result; assert((arg != NULL) && (cols != NULL)); errno = 0; - c = strtol(arg, NULL, 10); - if (errno == EINVAL || errno == ERANGE) + if (!*arg) { - perror(PROGRAMNAME ": strtol"); - return errno; + error_code = ERR_NO_CL_ARG_PARAM; + return 1; + } + result = sscanf(arg, "%zu", &c); + if (result == EOF || result < 1) + { + error_code = ERR_BAD_NUMBER; + return 1; } else *cols = c; - return ERR_NONE; + return 0; } static int -set_delimiter(u8* arg, u32* delimiter) +set_delimiter(const char* arg, u32* delimiter) { size_t delimiter_len = 0; u32 uch; assert((arg != NULL) && (delimiter != NULL)); - u8_rune_to_u32(&uch, arg, &delimiter_len); + u8_rune_to_u32(&uch, (u8*)arg, &delimiter_len); *delimiter = uch; return 0; } @@ -324,7 +346,7 @@ set_format(const char* arg, int** format, size_t* format_size) char* token = NULL; char* ptoken = NULL; size_t token_size = BUF_DEFAULT; - int num; + int num, result; assert((arg != NULL) && (format != NULL) && (format_size != NULL)); CALLOC(token, char, token_size); @@ -338,7 +360,8 @@ set_format(const char* arg, int** format, size_t* format_size) { free(token); free(*format); - *format = NULL; + *format = NULL; + error_code = ERR_BAD_FORMAT_SPEC; return 1; } if (*parg == ':') @@ -347,18 +370,19 @@ set_format(const char* arg, int** format, size_t* format_size) { free(token); free(*format); - *format = NULL; + *format = NULL; + error_code = ERR_BAD_FORMAT_SPEC; return 2; } *ptoken = 0; errno = 0; - num = strtol(token, NULL, 10); - if (errno) + result = sscanf(token, "%u", &num); + if (result == EOF || result < 1) { - perror(PROGRAMNAME ": strtol"); free(token); free(*format); - *format = NULL; + *format = NULL; + error_code = ERR_BAD_NUMBER; return 3; } CHECKSET(*format, pformat, *format_size, num); @@ -373,13 +397,13 @@ set_format(const char* arg, int** format, size_t* format_size) { *ptoken = 0; errno = 0; - num = strtol(token, NULL, 10); - if (errno) + result = sscanf(token, "%u", &num); + if (result == EOF || result < 1) { - perror(PROGRAMNAME ": strtol"); free(token); free(*format); - *format = NULL; + *format = NULL; + error_code = ERR_BAD_NUMBER; return 3; } CHECKSET(*format, pformat, *format_size, num); @@ -397,7 +421,10 @@ set_quotes_behavior(const char* arg, int* quotes) else if (!strcmp(arg, "ignore")) *quotes = Q_IGNORE; else + { + error_code = ERR_BAD_QUOTES_ARG; return 1; + } return 0; } @@ -408,14 +435,20 @@ set_symbol_set(char* arg, int* current_symbol_set, int* current_inner_symbol_set && (current_inner_symbol_set != NULL)); if (!arg[0] || !arg[1] || arg[2] || !strchr("ades", arg[0]) || !strchr("ades", arg[1])) + { + error_code = ERR_BAD_SET_SPEC; return 1; + } assert((arg[0] == 'a') || (arg[0] == 'd') || (arg[0] == 'e') || (arg[0] == 's') || (arg[1] == 'a') || (arg[1] == 'd') || (arg[1] == 'e') || (arg[1] == 's')); if (!strcmp(arg, "ad") || !strcmp(arg, "as") || !strcmp(arg, "ad") || !strcmp(arg, "as") || !strcmp(arg, "da") || !strcmp(arg, "sa")) + { + error_code = ERR_BAD_SET_SPEC; return 1; + } *current_symbol_set = symbol_sets[HASH(arg[0], arg[1])].border; *current_inner_symbol_set = symbol_sets[HASH(arg[0], arg[1])].inner; return 0; @@ -442,8 +475,8 @@ skip_while_sgr(const u8* s, size_t* char_delta, int also_print, int whole_string if (also_print) { - printf("%c", (char)*ps++); - printf("%c", (char)*ps++); + fputc((char)*ps++, stdout); + fputc((char)*ps++, stdout); } else ps += 2; @@ -452,7 +485,7 @@ skip_while_sgr(const u8* s, size_t* char_delta, int also_print, int whole_string while (*ps && *(ps - 1) != 'm') { if (also_print) - printf("%c", (char)*ps++); + fputc((char)*ps++, stdout); else ps++; (*char_delta)++; @@ -464,16 +497,15 @@ skip_while_sgr(const u8* s, size_t* char_delta, int also_print, int whole_string static int startswith(const char* s, const char* what) { - const char* ps = s; assert((s != NULL) && (what != NULL)); - while (*ps && *what) + while (*s && *s == *what) { - if (*what && *ps != *what) + if (*s != *what) return 0; - ps++; + s++; what++; } - return 1; + return *what == 0; } static int @@ -489,7 +521,7 @@ usage(void) " [-s <set> | --symbols=<set>] [-t | --expand-tabs]" " [-y | --yes-ansi] [pathname]\n", PROGRAMNAME, PROGRAMNAME); - return ERR_NONE; + return 0; } static int @@ -498,20 +530,22 @@ version(const int full) printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); if (full) puts(COPYRIGHT); - return ERR_NONE; + return 0; } static void warning(const char* fmt, ...) { va_list args; - char buf[line_max]; + char* buf = NULL; assert(fmt != NULL); + MALLOC(buf, line_max); va_start(args, fmt); - if (vsnprintf(buf, LEN(buf), fmt, args) >= (ssize_t)LEN(buf)) - buf[LEN(buf) - 1] = 0; + if (vsnprintf(buf, line_max, fmt, args) >= line_max) + buf[line_max - 1] = 0; va_end(args); fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); + free(buf); } static int @@ -553,7 +587,7 @@ main(int argc, char** argv) if (pledge("stdio rpath unveil", NULL) < 0) { perror(PROGRAMNAME ": pledge"); - exit(1); + exit(errno); } #endif @@ -564,7 +598,7 @@ main(int argc, char** argv) if (!strcmp(progname, "tsvtable")) { - delimiter = L'\t'; + delimiter = TAB; quotes = Q_IGNORE; } else if (!strcmp(progname, "tablep")) @@ -618,10 +652,7 @@ do_arg: { arg += strlen("align="); if (set_align(arg, &align, &align_size)) - exit(error(ERR_BAD_ALIGN, - "Invalid alignment " - "specification: '%s'", - arg)); + goto main_done; } else if (!strcmp(arg, "border-mode")) border_mode = 1; @@ -629,26 +660,21 @@ do_arg: { arg += strlen("columns="); if (set_columns(arg, &rune_columns)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: " - "'%s'", - arg)); + goto main_done; } else if (startswith(arg, "delimiter=")) { arg += strlen("delimiter="); - if (set_delimiter((u8*)arg, &delimiter)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: " - "'%s'", - arg)); + if (set_delimiter(arg, &delimiter)) + goto main_done; } else if (!strcmp(arg, "expand-tabs")) expand_tabs = 1; else if (startswith(arg, "format=")) { arg += strlen("format="); - set_format(arg, &format, &format_size); + if (set_format(arg, &format, &format_size)) + goto main_done; } else if (!strcmp(arg, "msdos")) msdos = 1; @@ -660,30 +686,27 @@ do_arg: { arg += strlen("quotes="); if (set_quotes_behavior(arg, &quotes)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: " - "'%s'", - arg)); + goto main_done; } else if (startswith(arg, "symbols=")) { arg += strlen("symbols="); if (set_symbol_set(arg, &current_symbol_set, &current_inner_symbol_set)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: " - "'%s'", - arg)); + goto main_done; } else if (!strcmp(arg, "yes-ansi")) no_ansi = 0; else if (!strcmp(arg, "help")) - return usage(); + { + usage(); + goto main_done; + } else { (void)usage(); - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: --%s", arg)); + error_code = ERR_BAD_CL_ARG; + goto main_done; } } else @@ -713,8 +736,7 @@ do_arg: break; case 'h': usage(); - exit(ERR_NONE); - break; + goto main_done; case 'm': msdos = 1; break; @@ -744,12 +766,19 @@ do_arg: break; default: (void)usage(); - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: -%c", c)); + error_code = ERR_BAD_CL_ARG; + goto main_done; } /* Gobble up the rest of the arg ("-d:" etc) */ if (*arg) goto continue_current_arg; + else if (cmd == CMD_COLUMNS || cmd == CMD_DELIMITER + || cmd == CMD_FORMAT || cmd == CMD_SYMBOLS + || cmd == CMD_QUOTES) + { + error_code = ERR_NO_CL_ARG_PARAM; + goto main_done; + } } } else @@ -758,40 +787,33 @@ do_arg: if (cmd == CMD_ALIGN) { if (set_align(arg, &align, &align_size)) - exit(error(ERR_BAD_ALIGN, - "Invalid alignment specification: '%s'", - arg)); + goto main_done; } else if (cmd == CMD_COLUMNS) { if (set_columns(arg, &rune_columns)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: '%s'", arg)); + goto main_done; } else if (cmd == CMD_DELIMITER) { - if (set_delimiter((u8*)arg, &delimiter)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: '%s'", arg)); + if (set_delimiter(arg, &delimiter)) + goto main_done; } else if (cmd == CMD_FORMAT) { if (set_format(arg, &format, &format_size)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: '%s'", arg)); + goto main_done; } else if (cmd == CMD_QUOTES) { if (set_quotes_behavior(arg, &quotes)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: '%s'", arg)); + goto main_done; } else if (cmd == CMD_SYMBOLS) { if (set_symbol_set(arg, &current_symbol_set, &current_inner_symbol_set)) - exit(error(ERR_BAD_CL_ARG, - "Invalid argument: '%s'", arg)); + goto main_done; } else if (filename) warning("Additional argument ignored: '%s'", arg); @@ -828,14 +850,19 @@ done_arg: exit(errno); } if (S_ISDIR(st.st_mode)) - exit(error(EISDIR, "Is a directory: '%s'", filename)); + { + error_code = ERR_ISDIR; + goto main_done; + } + if (!S_ISREG(st.st_mode)) + { + error_code = ERR_NOTREG_FILE; + goto main_done; + } errno = 0; input = fopen(filename, "r"); if (!input) - { - perror(PROGRAMNAME ": fopen"); - exit(error(errno, "Cannot open '%s'", filename)); - } + goto main_done; } else input = stdin; @@ -851,7 +878,14 @@ do_input: goto done_input; if (!fgets((char*)pline, line_size - (pline - line), input)) + { + if (ferror(input)) + { + error_code = ERR_STREAM_ERROR; + goto main_done; + } goto done_input; + } eol = strchr((char*)pline, '\n'); if (eol) @@ -890,8 +924,11 @@ do_input: table_columns = 1; /* Sanity check */ - if (rune_columns < table_columns + 2) + if (rune_columns < 0 || rune_columns < table_columns + 2) + { + warning("Columns adjusted to %u", table_columns + 2); rune_columns = table_columns + 2; + } assert(rune_columns >= table_columns + 2); if (format && !border_mode) @@ -923,9 +960,9 @@ do_column: if (current_rune_column == 0) { u8len = u32_rune_to_u8(u8ch, - *table_symbols[current_symbol_set][0]); + table_symbols[current_symbol_set][0]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); column_start++; } else if (!within_column(column_start, current_rune_column)) @@ -933,18 +970,18 @@ do_column: if (current_table_column == table_columns - 1) { u8len = u32_rune_to_u8(u8ch, - *table_symbols[current_symbol_set][2]); + table_symbols[current_symbol_set][2]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); } else { /* clang-format off */ - u8len = u32_rune_to_u8(u8ch, *table_inner_symbols[ + u8len = u32_rune_to_u8(u8ch, table_inner_symbols[ current_inner_symbol_set][0]); /* clang-format on */ u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); column_start++; if (format) column_start @@ -957,15 +994,15 @@ do_column: else { u8len = u32_rune_to_u8(u8ch, - *table_symbols[current_symbol_set][1]); + table_symbols[current_symbol_set][1]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); } current_rune_column++; goto do_column; done_columns: - printf("\n"); + puts(""); output_lines++; goto not_first; @@ -973,22 +1010,22 @@ empty_line: current_table_column = 0; current_rune_column = 0; column_start = 0; - u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][3]); + u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][3]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); while (current_table_column + 1 < table_columns) { while (within_column(column_start, current_rune_column)) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); current_rune_column++; } /* clang-format off */ u8len = u32_rune_to_u8(u8ch, - *table_inner_symbols[current_inner_symbol_set][1]); + table_inner_symbols[current_inner_symbol_set][1]); /* clang-format on */ u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); column_start++; if (format) column_start += *(format + current_table_column); @@ -999,12 +1036,12 @@ empty_line: } while (within_column(column_start, current_rune_column)) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); current_rune_column++; } - u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][5]); + u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][5]); u8ch[u8len] = 0; - printf("%s\n", u8ch); + puts((const char*)u8ch); pline = line; goto do_input; @@ -1015,17 +1052,18 @@ not_first: column_start = 0; in_sgr = 0; - u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][3]); + u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][3]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); do_line: if (!pline || !*pline) { if (in_quote) { - printf("\n"); - exit(error(ERR_UNMATCHED_QUOTE, "Unmatched quote")); + puts(""); + error_code = ERR_UNMATCHED_QUOTE; + goto main_done; } else goto done_line; @@ -1052,7 +1090,7 @@ continue_line: (ssize_t)(outbuf_size + sizeof((u8*)ANSI_SGR_BOLD_OFF)), u8); poutbuf = outbuf + outbuf_len; u8_rune_to_u32(&uch, pline, &ch_len); - if (uch == (u32)L'\033' && *(pline + 1) == '[') + if (uch == U32('\033') && *(pline + 1) == '[') { in_sgr = 1; if (!no_ansi) @@ -1063,7 +1101,7 @@ continue_line: pline++; colno++; } - else if (uch == (u32)L'm') + else if (uch == U32('m')) { if (!no_ansi || !in_sgr) *poutbuf++ = *pline; @@ -1101,13 +1139,13 @@ continue_line: (lineno == 0 && !align_header) ? ALIGN_LEFT : (current_table_column < align_size - ? align[current_table_column] - : ALIGN_LEFT), + ? align[current_table_column] + : ALIGN_LEFT), &current_rune_column); while (within_column(column_start, current_rune_column)) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); current_rune_column++; } @@ -1123,11 +1161,11 @@ continue_line: if (current_table_column != table_columns - 1) { /* clang-format off */ - u8len = u32_rune_to_u8(u8ch, *table_inner_symbols[ + u8len = u32_rune_to_u8(u8ch, table_inner_symbols[ current_inner_symbol_set][1]); /* clang-format on */ u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); current_rune_column++; current_table_column++; } @@ -1148,7 +1186,7 @@ continue_line: pline++; colno++; } - else if (uch == (u32)L'\t' && expand_tabs) + else if (uch == TAB && expand_tabs) { while ((current_rune_column + outbufrunelen) % tab_length != 0) { @@ -1174,7 +1212,7 @@ continue_line: done_line: if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_OFF); + fputs(ANSI_SGR_BOLD_OFF, stdout); if (*outbuf) { @@ -1189,8 +1227,8 @@ done_line: (lineno == 0 && !align_header) ? ALIGN_LEFT : (current_table_column < align_size - ? align[current_table_column] - : ALIGN_LEFT), + ? align[current_table_column] + : ALIGN_LEFT), &current_rune_column); *outbuf = 0; @@ -1199,13 +1237,13 @@ done_line: { while (within_column(column_start, current_rune_column)) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); current_rune_column++; } u8len = u32_rune_to_u8(u8ch, - *table_inner_symbols[current_inner_symbol_set][1]); + table_inner_symbols[current_inner_symbol_set][1]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); column_start++; if (format) column_start += *(format + current_table_column); @@ -1216,13 +1254,13 @@ done_line: } while (within_column(column_start, current_rune_column)) { - printf("%c", alignment_char); + fputc(alignment_char, stdout); current_rune_column++; } - u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][5]); + u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][5]); u8ch[u8len] = 0; - printf("%s\n", u8ch); + puts((const char*)u8ch); output_lines++; lineno++; @@ -1248,7 +1286,7 @@ do_bottom_border: if (current_rune_column == 0) { u8len = u32_rune_to_u8(u8ch, - *table_symbols[current_symbol_set][6]); + table_symbols[current_symbol_set][6]); column_start++; } else if (!within_column(column_start, current_rune_column)) @@ -1256,14 +1294,13 @@ do_bottom_border: if (current_table_column == table_columns - 1) { u8len = u32_rune_to_u8(u8ch, - *table_symbols[current_symbol_set][8]); + table_symbols[current_symbol_set][8]); current_table_column++; } else { u8len = u32_rune_to_u8(u8ch, - *table_inner_symbols[current_inner_symbol_set] - [2]); + table_inner_symbols[current_inner_symbol_set][2]); column_start++; if (format) column_start @@ -1275,28 +1312,60 @@ do_bottom_border: } else u8len = u32_rune_to_u8(u8ch, - *table_symbols[current_symbol_set][7]); + table_symbols[current_symbol_set][7]); u8ch[u8len] = 0; - printf("%s", u8ch); + fputs((const char*)u8ch, stdout); current_rune_column++; goto do_bottom_border; done_bottom_border: - printf("\n"); + puts(""); output_lines++; skip_bottom_border: +main_done: + if (errno) + perror(PROGRAMNAME); + free(format); free(align); if (opause) { - char buf[line_max]; - printf("Press Enter to continue: "); + char* buf = NULL; + MALLOC(buf, line_max); + puts("Press Enter to continue: "); fflush(stdout); clearerr(stdin); fgets(buf, line_max, stdin); + free(buf); } - fclose(input); - return 0; + if (input) + fclose(input); + + switch (error_code) + { + case ERR_NONE: + break; + case ERR_NO_CL_ARG_PARAM: + case ERR_BAD_CL_ARG: + case ERR_BAD_ALIGN: + error(error_code, errors[error_code], *argv); + break; + case ERR_ISDIR: + case ERR_NOTREG_FILE: + error(error_code, errors[error_code], filename); + break; + case ERR_BAD_NUMBER: + case ERR_BAD_FORMAT_SPEC: + case ERR_BAD_QUOTES_ARG: + case ERR_BAD_SET_SPEC: + error(error_code, errors[error_code], arg); + break; + default: + error(error_code, errors[error_code]); + break; + } + return errno ? errno + : (error_code ? CUSTOM_ERROR_START + error_code : 0); }