table

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

чување a5240433d0b525051afbb60cccb9cd1d7b1d6ecb
родитељ db90058840223f15d7460086fd4b81a7aa3c886b
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sat,  6 Jul 2024 14:23:21 +0200

table.c: Use assert(3); make setter functions use data structures; 
(error,warning): Null-terminate buf; (startswith): Rewrite; (main): 
Check alignment string for validity; replace redundant snprintf with 
direct assignment

Diffstat:
Mtable.c | 385++++++++++++++++++++++++++++++++++---------------------------------------------
измењених датотека: 1, додавања: 164(+), брисања: 221(-)

diff --git a/table.c b/table.c @@ -2,9 +2,13 @@ * any later version. Copyright (C) 2020-2024 Страхиња Радић. * See the file LICENSE for exact copyright and license details. */ +#define NDEBUG + +#include <assert.h> #include <errno.h> #include <limits.h> #include <stdarg.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -20,60 +24,9 @@ " See the file LICENSE for exact copyright and license " \ "details.") -#define CHECKEXITNOMEM(ptr) \ - do \ - { \ - if (!ptr) \ - exit(error(ENOMEM, \ - "Memory allocation failed (out of memory?)")); \ - } while (0) - -#define CALLOC(ptr, ptrtype, nmemb) \ - do \ - { \ - ptr = calloc(nmemb, sizeof(ptrtype)); \ - CHECKEXITNOMEM(ptr); \ - } while (0) - -#define REALLOC(ptr, ptrtype, newsize) \ - do \ - { \ - ptrtype* newptr = realloc(ptr, newsize); \ - CHECKEXITNOMEM(newptr); \ - ptr = newptr; \ - } while (0) - -#define REALLOCARRAY(ptr, membtype, newcount) \ - REALLOC(ptr, membtype, sizeof(membtype) * newcount); - -#define CHECKCOPY(token, ptoken, token_size, parg) \ - do \ - { \ - if (ptoken + 2 > token + token_size) \ - { \ - size_t old_size = token_size; \ - token_size += BUF_DELTA; \ - REALLOC(token, char, token_size); \ - ptoken = token + old_size - 1; \ - } \ - *ptoken++ = *parg++; \ - } while (0) - -#define CHECKSET(format, pformat, format_size, num) \ - do \ - { \ - if (pformat + 2 > format + format_size) \ - { \ - size_t old_size = format_size; \ - format_size += BUF_DELTA; \ - REALLOC(format, int, format_size); \ - pformat = format + old_size - 1; \ - } \ - *pformat++ = num; \ - } while (0) - long line_max; ssize_t line_size; +ssize_t outbuf_size; size_t colno = 0; size_t lineno = 0; int current_symbol_set = TABLE_SYMBOLS_DOUBLE; @@ -99,6 +52,44 @@ int expand_tabs = 0; int quotes = Q_PROCESS; char alignment_char = ' '; +/* clang-format off */ +static const int alignments[] = { + ['l'] = ALIGN_LEFT, + ['c'] = ALIGN_CENTER, + ['r'] = ALIGN_RIGHT +}; +#define HASH(a,b) ((unsigned int)((a) << 8) | (b)) +static const struct { + int border; + int inner; +} symbol_sets[] = { + [HASH('a','a')] = { .border = TABLE_SYMBOLS_ASCII, + .inner = TABLE_INNER_ASCII_ASCII }, + [HASH('a','e')] = { .border = TABLE_SYMBOLS_ASCII, + .inner = TABLE_INNER_ASCII_EMPTY }, + [HASH('d','d')] = { .border = TABLE_SYMBOLS_DOUBLE, + .inner = TABLE_INNER_DOUBLE_DOUBLE }, + [HASH('d','e')] = { .border = TABLE_SYMBOLS_DOUBLE, + .inner = TABLE_INNER_DOUBLE_EMPTY }, + [HASH('d','s')] = { .border = TABLE_SYMBOLS_DOUBLE, + .inner = TABLE_INNER_DOUBLE_SINGLE }, + [HASH('e','a')] = { .border = TABLE_SYMBOLS_EMPTY, + .inner = TABLE_INNER_EMPTY_ASCII }, + [HASH('e','d')] = { .border = TABLE_SYMBOLS_EMPTY, + .inner = TABLE_INNER_EMPTY_DOUBLE }, + [HASH('e','e')] = { .border = TABLE_SYMBOLS_EMPTY, + .inner = TABLE_INNER_EMPTY_EMPTY }, + [HASH('e','s')] = { .border = TABLE_SYMBOLS_EMPTY, + .inner = TABLE_INNER_EMPTY_SINGLE }, + [HASH('s','d')] = { .border = TABLE_SYMBOLS_SINGLE, + .inner = TABLE_INNER_SINGLE_DOUBLE }, + [HASH('s','e')] = { .border = TABLE_SYMBOLS_SINGLE, + .inner = TABLE_INNER_SINGLE_EMPTY }, + [HASH('s','s')] = { .border = TABLE_SYMBOLS_SINGLE, + .inner = TABLE_INNER_SINGLE_SINGLE } +}; +/* clang-format on */ + int version(const int full) { @@ -124,60 +115,48 @@ usage(void) return 0; } -void -warning(char* fmt, ...) +int +error(const int code, const char* fmt, ...) { - char buf[line_max]; va_list args; + char buf[line_max]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), (const char*)fmt, args); + if (vsnprintf(buf, LEN(buf), fmt, args) >= (ssize_t)LEN(buf)) + buf[LEN(buf) - 1] = 0; va_end(args); fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); + return code; } -int -error(int code, char* fmt, ...) +void +warning(const char* fmt, ...) { - char buf[line_max]; va_list args; + char buf[line_max]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), (const char*)fmt, args); + if (vsnprintf(buf, LEN(buf), fmt, args) >= (ssize_t)LEN(buf)) + buf[LEN(buf) - 1] = 0; va_end(args); fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); - return code; -} - -char* -substr(const char* src, int start, int finish) -{ - int len = strlen(src); - if (finish > len) - finish = len; - int substr_len = finish - start; - if (substr_len < 0) - substr_len = 0; - char* result = NULL; - CALLOC(result, char, substr_len + 1); - char* presult = result; - for (int i = start; i < finish && *(src + i) != '\0'; i++) - *presult++ = *(src + i); - *presult = 0; - - return result; } int startswith(const char* s, const char* what) { - if (!s || !what) - return 0; + const char* ps = s; - char* subs = substr(s, 0, strlen(what)); - int result = !strcmp(subs, what); + if (!ps || !what) + return 0; - free(subs); + while (*ps && *what) + { + if (*what && *ps != *what) + return 0; + ps++; + what++; + } - return result; + return 1; } size_t @@ -195,70 +174,18 @@ within_column(size_t column_start, size_t current_rune_column) int set_symbol_set(char* arg, int* current_symbol_set, int* current_inner_symbol_set) { - if (!strcmp(arg, "aa")) - { - *current_symbol_set = TABLE_SYMBOLS_ASCII; - *current_inner_symbol_set = TABLE_INNER_ASCII_ASCII; - } - else if (!strcmp(arg, "ss")) - { - *current_symbol_set = TABLE_SYMBOLS_SINGLE; - *current_inner_symbol_set = TABLE_INNER_SINGLE_SINGLE; - } - else if (!strcmp(arg, "sd")) - { - *current_symbol_set = TABLE_SYMBOLS_SINGLE; - *current_inner_symbol_set = TABLE_INNER_SINGLE_DOUBLE; - } - else if (!strcmp(arg, "ds")) - { - *current_symbol_set = TABLE_SYMBOLS_DOUBLE; - *current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; - } - else if (!strcmp(arg, "dd")) - { - *current_symbol_set = TABLE_SYMBOLS_DOUBLE; - *current_inner_symbol_set = TABLE_INNER_DOUBLE_DOUBLE; - } - else if (!strcmp(arg, "ee")) - { - *current_symbol_set = TABLE_SYMBOLS_EMPTY; - *current_inner_symbol_set = TABLE_INNER_EMPTY_EMPTY; - } - else if (!strcmp(arg, "ae")) - { - *current_symbol_set = TABLE_SYMBOLS_ASCII; - *current_inner_symbol_set = TABLE_INNER_ASCII_EMPTY; - } - else if (!strcmp(arg, "se")) - { - *current_symbol_set = TABLE_SYMBOLS_SINGLE; - *current_inner_symbol_set = TABLE_INNER_SINGLE_EMPTY; - } - else if (!strcmp(arg, "de")) - { - *current_symbol_set = TABLE_SYMBOLS_DOUBLE; - *current_inner_symbol_set = TABLE_INNER_DOUBLE_EMPTY; - } - else if (!strcmp(arg, "ea")) - { - *current_symbol_set = TABLE_SYMBOLS_EMPTY; - *current_inner_symbol_set = TABLE_INNER_EMPTY_ASCII; - } - else if (!strcmp(arg, "es")) - { - *current_symbol_set = TABLE_SYMBOLS_EMPTY; - *current_inner_symbol_set = TABLE_INNER_EMPTY_SINGLE; - } - else if (!strcmp(arg, "ed")) - { - *current_symbol_set = TABLE_SYMBOLS_EMPTY; - *current_inner_symbol_set = TABLE_INNER_EMPTY_DOUBLE; - } - else - { - return error(1, "Invalid symbol set: %s", arg); - } + if (!arg[0] || !arg[1] || arg[2] || !strchr("ades", arg[0]) + || !strchr("ades", arg[1])) + 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")) + 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; } @@ -274,24 +201,17 @@ set_align(const char* arg, Alignment** align, size_t* align_size) while (*parg) { - switch (*parg) + if (!strchr("lcr", *parg)) { - case 'l': - *palign++ = ALIGN_LEFT; - break; - case 'c': - *palign++ = ALIGN_CENTER; - break; - case 'r': - *palign++ = ALIGN_RIGHT; - break; - default: free(*align); *align = NULL; *align_size = 0; return 1; } - parg++; + + assert((*parg == 'l') || (*parg == 'c') || (*parg == 'r')); + + *palign++ = alignments[(int)*parg++]; } return 0; @@ -316,7 +236,7 @@ set_delimiter(u8* arg, u32* delimiter) size_t delimiter_len = 0; u32 uch; - u8_char_to_u32(&uch, arg, &delimiter_len); + u8_rune_to_u32(&uch, arg, &delimiter_len); *delimiter = uch; return 0; @@ -485,7 +405,7 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (!no_ansi && lineno == 0) printf("%s", ANSI_SGR_BOLD_ON); - u8_char_to_u32(&uch, ps, &ch_len); + u8_rune_to_u32(&uch, ps, &ch_len); output_chars = 0; while (output_chars != ch_len) { @@ -504,7 +424,7 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, skip_while_sgr(ps, &skipped_chars, 1, 0); ps += skipped_chars; - u8_char_to_u32(&uch, ps, &ch_len); + u8_rune_to_u32(&uch, ps, &ch_len); ps += ch_len; skipped_runes++; } @@ -518,7 +438,7 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (!no_ansi && lineno == 0) printf("%s", ANSI_SGR_BOLD_ON); - u8_char_to_u32(&uch, ps, &ch_len); + u8_rune_to_u32(&uch, ps, &ch_len); output_chars = 0; while (output_chars != ch_len) { @@ -537,7 +457,7 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, skip_while_sgr(ps, &skipped_chars, 1, 0); ps += skipped_chars; - u8_char_to_u32(&uch, ps, &ch_len); + u8_rune_to_u32(&uch, ps, &ch_len); ps += ch_len; skipped_runes++; } @@ -550,7 +470,7 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (!no_ansi && lineno == 0) printf("%s", ANSI_SGR_BOLD_ON); - u8_char_to_u32(&uch, ps, &ch_len); + u8_rune_to_u32(&uch, ps, &ch_len); output_chars = 0; while (output_chars != ch_len) { @@ -581,8 +501,9 @@ number_of_columns(const u8* input, u32 delimiter) while (pinput && *pinput) { - if (u8_char_to_u32(&uch, pinput, &ch_len)) - warning("Malformed UTF8 at position %td: uch=%02X, " + if (u8_rune_to_u32(&uch, pinput, &ch_len)) + warning("Malformed UTF-8 at position %td: " + "uch=%02X, " "pinput=%02X", pinput - input, uch, *pinput); if (uch == delimiter) @@ -605,17 +526,19 @@ main(int argc, char** argv) FILE* input = NULL; Command cmd = CMD_NONE; u32 uch; - u8* line = NULL; - u8* pline = NULL; - u8* outbuf = NULL; - u8* poutbuf = NULL; + u8* line = NULL; + u8* pline = NULL; + u8* outbuf = NULL; + u8* poutbuf = NULL; + u8* tpoutbuf = NULL; u8* pch_end; u8 u8ch[7]; char* arg; - char* filename = NULL; - char* sep = NULL; - char* progname = NULL; - char* eol = NULL; + char* filename = NULL; + char* sep = NULL; + char* progname = NULL; + char* eol = NULL; + ptrdiff_t outbuf_len; size_t outbufrunelen = 0; size_t ch_len = 0; size_t output_lines = 0; @@ -677,8 +600,9 @@ do_arg: if (*arg == '-') { + char c; arg++; - char c = *arg++; + c = *arg++; if (c == '-') { if (!strcmp(arg, "full-version")) @@ -692,7 +616,10 @@ do_arg: else if (startswith(arg, "align=")) { arg += strlen("align="); - set_align(arg, &align, &align_size); + if (set_align(arg, &align, &align_size)) + exit(error(1, + "Invalid alignment " + "specification")); } else if (!strcmp(arg, "border-mode")) border_mode = 1; @@ -899,7 +826,8 @@ done_arg: line_size = line_max; CALLOC(line, u8, line_size); - CALLOC(outbuf, u8, line_size); + outbuf_size = line_max; + CALLOC(outbuf, u8, outbuf_size); pline = line; do_input: @@ -920,7 +848,8 @@ do_input: { line_size += line_max; REALLOC(line, u8, line_size); - REALLOC(outbuf, u8, line_size); + outbuf_size += line_max; + REALLOC(outbuf, u8, outbuf_size); pline = line + line_size - line_max - 1; goto do_input; } @@ -947,6 +876,7 @@ do_input: /* Sanity check */ if (rune_columns < table_columns + 2) rune_columns = table_columns + 2; + assert(rune_columns >= table_columns + 2); if (format && !border_mode) { @@ -976,7 +906,7 @@ do_column: if (current_rune_column == 0) { - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][0]); u8ch[u8len] = 0; printf("%s", u8ch); @@ -986,16 +916,17 @@ do_column: { if (current_table_column == table_columns - 1) { - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][2]); u8ch[u8len] = 0; printf("%s", u8ch); } else { - u8len = u32_char_to_u8(u8ch, - *table_inner_symbols[current_inner_symbol_set] - [0]); + /* clang-format off */ + u8len = u32_rune_to_u8(u8ch, *table_inner_symbols[ + current_inner_symbol_set][0]); + /* clang-format on */ u8ch[u8len] = 0; printf("%s", u8ch); column_start++; @@ -1009,7 +940,7 @@ do_column: } else { - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][1]); u8ch[u8len] = 0; printf("%s", u8ch); @@ -1026,7 +957,7 @@ empty_line: current_table_column = 0; current_rune_column = 0; column_start = 0; - u8len = u32_char_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); while (current_table_column + 1 < table_columns) @@ -1036,8 +967,10 @@ empty_line: printf("%c", alignment_char); current_rune_column++; } - u8len = u32_char_to_u8(u8ch, - *table_inner_symbols[current_inner_symbol_set][1]); + /* clang-format off */ + u8len = u32_rune_to_u8(u8ch, + *table_inner_symbols[current_inner_symbol_set][1]); + /* clang-format on */ u8ch[u8len] = 0; printf("%s", u8ch); column_start++; @@ -1053,7 +986,7 @@ empty_line: printf("%c", alignment_char); current_rune_column++; } - u8len = u32_char_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); pline = line; @@ -1066,7 +999,7 @@ not_first: column_start = 0; in_sgr = 0; - u8len = u32_char_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); @@ -1097,21 +1030,27 @@ do_line: } continue_line: - u8_char_to_u32(&uch, pline, &ch_len); + outbuf_len = poutbuf - outbuf; + ENSURE_SIZE(outbuf, tpoutbuf, outbuf_size, + (ssize_t)(poutbuf - outbuf + sizeof((u8*)ANSI_SGR_BOLD_OFF)), + (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) == '[') { in_sgr = 1; if (!no_ansi) - poutbuf += snprintf((char*)poutbuf, BUF_DEFAULT, "%c", - *pline); + { + *poutbuf++ = *pline; + *poutbuf = 0; + } pline++; colno++; } else if (uch == (u32)L'm') { if (!no_ansi || !in_sgr) - poutbuf += snprintf((char*)poutbuf, BUF_DEFAULT, "%c", - *pline); + *poutbuf++ = *pline; if (!in_sgr) outbufrunelen++; in_sgr = 0; @@ -1124,8 +1063,10 @@ continue_line: { pch_end = pline + ch_len; while (pline != pch_end) - poutbuf += snprintf((char*)poutbuf, BUF_DEFAULT, - "%c", *pline++); + { + *poutbuf++ = *pline++; + *poutbuf = 0; + } } else pline += ch_len; @@ -1137,7 +1078,7 @@ continue_line: size_t colwidth = current_column_width(); if (!no_ansi && lineno == 0) - snprintf((char*)poutbuf, BUF_DEFAULT, "%s", + snprintf((char*)poutbuf, sizeof ANSI_SGR_BOLD_OFF, "%s", ANSI_SGR_BOLD_OFF); print_aligned(outbuf, outbufrunelen, colwidth, @@ -1165,9 +1106,10 @@ continue_line: column_start += format_value; if (current_table_column != table_columns - 1) { - u8len = u32_char_to_u8(u8ch, - *table_inner_symbols[current_inner_symbol_set] - [1]); + /* clang-format off */ + u8len = u32_rune_to_u8(u8ch, *table_inner_symbols[ + current_inner_symbol_set][1]); + /* clang-format on */ u8ch[u8len] = 0; printf("%s", u8ch); current_rune_column++; @@ -1175,7 +1117,8 @@ continue_line: } if (!no_ansi && lineno == 0) - poutbuf += snprintf((char*)poutbuf, BUF_DEFAULT, "%s", + poutbuf += snprintf((char*)poutbuf, + sizeof ANSI_SGR_BOLD_ON, "%s", ANSI_SGR_BOLD_ON); pline += ch_len; @@ -1183,8 +1126,8 @@ continue_line: } else if (!ch_len) /* UTF-8 error */ { - snprintf((char*)poutbuf, BUF_DEFAULT, "%c", *pline); - poutbuf++; + *poutbuf++ = *pline; + *poutbuf = 0; outbufrunelen++; pline++; colno++; @@ -1193,9 +1136,8 @@ continue_line: { while ((current_rune_column + outbufrunelen) % tab_length != 0) { - snprintf((char*)poutbuf, BUF_DEFAULT, "%c", - alignment_char); - poutbuf++; + *poutbuf++ = alignment_char; + *poutbuf = 0; outbufrunelen++; } pline++; @@ -1205,15 +1147,16 @@ continue_line: { pch_end = pline + ch_len; while (pline != pch_end) - poutbuf += snprintf((char*)poutbuf, BUF_DEFAULT, "%c", - *pline++); + { + *poutbuf++ = *pline++; + *poutbuf = 0; + } outbufrunelen++; colno += ch_len; } goto do_line; -done_line:; - +done_line: if (!no_ansi && lineno == 0) printf("%s", ANSI_SGR_BOLD_OFF); @@ -1223,7 +1166,7 @@ done_line:; : format_value; if (!no_ansi && lineno == 0) - snprintf((char*)poutbuf, BUF_DEFAULT, "%s", + snprintf((char*)poutbuf, sizeof ANSI_SGR_BOLD_OFF, "%s", ANSI_SGR_BOLD_OFF); print_aligned(outbuf, outbufrunelen, colwidth, @@ -1243,7 +1186,7 @@ done_line:; printf("%c", alignment_char); current_rune_column++; } - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_inner_symbols[current_inner_symbol_set][1]); u8ch[u8len] = 0; printf("%s", u8ch); @@ -1261,7 +1204,7 @@ done_line:; current_rune_column++; } - u8len = u32_char_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); @@ -1289,7 +1232,7 @@ do_bottom_border: if (current_rune_column == 0) { - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][6]); column_start++; } @@ -1297,13 +1240,13 @@ do_bottom_border: { if (current_table_column == table_columns - 1) { - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][8]); current_table_column++; } else { - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_inner_symbols[current_inner_symbol_set] [2]); column_start++; @@ -1316,7 +1259,7 @@ do_bottom_border: } } else - u8len = u32_char_to_u8(u8ch, + u8len = u32_rune_to_u8(u8ch, *table_symbols[current_symbol_set][7]); u8ch[u8len] = 0;