xref: /onnv-gate/usr/src/lib/libcmd/common/join.c (revision 12068:08a39a083754)
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