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

Annotation of src/usr.bin/join/join.c, Revision 1.33

1.33    ! martijn     1: /* $OpenBSD: join.c,v 1.32 2018/11/14 15:16:09 martijn Exp $   */
1.3       deraadt     2:
1.1       deraadt     3: /*-
1.4       michaels    4:  * Copyright (c) 1991, 1993, 1994
                      5:  *     The Regents of the University of California.  All rights reserved.
1.1       deraadt     6:  *
                      7:  * This code is derived from software contributed to Berkeley by
1.4       michaels    8:  * Steve Hayman of the Computer Science Department, Indiana University,
                      9:  * Michiro Hikida and David Goodenough.
1.1       deraadt    10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
1.15      millert    19:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    20:  *    may be used to endorse or promote products derived from this software
                     21:  *    without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     33:  * SUCH DAMAGE.
                     34:  */
                     35:
1.4       michaels   36: #include <err.h>
1.31      martijn    37: #include <errno.h>
                     38: #include <limits.h>
                     39: #include <locale.h>
1.1       deraadt    40: #include <stdio.h>
                     41: #include <stdlib.h>
                     42: #include <string.h>
1.22      millert    43: #include <unistd.h>
1.31      martijn    44: #include <wchar.h>
1.1       deraadt    45:
                     46: /*
                     47:  * There's a structure per input file which encapsulates the state of the
                     48:  * file.  We repeatedly read lines from each file until we've read in all
                     49:  * the consecutive lines from the file with a common join field.  Then we
                     50:  * compare the set of lines with an equivalent set from the other file.
                     51:  */
                     52: typedef struct {
1.7       michaels   53:        char *line;                     /* line */
1.33    ! martijn    54:        size_t linealloc;               /* bytes allocated for line */
1.4       michaels   55:        char **fields;                  /* line field(s) */
                     56:        u_long fieldcnt;                /* line field(s) count */
1.7       michaels   57:        u_long fieldalloc;              /* line field(s) allocated count */
1.1       deraadt    58: } LINE;
                     59:
                     60: typedef struct {
1.7       michaels   61:        FILE *fp;                       /* file descriptor */
1.4       michaels   62:        u_long joinf;                   /* join field (-1, -2, -j) */
1.7       michaels   63:        int unpair;                     /* output unpairable lines (-a) */
1.17      otto       64:        u_long number;                  /* 1 for file 1, 2 for file 2 */
1.7       michaels   65:        LINE *set;                      /* set of lines with same field */
1.4       michaels   66:        int pushbool;                   /* if pushback is set */
                     67:        u_long pushback;                /* line on the stack */
                     68:        u_long setcnt;                  /* set count */
                     69:        u_long setalloc;                /* set allocated count */
1.1       deraadt    70: } INPUT;
1.29      otto       71: INPUT input1 = { NULL, 0, 0, 1, NULL, 0, 0, 0, 0 },
                     72:       input2 = { NULL, 0, 0, 2, NULL, 0, 0, 0, 0 };
1.1       deraadt    73:
                     74: typedef struct {
1.4       michaels   75:        u_long  filenum;        /* file number */
1.1       deraadt    76:        u_long  fieldno;        /* field number */
                     77: } OLIST;
                     78: OLIST *olist;                  /* output field list */
                     79: u_long olistcnt;               /* output field list count */
                     80: u_long olistalloc;             /* output field allocated count */
                     81:
                     82: int joinout = 1;               /* show lines with matched join fields (-v) */
                     83: int needsep;                   /* need separator character */
                     84: int spans = 1;                 /* span multiple delimiters (-t) */
                     85: char *empty;                   /* empty field replacement string (-e) */
1.31      martijn    86: wchar_t tabchar[] = L" \t";    /* delimiter characters (-t) */
1.1       deraadt    87:
1.14      millert    88: int  cmp(LINE *, u_long, LINE *, u_long);
                     89: void fieldarg(char *);
                     90: void joinlines(INPUT *, INPUT *);
1.31      martijn    91: char *mbssep(char **, const wchar_t *);
1.14      millert    92: void obsolete(char **);
                     93: void outfield(LINE *, u_long, int);
                     94: void outoneline(INPUT *, LINE *);
                     95: void outtwoline(INPUT *, LINE *, INPUT *, LINE *);
                     96: void slurp(INPUT *);
                     97: void usage(void);
