1 /* 2 * $Id: columns.c,v 1.10 2011/10/20 20:53:55 tom Exp $ 3 * 4 * columns.c -- implements column-alignment 5 * 6 * Copyright 2008-2010,2011 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 int row; 97 98 if (column_separator()) { 99 char **value; 100 unsigned numcols = 1; 101 size_t maxcols = 0; 102 unsigned *widths; 103 unsigned *offsets; 104 unsigned *maxwidth; 105 unsigned realwidth; 106 unsigned n; 107 108 /* first allocate arrays for workspace */ 109 for (each(row, value)) { 110 size_t len = strlen(*value); 111 if (maxcols < len) 112 maxcols = len; 113 } 114 ++maxcols; 115 widths = dlg_calloc(unsigned, maxcols); 116 offsets = dlg_calloc(unsigned, maxcols); 117 maxwidth = dlg_calloc(unsigned, maxcols); 118 119 assert_ptr(widths, "dlg_align_columns"); 120 assert_ptr(offsets, "dlg_align_columns"); 121 assert_ptr(maxwidth, "dlg_align_columns"); 122 123 /* now, determine the number of columns and the column-widths */ 124 for (each(row, value)) { 125 unsigned cols = split_row(*value, offsets, widths); 126 if (numcols < cols) 127 numcols = cols; 128 for (n = 0; n < cols; ++n) { 129 if (maxwidth[n] < widths[n]) 130 maxwidth[n] = widths[n]; 131 } 132 } 133 realwidth = numcols - 1; 134 for (n = 0; n < numcols; ++n) { 135 realwidth += maxwidth[n]; 136 } 137 138 /* finally, construct reformatted strings */ 139 for (each(row, value)) { 140 unsigned cols = split_row(*value, offsets, widths); 141 unsigned offset = 0; 142 char *text = dlg_malloc(char, realwidth + 1); 143 144 assert_ptr(text, "dlg_align_columns"); 145 146 memset(text, ' ', (size_t) realwidth); 147 for (n = 0; n < cols; ++n) { 148 memcpy(text + offset, *value + offsets[n], (size_t) widths[n]); 149 offset += maxwidth[n] + 1; 150 } 151 text[realwidth] = 0; 152 *value = text; 153 } 154 155 free(widths); 156 free(offsets); 157 free(maxwidth); 158 } 159 } 160 161 /* 162 * Free temporary storage used while making column-aligned data. 163 */ 164 void 165 dlg_free_columns(char **target, int per_row, int num_rows) 166 { 167 int row; 168 char **value; 169 170 if (column_separator()) { 171 for (each(row, value)) { 172 free(*value); 173 } 174 } 175 } 176