back to topotato report
topotato coverage report
Current view: top level - lib - termtable.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 145 241 60.2 %
Date: 2023-02-24 14:41:08 Functions: 9 14 64.3 %

          Line data    Source code
       1             : /*
       2             :  * ASCII table generator.
       3             :  * Copyright (C) 2017  Cumulus Networks
       4             :  * Quentin Young
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the Free
       8             :  * Software Foundation; either version 2 of the License, or (at your option)
       9             :  * any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      12             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      14             :  * more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : #include <zebra.h>
      21             : #include <stdio.h>
      22             : 
      23             : #include "printfrr.h"
      24             : #include "memory.h"
      25             : #include "termtable.h"
      26             : 
      27         586 : DEFINE_MTYPE_STATIC(LIB, TTABLE, "ASCII table");
      28             : 
      29             : /* clang-format off */
      30             : const struct ttable_style ttable_styles[] = {
      31             :         {       // default ascii
      32             :                 .corner = '+',
      33             :                 .rownums_on = false,
      34             :                 .indent = 1,
      35             :                 .border = {
      36             :                         .top = '-',
      37             :                         .bottom = '-',
      38             :                         .left = '|',
      39             :                         .right = '|',
      40             :                         .top_on = true,
      41             :                         .bottom_on = true,
      42             :                         .left_on = true,
      43             :                         .right_on = true,
      44             :                 },
      45             :                 .cell = {
      46             :                         .lpad = 1,
      47             :                         .rpad = 1,
      48             :                         .align = LEFT,
      49             :                         .border = {
      50             :                                 .bottom = '-',
      51             :                                 .bottom_on = true,
      52             :                                 .top = '-',
      53             :                                 .top_on = false,
      54             :                                 .right = '|',
      55             :                                 .right_on = true,
      56             :                                 .left = '|',
      57             :                                 .left_on = false,
      58             :                         },
      59             :                 },
      60             :         }, {    // blank, suitable for plaintext alignment
      61             :                 .corner = ' ',
      62             :                 .rownums_on = false,
      63             :                 .indent = 1,
      64             :                 .border = {
      65             :                         .top = ' ',
      66             :                         .bottom = ' ',
      67             :                         .left = ' ',
      68             :                         .right = ' ',
      69             :                         .top_on = false,
      70             :                         .bottom_on = false,
      71             :                         .left_on = false,
      72             :                         .right_on = false,
      73             :                 },
      74             :                 .cell = {
      75             :                         .lpad = 0,
      76             :                         .rpad = 3,
      77             :                         .align = LEFT,
      78             :                         .border = {
      79             :                                 .bottom = ' ',
      80             :                                 .bottom_on = false,
      81             :                                 .top = ' ',
      82             :                                 .top_on = false,
      83             :                                 .right = ' ',
      84             :                                 .right_on = false,
      85             :                                 .left = ' ',
      86             :                                 .left_on = false,
      87             :                         },
      88             :                 }
      89             :    }
      90             : };
      91             : /* clang-format on */
      92             : 
      93           4 : void ttable_del(struct ttable *tt)
      94             : {
      95          10 :         for (int i = tt->nrows - 1; i >= 0; i--)
      96           6 :                 ttable_del_row(tt, i);
      97             : 
      98           4 :         XFREE(MTYPE_TTABLE, tt->table);
      99           4 :         XFREE(MTYPE_TTABLE, tt);
     100           4 : }
     101             : 
     102           4 : struct ttable *ttable_new(const struct ttable_style *style)
     103             : {
     104           4 :         struct ttable *tt;
     105             : 
     106           4 :         tt = XCALLOC(MTYPE_TTABLE, sizeof(struct ttable));
     107           4 :         tt->style = *style;
     108           4 :         tt->nrows = 0;
     109           4 :         tt->ncols = 0;
     110           4 :         tt->size = 0;
     111           4 :         tt->table = NULL;
     112             : 
     113           4 :         return tt;
     114             : }
     115             : 
     116             : /**
     117             :  * Inserts or appends a new row at the specified index.
     118             :  *
     119             :  * If the index is -1, the row is added to the end of the table. Otherwise the
     120             :  * index must be a valid index into tt->table.
     121             :  *
     122             :  * If the table already has at least one row (and therefore a determinate
     123             :  * number of columns), a format string specifying a number of columns not equal
     124             :  * to tt->ncols will result in a no-op and a return value of NULL.
     125             :  *
     126             :  * @param tt table to insert into
     127             :  * @param i insertion index; inserted row will be (i + 1)'th row
     128             :  * @param format printf format string as in ttable_[add|insert]_row()
     129             :  * @param ap pre-initialized variadic list of arguments for format string
     130             :  *
     131             :  * @return pointer to the first cell of allocated row
     132             :  */
     133             : PRINTFRR(3, 0)
     134           6 : static struct ttable_cell *ttable_insert_row_va(struct ttable *tt, int i,
     135             :                                                 const char *format, va_list ap)
     136             : {
     137           6 :         assert(i >= -1 && i < tt->nrows);
     138             : 
     139             :         char shortbuf[256];
     140             :         char *res, *orig, *section;
     141             :         struct ttable_cell *row;
     142         259 :         int col = 0;
     143             :         int ncols = 0;
     144             : 
     145             :         /* count how many columns we have */
     146         259 :         for (int j = 0; format[j]; j++)
     147         253 :                 ncols += !!(format[j] == '|');
     148           6 :         ncols++;
     149             : 
     150           6 :         if (tt->ncols == 0)
     151           4 :                 tt->ncols = ncols;
     152           2 :         else if (ncols != tt->ncols)
     153             :                 return NULL;
     154             : 
     155             :         /* reallocate chunk if necessary */
     156          10 :         while (tt->size < (tt->nrows + 1) * sizeof(struct ttable_cell *)) {
     157           4 :                 tt->size = MAX(2 * tt->size, 2 * sizeof(struct ttable_cell *));
     158           4 :                 tt->table = XREALLOC(MTYPE_TTABLE, tt->table, tt->size);
     159             :         }
     160             : 
     161             :         /* CALLOC a block of cells */
     162           6 :         row = XCALLOC(MTYPE_TTABLE, tt->ncols * sizeof(struct ttable_cell));
     163             : 
     164           6 :         res = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap);
     165           6 :         orig = res;
     166             : 
     167          41 :         while (res && col < tt->ncols) {
     168          35 :                 section = strsep(&res, "|");
     169          35 :                 row[col].text = XSTRDUP(MTYPE_TTABLE, section);
     170          35 :                 row[col].style = tt->style.cell;
     171          35 :                 col++;
     172             :         }
     173             : 
     174           6 :         if (orig != shortbuf)
     175           0 :                 XFREE(MTYPE_TMP, orig);
     176             : 
     177             :         /* insert row */
     178           6 :         if (i == -1 || i == tt->nrows)
     179           6 :                 tt->table[tt->nrows] = row;
     180             :         else {
     181           0 :                 memmove(&tt->table[i + 1], &tt->table[i],
     182           0 :                         (tt->nrows - i) * sizeof(struct ttable_cell *));
     183           0 :                 tt->table[i] = row;
     184             :         }
     185             : 
     186           6 :         tt->nrows++;
     187             : 
     188           6 :         return row;
     189             : }
     190             : 
     191           0 : struct ttable_cell *ttable_insert_row(struct ttable *tt, unsigned int i,
     192             :                                       const char *format, ...)
     193             : {
     194           0 :         struct ttable_cell *ret;
     195           0 :         va_list ap;
     196             : 
     197           0 :         va_start(ap, format);
     198           0 :         ret = ttable_insert_row_va(tt, i, format, ap);
     199           0 :         va_end(ap);
     200             : 
     201           0 :         return ret;
     202             : }
     203             : 
     204           6 : struct ttable_cell *ttable_add_row(struct ttable *tt, const char *format, ...)
     205             : {
     206           6 :         struct ttable_cell *ret;
     207           6 :         va_list ap;
     208             : 
     209           6 :         va_start(ap, format);
     210           6 :         ret = ttable_insert_row_va(tt, -1, format, ap);
     211           6 :         va_end(ap);
     212             : 
     213           6 :         return ret;
     214             : }
     215             : 
     216           6 : void ttable_del_row(struct ttable *tt, unsigned int i)
     217             : {
     218           6 :         assert((int)i < tt->nrows);
     219             : 
     220          41 :         for (int j = 0; j < tt->ncols; j++)
     221          35 :                 XFREE(MTYPE_TTABLE, tt->table[i][j].text);
     222             : 
     223           6 :         XFREE(MTYPE_TTABLE, tt->table[i]);
     224             : 
     225           6 :         memmove(&tt->table[i], &tt->table[i + 1],
     226           6 :                 (tt->nrows - i - 1) * sizeof(struct ttable_cell *));
     227             : 
     228           6 :         tt->nrows--;
     229             : 
     230           6 :         if (tt->nrows == 0)
     231           4 :                 tt->ncols = 0;
     232           6 : }
     233             : 
     234           0 : void ttable_align(struct ttable *tt, unsigned int row, unsigned int col,
     235             :                   unsigned int nrow, unsigned int ncol, enum ttable_align align)
     236             : {
     237           0 :         assert((int)row < tt->nrows);
     238           0 :         assert((int)col < tt->ncols);
     239           0 :         assert((int)row + (int)nrow <= tt->nrows);
     240           0 :         assert((int)col + (int)ncol <= tt->ncols);
     241             : 
     242           0 :         for (unsigned int i = row; i < row + nrow; i++)
     243           0 :                 for (unsigned int j = col; j < col + ncol; j++)
     244           0 :                         tt->table[i][j].style.align = align;
     245           0 : }
     246             : 
     247           0 : static void ttable_cell_pad(struct ttable_cell *cell, enum ttable_align align,
     248             :                             short pad)
     249             : {
     250           0 :         if (align == LEFT)
     251           0 :                 cell->style.lpad = pad;
     252             :         else
     253           0 :                 cell->style.rpad = pad;
     254             : }
     255             : 
     256           0 : void ttable_pad(struct ttable *tt, unsigned int row, unsigned int col,
     257             :                 unsigned int nrow, unsigned int ncol, enum ttable_align align,
     258             :                 short pad)
     259             : {
     260           0 :         assert((int)row < tt->nrows);
     261           0 :         assert((int)col < tt->ncols);
     262           0 :         assert((int)row + (int)nrow <= tt->nrows);
     263           0 :         assert((int)col + (int)ncol <= tt->ncols);
     264             : 
     265           0 :         for (unsigned int i = row; i < row + nrow; i++)
     266           0 :                 for (unsigned int j = col; j < col + ncol; j++)
     267           0 :                         ttable_cell_pad(&tt->table[i][j], align, pad);
     268           0 : }
     269             : 
     270           4 : void ttable_restyle(struct ttable *tt)
     271             : {
     272           8 :         for (int i = 0; i < tt->nrows; i++)
     273          27 :                 for (int j = 0; j < tt->ncols; j++)
     274          23 :                         tt->table[i][j].style = tt->style.cell;
     275           4 : }
     276             : 
     277           0 : void ttable_colseps(struct ttable *tt, unsigned int col,
     278             :                     enum ttable_align align, bool on, char sep)
     279             : {
     280           0 :         for (int i = 0; i < tt->nrows; i++) {
     281           0 :                 if (align == RIGHT) {
     282           0 :                         tt->table[i][col].style.border.right_on = on;
     283           0 :                         tt->table[i][col].style.border.right = sep;
     284             :                 } else {
     285           0 :                         tt->table[i][col].style.border.left_on = on;
     286           0 :                         tt->table[i][col].style.border.left = sep;
     287             :                 }
     288             :         }
     289           0 : }
     290             : 
     291           0 : void ttable_rowseps(struct ttable *tt, unsigned int row,
     292             :                     enum ttable_align align, bool on, char sep)
     293             : {
     294           0 :         for (int i = 0; i < tt->ncols; i++) {
     295           0 :                 if (align == TOP) {
     296           0 :                         tt->table[row][i].style.border.top_on = on;
     297           0 :                         tt->table[row][i].style.border.top = sep;
     298             :                 } else {
     299           0 :                         tt->table[row][i].style.border.bottom_on = on;
     300           0 :                         tt->table[row][i].style.border.bottom = sep;
     301             :                 }
     302             :         }
     303           0 : }
     304             : 
     305           4 : char *ttable_dump(struct ttable *tt, const char *newline)
     306           4 : {
     307             :         /* clang-format off */
     308           4 :         char *buf;         // print buffer
     309           4 :         size_t pos;        // position in buffer
     310           4 :         size_t nl_len;     // strlen(newline)
     311           4 :         int cw[tt->ncols]; // calculated column widths
     312           4 :         int nlines;        // total number of newlines / table lines
     313           4 :         size_t width;      // length of one line, with newline
     314           4 :         int abspad;        // calculated whitespace for sprintf
     315           4 :         char *left;        // left part of line
     316           4 :         size_t lsize;      // size of above
     317           4 :         char *right;       // right part of line
     318           4 :         size_t rsize;      // size of above
     319           4 :         struct ttable_cell *cell, *row; // iteration pointers
     320             :         /* clang-format on */
     321             : 
     322           4 :         nl_len = strlen(newline);
     323             : 
     324             :         /* calculate width of each column */
     325           4 :         memset(cw, 0x00, sizeof(int) * tt->ncols);
     326             : 
     327          27 :         for (int j = 0; j < tt->ncols; j++)
     328          58 :                 for (int i = 0, cellw = 0; i < tt->nrows; i++) {
     329          35 :                         cell = &tt->table[i][j];
     330          35 :                         cellw = 0;
     331          35 :                         cellw += (int)strlen(cell->text);
     332          35 :                         cellw += cell->style.lpad;
     333          35 :                         cellw += cell->style.rpad;
     334          35 :                         if (j != 0)
     335          29 :                                 cellw += cell->style.border.left_on ? 1 : 0;
     336          35 :                         if (j != tt->ncols - 1)
     337          29 :                                 cellw += cell->style.border.right_on ? 1 : 0;
     338          35 :                         cw[j] = MAX(cw[j], cellw);
     339             :                 }
     340             : 
     341             :         /* calculate overall line width, including newline */
     342           4 :         width = 0;
     343           4 :         width += tt->style.indent;
     344           4 :         width += tt->style.border.left_on ? 1 : 0;
     345           4 :         width += tt->style.border.right_on ? 1 : 0;
     346           4 :         width += strlen(newline);
     347          27 :         for (int i = 0; i < tt->ncols; i++)
     348          23 :                 width += cw[i];
     349             : 
     350             :         /* calculate number of lines en total */
     351           4 :         nlines = tt->nrows;
     352           4 :         nlines += tt->style.border.top_on ? 1 : 0;
     353           4 :         nlines += 1; // tt->style.border.bottom_on ? 1 : 1; makes life easier
     354          10 :         for (int i = 0; i < tt->nrows; i++) {
     355             :                 /* if leftmost cell has top / bottom border, whole row does */
     356           6 :                 nlines += tt->table[i][0].style.border.top_on ? 1 : 0;
     357           6 :                 nlines += tt->table[i][0].style.border.bottom_on ? 1 : 0;
     358             :         }
     359             : 
     360             :         /* initialize left & right */
     361           4 :         lsize = tt->style.indent + (tt->style.border.left_on ? 1 : 0);
     362           4 :         left = XCALLOC(MTYPE_TTABLE, lsize);
     363           4 :         rsize = nl_len + (tt->style.border.right_on ? 1 : 0);
     364           4 :         right = XCALLOC(MTYPE_TTABLE, rsize);
     365             : 
     366           4 :         memset(left, ' ', lsize);
     367             : 
     368           4 :         if (tt->style.border.left_on)
     369           0 :                 left[lsize - 1] = tt->style.border.left;
     370             : 
     371           4 :         if (tt->style.border.right_on) {
     372           0 :                 right[0] = tt->style.border.right;
     373           0 :                 memcpy(&right[1], newline, nl_len);
     374             :         } else
     375           4 :                 memcpy(&right[0], newline, nl_len);
     376             : 
     377             :         /* allocate print buffer */
     378           4 :         buf = XCALLOC(MTYPE_TMP, width * (nlines + 1) + 1);
     379           4 :         pos = 0;
     380             : 
     381           4 :         if (tt->style.border.top_on) {
     382           0 :                 memcpy(&buf[pos], left, lsize);
     383           0 :                 pos += lsize;
     384             : 
     385           0 :                 for (size_t i = 0; i < width - lsize - rsize; i++)
     386           0 :                         buf[pos++] = tt->style.border.top;
     387             : 
     388           0 :                 memcpy(&buf[pos], right, rsize);
     389           0 :                 pos += rsize;
     390             :         }
     391             : 
     392          10 :         for (int i = 0; i < tt->nrows; i++) {
     393           6 :                 row = tt->table[i];
     394             : 
     395             :                 /* if top border and not first row, print top row border */
     396           6 :                 if (row[0].style.border.top_on && i != 0) {
     397           0 :                         memcpy(&buf[pos], left, lsize);
     398           0 :                         pos += lsize;
     399             : 
     400           0 :                         for (size_t l = 0; l < width - lsize - rsize; l++)
     401           0 :                                 buf[pos++] = row[0].style.border.top;
     402             : 
     403           0 :                         pos -= width - lsize - rsize;
     404           0 :                         for (int k = 0; k < tt->ncols; k++) {
     405           0 :                                 if (k != 0 && row[k].style.border.left_on)
     406           0 :                                         buf[pos] = tt->style.corner;
     407           0 :                                 pos += cw[k];
     408           0 :                                 if (row[k].style.border.right_on
     409           0 :                                     && k != tt->ncols - 1)
     410           0 :                                         buf[pos - 1] = tt->style.corner;
     411             :                         }
     412             : 
     413           0 :                         memcpy(&buf[pos], right, rsize);
     414           0 :                         pos += rsize;
     415             :                 }
     416             : 
     417           6 :                 memcpy(&buf[pos], left, lsize);
     418           6 :                 pos += lsize;
     419             : 
     420          41 :                 for (int j = 0; j < tt->ncols; j++) {
     421             :                         /* if left border && not first col print left border */
     422          35 :                         if (row[j].style.border.left_on && j != 0)
     423           0 :                                 buf[pos++] = row[j].style.border.left;
     424             : 
     425             :                         /* print left padding */
     426          35 :                         for (int k = 0; k < row[j].style.lpad; k++)
     427           0 :                                 buf[pos++] = ' ';
     428             : 
     429             :                         /* calculate padding for sprintf */
     430          35 :                         abspad = cw[j];
     431          35 :                         abspad -= row[j].style.rpad;
     432          35 :                         abspad -= row[j].style.lpad;
     433          35 :                         if (j != 0)
     434          29 :                                 abspad -= row[j].style.border.left_on ? 1 : 0;
     435          35 :                         if (j != tt->ncols - 1)
     436          29 :                                 abspad -= row[j].style.border.right_on ? 1 : 0;
     437             : 
     438             :                         /* print text */
     439          35 :                         if (row[j].style.align == LEFT)
     440          35 :                                 pos += sprintf(&buf[pos], "%-*s", abspad,
     441             :                                                row[j].text);
     442             :                         else
     443           0 :                                 pos += sprintf(&buf[pos], "%*s", abspad,
     444             :                                                row[j].text);
     445             : 
     446             :                         /* print right padding */
     447         105 :                         for (int k = 0; k < row[j].style.rpad; k++)
     448          70 :                                 buf[pos++] = ' ';
     449             : 
     450             :                         /* if right border && not last col print right border */
     451          35 :                         if (row[j].style.border.right_on && j != tt->ncols - 1)
     452           0 :                                 buf[pos++] = row[j].style.border.right;
     453             :                 }
     454             : 
     455           6 :                 memcpy(&buf[pos], right, rsize);
     456           6 :                 pos += rsize;
     457             : 
     458             :                 /* if bottom border and not last row, print bottom border */
     459           6 :                 if (row[0].style.border.bottom_on && i != tt->nrows - 1) {
     460           0 :                         memcpy(&buf[pos], left, lsize);
     461           0 :                         pos += lsize;
     462             : 
     463           0 :                         for (size_t l = 0; l < width - lsize - rsize; l++)
     464           0 :                                 buf[pos++] = row[0].style.border.bottom;
     465             : 
     466           0 :                         pos -= width - lsize - rsize;
     467           0 :                         for (int k = 0; k < tt->ncols; k++) {
     468           0 :                                 if (k != 0 && row[k].style.border.left_on)
     469           0 :                                         buf[pos] = tt->style.corner;
     470           0 :                                 pos += cw[k];
     471           0 :                                 if (row[k].style.border.right_on
     472           0 :                                     && k != tt->ncols - 1)
     473           0 :                                         buf[pos - 1] = tt->style.corner;
     474             :                         }
     475             : 
     476           0 :                         memcpy(&buf[pos], right, rsize);
     477           0 :                         pos += rsize;
     478             :                 }
     479             : 
     480           6 :                 assert(!buf[pos]); /* pos == & of first \0 in buf */
     481             :         }
     482             : 
     483           4 :         if (tt->style.border.bottom_on) {
     484           0 :                 memcpy(&buf[pos], left, lsize);
     485           0 :                 pos += lsize;
     486             : 
     487           0 :                 for (size_t l = 0; l < width - lsize - rsize; l++)
     488           0 :                         buf[pos++] = tt->style.border.bottom;
     489             : 
     490           0 :                 memcpy(&buf[pos], right, rsize);
     491           0 :                 pos += rsize;
     492             :         }
     493             : 
     494           4 :         buf[pos] = '\0';
     495             : 
     496           4 :         XFREE(MTYPE_TTABLE, left);
     497           4 :         XFREE(MTYPE_TTABLE, right);
     498             : 
     499           4 :         return buf;
     500             : }

Generated by: LCOV version v1.16-topotato