1.1       deraadt    98:
                     99: int
1.16      deraadt   100: main(int argc, char *argv[])
1.1       deraadt   101: {
1.4       michaels  102:        INPUT *F1, *F2;
1.1       deraadt   103:        int aflag, ch, cval, vflag;
                    104:        char *end;
                    105:
1.31      martijn   106:        setlocale(LC_CTYPE, "");
                    107:
1.27      deraadt   108:        if (pledge("stdio rpath", NULL) == -1)
                    109:                err(1, "pledge");
1.26      deraadt   110:
1.1       deraadt   111:        F1 = &input1;
                    112:        F2 = &input2;
                    113:
                    114:        aflag = vflag = 0;
                    115:        obsolete(argv);
1.6       millert   116:        while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != -1) {
1.1       deraadt   117:                switch (ch) {
1.4       michaels  118:                case '\01':             /* See comment in obsolete(). */
1.1       deraadt   119:                        aflag = 1;
                    120:                        F1->unpair = F2->unpair = 1;
                    121:                        break;
                    122:                case '1':
                    123:                        if ((F1->joinf = strtol(optarg, &end, 10)) < 1)
1.4       michaels  124:                                errx(1, "-1 option field number less than 1");
1.1       deraadt   125:                        if (*end)
1.4       michaels  126:                                errx(1, "illegal field number -- %s", optarg);
1.1       deraadt   127:                        --F1->joinf;
                    128:                        break;
                    129:                case '2':
                    130:                        if ((F2->joinf = strtol(optarg, &end, 10)) < 1)
1.4       michaels  131:                                errx(1, "-2 option field number less than 1");
1.1       deraadt   132:                        if (*end)
1.4       michaels  133:                                errx(1, "illegal field number -- %s", optarg);
1.1       deraadt   134:                        --F2->joinf;
                    135:                        break;
                    136:                case 'a':
                    137:                        aflag = 1;
                    138:                        switch(strtol(optarg, &end, 10)) {
                    139:                        case 1:
                    140:                                F1->unpair = 1;
                    141:                                break;
                    142:                        case 2:
                    143:                                F2->unpair = 1;
                    144:                                break;
                    145:                        default:
1.4       michaels  146:                                errx(1, "-a option file number not 1 or 2");
1.1       deraadt   147:                                break;
                    148:                        }
                    149:                        if (*end)
1.4       michaels  150:                                errx(1, "illegal file number -- %s", optarg);
1.1       deraadt   151:                        break;
                    152:                case 'e':
                    153:                        empty = optarg;
                    154:                        break;
                    155:                case 'j':
1.4       michaels  156:                        if ((F1->joinf = F2->joinf = strtol(optarg, &end, 10)) < 1)
                    157:                                errx(1, "-j option field number less than 1");
1.1       deraadt   158:                        if (*end)
1.4       michaels  159:                                errx(1, "illegal field number -- %s", optarg);
1.1       deraadt   160:                        --F1->joinf;
                    161:                        --F2->joinf;
                    162:                        break;
                    163:                case 'o':
                    164:                        fieldarg(optarg);
                    165:                        break;
                    166:                case 't':
                    167:                        spans = 0;
1.31      martijn   168:                        if (mbtowc(tabchar, optarg, MB_CUR_MAX) !=
                    169:                            strlen(optarg))
1.4       michaels  170:                                errx(1, "illegal tab character specification");
1.31      martijn   171:                        tabchar[1] = L'\0';
1.1       deraadt   172:                        break;
                    173:                case 'v':
                    174:                        vflag = 1;
                    175:                        joinout = 0;
1.4       michaels  176:                        switch (strtol(optarg, &end, 10)) {
1.1       deraadt   177:                        case 1:
                    178:                                F1->unpair = 1;
                    179:                                break;
                    180:                        case 2:
                    181:                                F2->unpair = 1;
                    182:                                break;
                    183:                        default:
1.4       michaels  184:                                errx(1, "-v option file number not 1 or 2");
1.1       deraadt   185:                                break;
                    186:                        }
                    187:                        if (*end)
1.4       michaels  188:                                errx(1, "illegal file number -- %s", optarg);
1.1       deraadt   189:                        break;
                    190:                case '?':
                    191:                default:
                    192:                        usage();
                    193:                }
                    194:        }
                    195:        argc -= optind;
                    196:        argv += optind;
                    197:
                    198:        if (aflag && vflag)
