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