xref: /dragonfly/contrib/dialog/columns.c (revision b2dabe2e739bd72461a68ac543307c2dedfb048c)
1 /*
2  *  $Id: columns.c,v 1.12 2022/04/03 22:38:16 tom Exp $
3  *
4  *  columns.c -- implements column-alignment
5  *
6  *  Copyright 2008-2019,2022  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 <dlg_internals.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 *
column_separator(void)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 **
next_row(char ** target,int per_row)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 *
next_col(char * source,unsigned offset)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
split_row(char * source,unsigned * offsets,unsigned * widths)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
dlg_align_columns(char ** target,int per_row,int num_rows)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
dlg_free_columns(char ** target,int per_row,int num_rows)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