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:
M | build.jst.tmpl | | | 2 | +- |
D | main.c | | | 483 | ------------------------------------------------------------------------------- |
A | tabular.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);
+}