[BACK]Return to column.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / column

Annotation of src/usr.bin/column/column.c, Revision 1.26

1.26    ! rob         1: /*     $OpenBSD: column.c,v 1.25 2016/09/04 20:33:36 martijn Exp $     */
1.1       deraadt     2: /*     $NetBSD: column.c,v 1.4 1995/09/02 05:53:03 jtc Exp $   */
                      3:
                      4: /*
                      5:  * Copyright (c) 1989, 1993, 1994
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
1.8       millert    16:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    17:  *    may be used to endorse or promote products derived from this software
                     18:  *    without specific prior written permission.
                     19:  *
                     20:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     21:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     22:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     23:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     24:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     25:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     26:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     27:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     28:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     29:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     30:  * SUCH DAMAGE.
                     31:  */
                     32:
                     33: #include <sys/types.h>
                     34: #include <sys/ioctl.h>
                     35:
                     36: #include <ctype.h>
                     37: #include <err.h>
                     38: #include <limits.h>
1.25      martijn    39: #include <locale.h>
1.1       deraadt    40: #include <stdio.h>
                     41: #include <stdlib.h>
                     42: #include <string.h>
                     43: #include <unistd.h>
1.25      martijn    44: #include <wchar.h>
                     45: #include <wctype.h>
1.1       deraadt    46:
1.7       millert    47: void  c_columnate(void);
1.18      espie      48: void *ereallocarray(void *, size_t, size_t);
1.7       millert    49: void  input(FILE *);
                     50: void  maketbl(void);
                     51: void  print(void);
                     52: void  r_columnate(void);
1.24      martijn    53: __dead void usage(void);
                     54:
                     55: struct field {
                     56:        char *content;
                     57:        int width;
                     58: };
1.1       deraadt    59:
1.23      bentley    60: int termwidth;                 /* default terminal width */
1.1       deraadt    61: int entries;                   /* number of records */
                     62: int eval;                      /* exit value */
1.24      martijn    63: int *maxwidths;                        /* longest record per column */
                     64: struct field **table;          /* one array of pointers per line */
1.25      martijn    65: wchar_t *separator = L"\t ";   /* field separator for table option */
1.1       deraadt    66:
                     67: int
1.9       deraadt    68: main(int argc, char *argv[])
1.1       deraadt    69: {
                     70:        struct winsize win;
                     71:        FILE *fp;
                     72:        int ch, tflag, xflag;
                     73:        char *p;
1.13      jdixon     74:        const char *errstr;
1.1       deraadt    75:
1.25      martijn    76:        setlocale(LC_CTYPE, "");
                     77:
1.23      bentley    78:        termwidth = 0;
                     79:        if ((p = getenv("COLUMNS")) != NULL)
                     80:                termwidth = strtonum(p, 1, INT_MAX, NULL);
                     81:        if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
                     82:            win.ws_col > 0)
1.1       deraadt    83:                termwidth = win.ws_col;
1.23      bentley    84:        if (termwidth == 0)
                     85:                termwidth = 80;
1.1       deraadt    86:
1.21      deraadt    87:        if (pledge("stdio rpath", NULL) == -1)
                     88:                err(1, "pledge");
1.20      deraadt    89:
1.1       deraadt    90:        tflag = xflag = 0;
1.24      martijn    91:        while ((ch = getopt(argc, argv, "c:s:tx")) != -1) {
1.1       deraadt    92:                switch(ch) {
                     93:                case 'c':
1.13      jdixon     94:                        termwidth = strtonum(optarg, 1, INT_MAX, &errstr);
                     95:                        if (errstr != NULL)
                     96:                                errx(1, "%s: %s", errstr, optarg);
1.1       deraadt    97:                        break;
                     98:                case 's':
1.25      martijn    99:                        if ((separator = reallocarray(NULL, strlen(optarg) + 1,
                    100:                            sizeof(*separator))) == NULL)
                    101:                                err(1, NULL);
                    102:                        if (mbstowcs(separator, optarg, strlen(optarg) + 1) ==
                    103:                            (size_t) -1)
                    104:                                err(1, "sep");
1.1       deraadt   105:                        break;
                    106:                case 't':
                    107:                        tflag = 1;
                    108:                        break;
                    109:                case 'x':
                    110:                        xflag = 1;
                    111:                        break;
                    112:                default:
                    113:                        usage();
                    114:                }
1.24      martijn   115:        }
                    116:
                    117:        if (!tflag)
1.25      martijn   118:                separator = L"";
1.1       deraadt   119:        argv += optind;
                    120:
