"Fossies" - the Fresh Open Source Software Archive

Member "monit-5.28.0/src/terminal/TextBox.c" (28 Mar 2021, 11759 Bytes) of package /linux/privat/monit-5.28.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "TextBox.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) Tildeslash Ltd. All rights reserved.
    3  *
    4  * This program is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU Affero General Public License version 3.
    6  *
    7  * This program is distributed in the hope that it will be useful,
    8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  * GNU Affero General Public License for more details.
   11  *
   12  * You should have received a copy of the GNU Affero General Public License
   13  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   14  *
   15  * In addition, as a special exception, the copyright holders give
   16  * permission to link the code of portions of this program with the
   17  * OpenSSL library under certain conditions as described in each
   18  * individual source file, and distribute linked combinations
   19  * including the two.
   20  *
   21  * You must obey the GNU Affero General Public License in all respects
   22  * for all of the code used other than OpenSSL.  
   23  */
   24 
   25 
   26 #include "config.h"
   27 
   28 #ifdef HAVE_STDLIB_H
   29 #include <stdlib.h>
   30 #endif
   31 
   32 #include "monit.h"
   33 #include "TextColor.h"
   34 #include "TextBox.h"
   35 
   36 // libmonit
   37 #include "util/Str.h"
   38 
   39 
   40 /**
   41  * Implementation of the Terminal table interface using UTF-8 box:
   42  * http://www.unicode.org/charts/PDF/U2500.pdf
   43  *
   44  * @author http://www.tildeslash.com/
   45  * @see http://www.mmonit.com/
   46  * @file
   47  */
   48 
   49 
   50 /* ------------------------------------------------------------ Definitions */
   51 
   52 
   53 #define BOX_HORIZONTAL          "\u2500" // ─
   54 #define BOX_HORIZONTAL_DOWN     "\u252c" // ┬
   55 #define BOX_VERTICAL            "\u2502" // │
   56 #define BOX_VERTICAL_HORIZONTAL "\u253c" // ┼
   57 #define BOX_VERTICAL_RIGHT      "\u251c" // ├
   58 #define BOX_VERTICAL_LEFT       "\u2524" // ┤
   59 #define BOX_DOWN_RIGHT          "\u250c" // ┌
   60 #define BOX_DOWN_LEFT           "\u2510" // ┐
   61 #define BOX_UP_HORIZONTAL       "\u2534" // ┴
   62 #define BOX_UP_RIGHT            "\u2514" // └
   63 #define BOX_UP_LEFT             "\u2518" // ┘
   64 
   65 
   66 #define T TextBox_T
   67 struct T {
   68         struct {
   69                 unsigned int row;
   70                 unsigned int column;
   71         } index;
   72         struct {
   73                 struct {
   74                         bool enabled;
   75                         const char *color;
   76                 } header;
   77         } options;
   78         unsigned int columnsCount;
   79         TextBoxColumn_T *columns;
   80         StringBuffer_T b;
   81 };
   82 
   83 
   84 /* ------------------------------------------------------- Private Methods */
   85 
   86 
   87 static void _printBorderTop(T t) {
   88         StringBuffer_append(t->b, COLOR_DARKGRAY BOX_DOWN_RIGHT BOX_HORIZONTAL);
   89         for (unsigned int i = 0; i < t->columnsCount; i++) {
   90                 for (int j = 0; j < t->columns[i].width; j++)
   91                         StringBuffer_append(t->b, BOX_HORIZONTAL);
   92                 if (i < t->columnsCount - 1)
   93                         StringBuffer_append(t->b, BOX_HORIZONTAL BOX_HORIZONTAL_DOWN BOX_HORIZONTAL);
   94         }
   95         StringBuffer_append(t->b, BOX_HORIZONTAL BOX_DOWN_LEFT COLOR_RESET "\n");
   96 }
   97 
   98 
   99 static void _printBorderMiddle(T t) {
  100         StringBuffer_append(t->b, COLOR_DARKGRAY BOX_VERTICAL_RIGHT BOX_HORIZONTAL);
  101         for (unsigned int i = 0; i < t->columnsCount; i++) {
  102                 for (int j = 0; j < t->columns[i].width; j++)
  103                         StringBuffer_append(t->b, BOX_HORIZONTAL);
  104                 if (i < t->columnsCount - 1)
  105                         StringBuffer_append(t->b, BOX_HORIZONTAL BOX_VERTICAL_HORIZONTAL BOX_HORIZONTAL);
  106         }
  107         StringBuffer_append(t->b, BOX_HORIZONTAL BOX_VERTICAL_LEFT COLOR_RESET "\n");
  108 }
  109 
  110 
  111 static void _printBorderBottom(T t) {
  112         StringBuffer_append(t->b, COLOR_DARKGRAY BOX_UP_RIGHT BOX_HORIZONTAL);
  113         for (unsigned int i = 0; i < t->columnsCount; i++) {
  114                 for (int j = 0; j < t->columns[i].width; j++)
  115                         StringBuffer_append(t->b, BOX_HORIZONTAL);
  116                 if (i < t->columnsCount - 1)
  117                         StringBuffer_append(t->b, BOX_HORIZONTAL BOX_UP_HORIZONTAL BOX_HORIZONTAL);
  118         }
  119         StringBuffer_append(t->b, BOX_HORIZONTAL BOX_UP_LEFT COLOR_RESET "\n");
  120 }
  121 
  122 
  123 static void _printHeader(T t) {
  124         for (unsigned int i = 0; i < t->columnsCount; i++) {
  125                 StringBuffer_append(t->b, COLOR_DARKGRAY BOX_VERTICAL COLOR_RESET " ");
  126                 StringBuffer_append(t->b, "%s%-*s%s", t->options.header.color, t->columns[i].width, t->columns[i].name, COLOR_RESET);
  127                 StringBuffer_append(t->b, " ");
  128         }
  129         StringBuffer_append(t->b, COLOR_DARKGRAY BOX_VERTICAL COLOR_RESET "\n");
  130         t->index.row++;
  131 }
  132 
  133 
  134 static void _cacheColor(TextBoxColumn_T *column) {
  135         bool ansi = false;
  136         if (column->value) {
  137                 for (int i = 0, k = 0; column->value[i]; i++) {
  138                         if (column->value[i] == '\033' && column->value[i + 1] == '[') {
  139                                 // Escape sequence start
  140                                 column->_color[k++] = '\033';
  141                                 column->_color[k++] = '[';
  142                                 i++;
  143                                 ansi = true;
  144                         } else if (ansi) {
  145                                 column->_color[k++] = column->value[i];
  146                                 // Escape sequence stop
  147                                 if (column->value[i] >= 64 && column->value[i] <= 126)
  148                                         break;
  149                         }
  150                 }
  151         }
  152 }
  153 
  154 
  155 // Print a row. If wrap is enabled and the text excceeds width, return true (printed text up to column width, repetition possible to print the rest), otherwise false
  156 static bool _printRow(T t) {
  157         bool repeat = false;
  158         for (unsigned int i = 0; i < t->columnsCount; i++) {
  159                 StringBuffer_append(t->b, COLOR_DARKGRAY BOX_VERTICAL COLOR_RESET " ");
  160                 if (*(t->columns[i]._color))
  161                         StringBuffer_append(t->b, "%s", t->columns[i]._color);
  162                 if (! t->columns[i].value || t->columns[i]._cursor > strlen(t->columns[i].value) - 1) {
  163                         // Empty column padding
  164                         StringBuffer_append(t->b, "%*s", t->columns[i].width, " ");
  165                 } else if (strlen(t->columns[i].value + t->columns[i]._cursor) > (unsigned long)t->columns[i].width) {
  166                         if (t->columns[i].wrap) {
  167                                 // The value exceeds the column width and should be wrapped
  168                                 int column = 0;
  169                                 for (; t->columns[i].value[t->columns[i]._cursor] && (column == 0 || t->columns[i]._cursor % t->columns[i].width > 0); t->columns[i]._cursor++, column++)
  170                                         StringBuffer_append(t->b, "%c", t->columns[i].value[t->columns[i]._cursor]);
  171                                 if (t->columns[i]._cursor < t->columns[i]._valueLength)
  172                                         repeat = true;
  173                         } else {
  174                                 // The value exceeds the column width and should be truncated
  175                                 Str_trunc(t->columns[i].value, t->columns[i].width);
  176                                 StringBuffer_append(t->b, t->columns[i].align == TextBoxAlign_Right ? "%*s" : "%-*s", t->columns[i].width, t->columns[i].value);
  177                                 t->columns[i]._cursor = t->columns[i]._valueLength;
  178                         }
  179                 } else {
  180                         // The whole value fits in the column width
  181                         StringBuffer_append(t->b, t->columns[i].align == TextBoxAlign_Right ? "%*s" : "%-*s", t->columns[i].width, t->columns[i].value + t->columns[i]._cursor);
  182                         t->columns[i]._cursor = t->columns[i]._valueLength;
  183                 }
  184                 StringBuffer_append(t->b, " ");
  185                 if (*(t->columns[i]._color))
  186                         StringBuffer_append(t->b, COLOR_RESET);
  187         }
  188         StringBuffer_append(t->b, COLOR_DARKGRAY BOX_VERTICAL COLOR_RESET "\n");
  189         t->index.row++;
  190         return repeat;
  191 }
  192 
  193 
  194 static void _resetColumn(TextBoxColumn_T *column) {
  195         FREE(column->value);
  196         column->_cursor = column->_colorLength = column->_valueLength = 0;
  197         memset(column->_color, 0, sizeof(column->_color));
  198 }
  199 
  200 
  201 static void _resetRow(T t) {
  202         for (unsigned int i = 0; i < t->columnsCount; i++)
  203                 _resetColumn(&(t->columns[i]));
  204 }
  205 
  206 
  207 /* -------------------------------------------------------- Public Methods */
  208 
  209 
  210 T TextBox_new(StringBuffer_T b, int columnsCount, TextBoxColumn_T *columns, bool printHeader) {
  211         ASSERT(b);
  212         ASSERT(columns);
  213         ASSERT(columnsCount > 0);
  214         T t;
  215         NEW(t);
  216         t->b = b;
  217         t->columnsCount = columnsCount;
  218         t->columns = columns;
  219         // Default options
  220         t->options.header.color = COLOR_BOLDCYAN; // Note: hardcoded, option setting can be implemented if needed
  221         // Options
  222         t->options.header.enabled = printHeader;
  223         return t;
  224 }
  225 
  226 
  227 void TextBox_free(T *t) {
  228         ASSERT(t && *t);
  229         if ((*t)->index.row > 0)
  230                 _printBorderBottom(*t);
  231         for (unsigned int i = 0; i < (*t)->columnsCount; i++)
  232                 FREE((*t)->columns[i].value);
  233         FREE(*t);
  234 }
  235 
  236 
  237 void TextBox_setColumn(T t, unsigned int index, const char *format, ...) {
  238         ASSERT(t);
  239         ASSERT(index > 0);
  240         ASSERT(index <= t->columnsCount);
  241         int _index = index - 1;
  242         _resetColumn(&(t->columns[_index]));
  243         if (format) {
  244                 va_list ap;
  245                 va_start(ap, format);
  246                 t->columns[_index].value = Str_vcat(format, ap);
  247                 va_end(ap);
  248                 if ((t->columns[_index]._colorLength = TextColor_length(t->columns[_index].value))) {
  249                         _cacheColor(&(t->columns[_index]));
  250                         TextColor_strip(t->columns[_index].value); // Strip the escape sequences, so we can safely break the line
  251                 }
  252                 t->columns[_index]._valueLength = strlen(t->columns[_index].value);
  253         }
  254 }
  255 
  256 
  257 void TextBox_printRow(T t) {
  258         ASSERT(t);
  259         if (t->index.row == 0) {
  260                 _printBorderTop(t);
  261                 if (t->options.header.enabled) {
  262                         _printHeader(t);
  263                         _printBorderMiddle(t);
  264                 }
  265         } else {
  266                 _printBorderMiddle(t);
  267         }
  268         bool repeat = false;
  269         do {
  270                 repeat = _printRow(t);
  271         } while (repeat);
  272         _resetRow(t);
  273 }
  274 
  275 
  276 char *TextBox_strip(char *s) {
  277         if (STR_DEF(s)) {
  278                 int x, y;
  279                 unsigned char *_s = (unsigned char *)s;
  280                 bool separator = false;
  281                 for (x = 0, y = 0; s[y]; y++) {
  282                         if (! separator) {
  283                                 if (_s[y] == 0xE2 && _s[y + 1] == 0x94) {
  284                                         if (_s[y + 2] == 0x8c || _s[y + 2] == 0x94 || _s[y + 2] == 0x9c)
  285                                                 separator = true; // Drop the whole separator line
  286                                         else if (_s[y + 2] >= 0x80 && _s[y + 2] <= 0xBF)
  287                                                 y += 2; // to skip 3 characters of UTF-8 box drawing character
  288                                 } else {
  289                                         _s[x++] = _s[y];
  290                                 }
  291                         } else if (_s[y] == '\n') {
  292                                 separator = false;
  293                         }
  294                 }
  295                 _s[x] = 0;
  296         }
  297         return s;
  298 }
  299