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 *
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 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
dlg_free_columns(char ** target,int per_row,int num_rows)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