1.24      martijn   121:        if (*argv == NULL) {
1.1       deraadt   122:                input(stdin);
1.20      deraadt   123:        } else {
                    124:                for (; *argv; ++argv) {
                    125:                        if ((fp = fopen(*argv, "r"))) {
                    126:                                input(fp);
                    127:                                (void)fclose(fp);
                    128:                        } else {
                    129:                                warn("%s", *argv);
                    130:                                eval = 1;
                    131:                        }
1.1       deraadt   132:                }
1.20      deraadt   133:        }
1.22      mmcc      134:
1.21      deraadt   135:        if (pledge("stdio", NULL) == -1)
                    136:                err(1, "pledge");
1.1       deraadt   137:
                    138:        if (!entries)
1.24      martijn   139:                return eval;
1.1       deraadt   140:
                    141:        if (tflag)
                    142:                maketbl();
1.24      martijn   143:        else if (*maxwidths >= termwidth)
1.1       deraadt   144:                print();
                    145:        else if (xflag)
                    146:                c_columnate();
                    147:        else
                    148:                r_columnate();
1.24      martijn   149:        return eval;
1.1       deraadt   150: }
                    151:
1.24      martijn   152: #define        INCR_NEXTTAB(x) (x = (x + 8) & ~7)
1.1       deraadt   153: void
1.9       deraadt   154: c_columnate(void)
1.1       deraadt   155: {
1.24      martijn   156:        int col, numcols;
                    157:        struct field **row;
1.1       deraadt   158:
1.24      martijn   159:        INCR_NEXTTAB(*maxwidths);
                    160:        if ((numcols = termwidth / *maxwidths) == 0)
                    161:                numcols = 1;
                    162:        for (col = 0, row = table;; ++row) {
                    163:                fputs((*row)->content, stdout);
1.1       deraadt   164:                if (!--entries)
                    165:                        break;
                    166:                if (++col == numcols) {
1.24      martijn   167:                        col = 0;
1.1       deraadt   168:                        putchar('\n');
                    169:                } else {
1.24      martijn   170:                        while (INCR_NEXTTAB((*row)->width) <= *maxwidths)
                    171:                                putchar('\t');
1.1       deraadt   172:                }
                    173:        }
1.24      martijn   174:        putchar('\n');
1.1       deraadt   175: }
                    176:
                    177: void
1.9       deraadt   178: r_columnate(void)
1.1       deraadt   179: {
1.24      martijn   180:        int base, col, numcols, numrows, row;
1.1       deraadt   181:
1.24      martijn   182:        INCR_NEXTTAB(*maxwidths);
                    183:        if ((numcols = termwidth / *maxwidths) == 0)
1.5       millert   184:                numcols = 1;
1.1       deraadt   185:        numrows = entries / numcols;
                    186:        if (entries % numcols)
                    187:                ++numrows;
                    188:
1.24      martijn   189:        for (base = row = 0; row < numrows; base = ++row) {
                    190:                for (col = 0; col < numcols; ++col, base += numrows) {
                    191:                        fputs(table[base]->content, stdout);
                    192:                        if (base + numrows >= entries)
1.1       deraadt   193:                                break;
1.24      martijn   194:                        while (INCR_NEXTTAB(table[base]->width) <= *maxwidths)
                    195:                                putchar('\t');
1.1       deraadt   196:                }
                    197:                putchar('\n');
                    198:        }
                    199: }
                    200:
                    201: void
1.9       deraadt   202: print(void)
1.1       deraadt   203: {
1.24      martijn   204:        int row;
1.1       deraadt   205:
1.24      martijn   206:        for (row = 0; row < entries; row++)
                    207:                puts(table[row]->content);
1.1       deraadt   208: }
                    209:
                    210:
                    211: void
1.9       deraadt   212: maketbl(void)
1.1       deraadt   213: {
1.24      martijn   214:        struct field **row;
                    215:        int col;
                    216:
                    217:        for (row = table; entries--; ++row) {
                    218:                for (col = 0; (*row)[col + 1].content != NULL; ++col)
                    219:                        printf("%s%*s  ", (*row)[col].content,
                    220:                            maxwidths[col] - (*row)[col].width, "");
                    221:                puts((*row)[col].content);
1.1       deraadt   222:        }
                    223: }
                    224:
                    225: #define        DEFNUM          1000
1.24      martijn   226: #define        DEFCOLS         25
1.1       deraadt   227:
                    228: void
