|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.