1/*-
2 * Copyright (c) 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1993\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37static const char sccsid[] = "@(#)rs.c	8.1 (Berkeley) 6/6/93";
38#endif /* not lint */
39
40/*
41 *	rs - reshape a data array
42 *	Author:  John Kunze, Office of Comp. Affairs, UCB
43 *		BEWARE: lots of unfinished edges
44 */
45
46#include <sys/cdefs.h>
47__FBSDID("$FreeBSD$");
48
49#include <err.h>
50#include <ctype.h>
51#include <limits.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55
56static long	flags;
57#define	TRANSPOSE	000001
58#define	MTRANSPOSE	000002
59#define	ONEPERLINE	000004
60#define	ONEISEPONLY	000010
61#define	ONEOSEPONLY	000020
62#define	NOTRIMENDCOL	000040
63#define	SQUEEZE		000100
64#define	SHAPEONLY	000200
65#define	DETAILSHAPE	000400
66#define	RIGHTADJUST	001000
67#define	NULLPAD		002000
68#define	RECYCLE		004000
69#define	SKIPPRINT	010000
70#define	ICOLBOUNDS	020000
71#define	OCOLBOUNDS	040000
72#define ONEPERCHAR	0100000
73#define NOARGS		0200000
74
75static short	*colwidths;
76static short	*cord;
77static short	*icbd;
78static short	*ocbd;
79static int	nelem;
80static char	**elem;
81static char	**endelem;
82static char	*curline;
83static int	allocsize = BUFSIZ;
84static int	curlen;
85static int	irows, icols;
86static int	orows = 0, ocols = 0;
87static int	maxlen;
88static int	skip;
89static int	propgutter;
90static char	isep = ' ', osep = ' ';
91static char	blank[] = "";
92static int	owidth = 80, gutter = 2;
93
94static void	  getargs(int, char *[]);
95static void	  getfile(void);
96static int	  get_line(void);
97static char	 *getlist(short **, char *);
98static char	 *getnum(int *, char *, int);
99static char	**getptrs(char **);
100static void	  prepfile(void);
101static void	  prints(char *, int);
102static void	  putfile(void);
103static void usage(void);
104
105#define	INCR(ep) do {			\
106	if (++ep >= endelem)		\
107		ep = getptrs(ep);	\
108} while(0)
109
110int
111main(int argc, char *argv[])
112{
113	getargs(argc, argv);
114	getfile();
115	if (flags & SHAPEONLY) {
116		printf("%d %d\n", irows, icols);
117		exit(0);
118	}
119	prepfile();
120	putfile();
121	exit(0);
122}
123
124static void
125getfile(void)
126{
127	char *p;
128	char *endp;
129	char **ep;
130	int c;
131	int multisep = (flags & ONEISEPONLY ? 0 : 1);
132	int nullpad = flags & NULLPAD;
133	char **padto;
134
135	while (skip--) {
136		c = get_line();
137		if (flags & SKIPPRINT)
138			puts(curline);
139		if (c == EOF)
140			return;
141	}
142	get_line();
143	if (flags & NOARGS && curlen < owidth)
144		flags |= ONEPERLINE;
145	if (flags & ONEPERLINE)
146		icols = 1;
147	else				/* count cols on first line */
148		for (p = curline, endp = curline + curlen; p < endp; p++) {
149			if (*p == isep && multisep)
150				continue;
151			icols++;
152			while (*p && *p != isep)
153				p++;
154		}
155	ep = getptrs(elem);
156	do {
157		if (flags & ONEPERLINE) {
158			*ep = curline;
159			INCR(ep);		/* prepare for next entry */
160			if (maxlen < curlen)
161				maxlen = curlen;
162			irows++;
163			continue;
164		}
165		for (p = curline, endp = curline + curlen; p < endp; p++) {
166			if (*p == isep && multisep)
167				continue;	/* eat up column separators */
168			if (*p == isep)		/* must be an empty column */
169				*ep = blank;
170			else			/* store column entry */
171				*ep = p;
172			while (p < endp && *p != isep)
173				p++;		/* find end of entry */
174			*p = '\0';		/* mark end of entry */
175			if (maxlen < p - *ep)	/* update maxlen */
176				maxlen = p - *ep;
177			INCR(ep);		/* prepare for next entry */
178		}
179		irows++;			/* update row count */
180		if (nullpad) {			/* pad missing entries */
181			padto = elem + irows * icols;
182			while (ep < padto) {
183				*ep = blank;
184				INCR(ep);
185			}
186		}
187	} while (get_line() != EOF);
188	*ep = 0;				/* mark end of pointers */
189	nelem = ep - elem;
190}
191
192static void
193putfile(void)
194{
195	char **ep;
196	int i, j, k;
197
198	ep = elem;
199	if (flags & TRANSPOSE)
200		for (i = 0; i < orows; i++) {
201			for (j = i; j < nelem; j += orows)
202				prints(ep[j], (j - i) / orows);
203			putchar('\n');
204		}
205	else
206		for (i = k = 0; i < orows; i++) {
207			for (j = 0; j < ocols; j++, k++)
208				if (k < nelem)
209					prints(ep[k], j);
210			putchar('\n');
211		}
212}
213
214static void
215prints(char *s, int col)
216{
217	int n;
218	char *p = s;
219
220	while (*p)
221		p++;
222	n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
223	if (flags & RIGHTADJUST)
224		while (n-- > 0)
225			putchar(osep);
226	for (p = s; *p; p++)
227		putchar(*p);
228	while (n-- > 0)
229		putchar(osep);
230}
231
232static void
233usage(void)
234{
235	fprintf(stderr,
236		"usage: rs [-[csCS][x][kKgGw][N]tTeEnyjhHmz] [rows [cols]]\n");
237	exit(1);
238}
239
240static void
241prepfile(void)
242{
243	char **ep;
244	int  i;
245	int  j;
246	char **lp;
247	int colw;
248	int max;
249	int n;
250
251	if (!nelem)
252		exit(0);
253	gutter += maxlen * propgutter / 100.0;
254	colw = maxlen + gutter;
255	if (flags & MTRANSPOSE) {
256		orows = icols;
257		ocols = irows;
258	}
259	else if (orows == 0 && ocols == 0) {	/* decide rows and cols */
260		ocols = owidth / colw;
261		if (ocols == 0) {
262			warnx("display width %d is less than column width %d",
263					owidth, colw);
264			ocols = 1;
265		}
266		if (ocols > nelem)
267			ocols = nelem;
268		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
269	}
270	else if (orows == 0)			/* decide on rows */
271		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
272	else if (ocols == 0)			/* decide on cols */
273		ocols = nelem / orows + (nelem % orows ? 1 : 0);
274	lp = elem + orows * ocols;
275	while (lp > endelem) {
276		getptrs(elem + nelem);
277		lp = elem + orows * ocols;
278	}
279	if (flags & RECYCLE) {
280		for (ep = elem + nelem; ep < lp; ep++)
281			*ep = *(ep - nelem);
282		nelem = lp - elem;
283	}
284	if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
285		errx(1, "malloc");
286	if (flags & SQUEEZE) {
287		ep = elem;
288		if (flags & TRANSPOSE)
289			for (i = 0; i < ocols; i++) {
290				max = 0;
291				for (j = 0; *ep != NULL && j < orows; j++)
292					if ((n = strlen(*ep++)) > max)
293						max = n;
294				colwidths[i] = max + gutter;
295			}
296		else
297			for (i = 0; i < ocols; i++) {
298				max = 0;
299				for (j = i; j < nelem; j += ocols)
300					if ((n = strlen(ep[j])) > max)
301						max = n;
302				colwidths[i] = max + gutter;
303			}
304	}
305	/*	for (i = 0; i < orows; i++) {
306			for (j = i; j < nelem; j += orows)
307				prints(ep[j], (j - i) / orows);
308			putchar('\n');
309		}
310	else
311		for (i = 0; i < orows; i++) {
312			for (j = 0; j < ocols; j++)
313				prints(*ep++, j);
314			putchar('\n');
315		}*/
316	else
317		for (i = 0; i < ocols; i++)
318			colwidths[i] = colw;
319	if (!(flags & NOTRIMENDCOL)) {
320		if (flags & RIGHTADJUST)
321			colwidths[0] -= gutter;
322		else
323			colwidths[ocols - 1] = 0;
324	}
325	n = orows * ocols;
326	if (n > nelem && (flags & RECYCLE))
327		nelem = n;
328	/*for (i = 0; i < ocols; i++)
329		warnx("%d is colwidths, nelem %d", colwidths[i], nelem);*/
330}
331
332#define	BSIZE	(LINE_MAX * 2)
333static char	ibuf[BSIZE];
334
335static int
336get_line(void)	/* get line; maintain curline, curlen; manage storage */
337{
338	static	int putlength;
339	static	char *endblock = ibuf + BSIZE;
340	char *p;
341	int c, i;
342
343	if (!irows) {
344		curline = ibuf;
345		putlength = flags & DETAILSHAPE;
346	}
347	else if (skip <= 0) {			/* don't waste storage */
348		curline += curlen + 1;
349		if (putlength) {	/* print length, recycle storage */
350			printf(" %d line %d\n", curlen, irows);
351			curline = ibuf;
352		}
353	}
354	if (!putlength && endblock - curline < LINE_MAX + 1) { /* need storage */
355		/*ww = endblock-curline; tt += ww;*/
356		/*printf("#wasted %d total %d\n",ww,tt);*/
357		if (!(curline = (char *) malloc(BSIZE)))
358			errx(1, "file too large");
359		endblock = curline + BSIZE;
360		/*printf("#endb %d curline %d\n",endblock,curline);*/
361	}
362	for (p = curline, i = 0;; *p++ = c, i++) {
363		if ((c = getchar()) == EOF)
364			break;
365		if (i >= LINE_MAX)
366			errx(1, "maximum line length (%d) exceeded", LINE_MAX);
367		if (c == '\n')
368			break;
369	}
370	*p = '\0';
371	curlen = i;
372	return(c);
373}
374
375static char **
376getptrs(char **sp)
377{
378	char **p;
379
380	allocsize += allocsize;
381	p = (char **)realloc(elem, allocsize * sizeof(char *));
382	if (p == NULL)
383		err(1, "no memory");
384
385	sp += (p - elem);
386	endelem = (elem = p) + allocsize;
387	return(sp);
388}
389
390static void
391getargs(int ac, char *av[])
392{
393	char *p;
394
395	if (ac == 1) {
396		flags |= NOARGS | TRANSPOSE;
397	}
398	while (--ac && **++av == '-')
399		for (p = *av+1; *p; p++)
400			switch (*p) {
401			case 'T':
402				flags |= MTRANSPOSE;
403			case 't':
404				flags |= TRANSPOSE;
405				break;
406			case 'c':		/* input col. separator */
407				flags |= ONEISEPONLY;
408			case 's':		/* one or more allowed */
409				if (p[1])
410					isep = *++p;
411				else
412					isep = '\t';	/* default is ^I */
413				break;
414			case 'C':
415				flags |= ONEOSEPONLY;
416			case 'S':
417				if (p[1])
418					osep = *++p;
419				else
420					osep = '\t';	/* default is ^I */
421				break;
422			case 'w':		/* window width, default 80 */
423				p = getnum(&owidth, p, 0);
424				if (owidth <= 0)
425					errx(1, "width must be a positive integer");
426				break;
427			case 'K':			/* skip N lines */
428				flags |= SKIPPRINT;
429			case 'k':			/* skip, do not print */
430				p = getnum(&skip, p, 0);
431				if (!skip)
432					skip = 1;
433				break;
434			case 'm':
435				flags |= NOTRIMENDCOL;
436				break;
437			case 'g':		/* gutter space */
438				p = getnum(&gutter, p, 0);
439				break;
440			case 'G':
441				p = getnum(&propgutter, p, 0);
442				break;
443			case 'e':		/* each line is an entry */
444				flags |= ONEPERLINE;
445				break;
446			case 'E':
447				flags |= ONEPERCHAR;
448				break;
449			case 'j':			/* right adjust */
450				flags |= RIGHTADJUST;
451				break;
452			case 'n':	/* null padding for missing values */
453				flags |= NULLPAD;
454				break;
455			case 'y':
456				flags |= RECYCLE;
457				break;
458			case 'H':			/* print shape only */
459				flags |= DETAILSHAPE;
460			case 'h':
461				flags |= SHAPEONLY;
462				break;
463			case 'z':			/* squeeze col width */
464				flags |= SQUEEZE;
465				break;
466			/*case 'p':
467				ipagespace = atoi(++p);	(default is 1)
468				break;*/
469			case 'o':			/* col order */
470				p = getlist(&cord, p);
471				break;
472			case 'b':
473				flags |= ICOLBOUNDS;
474				p = getlist(&icbd, p);
475				break;
476			case 'B':
477				flags |= OCOLBOUNDS;
478				p = getlist(&ocbd, p);
479				break;
480			default:
481				usage();
482			}
483	/*if (!osep)
484		osep = isep;*/
485	switch (ac) {
486	/*case 3:
487		opages = atoi(av[2]);*/
488	case 2:
489		if ((ocols = atoi(av[1])) < 0)
490			ocols = 0;
491	case 1:
492		if ((orows = atoi(av[0])) < 0)
493			orows = 0;
494	case 0:
495		break;
496	default:
497		errx(1, "too many arguments");
498	}
499}
500
501static char *
502getlist(short **list, char *p)
503{
504	int count = 1;
505	char *t;
506
507	for (t = p + 1; *t; t++) {
508		if (!isdigit((unsigned char)*t))
509			errx(1,
510	"option %.1s requires a list of unsigned numbers separated by commas", t);
511		count++;
512		while (*t && isdigit((unsigned char)*t))
513			t++;
514		if (*t != ',')
515			break;
516	}
517	if (!(*list = (short *) malloc(count * sizeof(short))))
518		errx(1, "no list space");
519	count = 0;
520	for (t = p + 1; *t; t++) {
521		(*list)[count++] = atoi(t);
522		printf("++ %d ", (*list)[count-1]);
523		fflush(stdout);
524		while (*t && isdigit((unsigned char)*t))
525			t++;
526		if (*t != ',')
527			break;
528	}
529	(*list)[count] = 0;
530	return(t - 1);
531}
532
533/*
534 * num = number p points to; if (strict) complain
535 * returns pointer to end of num
536 */
537static char *
538getnum(int *num, char *p, int strict)
539{
540	char *t = p;
541
542	if (!isdigit((unsigned char)*++t)) {
543		if (strict || *t == '-' || *t == '+')
544			errx(1, "option %.1s requires an unsigned integer", p);
545		*num = 0;
546		return(p);
547	}
548	*num = atoi(t);
549	while (*++t)
550		if (!isdigit((unsigned char)*t))
551			break;
552	return(--t);
553}
554