table

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

чување 0d0724789508e45b2fbf6b206e7ae6d3d28b3cb6
родитељ 2be3b25e469dff8e2007e07b42b9cb202d090613
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Tue, 23 Jul 2024 20:24:42 +0200

Add table-specific exit status codes; table.c: Sort functions, add 
more assert calls

Diffstat:
Mdefs.h | 35++++++++++++++++++++++-------------
Mtable.1.in | 15++++++++++++++-
Mtable.c | 593++++++++++++++++++++++++++++++++++++++++---------------------------------------
измењених датотека: 3, додавања: 339(+), брисања: 304(-)

diff --git a/defs.h b/defs.h @@ -84,21 +84,16 @@ *pformat++ = num; \ } while (0) -typedef enum { - CMD_NONE, - CMD_ALIGN, - CMD_COLUMNS, - CMD_DELIMITER, - CMD_FORMAT, - CMD_QUOTES, - CMD_SYMBOLS, - CMD_VERSION, - CMD_FULL_VERSION -} Command; +#define TABLE_ERR_CODE(num) (200 + (num)) -enum { Q_PROCESS, Q_IGNORE }; +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), +}; -typedef enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT } Alignment; +enum { Q_PROCESS, Q_IGNORE }; enum { TABLE_SYMBOLS_ASCII, @@ -122,6 +117,20 @@ enum { TABLE_INNER_EMPTY_DOUBLE, }; +typedef enum { + CMD_NONE, + CMD_ALIGN, + CMD_COLUMNS, + CMD_DELIMITER, + CMD_FORMAT, + CMD_QUOTES, + CMD_SYMBOLS, + CMD_VERSION, + CMD_FULL_VERSION +} Command; + +typedef enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT } Alignment; + static const u32* table_symbols[][9] = { [TABLE_SYMBOLS_ASCII] = { diff --git a/table.1.in b/table.1.in @@ -29,7 +29,7 @@ .Sh COMMAND ALIASES .Bl -column -offset 1m "123456" .It Nm tablep Ta is a synonym for Ql "table -p" . -.It Nm tsvtable Ta is a synonym for Ql "table -d \(dq$(printf '\et')\(dq -q ignore" . +.It Nm tsvtable Ta is a synonym for Ql table -d Qo $(printf '\et') Qc -q ignore . .El . .Sh DESCRIPTION @@ -232,6 +232,19 @@ This switch enforces the output of ANSI SGR codes, even in that case. . .Sh EXIT STATUS .Ex -std +.Pp +For errors caused by unsuccessful calls to libc functions setting +.Va errno , +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. +.It Li 202 +Command line argument or its parameter are not recognized. +.It Li 203 +A quote is present without its corresponding closing quote. +.El . .Sh EXAMPLES .Bl -bullet -width 0 diff --git a/table.c b/table.c @@ -2,8 +2,6 @@ * 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> @@ -12,6 +10,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <unistd.h> #include "defs.h" @@ -24,33 +23,33 @@ " See the file LICENSE for exact copyright and license " \ "details.") -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; -int current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; -size_t current_table_column = 0; -size_t current_rune_column = 0; -size_t table_columns = 0; -size_t rune_columns = 80; -size_t tab_length = 8; -u32 delimiter = L','; -int* format = NULL; -size_t format_size = 0; -int format_value = 0; -Alignment* align = NULL; -int align_header = 0; -size_t align_size = 0; -int border_mode = 0; -int no_ansi = 0; -int in_sgr = 0; -int msdos = 0; -int opause = 0; -int expand_tabs = 0; -int quotes = Q_PROCESS; -char alignment_char = ' '; +static int* format = NULL; +static Alignment* align = NULL; +static ssize_t line_size; +static ssize_t outbuf_size; +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 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 expand_tabs = 0; +static int format_value = 0; +static int in_sgr = 0; +static int msdos = 0; +static int no_ansi = 0; +static int opause = 0; +static int quotes = Q_PROCESS; +static char alignment_char = ' '; /* clang-format off */ static const int alignments[] = { @@ -90,36 +89,22 @@ static const struct { }; /* clang-format on */ -int -version(const int full) -{ - printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); - if (full) - puts(COPYRIGHT); - return 0; -} +static int skip_while_sgr(const u8* s, size_t* char_delta, int also_print, + int whole_string); +static void warning(const char* fmt, ...); -int -usage(void) +static size_t +current_column_width(void) { - printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n" - "\t%s [-a <alignment> | --align=<alignment>]" - " [-b | --border-mode] [-c <cols> | --columns=<cols>]" - " [-D | --align-dot] [-d <delim> | --delimiter=<delim>]" - " [-f <format> | --format=<format>] [-H | --align-header]" - " [-m | --msdos] [-n | --no-ansi] [-p | --pause]" - " [-q <behavior> | --quotes=<behavior>]" - " [-s <set> | --symbols=<set>] [-t | --expand-tabs]" - " [-y | --yes-ansi] [pathname]\n", - PROGRAMNAME, PROGRAMNAME); - return 0; + return format ? format[current_table_column] : format_value; } -int +static int error(const int code, const char* fmt, ...) { va_list args; char buf[line_max]; + assert(fmt != NULL); va_start(args, fmt); if (vsnprintf(buf, LEN(buf), fmt, args) >= (ssize_t)LEN(buf)) buf[LEN(buf) - 1] = 0; @@ -128,77 +113,166 @@ error(const int code, const char* fmt, ...) return code; } -void -warning(const char* fmt, ...) +/* Return number of columns based on the input line */ +static size_t +number_of_columns(const u8* input, u32 delimiter) { - va_list args; - char buf[line_max]; - va_start(args, fmt); - 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); + const u8* pinput = NULL; + u32 uch; + size_t ch_len = 0; + size_t result = 1; + + assert(input != NULL); + pinput = input; + while (pinput && *pinput) + { + 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) + result++; + pinput += ch_len; + } + return result; } -int -startswith(const char* s, const char* what) +static int +print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, + size_t* current_rune_column) { - const char* ps = s; + const u8* ps = s; + u32 uch; + size_t i; + size_t ch_len; + size_t colwidth = current_column_width(); + size_t output_runes = 0; + size_t skipped_runes = 0; + size_t output_chars = 0; + size_t skipped_chars = 0; - if (!ps || !what) - return 0; + assert((s != NULL) && (current_rune_column != NULL)); + if (align == ALIGN_CENTER) + for (i = 0; 2 * i + rune_length < to_size; i++) + { + printf("%c", alignment_char); + (*current_rune_column)++; + } + else if (align == ALIGN_RIGHT) + for (i = 0; i + rune_length < to_size; i++) + { + printf("%c", alignment_char); + (*current_rune_column)++; + } + if (align == ALIGN_LEFT) + { + output_runes = 0; + while (output_runes != MIN(rune_length, colwidth)) + { + skip_while_sgr(ps, &skipped_chars, 1, 0); + ps += skipped_chars; - while (*ps && *what) + if (!no_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); + + u8_rune_to_u32(&uch, ps, &ch_len); + output_chars = 0; + while (output_chars != ch_len) + { + printf("%c", (char)*ps++); + output_chars++; + } + output_runes++; + } + skip_while_sgr(ps, &skipped_chars, 1, 1); + } + else if (align == ALIGN_CENTER) { - if (*what && *ps != *what) - return 0; - ps++; - what++; + skipped_runes = 0; + while (2 * skipped_runes + to_size < rune_length) + { + skip_while_sgr(ps, &skipped_chars, 1, 0); + ps += skipped_chars; + + u8_rune_to_u32(&uch, ps, &ch_len); + ps += ch_len; + skipped_runes++; + } + + output_runes = 0; + while (output_runes != MIN(rune_length, colwidth)) + { + skip_while_sgr(ps, &skipped_chars, 1, 0); + ps += skipped_chars; + + if (!no_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); + + u8_rune_to_u32(&uch, ps, &ch_len); + output_chars = 0; + while (output_chars != ch_len) + { + printf("%c", (char)*ps++); + output_chars++; + } + output_runes++; + } + skip_while_sgr(ps, &skipped_chars, 1, 1); } + else if (align == ALIGN_RIGHT) + { + skipped_runes = 0; + while (skipped_runes + to_size < rune_length) + { + skip_while_sgr(ps, &skipped_chars, 1, 0); + ps += skipped_chars; - return 1; -} + u8_rune_to_u32(&uch, ps, &ch_len); + ps += ch_len; + skipped_runes++; + } + output_runes = 0; + while (output_runes != MIN(rune_length, colwidth)) + { + skip_while_sgr(ps, &skipped_chars, 1, 0); + ps += skipped_chars; -size_t -current_column_width(void) -{ - return format ? format[current_table_column] : format_value; -} + if (!no_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); -int -within_column(size_t column_start, size_t current_rune_column) -{ - return current_rune_column < column_start + current_column_width(); + u8_rune_to_u32(&uch, ps, &ch_len); + output_chars = 0; + while (output_chars != ch_len) + { + printf("%c", (char)*ps++); + output_chars++; + } + output_runes++; + } + skip_while_sgr(ps, &skipped_chars, 1, 1); + } + (*current_rune_column) += output_runes; + return 0; } -int -set_symbol_set(char* arg, int* current_symbol_set, int* current_inner_symbol_set) +static unsigned int +round_div(unsigned int a, unsigned int b) { - 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; + assert(b != 0); + return (a + (b / 2)) / b; } -int +static int set_align(const char* arg, Alignment** align, size_t* align_size) { - const char* parg = arg; Alignment* palign = NULL; + const char* parg = arg; + assert((arg != NULL) && (align != NULL) && (align_size != NULL)); *align_size = strlen(arg); CALLOC(*align, Alignment, *align_size); palign = *align; - while (*parg) { if (!strchr("lcr", *parg)) @@ -208,56 +282,56 @@ set_align(const char* arg, Alignment** align, size_t* align_size) *align_size = 0; return 1; } - assert((*parg == 'l') || (*parg == 'c') || (*parg == 'r')); - *palign++ = alignments[(int)*parg++]; } - return 0; } -int +static int set_columns(const char* arg, size_t* cols) { size_t c; + assert((arg != NULL) && (cols != NULL)); errno = 0; c = strtol(arg, NULL, 10); if (errno == EINVAL || errno == ERANGE) - return error(1, "Invalid numeric value: %s", arg); + { + perror(PROGRAMNAME ": strtol"); + return errno; + } else *cols = c; - return 0; + return ERR_NONE; } -int +static int set_delimiter(u8* arg, u32* delimiter) { size_t delimiter_len = 0; u32 uch; - + assert((arg != NULL) && (delimiter != NULL)); u8_rune_to_u32(&uch, arg, &delimiter_len); *delimiter = uch; - return 0; } -int +static int set_format(const char* arg, int** format, size_t* format_size) { - const char* parg = arg; int* pformat = NULL; + const char* parg = arg; char* token = NULL; - size_t token_size = BUF_DEFAULT; char* ptoken = NULL; + size_t token_size = BUF_DEFAULT; int num; + assert((arg != NULL) && (format != NULL) && (format_size != NULL)); CALLOC(token, char, token_size); ptoken = token; *format_size = FORMAT_DEFAULT; CALLOC(*format, int, *format_size); pformat = *format; - while (*parg) { if (!((*parg >= '0' && *parg <= '9') || *parg == ':')) @@ -310,15 +384,14 @@ set_format(const char* arg, int** format, size_t* format_size) } CHECKSET(*format, pformat, *format_size, num); } - free(token); - return 0; } -int +static int set_quotes_behavior(const char* arg, int* quotes) { + assert((arg != NULL) && (quotes != NULL)); if (!strcmp(arg, "process")) *quotes = Q_PROCESS; else if (!strcmp(arg, "ignore")) @@ -328,11 +401,31 @@ set_quotes_behavior(const char* arg, int* quotes) return 0; } -int +static int +set_symbol_set(char* arg, int* current_symbol_set, int* current_inner_symbol_set) +{ + assert((arg != NULL) && (current_symbol_set != NULL) + && (current_inner_symbol_set != NULL)); + 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; +} + +static int skip_while_sgr(const u8* s, size_t* char_delta, int also_print, int whole_string) { const u8* ps = s; - + assert((s != NULL) && (char_delta != NULL)); *char_delta = 0; while (*ps) { @@ -368,162 +461,70 @@ skip_while_sgr(const u8* s, size_t* char_delta, int also_print, int whole_string return 0; } -int -print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, - size_t* current_rune_column) +static int +startswith(const char* s, const char* what) { - size_t i; - const u8* ps = s; - u32 uch; - size_t ch_len; - size_t colwidth = current_column_width(); - size_t output_runes = 0; - size_t skipped_runes = 0; - size_t output_chars = 0; - size_t skipped_chars = 0; - - if (align == ALIGN_CENTER) - for (i = 0; 2 * i + rune_length < to_size; i++) - { - printf("%c", alignment_char); - (*current_rune_column)++; - } - else if (align == ALIGN_RIGHT) - for (i = 0; i + rune_length < to_size; i++) - { - printf("%c", alignment_char); - (*current_rune_column)++; - } - if (align == ALIGN_LEFT) - { - output_runes = 0; - while (output_runes != MIN(rune_length, colwidth)) - { - skip_while_sgr(ps, &skipped_chars, 1, 0); - ps += skipped_chars; - - if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); - - u8_rune_to_u32(&uch, ps, &ch_len); - output_chars = 0; - while (output_chars != ch_len) - { - printf("%c", (char)*ps++); - output_chars++; - } - output_runes++; - } - skip_while_sgr(ps, &skipped_chars, 1, 1); - } - else if (align == ALIGN_CENTER) - { - skipped_runes = 0; - while (2 * skipped_runes + to_size < rune_length) - { - skip_while_sgr(ps, &skipped_chars, 1, 0); - ps += skipped_chars; - - u8_rune_to_u32(&uch, ps, &ch_len); - ps += ch_len; - skipped_runes++; - } - - output_runes = 0; - while (output_runes != MIN(rune_length, colwidth)) - { - skip_while_sgr(ps, &skipped_chars, 1, 0); - ps += skipped_chars; - - if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); - - u8_rune_to_u32(&uch, ps, &ch_len); - output_chars = 0; - while (output_chars != ch_len) - { - printf("%c", (char)*ps++); - output_chars++; - } - output_runes++; - } - skip_while_sgr(ps, &skipped_chars, 1, 1); - } - else if (align == ALIGN_RIGHT) + const char* ps = s; + assert((s != NULL) && (what != NULL)); + while (*ps && *what) { - skipped_runes = 0; - while (skipped_runes + to_size < rune_length) - { - skip_while_sgr(ps, &skipped_chars, 1, 0); - ps += skipped_chars; - - u8_rune_to_u32(&uch, ps, &ch_len); - ps += ch_len; - skipped_runes++; - } - output_runes = 0; - while (output_runes != MIN(rune_length, colwidth)) - { - skip_while_sgr(ps, &skipped_chars, 1, 0); - ps += skipped_chars; - - if (!no_ansi && lineno == 0) - printf("%s", ANSI_SGR_BOLD_ON); - - u8_rune_to_u32(&uch, ps, &ch_len); - output_chars = 0; - while (output_chars != ch_len) - { - printf("%c", (char)*ps++); - output_chars++; - } - output_runes++; - } - skip_while_sgr(ps, &skipped_chars, 1, 1); + if (*what && *ps != *what) + return 0; + ps++; + what++; } - (*current_rune_column) += output_runes; - return 0; + return 1; } -/* Return number of columns based on the input line */ -size_t -number_of_columns(const u8* input, u32 delimiter) +static int +usage(void) { - u32 uch; - size_t ch_len = 0; - size_t result = 1; - const u8* pinput = NULL; - - pinput = input; - - if (!input) - return result; + printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n" + "\t%s [-a <alignment> | --align=<alignment>]" + " [-b | --border-mode] [-c <cols> | --columns=<cols>]" + " [-D | --align-dot] [-d <delim> | --delimiter=<delim>]" + " [-f <format> | --format=<format>] [-H | --align-header]" + " [-m | --msdos] [-n | --no-ansi] [-p | --pause]" + " [-q <behavior> | --quotes=<behavior>]" + " [-s <set> | --symbols=<set>] [-t | --expand-tabs]" + " [-y | --yes-ansi] [pathname]\n", + PROGRAMNAME, PROGRAMNAME); + return ERR_NONE; +} - while (pinput && *pinput) - { - 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) - result++; - pinput += ch_len; - } +static int +version(const int full) +{ + printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); + if (full) + puts(COPYRIGHT); + return ERR_NONE; +} - return result; +static void +warning(const char* fmt, ...) +{ + va_list args; + char buf[line_max]; + assert(fmt != NULL); + va_start(args, fmt); + 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); } -unsigned int -round_div(unsigned int a, unsigned int b) +static int +within_column(size_t column_start, size_t current_rune_column) { - return (a + (b / 2)) / b; + return current_rune_column < column_start + current_column_width(); } int main(int argc, char** argv) { FILE* input = NULL; + struct stat st; Command cmd = CMD_NONE; u32 uch; u8* line = NULL; @@ -577,18 +578,18 @@ main(int argc, char** argv) else if (errno) { perror(PROGRAMNAME ": isatty"); - exit(1); + exit(errno); } } - errno = 0; + errno = 0; line_max = sysconf(_SC_LINE_MAX); if (line_max == -1) { if (errno) { perror(PROGRAMNAME ": sysconf"); - exit(1); + exit(errno); } line_max = _POSIX2_LINE_MAX; } @@ -617,9 +618,10 @@ do_arg: { arg += strlen("align="); if (set_align(arg, &align, &align_size)) - exit(error(1, + exit(error(ERR_BAD_ALIGN, "Invalid alignment " - "specification")); + "specification: '%s'", + arg)); } else if (!strcmp(arg, "border-mode")) border_mode = 1; @@ -627,7 +629,7 @@ do_arg: { arg += strlen("columns="); if (set_columns(arg, &rune_columns)) - exit(error(EINVAL, + exit(error(ERR_BAD_CL_ARG, "Invalid argument: " "'%s'", arg)); @@ -636,7 +638,7 @@ do_arg: { arg += strlen("delimiter="); if (set_delimiter((u8*)arg, &delimiter)) - exit(error(EINVAL, + exit(error(ERR_BAD_CL_ARG, "Invalid argument: " "'%s'", arg)); @@ -658,7 +660,7 @@ do_arg: { arg += strlen("quotes="); if (set_quotes_behavior(arg, &quotes)) - exit(error(EINVAL, + exit(error(ERR_BAD_CL_ARG, "Invalid argument: " "'%s'", arg)); @@ -668,7 +670,7 @@ do_arg: arg += strlen("symbols="); if (set_symbol_set(arg, &current_symbol_set, &current_inner_symbol_set)) - exit(error(EINVAL, + exit(error(ERR_BAD_CL_ARG, "Invalid argument: " "'%s'", arg)); @@ -679,9 +681,9 @@ do_arg: return usage(); else { - error(EINVAL, "Invalid argument: --%s", arg); - usage(); - exit(EINVAL); + (void)usage(); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: --%s", arg)); } } else @@ -710,7 +712,8 @@ do_arg: align_header = 1; break; case 'h': - return usage(); + usage(); + exit(ERR_NONE); break; case 'm': msdos = 1; @@ -740,9 +743,9 @@ do_arg: no_ansi = 0; break; default: - error(EINVAL, "Invalid argument: -%c", c); - usage(); - exit(EINVAL); + (void)usage(); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: -%c", c)); } /* Gobble up the rest of the arg ("-d:" etc) */ if (*arg) @@ -755,39 +758,40 @@ do_arg: if (cmd == CMD_ALIGN) { if (set_align(arg, &align, &align_size)) - exit(error(EINVAL, "Invalid argument: '%s'", + exit(error(ERR_BAD_ALIGN, + "Invalid alignment specification: '%s'", arg)); } else if (cmd == CMD_COLUMNS) { if (set_columns(arg, &rune_columns)) - exit(error(EINVAL, "Invalid argument: '%s'", - arg)); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: '%s'", arg)); } else if (cmd == CMD_DELIMITER) { if (set_delimiter((u8*)arg, &delimiter)) - exit(error(EINVAL, "Invalid argument: '%s'", - arg)); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: '%s'", arg)); } else if (cmd == CMD_FORMAT) { if (set_format(arg, &format, &format_size)) - exit(error(EINVAL, "Invalid argument: '%s'", - arg)); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: '%s'", arg)); } else if (cmd == CMD_QUOTES) { if (set_quotes_behavior(arg, &quotes)) - exit(error(EINVAL, "Invalid argument: '%s'", - arg)); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: '%s'", arg)); } else if (cmd == CMD_SYMBOLS) { if (set_symbol_set(arg, &current_symbol_set, &current_inner_symbol_set)) - exit(error(EINVAL, "Invalid argument: '%s'", - arg)); + exit(error(ERR_BAD_CL_ARG, + "Invalid argument: '%s'", arg)); } else if (filename) warning("Additional argument ignored: '%s'", arg); @@ -809,17 +813,29 @@ done_arg: if (unveil(filename, "r") < 0) { perror(PROGRAMNAME ": unveil"); - exit(1); + exit(errno); } if (unveil(NULL, NULL) < 0) { perror(PROGRAMNAME ": unveil"); - exit(1); + exit(errno); } #endif + errno = 0; + if (stat(filename, &st) < 0) + { + perror(PROGRAMNAME ": stat"); + exit(errno); + } + if (S_ISDIR(st.st_mode)) + exit(error(EISDIR, "Is a directory: '%s'", filename)); + errno = 0; input = fopen(filename, "r"); if (!input) - exit(error(ENOENT, "File not found: %s", filename)); + { + perror(PROGRAMNAME ": fopen"); + exit(error(errno, "Cannot open '%s'", filename)); + } } else input = stdin; @@ -1009,7 +1025,7 @@ do_line: if (in_quote) { printf("\n"); - exit(error(EINVAL, "Unmatched quote")); + exit(error(ERR_UNMATCHED_QUOTE, "Unmatched quote")); } else goto done_line; @@ -1210,7 +1226,6 @@ done_line: output_lines++; lineno++; - pline = line; goto do_input; @@ -1274,7 +1289,6 @@ done_bottom_border: skip_bottom_border: free(format); free(align); - if (opause) { char buf[line_max]; @@ -1284,6 +1298,5 @@ skip_bottom_border: fgets(buf, line_max, stdin); } fclose(input); - return 0; }