table

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

table.c (30871B)


      1 /* This program is licensed under the terms of GNU GPL v3 or (at your option)
      2  * any later version. Copyright (C) 2020-2026  Страхиња Радић.
      3  * See the file LICENSE for exact copyright and license details. */
      4 
      5 #include <assert.h>
      6 #include <errno.h>
      7 #include <limits.h>
      8 #include <stdarg.h>
      9 #include <stddef.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 #include <sys/stat.h>
     14 #include <unistd.h>
     15 
     16 #include "defs.h"
     17 #include "strtonum.h"
     18 #include "version.h"
     19 
     20 #define COPYRIGHT                                                            \
     21 	("  This program is licensed under the terms of GNU GPL v3"          \
     22 	 " or (at your option)\n"                                            \
     23 	 "  any later version. Copyright (C) 2020-2026  Strahinya Radich.\n" \
     24 	 "  See the file LICENSE for exact copyright and license "           \
     25 	 "details.")
     26 
     27 static int* format		   = NULL;
     28 static Alignment* align		   = NULL;
     29 static size_t align_size	   = 0;
     30 static size_t colno		   = 0;
     31 static size_t current_rune_column  = 0;
     32 static size_t current_table_column = 0;
     33 static size_t format_size	   = 0;
     34 static size_t lineno		   = 0;
     35 static size_t tab_length	   = 8;
     36 static size_t table_columns	   = 0;
     37 static ssize_t line_size;
     38 static ssize_t outbuf_size;
     39 static ssize_t rune_columns = 80;
     40 static u32 delimiter	    = U32(',');
     41 static long line_max;
     42 static int align_header		    = 0;
     43 static int border_mode		    = 0;
     44 static int current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE;
     45 static int current_symbol_set	    = TABLE_SYMBOLS_DOUBLE;
     46 static int error_code		    = ERR_NONE;
     47 static int expand_tabs		    = 0;
     48 static int format_value		    = 0;
     49 static int in_sgr		    = 0;
     50 static int msdos		    = 0;
     51 static int no_ansi		    = 0;
     52 static int opause		    = 0;
     53 static int quotes		    = Q_PROCESS;
     54 static char alignment_char	    = ' ';
     55 
     56 /* clang-format off */
     57 static const int alignments[] = {
     58 	['l'] = ALIGN_LEFT,
     59 	['c'] = ALIGN_CENTER,
     60 	['r'] = ALIGN_RIGHT
     61 };
     62 static const struct {
     63 	int border;
     64 	int inner;
     65 } symbol_sets[] = {
     66 	[HASH('a','a')] = {	.border = TABLE_SYMBOLS_ASCII,
     67 				.inner  = TABLE_INNER_ASCII_ASCII },
     68 	[HASH('a','e')] = {	.border = TABLE_SYMBOLS_ASCII,
     69 				.inner  = TABLE_INNER_ASCII_EMPTY },
     70 	[HASH('d','d')] = {	.border = TABLE_SYMBOLS_DOUBLE,
     71 				.inner  = TABLE_INNER_DOUBLE_DOUBLE },
     72 	[HASH('d','e')] = {	.border = TABLE_SYMBOLS_DOUBLE,
     73 				.inner  = TABLE_INNER_DOUBLE_EMPTY },
     74 	[HASH('d','s')] = {	.border = TABLE_SYMBOLS_DOUBLE,
     75 				.inner  = TABLE_INNER_DOUBLE_SINGLE },
     76 	[HASH('e','a')] = {	.border = TABLE_SYMBOLS_EMPTY,
     77 				.inner  = TABLE_INNER_EMPTY_ASCII },
     78 	[HASH('e','d')] = {	.border = TABLE_SYMBOLS_EMPTY,
     79 				.inner  = TABLE_INNER_EMPTY_DOUBLE },
     80 	[HASH('e','e')] = {	.border = TABLE_SYMBOLS_EMPTY,
     81 				.inner  = TABLE_INNER_EMPTY_EMPTY },
     82 	[HASH('e','s')] = {	.border = TABLE_SYMBOLS_EMPTY,
     83 				.inner  = TABLE_INNER_EMPTY_SINGLE },
     84 	[HASH('s','d')] = {	.border = TABLE_SYMBOLS_SINGLE,
     85 				.inner  = TABLE_INNER_SINGLE_DOUBLE },
     86 	[HASH('s','e')] = {	.border = TABLE_SYMBOLS_SINGLE,
     87 				.inner  = TABLE_INNER_SINGLE_EMPTY },
     88 	[HASH('s','s')] = {	.border = TABLE_SYMBOLS_SINGLE,
     89 				.inner  = TABLE_INNER_SINGLE_SINGLE }
     90 };
     91 const char* errors[] = {
     92 	[ERR_NONE]		= "",
     93 	[ERR_NO_CL_ARG_PARAM]	= "Option requires an argument: %s",
     94 	[ERR_BAD_CL_ARG]	= "Invalid argument: %s",
     95 	[ERR_BAD_ALIGN]		= "Invalid alignment specification: %s",
     96 	[ERR_BAD_NUMBER]	= "Invalid numeric argument: %s",
     97 	[ERR_BAD_FORMAT_SPEC]	= "Invalid format specification: %s",
     98 	[ERR_BAD_QUOTES_ARG]	= "Invalid quotes specification: %s",
     99 	[ERR_BAD_SET_SPEC]	= "Invalid set specification: %s",
    100 	[ERR_ISDIR]		= "'%s' is a directory",
    101 	[ERR_NOTREG_FILE]	= "'%s' is not a regular file",
    102 	[ERR_UNMATCHED_QUOTE]	= "Unmatched quote",
    103 	[ERR_STREAM_ERROR]	= "Input stream error",
    104 	[ERR_BINARY_INPUT]	= "Input is not text",
    105 };
    106 /* clang-format on */
    107 
    108 static int skip_while_sgr(const u8* s, size_t* char_delta, int also_print,
    109 	int whole_string);
    110 static void warning(const char* fmt, ...);
    111 
    112 static size_t
    113 current_column_width(void)
    114 {
    115 	return format ? format[current_table_column] : format_value;
    116 }
    117 
    118 static int
    119 error(const int code, const char* fmt, ...)
    120 {
    121 	va_list args;
    122 	char* buf = NULL;
    123 	assert(fmt != NULL);
    124 	MALLOC(buf, line_max);
    125 	va_start(args, fmt);
    126 	if (vsnprintf(buf, line_max, fmt, args) >= line_max)
    127 		buf[line_max - 1] = 0;
    128 	va_end(args);
    129 	fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf);
    130 	free(buf);
    131 	return code;
    132 }
    133 
    134 /* Return number of columns based on the input line */
    135 static size_t
    136 number_of_columns(const u8* input, u32 delimiter)
    137 {
    138 	const u8* pinput = NULL;
    139 	u32 uch;
    140 	size_t ch_len = 0;
    141 	size_t result = 1;
    142 
    143 	assert(input != NULL);
    144 	pinput = input;
    145 	while (*pinput)
    146 	{
    147 		if (u8_rune_to_u32(&uch, pinput, &ch_len))
    148 		{
    149 			warning("Malformed UTF-8 at position %td: "
    150 				"from=%02X",
    151 				pinput - input, *pinput);
    152 		}
    153 		if (uch == delimiter)
    154 			result++;
    155 		pinput += ch_len;
    156 	}
    157 	return result;
    158 }
    159 
    160 static int
    161 print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align,
    162 	size_t* current_rune_column)
    163 {
    164 	const u8* ps = s;
    165 	u32 uch;
    166 	size_t i;
    167 	size_t ch_len;
    168 	size_t colwidth	     = current_column_width();
    169 	size_t output_runes  = 0;
    170 	size_t skipped_runes = 0;
    171 	size_t output_chars  = 0;
    172 	size_t skipped_chars = 0;
    173 
    174 	assert((s != NULL) && (current_rune_column != NULL));
    175 	if (align == ALIGN_CENTER)
    176 		for (i = 0; 2 * i + rune_length < to_size; i++)
    177 		{
    178 			fputc(alignment_char, stdout);
    179 			(*current_rune_column)++;
    180 		}
    181 	else if (align == ALIGN_RIGHT)
    182 		for (i = 0; i + rune_length < to_size; i++)
    183 		{
    184 			fputc(alignment_char, stdout);
    185 			(*current_rune_column)++;
    186 		}
    187 	if (align == ALIGN_LEFT)
    188 	{
    189 		output_runes = 0;
    190 		while (output_runes != MIN(rune_length, colwidth))
    191 		{
    192 			skip_while_sgr(ps, &skipped_chars, 1, 0);
    193 			ps += skipped_chars;
    194 
    195 			if (!no_ansi && lineno == 0)
    196 				fputs(ANSI_SGR_BOLD_ON, stdout);
    197 
    198 			if (u8_rune_to_u32(&uch, ps, &ch_len))
    199 			{
    200 				warning("Malformed UTF-8 at position %td: "
    201 					"from=%02X",
    202 					ps - s, *ps);
    203 			}
    204 
    205 			output_chars = 0;
    206 			while (output_chars != ch_len)
    207 			{
    208 				fputc((char)*ps++, stdout);
    209 				output_chars++;
    210 			}
    211 			output_runes++;
    212 		}
    213 		skip_while_sgr(ps, &skipped_chars, 1, 1);
    214 	}
    215 	else if (align == ALIGN_CENTER)
    216 	{
    217 		skipped_runes = 0;
    218 		while (2 * skipped_runes + to_size < rune_length)
    219 		{
    220 			skip_while_sgr(ps, &skipped_chars, 1, 0);
    221 			ps += skipped_chars;
    222 
    223 			if (u8_rune_to_u32(&uch, ps, &ch_len))
    224 			{
    225 				warning("Malformed UTF-8 at position %td: "
    226 					"from=%02X",
    227 					ps - s, *ps);
    228 			}
    229 			ps += ch_len;
    230 			skipped_runes++;
    231 		}
    232 
    233 		output_runes = 0;
    234 		while (output_runes != MIN(rune_length, colwidth))
    235 		{
    236 			skip_while_sgr(ps, &skipped_chars, 1, 0);
    237 			ps += skipped_chars;
    238 
    239 			if (!no_ansi && lineno == 0)
    240 				fputs(ANSI_SGR_BOLD_ON, stdout);
    241 
    242 			if (u8_rune_to_u32(&uch, ps, &ch_len))
    243 			{
    244 				warning("Malformed UTF-8 at position %td: "
    245 					"from=%02X",
    246 					ps - s, *ps);
    247 			}
    248 			output_chars = 0;
    249 			while (output_chars != ch_len)
    250 			{
    251 				fputc((char)*ps++, stdout);
    252 				output_chars++;
    253 			}
    254 			output_runes++;
    255 		}
    256 		skip_while_sgr(ps, &skipped_chars, 1, 1);
    257 	}
    258 	else if (align == ALIGN_RIGHT)
    259 	{
    260 		skipped_runes = 0;
    261 		while (skipped_runes + to_size < rune_length)
    262 		{
    263 			skip_while_sgr(ps, &skipped_chars, 1, 0);
    264 			ps += skipped_chars;
    265 
    266 			if (u8_rune_to_u32(&uch, ps, &ch_len))
    267 			{
    268 				warning("Malformed UTF-8 at position %td: "
    269 					"from=%02X",
    270 					ps - s, *ps);
    271 			}
    272 			ps += ch_len;
    273 			skipped_runes++;
    274 		}
    275 		output_runes = 0;
    276 		while (output_runes != MIN(rune_length, colwidth))
    277 		{
    278 			skip_while_sgr(ps, &skipped_chars, 1, 0);
    279 			ps += skipped_chars;
    280 
    281 			if (!no_ansi && lineno == 0)
    282 				fputs(ANSI_SGR_BOLD_ON, stdout);
    283 
    284 			if (u8_rune_to_u32(&uch, ps, &ch_len))
    285 			{
    286 				warning("Malformed UTF-8 at position %td: "
    287 					"from=%02X",
    288 					ps - s, *ps);
    289 			}
    290 			output_chars = 0;
    291 			while (output_chars != ch_len)
    292 			{
    293 				fputc((char)*ps++, stdout);
    294 				output_chars++;
    295 			}
    296 			output_runes++;
    297 		}
    298 		skip_while_sgr(ps, &skipped_chars, 1, 1);
    299 	}
    300 	(*current_rune_column) += output_runes;
    301 	return 0;
    302 }
    303 
    304 static unsigned int
    305 round_div(unsigned int a, unsigned int b)
    306 {
    307 	assert(b != 0);
    308 	return (a + (b / 2)) / b;
    309 }
    310 
    311 static int
    312 set_align(const char* arg, Alignment** align, size_t* align_size)
    313 {
    314 	Alignment* palign = NULL;
    315 	const char* parg  = arg;
    316 
    317 	assert((arg != NULL) && (align != NULL) && (align_size != NULL));
    318 	*align_size = strlen(arg);
    319 	CALLOC(*align, Alignment, *align_size);
    320 	palign = *align;
    321 	while (*parg)
    322 	{
    323 		if (!strchr("lcr", *parg))
    324 		{
    325 			free(*align);
    326 			*align	    = NULL;
    327 			*align_size = 0;
    328 			error_code  = ERR_BAD_ALIGN;
    329 			return 1;
    330 		}
    331 		assert((*parg == 'l') || (*parg == 'c') || (*parg == 'r'));
    332 		*palign++ = alignments[(int)*parg++];
    333 	}
    334 	return 0;
    335 }
    336 
    337 static int
    338 set_columns(const char* arg, ssize_t* cols)
    339 {
    340 	ssize_t c;
    341 	int errornum;
    342 	assert((arg != NULL) && (cols != NULL));
    343 	errno = 0;
    344 	if (!*arg)
    345 	{
    346 		error_code = ERR_NO_CL_ARG_PARAM;
    347 		return 1;
    348 	}
    349 	c = strtonumber(arg, -((unsigned long long)SSIZE_MAX + 1),
    350 		(long long)SSIZE_MAX, &errornum);
    351 	if (stn_handle_error(arg, -((unsigned long long)SSIZE_MAX + 1),
    352 		    (long long)SSIZE_MAX, errornum))
    353 	{
    354 		error_code = ERR_BAD_NUMBER;
    355 		return 1;
    356 	}
    357 	else
    358 		*cols = c;
    359 	return 0;
    360 }
    361 
    362 static int
    363 set_delimiter(const char* arg, u32* delimiter)
    364 {
    365 	size_t delimiter_len = 0;
    366 	u32 uch;
    367 	assert((arg != NULL) && (delimiter != NULL));
    368 	if (!u8_rune_to_u32(&uch, (u8*)arg, &delimiter_len))
    369 		*delimiter = uch;
    370 	return 0;
    371 }
    372 
    373 static int
    374 set_format(const char* arg, int** format, size_t* format_size)
    375 {
    376 	int* pformat	  = NULL;
    377 	const char* parg  = arg;
    378 	char* token	  = NULL;
    379 	char* ptoken	  = NULL;
    380 	size_t token_size = BUF_DEFAULT;
    381 	int num, errornum;
    382 
    383 	assert((arg != NULL) && (format != NULL) && (format_size != NULL));
    384 	CALLOC(token, char, token_size);
    385 	ptoken	     = token;
    386 	*format_size = FORMAT_DEFAULT;
    387 	CALLOC(*format, int, *format_size);
    388 	pformat = *format;
    389 	while (*parg)
    390 	{
    391 		if (!((*parg >= '0' && *parg <= '9') || *parg == ':'))
    392 		{
    393 			free(token);
    394 			free(*format);
    395 			*format	   = NULL;
    396 			error_code = ERR_BAD_FORMAT_SPEC;
    397 			return 1;
    398 		}
    399 		if (*parg == ':')
    400 		{
    401 			if (parg == arg || !*(parg + 1))
    402 			{
    403 				free(token);
    404 				free(*format);
    405 				*format	   = NULL;
    406 				error_code = ERR_BAD_FORMAT_SPEC;
    407 				return 2;
    408 			}
    409 			*ptoken = 0;
    410 			errno	= 0;
    411 			num = strtonumber(token, INT_MIN, INT_MAX, &errornum);
    412 			if (stn_handle_error(token, INT_MIN, INT_MAX, errornum))
    413 			{
    414 				free(token);
    415 				free(*format);
    416 				*format	   = NULL;
    417 				error_code = ERR_BAD_NUMBER;
    418 				return 3;
    419 			}
    420 			CHECKSET(*format, pformat, *format_size, num);
    421 			*token = 0;
    422 			ptoken = token;
    423 			parg++;
    424 		}
    425 		else
    426 			CHECKCOPY(token, ptoken, token_size, parg);
    427 	}
    428 	if (*token)
    429 	{
    430 		*ptoken = 0;
    431 		errno	= 0;
    432 		num	= strtonumber(token, INT_MIN, INT_MAX, &errornum);
    433 		if (stn_handle_error(token, INT_MIN, INT_MAX, errornum))
    434 		{
    435 			free(token);
    436 			free(*format);
    437 			*format	   = NULL;
    438 			error_code = ERR_BAD_NUMBER;
    439 			return 3;
    440 		}
    441 		CHECKSET(*format, pformat, *format_size, num);
    442 	}
    443 	free(token);
    444 	return 0;
    445 }
    446 
    447 static int
    448 set_quotes_behavior(const char* arg, int* quotes)
    449 {
    450 	assert((arg != NULL) && (quotes != NULL));
    451 	if (!strcmp(arg, "process"))
    452 		*quotes = Q_PROCESS;
    453 	else if (!strcmp(arg, "ignore"))
    454 		*quotes = Q_IGNORE;
    455 	else
    456 	{
    457 		error_code = ERR_BAD_QUOTES_ARG;
    458 		return 1;
    459 	}
    460 	return 0;
    461 }
    462 
    463 static int
    464 set_symbol_set(char* arg, int* current_symbol_set, int* current_inner_symbol_set)
    465 {
    466 	assert((arg != NULL) && (current_symbol_set != NULL)
    467 		&& (current_inner_symbol_set != NULL));
    468 	if (!arg[0] || !arg[1] || arg[2] || !strchr("ades", arg[0])
    469 		|| !strchr("ades", arg[1]))
    470 	{
    471 		error_code = ERR_BAD_SET_SPEC;
    472 		return 1;
    473 	}
    474 	assert((arg[0] == 'a') || (arg[0] == 'd') || (arg[0] == 'e')
    475 		|| (arg[0] == 's') || (arg[1] == 'a') || (arg[1] == 'd')
    476 		|| (arg[1] == 'e') || (arg[1] == 's'));
    477 	if (!strcmp(arg, "ad") || !strcmp(arg, "as") || !strcmp(arg, "ad")
    478 		|| !strcmp(arg, "as") || !strcmp(arg, "da")
    479 		|| !strcmp(arg, "sa"))
    480 	{
    481 		error_code = ERR_BAD_SET_SPEC;
    482 		return 1;
    483 	}
    484 	*current_symbol_set	  = symbol_sets[HASH(arg[0], arg[1])].border;
    485 	*current_inner_symbol_set = symbol_sets[HASH(arg[0], arg[1])].inner;
    486 	return 0;
    487 }
    488 
    489 static int
    490 skip_while_sgr(const u8* s, size_t* char_delta, int also_print, int whole_string)
    491 {
    492 	const u8* ps = s;
    493 	assert((s != NULL) && (char_delta != NULL));
    494 	*char_delta = 0;
    495 	while (*ps)
    496 	{
    497 		if (*ps != '\033' || *(ps + 1) != '[')
    498 		{
    499 			if (whole_string)
    500 			{
    501 				ps++;
    502 				continue;
    503 			}
    504 			else
    505 				return 0;
    506 		}
    507 
    508 		if (also_print)
    509 		{
    510 			fputc((char)*ps++, stdout);
    511 			fputc((char)*ps++, stdout);
    512 		}
    513 		else
    514 			ps += 2;
    515 		*char_delta += 2;
    516 
    517 		while (*ps && *(ps - 1) != 'm')
    518 		{
    519 			if (also_print)
    520 				fputc((char)*ps++, stdout);
    521 			else
    522 				ps++;
    523 			(*char_delta)++;
    524 		}
    525 	}
    526 	return 0;
    527 }
    528 
    529 static int
    530 startswith(const char* s, const char* what)
    531 {
    532 	assert((s != NULL) && (what != NULL));
    533 	while (*s && *s == *what)
    534 	{
    535 		if (*s != *what)
    536 			return 0;
    537 		s++;
    538 		what++;
    539 	}
    540 	return *what == 0;
    541 }
    542 
    543 static int
    544 usage(void)
    545 {
    546 	printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n"
    547 	       "\t%s [-a <alignment> | --align=<alignment>]"
    548 	       " [-b | --border-mode] [-c <cols> | --columns=<cols>]"
    549 	       " [-D | --align-dot] [-d <delim> | --delimiter=<delim>]"
    550 	       " [-f <format> | --format=<format>] [-H | --align-header]"
    551 	       " [-m | --msdos] [-n | --no-ansi] [-p | --pause]"
    552 	       " [-q <behavior> | --quotes=<behavior>]"
    553 	       " [-s <set> | --symbols=<set>] [-t | --expand-tabs]"
    554 	       " [-y | --yes-ansi] [pathname]\n",
    555 		PROGRAMNAME, PROGRAMNAME);
    556 	return 0;
    557 }
    558 
    559 static int
    560 version(const int full)
    561 {
    562 	printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
    563 	if (full)
    564 		puts(COPYRIGHT);
    565 	return 0;
    566 }
    567 
    568 static void
    569 warning(const char* fmt, ...)
    570 {
    571 	va_list args;
    572 	char* buf = NULL;
    573 	assert(fmt != NULL);
    574 	MALLOC(buf, line_max);
    575 	va_start(args, fmt);
    576 	if (vsnprintf(buf, line_max, fmt, args) >= line_max)
    577 		buf[line_max - 1] = 0;
    578 	va_end(args);
    579 	fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf);
    580 	free(buf);
    581 }
    582 
    583 static int
    584 within_column(size_t column_start, size_t current_rune_column)
    585 {
    586 	return current_rune_column < column_start + current_column_width();
    587 }
    588 
    589 int
    590 main(int argc, char** argv)
    591 {
    592 	FILE* input = NULL;
    593 	struct stat st;
    594 	Command cmd = CMD_NONE;
    595 	u32 uch;
    596 	u8* line     = NULL;
    597 	u8* pline    = NULL;
    598 	u8* outbuf   = NULL;
    599 	u8* poutbuf  = NULL;
    600 	u8* tpoutbuf = NULL;
    601 	u8* pch_end;
    602 	u8 u8ch[7];
    603 	char* arg;
    604 	char* filename = NULL;
    605 	char* sep      = NULL;
    606 	char* progname = NULL;
    607 	char* eol      = NULL;
    608 	char* nulbyte  = NULL;
    609 	ptrdiff_t outbuf_len;
    610 	size_t outbufrunelen = 0;
    611 	size_t ch_len	     = 0;
    612 	size_t output_lines  = 0;
    613 	size_t column_start  = 0;
    614 	int in_quote	     = 0;
    615 	int possible_nulbyte = 0;
    616 	int u8len	     = 0;
    617 
    618 	UNUSED(argc);
    619 
    620 #ifdef __OpenBSD__
    621 	if (pledge("stdio rpath unveil", NULL) < 0)
    622 	{
    623 		perror(PROGRAMNAME ": pledge");
    624 		exit(errno);
    625 	}
    626 #endif
    627 
    628 	sep	 = strrchr(argv[0], '/');
    629 	progname = argv[0];
    630 	if (sep)
    631 		progname = sep + 1;
    632 
    633 	if (!strcmp(progname, "tsvtable"))
    634 	{
    635 		delimiter = TAB;
    636 		quotes	  = Q_IGNORE;
    637 	}
    638 	else if (!strcmp(progname, "tablep"))
    639 		opause = 1;
    640 
    641 	errno = 0;
    642 	if (!isatty(fileno(stdout)))
    643 	{
    644 		if (errno == ENOTTY)
    645 			no_ansi = 1;
    646 		else if (errno)
    647 		{
    648 			perror(PROGRAMNAME ": isatty");
    649 			exit(errno);
    650 		}
    651 	}
    652 
    653 	errno	 = 0;
    654 	line_max = sysconf(_SC_LINE_MAX);
    655 	if (line_max == -1)
    656 	{
    657 		if (errno)
    658 		{
    659 			perror(PROGRAMNAME ": sysconf");
    660 			exit(errno);
    661 		}
    662 		line_max = _POSIX2_LINE_MAX;
    663 	}
    664 do_arg:
    665 	arg = *++argv;
    666 
    667 	if (!arg)
    668 		goto done_arg;
    669 
    670 	if (*arg == '-')
    671 	{
    672 		char c;
    673 		arg++;
    674 		c = *arg++;
    675 		if (c == '-')
    676 		{
    677 			if (!strcmp(arg, "full-version"))
    678 				cmd = CMD_FULL_VERSION;
    679 			else if (!strcmp(arg, "version"))
    680 				cmd = CMD_VERSION;
    681 			else if (!strcmp(arg, "align-header"))
    682 				align_header = 1;
    683 			else if (!strcmp(arg, "align-dot"))
    684 				alignment_char = '.';
    685 			else if (startswith(arg, "align="))
    686 			{
    687 				arg += strlen("align=");
    688 				if (set_align(arg, &align, &align_size))
    689 					goto main_done;
    690 			}
    691 			else if (!strcmp(arg, "border-mode"))
    692 				border_mode = 1;
    693 			else if (startswith(arg, "columns="))
    694 			{
    695 				arg += strlen("columns=");
    696 				if (set_columns(arg, &rune_columns))
    697 					goto main_done;
    698 			}
    699 			else if (startswith(arg, "delimiter="))
    700 			{
    701 				arg += strlen("delimiter=");
    702 				if (set_delimiter(arg, &delimiter))
    703 					goto main_done;
    704 			}
    705 			else if (!strcmp(arg, "expand-tabs"))
    706 				expand_tabs = 1;
    707 			else if (startswith(arg, "format="))
    708 			{
    709 				arg += strlen("format=");
    710 				if (set_format(arg, &format, &format_size))
    711 					goto main_done;
    712 			}
    713 			else if (!strcmp(arg, "msdos"))
    714 				msdos = 1;
    715 			else if (!strcmp(arg, "no-ansi"))
    716 				no_ansi = 1;
    717 			else if (!strcmp(arg, "pause"))
    718 				opause = 1;
    719 			else if (startswith(arg, "quotes="))
    720 			{
    721 				arg += strlen("quotes=");
    722 				if (set_quotes_behavior(arg, &quotes))
    723 					goto main_done;
    724 			}
    725 			else if (startswith(arg, "symbols="))
    726 			{
    727 				arg += strlen("symbols=");
    728 				if (set_symbol_set(arg, &current_symbol_set,
    729 					    &current_inner_symbol_set))
    730 					goto main_done;
    731 			}
    732 			else if (!strcmp(arg, "yes-ansi"))
    733 				no_ansi = 0;
    734 			else if (!strcmp(arg, "help"))
    735 			{
    736 				usage();
    737 				goto main_done;
    738 			}
    739 			else
    740 			{
    741 				(void)usage();
    742 				error_code = ERR_BAD_CL_ARG;
    743 				goto main_done;
    744 			}
    745 		}
    746 		else
    747 		{
    748 			switch (c)
    749 			{
    750 			case 'a':
    751 				cmd = CMD_ALIGN;
    752 				break;
    753 			case 'b':
    754 				border_mode = 1;
    755 				break;
    756 			case 'c':
    757 				cmd = CMD_COLUMNS;
    758 				break;
    759 			case 'D':
    760 				alignment_char = '.';
    761 				break;
    762 			case 'd':
    763 				cmd = CMD_DELIMITER;
    764 				break;
    765 			case 'f':
    766 				cmd = CMD_FORMAT;
    767 				break;
    768 			case 'H':
    769 				align_header = 1;
    770 				break;
    771 			case 'h':
    772 				usage();
    773 				goto main_done;
    774 			case 'm':
    775 				msdos = 1;
    776 				break;
    777 			case 'n':
    778 				no_ansi = 1;
    779 				break;
    780 			case 'p':
    781 				opause = 1;
    782 				break;
    783 			case 'q':
    784 				cmd = CMD_QUOTES;
    785 				break;
    786 			case 's':
    787 				cmd = CMD_SYMBOLS;
    788 				break;
    789 			case 't':
    790 				expand_tabs = 1;
    791 				break;
    792 			case 'V':
    793 				cmd = CMD_FULL_VERSION;
    794 				break;
    795 			case 'v':
    796 				cmd = CMD_VERSION;
    797 				break;
    798 			case 'y':
    799 				no_ansi = 0;
    800 				break;
    801 			default:
    802 				(void)usage();
    803 				error_code = ERR_BAD_CL_ARG;
    804 				goto main_done;
    805 			}
    806 			/* Gobble up the rest of the arg ("-d:" etc) */
    807 			if (*arg)
    808 				goto continue_current_arg;
    809 			else
    810 				goto do_arg;
    811 		}
    812 	}
    813 	else
    814 	{
    815 	continue_current_arg:
    816 		if (cmd == CMD_ALIGN)
    817 		{
    818 			if (set_align(arg, &align, &align_size))
    819 				goto main_done;
    820 		}
    821 		else if (cmd == CMD_COLUMNS)
    822 		{
    823 			if (set_columns(arg, &rune_columns))
    824 				goto main_done;
    825 		}
    826 		else if (cmd == CMD_DELIMITER)
    827 		{
    828 			if (set_delimiter(arg, &delimiter))
    829 				goto main_done;
    830 		}
    831 		else if (cmd == CMD_FORMAT)
    832 		{
    833 			if (set_format(arg, &format, &format_size))
    834 				goto main_done;
    835 		}
    836 		else if (cmd == CMD_QUOTES)
    837 		{
    838 			if (set_quotes_behavior(arg, &quotes))
    839 				goto main_done;
    840 		}
    841 		else if (cmd == CMD_SYMBOLS)
    842 		{
    843 			if (set_symbol_set(arg, &current_symbol_set,
    844 				    &current_inner_symbol_set))
    845 				goto main_done;
    846 		}
    847 		else if (filename)
    848 			warning("Additional argument ignored: '%s'", arg);
    849 		else
    850 			filename = arg;
    851 		cmd = CMD_NONE;
    852 	}
    853 	goto do_arg;
    854 
    855 done_arg:
    856 	if (cmd == CMD_COLUMNS || cmd == CMD_DELIMITER || cmd == CMD_FORMAT
    857 		|| cmd == CMD_SYMBOLS || cmd == CMD_QUOTES)
    858 	{
    859 		error_code = ERR_NO_CL_ARG_PARAM;
    860 		goto main_done;
    861 	}
    862 	else if (cmd == CMD_FULL_VERSION)
    863 		return version(1);
    864 	else if (cmd == CMD_VERSION)
    865 		return version(0);
    866 
    867 	if (filename)
    868 	{
    869 #ifdef __OpenBSD__
    870 		if (unveil(filename, "r") < 0)
    871 		{
    872 			perror(PROGRAMNAME ": unveil");
    873 			exit(errno);
    874 		}
    875 		if (unveil(NULL, NULL) < 0)
    876 		{
    877 			perror(PROGRAMNAME ": unveil");
    878 			exit(errno);
    879 		}
    880 #endif
    881 		errno = 0;
    882 		if (stat(filename, &st) < 0)
    883 		{
    884 			perror(PROGRAMNAME ": stat");
    885 			exit(errno);
    886 		}
    887 		if (S_ISDIR(st.st_mode))
    888 		{
    889 			error_code = ERR_ISDIR;
    890 			goto main_done;
    891 		}
    892 		if (!S_ISREG(st.st_mode))
    893 		{
    894 			error_code = ERR_NOTREG_FILE;
    895 			goto main_done;
    896 		}
    897 		errno = 0;
    898 		input = fopen(filename, "r");
    899 		if (!input)
    900 			goto main_done;
    901 	}
    902 	else
    903 		input = stdin;
    904 
    905 	line_size = line_max;
    906 	CALLOC(line, u8, line_size);
    907 	outbuf_size = line_max;
    908 	CALLOC(outbuf, u8, outbuf_size);
    909 	pline		 = line;
    910 	possible_nulbyte = 0;
    911 
    912 do_input:
    913 	if (feof(input))
    914 		goto done_input;
    915 
    916 	if (!fgets((char*)pline, line_size - (pline - line), input))
    917 	{
    918 		if (ferror(input))
    919 		{
    920 			error_code = ERR_STREAM_ERROR;
    921 			goto main_done;
    922 		}
    923 		goto done_input;
    924 	}
    925 
    926 	if (possible_nulbyte)
    927 	{
    928 		error_code = ERR_BINARY_INPUT;
    929 		goto main_done;
    930 	}
    931 	eol = strchr((char*)pline, '\n');
    932 	if (eol)
    933 	{
    934 		if (msdos && *pline != '\n' && *(eol - 1) == '\r')
    935 			*(eol - 1) = 0;
    936 		*eol = 0;
    937 	}
    938 
    939 	nulbyte = strchr((char*)pline, 0);
    940 
    941 	/* We don't know if the NUL byte not being equal to
    942 	 * (possibly NULL) eol is the result of EOF or not
    943 	 */
    944 	if (nulbyte && (nulbyte != eol))
    945 		possible_nulbyte = 1;
    946 
    947 	if (!eol)
    948 	{
    949 		line_size += line_max;
    950 		REALLOC(line, u8, line_size);
    951 		outbuf_size += line_max;
    952 		REALLOC(outbuf, u8, outbuf_size);
    953 		pline = line + line_size - line_max - 1;
    954 		goto do_input;
    955 	}
    956 
    957 	if (!*line && lineno != 0)
    958 		goto empty_line;
    959 
    960 	in_quote	    = 0;
    961 	pline		    = line;
    962 	poutbuf		    = outbuf;
    963 	outbufrunelen	    = 0;
    964 	colno		    = 0;
    965 	current_rune_column = 0;
    966 
    967 	/* Top border */
    968 	if (lineno != 0)
    969 		goto not_first;
    970 
    971 	if (!border_mode)
    972 		table_columns = number_of_columns(line, delimiter);
    973 	else
    974 		table_columns = 1;
    975 
    976 	/* Sanity check */
    977 	if (rune_columns < 0 || rune_columns < (ssize_t)(2 * table_columns + 3))
    978 	{
    979 		warning("Columns adjusted to a reasonable minimum, %u",
    980 			2 * table_columns + 3);
    981 		rune_columns = 2 * table_columns + 3;
    982 	}
    983 	assert(rune_columns >= (ssize_t)(2 * table_columns + 3));
    984 
    985 	if (format && !border_mode)
    986 	{
    987 		int* pformat   = format;
    988 		int format_sum = 0;
    989 		while (*pformat && pformat - format < (ssize_t)table_columns)
    990 			format_sum += *pformat++;
    991 		pformat = format;
    992 		while (*pformat && format_sum)
    993 		{
    994 			*pformat = round_div((rune_columns - table_columns - 2)
    995 					* (*pformat),
    996 				format_sum);
    997 			assert(*pformat > 0);
    998 			pformat++;
    999 		}
   1000 	}
   1001 	else
   1002 	{
   1003 		format_value
   1004 			= (rune_columns - table_columns - 2) / table_columns;
   1005 
   1006 		assert(format_value >= 1);
   1007 	}
   1008 
   1009 	current_table_column = 0;
   1010 	column_start	     = 0;
   1011 
   1012 do_column:
   1013 	if (current_table_column >= table_columns)
   1014 		goto done_columns;
   1015 
   1016 	if (current_rune_column == 0)
   1017 	{
   1018 		u8len	    = u32_rune_to_u8(u8ch,
   1019 			      table_symbols[current_symbol_set][0]);
   1020 		u8ch[u8len] = 0;
   1021 		fputs((const char*)u8ch, stdout);
   1022 		column_start++;
   1023 	}
   1024 	else if (!within_column(column_start, current_rune_column))
   1025 	{
   1026 		if (current_table_column == table_columns - 1)
   1027 		{
   1028 			u8len	    = u32_rune_to_u8(u8ch,
   1029 				      table_symbols[current_symbol_set][2]);
   1030 			u8ch[u8len] = 0;
   1031 			fputs((const char*)u8ch, stdout);
   1032 		}
   1033 		else
   1034 		{
   1035 			/* clang-format off */
   1036 			u8len = u32_rune_to_u8(u8ch, table_inner_symbols[
   1037 					current_inner_symbol_set][0]);
   1038 			/* clang-format on */
   1039 			u8ch[u8len] = 0;
   1040 			fputs((const char*)u8ch, stdout);
   1041 			column_start++;
   1042 			if (format)
   1043 				column_start
   1044 					+= *(format + current_table_column);
   1045 			else
   1046 				column_start += format_value;
   1047 		}
   1048 		current_table_column++;
   1049 	}
   1050 	else
   1051 	{
   1052 		u8len	    = u32_rune_to_u8(u8ch,
   1053 			      table_symbols[current_symbol_set][1]);
   1054 		u8ch[u8len] = 0;
   1055 		fputs((const char*)u8ch, stdout);
   1056 	}
   1057 	current_rune_column++;
   1058 	goto do_column;
   1059 
   1060 done_columns:
   1061 	puts("");
   1062 	output_lines++;
   1063 	goto not_first;
   1064 
   1065 empty_line:
   1066 	current_table_column = 0;
   1067 	current_rune_column  = 0;
   1068 	column_start	     = 0;
   1069 	u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][3]);
   1070 	u8ch[u8len] = 0;
   1071 	fputs((const char*)u8ch, stdout);
   1072 	while (current_table_column + 1 < table_columns)
   1073 	{
   1074 		while (within_column(column_start, current_rune_column))
   1075 		{
   1076 			fputc(alignment_char, stdout);
   1077 			current_rune_column++;
   1078 		}
   1079 		/* clang-format off */
   1080 		u8len = u32_rune_to_u8(u8ch,
   1081 			table_inner_symbols[current_inner_symbol_set][1]);
   1082 		/* clang-format on */
   1083 		u8ch[u8len] = 0;
   1084 		fputs((const char*)u8ch, stdout);
   1085 		column_start++;
   1086 		if (format)
   1087 			column_start += *(format + current_table_column);
   1088 		else
   1089 			column_start += format_value;
   1090 		current_table_column++;
   1091 		current_rune_column++;
   1092 	}
   1093 	while (within_column(column_start, current_rune_column))
   1094 	{
   1095 		fputc(alignment_char, stdout);
   1096 		current_rune_column++;
   1097 	}
   1098 	u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][5]);
   1099 	u8ch[u8len] = 0;
   1100 	puts((const char*)u8ch);
   1101 	pline = line;
   1102 	goto do_input;
   1103 
   1104 not_first:
   1105 	/* Inner rows */
   1106 	current_table_column = 0;
   1107 	current_rune_column  = 0;
   1108 	column_start	     = 0;
   1109 	in_sgr		     = 0;
   1110 
   1111 	u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][3]);
   1112 	u8ch[u8len] = 0;
   1113 	fputs((const char*)u8ch, stdout);
   1114 
   1115 do_line:
   1116 	if (!pline || !*pline)
   1117 	{
   1118 		if (in_quote)
   1119 		{
   1120 			puts("");
   1121 			error_code = ERR_UNMATCHED_QUOTE;
   1122 			goto main_done;
   1123 		}
   1124 		else
   1125 			goto done_line;
   1126 	}
   1127 
   1128 	if (in_quote && *pline == '"' && *(pline + 1) == '"')
   1129 	{
   1130 		pline++;
   1131 		colno++;
   1132 		goto continue_line;
   1133 	}
   1134 	else if (quotes == Q_PROCESS && *pline == '"')
   1135 	{
   1136 		in_quote = !in_quote;
   1137 		pline++;
   1138 		colno++;
   1139 		goto do_line;
   1140 	}
   1141 
   1142 continue_line:
   1143 	outbuf_len = poutbuf - outbuf;
   1144 	ENSURE_SIZE(outbuf, tpoutbuf, outbuf_size,
   1145 		(ssize_t)(poutbuf - outbuf + sizeof((u8*)ANSI_SGR_BOLD_OFF)),
   1146 		(ssize_t)(outbuf_size + sizeof((u8*)ANSI_SGR_BOLD_OFF)), u8);
   1147 	poutbuf = outbuf + outbuf_len;
   1148 	if (u8_rune_to_u32(&uch, pline, &ch_len))
   1149 	{
   1150 		warning("Malformed UTF-8 at position %td: "
   1151 			"from=%02X",
   1152 			pline - line, *pline);
   1153 	}
   1154 	if (uch == U32('\033') && *(pline + 1) == '[')
   1155 	{
   1156 		in_sgr = 1;
   1157 		if (!no_ansi)
   1158 		{
   1159 			*poutbuf++ = *pline;
   1160 			*poutbuf   = 0;
   1161 		}
   1162 		pline++;
   1163 		colno++;
   1164 	}
   1165 	else if (uch == U32('m'))
   1166 	{
   1167 		if (!no_ansi || !in_sgr)
   1168 			*poutbuf++ = *pline;
   1169 		if (!in_sgr)
   1170 			outbufrunelen++;
   1171 		in_sgr = 0;
   1172 		pline++;
   1173 		colno++;
   1174 	}
   1175 	else if (in_sgr)
   1176 	{
   1177 		if (!no_ansi)
   1178 		{
   1179 			pch_end = pline + ch_len;
   1180 			while (pline != pch_end)
   1181 			{
   1182 				*poutbuf++ = *pline++;
   1183 				*poutbuf   = 0;
   1184 			}
   1185 		}
   1186 		else
   1187 			pline += ch_len;
   1188 		colno += ch_len;
   1189 	}
   1190 	else if (uch == delimiter && !in_quote
   1191 		&& current_table_column < table_columns - 1)
   1192 	{
   1193 		size_t colwidth = current_column_width();
   1194 
   1195 		if (!no_ansi && lineno == 0)
   1196 			snprintf((char*)poutbuf, sizeof ANSI_SGR_BOLD_OFF, "%s",
   1197 				ANSI_SGR_BOLD_OFF);
   1198 
   1199 		print_aligned(outbuf, outbufrunelen, colwidth,
   1200 			(lineno == 0 && !align_header)
   1201 				? ALIGN_LEFT
   1202 				: (current_table_column < align_size
   1203 						  ? align[current_table_column]
   1204 						  : ALIGN_LEFT),
   1205 			&current_rune_column);
   1206 
   1207 		while (within_column(column_start, current_rune_column))
   1208 		{
   1209 			fputc(alignment_char, stdout);
   1210 			current_rune_column++;
   1211 		}
   1212 
   1213 		*outbuf	      = 0;
   1214 		poutbuf	      = outbuf;
   1215 		outbufrunelen = 0;
   1216 
   1217 		column_start++;
   1218 		if (format)
   1219 			column_start += *(format + current_table_column);
   1220 		else
   1221 			column_start += format_value;
   1222 		if (current_table_column != table_columns - 1)
   1223 		{
   1224 			/* clang-format off */
   1225 			u8len = u32_rune_to_u8(u8ch, table_inner_symbols[
   1226 					current_inner_symbol_set][1]);
   1227 			/* clang-format on */
   1228 			u8ch[u8len] = 0;
   1229 			fputs((const char*)u8ch, stdout);
   1230 			current_rune_column++;
   1231 			current_table_column++;
   1232 		}
   1233 
   1234 		if (!no_ansi && lineno == 0)
   1235 			poutbuf += snprintf((char*)poutbuf,
   1236 				sizeof ANSI_SGR_BOLD_ON, "%s",
   1237 				ANSI_SGR_BOLD_ON);
   1238 
   1239 		pline += ch_len;
   1240 		colno += ch_len;
   1241 	}
   1242 	else if (uch == TAB && expand_tabs)
   1243 	{
   1244 		while ((current_rune_column + outbufrunelen) % tab_length != 0)
   1245 		{
   1246 			*poutbuf++ = alignment_char;
   1247 			*poutbuf   = 0;
   1248 			outbufrunelen++;
   1249 		}
   1250 		pline++;
   1251 		colno++;
   1252 	}
   1253 	else
   1254 	{
   1255 		pch_end = pline + ch_len;
   1256 		while (pline != pch_end)
   1257 		{
   1258 			*poutbuf++ = *pline++;
   1259 			*poutbuf   = 0;
   1260 		}
   1261 		outbufrunelen++;
   1262 		colno += ch_len;
   1263 	}
   1264 	goto do_line;
   1265 
   1266 done_line:
   1267 	if (!no_ansi && lineno == 0)
   1268 		fputs(ANSI_SGR_BOLD_OFF, stdout);
   1269 
   1270 	if (*outbuf)
   1271 	{
   1272 		size_t colwidth = format ? *(format + current_table_column)
   1273 					 : format_value;
   1274 
   1275 		assert(colwidth != 0);
   1276 
   1277 		if (!no_ansi && lineno == 0)
   1278 			snprintf((char*)poutbuf, sizeof ANSI_SGR_BOLD_OFF, "%s",
   1279 				ANSI_SGR_BOLD_OFF);
   1280 
   1281 		print_aligned(outbuf, outbufrunelen, colwidth,
   1282 			(lineno == 0 && !align_header)
   1283 				? ALIGN_LEFT
   1284 				: (current_table_column < align_size
   1285 						  ? align[current_table_column]
   1286 						  : ALIGN_LEFT),
   1287 			&current_rune_column);
   1288 
   1289 		*outbuf = 0;
   1290 	}
   1291 	while (current_table_column < table_columns - 1)
   1292 	{
   1293 		while (within_column(column_start, current_rune_column))
   1294 		{
   1295 			fputc(alignment_char, stdout);
   1296 			current_rune_column++;
   1297 		}
   1298 		u8len	    = u32_rune_to_u8(u8ch,
   1299 			      table_inner_symbols[current_inner_symbol_set][1]);
   1300 		u8ch[u8len] = 0;
   1301 		fputs((const char*)u8ch, stdout);
   1302 		column_start++;
   1303 		if (format)
   1304 			column_start += *(format + current_table_column);
   1305 		else
   1306 			column_start += format_value;
   1307 		current_table_column++;
   1308 		current_rune_column++;
   1309 	}
   1310 	while (within_column(column_start, current_rune_column))
   1311 	{
   1312 		fputc(alignment_char, stdout);
   1313 		current_rune_column++;
   1314 	}
   1315 
   1316 	u8len = u32_rune_to_u8(u8ch, table_symbols[current_symbol_set][5]);
   1317 	u8ch[u8len] = 0;
   1318 	puts((const char*)u8ch);
   1319 
   1320 	output_lines++;
   1321 	lineno++;
   1322 	pline = line;
   1323 	goto do_input;
   1324 
   1325 done_input:
   1326 	free(line);
   1327 	free(outbuf);
   1328 
   1329 	/* Bottom border */
   1330 	if (!output_lines)
   1331 		goto skip_bottom_border;
   1332 
   1333 	current_table_column = 0;
   1334 	current_rune_column  = 0;
   1335 	column_start	     = 0;
   1336 
   1337 do_bottom_border:
   1338 	if (current_table_column >= table_columns)
   1339 		goto done_bottom_border;
   1340 
   1341 	if (current_rune_column == 0)
   1342 	{
   1343 		u8len = u32_rune_to_u8(u8ch,
   1344 			table_symbols[current_symbol_set][6]);
   1345 		column_start++;
   1346 	}
   1347 	else if (!within_column(column_start, current_rune_column))
   1348 	{
   1349 		if (current_table_column == table_columns - 1)
   1350 		{
   1351 			u8len = u32_rune_to_u8(u8ch,
   1352 				table_symbols[current_symbol_set][8]);
   1353 			current_table_column++;
   1354 		}
   1355 		else
   1356 		{
   1357 			u8len = u32_rune_to_u8(u8ch,
   1358 				table_inner_symbols[current_inner_symbol_set][2]);
   1359 			column_start++;
   1360 			if (format)
   1361 				column_start
   1362 					+= *(format + current_table_column);
   1363 			else
   1364 				column_start += format_value;
   1365 			current_table_column++;
   1366 		}
   1367 	}
   1368 	else
   1369 		u8len = u32_rune_to_u8(u8ch,
   1370 			table_symbols[current_symbol_set][7]);
   1371 
   1372 	u8ch[u8len] = 0;
   1373 	fputs((const char*)u8ch, stdout);
   1374 	current_rune_column++;
   1375 	goto do_bottom_border;
   1376 
   1377 done_bottom_border:
   1378 	puts("");
   1379 	output_lines++;
   1380 
   1381 skip_bottom_border:
   1382 main_done:
   1383 	if (errno)
   1384 		perror(PROGRAMNAME);
   1385 
   1386 	free(format);
   1387 	free(align);
   1388 	if (opause)
   1389 	{
   1390 		char* buf = NULL;
   1391 		MALLOC(buf, line_max);
   1392 		puts("Press Enter to continue: ");
   1393 		fflush(stdout);
   1394 		clearerr(stdin);
   1395 		fgets(buf, line_max, stdin);
   1396 		free(buf);
   1397 	}
   1398 	if (input)
   1399 		fclose(input);
   1400 
   1401 	switch (error_code)
   1402 	{
   1403 	case ERR_NONE:
   1404 		break;
   1405 	case ERR_NO_CL_ARG_PARAM:
   1406 	case ERR_BAD_CL_ARG:
   1407 		error(error_code, errors[error_code], *argv);
   1408 		break;
   1409 	case ERR_ISDIR:
   1410 	case ERR_NOTREG_FILE:
   1411 		error(error_code, errors[error_code], filename);
   1412 		break;
   1413 	case ERR_BAD_ALIGN:
   1414 	case ERR_BAD_NUMBER:
   1415 	case ERR_BAD_FORMAT_SPEC:
   1416 	case ERR_BAD_QUOTES_ARG:
   1417 	case ERR_BAD_SET_SPEC:
   1418 		error(error_code, errors[error_code], arg);
   1419 		break;
   1420 	default:
   1421 		error(error_code, errors[error_code]);
   1422 		break;
   1423 	}
   1424 	return errno ? errno
   1425 		     : (error_code ? CUSTOM_ERROR_START + error_code : 0);
   1426 }