table

Помоћни програм из командне линије за форматирање и приказ CSV-а
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување c9ccb2055ddd582a7b9c5e0bc8bfeb31706d2715
родитељ f314368b3154611bdac47bdb21af3068635af924
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Mon, 15 Feb 2021 21:21:47 +0100

Core code rewrite; new examples directory, updated scripts in tests; updated manpage

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MREADME | 23+++++++++++++++--------
MTODO | 2+-
Mdefs.h | 8++++++--
Aexamples/quotes.csv | 6++++++
Rtest/sales.csv -> examples/sales.csv | 0
Rtest/sales.sc -> examples/sales.sc | 0
Mindex.html | 31+++++++++++++++++++++++++------
Mtable.1.in | 133+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mtable.c | 654+++++++++++++++++++++++++++----------------------------------------------------
Mtest/cp-help.sh | 2+-
Mtest/file.sh | 2+-
Mtest/ls-2col.sh | 3++-
Mtest/ls-adv.sh | 3++-
Mtest/ls-multicolumn.sh | 2+-
Mtest/lsb_release.sh | 2+-
Mtest/neofetch.sh | 2+-
Mtest/uname-a.sh | 2+-
измењених датотека: 17, додавања: 363(+), брисања: 512(-)

diff --git a/README b/README @@ -9,33 +9,40 @@ Command line utility to format and display CSV. $ git clone https://github.com/Strahinja/table.git $ cd table -$ make && sudo make install +$ su + + Then, if you have apenwarr/redo: + +# redo install + + if you don't: + +# ./do install + Examples -------- -$ watch "table /proc/loadavg -d ' ' -c 50 -s ss" - Periodically display CPU load information in a single-line table - -$ ls -l | tail -n +2 | sed -e 's/ \+/ /g' | table -d ' ' + $ watch "table /proc/loadavg -d ' ' -c 50 -s ss" Format a long list of files in the current directory in a double line table with single-line column divisors - -$ ls -l | tail -n +2 | awk '{ print $1 " " $9 }' | table -d ' ' -c 30 + $ ls -l | tail -n +2 | sed -e 's/ \+/ /g' | table -d ' ' Like the above, but only permissions and file names are printed in a narrow table + $ ls -l | tail -n +2 | awk '{ print $1 " " $9 }' | table -d ' ' -c 30 + License ------- table - Command line utility to format and display CSV. - Copyright (C) 2020 Страхиња Радић + Copyright (C) 2020, 2021 Страхиња Радић This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/TODO b/TODO @@ -3,5 +3,5 @@ * Current WIP - [ ] Handle tty ANSI codes (test/neofetch.sh) + [ ] Variable column widths diff --git a/defs.h b/defs.h @@ -20,6 +20,8 @@ #ifndef __DEFS_H #define __DEFS_H +#define _POSIX_C_SOURCE 200809L + #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -32,7 +34,7 @@ #define PROGRAMNAME "table" #define VERSION "0.1.2-beta" -#define BUFSIZE 1024 +#define BUFSIZE 4096 // Minimal table column width without the borders // | A | B | C | @@ -41,7 +43,9 @@ // [Max table columns] = ------------------------------------------ // MIN_TABLE_COL_WIDTH #define MIN_TABLE_COL_WIDTH 3 -#define ANSI_SGR_RESET "\e[0m\e[?25h" +//#define ANSI_SGR_RESET "\e[0m\e[?25h" +#define ANSI_SGR_BOLD_ON "\e[1m" +#define ANSI_SGR_BOLD_OFF "\e[0m" typedef enum { diff --git a/examples/quotes.csv b/examples/quotes.csv @@ -0,0 +1,6 @@ +"ID","Name","Surname","Age" +"001","John","Smith","34" +"002","Петар","Петровић","23" +"003","Richard","Smith","33" +"007","Bond, James","Bond","46" + diff --git a/test/sales.csv b/examples/sales.csv diff --git a/test/sales.sc b/examples/sales.sc diff --git a/index.html b/index.html @@ -1,9 +1,11 @@ <!DOCTYPE html> -<html> +<html lang="en"> <head> <meta charset="UTF-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>table - Command line utility to format and display CSV</title> + <link rel="canonical" href="https://strahinja.github.io/table/" /> + <meta name="description" content="Command line utility to format and display CSV" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> html, body, html * { @@ -68,19 +70,31 @@ $ git clone <a href="https://github.com/Strahinja/table.git">https://github.com/Strahinja/table.git</a> $ cd table -$ make &amp;&amp; sudo make install +$ su + </pre> + + <p>Then, if you have <a href="https://github.com/apenwarr/redo">apenwarr/redo</a>:</p> + + <pre> +# redo install + </pre> + + <p>if you don't:</p> + + <pre> +# ./do install </pre> <h2>Examples</h2> <pre class="example"> -$ watch "table /proc/loadavg -d ' ' -c 50 -s ss"</pre> +$ watch "table /proc/loadavg -d ' ' -c 50 -n -s ss"</pre> <p class="explanation">Periodically display CPU load information in a single-line table</p> <pre class="example"> -$ ls -l | tail -n +2 | sed -e 's/ \+/ /g' | table -d ' '</pre> +$ ls -l | tail -n +2 | sed -e 's/ \+/ /g' | table -d ' ' -n</pre> <p class="explanation">Format a long list of files in the current directory in a double line table with single-line column divisors</p> @@ -94,6 +108,11 @@ $ ls -l | tail -n +2 | awk '{ print $1 " " $9 }' | table -d ' ' -c 30</pre> <h2>Changelog</h2> + <h3>v0.2</h3> + <ul> + <li></li> + </ul> + <h3>v0.1.2-beta</h3> <ul> <li>Add --border-mode</li> @@ -120,7 +139,7 @@ $ ls -l | tail -n +2 | awk '{ print $1 " " $9 }' | table -d ' ' -c 30</pre> <h2>License</h2> <p>table - Command line utility to format and display CSV.<br /> - Copyright (C) 2020 Страхиња Радић</p> + Copyright (C) 2020, 2021 Страхиња Радић</p> <p>This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the diff --git a/table.1.in b/table.1.in @@ -2,19 +2,46 @@ .\" Manpage for table(1) . .mso an-ext.tmac +.de CDS +.EX +.RS \\$1 +.sp 1 +.. +.de CDE +.sp 1 +.RE +.EE +.. . .TH TABLE "1" "%DATE%" "%VERSION%" "General Commands Manual" .SH NAME table \- Command line utility to format and display CSV. . .SH SYNOPSIS +. +.SY table +.OP "\-h \fR|\fP \-\-help" +.YS +. +.SY table +.OP "\-v \fR|\fP \-\-version" +.YS +. .SY table -.RI [ options ] -.RI [ filename ] +.OP "\-b \fR|\fP \-\-border\-mode" +.OP "\-c \fR|\fP \-\-columns=" cols +.OP "\-d \fR|\fP \-\-delim=" delim +.OP "\-n \fR|\fP \-\-no\-ansi" +.OP "\-s \fR|\fP \-\-symbols=" set +.OP "\-t \fR|\fP \-\-expand-tabs" .YS . .SH COPYRIGHT -table Copyright \(co 2020 Strahinya Radich. +table Copyright \(co 2020, 2021 Strahinya Radich. +.br +This program is licensed under GNU GPL v3 or later. See the file +.I LICENSE +in the table repository for details. . .SH DESCRIPTION .B Table @@ -28,23 +55,6 @@ drawing. .SH OPTIONS . .TP -.B \-a -.TQ -.B \-\-ansi -.br -.SM ANSI SGR -processing in the input: add -.EX -.in +4 -\\e[0m\\e[?25h -.in -4 -.EE -to the end of each cell. (Also see -.SM -.B "BUGS " -below.) -. -.TP .B \-b .TQ .B \-\-border-mode @@ -52,24 +62,23 @@ below.) Border mode: force single column. . .TP -.BR \-c " <\fIcols\fR>" +.BI \-c " cols" .TQ -.BR \-\-columns= <\fIcols\fR> +.BI \-\-columns= cols .br Set maximum table width in columns (default 80). . .TP -.BR \-d " <\fIdelim\fR>" +.BI \-d " delim" .TQ -.BR \-\-delimiter= <\fIdelim\fR> +.BI \-\-delimiter= delim .br Set field delimiter (default ","). First line of the input determines the number of table columns. Special characters, like ";", need to be quoted: -.EX -.in +4 +. +.CDS 12 $ table -d ';' -.in -4 -.EE +.CDE . .TP .BR \-h @@ -79,12 +88,24 @@ $ table -d ';' Print this usage information screen. . .TP -.BR \-s " <\fIset\fR>" +.B \-n .TQ -.BR \-\-symbols= <\fIset\fR> +.B \-\-no\-ansi .br -Use table symbol set <\fIset\fR> for table lines, where <\fIset\fR> is one of -the following (format: <\fIborder\fR><\fIinner border\fR>): +Turn off +.SM ANSI SGR +codes in the output. By default, the header row is printed in bold using +.SM ANSI +codes. This switch prevents that. +. +.TP +.BI \-s " set" +.TQ +.BI \-\-symbols= set +.br +Use table symbol set \fIset\fP for table lines, where \fIset\fP is one of +the following (format: <\fIborder\fP><\fIinner border\fP>): +. .TS box tab(@); c l. @@ -100,6 +121,13 @@ _ .TE . .TP +.B \-t +.TQ +.B \-\-expand\-tabs +.br +Expand tabs to spaces. Default behavior is to output tab characters as-is. +. +.TP .B \-v .TQ .B \-\-version @@ -112,45 +140,36 @@ Print program version and exit. . .SH EXAMPLES . -.TP -.EX -$ watch "table /proc/loadavg -d ' ' -c 50 -s ss" -.EE +.LP Periodically display .SM CPU load information in a single-line table . -.TP -.EX -$ ls -l | tail -n +2 | sed -e 's/ \+/ /g' | table -d ' ' -.EE +.CDS 4 +$ watch "table /proc/loadavg -d ' ' -c 50 -s ss" +.CDE +. +.LP Format a long list of files in the current directory in a double line table with single-line column divisors . -.TP -.EX -$ ls -l | tail -n +2 | awk '{ print $1 " " $9 }' | table -d ' ' -c 30 -.EE +.CDS 4 +$ ls -l | tail -n +2 | sed -e 's/ \+/ /g' | table -d ' ' +.CDE +. +.LP Like the above, but only permissions and file names are printed in a narrow table . -.SH BUGS -. -.PP -.SM ANSI -sequences are not handled as well as they should as of -.B table -v0.1.2 and the code to format them will likely be rewritten. They are displayed -to an extent, together with the codes to reset the formatting, but the line and -column lengths are still not correctly determined. +.CDS 4 +$ ls -l | tail -n +2 | awk '{ print $1 " " $9 }' | table -d ' ' -c 30 +.CDE . -.PP -For more details, see the -.SM TODO -file in the repository. +.SH BUGS . .PP Bugs can be reported using the Github issue tracker at: .UR https://\:github.com/\:Strahinja/\:table/\:issues .UE +.\" vim: set filetype=groff: diff --git a/table.c b/table.c @@ -32,6 +32,12 @@ #define REALLOCARRAY(ptr, membtype, newcount) \ REALLOC(ptr, membtype, sizeof(membtype) * newcount) +#define WITHIN_COLUMN (current_rune_column < \ + (current_table_column+1) * max_table_column_width \ + + current_table_column) + +#define WITHIN_SINGLE_COLUMN (current_rune_column < rune_columns-2) + int version() { @@ -42,9 +48,9 @@ version() int usage() { - printf("Usage: %s [-a|--ansi] [-b|--border-mode] [-c" - " <cols>|--columns=<cols>] [-d <delim>|--delimiter=<delim>]" - " [-h|--help] [-s <set>|--symbols=<set>] [-v|--version]\n", + printf("Usage: %s [-b|--border-mode] [-c <cols>|--columns=<cols>] [-d" + " <delim>|--delimiter=<delim>] [-h|--help] [-n|--no-ansi] [-s" + " <set>|--symbols=<set>] [-t|--expand-tabs] [-v|--version]\n", PROGRAMNAME); return 0; } @@ -61,9 +67,6 @@ error(int code, uint8_t* fmt, ...) return code; } -void -u32_print(char* name, uint32_t* src, size_t src_len); - char* substr(const char* src, int start, int finish) { @@ -97,31 +100,6 @@ startswith(const char* s, const char* what) return result; } -uint32_t* -u32_substr(uint32_t* src, size_t src_len, size_t start, size_t finish, - size_t* dest_len) -{ - size_t normalized_finish = src_len; - uint32_t* dest = NULL; - if (finish < normalized_finish) - normalized_finish = finish; - if (normalized_finish < 0) - normalized_finish = 0; - size_t normalized_start = 0; - if (start > normalized_start) - normalized_start = start; - if (normalized_start > normalized_finish) - normalized_start = normalized_finish; - size_t len = normalized_finish - normalized_start; - - CALLOC(dest, uint32_t, len+1) - memcpy(dest, src + normalized_start, sizeof(uint32_t) * len); - *(dest+len) = 0; - *dest_len = len; - - return dest; -} - int set_symbol_set(char* arg, int* current_symbol_set, int* current_inner_symbol_set) @@ -170,63 +148,52 @@ set_columns(const char* arg, size_t* cols) } int -set_delimiter(char* arg, uint32_t* delimiter) +set_delimiter(uint8_t* arg, ucs4_t* delimiter) { - size_t start = 0; - size_t len = strlen(arg); - size_t finish = len; - - if (arg[0] == '"') - start = 1; - if (arg[len-1] == '"') - finish = len-2; - - char* stripped_arg = substr(arg, start, finish); - uint32_t* delim = NULL; - CALLOC(delim, uint32_t, BUFSIZE) - size_t delim_len = BUFSIZE; - u8_to_u32((const uint8_t*)stripped_arg, len, delim, &delim_len); - *delimiter = *delim; - free(delim); - free(stripped_arg); + size_t arg_len = u8_strlen(arg); + size_t delimiter_len = 0; + uint8_t* parg = arg; + ucs4_t uch; - return 0; + while (parg && *parg) + { + delimiter_len = u8_mbtouc(&uch, parg, arg_len); + if (uch != (ucs4_t)'"' && uch != (ucs4_t)'\'') + { + *delimiter = uch; + return 0; + } + parg += delimiter_len; + } + + return -1; } +/* Return number of columns based on the input line */ size_t -number_of_columns(const uint32_t* input, const size_t input_len, - uint32_t delimiter) +number_of_columns(const uint8_t* input, ucs4_t delimiter) { + ucs4_t uch; + size_t ch_len = 0; size_t result = 1; + const uint8_t* pinput = NULL; + size_t input_len = 0; - for (size_t i = 0; i < input_len; i++) - if (input[i] == delimiter) - result++; - return result; -} + if (!input) + return result; -uint32_t* -u32_tabs_to_spaces(const uint32_t* src, const size_t src_len, - size_t* dest_len, size_t tab_length) -{ - size_t rune_column = 0; - uint32_t* dest = NULL; - CALLOC(dest, uint32_t, BUFSIZE) + input_len = u8_strlen(input); + pinput = input; - for (size_t i = 0; i < src_len; i++) + while (pinput && *pinput) { - if (src[i] == (uint32_t)'\t') - { - do - dest[rune_column++] = (uint32_t)' '; - while (rune_column % tab_length != 0); - } - else - dest[rune_column++] = src[i]; + ch_len = u8_mbtouc(&uch, pinput, input_len); + if (uch == delimiter) + result++; + pinput += ch_len; } - *dest_len = rune_column; - dest[*dest_len] = (uint32_t)'\0'; - return dest; + + return result; } // | A | B | C | @@ -244,83 +211,6 @@ get_max_table_column_width(size_t rune_columns, size_t table_columns) : result; } -size_t -get_max_last_table_column_width(size_t rune_columns, size_t table_columns) -{ - size_t result = get_max_table_column_width(rune_columns, table_columns); - if (rune_columns > (table_columns-1) * (result+1) + 2) - return rune_columns - (table_columns-1) * (result+1) - 2; - return MIN_TABLE_COL_WIDTH; -} - -void -u32_print(char* name, uint32_t* src, size_t src_len) -{ - fprintf(stdout, "%s [%lu] = \"", name, src_len); - for (size_t k = 0; k < src_len; k++) - { - uint8_t charbuf[7]; - size_t charbuf_len = 7; - u32_to_u8(src+k, 1, charbuf, &charbuf_len); - charbuf[charbuf_len] = (uint8_t)'\0'; - fprintf(stdout, "%X='%s' ", src[k], charbuf); - } - fprintf(stdout, "\"\n"); -} - -// Returns the number of uint32_t characters that make up ANSI SGR codes and -// thus don't count towards the string rune column length -size_t -ansi_lookahead(uint32_t* src, size_t src_len) -{ - size_t i = 0; - size_t chars_in_sgr = 0; - size_t current_sgr_len = 0; - BOOL in_sgr = FALSE; - - while (i < src_len) - { - if (src[i] == (uint32_t)'\e' - && i+1 < src_len - && src[i+1] == (uint32_t)'[') - { - in_sgr = TRUE; - current_sgr_len = 0; - i++; - } - else if (in_sgr && (src[i] == (uint32_t)'0' - || src[i] == (uint32_t)'1' - || src[i] == (uint32_t)'2' - || src[i] == (uint32_t)'3' - || src[i] == (uint32_t)'4' - || src[i] == (uint32_t)'5' - || src[i] == (uint32_t)'6' - || src[i] == (uint32_t)'7' - || src[i] == (uint32_t)'8' - || src[i] == (uint32_t)'9' - || src[i] == (uint32_t)';')) - { - current_sgr_len++; - chars_in_sgr++; - } - else if (in_sgr && current_sgr_len > 0 && src[i] == (uint32_t)'m') - { - in_sgr = FALSE; - chars_in_sgr++; - } - else if (in_sgr) - { - // Only count full SGRs, \e[ n [; n...] m - in_sgr = FALSE; - chars_in_sgr -= current_sgr_len; - current_sgr_len = 0; - } - //else... - i++; - } - return chars_in_sgr; -} - int main(int argc, char** argv) { @@ -331,12 +221,12 @@ main(int argc, char** argv) int current_inner_symbol_set = TABLE_INNER_DOUBLE_SINGLE; size_t table_columns = 0; size_t max_table_column_width = MIN_TABLE_COL_WIDTH; - size_t max_last_table_column_width = MIN_TABLE_COL_WIDTH; size_t rune_columns = 80; size_t tab_length = 8; - uint32_t delimiter = ','; - BOOL handle_ansi = FALSE; + ucs4_t delimiter = ','; + BOOL handle_ansi = TRUE; BOOL border_mode = FALSE; + BOOL expand_tabs = FALSE; while ((arg = *++argv)) { @@ -349,11 +239,6 @@ main(int argc, char** argv) { if (!strcmp(arg, "version")) cmd = CMD_VERSION; - else if (startswith(arg, "ansi")) - { - arg += strlen("ansi"); - handle_ansi = TRUE; - } else if (startswith(arg, "border-mode")) { arg += strlen("border-mode"); @@ -369,10 +254,20 @@ main(int argc, char** argv) else if (startswith(arg, "delimiter=")) { arg += strlen("delimiter="); - result = set_delimiter(arg, &delimiter); + result = set_delimiter((uint8_t*)arg, &delimiter); if (result) return result; } + else if (startswith(arg, "expand-tabs")) + { + arg += strlen("expand-tabs"); + expand_tabs = TRUE; + } + else if (startswith(arg, "no-ansi")) + { + arg += strlen("no-ansi"); + handle_ansi = FALSE; + } else if (startswith(arg, "symbols=")) { arg += strlen("symbols="); @@ -380,7 +275,6 @@ main(int argc, char** argv) &current_inner_symbol_set); if (result) return result; - } else if (!strcmp(arg, "help")) return usage(); @@ -394,9 +288,6 @@ main(int argc, char** argv) { switch (c) { - case 'a': - handle_ansi = TRUE; - break; case 'b': border_mode = TRUE; break; @@ -409,9 +300,15 @@ main(int argc, char** argv) case 'h': return usage(); break; + case 'n': + handle_ansi = FALSE; + break; case 's': cmd = CMD_SYMBOLS; break; + case 't': + expand_tabs = TRUE; + break; case 'v': cmd = CMD_VERSION; break; @@ -439,7 +336,7 @@ main(int argc, char** argv) } else if (cmd == CMD_DELIMITER) { - result = set_delimiter(arg, &delimiter); + result = set_delimiter((uint8_t*)arg, &delimiter); if (result) return result; } @@ -464,309 +361,206 @@ main(int argc, char** argv) uint8_t* line = NULL; CALLOC(line, uint8_t, BUFSIZE) - size_t line_len = 0; - - uint32_t* unicode_line = NULL; - size_t unicode_line_len = 0; - - uint8_t* buffer = NULL; - size_t buffer_len = 0; + size_t pline_len = 0; + uint8_t* pline = NULL; + BOOL quote = FALSE; + size_t colno = 0; + size_t lineno = 0; + ucs4_t uch; + size_t ch_len = 0; + size_t current_table_column = 0; + size_t current_rune_column = 0; + + while (!feof(input)) + { + char* eol = NULL; + if (!fgets((char*)line, sizeof(uint8_t) * BUFSIZE-1, input)) + break; - uint32_t* unicode_buffer = NULL; - size_t unicode_buffer_len = 0; + eol = strchr((char*)line, '\n'); + if (eol) + *eol = 0; - size_t sgr_chars = 0; + if (!*line) + continue; - size_t col; + quote = FALSE; + pline = line; + colno = 0; + current_rune_column = 0; - do - { - fgets((char*)line, sizeof(uint8_t) * BUFSIZE, input); - line_len = u8_strlen(line); - - if (!feof(input)) + if (lineno == 0) { - uint8_t* eol = u8_strchr(line, (ucs4_t)'\n'); - if (eol) - *eol = 0; - - if (unicode_buffer) - REALLOC(unicode_buffer, uint32_t, BUFSIZE) + if (!border_mode) + table_columns = number_of_columns(line, delimiter); else - CALLOC(unicode_buffer, uint32_t, BUFSIZE) - unicode_buffer_len = BUFSIZE; - - unicode_line_len = BUFSIZE; - unicode_line = u8_to_u32(line, line_len, NULL, &unicode_line_len); - if (!unicode_line) + table_columns = 1; + max_table_column_width = get_max_table_column_width(rune_columns, + table_columns); + if (rune_columns < table_columns * MIN_TABLE_COL_WIDTH + + table_columns + 1) + rune_columns = table_columns * MIN_TABLE_COL_WIDTH + + table_columns + 1; + + current_table_column = 0; + for (size_t col = 0; col < rune_columns; col++) { - fprintf(stdout, "%s", table_symbols[current_symbol_set][3]); - for (size_t i = 0; i < max_table_column_width; i++) - fprintf(stdout, " "); - if (handle_ansi) - fprintf(stdout, "%s", ANSI_SGR_RESET); - fprintf(stdout, "%s\n", table_symbols[current_symbol_set][5]); - continue; - } - - if (!table_columns) - { - // Sanity check - if (rune_columns < MIN_TABLE_COL_WIDTH + 2) - rune_columns = MIN_TABLE_COL_WIDTH + 2; - - table_columns = border_mode ? 1 - : number_of_columns(unicode_line, unicode_line_len, - delimiter); - max_table_column_width = - get_max_table_column_width(rune_columns, table_columns); - max_last_table_column_width = - get_max_last_table_column_width(rune_columns, table_columns); - - // Sanity check - if (rune_columns < (table_columns-1) * max_table_column_width - + max_last_table_column_width + table_columns + 1) + if (col == 0) + printf("%s", table_symbols[current_symbol_set][0]); + else if (col == rune_columns-1) + printf("%s", table_symbols[current_symbol_set][2]); + else if (current_table_column < table_columns-1 + && col > 1 + && col % (max_table_column_width+1) == 0) { - rune_columns = (table_columns-1) * max_table_column_width - + max_last_table_column_width + table_columns + 1; - max_table_column_width = - get_max_table_column_width(rune_columns, table_columns); - max_last_table_column_width = - get_max_last_table_column_width(rune_columns, table_columns); + current_table_column++; + printf("%s", table_inner_symbols[current_inner_symbol_set][0]); } - - fprintf(stdout, "%s", table_symbols[current_symbol_set][0]); - col = 0; - size_t table_column = 0; - while (col++ < rune_columns-2) - if (table_column < table_columns-1 - && col % (max_table_column_width+1) == 0) - { - fprintf(stdout, "%s", - table_inner_symbols[current_inner_symbol_set][0]); - table_column++; - } - else - fprintf(stdout, "%s", table_symbols[current_symbol_set][1]); - fprintf(stdout, "%s\n", table_symbols[current_symbol_set][2]); + else + printf("%s", table_symbols[current_symbol_set][1]); } + printf("\n"); + } - if (unicode_buffer) - free(unicode_buffer); - unicode_buffer = u32_tabs_to_spaces(unicode_line, unicode_line_len, - &unicode_buffer_len, tab_length); - - if (unicode_line) - free(unicode_line); - CALLOC(unicode_line, uint32_t, BUFSIZE) - u32_strcpy(unicode_line, unicode_buffer); - unicode_line_len = unicode_buffer_len; + current_table_column = 0; - size_t substr_start = 0; - size_t substr_finish = 0; - size_t rune_column; - size_t column_width = max_table_column_width; - size_t written_columns = 0; - size_t current_table_column = 0; + printf("%s", table_symbols[current_symbol_set][3]); + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); - for (rune_column = 0; - rune_column < unicode_line_len; - rune_column++) + while (pline && *pline) + { + if (*pline == '"') { - if (table_columns>1 - && current_table_column < table_columns-1 - && unicode_line[rune_column] == delimiter) - { - column_width = max_table_column_width; - - fprintf(stdout, "%s", - current_table_column == 0 - ? table_symbols[current_symbol_set][3] - : table_inner_symbols[current_inner_symbol_set][1] - ); - written_columns++; - - substr_finish = rune_column; - if (unicode_buffer) - free(unicode_buffer); - unicode_buffer = u32_substr(unicode_line, - unicode_line_len, - substr_start, - substr_finish, - &unicode_buffer_len); - if (unicode_buffer_len < column_width) - column_width = unicode_buffer_len; - - if (buffer) - free(buffer); - CALLOC(buffer, uint8_t, BUFSIZE) - buffer_len = BUFSIZE; - u32_to_u8(unicode_buffer, column_width, - buffer, &buffer_len); - - if (buffer && buffer_len>0) - fprintf(stdout, "%s", buffer); - - if (handle_ansi) - sgr_chars = ansi_lookahead(unicode_buffer, column_width); - for (size_t i = 0; i + column_width < - max_table_column_width + sgr_chars; i++) - fprintf(stdout, " "); - if (handle_ansi) - fprintf(stdout, "%s", ANSI_SGR_RESET); - - written_columns += max_table_column_width; - - substr_start = rune_column+1; - current_table_column++; - } + quote = !quote; + pline++; + colno++; } - - // Leftovers - if (table_columns > 1) + else { - column_width = max_table_column_width; - // Not the last column - if (current_table_column > table_columns-2) - column_width = max_last_table_column_width; - - fprintf(stdout, "%s", - current_table_column == 0 - ? table_symbols[current_symbol_set][3] - : table_inner_symbols[current_inner_symbol_set][1] - ); - written_columns++; - - substr_finish = substr_start + column_width; - if (unicode_buffer) - free(unicode_buffer); - unicode_buffer = u32_substr(unicode_line, - unicode_line_len, - substr_start, - substr_finish, - &unicode_buffer_len); - unicode_buffer_len = u32_strlen(unicode_buffer); - if (unicode_buffer_len < column_width) - column_width = unicode_buffer_len; - - if (buffer) - free(buffer); - CALLOC(buffer, uint8_t, BUFSIZE) - buffer_len = BUFSIZE; - u32_to_u8(unicode_buffer, column_width, - buffer, &buffer_len); - - if (buffer && buffer_len>0) + uint8_t* pch_end; + pline_len = u8_strlen(line); + ch_len = u8_mbtouc(&uch, pline, pline_len); + if (border_mode) { - fprintf(stdout, "%s", buffer); - written_columns += column_width; + pch_end = pline + ch_len; + if (WITHIN_SINGLE_COLUMN) + { + while (pline != pch_end) + printf("%c", *pline++); + current_rune_column++; + } + pline = pch_end; + colno += ch_len; } - - // Not the last column - if (current_table_column+1 < table_columns) + else if (ch_len <= 0) { - if (handle_ansi) - sgr_chars = ansi_lookahead(unicode_buffer, column_width); - for (size_t i = 0; i < - max_table_column_width-column_width+sgr_chars; i++) + if (WITHIN_COLUMN) { - fprintf(stdout, " "); + printf("%c", *pline++); + current_rune_column++; + } + colno++; + } + else if (uch == '\t' && expand_tabs) + { + while (WITHIN_COLUMN) + { + printf(" "); + current_rune_column++; + if (current_rune_column % tab_length == 0) + break; + } + pline++; + colno++; + } + else if (uch == delimiter && !quote) + { + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_OFF); + + while (WITHIN_COLUMN) + { + printf(" "); + current_rune_column++; } - if (handle_ansi) - fprintf(stdout, "%s", ANSI_SGR_RESET); - written_columns += max_table_column_width - column_width; - for (size_t leftover_column = 0; - leftover_column < table_columns-current_table_column-1; - leftover_column++) + if (current_table_column != table_columns-1) { - fprintf(stdout, "%s", - table_inner_symbols[current_inner_symbol_set][1]); - - size_t table_column_width = max_table_column_width; - if (leftover_column == table_columns-current_table_column-2) - table_column_width = max_last_table_column_width; - for (size_t i = 0; i < table_column_width; i++) - fprintf(stdout, " "); + printf("%s", table_inner_symbols[current_inner_symbol_set][1]); + current_rune_column++; + current_table_column++; } + + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_ON); + + pline += ch_len; + colno += ch_len; } - // Last one else { - for (size_t i = written_columns+1; i < rune_columns; i++) - fprintf(stdout, " "); - if (handle_ansi) - fprintf(stdout, "%s", ANSI_SGR_RESET); + pch_end = pline + ch_len; + if (WITHIN_COLUMN) + { + while (pline != pch_end) + printf("%c", *pline++); + current_rune_column++; + } + pline = pch_end; + colno += ch_len; } - written_columns = rune_columns; } - // Handle a line with no delimiter - else + } + + if (handle_ansi && lineno == 0) + printf("%s", ANSI_SGR_BOLD_OFF); + + while (current_table_column < table_columns-1) + { + while (WITHIN_COLUMN) { - column_width = max_table_column_width; - - fprintf(stdout, "%s", table_symbols[current_symbol_set][3]); - - substr_finish = unicode_line_len; - if (unicode_buffer) - free(unicode_buffer); - unicode_buffer = u32_substr(unicode_line, unicode_line_len, - substr_start, substr_finish, &unicode_buffer_len); - if (unicode_buffer_len < column_width + 1) - column_width = unicode_buffer_len-1; - - if (buffer) - free(buffer); - CALLOC(buffer, uint8_t, BUFSIZE) - buffer_len = BUFSIZE; - u32_to_u8(unicode_buffer, column_width, buffer, &buffer_len); - - if (buffer && buffer_len>0) - fprintf(stdout, "%s", buffer); - - if (handle_ansi) - sgr_chars = ansi_lookahead(unicode_buffer, column_width); - for (size_t i = 0; i + column_width + table_columns + 1 < - rune_columns + sgr_chars; i++) - fprintf(stdout, " "); + printf(" "); + current_rune_column++; } - if (handle_ansi) - fprintf(stdout, "%s", ANSI_SGR_RESET); - fprintf(stdout, "%s\n", table_symbols[current_symbol_set][5]); - - if (unicode_line) - free(unicode_line); + printf("%s", table_inner_symbols[current_inner_symbol_set][1]); + current_table_column++; + current_rune_column++; } + while (current_rune_column < rune_columns-2) + { + printf(" "); + current_rune_column++; + } + + printf("%s\n", table_symbols[current_symbol_set][5]); + + lineno++; } - while (!feof(input)); fclose(input); - if (buffer) - free(buffer); - if (line) - free(line); - if (unicode_buffer) - free(unicode_buffer); + free(line); - if (table_columns) + current_table_column = 0; + for (size_t col = 0; col < rune_columns; col++) { - fprintf(stdout, "%s", table_symbols[current_symbol_set][6]); - size_t table_column = 0; - col = 0; - while (col++ < rune_columns-2) - if (table_column < table_columns-1 - && col % (max_table_column_width+1) == 0) - { - fprintf(stdout, "%s", - table_inner_symbols[current_inner_symbol_set][2]); - table_column++; - } - else - fprintf(stdout, "%s", table_symbols[current_symbol_set][7]); - fprintf(stdout, "%s\n", table_symbols[current_symbol_set][8]); + if (col == 0) + printf("%s", table_symbols[current_symbol_set][6]); + else if (col == rune_columns-1) + printf("%s", table_symbols[current_symbol_set][8]); + else if (current_table_column < table_columns-1 + && col > 1 + && col % (max_table_column_width+1) == 0) + { + current_table_column++; + printf("%s", table_inner_symbols[current_inner_symbol_set][2]); + } + else + printf("%s", table_symbols[current_symbol_set][7]); } + printf("\n"); return 0; } diff --git a/test/cp-help.sh b/test/cp-help.sh @@ -2,4 +2,4 @@ SRCDIR=. -cp --help | $SRCDIR/table +cp --help | $SRCDIR/table -n -t diff --git a/test/file.sh b/test/file.sh @@ -1,2 +1,2 @@ #!/bin/sh -file * | sed -e's/ \+/ /g' | table -d : -c 110 +file * | sed -e's/ \+/ /g' | table -d : -c 110 -n diff --git a/test/ls-2col.sh b/test/ls-2col.sh @@ -1,2 +1,3 @@ #!/bin/sh -ls -l | tail -n +2 | sed -e's/ \+/ /g' | cut -d' ' -f1,9 | table -d " " -c 30 +LC_ALL=C ls -l | tail -n +2 | sed -e's/ \+/ /g' | cut -d' ' -f1,9 \ + | table -d " " -c 30 -n diff --git a/test/ls-adv.sh b/test/ls-adv.sh @@ -1,2 +1,3 @@ #!/bin/sh -ls -l | tail -n 15 | sed -e's/ \+/ /g' -e's/\t/ /g' | table -d " " -c 120 -s ss +LC_ALL=C ls -l | tail -n 15 | sed -e's/ \+/ /g' -e's/\t/ /g' \ + | table -d " " -c 120 -s ss -n diff --git a/test/ls-multicolumn.sh b/test/ls-multicolumn.sh @@ -1,2 +1,2 @@ #!/bin/sh -ls -m /bin | sed -e's/,$//g' | table -c 160 +LC_ALL=C ls -m /bin | sed -e's/,$//g' -e's/, /,/g' | table -c 160 -n diff --git a/test/lsb_release.sh b/test/lsb_release.sh @@ -2,4 +2,4 @@ SRCDIR=. -lsb_release -a 2>/dev/null | $SRCDIR/table +lsb_release -a 2>/dev/null | $SRCDIR/table -n -t diff --git a/test/neofetch.sh b/test/neofetch.sh @@ -2,4 +2,4 @@ SRCDIR=. -neofetch | $SRCDIR/table -a -b +neofetch | $SRCDIR/table -b -t diff --git a/test/uname-a.sh b/test/uname-a.sh @@ -2,4 +2,4 @@ SRCDIR=. -uname -a | $SRCDIR/table +uname -a | $SRCDIR/table -d ' ' -c 160 -n