freebsd-skq/contrib/dialog/columns.c
2021-02-26 10:16:49 +01:00

175 lines
4.2 KiB
C

/*
* $Id: columns.c,v 1.11 2019/07/25 00:06:38 tom Exp $
*
* columns.c -- implements column-alignment
*
* Copyright 2008-2011,2019 Thomas E. Dickey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License, version 2.1
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to
* Free Software Foundation, Inc.
* 51 Franklin St., Fifth Floor
* Boston, MA 02110, USA.
*/
#include <dialog.h>
#define each(row, data) \
row = 0, data = target; \
row < num_rows; \
++row, data = next_row(data, per_row)
static char *
column_separator(void)
{
char *result = 0;
if ((result = dialog_vars.column_separator) != 0) {
if (*result == '\0')
result = 0;
}
return result;
}
static char **
next_row(char **target, int per_row)
{
char *result = (char *) target;
result += per_row;
return (char **) (void *) result;
}
static char *
next_col(char *source, unsigned offset)
{
char *mark = column_separator();
char *result = source + offset;
if (offset)
result += strlen(mark);
return strstr(result, mark);
}
/*
* Parse the source string, storing the offsets and widths of each column in
* the corresponding arrays. Return the number of columns.
*/
static unsigned
split_row(char *source, unsigned *offsets, unsigned *widths)
{
int mark = (int) strlen(column_separator());
char *next = 0;
unsigned result = 0;
unsigned offset = 0;
do {
if (result) {
offset = (unsigned) (mark + next - source);
widths[result - 1] = offset - offsets[result - 1] - (unsigned) mark;
}
offsets[result] = offset;
++result;
} while ((next = next_col(source, offset)) != 0);
offset = (unsigned) strlen(source);
widths[result - 1] = offset - offsets[result - 1];
return result;
}
/*
* The caller passes a pointer to a struct or array containing pointers
* to strings that we may want to copy and reformat according to the column
* separator.
*/
void
dlg_align_columns(char **target, int per_row, int num_rows)
{
if (column_separator()) {
char **value;
unsigned numcols = 1;
size_t maxcols = 0;
unsigned *widths;
unsigned *offsets;
unsigned *maxwidth;
unsigned realwidth;
unsigned n;
int row;
/* first allocate arrays for workspace */
for (each(row, value)) {
size_t len = strlen(*value);
if (maxcols < len)
maxcols = len;
}
++maxcols;
widths = dlg_calloc(unsigned, maxcols);
offsets = dlg_calloc(unsigned, maxcols);
maxwidth = dlg_calloc(unsigned, maxcols);
assert_ptr(widths, "dlg_align_columns");
assert_ptr(offsets, "dlg_align_columns");
assert_ptr(maxwidth, "dlg_align_columns");
/* now, determine the number of columns and the column-widths */
for (each(row, value)) {
unsigned cols = split_row(*value, offsets, widths);
if (numcols < cols)
numcols = cols;
for (n = 0; n < cols; ++n) {
if (maxwidth[n] < widths[n])
maxwidth[n] = widths[n];
}
}
realwidth = numcols - 1;
for (n = 0; n < numcols; ++n) {
realwidth += maxwidth[n];
}
/* finally, construct reformatted strings */
for (each(row, value)) {
unsigned cols = split_row(*value, offsets, widths);
unsigned offset = 0;
char *text = dlg_malloc(char, realwidth + 1);
assert_ptr(text, "dlg_align_columns");
memset(text, ' ', (size_t) realwidth);
for (n = 0; n < cols; ++n) {
memcpy(text + offset, *value + offsets[n], (size_t) widths[n]);
offset += maxwidth[n] + 1;
}
text[realwidth] = 0;
*value = text;
}
free(widths);
free(offsets);
free(maxwidth);
}
}
/*
* Free temporary storage used while making column-aligned data.
*/
void
dlg_free_columns(char **target, int per_row, int num_rows)
{
if (column_separator()) {
int row;
char **value;
for (each(row, value)) {
free(*value);
}
}
}