[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.6

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