1.9       deraadt   229: input(FILE *fp)
1.1       deraadt   230: {
1.24      martijn   231:        static int maxentry = 0;
                    232:        static int maxcols = 0;
                    233:        static struct field *cols = NULL;
1.25      martijn   234:        int col, width, twidth;
1.24      martijn   235:        size_t blen;
                    236:        ssize_t llen;
                    237:        char *p, *s, *buf = NULL;
1.25      martijn   238:        wchar_t wc;
                    239:        int wlen;
1.24      martijn   240:
                    241:        while ((llen = getline(&buf, &blen, fp)) > -1) {
                    242:                if (buf[llen - 1] == '\n')
                    243:                        buf[llen - 1] = '\0';
                    244:
                    245:                p = buf;
                    246:                for (col = 0;; col++) {
                    247:
                    248:                        /* Skip lines containing nothing but whitespace. */
                    249:
1.25      martijn   250:                        for (s = p; (wlen = mbtowc(&wc, s, MB_CUR_MAX)) > 0;
                    251:                             s += wlen)
                    252:                                if (!iswspace(wc))
1.24      martijn   253:                                        break;
                    254:                        if (*s == '\0')
                    255:                                break;
                    256:
                    257:                        /* Skip leading, multiple, and trailing separators. */
                    258:
1.25      martijn   259:                        while ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) > 0 &&
                    260:                            wcschr(separator, wc) != NULL)
                    261:                                p += wlen;
1.24      martijn   262:                        if (*p == '\0')
                    263:                                break;
                    264:
                    265:                        /*
                    266:                         * Found a non-empty field.
                    267:                         * Remember the start and measure the width.
                    268:                         */
                    269:
                    270:                        s = p;
                    271:                        width = 0;
1.25      martijn   272:                        while (*p != '\0') {
                    273:                                if ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) == -1) {
                    274:                                        width++;
                    275:                                        p++;
                    276:                                        continue;
                    277:                                }
                    278:                                if (wcschr(separator, wc) != NULL)
                    279:                                        break;
                    280:                                if (*p == '\t')
1.24      martijn   281:                                        INCR_NEXTTAB(width);
1.25      martijn   282:                                else  {
                    283:                                        width += (twidth = wcwidth(wc)) == -1 ?
                    284:                                            1 : twidth;
                    285:                                }
                    286:                                p += wlen;
1.24      martijn   287:                        }
                    288:
                    289:                        if (col + 1 >= maxcols) {
                    290:                                if (maxcols > INT_MAX - DEFCOLS)
                    291:                                        err(1, "too many columns");
                    292:                                maxcols += DEFCOLS;
                    293:                                cols = ereallocarray(cols, maxcols,
                    294:                                    sizeof(*cols));
                    295:                                maxwidths = ereallocarray(maxwidths, maxcols,
                    296:                                    sizeof(*maxwidths));
                    297:                                memset(maxwidths + col, 0,
                    298:                                    DEFCOLS * sizeof(*maxwidths));
                    299:                        }
                    300:
                    301:                        /*
                    302:                         * Remember the width of the field,
                    303:                         * NUL-terminate and remeber the content,
                    304:                         * and advance beyond the separator, if any.
                    305:                         */
                    306:
                    307:                        cols[col].width = width;
                    308:                        if (maxwidths[col] < width)
                    309:                                maxwidths[col] = width;
1.25      martijn   310:                        if (*p != '\0') {
                    311:                                *p = '\0';
                    312:                                p += wlen;
                    313:                        }
1.24      martijn   314:                        if ((cols[col].content = strdup(s)) == NULL)
                    315:                                err(1, NULL);
                    316:                }
                    317:                if (col == 0)
1.1       deraadt   318:                        continue;
1.24      martijn   319:
                    320:                /* Found a non-empty line; remember it. */
                    321:
1.1       deraadt   322:                if (entries == maxentry) {
1.24      martijn   323:                        if (maxentry > INT_MAX - DEFNUM)
                    324:                                errx(1, "too many input lines");
1.14      millert   325:                        maxentry += DEFNUM;
1.24      martijn   326:                        table = ereallocarray(table, maxentry, sizeof(*table));
1.1       deraadt   327:                }
1.24      martijn   328:                table[entries] = ereallocarray(NULL, col + 1,
                    329:                    sizeof(*(table[entries])));
                    330:                table[entries][col].content = NULL;
                    331:                while (col--)
                    332:                        table[entries][col] = cols[col];
                    333:                entries++;
1.1       deraadt   334:        }
                    335: }
                    336:
                    337: void *
1.24      martijn   338: ereallocarray(void *ptr, size_t nmemb, size_t size)
1.1       deraadt   339: {
1.24      martijn   340:        if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
1.14      millert   341:                err(1, NULL);
1.24      martijn   342:        return ptr;
1.1       deraadt   343: }
                    344:
1.24      martijn   345: __dead void
1.9       deraadt   346: usage(void)
1.1       deraadt   347: {
                    348:        (void)fprintf(stderr,
1.4       deraadt   349:            "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
1.1       deraadt   350:        exit(1);
                    351: }