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

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