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