1.4       michaels  199:                errx(1, "the -a and -v options are mutually exclusive");
1.1       deraadt   200:
                    201:        if (argc != 2)
                    202:                usage();
                    203:
                    204:        /* Open the files; "-" means stdin. */
                    205:        if (!strcmp(*argv, "-"))
                    206:                F1->fp = stdin;
                    207:        else if ((F1->fp = fopen(*argv, "r")) == NULL)
1.4       michaels  208:                err(1, "%s", *argv);
1.1       deraadt   209:        ++argv;
                    210:        if (!strcmp(*argv, "-"))
                    211:                F2->fp = stdin;
                    212:        else if ((F2->fp = fopen(*argv, "r")) == NULL)
1.4       michaels  213:                err(1, "%s", *argv);
1.1       deraadt   214:        if (F1->fp == stdin && F2->fp == stdin)
1.4       michaels  215:                errx(1, "only one input file may be stdin");
1.26      deraadt   216:
1.27      deraadt   217:        if (pledge("stdio", NULL) == -1)
                    218:                err(1, "pledge");
1.1       deraadt   219:
                    220:        slurp(F1);
                    221:        slurp(F2);
1.4       michaels  222:
1.5       michaels  223:        /*
                    224:         * We try to let the files have the same field value, advancing
                    225:         * whoever falls behind and always advancing the file(s) we output
                    226:         * from.
                    227:        */
1.1       deraadt   228:        while (F1->setcnt && F2->setcnt) {
                    229:                cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf);
                    230:                if (cval == 0) {
                    231:                        /* Oh joy, oh rapture, oh beauty divine! */
                    232:                        if (joinout)
                    233:                                joinlines(F1, F2);
1.5       michaels  234:                        slurp(F1);
1.1       deraadt   235:                        slurp(F2);
1.29      otto      236:                } else if (cval < 0) {
                    237:                        /* File 1 takes the lead... */
                    238:                        if (F1->unpair)
1.1       deraadt   239:                                joinlines(F1, NULL);
1.29      otto      240:                        slurp(F1);
                    241:                } else {
                    242:                        /* File 2 takes the lead... */
                    243:                        if (F2->unpair)
1.1       deraadt   244:                                joinlines(F2, NULL);
1.29      otto      245:                        slurp(F2);
1.1       deraadt   246:                }
                    247:        }
                    248:
                    249:        /*
                    250:         * Now that one of the files is used up, optionally output any
                    251:         * remaining lines from the other file.
                    252:         */
                    253:        if (F1->unpair)
                    254:                while (F1->setcnt) {
                    255:                        joinlines(F1, NULL);
                    256:                        slurp(F1);
                    257:                }
                    258:        if (F2->unpair)
                    259:                while (F2->setcnt) {
                    260:                        joinlines(F2, NULL);
                    261:                        slurp(F2);
                    262:                }
1.5       michaels  263:
1.4       michaels  264:        return 0;
1.1       deraadt   265: }
                    266:
                    267: void
