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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static const char sccsid[] = "@(#)rs.c	8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44/*
45 *	rs - reshape a data array
46 *	Author:  John Kunze, Office of Comp. Affairs, UCB
47 *		BEWARE: lots of unfinished edges
48 */
49
50#include <sys/cdefs.h>
51__FBSDID("$FreeBSD: src/usr.bin/rs/rs.c,v 1.13 2005/04/28 12:37:15 robert Exp $");
52
53#include <err.h>
54#include <ctype.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <sysexits.h>
59
60long	flags;
61#define	TRANSPOSE	000001
62#define	MTRANSPOSE	000002
63#define	ONEPERLINE	000004
64#define	ONEISEPONLY	000010
65#define	ONEOSEPONLY	000020
66#define	NOTRIMENDCOL	000040
67#define	SQUEEZE		000100
68#define	SHAPEONLY	000200
69#define	DETAILSHAPE	000400
70#define	RIGHTADJUST	001000
71#define	NULLPAD		002000
72#define	RECYCLE		004000
73#define	SKIPPRINT	010000
74#define	ICOLBOUNDS	020000
75#define	OCOLBOUNDS	040000
76#define ONEPERCHAR	0100000
77#define NOARGS		0200000
78
79short	*colwidths;
80short	*cord;
81short	*icbd;
82short	*ocbd;
83int	nelem;
84char	**elem;
85char	**endelem;
86char	*curline;
87int	allocsize = BUFSIZ;
88int	curlen;
89int	irows, icols;
90int	orows = 0, ocols = 0;
91int	maxlen;
92int	skip;
93int	propgutter;
94char	isep = ' ', osep = ' ';
95char	blank[] = "";
96int	owidth = 80, gutter = 2;
97
98void	  getargs(int, char *[]);
99void	  getfile(void);
100int	  rs_getline(void);
101char	 *getlist(short **, char *);
102char	 *getnum(int *, char *, int);
103char	**getptrs(char **);
104void	  prepfile(void);
105void	  prints(char *, int);
106void	  putfile(void);
107static void usage(void);
108
109#define	INCR(ep) do {			\
110	if (++ep >= endelem)		\
111		ep = getptrs(ep);	\
112} while(0)
113
114int
115main(int argc, char *argv[])
116{
117	getargs(argc, argv);
118	getfile();
119	if (flags & SHAPEONLY) {
120		printf("%d %d\n", irows, icols);
121		exit(0);
122	}
123	prepfile();
124	putfile();
125	exit(0);
126}
127
128void
129getfile(void)
130{
131	char *p;
132	char *endp;
133	char **ep;
134	int multisep = (flags & ONEISEPONLY ? 0 : 1);
135	int nullpad = flags & NULLPAD;
136	char **padto;
137
138	while (skip--) {
139		rs_getline();
140		if (flags & SKIPPRINT)
141			puts(curline);
142	}
143	rs_getline();
144	if (flags & NOARGS && curlen < owidth)
145		flags |= ONEPERLINE;
146	if (flags & ONEPERLINE)
147		icols = 1;
148	else				/* count cols on first line */
149		for (p = curline, endp = curline + curlen; p < endp; p++) {
150			if (*p == isep && multisep)
151				continue;
152			icols++;
153			while (*p && *p != isep)
154				p++;
155		}
156	ep = getptrs(elem);
157	p = curline;
158	do {
159		if (flags & ONEPERLINE) {
160			*ep = curline;
161			INCR(ep);		/* prepare for next entry */
162			if (maxlen < curlen)
163				maxlen = curlen;
164			irows++;
165			continue;
166		}
167		for (p = curline, endp = curline + curlen; p < endp; p++) {
168			if (*p == isep && multisep)
169				continue;	/* eat up column separators */
170			if (*p == isep)		/* must be an empty column */
171				*ep = blank;
172			else			/* store column entry */
173				*ep = p;
174			while (p < endp && *p != isep)
175				p++;		/* find end of entry */
176			*p = '\0';		/* mark end of entry */
177			if (maxlen < p - *ep)	/* update maxlen */
178				maxlen = p - *ep;
179			INCR(ep);		/* prepare for next entry */
180		}
181		irows++;			/* update row count */
182		if (nullpad) {			/* pad missing entries */
183			padto = elem + irows * icols;
184			while (ep < padto) {
185				*ep = blank;
186				INCR(ep);
187			}
188		}
189	} while (rs_getline() != EOF);
190	*ep = 0;				/* mark end of pointers */
191	nelem = ep - elem;
192}
193
194void
195putfile(void)
196{
197	char **ep;
198	int i, j, k;
199
200	ep = elem;
201	if (flags & TRANSPOSE)
202		for (i = 0; i < orows; i++) {
203			for (j = i; j < nelem; j += orows)
204				prints(ep[j], (j - i) / orows);
205			putchar('\n');
206		}
207	else
208		for (i = k = 0; i < orows; i++) {
209			for (j = 0; j < ocols; j++, k++)
210				if (k < nelem)
211					prints(ep[k], j);
212			putchar('\n');
213		}
214}
215
216void
217prints(char *s, int col)
218{
219	int n;
220	char *p = s;
221
222	while (*p)
223		p++;
224	n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
225	if (flags & RIGHTADJUST)
226		while (n-- > 0)
227			putchar(osep);
228	for (p = s; *p; p++)
229		putchar(*p);
230	while (n-- > 0)
231		putchar(osep);
232}
233
234static void
235usage(void)
236{
237	fprintf(stderr,
238		"usage: rs [-[csCS][x][kKgGw][N]tTeEnyjhHmz] [rows [cols]]\n");
239	exit(1);
240}
241
242void
243prepfile(void)
244{
245	char **ep;
246	int  i;
247	int  j;
248	char **lp;
249	int colw;
250	int max;
251	int n;
252
253	if (!nelem)
254		exit(0);
255	gutter += maxlen * propgutter / 100.0;
256	colw = maxlen + gutter;
257	if (flags & MTRANSPOSE) {
258		orows = icols;
259		ocols = irows;
260	}
261	else if (orows == 0 && ocols == 0) {	/* decide rows and cols */
262		ocols = owidth / colw;
263		if (ocols == 0) {
264			warnx("display width %d is less than column width %d",
265					owidth, colw);
266			ocols = 1;
267		}
268		if (ocols > nelem)
269			ocols = nelem;
270		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
271	}
272	else if (orows == 0)			/* decide on rows */
273		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
274	else if (ocols == 0)			/* decide on cols */
275		ocols = nelem / orows + (nelem % orows ? 1 : 0);
276	lp = elem + orows * ocols;
277	while (lp > endelem) {
278		getptrs(elem + nelem);
279		lp = elem + orows * ocols;
280	}
281	if (flags & RECYCLE) {
282		for (ep = elem + nelem; ep < lp; ep++)
283			*ep = *(ep - nelem);
284		nelem = lp - elem;
285	}
286	if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
287		errx(1, "malloc");
288	if (flags & SQUEEZE) {
289		ep = elem;
290		if (flags & TRANSPOSE)
291			for (i = 0; i < ocols; i++) {
292				max = 0;
293				for (j = 0; *ep != NULL && j < orows; j++)
294					if ((n = strlen(*ep++)) > max)
295						max = n;
296				colwidths[i] = max + gutter;
297			}
298		else
299			for (i = 0; i < ocols; i++) {
300				max = 0;
301				for (j = i; j < nelem; j += ocols)
302					if ((n = strlen(ep[j])) > max)
303						max = n;
304				colwidths[i] = max + gutter;
305			}
306	}
307	/*	for (i = 0; i < orows; i++) {
308			for (j = i; j < nelem; j += orows)
309				prints(ep[j], (j - i) / orows);
310			putchar('\n');
311		}
312	else
313		for (i = 0; i < orows; i++) {
314			for (j = 0; j < ocols; j++)
315				prints(*ep++, j);
316			putchar('\n');
317		}*/
318	else
319		for (i = 0; i < ocols; i++)
320			colwidths[i] = colw;
321	if (!(flags & NOTRIMENDCOL)) {
322		if (flags & RIGHTADJUST)
323			colwidths[0] -= gutter;
324		else
325			colwidths[ocols - 1] = 0;
326	}
327	n = orows * ocols;
328	if (n > nelem && (flags & RECYCLE))
329		nelem = n;
330	/*for (i = 0; i < ocols; i++)
331		warnx("%d is colwidths, nelem %d", colwidths[i], nelem);*/
332}
333
334#define	BSIZE	2048
335char	ibuf[BSIZE];		/* two screenfuls should do */
336
337int
338rs_getline(void)	/* get line; maintain curline, curlen; manage storage */
339{
340	static	int putlength;
341	static	char *endblock = ibuf + BSIZE;
342	char *p;
343	int c, i;
344
345	if (!irows) {
346		curline = ibuf;
347		putlength = flags & DETAILSHAPE;
348	}
349	else if (skip <= 0) {			/* don't waste storage */
350		curline += curlen + 1;
351		if (putlength) {	/* print length, recycle storage */
352			printf(" %d line %d\n", curlen, irows);
353			curline = ibuf;
354		}
355	}
356	if (!putlength && endblock - curline < BUFSIZ) {   /* need storage */
357		/*ww = endblock-curline; tt += ww;*/
358		/*printf("#wasted %d total %d\n",ww,tt);*/
359		if (!(curline = (char *) malloc(BSIZE)))
360			errx(1, "file too large");
361		endblock = curline + BSIZE;
362		/*printf("#endb %d curline %d\n",endblock,curline);*/
363	}
364	for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++)
365		if ((c = getchar()) == EOF || c == '\n')
366			break;
367	if (ferror(stdin)) {
368		errx(EX_IOERR, "Read error");
369	}
370	*p = '\0';
371	curlen = i - 1;
372	return(c);
373}
374
375char **
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
390void
391getargs(int ac, char *av[])
392{
393	char *p;
394
395	if (ac == 1) {
396		flags |= NOARGS | TRANSPOSE;
397	}
398	while (--ac>0 && *++av && **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
501char *
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 */
537char *
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