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