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