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