1.29      otto      268: slurp(INPUT *F)
1.1       deraadt   269: {
1.4       michaels  270:        LINE *lp, *lastlp, tmp;
1.28      millert   271:        ssize_t len;
1.17      otto      272:        u_long cnt;
1.33    ! martijn   273:        char *bp, *fieldp;
1.29      otto      274:
1.1       deraadt   275:        /*
                    276:         * Read all of the lines from an input file that have the same
                    277:         * join field.
                    278:         */
1.4       michaels  279:
1.1       deraadt   280:        F->setcnt = 0;
1.29      otto      281:        for (lastlp = NULL; ; ++F->setcnt) {
1.1       deraadt   282:                /*
                    283:                 * If we're out of space to hold line structures, allocate
                    284:                 * more.  Initialize the structure so that we know that this
                    285:                 * is new space.
                    286:                 */
                    287:                if (F->setcnt == F->setalloc) {
1.17      otto      288:                        LINE *p;
                    289:                        u_long newsize = F->setalloc + 50;
1.1       deraadt   290:                        cnt = F->setalloc;
1.23      doug      291:                        if ((p = reallocarray(F->set, newsize, sizeof(LINE)))
                    292:                            == NULL)
1.4       michaels  293:                                err(1, NULL);
1.17      otto      294:                        F->set = p;
                    295:                        F->setalloc = newsize;
1.4       michaels  296:                        memset(F->set + cnt, 0, 50 * sizeof(LINE));
                    297:                        /* re-set lastlp in case it moved */
                    298:                        if (lastlp != NULL)
                    299:                                lastlp = &F->set[F->setcnt - 1];
1.1       deraadt   300:                }
                    301:                /*
                    302:                 * Get any pushed back line, else get the next line.  Allocate
                    303:                 * space as necessary.  If taking the line from the stack swap
1.4       michaels  304:                 * the two structures so that we don't lose space allocated to
                    305:                 * either structure.  This could be avoided by doing another
                    306:                 * level of indirection, but it's probably okay as is.
1.1       deraadt   307:                 */
                    308:                lp = &F->set[F->setcnt];
1.29      otto      309:                if (F->setcnt)
                    310:                        lastlp = &F->set[F->setcnt - 1];
1.4       michaels  311:                if (F->pushbool) {
1.1       deraadt   312:                        tmp = F->set[F->setcnt];
                    313:                        F->set[F->setcnt] = F->set[F->pushback];
                    314:                        F->set[F->pushback] = tmp;
1.4       michaels  315:                        F->pushbool = 0;
1.1       deraadt   316:                        continue;
                    317:                }
1.33    ! martijn   318:                if ((len = getline(&(lp->line), &(lp->linealloc), F->fp)) == -1)
1.28      millert   319:                        break;
                    320:
1.33    ! martijn   321:                /* Remove the trailing newline, if any. */
        !           322:                if (lp->line[len - 1] == '\n')
        !           323:                        lp->line[--len] = '\0';
1.1       deraadt   324:
                    325:                /* Split the line into fields, allocate space as necessary. */
                    326:                lp->fieldcnt = 0;
1.28      millert   327:                bp = lp->line;
1.31      martijn   328:                while ((fieldp = mbssep(&bp, tabchar)) != NULL) {
1.1       deraadt   329:                        if (spans && *fieldp == '\0')
                    330:                                continue;
                    331:                        if (lp->fieldcnt == lp->fieldalloc) {
1.17      otto      332:                                char **p;
                    333:                                u_long newsize = lp->fieldalloc + 50;
1.23      doug      334:                                if ((p = reallocarray(lp->fields, newsize,
                    335:                                    sizeof(char *))) == NULL)
1.4       michaels  336:                                        err(1, NULL);
1.17      otto      337:                                lp->fields = p;
                    338:                                lp->fieldalloc = newsize;
1.1       deraadt   339:                        }
                    340:                        lp->fields[lp->fieldcnt++] = fieldp;
                    341:                }
                    342:
                    343:                /* See if the join field value has changed. */
                    344:                if (lastlp != NULL && cmp(lp, F->joinf, lastlp, F->joinf)) {
1.4       michaels  345:                        F->pushbool = 1;
1.1       deraadt   346:                        F->pushback = F->setcnt;
                    347:                        break;
                    348:                }
                    349:        }
                    350: }
                    351:
1.31      martijn   352: char *
                    353: mbssep(char **stringp, const wchar_t *wcdelim)
                    354: {
                    355:        char *s, *p;
                    356:        size_t ndelim;
                    357:        int i;
                    358:        /* tabchar is never more than 2 */
                    359:        char mbdelim[2][MB_LEN_MAX + 1];
                    360:        size_t mblen[2];
                    361:
                    362:        if ((s = *stringp) == NULL)
                    363:                return NULL;
                    364:        ndelim = wcslen(wcdelim);
                    365:        for (i = 0; i < ndelim; i++) {
                    366:                /* wcdelim generated via mbtowc */
                    367:                mblen[i] = wctomb(mbdelim[i], wcdelim[i]);
                    368:        }
                    369:        for (p = s; *p != '\0'; p++) {
                    370:                for (i = 0; i < ndelim; i++) {
                    371:                        if (strncmp(p, mbdelim[i], mblen[i]) == 0) {
                    372:                                *p = '\0';
                    373:                                *stringp = p + mblen[i];
                    374:                                return s;
                    375:                        }
                    376:                }
                    377:        }
                    378:        *stringp = NULL;
                    379:        return s;
                    380: }
                    381:
