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

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