1 /* $OpenBSD: rs.c,v 1.30 2015/12/03 12:23:15 schwarze 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 /*
33 * rs - reshape a data array
34 * Author: John Kunze, Office of Comp. Affairs, UCB
35 * BEWARE: lots of unfinished edges
36 */
37
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <locale.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 struct entry {
49 int w; /* Display width. */
50 char *s; /* Multibyte string. */
51 };
52
53 long flags;
54 #define TRANSPOSE 000001
55 #define MTRANSPOSE 000002
56 #define ONEPERLINE 000004
57 #define ONEISEPONLY 000010
58 #define ONEOSEPONLY 000020
59 #define NOTRIMENDCOL 000040
60 #define SQUEEZE 000100
61 #define SHAPEONLY 000200
62 #define DETAILSHAPE 000400
63 #define RIGHTADJUST 001000
64 #define NULLPAD 002000
65 #define RECYCLE 004000
66 #define SKIPPRINT 010000
67 #define ONEPERCHAR 0100000
68 #define NOARGS 0200000
69
70 short *colwidths;
71 int nelem;
72 struct entry *elem;
73 struct entry *endelem;
74 char *curline;
75 int allocsize = BUFSIZ;
76 int irows, icols;
77 int orows, ocols;
78 int maxwidth;
79 int skip;
80 int propgutter;
81 char isep = ' ', osep = ' ';
82 int owidth = 80, gutter = 2;
83
84 int mbsavis(char **, const char *);
85
86 void usage(void);
87 void getargs(int, char *[]);
88 void getfile(void);
89 int get_line(void);
90 struct entry *getptrs(struct entry *);
91 void prepfile(void);
92 void prints(struct entry *, int);
93 void putfile(void);
94
95 #define INCR(ep) do { \
96 if (++ep >= endelem) \
97 ep = getptrs(ep); \
98 } while(0)
99
100 int
main(int argc,char * argv[])101 main(int argc, char *argv[])
102 {
103 setlocale(LC_CTYPE, "");
104
105 if (pledge("stdio", NULL) == -1)
106 err(1, "pledge");
107
108 getargs(argc, argv);
109 getfile();
110 if (flags & SHAPEONLY) {
111 printf("%d %d\n", irows, icols);
112 exit(0);
113 }
114 prepfile();
115 putfile();
116 exit(0);
117 }
118
119 void
getfile(void)120 getfile(void)
121 {
122 const char delim[2] = { isep, '\0' };
123 char *p;
124 struct entry *ep;
125 int multisep = (flags & ONEISEPONLY ? 0 : 1);
126 int nullpad = flags & NULLPAD;
127 struct entry *padto;
128
129 curline = NULL;
130 while (skip--) {
131 if (get_line() == EOF)
132 return;
133 if (flags & SKIPPRINT)
134 puts(curline);
135 }
136 if (get_line() == EOF)
137 return;
138 if (flags & NOARGS && strlen(curline) < (size_t)owidth)
139 flags |= ONEPERLINE;
140 if (flags & ONEPERLINE)
141 icols = 1;
142 else /* count cols on first line */
143 for (p = curline; *p != '\0'; p++) {
144 if (*p == isep && multisep)
145 continue;
146 icols++;
147 while (*p && *p != isep)
148 p++;
149 }
150 ep = getptrs(NULL);
151 p = curline;
152 do {
153 if (flags & ONEPERLINE) {
154 ep->w = mbsavis(&ep->s, curline);
155 if (maxwidth < ep->w)
156 maxwidth = ep->w;
157 INCR(ep); /* prepare for next entry */
158 irows++;
159 continue;
160 }
161 p = curline;
162 while (p != NULL && *p != '\0') {
163 if (*p == isep) {
164 p++;
165 if (multisep)
166 continue;
167 ep->s = ""; /* empty column */
168 ep->w = 0;
169 } else
170 ep->w = mbsavis(&ep->s, strsep(&p, delim));
171 if (maxwidth < ep->w)
172 maxwidth = ep->w;
173 INCR(ep); /* prepare for next entry */
174 }
175 irows++; /* update row count */
176 if (nullpad) { /* pad missing entries */
177 padto = elem + irows * icols;
178 while (ep < padto) {
179 ep->s = "";
180 ep->w = 0;
181 INCR(ep);
182 }
183 }
184 } while (get_line() != EOF);
185 nelem = ep - elem;
186 }
187
188 void
putfile(void)189 putfile(void)
190 {
191 struct entry *ep;
192 int i, j, n;
193
194 ep = elem;
195 if (flags & TRANSPOSE) {
196 for (i = 0; i < orows; i++) {
197 for (j = i; j < nelem; j += orows)
198 prints(ep + j, (j - i) / orows);
199 putchar('\n');
200 }
201 } else {
202 for (n = 0, i = 0; i < orows && n < nelem; i++) {
203 for (j = 0; j < ocols; j++) {
204 if (n++ >= nelem)
205 break;
206 prints(ep++, j);
207 }
208 putchar('\n');
209 }
210 }
211 }
212
213 void
prints(struct entry * ep,int col)214 prints(struct entry *ep, int col)
215 {
216 int n;
217
218 n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - ep->w);
219 if (flags & RIGHTADJUST)
220 while (n-- > 0)
221 putchar(osep);
222 fputs(ep->s, stdout);
223 while (n-- > 0)
224 putchar(osep);
225 }
226
227 void
usage(void)228 usage(void)
229 {
230 extern char *__progname;
231
232 fprintf(stderr,
233 "usage: %s [-CcSs[x]] [-GgKkw N] [-EeHhjmnTtyz] [rows [cols]]\n",
234 __progname);
235 exit(1);
236 }
237
238 void
prepfile(void)239 prepfile(void)
240 {
241 struct entry *ep;
242 int i;
243 int j;
244 struct entry *lp;
245 int colw;
246 int max = 0;
247 int n;
248
249 if (!nelem)
250 exit(0);
251 gutter += maxwidth * propgutter / 100.0;
252 colw = maxwidth + gutter;
253 if (flags & MTRANSPOSE) {
254 orows = icols;
255 ocols = irows;
256 }
257 else if (orows == 0 && ocols == 0) { /* decide rows and cols */
258 ocols = owidth / colw;
259 if (ocols == 0) {
260 warnx("Display width %d is less than column width %d",
261 owidth, colw);
262 ocols = 1;
263 }
264 if (ocols > nelem)
265 ocols = nelem;
266 orows = nelem / ocols + (nelem % ocols ? 1 : 0);
267 }
268 else if (orows == 0) /* decide on rows */
269 orows = nelem / ocols + (nelem % ocols ? 1 : 0);
270 else if (ocols == 0) /* decide on cols */
271 ocols = nelem / orows + (nelem % orows ? 1 : 0);
272 while ((lp = elem + orows * ocols) > endelem)
273 (void)getptrs(NULL);
274 if (flags & RECYCLE) {
275 for (ep = elem + nelem; ep < lp; ep++)
276 memcpy(ep, ep - nelem, sizeof(*ep));
277 nelem = lp - elem;
278 }
279 if (!(colwidths = calloc(ocols, sizeof(short))))
280 errx(1, "malloc: No gutter space");
281 if (flags & SQUEEZE) {
282 for (ep = elem, i = 0; i < ocols; i++) {
283 max = 0;
284 if (flags & TRANSPOSE) {
285 for (j = 0; j < orows; j++, ep++)
286 if (ep->w > max)
287 max = ep->w;
288 } else {
289 for (j = i; j < nelem; j += ocols)
290 if (ep[j].w > max)
291 max = ep[j].w;
292 }
293 colwidths[i] = max + gutter;
294 }
295 } else {
296 for (i = 0; i < ocols; i++)
297 colwidths[i] = colw;
298 }
299 if (!(flags & NOTRIMENDCOL)) {
300 if (flags & RIGHTADJUST)
301 colwidths[0] -= gutter;
302 else
303 colwidths[ocols - 1] = 0;
304 }
305 n = orows * ocols;
306 if (n > nelem && (flags & RECYCLE))
307 nelem = n;
308 }
309
310 int
get_line(void)311 get_line(void)
312 {
313 static size_t cursz;
314 static ssize_t curlen;
315
316 if (irows > 0 && flags & DETAILSHAPE)
317 printf(" %zd line %d\n", curlen, irows);
318
319 if ((curlen = getline(&curline, &cursz, stdin)) == EOF) {
320 if (ferror(stdin))
321 err(1, NULL);
322 return EOF;
323 }
324 if (curlen > 0 && curline[curlen - 1] == '\n')
325 curline[--curlen] = '\0';
326
327 return 0;
328 }
329
330 struct entry *
getptrs(struct entry * sp)331 getptrs(struct entry *sp)
332 {
333 struct entry *p;
334 int newsize;
335
336 newsize = allocsize * 2;
337 p = reallocarray(elem, newsize, sizeof(*p));
338 if (p == NULL)
339 err(1, "no memory");
340
341 allocsize = newsize;
342 sp = sp == NULL ? p : p + (sp - elem);
343 elem = p;
344 endelem = elem + allocsize;
345 return(sp);
346 }
347
348 void
getargs(int ac,char * av[])349 getargs(int ac, char *av[])
350 {
351 int ch;
352 const char *errstr;
353
354 if (ac == 1)
355 flags |= NOARGS | TRANSPOSE;
356 while ((ch = getopt(ac, av, "c::C::s::S::k:K:g:G:w:tTeEnyjhHmz")) != -1) {
357 switch (ch) {
358 case 'T':
359 flags |= MTRANSPOSE;
360 /* FALLTHROUGH */
361 case 't':
362 flags |= TRANSPOSE;
363 break;
364 case 'c': /* input col. separator */
365 flags |= ONEISEPONLY;
366 /* FALLTHROUGH */
367 case 's': /* one or more allowed */
368 if (optarg == NULL)
369 isep = '\t'; /* default is ^I */
370 else if (optarg[1] != '\0')
371 usage(); /* single char only */
372 else
373 isep = *optarg;
374 break;
375 case 'C':
376 flags |= ONEOSEPONLY;
377 /* FALLTHROUGH */
378 case 'S':
379 if (optarg == NULL)
380 osep = '\t'; /* default is ^I */
381 else if (optarg[1] != '\0')
382 usage(); /* single char only */
383 else
384 osep = *optarg;
385 break;
386 case 'w': /* window width, default 80 */
387 owidth = strtonum(optarg, 1, INT_MAX, &errstr);
388 if (errstr) {
389 warnx("width %s", errstr);
390 usage();
391 }
392 break;
393 case 'K': /* skip N lines */
394 flags |= SKIPPRINT;
395 /* FALLTHROUGH */
396 case 'k': /* skip, do not print */
397 skip = strtonum(optarg, 0, INT_MAX, &errstr);
398 if (errstr) {
399 warnx("skip value %s", errstr);
400 usage();
401 }
402 if (skip == 0)
403 skip = 1;
404 break;
405 case 'm':
406 flags |= NOTRIMENDCOL;
407 break;
408 case 'g': /* gutter width */
409 gutter = strtonum(optarg, 0, INT_MAX, &errstr);
410 if (errstr) {
411 warnx("gutter width %s", errstr);
412 usage();
413 }
414 break;
415 case 'G':
416 propgutter = strtonum(optarg, 0, INT_MAX, &errstr);
417 if (errstr) {
418 warnx("gutter proportion %s", errstr);
419 usage();
420 }
421 break;
422 case 'e': /* each line is an entry */
423 flags |= ONEPERLINE;
424 break;
425 case 'E':
426 flags |= ONEPERCHAR;
427 break;
428 case 'j': /* right adjust */
429 flags |= RIGHTADJUST;
430 break;
431 case 'n': /* null padding for missing values */
432 flags |= NULLPAD;
433 break;
434 case 'y':
435 flags |= RECYCLE;
436 break;
437 case 'H': /* print shape only */
438 flags |= DETAILSHAPE;
439 /* FALLTHROUGH */
440 case 'h':
441 flags |= SHAPEONLY;
442 break;
443 case 'z': /* squeeze col width */
444 flags |= SQUEEZE;
445 break;
446 default:
447 usage();
448 }
449 }
450 ac -= optind;
451 av += optind;
452
453 switch (ac) {
454 case 2:
455 ocols = strtonum(av[1], 0, INT_MAX, &errstr);
456 if (errstr) {
457 warnx("columns value %s", errstr);
458 usage();
459 }
460 /* FALLTHROUGH */
461 case 1:
462 orows = strtonum(av[0], 0, INT_MAX, &errstr);
463 if (errstr) {
464 warnx("columns value %s", errstr);
465 usage();
466 }
467 /* FALLTHROUGH */
468 case 0:
469 break;
470 default:
471 usage();
472 }
473 }
474