1.1       deraadt   382: int
1.16      deraadt   383: cmp(LINE *lp1, u_long fieldno1, LINE *lp2, u_long fieldno2)
1.1       deraadt   384: {
1.4       michaels  385:        if (lp1->fieldcnt <= fieldno1)
1.30      martijn   386:                return lp2->fieldcnt <= fieldno2 ? 0 : -1;
                    387:        if (lp2->fieldcnt <= fieldno2)
                    388:                return 1;
                    389:        return strcmp(lp1->fields[fieldno1], lp2->fields[fieldno2]);
1.1       deraadt   390: }
                    391:
                    392: void
1.16      deraadt   393: joinlines(INPUT *F1, INPUT *F2)
1.1       deraadt   394: {
1.17      otto      395:        u_long cnt1, cnt2;
1.1       deraadt   396:
                    397:        /*
                    398:         * Output the results of a join comparison.  The output may be from
                    399:         * either file 1 or file 2 (in which case the first argument is the
                    400:         * file from which to output) or from both.
                    401:         */
                    402:        if (F2 == NULL) {
                    403:                for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
                    404:                        outoneline(F1, &F1->set[cnt1]);
                    405:                return;
                    406:        }
                    407:        for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
                    408:                for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2)
                    409:                        outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]);
                    410: }
                    411:
                    412: void
1.16      deraadt   413: outoneline(INPUT *F, LINE *lp)
1.1       deraadt   414: {
1.17      otto      415:        u_long cnt;
1.1       deraadt   416:
                    417:        /*
                    418:         * Output a single line from one of the files, according to the
                    419:         * join rules.  This happens when we are writing unmatched single
                    420:         * lines.  Output empty fields in the right places.
                    421:         */
                    422:        if (olist)
                    423:                for (cnt = 0; cnt < olistcnt; ++cnt) {
1.4       michaels  424:                        if (olist[cnt].filenum == F->number)
                    425:                                outfield(lp, olist[cnt].fieldno, 0);
1.17      otto      426:                        else if (olist[cnt].filenum == 0)
                    427:                                outfield(lp, F->joinf, 0);
1.4       michaels  428:                        else
                    429:                                outfield(lp, 0, 1);
1.1       deraadt   430:                }
1.18      otto      431:        else {
                    432:                /*
                    433:                 * Output the join field, then the remaining fields from F
                    434:                 */
                    435:                outfield(lp, F->joinf, 0);
1.1       deraadt   436:                for (cnt = 0; cnt < lp->fieldcnt; ++cnt)
1.18      otto      437:                        if (F->joinf != cnt)
                    438:                                outfield(lp, cnt, 0);
                    439:        }
                    440:
1.4       michaels  441:        putchar('\n');
1.1       deraadt   442:        if (ferror(stdout))
1.4       michaels  443:                err(1, "stdout");
1.1       deraadt   444:        needsep = 0;
                    445: }
                    446:
                    447: void
