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