xref: /original-bsd/usr.bin/rs/rs.c (revision 60c3b96a)
1 /* Copyright (c) 1983 Regents of the University of California */
2 
3 #ifndef lint
4 static char sccsid[] = "@(#)rs.c	4.3	(Berkeley)	04/05/86";
5 #endif not lint
6 
7 /*
8  *	rs - reshape a data array
9  *	Author:  John Kunze, Office of Comp. Affairs, UCB
10  *		BEWARE: lots of unfinished edges
11  */
12 
13 #include <stdio.h>
14 #include <ctype.h>
15 
16 long	flags;
17 #define	TRANSPOSE	000001
18 #define	MTRANSPOSE	000002
19 #define	ONEPERLINE	000004
20 #define	ONEISEPONLY	000010
21 #define	ONEOSEPONLY	000020
22 #define	NOTRIMENDCOL	000040
23 #define	SQUEEZE		000100
24 #define	SHAPEONLY	000200
25 #define	DETAILSHAPE	000400
26 #define	RIGHTADJUST	001000
27 #define	NULLPAD		002000
28 #define	RECYCLE		004000
29 #define	SKIPPRINT	010000
30 #define	ICOLBOUNDS	020000
31 #define	OCOLBOUNDS	040000
32 #define ONEPERCHAR	0100000
33 #define NOARGS		0200000
34 
35 char	buf[BUFSIZ];
36 short	*colwidths;
37 short	*cord;
38 short	*icbd;
39 short	*ocbd;
40 int	nelem;
41 char	**elem;
42 char	**endelem;
43 char	*curline;
44 int	allocsize = BUFSIZ;
45 int	curlen;
46 int	irows, icols;
47 int	orows, ocols;
48 int	maxlen;
49 int	skip;
50 int	propgutter;
51 char	isep = ' ', osep = ' ';
52 int	owidth = 80, gutter = 2;
53 
54 char	**getptrs();
55 
56 main(argc, argv)
57 int	argc;
58 char	**argv;
59 {
60 	setbuf(stdout, buf);
61 	getargs(argc, argv);
62 	getfile();
63 	if (flags & SHAPEONLY) {
64 		printf("%d %d\n", irows, icols);
65 		exit(0);
66 	}
67 	prepfile();
68 	/*fprintf(stderr, "#irows %d icols %d orows %d ocols %d\n",irows,icols,orows,ocols);*/
69 	putfile();
70 	exit(0);
71 }
72 
73 getfile()
74 {
75 	register char	*p;
76 	register char	*endp;
77 	register char	**ep = 0;
78 	int	multisep = (flags & ONEISEPONLY ? 0 : 1);
79 	int	nullpad = flags & NULLPAD;
80 	char	**padto;
81 
82 	while (skip--) {
83 		getline();
84 		if (flags & SKIPPRINT)
85 			puts(curline);
86 	}
87 	getline();
88 	if (flags & NOARGS && curlen < owidth)
89 		flags |= ONEPERLINE;
90 	if (flags & ONEPERLINE)
91 		icols = 1;
92 	else				/* count cols on first line */
93 		for (p = curline, endp = curline + curlen; p < endp; p++) {
94 			if (*p == isep && multisep)
95 				continue;
96 			icols++;
97 			while (*p && *p != isep)
98 				p++;
99 		}
100 	ep = getptrs(elem);
101 	p = curline;
102 	do {
103 		if (flags & ONEPERLINE) {
104 			*ep++ = curline;
105 			if (maxlen < curlen)
106 				maxlen = curlen;
107 			irows++;
108 			continue;
109 		}
110 		for (p = curline, endp = curline + curlen; p < endp; p++) {
111 			if (*p == isep && multisep)
112 				continue;	/* eat up column separators */
113 			if (*p == isep)		/* must be an empty column */
114 				*ep = "";
115 			else			/* store column entry */
116 				*ep = p;
117 			while (p < endp && *p != isep)
118 				p++;		/* find end of entry */
119 			*p = '\0';		/* mark end of entry */
120 			if (maxlen < p - *ep)	/* update maxlen */
121 				maxlen = p - *ep;
122 			ep++;			/* prepare for next entry */
123 		}
124 		irows++;			/* update row count */
125 		if (nullpad) {			/* pad missing entries */
126 			padto = elem + irows * icols;
127 			while  (ep < padto)
128 				*ep++ = "";
129 		}
130 	if (ep > endelem)			/* if low on pointers */
131 		ep = getptrs(ep);		/* get some more */
132 	} while (getline() != EOF);
133 	*ep = 0;				/* mark end of pointers */
134 	nelem = ep - elem;
135 }
136 
137 putfile()
138 {
139 	register char	**ep;
140 	register int	i;
141 	register int	j;
142 
143 	ep = elem;
144 	if (flags & TRANSPOSE)
145 		for (i = 0; i < orows; i++) {
146 			for (j = i; j < nelem; j += orows)
147 				prints(ep[j], (j - i) / orows);
148 			putchar('\n');
149 		}
150 	else
151 		for (i = 0; i < orows; i++) {
152 			for (j = 0; j < ocols; j++)
153 				prints(*ep++, j);
154 			putchar('\n');
155 		}
156 }
157 
158 prints(s, col)
159 char	*s;
160 int	col;
161 {
162 	register char	*p = s;
163 	register int	n;
164 
165 	while (*p)
166 		p++;
167 	n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
168 	if (flags & RIGHTADJUST)
169 		while (n-- > 0)
170 			putchar(osep);
171 	for (p = s; *p; p++)
172 		putchar(*p);
173 	while (n-- > 0)
174 		putchar(osep);
175 }
176 
177 error(msg, s)
178 char	*msg;
179 char	*s;
180 {
181 	fprintf(stderr, "rs:  ");
182 	fprintf(stderr, msg, s);
183 	fprintf(stderr, "\nUsage:  rs [ -[csCS][x][kKgGw][N]tTeEnyjhHm ] [ rows [ cols ] ]\n");
184 	exit(1);
185 }
186 
187 prepfile()
188 {
189 	register char	**ep;
190 	register int 	i;
191 	register int 	j;
192 	char	**lp;
193 	int	colw;
194 	int	max = 0;
195 	int	n;
196 
197 	if (!nelem)
198 		exit(0);
199 	gutter += maxlen * propgutter / 100.0;
200 	colw = maxlen + gutter;
201 	if (flags & MTRANSPOSE) {
202 		orows = icols;
203 		ocols = irows;
204 	}
205 	else if (orows == 0 && ocols == 0) {	/* decide rows and cols */
206 		ocols = owidth / colw;
207 		if (ocols == 0)
208 			fprintf(stderr, "Display width %d is less than column width %d\n", owidth, colw);
209 		if (ocols > nelem)
210 			ocols = nelem;
211 		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
212 	}
213 	else if (orows == 0)			/* decide on rows */
214 		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
215 	else if (ocols == 0)			/* decide on cols */
216 		ocols = nelem / orows + (nelem % orows ? 1 : 0);
217 	lp = elem + orows * ocols;
218 	while (lp > endelem) {
219 		getptrs(elem + nelem);
220 		lp = elem + orows * ocols;
221 	}
222 	if (flags & RECYCLE) {
223 		for (ep = elem + nelem; ep < lp; ep++)
224 			*ep = *(ep - nelem);
225 		nelem = lp - elem;
226 	}
227 	if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
228 		error("malloc:  No gutter space", "");
229 	if (flags & SQUEEZE) {
230 		if (flags & TRANSPOSE)
231 			for (ep = elem, i = 0; i < ocols; i++) {
232 				for (j = 0; j < orows; j++)
233 					if ((n = strlen(*ep++)) > max)
234 						max = n;
235 				colwidths[i] = max + gutter;
236 			}
237 		else
238 			for (i = 0; i < ocols; i++) {
239 				for (j = i; j < nelem; j += ocols)
240 					if ((n = strlen(ep[j])) > max)
241 						max = n;
242 				colwidths[i] = max + gutter;
243 			}
244 	}
245 	/*	for (i = 0; i < orows; i++) {
246 			for (j = i; j < nelem; j += orows)
247 				prints(ep[j], (j - i) / orows);
248 			putchar('\n');
249 		}
250 	else
251 		for (i = 0; i < orows; i++) {
252 			for (j = 0; j < ocols; j++)
253 				prints(*ep++, j);
254 			putchar('\n');
255 		}*/
256 	else
257 		for (i = 0; i < ocols; i++)
258 			colwidths[i] = colw;
259 	if (!(flags & NOTRIMENDCOL)) {
260 		if (flags & RIGHTADJUST)
261 			colwidths[0] -= gutter;
262 		else
263 			colwidths[ocols - 1] = 0;
264 	}
265 	n = orows * ocols;
266 	if (n > nelem && (flags & RECYCLE))
267 		nelem = n;
268 	/*for (i = 0; i < ocols; i++)
269 		fprintf(stderr, "%d ",colwidths[i]);
270 	fprintf(stderr, "is colwidths, nelem %d\n", nelem);*/
271 }
272 
273 #define	BSIZE	2048
274 char	ibuf[BSIZE];		/* two screenfuls should do */
275 
276 getline()	/* get line; maintain curline, curlen; manage storage */
277 {
278 	register char	*p;
279 	register int	c;
280 	register int	i;
281 	static	int	putlength;
282 	static	char	*endblock = ibuf + BSIZE;
283 
284 	if (!irows) {
285 		curline = ibuf;
286 		putlength = flags & DETAILSHAPE;
287 	}
288 	else if (skip <= 0) {			/* don't waste storage */
289 		curline += curlen + 1;
290 		if (putlength)		/* print length, recycle storage */
291 			printf(" %d line %d\n", curlen, irows);
292 	}
293 	if (!putlength && endblock - curline < BUFSIZ) {   /* need storage */
294 		/*ww = endblock-curline; tt += ww;*/
295 		/*printf("#wasted %d total %d\n",ww,tt);*/
296 		if (!(curline = (char *) malloc(BSIZE)))
297 			error("File too large", "");
298 		endblock = curline + BSIZE;
299 		/*printf("#endb %d curline %d\n",endblock,curline);*/
300 	}
301 	for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++)
302 		if ((c = getchar()) == EOF || c == '\n')
303 			break;
304 	*p = '\0';
305 	curlen = i - 1;
306 	return(c);
307 }
308 
309 char	**
310 getptrs(sp)
311 char	**sp;
312 {
313 	register char	**p;
314 	register char	**ep;
315 
316 	for (;;) {
317 		allocsize += allocsize;
318 		if (!(p = (char **) malloc(allocsize * sizeof(char *)))) {
319 			perror("rs");
320 			exit(1);
321 		}
322 		if ((endelem = p + allocsize - icols) <= p) {
323 			free(p);
324 			continue;
325 		}
326 		if (elem != 0)
327 			free(elem);
328 		ep = elem;
329 		elem = p;
330 		while (ep < sp)
331 			*p++ = *ep++;
332 		return(p);
333 	}
334 }
335 
336 getargs(ac, av)
337 int	ac;
338 char	**av;
339 {
340 	register char	*p;
341 	char	*getnum(), *getlist();
342 
343 	if (ac == 1) {
344 		flags |= NOARGS | TRANSPOSE;
345 	}
346 	while (--ac && **++av == '-')
347 		for (p = *av+1; *p; p++)
348 			switch (*p) {
349 			case 'T':
350 				flags |= MTRANSPOSE;
351 			case 't':
352 				flags |= TRANSPOSE;
353 				break;
354 			case 'c':		/* input col. separator */
355 				flags |= ONEISEPONLY;
356 			case 's':		/* one or more allowed */
357 				if (p[1])
358 					isep = *++p;
359 				else
360 					isep = '\t';	/* default is ^I */
361 				break;
362 			case 'C':
363 				flags |= ONEOSEPONLY;
364 			case 'S':
365 				if (p[1])
366 					osep = *++p;
367 				else
368 					osep = '\t';	/* default is ^I */
369 				break;
370 			case 'w':		/* window width, default 80 */
371 				p = getnum(&owidth, p, 0);
372 				if (owidth <= 0)
373 					error("Width must be a positive integer", "");
374 				break;
375 			case 'K':			/* skip N lines */
376 				flags |= SKIPPRINT;
377 			case 'k':			/* skip, do not print */
378 				p = getnum(&skip, p, 0);
379 				if (!skip)
380 					skip = 1;
381 				break;
382 			case 'm':
383 				flags |= NOTRIMENDCOL;
384 				break;
385 			case 'g':		/* gutter space */
386 				p = getnum(&gutter, p, 0);
387 				break;
388 			case 'G':
389 				p = getnum(&propgutter, p, 0);
390 				break;
391 			case 'e':		/* each line is an entry */
392 				flags |= ONEPERLINE;
393 				break;
394 			case 'E':
395 				flags |= ONEPERCHAR;
396 				break;
397 			case 'j':			/* right adjust */
398 				flags |= RIGHTADJUST;
399 				break;
400 			case 'n':	/* null padding for missing values */
401 				flags |= NULLPAD;
402 				break;
403 			case 'y':
404 				flags |= RECYCLE;
405 				break;
406 			case 'H':			/* print shape only */
407 				flags |= DETAILSHAPE;
408 			case 'h':
409 				flags |= SHAPEONLY;
410 				break;
411 			case 'z':			/* squeeze col width */
412 				flags |= SQUEEZE;
413 				break;
414 			/*case 'p':
415 				ipagespace = atoi(++p);	(default is 1)
416 				break;*/
417 			case 'o':			/* col order */
418 				p = getlist(&cord, p);
419 				break;
420 			case 'b':
421 				flags |= ICOLBOUNDS;
422 				p = getlist(&icbd, p);
423 				break;
424 			case 'B':
425 				flags |= OCOLBOUNDS;
426 				p = getlist(&ocbd, p);
427 				break;
428 			default:
429 				error("Bad flag:  %.1s", p);
430 			}
431 	/*if (!osep)
432 		osep = isep;*/
433 	switch (ac) {
434 	/*case 3:
435 		opages = atoi(av[2]);*/
436 	case 2:
437 		ocols = atoi(av[1]);
438 	case 1:
439 		orows = atoi(av[0]);
440 	case 0:
441 		break;
442 	default:
443 		error("Too many arguments.  What do you mean by `%s'?", av[3]);
444 	}
445 }
446 
447 char	*
448 getlist(list, p)
449 short	**list;
450 char	*p;
451 {
452 	register char	*t;
453 	register int	count = 1;
454 
455 	for (t = p + 1; *t; t++) {
456 		if (!isdigit(*t))
457 			error("Option %.1s requires a list of unsigned numbers separated by commas", t);
458 		count++;
459 		while (*t && isdigit(*t))
460 			t++;
461 		if (*t != ',')
462 			break;
463 	}
464 	if (!(*list = (short *) malloc(count * sizeof(short))))
465 		error("No list space", "");
466 	count = 0;
467 	for (t = p + 1; *t; t++) {
468 		(*list)[count++] = atoi(t);
469 		printf("++ %d ", (*list)[count-1]);
470 		fflush(stdout);
471 		while (*t && isdigit(*t))
472 			t++;
473 		if (*t != ',')
474 			break;
475 	}
476 	(*list)[count] = 0;
477 	return(t - 1);
478 }
479 
480 char	*
481 getnum(num, p, strict)	/* num = number p points to; if (strict) complain */
482 int	*num;				/* returns pointer to end of num */
483 char	*p;
484 int	strict;
485 {
486 	register char	*t = p;
487 
488 	if (!isdigit(*++t)) {
489 		if (strict || *t == '-' || *t == '+')
490 			error("Option %.1s requires an unsigned integer", p);
491 		*num = 0;
492 		return(p);
493 	}
494 	*num = atoi(t);
495 	while (*++t)
496 		if (!isdigit(*t))
497 			break;
498 	return(--t);
499 }
500