14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin * David Korn
244887Schin * Glenn Fowler
254887Schin * AT&T Research
264887Schin *
274887Schin * join
284887Schin */
294887Schin
304887Schin static const char usage[] =
31*12068SRoger.Faulkner@Oracle.COM "[-?\n@(#)$Id: join (AT&T Research) 2009-12-10 $\n]"
324887Schin USAGE_LICENSE
334887Schin "[+NAME?join - relational database operator]"
344887Schin "[+DESCRIPTION?\bjoin\b performs an \aequality join\a on the files \afile1\a "
354887Schin "and \afile2\a and writes the resulting joined files to standard "
364887Schin "output. By default, a field is delimited by one or more spaces "
374887Schin "and tabs with leading spaces and/or tabs ignored. The \b-t\b option "
384887Schin "can be used to change the field delimiter.]"
394887Schin "[+?The \ajoin field\a is a field in each file on which files are compared. "
404887Schin "By default \bjoin\b writes one line in the output for each pair "
414887Schin "of lines in \afiles1\a and \afiles2\a that have identical join "
424887Schin "fields. The default output line consists of the join field, "
434887Schin "then the remaining fields from \afile1\a, then the remaining "
444887Schin "fields from \afile2\a, but this can be changed with the \b-o\b "
454887Schin "option. The \b-a\b option can be used to add unmatched lines "
464887Schin "to the output. The \b-v\b option can be used to output only "
474887Schin "unmatched lines.]"
484887Schin "[+?The files \afile1\a and \afile2\a must be ordered in the collating "
494887Schin "sequence of \bsort -b\b on the fields on which they are to be "
504887Schin "joined otherwise the results are unspecified.]"
514887Schin "[+?If either \afile1\a or \afile2\a is \b-\b, \bjoin\b "
524887Schin "uses standard input starting at the current location.]"
534887Schin
544887Schin "[e:empty]:[string?Replace empty output fields in the list selected with"
554887Schin " \b-o\b with \astring\a.]"
564887Schin "[o:output]:[list?Construct the output line to comprise the fields specified "
574887Schin "in a blank or comma separated list \alist\a. Each element in "
584887Schin "\alist\a consists of a file number (either 1 or 2), a period, "
594887Schin "and a field number or \b0\b representing the join field. "
604887Schin "As an obsolete feature multiple occurrences of \b-o\b can "
614887Schin "be specified.]"
624887Schin "[t:separator|tabs]:[delim?Use \adelim\a as the field separator for both input"
634887Schin " and output.]"
644887Schin "[1:j1]#[field?Join on field \afield\a of \afile1\a. Fields start at 1.]"
654887Schin "[2:j2]#[field?Join on field \afield\a of \afile2\a. Fields start at 1.]"
664887Schin "[j:join]#[field?Equivalent to \b-1\b \afield\a \b-2\b \afield\a.]"
674887Schin "[a:unpairable]#[fileno?Write a line for each unpairable line in file"
684887Schin " \afileno\a, where \afileno\a is either 1 or 2, in addition to the"
694887Schin " normal output. If \b-a\b options appear for both 1 and 2, then "
704887Schin "all unpairable lines will be output.]"
714887Schin "[v:suppress]#[fileno?Write a line for each unpairable line in file"
724887Schin " \afileno\a, where \afileno\a is either 1 or 2, instead of the normal "
734887Schin "output. If \b-v\b options appear for both 1 and 2, then "
744887Schin "all unpairable lines will be output.] ]"
754887Schin "[i:ignorecase?Ignore case in field comparisons.]"
764887Schin "[B!:mmap?Enable memory mapped reads instead of buffered.]"
774887Schin
784887Schin "[+?The following obsolete option forms are also recognized: \b-j\b \afield\a"
794887Schin " is equivalent to \b-1\b \afield\a \b-2\b \afield\a, \b-j1\b \afield\a"
804887Schin " is equivalent to \b-1\b \afield\a, and \b-j2\b \afield\a is"
814887Schin " equivalent to \b-2\b \afield\a.]"
824887Schin
834887Schin "\n"
844887Schin "\nfile1 file2\n"
854887Schin "\n"
864887Schin "[+EXIT STATUS?]{"
874887Schin "[+0?Both files processed successfully.]"
884887Schin "[+>0?An error occurred.]"
894887Schin "}"
904887Schin "[+SEE ALSO?\bcut\b(1), \bcomm\b(1), \bpaste\b(1), \bsort\b(1), \buniq\b(1)]"
914887Schin ;
924887Schin
934887Schin #include <cmd.h>
944887Schin #include <sfdisc.h>
954887Schin
96*12068SRoger.Faulkner@Oracle.COM #if _hdr_wchar && _hdr_wctype && _lib_iswctype
97*12068SRoger.Faulkner@Oracle.COM
98*12068SRoger.Faulkner@Oracle.COM #include <wchar.h>
99*12068SRoger.Faulkner@Oracle.COM #include <wctype.h>
100*12068SRoger.Faulkner@Oracle.COM
101*12068SRoger.Faulkner@Oracle.COM #else
102*12068SRoger.Faulkner@Oracle.COM
103*12068SRoger.Faulkner@Oracle.COM #include <ctype.h>
104*12068SRoger.Faulkner@Oracle.COM
105*12068SRoger.Faulkner@Oracle.COM #ifndef iswspace
106*12068SRoger.Faulkner@Oracle.COM #define iswspace(x) isspace(x)
107*12068SRoger.Faulkner@Oracle.COM #endif
108*12068SRoger.Faulkner@Oracle.COM
109*12068SRoger.Faulkner@Oracle.COM #endif
110*12068SRoger.Faulkner@Oracle.COM
1114887Schin #define C_FILE1 001
1124887Schin #define C_FILE2 002
1134887Schin #define C_COMMON 004
1144887Schin #define C_ALL (C_FILE1|C_FILE2|C_COMMON)
1154887Schin
1164887Schin #define NFIELD 10
1174887Schin #define JOINFIELD 2
1184887Schin
1194887Schin #define S_DELIM 1
1204887Schin #define S_SPACE 2
1214887Schin #define S_NL 3
122*12068SRoger.Faulkner@Oracle.COM #define S_WIDE 4
1234887Schin
124*12068SRoger.Faulkner@Oracle.COM typedef struct Field_s
125*12068SRoger.Faulkner@Oracle.COM {
126*12068SRoger.Faulkner@Oracle.COM char* beg;
127*12068SRoger.Faulkner@Oracle.COM char* end;
128*12068SRoger.Faulkner@Oracle.COM } Field_t;
129*12068SRoger.Faulkner@Oracle.COM
130*12068SRoger.Faulkner@Oracle.COM typedef struct File_s
1314887Schin {
1324887Schin Sfio_t* iop;
1334887Schin char* name;
1344887Schin char* recptr;
1354887Schin int reclen;
1364887Schin int field;
1374887Schin int fieldlen;
1384887Schin int nfields;
1394887Schin int maxfields;
1404887Schin int spaces;
1414887Schin int hit;
1424887Schin int discard;
143*12068SRoger.Faulkner@Oracle.COM Field_t* fields;
1444887Schin } File_t;
1454887Schin
146*12068SRoger.Faulkner@Oracle.COM typedef struct Join_s
1474887Schin {
1484887Schin unsigned char state[1<<CHAR_BIT];
1494887Schin Sfio_t* outfile;
1504887Schin int* outlist;
1514887Schin int outmode;
1524887Schin int ooutmode;
1534887Schin char* nullfield;
154*12068SRoger.Faulkner@Oracle.COM char* delimstr;
1554887Schin int delim;
156*12068SRoger.Faulkner@Oracle.COM int delimlen;
1574887Schin int buffered;
1584887Schin int ignorecase;
159*12068SRoger.Faulkner@Oracle.COM int mb;
1604887Schin char* same;
1614887Schin int samesize;
1628462SApril.Chin@Sun.COM void* context;
1634887Schin File_t file[2];
1644887Schin } Join_t;
1654887Schin
1664887Schin static void
done(register Join_t * jp)1674887Schin done(register Join_t* jp)
1684887Schin {
1694887Schin if (jp->file[0].iop && jp->file[0].iop != sfstdin)
1704887Schin sfclose(jp->file[0].iop);
1714887Schin if (jp->file[1].iop && jp->file[1].iop != sfstdin)
1724887Schin sfclose(jp->file[1].iop);
1734887Schin if (jp->outlist)
1744887Schin free(jp->outlist);
175*12068SRoger.Faulkner@Oracle.COM if (jp->file[0].fields)
176*12068SRoger.Faulkner@Oracle.COM free(jp->file[0].fields);
177*12068SRoger.Faulkner@Oracle.COM if (jp->file[1].fields)
178*12068SRoger.Faulkner@Oracle.COM free(jp->file[1].fields);
1794887Schin if (jp->same)
1804887Schin free(jp->same);
1814887Schin free(jp);
1824887Schin }
1834887Schin
1844887Schin static Join_t*
init(void)1854887Schin init(void)
1864887Schin {
1874887Schin register Join_t* jp;
188*12068SRoger.Faulkner@Oracle.COM register int i;
1894887Schin
190*12068SRoger.Faulkner@Oracle.COM setlocale(LC_ALL, "");
1914887Schin if (jp = newof(0, Join_t, 1, 0))
1924887Schin {
193*12068SRoger.Faulkner@Oracle.COM if (jp->mb = mbwide())
194*12068SRoger.Faulkner@Oracle.COM for (i = 0x80; i <= 0xff; i++)
195*12068SRoger.Faulkner@Oracle.COM jp->state[i] = S_WIDE;
1964887Schin jp->state[' '] = jp->state['\t'] = S_SPACE;
197*12068SRoger.Faulkner@Oracle.COM jp->state['\n'] = S_NL;
1984887Schin jp->delim = -1;
1994887Schin jp->nullfield = 0;
200*12068SRoger.Faulkner@Oracle.COM if (!(jp->file[0].fields = newof(0, Field_t, NFIELD + 1, 0)) ||
201*12068SRoger.Faulkner@Oracle.COM !(jp->file[1].fields = newof(0, Field_t, NFIELD + 1, 0)))
2024887Schin {
2034887Schin done(jp);
2044887Schin return 0;
2054887Schin }
2064887Schin jp->file[0].maxfields = NFIELD;
2074887Schin jp->file[1].maxfields = NFIELD;
2084887Schin jp->outmode = C_COMMON;
2094887Schin }
2104887Schin return jp;
2114887Schin }
2124887Schin
2134887Schin static int
getolist(Join_t * jp,const char * first,char ** arglist)2144887Schin getolist(Join_t* jp, const char* first, char** arglist)
2154887Schin {
2164887Schin register const char* cp = first;
2174887Schin char** argv = arglist;
2184887Schin register int c;
2194887Schin int* outptr;
2204887Schin int* outmax;
2214887Schin int nfield = NFIELD;
2224887Schin char* str;
2234887Schin
2244887Schin outptr = jp->outlist = newof(0, int, NFIELD + 1, 0);
2254887Schin outmax = outptr + NFIELD;
2264887Schin while (c = *cp++)
2274887Schin {
2284887Schin if (c==' ' || c=='\t' || c==',')
2294887Schin continue;
2304887Schin str = (char*)--cp;
2314887Schin if (*cp=='0' && ((c=cp[1])==0 || c==' ' || c=='\t' || c==','))
2324887Schin {
2334887Schin str++;
2344887Schin c = JOINFIELD;
2354887Schin goto skip;
2364887Schin }
2374887Schin if (cp[1]!='.' || (*cp!='1' && *cp!='2') || (c=strtol(cp+2,&str,10)) <=0)
2384887Schin {
2394887Schin error(2,"%s: invalid field list",first);
2404887Schin break;
2414887Schin }
2424887Schin c--;
2434887Schin c <<=2;
2444887Schin if (*cp=='2')
2454887Schin c |=1;
2464887Schin skip:
2474887Schin if (outptr >= outmax)
2484887Schin {
2494887Schin jp->outlist = newof(jp->outlist, int, 2 * nfield + 1, 0);
2504887Schin outptr = jp->outlist + nfield;
2514887Schin nfield *= 2;
2524887Schin outmax = jp->outlist + nfield;
2534887Schin }
2544887Schin *outptr++ = c;
2554887Schin cp = str;
2564887Schin }
2574887Schin /* need to accept obsolescent command syntax */
2584887Schin while (1)
2594887Schin {
2604887Schin if (!(cp= *argv) || cp[1]!='.' || (*cp!='1' && *cp!='2'))
2614887Schin {
2624887Schin if (*cp=='0' && cp[1]==0)
2634887Schin {
2644887Schin c = JOINFIELD;
2654887Schin goto skip2;
2664887Schin }
2674887Schin break;
2684887Schin }
2694887Schin str = (char*)cp;
2704887Schin c = strtol(cp+2, &str,10);
2714887Schin if (*str || --c<0)
2724887Schin break;
2734887Schin argv++;
2744887Schin c <<= 2;
2754887Schin if (*cp=='2')
2764887Schin c |=1;
2774887Schin skip2:
2784887Schin if (outptr >= outmax)
2794887Schin {
2804887Schin jp->outlist = newof(jp->outlist, int, 2 * nfield + 1, 0);
2814887Schin outptr = jp->outlist + nfield;
2824887Schin nfield *= 2;
2834887Schin outmax = jp->outlist + nfield;
2844887Schin }
2854887Schin *outptr++ = c;
2864887Schin }
2874887Schin *outptr = -1;
2884887Schin return argv-arglist;
2894887Schin }
2904887Schin
2914887Schin /*
2924887Schin * read in a record from file <index> and split into fields
2934887Schin */
2944887Schin static unsigned char*
getrec(Join_t * jp,int index,int discard)2954887Schin getrec(Join_t* jp, int index, int discard)
2964887Schin {
2974887Schin register unsigned char* sp = jp->state;
2984887Schin register File_t* fp = &jp->file[index];
299*12068SRoger.Faulkner@Oracle.COM register Field_t* field = fp->fields;
300*12068SRoger.Faulkner@Oracle.COM register Field_t* fieldmax = field + fp->maxfields;
3014887Schin register char* cp;
302*12068SRoger.Faulkner@Oracle.COM register int n;
303*12068SRoger.Faulkner@Oracle.COM char* tp;
3044887Schin
3058462SApril.Chin@Sun.COM if (sh_checksig(jp->context))
3064887Schin return 0;
3074887Schin if (discard && fp->discard)
3084887Schin sfraise(fp->iop, SFSK_DISCARD, NiL);
3094887Schin fp->spaces = 0;
3104887Schin fp->hit = 0;
3114887Schin if (!(cp = sfgetr(fp->iop, '\n', 0)))
3124887Schin {
3134887Schin jp->outmode &= ~(1<<index);
3144887Schin return 0;
3154887Schin }
3164887Schin fp->recptr = cp;
3174887Schin fp->reclen = sfvalue(fp->iop);
318*12068SRoger.Faulkner@Oracle.COM if (jp->delim == '\n') /* handle new-line delimiter specially */
3194887Schin {
320*12068SRoger.Faulkner@Oracle.COM field->beg = cp;
3214887Schin cp += fp->reclen;
322*12068SRoger.Faulkner@Oracle.COM field->end = cp - 1;
323*12068SRoger.Faulkner@Oracle.COM field++;
3244887Schin }
325*12068SRoger.Faulkner@Oracle.COM else
326*12068SRoger.Faulkner@Oracle.COM do /* separate into fields */
3274887Schin {
328*12068SRoger.Faulkner@Oracle.COM if (field >= fieldmax)
329*12068SRoger.Faulkner@Oracle.COM {
330*12068SRoger.Faulkner@Oracle.COM n = 2 * fp->maxfields;
331*12068SRoger.Faulkner@Oracle.COM fp->fields = newof(fp->fields, Field_t, n + 1, 0);
332*12068SRoger.Faulkner@Oracle.COM field = fp->fields + fp->maxfields;
333*12068SRoger.Faulkner@Oracle.COM fp->maxfields = n;
334*12068SRoger.Faulkner@Oracle.COM fieldmax = fp->fields + n;
335*12068SRoger.Faulkner@Oracle.COM }
336*12068SRoger.Faulkner@Oracle.COM field->beg = cp;
337*12068SRoger.Faulkner@Oracle.COM if (jp->delim == -1)
338*12068SRoger.Faulkner@Oracle.COM {
339*12068SRoger.Faulkner@Oracle.COM switch (sp[*(unsigned char*)cp])
340*12068SRoger.Faulkner@Oracle.COM {
341*12068SRoger.Faulkner@Oracle.COM case S_SPACE:
342*12068SRoger.Faulkner@Oracle.COM cp++;
343*12068SRoger.Faulkner@Oracle.COM break;
344*12068SRoger.Faulkner@Oracle.COM case S_WIDE:
345*12068SRoger.Faulkner@Oracle.COM tp = cp;
346*12068SRoger.Faulkner@Oracle.COM if (iswspace(mbchar(tp)))
347*12068SRoger.Faulkner@Oracle.COM {
348*12068SRoger.Faulkner@Oracle.COM cp = tp;
349*12068SRoger.Faulkner@Oracle.COM break;
350*12068SRoger.Faulkner@Oracle.COM }
351*12068SRoger.Faulkner@Oracle.COM /*FALLTHROUGH*/
352*12068SRoger.Faulkner@Oracle.COM default:
353*12068SRoger.Faulkner@Oracle.COM goto next;
354*12068SRoger.Faulkner@Oracle.COM }
355*12068SRoger.Faulkner@Oracle.COM fp->spaces = 1;
356*12068SRoger.Faulkner@Oracle.COM if (jp->mb)
357*12068SRoger.Faulkner@Oracle.COM for (;;)
358*12068SRoger.Faulkner@Oracle.COM {
359*12068SRoger.Faulkner@Oracle.COM switch (sp[*(unsigned char*)cp++])
360*12068SRoger.Faulkner@Oracle.COM {
361*12068SRoger.Faulkner@Oracle.COM case S_SPACE:
362*12068SRoger.Faulkner@Oracle.COM continue;
363*12068SRoger.Faulkner@Oracle.COM case S_WIDE:
364*12068SRoger.Faulkner@Oracle.COM tp = cp - 1;
365*12068SRoger.Faulkner@Oracle.COM if (iswspace(mbchar(tp)))
366*12068SRoger.Faulkner@Oracle.COM {
367*12068SRoger.Faulkner@Oracle.COM cp = tp;
368*12068SRoger.Faulkner@Oracle.COM continue;
369*12068SRoger.Faulkner@Oracle.COM }
370*12068SRoger.Faulkner@Oracle.COM break;
371*12068SRoger.Faulkner@Oracle.COM }
372*12068SRoger.Faulkner@Oracle.COM break;
373*12068SRoger.Faulkner@Oracle.COM }
374*12068SRoger.Faulkner@Oracle.COM else
375*12068SRoger.Faulkner@Oracle.COM while (sp[*(unsigned char*)cp++]==S_SPACE);
376*12068SRoger.Faulkner@Oracle.COM cp--;
377*12068SRoger.Faulkner@Oracle.COM }
378*12068SRoger.Faulkner@Oracle.COM next:
379*12068SRoger.Faulkner@Oracle.COM if (jp->mb)
380*12068SRoger.Faulkner@Oracle.COM {
381*12068SRoger.Faulkner@Oracle.COM for (;;)
382*12068SRoger.Faulkner@Oracle.COM {
383*12068SRoger.Faulkner@Oracle.COM tp = cp;
384*12068SRoger.Faulkner@Oracle.COM switch (n = sp[*(unsigned char*)cp++])
385*12068SRoger.Faulkner@Oracle.COM {
386*12068SRoger.Faulkner@Oracle.COM case 0:
387*12068SRoger.Faulkner@Oracle.COM continue;
388*12068SRoger.Faulkner@Oracle.COM case S_WIDE:
389*12068SRoger.Faulkner@Oracle.COM cp--;
390*12068SRoger.Faulkner@Oracle.COM n = mbchar(cp);
391*12068SRoger.Faulkner@Oracle.COM if (n == jp->delim)
392*12068SRoger.Faulkner@Oracle.COM {
393*12068SRoger.Faulkner@Oracle.COM n = S_DELIM;
394*12068SRoger.Faulkner@Oracle.COM break;
395*12068SRoger.Faulkner@Oracle.COM }
396*12068SRoger.Faulkner@Oracle.COM if (jp->delim == -1 && iswspace(n))
397*12068SRoger.Faulkner@Oracle.COM {
398*12068SRoger.Faulkner@Oracle.COM n = S_SPACE;
399*12068SRoger.Faulkner@Oracle.COM break;
400*12068SRoger.Faulkner@Oracle.COM }
401*12068SRoger.Faulkner@Oracle.COM continue;
402*12068SRoger.Faulkner@Oracle.COM }
403*12068SRoger.Faulkner@Oracle.COM break;
404*12068SRoger.Faulkner@Oracle.COM }
405*12068SRoger.Faulkner@Oracle.COM field->end = tp;
406*12068SRoger.Faulkner@Oracle.COM }
407*12068SRoger.Faulkner@Oracle.COM else
408*12068SRoger.Faulkner@Oracle.COM {
409*12068SRoger.Faulkner@Oracle.COM while (!(n = sp[*(unsigned char*)cp++]));
410*12068SRoger.Faulkner@Oracle.COM field->end = cp - 1;
411*12068SRoger.Faulkner@Oracle.COM }
412*12068SRoger.Faulkner@Oracle.COM field++;
413*12068SRoger.Faulkner@Oracle.COM } while (n != S_NL);
414*12068SRoger.Faulkner@Oracle.COM fp->nfields = field - fp->fields;
415*12068SRoger.Faulkner@Oracle.COM if ((n = fp->field) < fp->nfields)
4164887Schin {
417*12068SRoger.Faulkner@Oracle.COM cp = fp->fields[n].beg;
4184887Schin /* eliminate leading spaces */
4194887Schin if (fp->spaces)
4204887Schin {
421*12068SRoger.Faulkner@Oracle.COM if (jp->mb)
422*12068SRoger.Faulkner@Oracle.COM for (;;)
423*12068SRoger.Faulkner@Oracle.COM {
424*12068SRoger.Faulkner@Oracle.COM switch (sp[*(unsigned char*)cp++])
425*12068SRoger.Faulkner@Oracle.COM {
426*12068SRoger.Faulkner@Oracle.COM case S_SPACE:
427*12068SRoger.Faulkner@Oracle.COM continue;
428*12068SRoger.Faulkner@Oracle.COM case S_WIDE:
429*12068SRoger.Faulkner@Oracle.COM tp = cp - 1;
430*12068SRoger.Faulkner@Oracle.COM if (iswspace(mbchar(tp)))
431*12068SRoger.Faulkner@Oracle.COM {
432*12068SRoger.Faulkner@Oracle.COM cp = tp;
433*12068SRoger.Faulkner@Oracle.COM continue;
434*12068SRoger.Faulkner@Oracle.COM }
435*12068SRoger.Faulkner@Oracle.COM break;
436*12068SRoger.Faulkner@Oracle.COM }
437*12068SRoger.Faulkner@Oracle.COM break;
438*12068SRoger.Faulkner@Oracle.COM }
439*12068SRoger.Faulkner@Oracle.COM else
440*12068SRoger.Faulkner@Oracle.COM while (sp[*(unsigned char*)cp++]==S_SPACE);
4414887Schin cp--;
4424887Schin }
443*12068SRoger.Faulkner@Oracle.COM fp->fieldlen = fp->fields[n].end - cp;
4444887Schin return (unsigned char*)cp;
4454887Schin }
4464887Schin fp->fieldlen = 0;
4474887Schin return (unsigned char*)"";
4484887Schin }
4494887Schin
450*12068SRoger.Faulkner@Oracle.COM static unsigned char*
_trace_getrec(Join_t * jp,int index,int discard)451*12068SRoger.Faulkner@Oracle.COM _trace_getrec(Join_t* jp, int index, int discard)
452*12068SRoger.Faulkner@Oracle.COM {
453*12068SRoger.Faulkner@Oracle.COM unsigned char* r;
454*12068SRoger.Faulkner@Oracle.COM
455*12068SRoger.Faulkner@Oracle.COM r = getrec(jp, index, discard);
456*12068SRoger.Faulkner@Oracle.COM return r;
457*12068SRoger.Faulkner@Oracle.COM }
458*12068SRoger.Faulkner@Oracle.COM #define getrec _trace_getrec
459*12068SRoger.Faulkner@Oracle.COM
4604887Schin #if DEBUG_TRACE
4614887Schin static unsigned char* u1,u2,u3;
4624887Schin #define getrec(p,n,d) (u1 = getrec(p, n, d), sfprintf(sfstdout, "[G%d#%d@%I*d:%-.8s]", __LINE__, n, sizeof(Sfoff_t), sftell(p->file[n].iop), u1), u1)
4634887Schin #endif
4644887Schin
4654887Schin /*
4664887Schin * print field <n> from file <index>
4674887Schin */
4684887Schin static int
outfield(Join_t * jp,int index,register int n,int last)4694887Schin outfield(Join_t* jp, int index, register int n, int last)
4704887Schin {
4714887Schin register File_t* fp = &jp->file[index];
4724887Schin register char* cp;
4734887Schin register char* cpmax;
4744887Schin register int size;
4754887Schin register Sfio_t* iop = jp->outfile;
476*12068SRoger.Faulkner@Oracle.COM char* tp;
4774887Schin
4784887Schin if (n < fp->nfields)
4794887Schin {
480*12068SRoger.Faulkner@Oracle.COM cp = fp->fields[n].beg;
481*12068SRoger.Faulkner@Oracle.COM cpmax = fp->fields[n].end + 1;
4824887Schin }
4834887Schin else
4844887Schin cp = 0;
485*12068SRoger.Faulkner@Oracle.COM if ((n = jp->delim) == -1)
4864887Schin {
48710898Sroland.mainz@nrubsig.org if (cp && fp->spaces)
4884887Schin {
489*12068SRoger.Faulkner@Oracle.COM register unsigned char* sp = jp->state;
490*12068SRoger.Faulkner@Oracle.COM
4914887Schin /*eliminate leading spaces */
492*12068SRoger.Faulkner@Oracle.COM if (jp->mb)
493*12068SRoger.Faulkner@Oracle.COM for (;;)
494*12068SRoger.Faulkner@Oracle.COM {
495*12068SRoger.Faulkner@Oracle.COM switch (sp[*(unsigned char*)cp++])
496*12068SRoger.Faulkner@Oracle.COM {
497*12068SRoger.Faulkner@Oracle.COM case S_SPACE:
498*12068SRoger.Faulkner@Oracle.COM continue;
499*12068SRoger.Faulkner@Oracle.COM case S_WIDE:
500*12068SRoger.Faulkner@Oracle.COM tp = cp - 1;
501*12068SRoger.Faulkner@Oracle.COM if (iswspace(mbchar(tp)))
502*12068SRoger.Faulkner@Oracle.COM {
503*12068SRoger.Faulkner@Oracle.COM cp = tp;
504*12068SRoger.Faulkner@Oracle.COM continue;
505*12068SRoger.Faulkner@Oracle.COM }
506*12068SRoger.Faulkner@Oracle.COM break;
507*12068SRoger.Faulkner@Oracle.COM }
508*12068SRoger.Faulkner@Oracle.COM break;
509*12068SRoger.Faulkner@Oracle.COM }
510*12068SRoger.Faulkner@Oracle.COM else
511*12068SRoger.Faulkner@Oracle.COM while (sp[*(unsigned char*)cp++]==S_SPACE);
5124887Schin cp--;
5134887Schin }
5144887Schin n = ' ';
5154887Schin }
516*12068SRoger.Faulkner@Oracle.COM else if (jp->delimstr)
517*12068SRoger.Faulkner@Oracle.COM n = -1;
5184887Schin if (last)
5194887Schin n = '\n';
5204887Schin if (cp)
521*12068SRoger.Faulkner@Oracle.COM size = cpmax - cp;
5224887Schin else
5234887Schin size = 0;
524*12068SRoger.Faulkner@Oracle.COM if (n == -1)
525*12068SRoger.Faulkner@Oracle.COM {
526*12068SRoger.Faulkner@Oracle.COM if (size<=1)
527*12068SRoger.Faulkner@Oracle.COM {
528*12068SRoger.Faulkner@Oracle.COM if (jp->nullfield && sfputr(iop, jp->nullfield, -1) < 0)
529*12068SRoger.Faulkner@Oracle.COM return -1;
530*12068SRoger.Faulkner@Oracle.COM }
531*12068SRoger.Faulkner@Oracle.COM else if (sfwrite(iop, cp, size) < 0)
532*12068SRoger.Faulkner@Oracle.COM return -1;
533*12068SRoger.Faulkner@Oracle.COM if (sfwrite(iop, jp->delimstr, jp->delimlen) < 0)
534*12068SRoger.Faulkner@Oracle.COM return -1;
535*12068SRoger.Faulkner@Oracle.COM }
536*12068SRoger.Faulkner@Oracle.COM else if (size <= 1)
5374887Schin {
5384887Schin if (!jp->nullfield)
539*12068SRoger.Faulkner@Oracle.COM sfputc(iop, n);
540*12068SRoger.Faulkner@Oracle.COM else if (sfputr(iop, jp->nullfield, n) < 0)
5414887Schin return -1;
5424887Schin }
5434887Schin else
5444887Schin {
5454887Schin last = cp[size-1];
5464887Schin cp[size-1] = n;
547*12068SRoger.Faulkner@Oracle.COM if (sfwrite(iop, cp, size) < 0)
5484887Schin return -1;
5494887Schin cp[size-1] = last;
5504887Schin }
5514887Schin return 0;
5524887Schin }
5534887Schin
5544887Schin #if DEBUG_TRACE
5554887Schin static int i1,i2,i3;
5564887Schin #define outfield(p,i,n,f) (sfprintf(sfstdout, "[F%d#%d:%d,%d]", __LINE__, i1=i, i2=n, i3=f), outfield(p, i1, i2, i3))
5574887Schin #endif
5584887Schin
5594887Schin static int
outrec(register Join_t * jp,int mode)5604887Schin outrec(register Join_t* jp, int mode)
5614887Schin {
5624887Schin register File_t* fp;
5634887Schin register int i;
5644887Schin register int j;
5654887Schin register int k;
5664887Schin register int n;
5674887Schin int* out;
5684887Schin
5694887Schin if (mode < 0 && jp->file[0].hit++)
5704887Schin return 0;
5714887Schin if (mode > 0 && jp->file[1].hit++)
5724887Schin return 0;
5734887Schin if (out = jp->outlist)
5744887Schin {
5754887Schin while ((n = *out++) >= 0)
5764887Schin {
5774887Schin if (n == JOINFIELD)
5784887Schin {
5794887Schin i = mode >= 0;
5804887Schin j = jp->file[i].field;
5814887Schin }
5824887Schin else
5834887Schin {
5844887Schin i = n & 1;
5854887Schin j = (mode<0 && i || mode>0 && !i) ?
5864887Schin jp->file[i].nfields :
5874887Schin n >> 2;
5884887Schin }
5894887Schin if (outfield(jp, i, j, *out < 0) < 0)
5904887Schin return -1;
5914887Schin }
5924887Schin return 0;
5934887Schin }
5944887Schin k = jp->file[0].nfields;
5954887Schin if (mode >= 0)
5964887Schin k += jp->file[1].nfields - 1;
5974887Schin for (i=0; i<2; i++)
5984887Schin {
5994887Schin fp = &jp->file[i];
6004887Schin if (mode>0 && i==0)
6014887Schin {
6024887Schin k -= (fp->nfields - 1);
6034887Schin continue;
6044887Schin }
6054887Schin n = fp->field;
6064887Schin if (mode||i==0)
6074887Schin {
6084887Schin /* output join field first */
6094887Schin if (outfield(jp,i,n,!--k) < 0)
6104887Schin return -1;
6114887Schin if (!k)
6124887Schin return 0;
6134887Schin for (j=0; j<n; j++)
6144887Schin {
6154887Schin if (outfield(jp,i,j,!--k) < 0)
6164887Schin return -1;
6174887Schin if (!k)
6184887Schin return 0;
6194887Schin }
6204887Schin j = n + 1;
6214887Schin }
6224887Schin else
6234887Schin j = 0;
6244887Schin for (;j<fp->nfields; j++)
6254887Schin {
6264887Schin if (j!=n && outfield(jp,i,j,!--k) < 0)
6274887Schin return -1;
6284887Schin if (!k)
6294887Schin return 0;
6304887Schin }
6314887Schin }
6324887Schin return 0;
6334887Schin }
6344887Schin
6354887Schin #if DEBUG_TRACE
6364887Schin #define outrec(p,n) (sfprintf(sfstdout, "[R#%d,%d,%lld,%lld:%-.*s{%d}:%-.*s{%d}]", __LINE__, i1=n, lo, hi, jp->file[0].fieldlen, cp1, jp->file[0].hit, jp->file[1].fieldlen, cp2, jp->file[1].hit), outrec(p, i1))
6374887Schin #endif
6384887Schin
6394887Schin static int
join(Join_t * jp)6404887Schin join(Join_t* jp)
6414887Schin {
6424887Schin register unsigned char* cp1;
6434887Schin register unsigned char* cp2;
6444887Schin register int n1;
6454887Schin register int n2;
6464887Schin register int n;
6474887Schin register int cmp;
6484887Schin register int same;
6494887Schin int o2;
6504887Schin Sfoff_t lo = -1;
6514887Schin Sfoff_t hi = -1;
6524887Schin
6534887Schin if ((cp1 = getrec(jp, 0, 0)) && (cp2 = getrec(jp, 1, 0)) || (cp2 = 0))
6544887Schin {
6554887Schin n1 = jp->file[0].fieldlen;
6564887Schin n2 = jp->file[1].fieldlen;
6574887Schin same = 0;
6584887Schin for (;;)
6594887Schin {
6604887Schin n = n1 < n2 ? n1 : n2;
6614887Schin #if DEBUG_TRACE
6624887Schin if (!n && !(cmp = n1 < n2 ? -1 : (n1 > n2)) || n && !(cmp = (int)*cp1 - (int)*cp2) && !(cmp = jp->ignorecase ? strncasecmp((char*)cp1, (char*)cp2, n) : memcmp(cp1, cp2, n)))
6634887Schin cmp = n1 - n2;
6644887Schin sfprintf(sfstdout, "[C#%d:%d(%c-%c),%d,%lld,%lld%s]", __LINE__, cmp, *cp1, *cp2, same, lo, hi, (jp->outmode & C_COMMON) ? ",COMMON" : "");
6654887Schin if (!cmp)
6664887Schin #else
6674887Schin if (!n && !(cmp = n1 < n2 ? -1 : (n1 > n2)) || n && !(cmp = (int)*cp1 - (int)*cp2) && !(cmp = jp->ignorecase ? strncasecmp((char*)cp1, (char*)cp2, n) : memcmp(cp1, cp2, n)) && !(cmp = n1 - n2))
6684887Schin #endif
6694887Schin {
6704887Schin if (!(jp->outmode & C_COMMON))
6714887Schin {
6724887Schin if (cp1 = getrec(jp, 0, 1))
6734887Schin {
6744887Schin n1 = jp->file[0].fieldlen;
6754887Schin same = 1;
6764887Schin continue;
6774887Schin }
6784887Schin if ((jp->ooutmode & (C_FILE1|C_FILE2)) != C_FILE2)
6794887Schin break;
6804887Schin if (sfseek(jp->file[0].iop, (Sfoff_t)-jp->file[0].reclen, SEEK_CUR) < 0 || !(cp1 = getrec(jp, 0, 0)))
6814887Schin {
6824887Schin error(ERROR_SYSTEM|2, "%s: seek error", jp->file[0].name);
6834887Schin return -1;
6844887Schin }
6854887Schin }
6864887Schin else if (outrec(jp, 0) < 0)
6874887Schin return -1;
6884887Schin else if (lo < 0 && (jp->outmode & C_COMMON))
6894887Schin {
6904887Schin if ((lo = sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR)) < 0)
6914887Schin {
6924887Schin error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
6934887Schin return -1;
6944887Schin }
6954887Schin lo -= jp->file[1].reclen;
6964887Schin }
6974887Schin if (cp2 = getrec(jp, 1, lo < 0))
6984887Schin {
6994887Schin n2 = jp->file[1].fieldlen;
7004887Schin continue;
7014887Schin }
7024887Schin #if DEBUG_TRACE
7034887Schin sfprintf(sfstdout, "[2#%d:0,%lld,%lld]", __LINE__, lo, hi);
7044887Schin #endif
7054887Schin }
7064887Schin else if (cmp > 0)
7074887Schin {
7084887Schin if (same)
7094887Schin {
7104887Schin same = 0;
7114887Schin next:
7124887Schin if (n2 > jp->samesize)
7134887Schin {
7144887Schin jp->samesize = roundof(n2, 16);
7154887Schin if (!(jp->same = newof(jp->same, char, jp->samesize, 0)))
7164887Schin {
7174887Schin error(ERROR_SYSTEM|2, "out of space");
7184887Schin return -1;
7194887Schin }
7204887Schin }
7214887Schin memcpy(jp->same, cp2, o2 = n2);
7224887Schin if (!(cp2 = getrec(jp, 1, 0)))
7234887Schin break;
7244887Schin n2 = jp->file[1].fieldlen;
7254887Schin if (n2 == o2 && *cp2 == *jp->same && !memcmp(cp2, jp->same, n2))
7264887Schin goto next;
7274887Schin continue;
7284887Schin }
7294887Schin if (hi >= 0)
7304887Schin {
7314887Schin if (sfseek(jp->file[1].iop, hi, SEEK_SET) != hi)
7324887Schin {
7334887Schin error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
7344887Schin return -1;
7354887Schin }
7364887Schin hi = -1;
7374887Schin }
7384887Schin else if ((jp->outmode & C_FILE2) && outrec(jp, 1) < 0)
7394887Schin return -1;
7404887Schin lo = -1;
7414887Schin if (cp2 = getrec(jp, 1, 1))
7424887Schin {
7434887Schin n2 = jp->file[1].fieldlen;
7444887Schin continue;
7454887Schin }
7464887Schin #if DEBUG_TRACE
7474887Schin sfprintf(sfstdout, "[2#%d:0,%lld,%lld]", __LINE__, lo, hi);
7484887Schin #endif
7494887Schin }
7504887Schin else if (same)
7514887Schin {
7524887Schin same = 0;
7534887Schin if (!(cp1 = getrec(jp, 0, 0)))
7544887Schin break;
7554887Schin n1 = jp->file[0].fieldlen;
7564887Schin continue;
7574887Schin }
7584887Schin if (lo >= 0)
7594887Schin {
7604887Schin if ((hi = sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR)) < 0 ||
7614887Schin (hi -= jp->file[1].reclen) < 0 ||
7624887Schin sfseek(jp->file[1].iop, lo, SEEK_SET) != lo ||
7634887Schin !(cp2 = getrec(jp, 1, 0)))
7644887Schin {
7654887Schin error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
7664887Schin return -1;
7674887Schin }
7684887Schin n2 = jp->file[1].fieldlen;
7694887Schin lo = -1;
7704887Schin if (jp->file[1].discard)
7714887Schin sfseek(jp->file[1].iop, (Sfoff_t)-1, SEEK_SET);
7724887Schin }
7734887Schin else if (!cp2)
7744887Schin break;
7754887Schin else if ((jp->outmode & C_FILE1) && outrec(jp, -1) < 0)
7764887Schin return -1;
7774887Schin if (!(cp1 = getrec(jp, 0, 1)))
7784887Schin break;
7794887Schin n1 = jp->file[0].fieldlen;
7804887Schin }
7814887Schin }
7824887Schin #if DEBUG_TRACE
7834887Schin sfprintf(sfstdout, "[X#%d:?,%p,%p,%d%,%d,%d%s]", __LINE__, cp1, cp2, cmp, lo, hi, (jp->outmode & C_COMMON) ? ",COMMON" : "");
7844887Schin #endif
7854887Schin if (cp2)
7864887Schin {
7874887Schin if (hi >= 0 &&
7884887Schin sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR) < hi &&
7894887Schin sfseek(jp->file[1].iop, hi, SEEK_SET) != hi)
7904887Schin {
7914887Schin error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
7924887Schin return -1;
7934887Schin }
7944887Schin #if DEBUG_TRACE
7954887Schin sfprintf(sfstdout, "[O#%d:%02o:%02o]", __LINE__, jp->ooutmode, jp->outmode);
7964887Schin #endif
7974887Schin cp1 = (!cp1 && cmp && hi < 0 && !jp->file[1].hit && ((jp->ooutmode ^ C_ALL) <= 1 || jp->outmode == 2)) ? cp2 : getrec(jp, 1, 0);
7984887Schin cmp = 1;
7994887Schin n = 1;
8004887Schin }
8014887Schin else
8024887Schin {
8034887Schin cmp = -1;
8044887Schin n = 0;
8054887Schin }
8064887Schin #if DEBUG_TRACE
8074887Schin sfprintf(sfstdout, "[X#%d:%d,%p,%p,%d,%02o,%02o%s]", __LINE__, n, cp1, cp2, cmp, jp->ooutmode, jp->outmode, (jp->outmode & C_COMMON) ? ",COMMON" : "");
8084887Schin #endif
8094887Schin if (!cp1 || !(jp->outmode & (1<<n)))
8104887Schin {
8114887Schin if (cp1 && jp->file[n].iop == sfstdin)
8124887Schin sfseek(sfstdin, (Sfoff_t)0, SEEK_END);
8134887Schin return 0;
8144887Schin }
8154887Schin if (outrec(jp, cmp) < 0)
8164887Schin return -1;
8174887Schin do
8184887Schin {
8194887Schin if (!getrec(jp, n, 1))
8204887Schin return 0;
8214887Schin } while (outrec(jp, cmp) >= 0);
8224887Schin return -1;
8234887Schin }
8244887Schin
8254887Schin int
b_join(int argc,char ** argv,void * context)8264887Schin b_join(int argc, char** argv, void* context)
8274887Schin {
8284887Schin register int n;
8294887Schin register char* cp;
8304887Schin register Join_t* jp;
8314887Schin char* e;
8324887Schin
8334887Schin #if !DEBUG_TRACE
8344887Schin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
8354887Schin #endif
8364887Schin if (!(jp = init()))
8374887Schin error(ERROR_system(1),"out of space");
8388462SApril.Chin@Sun.COM jp->context = context;
8394887Schin for (;;)
8404887Schin {
8414887Schin switch (n = optget(argv, usage))
8424887Schin {
8434887Schin case 0:
8444887Schin break;
8454887Schin case 'j':
8464887Schin /*
8474887Schin * check for obsolete "-j1 field" and "-j2 field"
8484887Schin */
8494887Schin
8504887Schin if (opt_info.offset == 0)
8514887Schin {
8524887Schin cp = argv[opt_info.index - 1];
8534887Schin for (n = strlen(cp) - 1; n > 0 && cp[n] != 'j'; n--);
8544887Schin n = cp[n] == 'j';
8554887Schin }
8564887Schin else
8574887Schin n = 0;
8584887Schin if (n)
8594887Schin {
8604887Schin if (opt_info.num!=1 && opt_info.num!=2)
8614887Schin error(2,"-jfileno field: fileno must be 1 or 2");
8624887Schin n = '0' + opt_info.num;
8634887Schin if (!(cp = argv[opt_info.index]))
8644887Schin {
8654887Schin argc = 0;
8664887Schin break;
8674887Schin }
8684887Schin opt_info.num = strtol(cp, &e, 10);
8694887Schin if (*e)
8704887Schin {
8714887Schin argc = 0;
8724887Schin break;
8734887Schin }
8744887Schin opt_info.index++;
8754887Schin }
8764887Schin else
8774887Schin {
8784887Schin jp->file[0].field = (int)(opt_info.num-1);
8794887Schin n = '2';
8804887Schin }
8814887Schin /*FALLTHROUGH*/
8824887Schin case '1':
8834887Schin case '2':
8844887Schin if (opt_info.num <=0)
8854887Schin error(2,"field number must positive");
8864887Schin jp->file[n-'1'].field = (int)(opt_info.num-1);
8874887Schin continue;
8884887Schin case 'v':
8894887Schin jp->outmode &= ~C_COMMON;
8904887Schin /*FALLTHROUGH*/
8914887Schin case 'a':
8924887Schin if (opt_info.num!=1 && opt_info.num!=2)
8934887Schin error(2,"%s: file number must be 1 or 2", opt_info.name);
8944887Schin jp->outmode |= 1<<(opt_info.num-1);
8954887Schin continue;
8964887Schin case 'e':
8974887Schin jp->nullfield = opt_info.arg;
8984887Schin continue;
8994887Schin case 'o':
9004887Schin /* need to accept obsolescent command syntax */
9014887Schin n = getolist(jp, opt_info.arg, argv+opt_info.index);
9024887Schin opt_info.index += n;
9034887Schin continue;
9044887Schin case 't':
9054887Schin jp->state[' '] = jp->state['\t'] = 0;
906*12068SRoger.Faulkner@Oracle.COM if (jp->mb)
907*12068SRoger.Faulkner@Oracle.COM {
908*12068SRoger.Faulkner@Oracle.COM cp = opt_info.arg;
909*12068SRoger.Faulkner@Oracle.COM jp->delim = mbchar(cp);
910*12068SRoger.Faulkner@Oracle.COM if ((n = cp - opt_info.arg) > 1)
911*12068SRoger.Faulkner@Oracle.COM {
912*12068SRoger.Faulkner@Oracle.COM jp->delimlen = n;
913*12068SRoger.Faulkner@Oracle.COM jp->delimstr = opt_info.arg;
914*12068SRoger.Faulkner@Oracle.COM continue;
915*12068SRoger.Faulkner@Oracle.COM }
916*12068SRoger.Faulkner@Oracle.COM }
917*12068SRoger.Faulkner@Oracle.COM n = *(unsigned char*)opt_info.arg;
9184887Schin jp->state[n] = S_DELIM;
9194887Schin jp->delim = n;
9204887Schin continue;
9214887Schin case 'i':
9224887Schin jp->ignorecase = !opt_info.num;
9234887Schin continue;
9244887Schin case 'B':
9254887Schin jp->buffered = !opt_info.num;
9264887Schin continue;
9274887Schin case ':':
9284887Schin error(2, "%s", opt_info.arg);
9294887Schin break;
9304887Schin case '?':
9314887Schin done(jp);
9324887Schin error(ERROR_usage(2), "%s", opt_info.arg);
9334887Schin break;
9344887Schin }
9354887Schin break;
9364887Schin }
9374887Schin argv += opt_info.index;
9384887Schin argc -= opt_info.index;
9394887Schin if (error_info.errors || argc!=2)
9404887Schin {
9414887Schin done(jp);
9424887Schin error(ERROR_usage(2),"%s", optusage(NiL));
9434887Schin }
9444887Schin jp->ooutmode = jp->outmode;
9454887Schin jp->file[0].name = cp = *argv++;
9464887Schin if (streq(cp,"-"))
9474887Schin {
9484887Schin if (sfseek(sfstdin,(Sfoff_t)0,SEEK_CUR) < 0)
9494887Schin {
9504887Schin if (sfdcseekable(sfstdin))
9514887Schin error(ERROR_warn(0),"%s: seek may fail",cp);
9524887Schin else
9534887Schin jp->file[0].discard = 1;
9544887Schin }
9554887Schin jp->file[0].iop = sfstdin;
9564887Schin }
9574887Schin else if (!(jp->file[0].iop = sfopen(NiL, cp, "r")))
9584887Schin {
9594887Schin done(jp);
9604887Schin error(ERROR_system(1),"%s: cannot open",cp);
9614887Schin }
9624887Schin jp->file[1].name = cp = *argv;
9634887Schin if (streq(cp,"-"))
9644887Schin {
9654887Schin if (sfseek(sfstdin,(Sfoff_t)0,SEEK_CUR) < 0)
9664887Schin {
9674887Schin if (sfdcseekable(sfstdin))
9684887Schin error(ERROR_warn(0),"%s: seek may fail",cp);
9694887Schin else
9704887Schin jp->file[1].discard = 1;
9714887Schin }
9724887Schin jp->file[1].iop = sfstdin;
9734887Schin }
9744887Schin else if (!(jp->file[1].iop = sfopen(NiL, cp, "r")))
9754887Schin {
9764887Schin done(jp);
9774887Schin error(ERROR_system(1),"%s: cannot open",cp);
9784887Schin }
9794887Schin if (jp->buffered)
9804887Schin {
9814887Schin sfsetbuf(jp->file[0].iop, jp->file[0].iop, SF_UNBOUND);
98210898Sroland.mainz@nrubsig.org sfsetbuf(jp->file[1].iop, jp->file[1].iop, SF_UNBOUND);
9834887Schin }
9844887Schin jp->outfile = sfstdout;
9854887Schin if (!jp->outlist)
9864887Schin jp->nullfield = 0;
9874887Schin if (join(jp) < 0)
9884887Schin {
9894887Schin done(jp);
9904887Schin error(ERROR_system(1),"write error");
9914887Schin }
9924887Schin else if (jp->file[0].iop==sfstdin || jp->file[1].iop==sfstdin)
9934887Schin sfseek(sfstdin,(Sfoff_t)0,SEEK_END);
9944887Schin done(jp);
9954887Schin return error_info.errors;
9964887Schin }
997