columns.c revision 267654
11556Srgrimes/*
21556Srgrimes *  $Id: columns.c,v 1.8 2011/06/28 09:26:23 tom Exp $
31556Srgrimes *
41556Srgrimes *  columns.c -- implements column-alignment
51556Srgrimes *
61556Srgrimes *  Copyright 2008-2010,2011	Thomas E. Dickey
71556Srgrimes *
81556Srgrimes *  This program is free software; you can redistribute it and/or modify
91556Srgrimes *  it under the terms of the GNU Lesser General Public License, version 2.1
101556Srgrimes *  as published by the Free Software Foundation.
111556Srgrimes *
121556Srgrimes *  This program is distributed in the hope that it will be useful, but
131556Srgrimes *  WITHOUT ANY WARRANTY; without even the implied warranty of
141556Srgrimes *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
151556Srgrimes *  Lesser General Public License for more details.
161556Srgrimes *
171556Srgrimes *  You should have received a copy of the GNU Lesser General Public
181556Srgrimes *  License along with this program; if not, write to
191556Srgrimes *	Free Software Foundation, Inc.
201556Srgrimes *	51 Franklin St., Fifth Floor
211556Srgrimes *	Boston, MA 02110, USA.
221556Srgrimes */
231556Srgrimes
241556Srgrimes#include <dialog.h>
251556Srgrimes
261556Srgrimes#define each(row, data) \
271556Srgrimes 		row = 0, data = target; \
281556Srgrimes 		row < num_rows; \
291556Srgrimes		++row, data = next_row(data, per_row)
3090143Smarkm
311556Srgrimesstatic char *
3236049Scharniercolumn_separator(void)
3390143Smarkm{
3436049Scharnier    char *result = 0;
35110391Scharnier
3699110Sobrien    if ((result = dialog_vars.column_separator) != 0) {
3799110Sobrien	if (*result == '\0')
381556Srgrimes	    result = 0;
391556Srgrimes    }
401556Srgrimes    return result;
411556Srgrimes}
421556Srgrimes
431556Srgrimesstatic char **
441556Srgrimesnext_row(char **target, int per_row)
45105831Srwatson{
4611890Sphk    char *result = (char *) target;
471556Srgrimes    result += per_row;
48217192Skib    return (char **) result;
491556Srgrimes}
501556Srgrimes
5190878Simpstatic char *
5273369Sachenext_col(char *source, unsigned offset)
5372343Sache{
541556Srgrimes    char *mark = column_separator();
551556Srgrimes    char *result = source + offset;
5690878Simp    if (offset)
571556Srgrimes	result += strlen(mark);
58205271Sjmallett    return strstr(result, mark);
591556Srgrimes}
601556Srgrimes
6190143Smarkm/*
6216835Speter * Parse the source string, storing the offsets and widths of each column in
631556Srgrimes * the corresponding arrays.  Return the number of columns.
641556Srgrimes */
651556Srgrimesstatic unsigned
661556Srgrimessplit_row(char *source, unsigned *offsets, unsigned *widths)
67225868Strasz{
68225868Strasz    int mark = (int) strlen(column_separator());
69225868Strasz    char *next = 0;
70103274Speter    unsigned result = 0;
7190143Smarkm    unsigned offset = 0;
721556Srgrimes
7390110Simp    do {
741556Srgrimes	if (result) {
751556Srgrimes	    offset = (unsigned) (mark + next - source);
761556Srgrimes	    widths[result - 1] = offset - offsets[result - 1] - (unsigned) mark;
771556Srgrimes	}
78130999Sgad	offsets[result] = offset;
79130999Sgad	++result;
80106251Stjr    } while ((next = next_col(source, offset)) != 0);
81130999Sgad
82106251Stjr    offset = (unsigned) strlen(source);
83130999Sgad    widths[result - 1] = offset - offsets[result - 1];
84130999Sgad
851556Srgrimes    return result;
861556Srgrimes}
87130999Sgad
88109504Sjmallett/*
891556Srgrimes * The caller passes a pointer to a struct or array containing pointers
90109504Sjmallett * to strings that we may want to copy and reformat according to the column
911556Srgrimes * separator.
92109504Sjmallett */
93130999Sgadvoid
941556Srgrimesdlg_align_columns(char **target, int per_row, int num_rows)
951556Srgrimes{
961556Srgrimes    int row;
971556Srgrimes
981556Srgrimes    if (column_separator()) {
99225868Strasz	char **value;
10097958Sjmallett	unsigned numcols = 1;
10197958Sjmallett	size_t maxcols = 0;
102225868Strasz	unsigned *widths;
10397958Sjmallett	unsigned *offsets;
10497958Sjmallett	unsigned *maxwidth;
10597958Sjmallett	unsigned realwidth;
10697958Sjmallett	unsigned n;
107225868Strasz
108225868Strasz	/* first allocate arrays for workspace */
109225868Strasz	for (each(row, value)) {
110225868Strasz	    size_t len = strlen(*value);
111225868Strasz	    if (maxcols < len)
11297958Sjmallett		maxcols = len;
11397958Sjmallett	}
114225868Strasz	++maxcols;
11590110Simp	widths = dlg_calloc(unsigned, maxcols);
1161556Srgrimes	offsets = dlg_calloc(unsigned, maxcols);
117225868Strasz	maxwidth = dlg_calloc(unsigned, maxcols);
1181556Srgrimes
11919068Speter	assert_ptr(widths, "dlg_align_columns");
120130999Sgad	assert_ptr(offsets, "dlg_align_columns");
121173004Sjulian	assert_ptr(maxwidth, "dlg_align_columns");
122225868Strasz
123225868Strasz	/* now, determine the number of columns and the column-widths */
124225868Strasz	for (each(row, value)) {
125225868Strasz	    unsigned cols = split_row(*value, offsets, widths);
126225868Strasz	    if (numcols < cols)
127173004Sjulian		numcols = cols;
128225868Strasz	    for (n = 0; n < cols; ++n) {
129225868Strasz		if (maxwidth[n] < widths[n])
130225868Strasz		    maxwidth[n] = widths[n];
13119068Speter	    }
1321556Srgrimes	}
13397877Sjmallett	realwidth = numcols - 1;
1341556Srgrimes	for (n = 0; n < numcols; ++n) {
1351556Srgrimes	    realwidth += maxwidth[n];
136130999Sgad	}
1371556Srgrimes
138192239Sbrian	/* finally, construct reformatted strings */
139192239Sbrian	for (each(row, value)) {
140192239Sbrian	    unsigned cols = split_row(*value, offsets, widths);
141192239Sbrian	    unsigned offset = 0;
142192239Sbrian	    char *text = dlg_malloc(char, realwidth + 1);
143192239Sbrian
144192239Sbrian	    assert_ptr(text, "dlg_align_columns");
145192239Sbrian
146192239Sbrian	    memset(text, ' ', (size_t) realwidth);
147192239Sbrian	    for (n = 0; n < cols; ++n) {
148225868Strasz		memcpy(text + offset, *value + offsets[n], (size_t) widths[n]);
149225868Strasz		offset += maxwidth[n] + 1;
150225868Strasz	    }
151225868Strasz	    *value = text;
152225868Strasz	}
153225868Strasz
154192239Sbrian	free(widths);
155192239Sbrian	free(offsets);
156225868Strasz	free(maxwidth);
157225868Strasz    }
158192239Sbrian}
159225868Strasz
160225868Strasz/*
161225868Strasz * Free temporary storage used while making column-aligned data.
162225868Strasz */
163225868Straszvoid
164225868Straszdlg_free_columns(char **target, int per_row, int num_rows)
165225868Strasz{
1661556Srgrimes    int row;
1671556Srgrimes    char **value;
168225868Strasz
16990110Simp    if (column_separator()) {
1701556Srgrimes	for (each(row, value)) {
171225868Strasz	    free(*value);
1721556Srgrimes	}
173173004Sjulian    }
174225868Strasz}
175225868Strasz