tabular

Flexible input tabulator
git clone https://git.sinitax.com/sinitax/tabular
Log | Files | Refs | Submodules | sfeed.txt

commit 446c7a8998749b9ff993bd418c4df482f5553a68
parent a019364e51a8831fc2e7ea3ac57dbc56df5e2a5b
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 30 Jul 2023 06:08:58 +0200

Rename main.c to tabular.c and remove default pad

Diffstat:
Mbuild.jst.tmpl | 2+-
Dmain.c | 483-------------------------------------------------------------------------------
Atabular.c | 488+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 489 insertions(+), 484 deletions(-)

diff --git a/build.jst.tmpl b/build.jst.tmpl @@ -29,7 +29,7 @@ target lib/liballoc/build/liballoc.a just lib/liballoc target tabular - cc main.c lib/libdvec/build/libdvec.a lib/libhmap/build/libhmap.a + cc tabular.c lib/libdvec/build/libdvec.a lib/libhmap/build/libhmap.a lib/libtabular/build/libtabular.a lib/liballoc/build/liballoc.a command clean diff --git a/main.c b/main.c @@ -1,483 +0,0 @@ -#include "tabular.h" -#include "hmap.h" -#include "dvec.h" -#include "allocator.h" - -#include <sys/ioctl.h> -#include <unistd.h> -#include <err.h> -#include <string.h> -#include <stdbool.h> -#include <stdarg.h> -#include <stdio.h> - -#define ARRLEN(x) (sizeof(x)/sizeof(*(x))) - -struct col { - struct tabular_col tabular; - const char *hide_out; -}; - -static bool print_style(FILE *file, const struct tabular_cfg *cfg, - const struct tabular_row *row, const struct tabular_col *col); - -static struct tabular_row *row_gen(const struct tabular_user *user); -static char *col_str(const struct tabular_user *user_row, - const struct tabular_user *user_col); -static bool col_hidden(const struct tabular_user *user_row, - const struct tabular_user *user_col); - -static const struct allocator *ga = &stdlib_strict_heap_allocator; - -static bool hide_empty = true; -static bool skip_empty_lines = true; -static bool skip_empty_entries = false; -static bool header_underline = false; - -static char entry_sep = '\t'; -static char line_sep = '\n'; - -static struct hmap colmap; -static struct dvec cols; -static size_t colcnt = 0; - -static struct dvec input; -static size_t input_off = 0; -static bool input_done = false; - -static struct dvec line; -static struct dvec entries; -static size_t linecnt = 0; - -static struct tabular_cfg cfg = { - .colors = 256, - - .columns = NULL, - .column_count = 0, - - .fit_rows = false, - - .hsep = "│ ", - .vsep = "─", - .xsep = "┼─", - - .outw = 0, - .outh = 0, - - .lpad = 1, - .rpad = 1, - - .user.ptr = NULL, - .row_gen = row_gen, - .print_style = print_style, - - .skip_lines = 4, - - .allocator = &stdlib_heap_allocator -}; - -static void __attribute__((noreturn)) -die(const char *fmt, ...) -{ - va_list ap; - - fputs("tabular: ", stderr); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - if (*fmt && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - - exit(1); -} - -static bool -print_style(FILE *file, const struct tabular_cfg *cfg, - const struct tabular_row *row, const struct tabular_col *col) -{ - if (cfg->colors == 256) { - if (!col) { /* separators */ - fprintf(file, "\x1b[90m"); - return true; - } else if (!row) { /* header */ - fprintf(file, "\x1b[1m"); - if (header_underline) - fprintf(file, "\x1b[4m"); - return true; - } - } - - return false; -} - -static bool -read_line(void) -{ - void *tok, *sep, *end, *line_end; - ssize_t n; - - if (input_done) return false; - - while (1) { - while (!(line_end = memchr(input.data + input_off, - line_sep, input.len - input_off))) { - dvec_rm(&input, 0, input_off); - input_off = 0; - dvec_reserve(&input, input.len + BUFSIZ + 1); - n = read(0, input.data + input.len, BUFSIZ); - if (n <= 0) { - input_done = true; - line_end = input.data + input.len; - break; - } - input.len += (size_t) n; - } - if (input_done && !input.len) return false; - - if (line_end != input.data || !skip_empty_lines) - break; - input_off += 1; - } - *(char *)line_end = '\0'; - - dvec_clear(&entries); - tok = input.data + input_off; - while (tok) { - sep = memchr(tok, entry_sep, - (size_t) (input.data + input.len - tok)); - end = (sep && sep < line_end) ? sep : line_end; - *(char *)end = '\0'; - if (tok != end || !skip_empty_entries) { - dvec_add_back(&entries, 1); - *(size_t *)dvec_back(&entries) = - (size_t) (tok - input.data) - input_off; - } - tok = (sep && sep < line_end) ? sep + 1 : NULL; - } - - dvec_clear(&line); - dvec_add_back(&line, (size_t) (line_end - input.data) - input_off + 1); - memcpy(line.data, input.data + input_off, line.len); - input_off += line.len; - - return true; -} - -static struct tabular_row * -row_gen(const struct tabular_user *user_cfg) -{ - struct tabular_row *row; - size_t i; - int rc; - - if (!read_line()) return NULL; - - row = tabular_alloc_row(&cfg, &rc, - (struct tabular_user) { .id = linecnt++ }); - if (!row) errx(1, "tabular_append_row %i", rc); - - for (i = 0; i < cols.len; i++) { - tabular_load_row_entry_hidden(&cfg, row, i); - tabular_load_row_entry_str(&cfg, row, i); - } - - return row; -} - -static char * -col_str(const struct tabular_user *user_row, const struct tabular_user *user_col) -{ - struct col *col = user_col->ptr; - char *str; - size_t *off; - - if (col->tabular.user.id >= dvec_len(&entries)) - return NULL; - - off = dvec_at(&entries, col->tabular.user.id); - str = strdup(line.data + *off); - if (!str) die("strdup:"); - - return str; -} - -static bool -col_hidden(const struct tabular_user *user, const struct tabular_user *user_col) -{ - struct col *col = user_col->ptr; - size_t *off; - - if (col->tabular.user.id >= dvec_len(&entries)) - return hide_empty; - - off = dvec_at(&entries, col->tabular.user.id); - - if (col->hide_out && !strcmp(line.data + *off, col->hide_out)) - return true; - - return hide_empty && !strcmp(line.data + *off, ""); -} - -static bool -bool_arg(const char *arg, const char *name) -{ - if (!strcmp(arg, "1") || !strcmp(arg, "true")) - return true; - else if (!strcmp(arg, "0") || !strcmp(arg, "false")) - return false; - else - die("bad %s", name); -} - -static void -parse(int argc, const char **argv) -{ - struct col *col; - struct tabular_col *tcol; - const char **arg, **dst; - struct hmap_link *link; - struct hmap_iter iter; - struct winsize ws; - char namebuf[64]; - char *end, *c, *upper; - size_t len; - int rc, n; - - hmap_init(&colmap, 16, hmap_str_hash, hmap_str_keycmp, ga); - dvec_init(&cols, sizeof(struct tabular_col), 0, ga); - - /* get general flags */ - for (dst = arg = argv + 1; *arg; arg++) { - if (!strcmp(*arg, "--hsep")) { - if (!*++arg) die("missing args"); - cfg.hsep = *arg; - } else if (!strcmp(*arg, "--vsep")) { - if (!*++arg) die("missing args"); - cfg.vsep = *arg; - } else if (!strcmp(*arg, "--xsep")) { - if (!*++arg) die("missing args"); - cfg.xsep = *arg; - } else if (!strcmp(*arg, "--lpad")) { - if (!*++arg) die("missing args"); - cfg.lpad = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg, "--rpad")) { - if (!*++arg) die("missing args"); - cfg.rpad = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg, "--skip-lines")) { - if (!*++arg) die("missing args"); - cfg.skip_lines = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg, "--fit-rows")) { - if (!*++arg) die("missing args"); - cfg.fit_rows = bool_arg(*arg, "--fit-rows"); - } else if (!strcmp(*arg, "--outw")) { - if (!*++arg) die("missing args"); - cfg.outw = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg, "--outh")) { - if (!*++arg) die("missing args"); - cfg.outh = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg, "--skip-lines")) { - if (!*++arg) die("missing args"); - cfg.skip_lines = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg, "--hide-empty")) { - if (!*++arg) die("missing args"); - hide_empty = bool_arg(*arg, arg[-1]); - } else if (!strcmp(*arg, "--line-sep")) { - if (!*++arg || !**arg || *(*arg+1)) - die("missing args"); - line_sep = **arg; - } else if (!strcmp(*arg, "--entry-sep")) { - if (!*++arg || !**arg || *(*arg+1)) - die("missing args"); - entry_sep = **arg; - } else if (!strcmp(*arg, "--header-underline")) { - if (!*++arg) die("missing args"); - header_underline = bool_arg(*arg, arg[-1]); - } else if (!strcmp(*arg, "--skip-empty-entries")) { - if (!*++arg) die("missing args"); - skip_empty_entries = bool_arg(*arg, arg[-1]); - } else if (!strcmp(*arg, "--skip-empty-lines")) { - if (!*++arg) die("missing args"); - skip_empty_lines = bool_arg(*arg, arg[-1]); - if (!*++arg) die("missing args"); - } else if (!strcmp(*arg, "--colors")) { - if (!*++arg) die("missing args"); - cfg.colors = (int) strtol(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else { - *dst++ = *arg; - } - } - *dst = NULL; - - if (!cfg.outw || !cfg.outh) { - rc = ioctl(1, TIOCGWINSZ, &ws); - if (!rc) { - cfg.outw = ws.ws_col; - cfg.outh = ws.ws_row; - } else { - cfg.outw = 80; - cfg.outh = 26; - } - } - - /* get columns */ - for (dst = arg = argv + 1; *arg; arg++) { - if (!strcmp(*arg, "--col")) { - if (!*++arg) die("missing args"); - col = ga->alloc(ga, sizeof(struct col), NULL); - tcol = &col->tabular; - - if (strlen(*arg) > 63) die("col name too long"); - strncpy(namebuf, *arg, 64); - for (c = namebuf; *c; c++) - *c = (*c >= 'a' && *c <= 'z') ? *c - 32 : *c; - upper = strdup(namebuf); - if (!upper) die("strdup:"); - - col->hide_out = NULL; - tcol->name = *arg; - tcol->align = TABULAR_ALIGN_LEFT; - tcol->essential = true; - tcol->is_hidden = col_hidden; - tcol->to_str = col_str; - tcol->lpad = 0; - tcol->rpad = 0; - tcol->minwidth = strlen(tcol->name); - tcol->maxwidth = cfg.outw; - tcol->strategy = TABULAR_WRAP_WORDAWARE; - tcol->squashable = false; - tcol->user.id = colcnt++; - rc = hmap_add(&colmap, - (struct hmap_key) { .p = upper }, - (struct hmap_val) { .p = col }); - if (rc) die("duplicate col %s", tcol->name); - } else { - *dst++ = *arg; - } - } - *dst = NULL; - - /* set column attrs */ - for (dst = arg = argv + 1; *arg; arg++) { - if (!strncmp(*arg, "--", 2)) { - rc = sscanf(*arg, "--%63[^-]-%n", namebuf, &n); - if (rc != 1 || *(*arg+n-1) != '-') goto skip; - link = hmap_get(&colmap, - (struct hmap_key) { .p = namebuf }); - if (!link) goto skip; - col = link->value._p; - tcol = &col->tabular; - - if (!strcmp(*arg + n, "lpad")) { - if (!*++arg) die("missing args"); - tcol->lpad = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - len = tcol->lpad + strlen(tcol->name) + tcol->rpad; - if (tcol->minwidth < len) - tcol->minwidth = len; - } else if (!strcmp(*arg + n, "rpad")) { - if (!*++arg) die("missing args"); - tcol->rpad = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - len = tcol->lpad + strlen(tcol->name) + tcol->rpad; - if (tcol->minwidth < len) - tcol->minwidth = len; - } else if (!strcmp(*arg + n, "align")) { - if (!*++arg) die("missing args"); - if (!strcmp(*arg, "left")) { - tcol->align = TABULAR_ALIGN_LEFT; - } else if (!strcmp(*arg, "right")) { - tcol->align = TABULAR_ALIGN_RIGHT; - } else if (!strcmp(*arg, "center")) { - tcol->align = TABULAR_ALIGN_CENTER; - } else { - die("bad %s", arg[-1]); - } - } else if (!strcmp(*arg + n, "squashable")) { - if (!*++arg) die("missing args"); - tcol->squashable = bool_arg(*arg, arg[-1]); - } else if (!strcmp(*arg + n, "essential")) { - if (!*++arg) die("missing args"); - tcol->essential = bool_arg(*arg, arg[-1]); - } else if (!strcmp(*arg + n, "minwidth")) { - if (!*++arg) die("missing args"); - tcol->minwidth = strtoul(*arg, &end, 10); - if (tcol->minwidth > tcol->maxwidth) - tcol->maxwidth = tcol->minwidth; - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg + n, "maxwidth")) { - if (!*++arg) die("missing args"); - tcol->maxwidth = strtoul(*arg, &end, 10); - if (end && *end) die("bad %s", arg[-1]); - } else if (!strcmp(*arg + n, "strategy")) { - if (!*++arg) die("missing args"); - if (!strcmp(*arg, "word-aware")) { - tcol->strategy = TABULAR_WRAP_WORDAWARE; - } else if (!strcmp(*arg, "wrap")) { - tcol->strategy = TABULAR_WRAP; - } else if (!strcmp(*arg, "trunc")) { - tcol->strategy = TABULAR_TRUNC; - } else { - die("bad %s", arg[-1]); - } - } else if (!strcmp(*arg + n, "hide")) { - if (!*++arg) die("missing args"); - col->hide_out = *arg; - } else { - goto skip; - } - } else { -skip: - *dst++ = *arg; - } - } - *dst = NULL; - - if (argv[1]) die("unused argument '%s'", argv[1]); - - dvec_add_back(&cols, colcnt); - for (HMAP_ITER(&colmap, iter)) { - col = iter.link->value._p; - tcol = dvec_at(&cols, col->tabular.user.id); - memcpy(tcol, &col->tabular, sizeof(struct tabular_col)); - tcol->user.ptr = col; - } - cfg.columns = cols.data; - cfg.column_count = cols.len; -} - -int -main(int argc, const char **argv) -{ - struct tabular_row *rows; - struct tabular_stats stats; - int rc; - - parse(argc, argv); - - dvec_init(&line, 1, 1024, ga); - dvec_init(&input, 1, 1024, ga); - dvec_init(&entries, sizeof(size_t), 1, ga); - - rows = NULL; - rc = tabular_format(stdout, &cfg, &stats, &rows); - if (rc) errx(1, "tabular_format (%i)", rc); - - printf("\n%lu lines, %lu rows", - stats.lines_used, stats.rows_displayed); - if (stats.rows_truncated) printf(" (rows truncated)"); - if (stats.cols_truncated) printf(" (cols truncated)"); - printf("\n"); - - tabular_free_rows(&cfg, rows); -} diff --git a/tabular.c b/tabular.c @@ -0,0 +1,488 @@ +#include "tabular.h" +#include "hmap.h" +#include "dvec.h" +#include "allocator.h" + +#include <sys/ioctl.h> +#include <unistd.h> +#include <err.h> +#include <string.h> +#include <stdbool.h> +#include <stdarg.h> +#include <stdio.h> + +#define ARRLEN(x) (sizeof(x)/sizeof(*(x))) + +struct col { + struct tabular_col tabular; + const char *hide_out; +}; + +static bool print_style(FILE *file, const struct tabular_cfg *cfg, + const struct tabular_row *row, const struct tabular_col *col); + +static struct tabular_row *row_gen(const struct tabular_user *user); +static char *col_str(const struct tabular_user *user_row, + const struct tabular_user *user_col); +static bool col_hidden(const struct tabular_user *user_row, + const struct tabular_user *user_col); + +static const struct allocator *ga = &stdlib_strict_heap_allocator; + +static bool hide_empty = true; +static bool skip_empty_lines = true; +static bool skip_empty_entries = false; +static bool header_underline = false; + +static char entry_sep = '\t'; +static char line_sep = '\n'; + +static struct hmap colmap; +static struct dvec cols; +static size_t colcnt = 0; + +static struct dvec input; +static size_t input_off = 0; +static bool input_done = false; + +static struct dvec line; +static struct dvec entries; +static size_t linecnt = 0; + +static struct tabular_cfg cfg = { + .colors = 256, + + .columns = NULL, + .column_count = 0, + + .fit_rows = false, + + .hsep = "│", + .vsep = "─", + .xsep = "┼", + + .outw = 0, + .outh = 0, + + .lpad = 0, + .rpad = 0, + + .user.ptr = NULL, + .row_gen = row_gen, + .print_style = print_style, + + .skip_lines = 4, + + .allocator = &stdlib_heap_allocator +}; + +static void __attribute__((noreturn)) +die(const char *fmt, ...) +{ + va_list ap; + + fputs("tabular: ", stderr); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +static bool +print_style(FILE *file, const struct tabular_cfg *cfg, + const struct tabular_row *row, const struct tabular_col *col) +{ + if (cfg->colors == 256) { + if (!col) { /* separators */ + fprintf(file, "\x1b[90m"); + return true; + } else if (!row) { /* header */ + fprintf(file, "\x1b[1m"); + if (header_underline) + fprintf(file, "\x1b[4m"); + return true; + } + } + + return false; +} + +static bool +read_line(void) +{ + void *tok, *sep, *end, *line_end; + ssize_t n; + + if (input_done) return false; + + while (1) { + while (!(line_end = memchr(input.data + input_off, + line_sep, input.len - input_off))) { + dvec_rm(&input, 0, input_off); + input_off = 0; + dvec_reserve(&input, input.len + BUFSIZ + 1); + n = read(0, input.data + input.len, BUFSIZ); + if (n <= 0) { + input_done = true; + line_end = input.data + input.len; + break; + } + input.len += (size_t) n; + } + if (input_done && !input.len) return false; + + if (line_end != input.data || !skip_empty_lines) + break; + input_off += 1; + } + *(char *)line_end = '\0'; + + dvec_clear(&entries); + tok = input.data + input_off; + while (tok) { + sep = memchr(tok, entry_sep, + (size_t) (input.data + input.len - tok)); + end = (sep && sep < line_end) ? sep : line_end; + *(char *)end = '\0'; + if (tok != end || !skip_empty_entries) { + dvec_add_back(&entries, 1); + *(size_t *)dvec_back(&entries) = + (size_t) (tok - input.data) - input_off; + } + tok = (sep && sep < line_end) ? sep + 1 : NULL; + } + + dvec_clear(&line); + dvec_add_back(&line, (size_t) (line_end - input.data) - input_off + 1); + memcpy(line.data, input.data + input_off, line.len); + input_off += line.len; + + return true; +} + +static struct tabular_row * +row_gen(const struct tabular_user *user_cfg) +{ + struct tabular_row *row; + size_t i; + int rc; + + if (!read_line()) return NULL; + + row = tabular_alloc_row(&cfg, &rc, + (struct tabular_user) { .id = linecnt++ }); + if (!row) errx(1, "tabular_append_row %i", rc); + + for (i = 0; i < cols.len; i++) { + tabular_load_row_entry_hidden(&cfg, row, i); + tabular_load_row_entry_str(&cfg, row, i); + } + + return row; +} + +static char * +col_str(const struct tabular_user *user_row, const struct tabular_user *user_col) +{ + struct col *col = user_col->ptr; + char *str; + size_t *off; + + if (col->tabular.user.id >= dvec_len(&entries)) + return NULL; + + off = dvec_at(&entries, col->tabular.user.id); + str = strdup(line.data + *off); + if (!str) die("strdup:"); + + return str; +} + +static bool +col_hidden(const struct tabular_user *user, const struct tabular_user *user_col) +{ + struct col *col = user_col->ptr; + size_t *off; + + if (col->tabular.user.id >= dvec_len(&entries)) + return hide_empty; + + off = dvec_at(&entries, col->tabular.user.id); + + if (col->hide_out && !strcmp(line.data + *off, col->hide_out)) + return true; + + return hide_empty && !strcmp(line.data + *off, ""); +} + +static bool +bool_arg(const char *arg, const char *name) +{ + if (!strcmp(arg, "1") || !strcmp(arg, "true")) + return true; + else if (!strcmp(arg, "0") || !strcmp(arg, "false")) + return false; + else + die("bad %s", name); +} + +static void +parse(int argc, const char **argv) +{ + struct col *col; + struct tabular_col *tcol; + const char **arg, **dst; + struct hmap_link *link; + struct hmap_iter iter; + struct winsize ws; + char namebuf[64]; + char *end, *c, *upper; + size_t len; + int rc, n; + + hmap_init(&colmap, 16, hmap_str_hash, hmap_str_keycmp, ga); + dvec_init(&cols, sizeof(struct tabular_col), 0, ga); + + /* get general flags */ + for (dst = arg = argv + 1; *arg; arg++) { + if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) { + fprintf(stderr, "Usage: tabular " + "[--col NAME].. [--NAME-OPT VAL]..\n"); + exit(1); + } else if (!strcmp(*arg, "--hsep")) { + if (!*++arg) die("missing args"); + cfg.hsep = *arg; + } else if (!strcmp(*arg, "--vsep")) { + if (!*++arg) die("missing args"); + cfg.vsep = *arg; + } else if (!strcmp(*arg, "--xsep")) { + if (!*++arg) die("missing args"); + cfg.xsep = *arg; + } else if (!strcmp(*arg, "--lpad")) { + if (!*++arg) die("missing args"); + cfg.lpad = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg, "--rpad")) { + if (!*++arg) die("missing args"); + cfg.rpad = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg, "--skip-lines")) { + if (!*++arg) die("missing args"); + cfg.skip_lines = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg, "--fit-rows")) { + if (!*++arg) die("missing args"); + cfg.fit_rows = bool_arg(*arg, "--fit-rows"); + } else if (!strcmp(*arg, "--outw")) { + if (!*++arg) die("missing args"); + cfg.outw = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg, "--outh")) { + if (!*++arg) die("missing args"); + cfg.outh = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg, "--skip-lines")) { + if (!*++arg) die("missing args"); + cfg.skip_lines = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg, "--hide-empty")) { + if (!*++arg) die("missing args"); + hide_empty = bool_arg(*arg, arg[-1]); + } else if (!strcmp(*arg, "--line-sep")) { + if (!*++arg || !**arg || *(*arg+1)) + die("missing args"); + line_sep = **arg; + } else if (!strcmp(*arg, "--entry-sep")) { + if (!*++arg || !**arg || *(*arg+1)) + die("missing args"); + entry_sep = **arg; + } else if (!strcmp(*arg, "--header-underline")) { + if (!*++arg) die("missing args"); + header_underline = bool_arg(*arg, arg[-1]); + } else if (!strcmp(*arg, "--skip-empty-entries")) { + if (!*++arg) die("missing args"); + skip_empty_entries = bool_arg(*arg, arg[-1]); + } else if (!strcmp(*arg, "--skip-empty-lines")) { + if (!*++arg) die("missing args"); + skip_empty_lines = bool_arg(*arg, arg[-1]); + if (!*++arg) die("missing args"); + } else if (!strcmp(*arg, "--colors")) { + if (!*++arg) die("missing args"); + cfg.colors = (int) strtol(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else { + *dst++ = *arg; + } + } + *dst = NULL; + + if (!cfg.outw || !cfg.outh) { + rc = ioctl(1, TIOCGWINSZ, &ws); + if (!rc) { + cfg.outw = ws.ws_col; + cfg.outh = ws.ws_row; + } else { + cfg.outw = 80; + cfg.outh = 26; + } + } + + /* get columns */ + for (dst = arg = argv + 1; *arg; arg++) { + if (!strcmp(*arg, "--col")) { + if (!*++arg) die("missing args"); + col = ga->alloc(ga, sizeof(struct col), NULL); + tcol = &col->tabular; + + if (strlen(*arg) > 63) die("col name too long"); + strncpy(namebuf, *arg, 64); + for (c = namebuf; *c; c++) + *c = (*c >= 'a' && *c <= 'z') ? *c - 32 : *c; + upper = strdup(namebuf); + if (!upper) die("strdup:"); + + col->hide_out = NULL; + tcol->name = *arg; + tcol->align = TABULAR_ALIGN_LEFT; + tcol->essential = true; + tcol->is_hidden = col_hidden; + tcol->to_str = col_str; + tcol->lpad = 0; + tcol->rpad = 0; + tcol->minwidth = strlen(tcol->name); + tcol->maxwidth = cfg.outw; + tcol->strategy = TABULAR_WRAP_WORDAWARE; + tcol->squashable = false; + tcol->user.id = colcnt++; + rc = hmap_add(&colmap, + (struct hmap_key) { .p = upper }, + (struct hmap_val) { .p = col }); + if (rc) die("duplicate col %s", tcol->name); + } else { + *dst++ = *arg; + } + } + *dst = NULL; + + /* set column attrs */ + for (dst = arg = argv + 1; *arg; arg++) { + if (!strncmp(*arg, "--", 2)) { + n = 0; + rc = sscanf(*arg, "--%63[^-]-%n", namebuf, &n); + if (rc != 1 || *(*arg+n-1) != '-') goto skip; + link = hmap_get(&colmap, + (struct hmap_key) { .p = namebuf }); + if (!link) goto skip; + col = link->value._p; + tcol = &col->tabular; + + if (!strcmp(*arg + n, "lpad")) { + if (!*++arg) die("missing args"); + tcol->lpad = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + len = tcol->lpad + strlen(tcol->name) + tcol->rpad; + if (tcol->minwidth < len) + tcol->minwidth = len; + } else if (!strcmp(*arg + n, "rpad")) { + if (!*++arg) die("missing args"); + tcol->rpad = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + len = tcol->lpad + strlen(tcol->name) + tcol->rpad; + if (tcol->minwidth < len) + tcol->minwidth = len; + } else if (!strcmp(*arg + n, "align")) { + if (!*++arg) die("missing args"); + if (!strcmp(*arg, "left")) { + tcol->align = TABULAR_ALIGN_LEFT; + } else if (!strcmp(*arg, "right")) { + tcol->align = TABULAR_ALIGN_RIGHT; + } else if (!strcmp(*arg, "center")) { + tcol->align = TABULAR_ALIGN_CENTER; + } else { + die("bad %s", arg[-1]); + } + } else if (!strcmp(*arg + n, "squashable")) { + if (!*++arg) die("missing args"); + tcol->squashable = bool_arg(*arg, arg[-1]); + } else if (!strcmp(*arg + n, "essential")) { + if (!*++arg) die("missing args"); + tcol->essential = bool_arg(*arg, arg[-1]); + } else if (!strcmp(*arg + n, "minwidth")) { + if (!*++arg) die("missing args"); + tcol->minwidth = strtoul(*arg, &end, 10); + if (tcol->minwidth > tcol->maxwidth) + tcol->maxwidth = tcol->minwidth; + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg + n, "maxwidth")) { + if (!*++arg) die("missing args"); + tcol->maxwidth = strtoul(*arg, &end, 10); + if (end && *end) die("bad %s", arg[-1]); + } else if (!strcmp(*arg + n, "strategy")) { + if (!*++arg) die("missing args"); + if (!strcmp(*arg, "word-aware")) { + tcol->strategy = TABULAR_WRAP_WORDAWARE; + } else if (!strcmp(*arg, "wrap")) { + tcol->strategy = TABULAR_WRAP; + } else if (!strcmp(*arg, "trunc")) { + tcol->strategy = TABULAR_TRUNC; + } else { + die("bad %s", arg[-1]); + } + } else if (!strcmp(*arg + n, "hide")) { + if (!*++arg) die("missing args"); + col->hide_out = *arg; + } else { + goto skip; + } + } else { +skip: + *dst++ = *arg; + } + } + *dst = NULL; + + if (argv[1]) die("unused argument '%s'", argv[1]); + + dvec_add_back(&cols, colcnt); + for (HMAP_ITER(&colmap, iter)) { + col = iter.link->value._p; + tcol = dvec_at(&cols, col->tabular.user.id); + memcpy(tcol, &col->tabular, sizeof(struct tabular_col)); + tcol->user.ptr = col; + } + cfg.columns = cols.data; + cfg.column_count = cols.len; +} + +int +main(int argc, const char **argv) +{ + struct tabular_row *rows; + struct tabular_stats stats; + int rc; + + parse(argc, argv); + + dvec_init(&line, 1, 1024, ga); + dvec_init(&input, 1, 1024, ga); + dvec_init(&entries, sizeof(size_t), 1, ga); + + rows = NULL; + rc = tabular_format(stdout, &cfg, &stats, &rows); + if (rc) errx(1, "tabular_format (%i)", rc); + + printf("\n%lu lines, %lu rows", + stats.lines_used, stats.rows_displayed); + if (stats.rows_truncated) printf(" (rows truncated)"); + if (stats.cols_truncated) printf(" (cols truncated)"); + printf("\n"); + + tabular_free_rows(&cfg, rows); +}