|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 7212 (0x1c2c) Types: TextFile Notes: UNIX file Names: »join.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦f4b8d8c84⟧ UNIX Filesystem └─⟦this⟧ »cmd/join.c«
/* * Join -- relational database join operation. */ #include <stdio.h> #define NREC 512 /* Maximum record length */ #define NFIELD 200 /* Maximum number of fields */ #define NOFIELD 150 /* Maximum fields in output list */ typedef unsigned char uchar; /* * A record that has been split into * fields. */ typedef struct RECORD { char r_record[NREC]; /* The text for the whole record */ FILE *r_fp; /* File stream pointer of input */ int r_jfield; /* Join field number */ int r_eof; /* EOF flag */ unsigned r_nfield; /* Number of fields */ char *r_fields[NFIELD]; /* Field pointers */ } RECORD; /* * The indicator for what field is to be * printed upon output. * Whhen there is no `-o' list, then * the default of the key field followed * by all fields in file1 followed by all * other fields in file2 is used. */ typedef struct FIELD { char f_fileno; /* File number (1,2,or3) */ uchar f_fieldno; /* Field number */ } FIELD; RECORD rec1; RECORD rec2; FIELD fields[NOFIELD]; FIELD *efp = &fields[0]; char usemsg[] = "\ Usage: join [-a[f]] [-e s] [-j[f] n] [-o f.n ...] [-tc] file1 file2\n\ "; char tabc; /* Tab character -- if null, default */ char otabc = ' '; /* Output separator character */ int unpair; /* Print unpairable records in these */ int tflag; /* Set when -t given */ char *empty = ""; /* Put out for empty fields */ FILE *jopen(); main(argc, argv) int argc; char *argv[]; { register char *ap; register int n; register FIELD *ep = fields; rec1.r_jfield = rec2.r_jfield = 1; while (argc>1 && *argv[1]=='-') { ap = &argv[1][1]; if (*ap == '\0') break; switch (*ap) { case 'a': if (ap[1] == '\0') unpair = 01|02; else { if (ap[2] != '\0') usage(); if (ap[1] == '1') unpair = 01; else if (ap[1] == '2') unpair = 02; else fnusage("-a"); } break; case 'e': if (argc<2 || ap[1]!='\0') usage(); argv++; argc--; empty = argv[1]; break; case 'j': if (argc < 2) usage(); argv++; argc--; if ((n = atoi(argv[1])) == 0) usage(); if (ap[1] == '\0') rec1.r_jfield = rec2.r_jfield = n; else if (ap[1] == '1') rec1.r_jfield = n; else if (ap[1] == '2') rec2.r_jfield = n; else fnusage("-j"); break; case 'o': ep = efp; while (argc > 2) { if (!addlist(argv[2])) break; argv++; argc--; } if (ep==efp || ap[1]!='\0') usage(); break; case 't': tflag++; if ((tabc = otabc = ap[1]) == '\0') usage(); break; default: usage(); } argv++; argc--; } if (argc != 3) usage(); rec1.r_fp = jopen(argv[1]); rec2.r_fp = jopen(argv[2]); if (rec1.r_fp==stdin && rec2.r_fp==stdin) cerr("both files are `-'"); join(); exit(0); } /* * Add a new element to the output list for `join'. * Check for two many elements on the list. * Return 0 if this isn't a list element type argument. * Return 1 if added element. */ addlist(s) register char *s; { register int n; register int fn; fn = 3; for (n=0; *s>='0' && *s<='9'; ) n = n*10 + *s++-'0'; if (*s == '.') { s++; fn = n; for (n=0; *s>='0' && *s<='9'; ) n = n*10 + *s++-'0'; } if (*s!='\0' || fn<1 || fn>3) return (0); if (efp >= &fields[NOFIELD-1]) cerr("too many elements for `-o' list"); efp->f_fileno = fn; efp->f_fieldno = n; efp++; return (1); } /* * Open one of join's input files. * Check for the special filename `-'. */ FILE * jopen(fn) register char *fn; { register FILE *fp; if (fn[0]=='-' && fn[1]=='\0') fp = stdin; else if ((fp = fopen(fn, "r")) == NULL) cerr("cannot open input `%s'", fn); return (fp); } /* * Do the work of joining two files * containing relations. * The files should be ordered on * the selected primary keys. */ join() { jread(&rec1); jread(&rec2); while (!rec1.r_eof && !rec2.r_eof) { switch (jcmp()) { case 0: jwrite(03); jread(&rec1); jread(&rec2); break; case -1: /* file1 < file2 */ if (unpair & 01) jwrite(01); jread(&rec1); break; case 1: /* file2 < file1 */ if (unpair & 02) jwrite(02); jread(&rec2); break; } } if (rec1.r_eof) { while (!rec2.r_eof) { if (unpair & 02) jwrite(02); jread(&rec2); } } else while (!rec1.r_eof) { if (unpair & 01) jwrite(01); jread(&rec1); } } /* * Read a record and divide it into fields. * The argument is a record pointer. */ jread(rp) register RECORD *rp; { register char *cp; register int c; register int nf = 0; rp->r_nfield = 0; if (fgets(rp->r_record, NFIELD, rp->r_fp) == NULL) { rp->r_eof++; return; } cp = rp->r_record; if (tflag) { for (nf=0; ; nf++) { if (nf >= NFIELD) cerr("too many fields in input"); rp->r_fields[nf] = cp; while ((c = *cp)!='\n' && c!='\0' && c!=tabc) cp++; *cp++ = '\0'; if (c != tabc) break; } rp->r_nfield = nf+1; } else { nf = 0; for (;;) { while ((c = *cp)==' ' || c=='\t') cp++; if (c=='\n' || c=='\0') { *cp = '\0'; break; } if (nf >= NFIELD) cerr("too many field in input"); rp->r_fields[nf++] = cp; while ((c = *cp)!=' ' && c!='\t' && c!='\n' && c!='\0') cp++; if (c != '\0') *cp++ = '\0'; } rp->r_nfield = nf; } } /* * Output a record for the given file numbers. * Numbers can be 1, 2, or 3 (for both files). */ jwrite(fn) int fn; { register int i; if (efp > &fields[0]) { register FIELD *fp; for (fp = &fields[0]; fp < efp; fp++) { switch (fn & fp->f_fileno) { case 1: jpfield(&rec1, fp->f_fieldno); break; case 2: jpfield(&rec2, fp->f_fieldno); break; case 3: jpfield(&rec1, fp->f_fieldno); jpfield(&rec2, fp->f_fieldno); break; } } } else { register int jf; if (fn & 01) { jf = rec1.r_jfield; jpfield(&rec1, jf); for (i=1; i<=rec1.r_nfield; i++) if (i != jf) jpfield(&rec1, i); } if (fn & 02) { jf = rec2.r_jfield; if (fn != 03) jpfield(&rec2, jf); for (i=1; i<=rec2.r_nfield; i++) if (i != jf) jpfield(&rec2, i); } } jpfield(NULL, 0); } /* * Print out a field. * When asked to print from record * `NULL', this signifies the reset at end * of line. * Don't print fields that are out of range * (perhaps these should be considered as NULL fields). */ jpfield(rp, fieldno) register RECORD *rp; int fieldno; { static int first; register char *fp; if (rp == NULL) { putchar('\n'); first = 0; return; } if (fieldno > rp->r_nfield) return; if (first++ != 0) putchar(otabc); fp = rp->r_fields[fieldno-1]; if (*fp == '\0') fp = empty; fputs(fp, stdout); } /* * Do a comparison of the join fields. * This has the same return values as * strcmp. If the field is missing, complain. */ jcmp() { if (rec1.r_jfield>rec1.r_nfield || rec2.r_jfield>rec2.r_nfield) cerr("join field is missing in input"); return (strcmp(rec1.r_fields[rec1.r_jfield-1], rec2.r_fields[rec2.r_jfield-1])); } fnusage(opt) char *opt; { fprintf(stderr, "join: `%s' file number must be 1 or 2\n", opt); usage(); } usage() { fprintf(stderr, usemsg); exit(1); } /* VARARGS */ cerr(x) { fprintf(stderr, "join: %r\n", &x); exit(1); }