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