xref: /titanic_51/usr/src/lib/libcmd/common/join.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*3e14f97fSRoger A. Faulkner *          Copyright (c) 1992-2010 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6da2e3ebdSchin *                  Common Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19da2e3ebdSchin *                                                                      *
20da2e3ebdSchin ***********************************************************************/
21da2e3ebdSchin #pragma prototyped
22da2e3ebdSchin /*
23da2e3ebdSchin  * David Korn
24da2e3ebdSchin  * Glenn Fowler
25da2e3ebdSchin  * AT&T Research
26da2e3ebdSchin  *
27da2e3ebdSchin  * join
28da2e3ebdSchin  */
29da2e3ebdSchin 
30da2e3ebdSchin static const char usage[] =
31*3e14f97fSRoger A. Faulkner "[-?\n@(#)$Id: join (AT&T Research) 2009-12-10 $\n]"
32da2e3ebdSchin USAGE_LICENSE
33da2e3ebdSchin "[+NAME?join - relational database operator]"
34da2e3ebdSchin "[+DESCRIPTION?\bjoin\b performs an \aequality join\a on the files \afile1\a "
35da2e3ebdSchin 	"and \afile2\a and writes the resulting joined files to standard "
36da2e3ebdSchin 	"output.  By default, a field is delimited by one or more spaces "
37da2e3ebdSchin 	"and tabs with leading spaces and/or tabs ignored.  The \b-t\b option "
38da2e3ebdSchin 	"can be used to change the field delimiter.]"
39da2e3ebdSchin "[+?The \ajoin field\a is a field in each file on which files are compared. "
40da2e3ebdSchin 	"By default \bjoin\b writes one line in the output for each pair "
41da2e3ebdSchin 	"of lines in \afiles1\a and \afiles2\a that have identical join "
42da2e3ebdSchin 	"fields.  The default output line consists of the join field, "
43da2e3ebdSchin 	"then the remaining fields from \afile1\a, then the remaining "
44da2e3ebdSchin 	"fields from \afile2\a, but this can be changed with the \b-o\b "
45da2e3ebdSchin 	"option.  The \b-a\b option can be used to add unmatched lines "
46da2e3ebdSchin 	"to the output.  The \b-v\b option can be used to output only "
47da2e3ebdSchin 	"unmatched lines.]"
48da2e3ebdSchin "[+?The files \afile1\a and \afile2\a must be ordered in the collating "
49da2e3ebdSchin 	"sequence of \bsort -b\b on the fields on which they are to be "
50da2e3ebdSchin 	"joined otherwise the results are unspecified.]"
51da2e3ebdSchin "[+?If either \afile1\a or \afile2\a is \b-\b, \bjoin\b "
52da2e3ebdSchin         "uses standard input starting at the current location.]"
53da2e3ebdSchin 
54da2e3ebdSchin "[e:empty]:[string?Replace empty output fields in the list selected with"
55da2e3ebdSchin "	\b-o\b with \astring\a.]"
56da2e3ebdSchin "[o:output]:[list?Construct the output line to comprise the fields specified "
57da2e3ebdSchin 	"in a blank or comma separated list \alist\a.  Each element in "
58da2e3ebdSchin 	"\alist\a consists of a file number (either 1 or 2), a period, "
59da2e3ebdSchin 	"and a field number or \b0\b representing the join field.  "
60da2e3ebdSchin 	"As an obsolete feature multiple occurrences of \b-o\b can "
61da2e3ebdSchin 	"be specified.]"
62da2e3ebdSchin "[t:separator|tabs]:[delim?Use \adelim\a as the field separator for both input"
63da2e3ebdSchin "	and output.]"
64da2e3ebdSchin "[1:j1]#[field?Join on field \afield\a of \afile1\a.  Fields start at 1.]"
65da2e3ebdSchin "[2:j2]#[field?Join on field \afield\a of \afile2\a.  Fields start at 1.]"
66da2e3ebdSchin "[j:join]#[field?Equivalent to \b-1\b \afield\a \b-2\b \afield\a.]"
67da2e3ebdSchin "[a:unpairable]#[fileno?Write a line for each unpairable line in file"
68da2e3ebdSchin "	\afileno\a, where \afileno\a is either 1 or 2, in addition to the"
69da2e3ebdSchin "	normal output.  If \b-a\b options appear for both 1 and 2, then "
70da2e3ebdSchin 	"all unpairable lines will be output.]"
71da2e3ebdSchin "[v:suppress]#[fileno?Write a line for each unpairable line in file"
72da2e3ebdSchin "	\afileno\a, where \afileno\a is either 1 or 2, instead of the normal "
73da2e3ebdSchin 	"output.  If \b-v\b options appear for both 1 and 2, then "
74da2e3ebdSchin 	"all unpairable lines will be output.] ]"
75da2e3ebdSchin "[i:ignorecase?Ignore case in field comparisons.]"
76da2e3ebdSchin "[B!:mmap?Enable memory mapped reads instead of buffered.]"
77da2e3ebdSchin 
78da2e3ebdSchin "[+?The following obsolete option forms are also recognized: \b-j\b \afield\a"
79da2e3ebdSchin "	is equivalent to \b-1\b \afield\a \b-2\b \afield\a, \b-j1\b \afield\a"
80da2e3ebdSchin "	is equivalent to \b-1\b \afield\a, and \b-j2\b \afield\a is"
81da2e3ebdSchin "	equivalent to \b-2\b \afield\a.]"
82da2e3ebdSchin 
83da2e3ebdSchin "\n"
84da2e3ebdSchin "\nfile1 file2\n"
85da2e3ebdSchin "\n"
86da2e3ebdSchin "[+EXIT STATUS?]{"
87da2e3ebdSchin 	"[+0?Both files processed successfully.]"
88da2e3ebdSchin 	"[+>0?An error occurred.]"
89da2e3ebdSchin "}"
90da2e3ebdSchin "[+SEE ALSO?\bcut\b(1), \bcomm\b(1), \bpaste\b(1), \bsort\b(1), \buniq\b(1)]"
91da2e3ebdSchin ;
92da2e3ebdSchin 
93da2e3ebdSchin #include <cmd.h>
94da2e3ebdSchin #include <sfdisc.h>
95da2e3ebdSchin 
96*3e14f97fSRoger A. Faulkner #if _hdr_wchar && _hdr_wctype && _lib_iswctype
97*3e14f97fSRoger A. Faulkner 
98*3e14f97fSRoger A. Faulkner #include <wchar.h>
99*3e14f97fSRoger A. Faulkner #include <wctype.h>
100*3e14f97fSRoger A. Faulkner 
101*3e14f97fSRoger A. Faulkner #else
102*3e14f97fSRoger A. Faulkner 
103*3e14f97fSRoger A. Faulkner #include <ctype.h>
104*3e14f97fSRoger A. Faulkner 
105*3e14f97fSRoger A. Faulkner #ifndef iswspace
106*3e14f97fSRoger A. Faulkner #define iswspace(x)	isspace(x)
107*3e14f97fSRoger A. Faulkner #endif
108*3e14f97fSRoger A. Faulkner 
109*3e14f97fSRoger A. Faulkner #endif
110*3e14f97fSRoger A. Faulkner 
111da2e3ebdSchin #define C_FILE1		001
112da2e3ebdSchin #define C_FILE2		002
113da2e3ebdSchin #define C_COMMON	004
114da2e3ebdSchin #define C_ALL		(C_FILE1|C_FILE2|C_COMMON)
115da2e3ebdSchin 
116da2e3ebdSchin #define NFIELD		10
117da2e3ebdSchin #define JOINFIELD	2
118da2e3ebdSchin 
119da2e3ebdSchin #define S_DELIM		1
120da2e3ebdSchin #define S_SPACE		2
121da2e3ebdSchin #define S_NL		3
122*3e14f97fSRoger A. Faulkner #define S_WIDE		4
123da2e3ebdSchin 
124*3e14f97fSRoger A. Faulkner typedef struct Field_s
125*3e14f97fSRoger A. Faulkner {
126*3e14f97fSRoger A. Faulkner 	char*		beg;
127*3e14f97fSRoger A. Faulkner 	char*		end;
128*3e14f97fSRoger A. Faulkner } Field_t;
129*3e14f97fSRoger A. Faulkner 
130*3e14f97fSRoger A. Faulkner typedef struct File_s
131da2e3ebdSchin {
132da2e3ebdSchin 	Sfio_t*		iop;
133da2e3ebdSchin 	char*		name;
134da2e3ebdSchin 	char*		recptr;
135da2e3ebdSchin 	int		reclen;
136da2e3ebdSchin 	int		field;
137da2e3ebdSchin 	int		fieldlen;
138da2e3ebdSchin 	int		nfields;
139da2e3ebdSchin 	int		maxfields;
140da2e3ebdSchin 	int		spaces;
141da2e3ebdSchin 	int		hit;
142da2e3ebdSchin 	int		discard;
143*3e14f97fSRoger A. Faulkner 	Field_t*	fields;
144da2e3ebdSchin } File_t;
145da2e3ebdSchin 
146*3e14f97fSRoger A. Faulkner typedef struct Join_s
147da2e3ebdSchin {
148da2e3ebdSchin 	unsigned char	state[1<<CHAR_BIT];
149da2e3ebdSchin 	Sfio_t*		outfile;
150da2e3ebdSchin 	int*		outlist;
151da2e3ebdSchin 	int		outmode;
152da2e3ebdSchin 	int		ooutmode;
153da2e3ebdSchin 	char*		nullfield;
154*3e14f97fSRoger A. Faulkner 	char*		delimstr;
155da2e3ebdSchin 	int		delim;
156*3e14f97fSRoger A. Faulkner 	int		delimlen;
157da2e3ebdSchin 	int		buffered;
158da2e3ebdSchin 	int		ignorecase;
159*3e14f97fSRoger A. Faulkner 	int		mb;
160da2e3ebdSchin 	char*		same;
161da2e3ebdSchin 	int		samesize;
1627c2fbfb3SApril Chin 	void*		context;
163da2e3ebdSchin 	File_t		file[2];
164da2e3ebdSchin } Join_t;
165da2e3ebdSchin 
166da2e3ebdSchin static void
done(register Join_t * jp)167da2e3ebdSchin done(register Join_t* jp)
168da2e3ebdSchin {
169da2e3ebdSchin 	if (jp->file[0].iop && jp->file[0].iop != sfstdin)
170da2e3ebdSchin 		sfclose(jp->file[0].iop);
171da2e3ebdSchin 	if (jp->file[1].iop && jp->file[1].iop != sfstdin)
172da2e3ebdSchin 		sfclose(jp->file[1].iop);
173da2e3ebdSchin 	if (jp->outlist)
174da2e3ebdSchin 		free(jp->outlist);
175*3e14f97fSRoger A. Faulkner 	if (jp->file[0].fields)
176*3e14f97fSRoger A. Faulkner 		free(jp->file[0].fields);
177*3e14f97fSRoger A. Faulkner 	if (jp->file[1].fields)
178*3e14f97fSRoger A. Faulkner 		free(jp->file[1].fields);
179da2e3ebdSchin 	if (jp->same)
180da2e3ebdSchin 		free(jp->same);
181da2e3ebdSchin 	free(jp);
182da2e3ebdSchin }
183da2e3ebdSchin 
184da2e3ebdSchin static Join_t*
init(void)185da2e3ebdSchin init(void)
186da2e3ebdSchin {
187da2e3ebdSchin 	register Join_t*	jp;
188*3e14f97fSRoger A. Faulkner 	register int		i;
189da2e3ebdSchin 
190*3e14f97fSRoger A. Faulkner 	setlocale(LC_ALL, "");
191da2e3ebdSchin 	if (jp = newof(0, Join_t, 1, 0))
192da2e3ebdSchin 	{
193*3e14f97fSRoger A. Faulkner 		if (jp->mb = mbwide())
194*3e14f97fSRoger A. Faulkner 			for (i = 0x80; i <= 0xff; i++)
195*3e14f97fSRoger A. Faulkner 				jp->state[i] = S_WIDE;
196da2e3ebdSchin 		jp->state[' '] = jp->state['\t'] = S_SPACE;
197*3e14f97fSRoger A. Faulkner 		jp->state['\n'] = S_NL;
198da2e3ebdSchin 		jp->delim = -1;
199da2e3ebdSchin 		jp->nullfield = 0;
200*3e14f97fSRoger A. Faulkner 		if (!(jp->file[0].fields = newof(0, Field_t, NFIELD + 1, 0)) ||
201*3e14f97fSRoger A. Faulkner 		    !(jp->file[1].fields = newof(0, Field_t, NFIELD + 1, 0)))
202da2e3ebdSchin 		{
203da2e3ebdSchin 			done(jp);
204da2e3ebdSchin 			return 0;
205da2e3ebdSchin 		}
206da2e3ebdSchin 		jp->file[0].maxfields = NFIELD;
207da2e3ebdSchin 		jp->file[1].maxfields = NFIELD;
208da2e3ebdSchin 		jp->outmode = C_COMMON;
209da2e3ebdSchin 	}
210da2e3ebdSchin 	return jp;
211da2e3ebdSchin }
212da2e3ebdSchin 
213da2e3ebdSchin static int
getolist(Join_t * jp,const char * first,char ** arglist)214da2e3ebdSchin getolist(Join_t* jp, const char* first, char** arglist)
215da2e3ebdSchin {
216da2e3ebdSchin 	register const char*	cp = first;
217da2e3ebdSchin 	char**			argv = arglist;
218da2e3ebdSchin 	register int		c;
219da2e3ebdSchin 	int*			outptr;
220da2e3ebdSchin 	int*			outmax;
221da2e3ebdSchin 	int			nfield = NFIELD;
222da2e3ebdSchin 	char*			str;
223da2e3ebdSchin 
224da2e3ebdSchin 	outptr = jp->outlist = newof(0, int, NFIELD + 1, 0);
225da2e3ebdSchin 	outmax = outptr + NFIELD;
226da2e3ebdSchin 	while (c = *cp++)
227da2e3ebdSchin 	{
228da2e3ebdSchin 		if (c==' ' || c=='\t' || c==',')
229da2e3ebdSchin 			continue;
230da2e3ebdSchin 		str = (char*)--cp;
231da2e3ebdSchin 		if (*cp=='0' && ((c=cp[1])==0 || c==' ' || c=='\t' || c==','))
232da2e3ebdSchin 		{
233da2e3ebdSchin 			str++;
234da2e3ebdSchin 			c = JOINFIELD;
235da2e3ebdSchin 			goto skip;
236da2e3ebdSchin 		}
237da2e3ebdSchin 		if (cp[1]!='.' || (*cp!='1' && *cp!='2') || (c=strtol(cp+2,&str,10)) <=0)
238da2e3ebdSchin 		{
239da2e3ebdSchin 			error(2,"%s: invalid field list",first);
240da2e3ebdSchin 			break;
241da2e3ebdSchin 		}
242da2e3ebdSchin 		c--;
243da2e3ebdSchin 		c <<=2;
244da2e3ebdSchin 		if (*cp=='2')
245da2e3ebdSchin 			c |=1;
246da2e3ebdSchin 	skip:
247da2e3ebdSchin 		if (outptr >= outmax)
248da2e3ebdSchin 		{
249da2e3ebdSchin 			jp->outlist = newof(jp->outlist, int, 2 * nfield + 1, 0);
250da2e3ebdSchin 			outptr = jp->outlist + nfield;
251da2e3ebdSchin 			nfield *= 2;
252da2e3ebdSchin 			outmax = jp->outlist + nfield;
253da2e3ebdSchin 		}
254da2e3ebdSchin 		*outptr++ = c;
255da2e3ebdSchin 		cp = str;
256da2e3ebdSchin 	}
257da2e3ebdSchin 	/* need to accept obsolescent command syntax */
258da2e3ebdSchin 	while (1)
259da2e3ebdSchin 	{
260da2e3ebdSchin 		if (!(cp= *argv) || cp[1]!='.' || (*cp!='1' && *cp!='2'))
261da2e3ebdSchin 		{
262da2e3ebdSchin 			if (*cp=='0' && cp[1]==0)
263da2e3ebdSchin 			{
264da2e3ebdSchin 				c = JOINFIELD;
265da2e3ebdSchin 				goto skip2;
266da2e3ebdSchin 			}
267da2e3ebdSchin 			break;
268da2e3ebdSchin 		}
269da2e3ebdSchin 		str = (char*)cp;
270da2e3ebdSchin 		c = strtol(cp+2, &str,10);
271da2e3ebdSchin 		if (*str || --c<0)
272da2e3ebdSchin 			break;
273da2e3ebdSchin 		argv++;
274da2e3ebdSchin 		c <<= 2;
275da2e3ebdSchin 		if (*cp=='2')
276da2e3ebdSchin 			c |=1;
277da2e3ebdSchin 	skip2:
278da2e3ebdSchin 		if (outptr >= outmax)
279da2e3ebdSchin 		{
280da2e3ebdSchin 			jp->outlist = newof(jp->outlist, int, 2 * nfield + 1, 0);
281da2e3ebdSchin 			outptr = jp->outlist + nfield;
282da2e3ebdSchin 			nfield *= 2;
283da2e3ebdSchin 			outmax = jp->outlist + nfield;
284da2e3ebdSchin 		}
285da2e3ebdSchin 		*outptr++ = c;
286da2e3ebdSchin 	}
287da2e3ebdSchin 	*outptr = -1;
288da2e3ebdSchin 	return argv-arglist;
289da2e3ebdSchin }
290da2e3ebdSchin 
291da2e3ebdSchin /*
292da2e3ebdSchin  * read in a record from file <index> and split into fields
293da2e3ebdSchin  */
294da2e3ebdSchin static unsigned char*
getrec(Join_t * jp,int index,int discard)295da2e3ebdSchin getrec(Join_t* jp, int index, int discard)
296da2e3ebdSchin {
297da2e3ebdSchin 	register unsigned char*	sp = jp->state;
298da2e3ebdSchin 	register File_t*	fp = &jp->file[index];
299*3e14f97fSRoger A. Faulkner 	register Field_t*	field = fp->fields;
300*3e14f97fSRoger A. Faulkner 	register Field_t*	fieldmax = field + fp->maxfields;
301da2e3ebdSchin 	register char*		cp;
302*3e14f97fSRoger A. Faulkner 	register int		n;
303*3e14f97fSRoger A. Faulkner 	char*			tp;
304da2e3ebdSchin 
3057c2fbfb3SApril Chin 	if (sh_checksig(jp->context))
306da2e3ebdSchin 		return 0;
307da2e3ebdSchin 	if (discard && fp->discard)
308da2e3ebdSchin 		sfraise(fp->iop, SFSK_DISCARD, NiL);
309da2e3ebdSchin 	fp->spaces = 0;
310da2e3ebdSchin 	fp->hit = 0;
311da2e3ebdSchin 	if (!(cp = sfgetr(fp->iop, '\n', 0)))
312da2e3ebdSchin 	{
313da2e3ebdSchin 		jp->outmode &= ~(1<<index);
314da2e3ebdSchin 		return 0;
315da2e3ebdSchin 	}
316da2e3ebdSchin 	fp->recptr = cp;
317da2e3ebdSchin 	fp->reclen = sfvalue(fp->iop);
318da2e3ebdSchin 	if (jp->delim == '\n')	/* handle new-line delimiter specially */
319da2e3ebdSchin 	{
320*3e14f97fSRoger A. Faulkner 		field->beg = cp;
321da2e3ebdSchin 		cp += fp->reclen;
322*3e14f97fSRoger A. Faulkner 		field->end = cp - 1;
323*3e14f97fSRoger A. Faulkner 		field++;
324da2e3ebdSchin 	}
325*3e14f97fSRoger A. Faulkner 	else
326*3e14f97fSRoger A. Faulkner 		do /* separate into fields */
327da2e3ebdSchin 		{
328*3e14f97fSRoger A. Faulkner 			if (field >= fieldmax)
329da2e3ebdSchin 			{
330da2e3ebdSchin 				n = 2 * fp->maxfields;
331*3e14f97fSRoger A. Faulkner 				fp->fields = newof(fp->fields, Field_t, n + 1, 0);
332*3e14f97fSRoger A. Faulkner 				field = fp->fields + fp->maxfields;
333da2e3ebdSchin 				fp->maxfields = n;
334*3e14f97fSRoger A. Faulkner 				fieldmax = fp->fields + n;
335da2e3ebdSchin 			}
336*3e14f97fSRoger A. Faulkner 			field->beg = cp;
337*3e14f97fSRoger A. Faulkner 			if (jp->delim == -1)
338da2e3ebdSchin 			{
339*3e14f97fSRoger A. Faulkner 				switch (sp[*(unsigned char*)cp])
340*3e14f97fSRoger A. Faulkner 				{
341*3e14f97fSRoger A. Faulkner 				case S_SPACE:
342*3e14f97fSRoger A. Faulkner 					cp++;
343*3e14f97fSRoger A. Faulkner 					break;
344*3e14f97fSRoger A. Faulkner 				case S_WIDE:
345*3e14f97fSRoger A. Faulkner 					tp = cp;
346*3e14f97fSRoger A. Faulkner 					if (iswspace(mbchar(tp)))
347*3e14f97fSRoger A. Faulkner 					{
348*3e14f97fSRoger A. Faulkner 						cp = tp;
349*3e14f97fSRoger A. Faulkner 						break;
350*3e14f97fSRoger A. Faulkner 					}
351*3e14f97fSRoger A. Faulkner 					/*FALLTHROUGH*/
352*3e14f97fSRoger A. Faulkner 				default:
353*3e14f97fSRoger A. Faulkner 					goto next;
354*3e14f97fSRoger A. Faulkner 				}
355da2e3ebdSchin 				fp->spaces = 1;
356*3e14f97fSRoger A. Faulkner 				if (jp->mb)
357*3e14f97fSRoger A. Faulkner 					for (;;)
358*3e14f97fSRoger A. Faulkner 					{
359*3e14f97fSRoger A. Faulkner 						switch (sp[*(unsigned char*)cp++])
360*3e14f97fSRoger A. Faulkner 						{
361*3e14f97fSRoger A. Faulkner 						case S_SPACE:
362*3e14f97fSRoger A. Faulkner 							continue;
363*3e14f97fSRoger A. Faulkner 						case S_WIDE:
364*3e14f97fSRoger A. Faulkner 							tp = cp - 1;
365*3e14f97fSRoger A. Faulkner 							if (iswspace(mbchar(tp)))
366*3e14f97fSRoger A. Faulkner 							{
367*3e14f97fSRoger A. Faulkner 								cp = tp;
368*3e14f97fSRoger A. Faulkner 								continue;
369*3e14f97fSRoger A. Faulkner 							}
370*3e14f97fSRoger A. Faulkner 							break;
371*3e14f97fSRoger A. Faulkner 						}
372*3e14f97fSRoger A. Faulkner 						break;
373*3e14f97fSRoger A. Faulkner 					}
374*3e14f97fSRoger A. Faulkner 				else
375da2e3ebdSchin 					while (sp[*(unsigned char*)cp++]==S_SPACE);
376da2e3ebdSchin 				cp--;
377da2e3ebdSchin 			}
378*3e14f97fSRoger A. Faulkner 		next:
379*3e14f97fSRoger A. Faulkner 			if (jp->mb)
380*3e14f97fSRoger A. Faulkner 			{
381*3e14f97fSRoger A. Faulkner 				for (;;)
382*3e14f97fSRoger A. Faulkner 				{
383*3e14f97fSRoger A. Faulkner 					tp = cp;
384*3e14f97fSRoger A. Faulkner 					switch (n = sp[*(unsigned char*)cp++])
385*3e14f97fSRoger A. Faulkner 					{
386*3e14f97fSRoger A. Faulkner 					case 0:
387*3e14f97fSRoger A. Faulkner 						continue;
388*3e14f97fSRoger A. Faulkner 					case S_WIDE:
389*3e14f97fSRoger A. Faulkner 						cp--;
390*3e14f97fSRoger A. Faulkner 						n = mbchar(cp);
391*3e14f97fSRoger A. Faulkner 						if (n == jp->delim)
392*3e14f97fSRoger A. Faulkner 						{
393*3e14f97fSRoger A. Faulkner 							n = S_DELIM;
394*3e14f97fSRoger A. Faulkner 							break;
395da2e3ebdSchin 						}
396*3e14f97fSRoger A. Faulkner 						if (jp->delim == -1 && iswspace(n))
397*3e14f97fSRoger A. Faulkner 						{
398*3e14f97fSRoger A. Faulkner 							n = S_SPACE;
399*3e14f97fSRoger A. Faulkner 							break;
400*3e14f97fSRoger A. Faulkner 						}
401*3e14f97fSRoger A. Faulkner 						continue;
402*3e14f97fSRoger A. Faulkner 					}
403*3e14f97fSRoger A. Faulkner 					break;
404*3e14f97fSRoger A. Faulkner 				}
405*3e14f97fSRoger A. Faulkner 				field->end = tp;
406*3e14f97fSRoger A. Faulkner 			}
407*3e14f97fSRoger A. Faulkner 			else
408*3e14f97fSRoger A. Faulkner 			{
409*3e14f97fSRoger A. Faulkner 				while (!(n = sp[*(unsigned char*)cp++]));
410*3e14f97fSRoger A. Faulkner 				field->end = cp - 1;
411*3e14f97fSRoger A. Faulkner 			}
412*3e14f97fSRoger A. Faulkner 			field++;
413*3e14f97fSRoger A. Faulkner 		} while (n != S_NL);
414*3e14f97fSRoger A. Faulkner 	fp->nfields = field - fp->fields;
415da2e3ebdSchin 	if ((n = fp->field) < fp->nfields)
416da2e3ebdSchin 	{
417*3e14f97fSRoger A. Faulkner 		cp = fp->fields[n].beg;
418da2e3ebdSchin 		/* eliminate leading spaces */
419da2e3ebdSchin 		if (fp->spaces)
420da2e3ebdSchin 		{
421*3e14f97fSRoger A. Faulkner 			if (jp->mb)
422*3e14f97fSRoger A. Faulkner 				for (;;)
423*3e14f97fSRoger A. Faulkner 				{
424*3e14f97fSRoger A. Faulkner 					switch (sp[*(unsigned char*)cp++])
425*3e14f97fSRoger A. Faulkner 					{
426*3e14f97fSRoger A. Faulkner 					case S_SPACE:
427*3e14f97fSRoger A. Faulkner 						continue;
428*3e14f97fSRoger A. Faulkner 					case S_WIDE:
429*3e14f97fSRoger A. Faulkner 						tp = cp - 1;
430*3e14f97fSRoger A. Faulkner 						if (iswspace(mbchar(tp)))
431*3e14f97fSRoger A. Faulkner 						{
432*3e14f97fSRoger A. Faulkner 							cp = tp;
433*3e14f97fSRoger A. Faulkner 							continue;
434*3e14f97fSRoger A. Faulkner 						}
435*3e14f97fSRoger A. Faulkner 						break;
436*3e14f97fSRoger A. Faulkner 					}
437*3e14f97fSRoger A. Faulkner 					break;
438*3e14f97fSRoger A. Faulkner 				}
439*3e14f97fSRoger A. Faulkner 			else
440da2e3ebdSchin 				while (sp[*(unsigned char*)cp++]==S_SPACE);
441da2e3ebdSchin 			cp--;
442da2e3ebdSchin 		}
443*3e14f97fSRoger A. Faulkner 		fp->fieldlen = fp->fields[n].end - cp;
444da2e3ebdSchin 		return (unsigned char*)cp;
445da2e3ebdSchin 	}
446da2e3ebdSchin 	fp->fieldlen = 0;
447da2e3ebdSchin 	return (unsigned char*)"";
448da2e3ebdSchin }
449da2e3ebdSchin 
450*3e14f97fSRoger A. Faulkner static unsigned char*
_trace_getrec(Join_t * jp,int index,int discard)451*3e14f97fSRoger A. Faulkner _trace_getrec(Join_t* jp, int index, int discard)
452*3e14f97fSRoger A. Faulkner {
453*3e14f97fSRoger A. Faulkner 	unsigned char*	r;
454*3e14f97fSRoger A. Faulkner 
455*3e14f97fSRoger A. Faulkner 	r = getrec(jp, index, discard);
456*3e14f97fSRoger A. Faulkner 	return r;
457*3e14f97fSRoger A. Faulkner }
458*3e14f97fSRoger A. Faulkner #define getrec	_trace_getrec
459*3e14f97fSRoger A. Faulkner 
460da2e3ebdSchin #if DEBUG_TRACE
461da2e3ebdSchin static unsigned char* u1,u2,u3;
462da2e3ebdSchin #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)
463da2e3ebdSchin #endif
464da2e3ebdSchin 
465da2e3ebdSchin /*
466da2e3ebdSchin  * print field <n> from file <index>
467da2e3ebdSchin  */
468da2e3ebdSchin static int
outfield(Join_t * jp,int index,register int n,int last)469da2e3ebdSchin outfield(Join_t* jp, int index, register int n, int last)
470da2e3ebdSchin {
471da2e3ebdSchin 	register File_t*	fp = &jp->file[index];
472da2e3ebdSchin 	register char*		cp;
473da2e3ebdSchin 	register char*		cpmax;
474da2e3ebdSchin 	register int		size;
475da2e3ebdSchin 	register Sfio_t*	iop = jp->outfile;
476*3e14f97fSRoger A. Faulkner 	char*			tp;
477da2e3ebdSchin 
478da2e3ebdSchin 	if (n < fp->nfields)
479da2e3ebdSchin 	{
480*3e14f97fSRoger A. Faulkner 		cp = fp->fields[n].beg;
481*3e14f97fSRoger A. Faulkner 		cpmax = fp->fields[n].end + 1;
482da2e3ebdSchin 	}
483da2e3ebdSchin 	else
484da2e3ebdSchin 		cp = 0;
485*3e14f97fSRoger A. Faulkner 	if ((n = jp->delim) == -1)
486da2e3ebdSchin 	{
48734f9b3eeSRoland Mainz 		if (cp && fp->spaces)
488da2e3ebdSchin 		{
489*3e14f97fSRoger A. Faulkner 			register unsigned char*	sp = jp->state;
490*3e14f97fSRoger A. Faulkner 
491da2e3ebdSchin 			/*eliminate leading spaces */
492*3e14f97fSRoger A. Faulkner 			if (jp->mb)
493*3e14f97fSRoger A. Faulkner 				for (;;)
494*3e14f97fSRoger A. Faulkner 				{
495*3e14f97fSRoger A. Faulkner 					switch (sp[*(unsigned char*)cp++])
496*3e14f97fSRoger A. Faulkner 					{
497*3e14f97fSRoger A. Faulkner 					case S_SPACE:
498*3e14f97fSRoger A. Faulkner 						continue;
499*3e14f97fSRoger A. Faulkner 					case S_WIDE:
500*3e14f97fSRoger A. Faulkner 						tp = cp - 1;
501*3e14f97fSRoger A. Faulkner 						if (iswspace(mbchar(tp)))
502*3e14f97fSRoger A. Faulkner 						{
503*3e14f97fSRoger A. Faulkner 							cp = tp;
504*3e14f97fSRoger A. Faulkner 							continue;
505*3e14f97fSRoger A. Faulkner 						}
506*3e14f97fSRoger A. Faulkner 						break;
507*3e14f97fSRoger A. Faulkner 					}
508*3e14f97fSRoger A. Faulkner 					break;
509*3e14f97fSRoger A. Faulkner 				}
510*3e14f97fSRoger A. Faulkner 			else
511*3e14f97fSRoger A. Faulkner 				while (sp[*(unsigned char*)cp++]==S_SPACE);
512da2e3ebdSchin 			cp--;
513da2e3ebdSchin 		}
514da2e3ebdSchin 		n = ' ';
515da2e3ebdSchin 	}
516*3e14f97fSRoger A. Faulkner 	else if (jp->delimstr)
517*3e14f97fSRoger A. Faulkner 		n = -1;
518da2e3ebdSchin 	if (last)
519da2e3ebdSchin 		n = '\n';
520da2e3ebdSchin 	if (cp)
521da2e3ebdSchin 		size = cpmax - cp;
522da2e3ebdSchin 	else
523da2e3ebdSchin 		size = 0;
524*3e14f97fSRoger A. Faulkner 	if (n == -1)
525*3e14f97fSRoger A. Faulkner 	{
52634f9b3eeSRoland Mainz 		if (size<=1)
527da2e3ebdSchin 		{
528*3e14f97fSRoger A. Faulkner 			if (jp->nullfield && sfputr(iop, jp->nullfield, -1) < 0)
529*3e14f97fSRoger A. Faulkner 				return -1;
530*3e14f97fSRoger A. Faulkner 		}
531*3e14f97fSRoger A. Faulkner 		else if (sfwrite(iop, cp, size) < 0)
532*3e14f97fSRoger A. Faulkner 			return -1;
533*3e14f97fSRoger A. Faulkner 		if (sfwrite(iop, jp->delimstr, jp->delimlen) < 0)
534*3e14f97fSRoger A. Faulkner 			return -1;
535*3e14f97fSRoger A. Faulkner 	}
536*3e14f97fSRoger A. Faulkner 	else if (size <= 1)
537*3e14f97fSRoger A. Faulkner 	{
538da2e3ebdSchin 		if (!jp->nullfield)
539da2e3ebdSchin 			sfputc(iop, n);
540da2e3ebdSchin 		else if (sfputr(iop, jp->nullfield, n) < 0)
541da2e3ebdSchin 			return -1;
542da2e3ebdSchin 	}
543da2e3ebdSchin 	else
544da2e3ebdSchin 	{
545da2e3ebdSchin 		last = cp[size-1];
546da2e3ebdSchin 		cp[size-1] = n;
547da2e3ebdSchin 		if (sfwrite(iop, cp, size) < 0)
548da2e3ebdSchin 			return -1;
549da2e3ebdSchin 		cp[size-1] = last;
550da2e3ebdSchin 	}
551da2e3ebdSchin 	return 0;
552da2e3ebdSchin }
553da2e3ebdSchin 
554da2e3ebdSchin #if DEBUG_TRACE
555da2e3ebdSchin static int i1,i2,i3;
556da2e3ebdSchin #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))
557da2e3ebdSchin #endif
558da2e3ebdSchin 
559da2e3ebdSchin static int
outrec(register Join_t * jp,int mode)560da2e3ebdSchin outrec(register Join_t* jp, int mode)
561da2e3ebdSchin {
562da2e3ebdSchin 	register File_t*	fp;
563da2e3ebdSchin 	register int		i;
564da2e3ebdSchin 	register int		j;
565da2e3ebdSchin 	register int		k;
566da2e3ebdSchin 	register int		n;
567da2e3ebdSchin 	int*			out;
568da2e3ebdSchin 
569da2e3ebdSchin 	if (mode < 0 && jp->file[0].hit++)
570da2e3ebdSchin 		return 0;
571da2e3ebdSchin 	if (mode > 0 && jp->file[1].hit++)
572da2e3ebdSchin 		return 0;
573da2e3ebdSchin 	if (out = jp->outlist)
574da2e3ebdSchin 	{
575da2e3ebdSchin 		while ((n = *out++) >= 0)
576da2e3ebdSchin 		{
577da2e3ebdSchin 			if (n == JOINFIELD)
578da2e3ebdSchin 			{
579da2e3ebdSchin 				i = mode >= 0;
580da2e3ebdSchin 				j = jp->file[i].field;
581da2e3ebdSchin 			}
582da2e3ebdSchin 			else
583da2e3ebdSchin 			{
584da2e3ebdSchin 				i = n & 1;
585da2e3ebdSchin 				j = (mode<0 && i || mode>0 && !i) ?
586da2e3ebdSchin 					jp->file[i].nfields :
587da2e3ebdSchin 					n >> 2;
588da2e3ebdSchin 			}
589da2e3ebdSchin 			if (outfield(jp, i, j, *out < 0) < 0)
590da2e3ebdSchin 				return -1;
591da2e3ebdSchin 		}
592da2e3ebdSchin 		return 0;
593da2e3ebdSchin 	}
594da2e3ebdSchin 	k = jp->file[0].nfields;
595da2e3ebdSchin 	if (mode >= 0)
596da2e3ebdSchin 		k += jp->file[1].nfields - 1;
597da2e3ebdSchin 	for (i=0; i<2; i++)
598da2e3ebdSchin 	{
599da2e3ebdSchin 		fp = &jp->file[i];
600da2e3ebdSchin 		if (mode>0 && i==0)
601da2e3ebdSchin 		{
602da2e3ebdSchin 			k -= (fp->nfields - 1);
603da2e3ebdSchin 			continue;
604da2e3ebdSchin 		}
605da2e3ebdSchin 		n = fp->field;
606da2e3ebdSchin 		if (mode||i==0)
607da2e3ebdSchin 		{
608da2e3ebdSchin 			/* output join field first */
609da2e3ebdSchin 			if (outfield(jp,i,n,!--k) < 0)
610da2e3ebdSchin 				return -1;
611da2e3ebdSchin 			if (!k)
612da2e3ebdSchin 				return 0;
613da2e3ebdSchin 			for (j=0; j<n; j++)
614da2e3ebdSchin 			{
615da2e3ebdSchin 				if (outfield(jp,i,j,!--k) < 0)
616da2e3ebdSchin 					return -1;
617da2e3ebdSchin 				if (!k)
618da2e3ebdSchin 					return 0;
619da2e3ebdSchin 			}
620da2e3ebdSchin 			j = n + 1;
621da2e3ebdSchin 		}
622da2e3ebdSchin 		else
623da2e3ebdSchin 			j = 0;
624da2e3ebdSchin 		for (;j<fp->nfields; j++)
625da2e3ebdSchin 		{
626da2e3ebdSchin 			if (j!=n && outfield(jp,i,j,!--k) < 0)
627da2e3ebdSchin 				return -1;
628da2e3ebdSchin 			if (!k)
629da2e3ebdSchin 				return 0;
630da2e3ebdSchin 		}
631da2e3ebdSchin 	}
632da2e3ebdSchin 	return 0;
633da2e3ebdSchin }
634da2e3ebdSchin 
635da2e3ebdSchin #if DEBUG_TRACE
636da2e3ebdSchin #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))
637da2e3ebdSchin #endif
638da2e3ebdSchin 
639da2e3ebdSchin static int
join(Join_t * jp)640da2e3ebdSchin join(Join_t* jp)
641da2e3ebdSchin {
642da2e3ebdSchin 	register unsigned char*	cp1;
643da2e3ebdSchin 	register unsigned char*	cp2;
644da2e3ebdSchin 	register int		n1;
645da2e3ebdSchin 	register int		n2;
646da2e3ebdSchin 	register int		n;
647da2e3ebdSchin 	register int		cmp;
648da2e3ebdSchin 	register int		same;
649da2e3ebdSchin 	int			o2;
650da2e3ebdSchin 	Sfoff_t			lo = -1;
651da2e3ebdSchin 	Sfoff_t			hi = -1;
652da2e3ebdSchin 
653da2e3ebdSchin 	if ((cp1 = getrec(jp, 0, 0)) && (cp2 = getrec(jp, 1, 0)) || (cp2 = 0))
654da2e3ebdSchin 	{
655da2e3ebdSchin 		n1 = jp->file[0].fieldlen;
656da2e3ebdSchin 		n2 = jp->file[1].fieldlen;
657da2e3ebdSchin 		same = 0;
658da2e3ebdSchin 		for (;;)
659da2e3ebdSchin 		{
660da2e3ebdSchin 			n = n1 < n2 ? n1 : n2;
661da2e3ebdSchin #if DEBUG_TRACE
662da2e3ebdSchin 			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)))
663da2e3ebdSchin 				cmp = n1 - n2;
664da2e3ebdSchin sfprintf(sfstdout, "[C#%d:%d(%c-%c),%d,%lld,%lld%s]", __LINE__, cmp, *cp1, *cp2, same, lo, hi, (jp->outmode & C_COMMON) ? ",COMMON" : "");
665da2e3ebdSchin 			if (!cmp)
666da2e3ebdSchin #else
667da2e3ebdSchin 			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))
668da2e3ebdSchin #endif
669da2e3ebdSchin 			{
670da2e3ebdSchin 				if (!(jp->outmode & C_COMMON))
671da2e3ebdSchin 				{
672da2e3ebdSchin 					if (cp1 = getrec(jp, 0, 1))
673da2e3ebdSchin 					{
674da2e3ebdSchin 						n1 = jp->file[0].fieldlen;
675da2e3ebdSchin 						same = 1;
676da2e3ebdSchin 						continue;
677da2e3ebdSchin 					}
678da2e3ebdSchin 					if ((jp->ooutmode & (C_FILE1|C_FILE2)) != C_FILE2)
679da2e3ebdSchin 						break;
680da2e3ebdSchin 					if (sfseek(jp->file[0].iop, (Sfoff_t)-jp->file[0].reclen, SEEK_CUR) < 0 || !(cp1 = getrec(jp, 0, 0)))
681da2e3ebdSchin 					{
682da2e3ebdSchin 						error(ERROR_SYSTEM|2, "%s: seek error", jp->file[0].name);
683da2e3ebdSchin 						return -1;
684da2e3ebdSchin 					}
685da2e3ebdSchin 				}
686da2e3ebdSchin 				else if (outrec(jp, 0) < 0)
687da2e3ebdSchin 					return -1;
688da2e3ebdSchin 				else if (lo < 0 && (jp->outmode & C_COMMON))
689da2e3ebdSchin 				{
690da2e3ebdSchin 					if ((lo = sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR)) < 0)
691da2e3ebdSchin 					{
692da2e3ebdSchin 						error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
693da2e3ebdSchin 						return -1;
694da2e3ebdSchin 					}
695da2e3ebdSchin 					lo -= jp->file[1].reclen;
696da2e3ebdSchin 				}
697da2e3ebdSchin 				if (cp2 = getrec(jp, 1, lo < 0))
698da2e3ebdSchin 				{
699da2e3ebdSchin 					n2 = jp->file[1].fieldlen;
700da2e3ebdSchin 					continue;
701da2e3ebdSchin 				}
702da2e3ebdSchin #if DEBUG_TRACE
703da2e3ebdSchin sfprintf(sfstdout, "[2#%d:0,%lld,%lld]", __LINE__, lo, hi);
704da2e3ebdSchin #endif
705da2e3ebdSchin 			}
706da2e3ebdSchin 			else if (cmp > 0)
707da2e3ebdSchin 			{
708da2e3ebdSchin 				if (same)
709da2e3ebdSchin 				{
710da2e3ebdSchin 					same = 0;
711da2e3ebdSchin 				next:
712da2e3ebdSchin 					if (n2 > jp->samesize)
713da2e3ebdSchin 					{
714da2e3ebdSchin 						jp->samesize = roundof(n2, 16);
715da2e3ebdSchin 						if (!(jp->same = newof(jp->same, char, jp->samesize, 0)))
716da2e3ebdSchin 						{
717da2e3ebdSchin 							error(ERROR_SYSTEM|2, "out of space");
718da2e3ebdSchin 							return -1;
719da2e3ebdSchin 						}
720da2e3ebdSchin 					}
721da2e3ebdSchin 					memcpy(jp->same, cp2, o2 = n2);
722da2e3ebdSchin 					if (!(cp2 = getrec(jp, 1, 0)))
723da2e3ebdSchin 						break;
724da2e3ebdSchin 					n2 = jp->file[1].fieldlen;
725da2e3ebdSchin 					if (n2 == o2 && *cp2 == *jp->same && !memcmp(cp2, jp->same, n2))
726da2e3ebdSchin 						goto next;
727da2e3ebdSchin 					continue;
728da2e3ebdSchin 				}
729da2e3ebdSchin 				if (hi >= 0)
730da2e3ebdSchin 				{
731da2e3ebdSchin 					if (sfseek(jp->file[1].iop, hi, SEEK_SET) != hi)
732da2e3ebdSchin 					{
733da2e3ebdSchin 						error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
734da2e3ebdSchin 						return -1;
735da2e3ebdSchin 					}
736da2e3ebdSchin 					hi = -1;
737da2e3ebdSchin 				}
738da2e3ebdSchin 				else if ((jp->outmode & C_FILE2) && outrec(jp, 1) < 0)
739da2e3ebdSchin 					return -1;
740da2e3ebdSchin 				lo = -1;
741da2e3ebdSchin 				if (cp2 = getrec(jp, 1, 1))
742da2e3ebdSchin 				{
743da2e3ebdSchin 					n2 = jp->file[1].fieldlen;
744da2e3ebdSchin 					continue;
745da2e3ebdSchin 				}
746da2e3ebdSchin #if DEBUG_TRACE
747da2e3ebdSchin sfprintf(sfstdout, "[2#%d:0,%lld,%lld]", __LINE__, lo, hi);
748da2e3ebdSchin #endif
749da2e3ebdSchin 			}
750da2e3ebdSchin 			else if (same)
751da2e3ebdSchin 			{
752da2e3ebdSchin 				same = 0;
753da2e3ebdSchin 				if (!(cp1 = getrec(jp, 0, 0)))
754da2e3ebdSchin 					break;
755da2e3ebdSchin 				n1 = jp->file[0].fieldlen;
756da2e3ebdSchin 				continue;
757da2e3ebdSchin 			}
758da2e3ebdSchin 			if (lo >= 0)
759da2e3ebdSchin 			{
760da2e3ebdSchin 				if ((hi = sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR)) < 0 ||
761da2e3ebdSchin 				    (hi -= jp->file[1].reclen) < 0 ||
762da2e3ebdSchin 				    sfseek(jp->file[1].iop, lo, SEEK_SET) != lo ||
763da2e3ebdSchin 				    !(cp2 = getrec(jp, 1, 0)))
764da2e3ebdSchin 				{
765da2e3ebdSchin 					error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
766da2e3ebdSchin 					return -1;
767da2e3ebdSchin 				}
768da2e3ebdSchin 				n2 = jp->file[1].fieldlen;
769da2e3ebdSchin 				lo = -1;
770da2e3ebdSchin 				if (jp->file[1].discard)
771da2e3ebdSchin 					sfseek(jp->file[1].iop, (Sfoff_t)-1, SEEK_SET);
772da2e3ebdSchin 			}
773da2e3ebdSchin 			else if (!cp2)
774da2e3ebdSchin 				break;
775da2e3ebdSchin 			else if ((jp->outmode & C_FILE1) && outrec(jp, -1) < 0)
776da2e3ebdSchin 				return -1;
777da2e3ebdSchin 			if (!(cp1 = getrec(jp, 0, 1)))
778da2e3ebdSchin 				break;
779da2e3ebdSchin 			n1 = jp->file[0].fieldlen;
780da2e3ebdSchin 		}
781da2e3ebdSchin 	}
782da2e3ebdSchin #if DEBUG_TRACE
783da2e3ebdSchin sfprintf(sfstdout, "[X#%d:?,%p,%p,%d%,%d,%d%s]", __LINE__, cp1, cp2, cmp, lo, hi, (jp->outmode & C_COMMON) ? ",COMMON" : "");
784da2e3ebdSchin #endif
785da2e3ebdSchin 	if (cp2)
786da2e3ebdSchin 	{
787da2e3ebdSchin 		if (hi >= 0 &&
788da2e3ebdSchin 		    sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR) < hi &&
789da2e3ebdSchin 		    sfseek(jp->file[1].iop, hi, SEEK_SET) != hi)
790da2e3ebdSchin 		{
791da2e3ebdSchin 			error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
792da2e3ebdSchin 			return -1;
793da2e3ebdSchin 		}
794da2e3ebdSchin #if DEBUG_TRACE
795da2e3ebdSchin sfprintf(sfstdout, "[O#%d:%02o:%02o]", __LINE__, jp->ooutmode, jp->outmode);
796da2e3ebdSchin #endif
797da2e3ebdSchin 		cp1 = (!cp1 && cmp && hi < 0 && !jp->file[1].hit && ((jp->ooutmode ^ C_ALL) <= 1 || jp->outmode == 2)) ? cp2 : getrec(jp, 1, 0);
798da2e3ebdSchin 		cmp = 1;
799da2e3ebdSchin 		n = 1;
800da2e3ebdSchin 	}
801da2e3ebdSchin 	else
802da2e3ebdSchin 	{
803da2e3ebdSchin 		cmp = -1;
804da2e3ebdSchin 		n = 0;
805da2e3ebdSchin 	}
806da2e3ebdSchin #if DEBUG_TRACE
807da2e3ebdSchin 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" : "");
808da2e3ebdSchin #endif
809da2e3ebdSchin 	if (!cp1 || !(jp->outmode & (1<<n)))
810da2e3ebdSchin 	{
811da2e3ebdSchin 		if (cp1 && jp->file[n].iop == sfstdin)
812da2e3ebdSchin 			sfseek(sfstdin, (Sfoff_t)0, SEEK_END);
813da2e3ebdSchin 		return 0;
814da2e3ebdSchin 	}
815da2e3ebdSchin 	if (outrec(jp, cmp) < 0)
816da2e3ebdSchin 		return -1;
817da2e3ebdSchin 	do
818da2e3ebdSchin 	{
819da2e3ebdSchin 		if (!getrec(jp, n, 1))
820da2e3ebdSchin 			return 0;
821da2e3ebdSchin 	} while (outrec(jp, cmp) >= 0);
822da2e3ebdSchin 	return -1;
823da2e3ebdSchin }
824da2e3ebdSchin 
825da2e3ebdSchin int
b_join(int argc,char ** argv,void * context)826da2e3ebdSchin b_join(int argc, char** argv, void* context)
827da2e3ebdSchin {
828da2e3ebdSchin 	register int		n;
829da2e3ebdSchin 	register char*		cp;
830da2e3ebdSchin 	register Join_t*	jp;
831da2e3ebdSchin 	char*			e;
832da2e3ebdSchin 
833da2e3ebdSchin #if !DEBUG_TRACE
834da2e3ebdSchin 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
835da2e3ebdSchin #endif
836da2e3ebdSchin 	if (!(jp = init()))
837da2e3ebdSchin 		error(ERROR_system(1),"out of space");
8387c2fbfb3SApril Chin 	jp->context = context;
839da2e3ebdSchin 	for (;;)
840da2e3ebdSchin 	{
841da2e3ebdSchin 		switch (n = optget(argv, usage))
842da2e3ebdSchin 		{
843da2e3ebdSchin 		case 0:
844da2e3ebdSchin 			break;
845da2e3ebdSchin  		case 'j':
846da2e3ebdSchin 			/*
847da2e3ebdSchin 			 * check for obsolete "-j1 field" and "-j2 field"
848da2e3ebdSchin 			 */
849da2e3ebdSchin 
850da2e3ebdSchin 			if (opt_info.offset == 0)
851da2e3ebdSchin 			{
852da2e3ebdSchin 				cp = argv[opt_info.index - 1];
853da2e3ebdSchin 				for (n = strlen(cp) - 1; n > 0 && cp[n] != 'j'; n--);
854da2e3ebdSchin 				n = cp[n] == 'j';
855da2e3ebdSchin 			}
856da2e3ebdSchin 			else
857da2e3ebdSchin 				n = 0;
858da2e3ebdSchin 			if (n)
859da2e3ebdSchin 			{
860da2e3ebdSchin 				if (opt_info.num!=1 && opt_info.num!=2)
861da2e3ebdSchin 					error(2,"-jfileno field: fileno must be 1 or 2");
862da2e3ebdSchin 				n = '0' + opt_info.num;
863da2e3ebdSchin 				if (!(cp = argv[opt_info.index]))
864da2e3ebdSchin 				{
865da2e3ebdSchin 					argc = 0;
866da2e3ebdSchin 					break;
867da2e3ebdSchin 				}
868da2e3ebdSchin 				opt_info.num = strtol(cp, &e, 10);
869da2e3ebdSchin 				if (*e)
870da2e3ebdSchin 				{
871da2e3ebdSchin 					argc = 0;
872da2e3ebdSchin 					break;
873da2e3ebdSchin 				}
874da2e3ebdSchin 				opt_info.index++;
875da2e3ebdSchin 			}
876da2e3ebdSchin 			else
877da2e3ebdSchin 			{
878da2e3ebdSchin 				jp->file[0].field = (int)(opt_info.num-1);
879da2e3ebdSchin 				n = '2';
880da2e3ebdSchin 			}
881da2e3ebdSchin 			/*FALLTHROUGH*/
882da2e3ebdSchin  		case '1':
883da2e3ebdSchin 		case '2':
884da2e3ebdSchin 			if (opt_info.num <=0)
885da2e3ebdSchin 				error(2,"field number must positive");
886da2e3ebdSchin 			jp->file[n-'1'].field = (int)(opt_info.num-1);
887da2e3ebdSchin 			continue;
888da2e3ebdSchin 		case 'v':
889da2e3ebdSchin 			jp->outmode &= ~C_COMMON;
890da2e3ebdSchin 			/*FALLTHROUGH*/
891da2e3ebdSchin 		case 'a':
892da2e3ebdSchin 			if (opt_info.num!=1 && opt_info.num!=2)
893da2e3ebdSchin 				error(2,"%s: file number must be 1 or 2", opt_info.name);
894da2e3ebdSchin 			jp->outmode |= 1<<(opt_info.num-1);
895da2e3ebdSchin 			continue;
896da2e3ebdSchin 		case 'e':
897da2e3ebdSchin 			jp->nullfield = opt_info.arg;
898da2e3ebdSchin 			continue;
899da2e3ebdSchin 		case 'o':
900da2e3ebdSchin 			/* need to accept obsolescent command syntax */
901da2e3ebdSchin 			n = getolist(jp, opt_info.arg, argv+opt_info.index);
902da2e3ebdSchin 			opt_info.index += n;
903da2e3ebdSchin 			continue;
904da2e3ebdSchin 		case 't':
905da2e3ebdSchin 			jp->state[' '] = jp->state['\t'] = 0;
906*3e14f97fSRoger A. Faulkner 			if (jp->mb)
907*3e14f97fSRoger A. Faulkner 			{
908*3e14f97fSRoger A. Faulkner 				cp = opt_info.arg;
909*3e14f97fSRoger A. Faulkner 				jp->delim = mbchar(cp);
910*3e14f97fSRoger A. Faulkner 				if ((n = cp - opt_info.arg) > 1)
911*3e14f97fSRoger A. Faulkner 				{
912*3e14f97fSRoger A. Faulkner 					jp->delimlen = n;
913*3e14f97fSRoger A. Faulkner 					jp->delimstr = opt_info.arg;
914*3e14f97fSRoger A. Faulkner 					continue;
915*3e14f97fSRoger A. Faulkner 				}
916*3e14f97fSRoger A. Faulkner 			}
917da2e3ebdSchin 			n = *(unsigned char*)opt_info.arg;
918da2e3ebdSchin 			jp->state[n] = S_DELIM;
919da2e3ebdSchin 			jp->delim = n;
920da2e3ebdSchin 			continue;
921da2e3ebdSchin 		case 'i':
922da2e3ebdSchin 			jp->ignorecase = !opt_info.num;
923da2e3ebdSchin 			continue;
924da2e3ebdSchin 		case 'B':
925da2e3ebdSchin 			jp->buffered = !opt_info.num;
926da2e3ebdSchin 			continue;
927da2e3ebdSchin 		case ':':
928da2e3ebdSchin 			error(2, "%s", opt_info.arg);
929da2e3ebdSchin 			break;
930da2e3ebdSchin 		case '?':
931da2e3ebdSchin 			done(jp);
932da2e3ebdSchin 			error(ERROR_usage(2), "%s", opt_info.arg);
933da2e3ebdSchin 			break;
934da2e3ebdSchin 		}
935da2e3ebdSchin 		break;
936da2e3ebdSchin 	}
937da2e3ebdSchin 	argv += opt_info.index;
938da2e3ebdSchin 	argc -= opt_info.index;
939da2e3ebdSchin 	if (error_info.errors || argc!=2)
940da2e3ebdSchin 	{
941da2e3ebdSchin 		done(jp);
942da2e3ebdSchin 		error(ERROR_usage(2),"%s", optusage(NiL));
943da2e3ebdSchin 	}
944da2e3ebdSchin 	jp->ooutmode = jp->outmode;
945da2e3ebdSchin 	jp->file[0].name = cp = *argv++;
946da2e3ebdSchin 	if (streq(cp,"-"))
947da2e3ebdSchin 	{
948da2e3ebdSchin 		if (sfseek(sfstdin,(Sfoff_t)0,SEEK_CUR) < 0)
949da2e3ebdSchin 		{
950da2e3ebdSchin 			if (sfdcseekable(sfstdin))
951da2e3ebdSchin 				error(ERROR_warn(0),"%s: seek may fail",cp);
952da2e3ebdSchin 			else
953da2e3ebdSchin 				jp->file[0].discard = 1;
954da2e3ebdSchin 		}
955da2e3ebdSchin 		jp->file[0].iop = sfstdin;
956da2e3ebdSchin 	}
957da2e3ebdSchin 	else if (!(jp->file[0].iop = sfopen(NiL, cp, "r")))
958da2e3ebdSchin 	{
959da2e3ebdSchin 		done(jp);
960da2e3ebdSchin 		error(ERROR_system(1),"%s: cannot open",cp);
961da2e3ebdSchin 	}
962da2e3ebdSchin 	jp->file[1].name = cp = *argv;
963da2e3ebdSchin 	if (streq(cp,"-"))
964da2e3ebdSchin 	{
965da2e3ebdSchin 		if (sfseek(sfstdin,(Sfoff_t)0,SEEK_CUR) < 0)
966da2e3ebdSchin 		{
967da2e3ebdSchin 			if (sfdcseekable(sfstdin))
968da2e3ebdSchin 				error(ERROR_warn(0),"%s: seek may fail",cp);
969da2e3ebdSchin 			else
970da2e3ebdSchin 				jp->file[1].discard = 1;
971da2e3ebdSchin 		}
972da2e3ebdSchin 		jp->file[1].iop = sfstdin;
973da2e3ebdSchin 	}
974da2e3ebdSchin 	else if (!(jp->file[1].iop = sfopen(NiL, cp, "r")))
975da2e3ebdSchin 	{
976da2e3ebdSchin 		done(jp);
977da2e3ebdSchin 		error(ERROR_system(1),"%s: cannot open",cp);
978da2e3ebdSchin 	}
979da2e3ebdSchin 	if (jp->buffered)
980da2e3ebdSchin 	{
981da2e3ebdSchin 		sfsetbuf(jp->file[0].iop, jp->file[0].iop, SF_UNBOUND);
98234f9b3eeSRoland Mainz 		sfsetbuf(jp->file[1].iop, jp->file[1].iop, SF_UNBOUND);
983da2e3ebdSchin 	}
984da2e3ebdSchin 	jp->outfile = sfstdout;
985da2e3ebdSchin 	if (!jp->outlist)
986da2e3ebdSchin 		jp->nullfield = 0;
987da2e3ebdSchin 	if (join(jp) < 0)
988da2e3ebdSchin 	{
989da2e3ebdSchin 		done(jp);
990da2e3ebdSchin 		error(ERROR_system(1),"write error");
991da2e3ebdSchin 	}
992da2e3ebdSchin 	else if (jp->file[0].iop==sfstdin || jp->file[1].iop==sfstdin)
993da2e3ebdSchin 		sfseek(sfstdin,(Sfoff_t)0,SEEK_END);
994da2e3ebdSchin 	done(jp);
995da2e3ebdSchin 	return error_info.errors;
996da2e3ebdSchin }
997