Annotation of coherent/d/bin/join.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Join -- relational database join operation.
                      3:  */
                      4: 
                      5: #include <stdio.h>
                      6: 
                      7: #define        NREC    512                     /* Maximum record length */
                      8: #define        NFIELD  200                     /* Maximum number of fields */
                      9: #define        NOFIELD 150                     /* Maximum fields in output list */
                     10: 
                     11: typedef        unsigned char   uchar;
                     12: 
                     13: /*
                     14:  * A record that has been split into
                     15:  * fields.
                     16:  */
                     17: typedef        struct  RECORD {
                     18:        char    r_record[NREC];         /* The text for the whole record */
                     19:        FILE    *r_fp;                  /* File stream pointer of input */
                     20:        int     r_jfield;               /* Join field number */
                     21:        int     r_eof;                  /* EOF flag */
                     22:        unsigned r_nfield;              /* Number of fields */
                     23:        char    *r_fields[NFIELD];      /* Field pointers */
                     24: }      RECORD;
                     25: 
                     26: /*
                     27:  * The indicator for what field is to be
                     28:  * printed upon output.
                     29:  * Whhen there is no `-o' list, then
                     30:  * the default of the key field followed
                     31:  * by all fields in file1 followed by all
                     32:  * other fields in file2 is used.
                     33:  */
                     34: typedef        struct  FIELD {
                     35:        char    f_fileno;               /* File number (1,2,or3) */
                     36:        uchar   f_fieldno;              /* Field number */
                     37: }      FIELD;
                     38: 
                     39: RECORD rec1;
                     40: RECORD rec2;
                     41: FIELD  fields[NOFIELD];
                     42: FIELD  *efp = &fields[0];
                     43: 
                     44: char   usemsg[] = "\
                     45: Usage: join [-a[f]] [-e s] [-j[f] n] [-o f.n ...] [-tc] file1 file2\n\
                     46: ";
                     47: 
                     48: char   tabc;                           /* Tab character -- if null, default */
                     49: char   otabc = ' ';                    /* Output separator character */
                     50: int    unpair;                         /* Print unpairable records in these */
                     51: int    tflag;                          /* Set when -t given */
                     52: char   *empty = "";                    /* Put out for empty fields */
                     53: FILE   *jopen();
                     54: 
                     55: main(argc, argv)
                     56: int argc;
                     57: char *argv[];
                     58: {
                     59:        register char *ap;
                     60:        register int n;
                     61:        register FIELD *ep = fields;
                     62: 
                     63:        rec1.r_jfield = rec2.r_jfield = 1;
                     64:        while (argc>1 && *argv[1]=='-') {
                     65:                ap = &argv[1][1];
                     66:                if (*ap == '\0')
                     67:                        break;
                     68:                switch (*ap) {
                     69:                case 'a':
                     70:                        if (ap[1] == '\0')
                     71:                                unpair = 01|02;
                     72:                        else {
                     73:                                if (ap[2] != '\0')
                     74:                                        usage();
                     75:                                if (ap[1] == '1')
                     76:                                        unpair = 01;
                     77:                                else if (ap[1] == '2')
                     78:                                        unpair = 02;
                     79:                                else
                     80:                                        fnusage("-a");
                     81:                        }
                     82:                        break;
                     83: 
                     84:                case 'e':
                     85:                        if (argc<2 || ap[1]!='\0')
                     86:                                usage();
                     87:                        argv++;
                     88:                        argc--;
                     89:                        empty = argv[1];
                     90:                        break;
                     91: 
                     92:                case 'j':
                     93:                        if (argc < 2)
                     94:                                usage();
                     95:                        argv++;
                     96:                        argc--;
                     97:                        if ((n = atoi(argv[1])) == 0)
                     98:                                usage();
                     99:                        if (ap[1] == '\0')
                    100:                                rec1.r_jfield = rec2.r_jfield = n;
                    101:                        else if (ap[1] == '1')
                    102:                                rec1.r_jfield = n;
                    103:                        else if (ap[1] == '2')
                    104:                                rec2.r_jfield = n;
                    105:                        else
                    106:                                fnusage("-j");
                    107:                        break;
                    108: 
                    109:                case 'o':
                    110:                        ep = efp;
                    111:                        while (argc > 2) {
                    112:                                if (!addlist(argv[2]))
                    113:                                        break;
                    114:                                argv++;
                    115:                                argc--;
                    116:                        }
                    117:                        if (ep==efp || ap[1]!='\0')
                    118:                                usage();
                    119:                        break;
                    120: 
                    121:                case 't':
                    122:                        tflag++;
                    123:                        if ((tabc = otabc = ap[1]) == '\0')
                    124:                                usage();
                    125:                        break;
                    126: 
                    127:                default:
                    128:                        usage();
                    129:                }
                    130:                argv++;
                    131:                argc--;
                    132:        }
                    133:        if (argc != 3)
                    134:                usage();
                    135:        rec1.r_fp = jopen(argv[1]);
                    136:        rec2.r_fp = jopen(argv[2]);
                    137:        if (rec1.r_fp==stdin && rec2.r_fp==stdin)
                    138:                cerr("both files are `-'");
                    139:        join();
                    140:        exit(0);
                    141: }
                    142: 
                    143: /*
                    144:  * Add a new element to the output list for `join'.
                    145:  * Check for two many elements on the list.
                    146:  * Return 0 if this isn't a list element type argument.
                    147:  * Return 1 if added element.
                    148:  */
                    149: addlist(s)
                    150: register char *s;
                    151: {
                    152:        register int n;
                    153:        register int fn;
                    154: 
                    155:        fn = 3;
                    156:        for (n=0; *s>='0' && *s<='9'; )
                    157:                n = n*10 + *s++-'0';
                    158:        if (*s == '.') {
                    159:                s++;
                    160:                fn = n;
                    161:                for (n=0; *s>='0' && *s<='9'; )
                    162:                        n = n*10 + *s++-'0';
                    163:        }
                    164:        if (*s!='\0' || fn<1 || fn>3)
                    165:                return (0);
                    166:        if (efp >= &fields[NOFIELD-1])
                    167:                cerr("too many elements for `-o' list");
                    168:        efp->f_fileno = fn;
                    169:        efp->f_fieldno = n;
                    170:        efp++;
                    171:        return (1);
                    172: }
                    173: 
                    174: /*
                    175:  * Open one of join's input files.
                    176:  * Check for the special filename `-'.
                    177:  */
                    178: FILE   *
                    179: jopen(fn)
                    180: register char *fn;
                    181: {
                    182:        register FILE *fp;
                    183: 
                    184:        if (fn[0]=='-' && fn[1]=='\0')
                    185:                fp = stdin;
                    186:        else if ((fp = fopen(fn, "r")) == NULL)
                    187:                cerr("cannot open input `%s'", fn);
                    188:        return (fp);
                    189: }
                    190: 
                    191: /*
                    192:  * Do the work of joining two files
                    193:  * containing relations.
                    194:  * The files should be ordered on
                    195:  * the selected primary keys.
                    196:  */
                    197: join()
                    198: {
                    199:        jread(&rec1);
                    200:        jread(&rec2);
                    201:        while (!rec1.r_eof && !rec2.r_eof) {
                    202:                switch (jcmp()) {
                    203:                case 0:
                    204:                        jwrite(03);
                    205:                        jread(&rec2);
                    206:                        break;
                    207: 
                    208:                case -1:        /* file1 < file2 */
                    209:                        if (unpair & 01)
                    210:                                jwrite(01);
                    211:                        jread(&rec1);
                    212:                        break;
                    213: 
                    214:                case 1:         /* file2 < file1 */
                    215:                        if (unpair & 02)
                    216:                                jwrite(02);
                    217:                        jread(&rec2);
                    218:                        break;
                    219:                }
                    220:        }
                    221:        while (!rec2.r_eof) {
                    222:                if (unpair & 02)
                    223:                        jwrite(02);
                    224:                jread(&rec2);
                    225:        }
                    226: 
                    227:        while (!rec1.r_eof) {
                    228:                if (unpair & 01)
                    229:                        jwrite(01);
                    230:                jread(&rec1);
                    231:        }
                    232: }
                    233: 
                    234: /*
                    235:  * Read a record and divide it into fields.
                    236:  * The argument is a record pointer.
                    237:  */
                    238: jread(rp)
                    239: register RECORD *rp;
                    240: {
                    241:        register char *cp;
                    242:        register int c;
                    243:        register int nf = 0;
                    244: 
                    245:        rp->r_nfield = 0;
                    246:        if (fgets(rp->r_record, NFIELD, rp->r_fp) == NULL) {
                    247:                rp->r_eof++;
                    248:                return;
                    249:        }
                    250:        cp = rp->r_record;
                    251:        if (tflag) {
                    252:                for (nf=0; ; nf++) {
                    253:                        if (nf >= NFIELD)
                    254:                                cerr("too many fields in input");
                    255:                        rp->r_fields[nf] = cp;
                    256:                        while ((c = *cp)!='\n' && c!='\0' && c!=tabc)
                    257:                                cp++;
                    258:                        *cp++ = '\0';
                    259:                        if (c != tabc)
                    260:                                break;
                    261:                }
                    262:                rp->r_nfield = nf+1;
                    263:        } else {
                    264:                nf = 0;
                    265:                for (;;) {
                    266:                        while ((c = *cp)==' ' || c=='\t')
                    267:                                cp++;
                    268:                        if (c=='\n' || c=='\0') {
                    269:                                *cp = '\0';
                    270:                                break;
                    271:                        }
                    272:                        if (nf >= NFIELD)
                    273:                                cerr("too many field in input");
                    274:                        rp->r_fields[nf++] = cp;
                    275:                        while ((c = *cp)!=' ' && c!='\t' && c!='\n' && c!='\0')
                    276:                                cp++;
                    277:                        if (c != '\0')
                    278:                                *cp++ = '\0';
                    279:                }
                    280:                rp->r_nfield = nf;
                    281:        }
                    282: }
                    283: 
                    284: /*
                    285:  * Output a record for the given file numbers.
                    286:  * Numbers can be 1, 2, or 3 (for both files).
                    287:  */
                    288: jwrite(fn)
                    289: int fn;
                    290: {
                    291:        register int i;
                    292: 
                    293:        if (efp > &fields[0]) {
                    294:                register FIELD *fp;
                    295: 
                    296:                for (fp = &fields[0]; fp < efp; fp++) {
                    297:                        switch (fn & fp->f_fileno) {
                    298:                        case 1:
                    299:                                jpfield(&rec1, fp->f_fieldno);
                    300:                                break;
                    301: 
                    302:                        case 2:
                    303:                                jpfield(&rec2, fp->f_fieldno);
                    304:                                break;
                    305: 
                    306:                        case 3:
                    307:                                jpfield(&rec1, fp->f_fieldno);
                    308:                                jpfield(&rec2, fp->f_fieldno);
                    309:                                break;
                    310:                        }
                    311:                }
                    312:        } else {
                    313:                register int jf;
                    314: 
                    315:                if (fn & 01) {
                    316:                        jf = rec1.r_jfield;
                    317:                        jpfield(&rec1, jf);
                    318:                        for (i=1; i<=rec1.r_nfield; i++)
                    319:                                if (i != jf)
                    320:                                        jpfield(&rec1, i);
                    321:                }
                    322:                if (fn & 02) {
                    323:                        jf = rec2.r_jfield;
                    324:                        if (fn != 03)
                    325:                                jpfield(&rec2, jf);
                    326:                        for (i=1; i<=rec2.r_nfield; i++)
                    327:                                if (i != jf)
                    328:                                        jpfield(&rec2, i);
                    329:                }
                    330:        }
                    331:        jpfield(NULL, 0);
                    332: }
                    333: 
                    334: /*
                    335:  * Print out a field.
                    336:  * When asked to print from record
                    337:  * `NULL', this signifies the reset at end
                    338:  * of line.
                    339:  * Don't print fields that are out of range
                    340:  * (perhaps these should be considered as NULL fields).
                    341:  */
                    342: jpfield(rp, fieldno)
                    343: register RECORD *rp;
                    344: int fieldno;
                    345: {
                    346:        static int first;
                    347:        register char *fp;
                    348: 
                    349:        if (rp == NULL) {
                    350:                putchar('\n');
                    351:                first = 0;
                    352:                return;
                    353:        }
                    354:        if (fieldno > rp->r_nfield)
                    355:                return;
                    356:        if (first++ != 0)
                    357:                putchar(otabc);
                    358:        fp = rp->r_fields[fieldno-1];
                    359:        if (*fp == '\0')
                    360:                fp = empty;
                    361:        fputs(fp, stdout);
                    362: }
                    363: 
                    364: /*
                    365:  * Do a comparison of the join fields.
                    366:  * This has the same return values as
                    367:  * strcmp.  If the field is missing, complain.
                    368:  */
                    369: jcmp()
                    370: {
                    371:        if (rec1.r_jfield>rec1.r_nfield || rec2.r_jfield>rec2.r_nfield)
                    372:                cerr("join field is missing in input");
                    373:        return (strcmp(rec1.r_fields[rec1.r_jfield-1],
                    374:                        rec2.r_fields[rec2.r_jfield-1]));
                    375: }
                    376: 
                    377: fnusage(opt)
                    378: char *opt;
                    379: {
                    380:        fprintf(stderr, "join: `%s' file number must be 1 or 2\n", opt);
                    381:        usage();
                    382: }
                    383: 
                    384: usage()
                    385: {
                    386:        fprintf(stderr, usemsg);
                    387:        exit(1);
                    388: }
                    389: 
                    390: /* VARARGS */
                    391: cerr(x)
                    392: {
                    393:        fprintf(stderr, "join: %r\n", &x);
                    394:        exit(1);
                    395: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.