back to topotato report
topotato coverage report
Current view: top level - lib - termtable.c (source / functions) Hit Total Coverage
Test: test_bgp_ecmp_enhe.py::BGP_Unnumbered_ECMP Lines: 1 263 0.4 %
Date: 2023-11-16 17:19:14 Functions: 2 29 6.9 %

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

Generated by: LCOV version v1.16-topotato