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
31static char *
32column_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
43static char **
44next_row(char **target, int per_row)
45{
46    char *result = (char *) target;
47    result += per_row;
48    return (char **) (void *) result;
49}
50
51static char *
52next_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 */
65static unsigned
66split_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 */
93void
94dlg_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 */
164void
165dlg_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