1.16      deraadt   448: outtwoline(INPUT *F1, LINE *lp1, INPUT *F2, LINE *lp2)
1.1       deraadt   449: {
1.17      otto      450:        u_long cnt;
1.1       deraadt   451:
                    452:        /* Output a pair of lines according to the join list (if any). */
1.11      deraadt   453:        if (olist) {
1.1       deraadt   454:                for (cnt = 0; cnt < olistcnt; ++cnt)
1.17      otto      455:                        if (olist[cnt].filenum == 0) {
                    456:                                if (lp1->fieldcnt >= F1->joinf)
                    457:                                        outfield(lp1, F1->joinf, 0);
                    458:                                else
                    459:                                        outfield(lp2, F2->joinf, 0);
                    460:                        } else if (olist[cnt].filenum == 1)
1.4       michaels  461:                                outfield(lp1, olist[cnt].fieldno, 0);
                    462:                        else /* if (olist[cnt].filenum == 2) */
                    463:                                outfield(lp2, olist[cnt].fieldno, 0);
1.11      deraadt   464:        } else {
1.1       deraadt   465:                /*
                    466:                 * Output the join field, then the remaining fields from F1
                    467:                 * and F2.
                    468:                 */
1.4       michaels  469:                outfield(lp1, F1->joinf, 0);
1.1       deraadt   470:                for (cnt = 0; cnt < lp1->fieldcnt; ++cnt)
                    471:                        if (F1->joinf != cnt)
1.4       michaels  472:                                outfield(lp1, cnt, 0);
1.1       deraadt   473:                for (cnt = 0; cnt < lp2->fieldcnt; ++cnt)
                    474:                        if (F2->joinf != cnt)
1.4       michaels  475:                                outfield(lp2, cnt, 0);
1.1       deraadt   476:        }
1.4       michaels  477:        putchar('\n');
1.1       deraadt   478:        if (ferror(stdout))
1.4       michaels  479:                err(1, "stdout");
1.1       deraadt   480:        needsep = 0;
                    481: }
                    482:
                    483: void
1.16      deraadt   484: outfield(LINE *lp, u_long fieldno, int out_empty)
1.1       deraadt   485: {
                    486:        if (needsep++)
1.31      martijn   487:                putwchar(*tabchar);
1.11      deraadt   488:        if (!ferror(stdout)) {
1.12      aaron     489:                if (lp->fieldcnt <= fieldno || out_empty) {
1.1       deraadt   490:                        if (empty != NULL)
1.4       michaels  491:                                fputs(empty, stdout);
1.1       deraadt   492:                } else {
                    493:                        if (*lp->fields[fieldno] == '\0')
                    494:                                return;
1.4       michaels  495:                        fputs(lp->fields[fieldno], stdout);
1.1       deraadt   496:                }
1.11      deraadt   497:        }
1.1       deraadt   498:        if (ferror(stdout))
1.4       michaels  499:                err(1, "stdout");
1.1       deraadt   500: }
                    501:
                    502: /*
                    503:  * Convert an output list argument "2.1, 1.3, 2.4" into an array of output
                    504:  * fields.
                    505:  */
                    506: void
1.16      deraadt   507: fieldarg(char *option)
1.1       deraadt   508: {
1.17      otto      509:        u_long fieldno, filenum;
1.1       deraadt   510:        char *end, *token;
                    511:
1.4       michaels  512:        while ((token = strsep(&option, ", \t")) != NULL) {
1.1       deraadt   513:                if (*token == '\0')
                    514:                        continue;
1.17      otto      515:                if (token[0] == '0')
                    516:                        filenum = fieldno = 0;
                    517:                else if ((token[0] == '1' || token[0] == '2') &&
                    518:                    token[1] == '.') {
                    519:                        filenum = token[0] - '0';
                    520:                        fieldno = strtol(token + 2, &end, 10);
                    521:                        if (*end)
                    522:                                errx(1, "malformed -o option field");
                    523:                        if (fieldno == 0)
                    524:                                errx(1, "field numbers are 1 based");
                    525:                        --fieldno;
                    526:                } else
1.4       michaels  527:                        errx(1, "malformed -o option field");
1.1       deraadt   528:                if (olistcnt == olistalloc) {
1.17      otto      529:                        OLIST *p;
                    530:                        u_long newsize = olistalloc + 50;
1.23      doug      531:                        if ((p = reallocarray(olist, newsize, sizeof(OLIST)))
                    532:                            == NULL)
1.4       michaels  533:                                err(1, NULL);
1.17      otto      534:                        olist = p;
                    535:                        olistalloc = newsize;
1.1       deraadt   536:                }
1.17      otto      537:                olist[olistcnt].filenum = filenum;
                    538:                olist[olistcnt].fieldno = fieldno;
1.1       deraadt   539:                ++olistcnt;
                    540:        }
                    541: }
                    542:
                    543: void
