libtabular-c

C tabular formatting library
git clone https://git.sinitax.com/sinitax/libtabular-c
Log | Files | Refs | Submodules | sfeed.txt

commit 466a4dcdf8adc4a08c93080e59a7c401779462df
parent 0367626d37f9352378cab11f8b76cc42c78060b5
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed,  7 Jun 2023 04:58:36 +0200

Add squashable column attribution and fix rows_truncated stat

Diffstat:
M.gitignore | 2++
Minclude/tabular.h | 13++++++++-----
Msrc/tabular.c | 81++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/test.c | 21+++++++++++----------
4 files changed, 72 insertions(+), 45 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -2,3 +2,5 @@ compile_commands.json build build.jst .cache +.gdb_history +vgcore* diff --git a/include/tabular.h b/include/tabular.h @@ -17,14 +17,14 @@ enum { enum { TABULAR_TRUNC, - TABULAR_SQUASH, - TABULAR_SQUASH_WORDAWARE + TABULAR_WRAP, + TABULAR_WRAP_WORDAWARE }; enum { TABULAR_ENTRY_HIDDEN, - TABULAR_ENTRY_STR_AVAIL, - TABULAR_ENTRY_HIDDEN_AVAIL, + TABULAR_ENTRY_STR_SET, + TABULAR_ENTRY_HIDDEN_SET, }; struct tabular_user { @@ -72,8 +72,11 @@ struct tabular_col { /* content packing strategy */ int strategy; - /* omiting column due to space disallowed */ + /* omiting column due to col contraints disallowed */ bool essential; + + /* reducing length due to col contraints allowed */ + bool squashable; }; struct tabular_cfg { diff --git a/src/tabular.c b/src/tabular.c @@ -12,6 +12,8 @@ #include <stdint.h> #include <stddef.h> +#define BIT(i) (1U << (i)) + struct col_state { size_t width; size_t written; @@ -39,7 +41,7 @@ static void calc_word_aware(struct tabular_entry *entry, size_t maxwidth, size_t *offset, size_t *width, size_t *lines); static size_t recalc_col_word_aware(const struct tabular_cfg *cfg, struct tabular_row *rows, size_t limit, - size_t col, size_t maxwidth, size_t remaining); + size_t col, size_t maxwidth); static size_t calc_row_width(struct fmt_state *fmt); static size_t calc_output_lines(const struct tabular_cfg *cfg, struct fmt_state *fmt, struct tabular_row *rows, size_t limit); @@ -138,7 +140,7 @@ calc_word_aware(struct tabular_entry *entry, size_t maxwidth, size_t recalc_col_word_aware(const struct tabular_cfg *cfg, struct tabular_row *rows, size_t limit, size_t col, - size_t maxwidth, size_t remaining) + size_t maxwidth) { size_t off, wwidth, max_wwidth, rowcnt; struct tabular_row *row; @@ -194,10 +196,10 @@ calc_output_lines(const struct tabular_cfg *cfg, struct fmt_state *fmt, case TABULAR_TRUNC: entry_lines = 1; break; - case TABULAR_SQUASH: + case TABULAR_WRAP: entry_lines = CEILDIV(row->entries[i].ulen, width); break; - case TABULAR_SQUASH_WORDAWARE: + case TABULAR_WRAP_WORDAWARE: calc_word_aware(&row->entries[i], width, NULL, NULL, &entry_lines); break; @@ -216,22 +218,33 @@ bool recalc_cols(const struct tabular_cfg *cfg, struct tabular_stats *stats, struct fmt_state *fmt, struct tabular_row *rows, size_t limit) { - size_t width, fullwidth; - size_t i, remaining; + size_t width, fullwidth, remaining; + ssize_t i; fullwidth = cfg->outw - cfg->lpad - cfg->rpad; /* reset widths to minimum requirement */ for (i = 0; i < cfg->column_count; i++) { if (fmt->columns[i].hidden) continue; - fmt->columns[i].width = cfg->columns[i].minwidth; + if (cfg->columns[i].squashable) { + fmt->columns[i].width = cfg->columns[i].minwidth; + } else { + width = MIN(fmt->columns[i].maxlen, + cfg->columns[i].maxwidth - cfg->columns[i].lpad + - cfg->columns[i].rpad); + if (cfg->columns[i].strategy == TABULAR_WRAP_WORDAWARE) + width = recalc_col_word_aware(cfg, rows, + limit, (size_t) i, width); + fmt->columns[i].width = width + cfg->columns[i].lpad + + cfg->columns[i].rpad; + } } /* could not fit all necessary columns at minimum width */ width = calc_row_width(fmt); while (width > fullwidth) { /* remove non-essential columns */ - for (i = cfg->column_count - 1; i >= 0; i--) { + for (i = ((ssize_t) cfg->column_count) - 1; i >= 0; i--) { if (!cfg->columns[i].essential && !fmt->columns[i].hidden) { fmt->columns[i].hidden = true; @@ -250,12 +263,14 @@ recalc_cols(const struct tabular_cfg *cfg, struct tabular_stats *stats, remaining = fullwidth - width; for (i = 0; remaining > 0 && i < cfg->column_count; i++) { if (fmt->columns[i].hidden) continue; - width = MIN(fmt->columns[i].maxlen, cfg->columns[i].maxwidth); - width = MIN(width, fmt->columns[i].width + remaining); - width = width - fmt->columns[i].lpad - fmt->columns[i].rpad; - if (cfg->columns[i].strategy == TABULAR_SQUASH_WORDAWARE) + if (!cfg->columns[i].squashable) continue; + width = MIN(fmt->columns[i].maxlen, cfg->columns[i].maxwidth + - fmt->columns[i].lpad - fmt->columns[i].rpad); + width = MIN(width, fmt->columns[i].width + remaining + - fmt->columns[i].lpad - fmt->columns[i].rpad); + if (cfg->columns[i].strategy == TABULAR_WRAP_WORDAWARE) width = recalc_col_word_aware(cfg, rows, - limit, i, width, remaining); + limit, (size_t) i, width); width = MAX(width + fmt->columns[i].lpad + fmt->columns[i].rpad, fmt->columns[i].width); remaining -= width - fmt->columns[i].width; @@ -285,7 +300,7 @@ calc_params_row(const struct tabular_cfg *cfg, struct tabular_stats *stats, tabular_load_row_entry_hidden(cfg, row, i); if (fmt->columns[i].hidden) { fmt->columns[i].hidden = - (row->entries[i].flags & TABULAR_ENTRY_HIDDEN); + (row->entries[i].flags & BIT(TABULAR_ENTRY_HIDDEN)); } if (fmt->columns[i].hidden) continue; } @@ -358,20 +373,27 @@ calc_params(const struct tabular_cfg *cfg, struct tabular_stats *stats, fmt->columns[i].written = 0; } + fmt->row_limit = 0; + + if (!*rows && cfg->row_gen) *rows = cfg->row_gen(&cfg->user); + if (!*rows) return 0; + if (!recalc_cols(cfg, stats, fmt, *rows, 0)) { stats->cols_truncated = true; stats->rows_truncated = true; return 0; } - fmt->row_limit = 0; for (row = rows; ; row = &(*row)->next) { if (!*row && cfg->row_gen) *row = cfg->row_gen(&cfg->user); if (!*row) break; rc = calc_params_row(cfg, stats, *rows, *row, fmt, fmt->row_limit + 1); if (rc < 0) return rc; - if (rc > 0) break; + if (rc > 0) { + stats->rows_truncated = true; + break; + } fmt->row_limit++; } @@ -494,7 +516,7 @@ output_row(FILE *file, const struct tabular_cfg *cfg, width = fmt->columns[i].width - fmt->columns[i].lpad - fmt->columns[i].rpad; padwidth = width; - if (cfg->columns[i].strategy == TABULAR_SQUASH_WORDAWARE) { + if (cfg->columns[i].strategy == TABULAR_WRAP_WORDAWARE) { calc_word_aware_line(entry, width, &off, &wwidth); entry += off; width = wwidth; @@ -547,8 +569,6 @@ output_rows(FILE *file, const struct tabular_cfg *cfg, count += 1; } - stats->rows_truncated = !row; - return 0; } @@ -560,13 +580,14 @@ tabular_format(FILE *file, const struct tabular_cfg *cfg, size_t i; int rc; + stats->lines_used = 0; stats->rows_displayed = 0; stats->rows_truncated = false; stats->cols_truncated = false; - stats->lines_used = 0; - if (!cfg->column_count || !rows) - return 0; + if (!rows) return 1; + + if (!cfg->column_count) return 0; for (i = 0; i < cfg->column_count; i++) { if (cfg->columns[i].minwidth > cfg->columns[i].maxwidth) @@ -575,8 +596,8 @@ tabular_format(FILE *file, const struct tabular_cfg *cfg, >= cfg->columns[i].minwidth) return 1; if (cfg->columns[i].strategy != TABULAR_TRUNC - && cfg->columns[i].strategy != TABULAR_SQUASH - && cfg->columns[i].strategy != TABULAR_SQUASH_WORDAWARE) + && cfg->columns[i].strategy != TABULAR_WRAP + && cfg->columns[i].strategy != TABULAR_WRAP_WORDAWARE) return 1; } @@ -643,7 +664,7 @@ tabular_load_row_entry_hidden(const struct tabular_cfg *cfg, { bool hidden; - if (row->entries[col].flags & TABULAR_ENTRY_HIDDEN_AVAIL) return; + if (row->entries[col].flags & BIT(TABULAR_ENTRY_HIDDEN_SET)) return; if (cfg->columns[col].is_hidden) { hidden = cfg->columns[col].is_hidden( @@ -653,22 +674,22 @@ tabular_load_row_entry_hidden(const struct tabular_cfg *cfg, } if (hidden) { - row->entries[col].flags |= TABULAR_ENTRY_HIDDEN; + row->entries[col].flags |= BIT(TABULAR_ENTRY_HIDDEN); } else { - row->entries[col].flags &= TABULAR_ENTRY_HIDDEN; + row->entries[col].flags &= ~BIT(TABULAR_ENTRY_HIDDEN); } - row->entries[col].flags |= TABULAR_ENTRY_HIDDEN_AVAIL; + row->entries[col].flags |= BIT(TABULAR_ENTRY_HIDDEN_SET); } void tabular_load_row_entry_str(const struct tabular_cfg *cfg, struct tabular_row *row, size_t col) { - if (row->entries[col].flags & TABULAR_ENTRY_STR_AVAIL) return; + if (row->entries[col].flags & BIT(TABULAR_ENTRY_STR_SET)) return; row->entries[col].str = cfg->columns[col].to_str( &row->user, &cfg->columns[col].user); row->entries[col].ulen = (uint32_t) u8strlen(row->entries[col].str); - row->entries[col].flags |= TABULAR_ENTRY_STR_AVAIL; + row->entries[col].flags |= BIT(TABULAR_ENTRY_STR_SET); } diff --git a/src/test.c b/src/test.c @@ -12,10 +12,10 @@ bool print_style(FILE *file, const struct tabular_cfg *cfg, const struct tabular_row *row, const struct tabular_col *col); -char *col_pos_str(const struct tabular_user *user); +char *col_pos_str(const struct tabular_user *row, const struct tabular_user *col); bool col_pos_hidden(const struct tabular_user *user); -char *col_name_str(const struct tabular_user *user); +char *col_name_str(const struct tabular_user *row, const struct tabular_user *col); bool col_name_hidden(const struct tabular_user *user); static const char **argv = NULL; @@ -50,7 +50,7 @@ static struct tabular_col columns[] = { .align = TABULAR_ALIGN_LEFT, - .strategy = TABULAR_SQUASH_WORDAWARE, + .strategy = TABULAR_WRAP_WORDAWARE, .essential = true, }, }; @@ -101,19 +101,19 @@ print_style(FILE *file, const struct tabular_cfg *cfg, } char * -col_pos_str(const struct tabular_user *user) +col_pos_str(const struct tabular_user *row, const struct tabular_user *col) { char buf[16]; - snprintf(buf, sizeof(buf), "%li", user->id); + snprintf(buf, sizeof(buf), "%li", row->id); return strdup(buf); } char * -col_name_str(const struct tabular_user *user) +col_name_str(const struct tabular_user *row, const struct tabular_user *col) { - return strdup(argv[user->id]); + return strdup(argv[row->id]); } int @@ -138,12 +138,13 @@ main(int argc, const char **_argv) rows = NULL; end = &rows; for (i = 0; i < argc; i++) { - end = tabular_alloc_row(&cfg, &rc, end, + *end = tabular_alloc_row(&cfg, &rc, (struct tabular_user) { .id = (size_t) i }); - if (!end) errx(1, "tabular_append_row %i", rc); + if (!*end) errx(1, "tabular_append_row %i", rc); + end = &(*end)->next; } - rc = tabular_format(stdout, &cfg, &stats, rows); + rc = tabular_format(stdout, &cfg, &stats, &rows); if (rc) errx(1, "tabular_format %i", rc); printf("\n%lu lines, %lu rows\n",