1 /* 2 * $Id: columns.c,v 1.11 2019/07/25 00:06:38 tom Exp $ 3 * 4 * columns.c -- implements column-alignment 5 * 6 * Copyright 2008-2011,2019 Thomas E. Dickey 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License, version 2.1 10 * as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program; if not, write to 19 * Free Software Foundation, Inc. 20 * 51 Franklin St., Fifth Floor 21 * Boston, MA 02110, USA. 22 */ 23 24 #include <dialog.h> 25 26 #define each(row, data) \ 27 row = 0, data = target; \ 28 row < num_rows; \ 29 ++row, data = next_row(data, per_row) 30 31 static char * 32 column_separator(void) 33 { 34 char *result = 0; 35 36 if ((result = dialog_vars.column_separator) != 0) { 37 if (*result == '\0') 38 result = 0; 39 } 40 return result; 41 } 42 43 static char ** 44 next_row(char **target, int per_row) 45 { 46 char *result = (char *) target; 47 result += per_row; 48 return (char **) (void *) result; 49 } 50 51 static char * 52 next_col(char *source, unsigned offset) 53 { 54 char *mark = column_separator(); 55 char *result = source + offset; 56 if (offset) 57 result += strlen(mark); 58 return strstr(result, mark); 59 } 60 61 /* 62 * Parse the source string, storing the offsets and widths of each column in 63 * the corresponding arrays. Return the number of columns. 64 */ 65 static unsigned 66 split_row(char *source, unsigned *offsets, unsigned *widths) 67 { 68 int mark = (int) strlen(column_separator()); 69 char *next = 0; 70 unsigned result = 0; 71 unsigned offset = 0; 72 73 do { 74 if (result) { 75 offset = (unsigned) (mark + next - source); 76 widths[result - 1] = offset - offsets[result - 1] - (unsigned) mark; 77 } 78 offsets[result] = offset; 79 ++result; 80 } while ((next = next_col(source, offset)) != 0); 81 82 offset = (unsigned) strlen(source); 83 widths[result - 1] = offset - offsets[result - 1]; 84 85 return result; 86 } 87 88 /* 89 * The caller passes a pointer to a struct or array containing pointers 90 * to strings that we may want to copy and reformat according to the column 91 * separator. 92 */ 93 void 94 dlg_align_columns(char **target, int per_row, int num_rows) 95 { 96 if (column_separator()) { 97 char **value; 98 unsigned numcols = 1; 99 size_t maxcols = 0; 100 unsigned *widths; 101 unsigned *offsets; 102 unsigned *maxwidth; 103 unsigned realwidth; 104 unsigned n; 105 int row; 106 107 /* first allocate arrays for workspace */ 108 for (each(row, value)) { 109 size_t len = strlen(*value); 110 if (maxcols < len) 111 maxcols = len; 112 } 113 ++maxcols; 114 widths = dlg_calloc(unsigned, maxcols); 115 offsets = dlg_calloc(unsigned, maxcols); 116 maxwidth = dlg_calloc(unsigned, maxcols); 117 118 assert_ptr(widths, "dlg_align_columns"); 119 assert_ptr(offsets, "dlg_align_columns"); 120 assert_ptr(maxwidth, "dlg_align_columns"); 121 122 /* now, determine the number of columns and the column-widths */ 123 for (each(row, value)) { 124 unsigned cols = split_row(*value, offsets, widths); 125 if (numcols < cols) 126 numcols = cols; 127 for (n = 0; n < cols; ++n) { 128 if (maxwidth[n] < widths[n]) 129 maxwidth[n] = widths[n]; 130 } 131 } 132 realwidth = numcols - 1; 133 for (n = 0; n < numcols; ++n) { 134 realwidth += maxwidth[n]; 135 } 136 137 /* finally, construct reformatted strings */ 138 for (each(row, value)) { 139 unsigned cols = split_row(*value, offsets, widths); 140 unsigned offset = 0; 141 char *text = dlg_malloc(char, realwidth + 1); 142 143 assert_ptr(text, "dlg_align_columns"); 144 145 memset(text, ' ', (size_t) realwidth); 146 for (n = 0; n < cols; ++n) { 147 memcpy(text + offset, *value + offsets[n], (size_t) widths[n]); 148 offset += maxwidth[n] + 1; 149 } 150 text[realwidth] = 0; 151 *value = text; 152 } 153 154 free(widths); 155 free(offsets); 156 free(maxwidth); 157 } 158 } 159 160 /* 161 * Free temporary storage used while making column-aligned data. 162 */ 163 void 164 dlg_free_columns(char **target, int per_row, int num_rows) 165 { 166 if (column_separator()) { 167 int row; 168 char **value; 169 170 for (each(row, value)) { 171 free(*value); 172 } 173 } 174 } 175