1.16      deraadt   544: obsolete(char **argv)
1.1       deraadt   545: {
1.17      otto      546:        size_t len;
1.1       deraadt   547:        char **p, *ap, *t;
                    548:
1.4       michaels  549:        while ((ap = *++argv) != NULL) {
1.1       deraadt   550:                /* Return if "--". */
1.9       deraadt   551:                if (ap[0] == '-' && ap[1] == '-')
1.1       deraadt   552:                        return;
1.10      deraadt   553:                /* skip if not an option */
                    554:                if (ap[0] != '-')
                    555:                        continue;
1.1       deraadt   556:                switch (ap[1]) {
                    557:                case 'a':
1.25      jasper    558:                        /*
1.1       deraadt   559:                         * The original join allowed "-a", which meant the
                    560:                         * same as -a1 plus -a2.  POSIX 1003.2, Draft 11.2
                    561:                         * only specifies this as "-a 1" and "a -2", so we
                    562:                         * have to use another option flag, one that is
                    563:                         * unlikely to ever be used or accidentally entered
                    564:                         * on the command line.  (Well, we could reallocate
                    565:                         * the argv array, but that hardly seems worthwhile.)
                    566:                         */
1.17      otto      567:                        if (ap[2] == '\0' && (argv[1] == NULL ||
                    568:                            (strcmp(argv[1], "1") != 0 &&
                    569:                            strcmp(argv[1], "2") != 0))) {
1.1       deraadt   570:                                ap[1] = '\01';
1.17      otto      571:                                warnx("-a option used without an argument; "
                    572:                                    "reverting to historical behavior");
                    573:                        }
1.1       deraadt   574:                        break;
                    575:                case 'j':
                    576:                        /*
                    577:                         * The original join allowed "-j[12] arg" and "-j arg".
                    578:                         * Convert the former to "-[12] arg".  Don't convert
                    579:                         * the latter since getopt(3) can handle it.
                    580:                         */
                    581:                        switch(ap[2]) {
                    582:                        case '1':
                    583:                        case '2':
                    584:                                if (ap[3] != '\0')
                    585:                                        goto jbad;
1.19      millert   586:                                ap[1] = ap[2];
1.1       deraadt   587:                                ap[2] = '\0';
                    588:                                break;
                    589:                        case '\0':
                    590:                                break;
                    591:                        default:
1.19      millert   592: jbad:                          warnx("unknown option -- %s", ap + 1);
1.1       deraadt   593:                                usage();
                    594:                        }
                    595:                        break;
                    596:                case 'o':
                    597:                        /*
1.4       michaels  598:                         * The original join allowed "-o arg arg".
                    599:                         * Convert to "-o arg -o arg".
1.1       deraadt   600:                         */
1.19      millert   601:                        if (ap[2] != '\0' || argv[1] == NULL)
1.1       deraadt   602:                                break;
1.4       michaels  603:                        for (p = argv + 2; *p != NULL; ++p) {
1.17      otto      604:                                if (p[0][0] == '0' || ((p[0][0] != '1' &&
                    605:                                    p[0][0] != '2') || p[0][1] != '.'))
1.1       deraadt   606:                                        break;
                    607:                                len = strlen(*p);
                    608:                                if (len - 2 != strspn(*p + 2, "0123456789"))
                    609:                                        break;
                    610:                                if ((t = malloc(len + 3)) == NULL)
1.4       michaels  611:                                        err(1, NULL);
1.1       deraadt   612:                                t[0] = '-';
                    613:                                t[1] = 'o';
1.4       michaels  614:                                memmove(t + 2, *p, len + 1);
1.1       deraadt   615:                                *p = t;
                    616:                        }
                    617:                        argv = p - 1;
                    618:                        break;
                    619:                }
                    620:        }
                    621: }
                    622:
                    623: void
1.16      deraadt   624: usage(void)
1.1       deraadt   625: {
1.19      millert   626:        int len;
                    627:        extern char *__progname;
                    628:
                    629:        len = strlen(__progname) + sizeof("usage: ");
                    630:        (void)fprintf(stderr, "usage: %s [-1 field] [-2 field] "
                    631:            "[-a file_number | -v file_number] [-e string]\n"
1.20      jmc       632:            "%*s[-o list] [-t char] file1 file2\n",
1.19      millert   633:            __progname, len, "");
1.1       deraadt   634:        exit(1);
                    635: }