xref: /plan9/sys/src/cmd/sort.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1bd389b36SDavid du Colombier #include	<u.h>
2bd389b36SDavid du Colombier #include	<libc.h>
3bd389b36SDavid du Colombier #include	<bio.h>
4bd389b36SDavid du Colombier 
5bd389b36SDavid du Colombier /*
6bd389b36SDavid du Colombier bugs:
7bd389b36SDavid du Colombier 	00/ff for end of file can conflict with 00/ff characters
8bd389b36SDavid du Colombier */
9bd389b36SDavid du Colombier 
10bd389b36SDavid du Colombier enum
11bd389b36SDavid du Colombier {
12bd389b36SDavid du Colombier 	Nline	= 100000,		/* max number of lines saved in memory */
13bd389b36SDavid du Colombier 	Nmerge	= 10,			/* max number of temporary files merged */
14bd389b36SDavid du Colombier 	Nfield	= 20,			/* max number of argument fields */
15bd389b36SDavid du Colombier 
16bd389b36SDavid du Colombier 	Bflag	= 1<<0,			/* flags per field */
17bd389b36SDavid du Colombier 	B1flag	= 1<<1,
18bd389b36SDavid du Colombier 
19bd389b36SDavid du Colombier 	Dflag	= 1<<2,
20bd389b36SDavid du Colombier 	Fflag	= 1<<3,
21bd389b36SDavid du Colombier 	Gflag	= 1<<4,
22bd389b36SDavid du Colombier 	Iflag	= 1<<5,
23bd389b36SDavid du Colombier 	Mflag	= 1<<6,
24bd389b36SDavid du Colombier 	Nflag	= 1<<7,
25bd389b36SDavid du Colombier 	Rflag	= 1<<8,
26bd389b36SDavid du Colombier 	Wflag	= 1<<9,
27bd389b36SDavid du Colombier 
28bd389b36SDavid du Colombier 	NSstart	= 0,			/* states for number to key decoding */
29bd389b36SDavid du Colombier 	NSsign,
30bd389b36SDavid du Colombier 	NSzero,
31bd389b36SDavid du Colombier 	NSdigit,
32bd389b36SDavid du Colombier 	NSpoint,
33bd389b36SDavid du Colombier 	NSfract,
34bd389b36SDavid du Colombier 	NSzerofract,
35bd389b36SDavid du Colombier 	NSexp,
36bd389b36SDavid du Colombier 	NSexpsign,
37bd389b36SDavid du Colombier 	NSexpdigit,
38bd389b36SDavid du Colombier };
39bd389b36SDavid du Colombier 
40bd389b36SDavid du Colombier typedef	struct	Line	Line;
41bd389b36SDavid du Colombier typedef	struct	Key	Key;
42bd389b36SDavid du Colombier typedef	struct	Merge	Merge;
43bd389b36SDavid du Colombier typedef	struct	Field	Field;
44bd389b36SDavid du Colombier 
45bd389b36SDavid du Colombier struct	Line
46bd389b36SDavid du Colombier {
47bd389b36SDavid du Colombier 	Key*	key;
48bd389b36SDavid du Colombier 	int	llen;		/* always >= 1 */
49bd389b36SDavid du Colombier 	uchar	line[1];	/* always ends in '\n' */
50bd389b36SDavid du Colombier };
51bd389b36SDavid du Colombier 
52bd389b36SDavid du Colombier struct	Merge
53bd389b36SDavid du Colombier {
54bd389b36SDavid du Colombier 	Key*	key;		/* copy of line->key so (Line*) looks like (Merge*) */
55bd389b36SDavid du Colombier 	Line*	line;		/* line at the head of a merged temp file */
56bd389b36SDavid du Colombier 	int	fd;		/* file descriptor */
57bd389b36SDavid du Colombier 	Biobuf	b;		/* iobuf for reading a temp file */
58bd389b36SDavid du Colombier };
59bd389b36SDavid du Colombier 
60bd389b36SDavid du Colombier struct	Key
61bd389b36SDavid du Colombier {
62bd389b36SDavid du Colombier 	int	klen;
63bd389b36SDavid du Colombier 	uchar	key[1];
64bd389b36SDavid du Colombier };
65bd389b36SDavid du Colombier 
66bd389b36SDavid du Colombier struct	Field
67bd389b36SDavid du Colombier {
68bd389b36SDavid du Colombier 	int	beg1;
69bd389b36SDavid du Colombier 	int	beg2;
70bd389b36SDavid du Colombier 	int	end1;
71bd389b36SDavid du Colombier 	int	end2;
72bd389b36SDavid du Colombier 
73bd389b36SDavid du Colombier 	long	flags;
74bd389b36SDavid du Colombier 	uchar	mapto[256];
75bd389b36SDavid du Colombier 
76bd389b36SDavid du Colombier 	void	(*dokey)(Key*, uchar*, uchar*, Field*);
77bd389b36SDavid du Colombier };
78bd389b36SDavid du Colombier 
79*219b2ee8SDavid du Colombier struct args
80bd389b36SDavid du Colombier {
81bd389b36SDavid du Colombier 	char*	ofile;
82bd389b36SDavid du Colombier 	char*	tname;
83bd389b36SDavid du Colombier 	Rune	tabchar;
84bd389b36SDavid du Colombier 	char	cflag;
85bd389b36SDavid du Colombier 	char	uflag;
86bd389b36SDavid du Colombier 	char	vflag;
87bd389b36SDavid du Colombier 	int	nfield;
88bd389b36SDavid du Colombier 	int	nfile;
89bd389b36SDavid du Colombier 	Field	field[Nfield];
90bd389b36SDavid du Colombier 
91bd389b36SDavid du Colombier 	Line**	linep;
92bd389b36SDavid du Colombier 	long	nline;			/* number of lines in this temp file */
93bd389b36SDavid du Colombier 	long	lineno;			/* overall ordinal for -s option */
94bd389b36SDavid du Colombier 	int	ntemp;
95bd389b36SDavid du Colombier } args;
96bd389b36SDavid du Colombier 
97bd389b36SDavid du Colombier extern	int	latinmap[];
98bd389b36SDavid du Colombier extern	Rune*	month[12];
99bd389b36SDavid du Colombier 
100bd389b36SDavid du Colombier void	buildkey(Line*);
101bd389b36SDavid du Colombier void	doargs(int, char*[]);
102bd389b36SDavid du Colombier void	dofield(char*, int*, int*, int, int);
103bd389b36SDavid du Colombier void	dofile(Biobuf*);
104bd389b36SDavid du Colombier void	dokey_(Key*, uchar*, uchar*, Field*);
105bd389b36SDavid du Colombier void	dokey_dfi(Key*, uchar*, uchar*, Field*);
106bd389b36SDavid du Colombier void	dokey_gn(Key*, uchar*, uchar*, Field*);
107bd389b36SDavid du Colombier void	dokey_m(Key*, uchar*, uchar*, Field*);
108bd389b36SDavid du Colombier void	dokey_r(Key*, uchar*, uchar*, Field*);
109bd389b36SDavid du Colombier void	done(char*);
110bd389b36SDavid du Colombier int	kcmp(Key*, Key*);
111bd389b36SDavid du Colombier void	makemapd(Field*);
112bd389b36SDavid du Colombier void	makemapm(Field*);
113bd389b36SDavid du Colombier void	mergefiles(int, int, Biobuf*);
114bd389b36SDavid du Colombier void	mergeout(Biobuf*);
115bd389b36SDavid du Colombier void	newfield(void);
116bd389b36SDavid du Colombier Line*	newline(Biobuf*);
117bd389b36SDavid du Colombier void	nomem(void);
118bd389b36SDavid du Colombier void	notifyf(void*, char*);
119bd389b36SDavid du Colombier void	printargs(void);
120bd389b36SDavid du Colombier void	printout(Biobuf*);
121bd389b36SDavid du Colombier void	setfield(int, int);
122bd389b36SDavid du Colombier uchar*	skip(uchar*, int, int, int, int);
123bd389b36SDavid du Colombier void	sort4(void*, ulong);
124bd389b36SDavid du Colombier char*	tempfile(int);
125bd389b36SDavid du Colombier void	tempout(void);
126*219b2ee8SDavid du Colombier void	lineout(Biobuf*, Line*);
127bd389b36SDavid du Colombier 
128bd389b36SDavid du Colombier void
129bd389b36SDavid du Colombier main(int argc, char *argv[])
130bd389b36SDavid du Colombier {
131bd389b36SDavid du Colombier 	int i, f;
132bd389b36SDavid du Colombier 	char *s;
133bd389b36SDavid du Colombier 	Biobuf bbuf;
134bd389b36SDavid du Colombier 
135bd389b36SDavid du Colombier 	notify(notifyf);	/**/
136bd389b36SDavid du Colombier 	doargs(argc, argv);
137bd389b36SDavid du Colombier 	if(args.vflag)
138bd389b36SDavid du Colombier 		printargs();
139bd389b36SDavid du Colombier 
140bd389b36SDavid du Colombier 	for(i=1; i<argc; i++) {
141bd389b36SDavid du Colombier 		s = argv[i];
142bd389b36SDavid du Colombier 		if(s == 0)
143bd389b36SDavid du Colombier 			continue;
144bd389b36SDavid du Colombier 		if(strcmp(s, "-") == 0) {
145bd389b36SDavid du Colombier 			Binit(&bbuf, 0, OREAD);
146bd389b36SDavid du Colombier 			dofile(&bbuf);
147*219b2ee8SDavid du Colombier 			Bterm(&bbuf);
148bd389b36SDavid du Colombier 			continue;
149bd389b36SDavid du Colombier 		}
150bd389b36SDavid du Colombier 		f = open(s, OREAD);
151bd389b36SDavid du Colombier 		if(f < 0) {
152bd389b36SDavid du Colombier 			fprint(2, "sort: open %s: %r\n", s);
153bd389b36SDavid du Colombier 			done("open");
154bd389b36SDavid du Colombier 		}
155bd389b36SDavid du Colombier 		Binit(&bbuf, f, OREAD);
156bd389b36SDavid du Colombier 		dofile(&bbuf);
157*219b2ee8SDavid du Colombier 		Bterm(&bbuf);
158bd389b36SDavid du Colombier 		close(f);
159bd389b36SDavid du Colombier 	}
160bd389b36SDavid du Colombier 	if(args.nfile == 0) {
161bd389b36SDavid du Colombier 		Binit(&bbuf, 0, OREAD);
162bd389b36SDavid du Colombier 		dofile(&bbuf);
163*219b2ee8SDavid du Colombier 		Bterm(&bbuf);
164bd389b36SDavid du Colombier 	}
165bd389b36SDavid du Colombier 	if(args.cflag)
166bd389b36SDavid du Colombier 		done(0);
167bd389b36SDavid du Colombier 	if(args.vflag)
168bd389b36SDavid du Colombier 		fprint(2, "=========\n");
169bd389b36SDavid du Colombier 
170bd389b36SDavid du Colombier 	f = 1;
171bd389b36SDavid du Colombier 	if(args.ofile) {
172bd389b36SDavid du Colombier 		f = create(args.ofile, OWRITE, 0666);
173bd389b36SDavid du Colombier 		if(f < 0) {
174bd389b36SDavid du Colombier 			fprint(2, "sort: create %s: %r\n", args.ofile);
175bd389b36SDavid du Colombier 			done("create");
176bd389b36SDavid du Colombier 		}
177bd389b36SDavid du Colombier 	}
178bd389b36SDavid du Colombier 
179bd389b36SDavid du Colombier 	Binit(&bbuf, f, OWRITE);
180bd389b36SDavid du Colombier 	if(args.ntemp) {
181bd389b36SDavid du Colombier 		tempout();
182bd389b36SDavid du Colombier 		mergeout(&bbuf);
183bd389b36SDavid du Colombier 	} else {
184bd389b36SDavid du Colombier 		printout(&bbuf);
185bd389b36SDavid du Colombier 	}
186*219b2ee8SDavid du Colombier 	Bterm(&bbuf);
187bd389b36SDavid du Colombier 	done(0);
188bd389b36SDavid du Colombier }
189bd389b36SDavid du Colombier 
190bd389b36SDavid du Colombier void
191bd389b36SDavid du Colombier dofile(Biobuf *b)
192bd389b36SDavid du Colombier {
193bd389b36SDavid du Colombier 	Line *l, *ol;
194bd389b36SDavid du Colombier 	int n;
195bd389b36SDavid du Colombier 
196bd389b36SDavid du Colombier 	if(args.cflag) {
197bd389b36SDavid du Colombier 		ol = newline(b);
198bd389b36SDavid du Colombier 		if(ol == 0)
199bd389b36SDavid du Colombier 			return;
200bd389b36SDavid du Colombier 		for(;;) {
201bd389b36SDavid du Colombier 			l = newline(b);
202bd389b36SDavid du Colombier 			if(l == 0)
203bd389b36SDavid du Colombier 				break;
204bd389b36SDavid du Colombier 			n = kcmp(ol->key, l->key);
205bd389b36SDavid du Colombier 			if(n > 0 || (n == 0 && args.uflag)) {
206bd389b36SDavid du Colombier 				/* no output shall be produced
207bd389b36SDavid du Colombier 				fprint(2, "sort: -c file not in sort\n"); /**/
208bd389b36SDavid du Colombier 				done("order");
209bd389b36SDavid du Colombier 			}
210bd389b36SDavid du Colombier 			free(ol->key);
211bd389b36SDavid du Colombier 			free(ol);
212bd389b36SDavid du Colombier 			ol = l;
213bd389b36SDavid du Colombier 		}
214bd389b36SDavid du Colombier 		return;
215bd389b36SDavid du Colombier 	}
216bd389b36SDavid du Colombier 
217bd389b36SDavid du Colombier 	if(args.linep == 0) {
218bd389b36SDavid du Colombier 		args.linep = malloc(Nline * sizeof(args.linep));
219bd389b36SDavid du Colombier 		if(args.linep == 0)
220bd389b36SDavid du Colombier 			nomem();
221bd389b36SDavid du Colombier 	}
222bd389b36SDavid du Colombier 	for(;;) {
223bd389b36SDavid du Colombier 		l = newline(b);
224bd389b36SDavid du Colombier 		if(l == 0)
225bd389b36SDavid du Colombier 			break;
226bd389b36SDavid du Colombier 		if(args.nline >= Nline)
227bd389b36SDavid du Colombier 			tempout();
228bd389b36SDavid du Colombier 		args.linep[args.nline] = l;
229bd389b36SDavid du Colombier 		args.nline++;
230bd389b36SDavid du Colombier 		args.lineno++;
231bd389b36SDavid du Colombier 	}
232bd389b36SDavid du Colombier }
233bd389b36SDavid du Colombier 
234bd389b36SDavid du Colombier void
235bd389b36SDavid du Colombier notifyf(void *a, char *s)
236bd389b36SDavid du Colombier {
237bd389b36SDavid du Colombier 
238bd389b36SDavid du Colombier 	USED(a);
239bd389b36SDavid du Colombier 	if(strcmp(s, "interrupt") == 0)
240bd389b36SDavid du Colombier 		done(0);
241bd389b36SDavid du Colombier 	if(strcmp(s, "hangup") == 0)
242bd389b36SDavid du Colombier 		done(0);
243*219b2ee8SDavid du Colombier 	if(strcmp(s, "kill") == 0)
244*219b2ee8SDavid du Colombier 		done(0);
245*219b2ee8SDavid du Colombier 	if(strcmp(s, "sys: write on closed pipe") == 0)
246*219b2ee8SDavid du Colombier 		done(0);
247bd389b36SDavid du Colombier 	fprint(2, "sort: note: %s\n", s);
248bd389b36SDavid du Colombier 	abort();
249bd389b36SDavid du Colombier }
250bd389b36SDavid du Colombier 
251bd389b36SDavid du Colombier Line*
252bd389b36SDavid du Colombier newline(Biobuf *b)
253bd389b36SDavid du Colombier {
254bd389b36SDavid du Colombier 	Line *l;
255bd389b36SDavid du Colombier 	char *p;
256bd389b36SDavid du Colombier 	int n, c;
257bd389b36SDavid du Colombier 
258bd389b36SDavid du Colombier 	p = Brdline(b, '\n');
259bd389b36SDavid du Colombier 	n = Blinelen(b);
260bd389b36SDavid du Colombier 	if(p == 0) {
261bd389b36SDavid du Colombier 		if(n == 0)
262bd389b36SDavid du Colombier 			return 0;
263bd389b36SDavid du Colombier 		l = 0;
264bd389b36SDavid du Colombier 		for(n=0;;) {
265bd389b36SDavid du Colombier 			if((n & 31) == 0) {
266bd389b36SDavid du Colombier 				l = realloc(l, sizeof(Line) +
267bd389b36SDavid du Colombier 					(n+31)*sizeof(l->line[0]));
268bd389b36SDavid du Colombier 				if(l == 0)
269bd389b36SDavid du Colombier 					nomem();
270bd389b36SDavid du Colombier 			}
271bd389b36SDavid du Colombier 			c = Bgetc(b);
272bd389b36SDavid du Colombier 			if(c < 0) {
273bd389b36SDavid du Colombier 				fprint(2, "sort: newline added\n");
274bd389b36SDavid du Colombier 				c = '\n';
275bd389b36SDavid du Colombier 			}
276bd389b36SDavid du Colombier 			l->line[n++] = c;
277bd389b36SDavid du Colombier 			if(c == '\n')
278bd389b36SDavid du Colombier 				break;
279bd389b36SDavid du Colombier 		}
280bd389b36SDavid du Colombier 		l->llen = n;
281bd389b36SDavid du Colombier 		buildkey(l);
282bd389b36SDavid du Colombier 		return l;
283bd389b36SDavid du Colombier 	}
284bd389b36SDavid du Colombier 	l = malloc(sizeof(Line) +
285bd389b36SDavid du Colombier 		(n-1)*sizeof(l->line[0]));
286bd389b36SDavid du Colombier 	if(l == 0)
287bd389b36SDavid du Colombier 		nomem();
288bd389b36SDavid du Colombier 	l->llen = n;
289bd389b36SDavid du Colombier 	memmove(l->line, p, n);
290bd389b36SDavid du Colombier 	buildkey(l);
291bd389b36SDavid du Colombier 	return l;
292bd389b36SDavid du Colombier }
293bd389b36SDavid du Colombier 
294bd389b36SDavid du Colombier void
295*219b2ee8SDavid du Colombier lineout(Biobuf *b, Line *l)
296*219b2ee8SDavid du Colombier {
297*219b2ee8SDavid du Colombier 	int n, m;
298*219b2ee8SDavid du Colombier 
299*219b2ee8SDavid du Colombier 	n = l->llen;
300*219b2ee8SDavid du Colombier 	m = Bwrite(b, l->line, n);
301*219b2ee8SDavid du Colombier 	if(n != m)
302*219b2ee8SDavid du Colombier 		exits("write");
303*219b2ee8SDavid du Colombier }
304*219b2ee8SDavid du Colombier 
305*219b2ee8SDavid du Colombier void
306bd389b36SDavid du Colombier tempout(void)
307bd389b36SDavid du Colombier {
308bd389b36SDavid du Colombier 	long n;
309bd389b36SDavid du Colombier 	Line **lp, *l;
310bd389b36SDavid du Colombier 	char *tf;
311*219b2ee8SDavid du Colombier 	int f;
312bd389b36SDavid du Colombier 	Biobuf tb;
313bd389b36SDavid du Colombier 
314bd389b36SDavid du Colombier 	sort4(args.linep, args.nline);
315bd389b36SDavid du Colombier 	tf = tempfile(args.ntemp);
316bd389b36SDavid du Colombier 	args.ntemp++;
317bd389b36SDavid du Colombier 	f = create(tf, OWRITE, 0666);
318bd389b36SDavid du Colombier 	if(f < 0) {
319bd389b36SDavid du Colombier 		fprint(2, "sort: create %s: %r\n", tf);
320bd389b36SDavid du Colombier 		done("create");
321bd389b36SDavid du Colombier 	}
322bd389b36SDavid du Colombier 
323bd389b36SDavid du Colombier 	Binit(&tb, f, OWRITE);
324bd389b36SDavid du Colombier 	lp = args.linep;
325bd389b36SDavid du Colombier 	for(n=args.nline; n>0; n--) {
326bd389b36SDavid du Colombier 		l = *lp++;
327*219b2ee8SDavid du Colombier 		lineout(&tb, l);
328bd389b36SDavid du Colombier 		free(l->key);
329bd389b36SDavid du Colombier 		free(l);
330bd389b36SDavid du Colombier 	}
331bd389b36SDavid du Colombier 	args.nline = 0;
332*219b2ee8SDavid du Colombier 	Bterm(&tb);
333bd389b36SDavid du Colombier 	close(f);
334bd389b36SDavid du Colombier }
335bd389b36SDavid du Colombier 
336bd389b36SDavid du Colombier void
337bd389b36SDavid du Colombier done(char *xs)
338bd389b36SDavid du Colombier {
339bd389b36SDavid du Colombier 	int i;
340bd389b36SDavid du Colombier 
341bd389b36SDavid du Colombier 	for(i=0; i<args.ntemp; i++)
342bd389b36SDavid du Colombier 		remove(tempfile(i));
343bd389b36SDavid du Colombier 	exits(xs);
344bd389b36SDavid du Colombier }
345bd389b36SDavid du Colombier 
346bd389b36SDavid du Colombier void
347bd389b36SDavid du Colombier nomem(void)
348bd389b36SDavid du Colombier {
349bd389b36SDavid du Colombier 	fprint(2, "sort: out of memory\n");
350bd389b36SDavid du Colombier 	done("mem");
351bd389b36SDavid du Colombier }
352bd389b36SDavid du Colombier 
353bd389b36SDavid du Colombier char*
354bd389b36SDavid du Colombier tempfile(int n)
355bd389b36SDavid du Colombier {
356bd389b36SDavid du Colombier 	static char file[20];
357bd389b36SDavid du Colombier 	static int pid;
358bd389b36SDavid du Colombier 	char *dir;
359bd389b36SDavid du Colombier 
360bd389b36SDavid du Colombier 	dir = "/tmp";
361bd389b36SDavid du Colombier 	if(args.tname)
362bd389b36SDavid du Colombier 		dir = args.tname;
363bd389b36SDavid du Colombier 	if(pid == 0)
364bd389b36SDavid du Colombier 		pid = getpid() % 1000;
365bd389b36SDavid du Colombier 	if(pid == 0)
366bd389b36SDavid du Colombier 		pid = 1;
367bd389b36SDavid du Colombier 	sprint(file, "%s/sort.%.3d.%.3d", dir, pid, n);
368bd389b36SDavid du Colombier 	return file;
369bd389b36SDavid du Colombier }
370bd389b36SDavid du Colombier 
371bd389b36SDavid du Colombier void
372bd389b36SDavid du Colombier mergeout(Biobuf *b)
373bd389b36SDavid du Colombier {
374bd389b36SDavid du Colombier 	int n, i, f;
375bd389b36SDavid du Colombier 	char *tf;
376bd389b36SDavid du Colombier 	Biobuf tb;
377bd389b36SDavid du Colombier 
378bd389b36SDavid du Colombier 	for(i=0; i<args.ntemp; i+=n) {
379bd389b36SDavid du Colombier 		n = args.ntemp - i;
380bd389b36SDavid du Colombier 		if(n > Nmerge) {
381bd389b36SDavid du Colombier 			tf = tempfile(args.ntemp);
382bd389b36SDavid du Colombier 			args.ntemp++;
383bd389b36SDavid du Colombier 			f = create(tf, OWRITE, 0666);
384bd389b36SDavid du Colombier 			if(f < 0) {
385bd389b36SDavid du Colombier 				fprint(2, "sort: create %s: %r\n", tf);
386bd389b36SDavid du Colombier 				done("create");
387bd389b36SDavid du Colombier 			}
388bd389b36SDavid du Colombier 			Binit(&tb, f, OWRITE);
389bd389b36SDavid du Colombier 
390bd389b36SDavid du Colombier 			n = Nmerge;
391bd389b36SDavid du Colombier 			mergefiles(i, n, &tb);
392bd389b36SDavid du Colombier 
393*219b2ee8SDavid du Colombier 			Bterm(&tb);
394bd389b36SDavid du Colombier 			close(f);
395bd389b36SDavid du Colombier 		} else
396bd389b36SDavid du Colombier 			mergefiles(i, n, b);
397bd389b36SDavid du Colombier 	}
398bd389b36SDavid du Colombier }
399bd389b36SDavid du Colombier 
400bd389b36SDavid du Colombier void
401bd389b36SDavid du Colombier mergefiles(int t, int n, Biobuf *b)
402bd389b36SDavid du Colombier {
403bd389b36SDavid du Colombier 	Merge *m, *mp, **mmp;
404bd389b36SDavid du Colombier 	Key *ok;
405bd389b36SDavid du Colombier 	Line *l;
406bd389b36SDavid du Colombier 	char *tf;
407bd389b36SDavid du Colombier 	int i, f, nn;
408bd389b36SDavid du Colombier 
409bd389b36SDavid du Colombier 	mmp = malloc(n*sizeof(*mmp));
410bd389b36SDavid du Colombier 	mp = malloc(n*sizeof(*mp));
411bd389b36SDavid du Colombier 	if(mmp == 0 || mp == 0)
412bd389b36SDavid du Colombier 		nomem();
413bd389b36SDavid du Colombier 
414bd389b36SDavid du Colombier 	nn = 0;
415bd389b36SDavid du Colombier 	m = mp;
416bd389b36SDavid du Colombier 	for(i=0; i<n; i++,m++) {
417bd389b36SDavid du Colombier 		tf = tempfile(t+i);
418bd389b36SDavid du Colombier 		f = open(tf, OREAD);
419bd389b36SDavid du Colombier 		if(f < 0) {
420bd389b36SDavid du Colombier 			fprint(2, "sort: reopen %s: %r\n", tf);
421bd389b36SDavid du Colombier 			done("open");
422bd389b36SDavid du Colombier 		}
423bd389b36SDavid du Colombier 		m->fd = f;
424bd389b36SDavid du Colombier 		Binit(&m->b, f, OREAD);
425bd389b36SDavid du Colombier 		mmp[nn] = m;
426bd389b36SDavid du Colombier 
427bd389b36SDavid du Colombier 		l = newline(&m->b);
428bd389b36SDavid du Colombier 		if(l == 0)
429bd389b36SDavid du Colombier 			continue;
430bd389b36SDavid du Colombier 		nn++;
431bd389b36SDavid du Colombier 		m->line = l;
432bd389b36SDavid du Colombier 		m->key = l->key;
433bd389b36SDavid du Colombier 	}
434bd389b36SDavid du Colombier 
435bd389b36SDavid du Colombier 	ok = 0;
436bd389b36SDavid du Colombier 	for(;;) {
437bd389b36SDavid du Colombier 		sort4(mmp, nn);
438bd389b36SDavid du Colombier 		m = *mmp;
439bd389b36SDavid du Colombier 		if(nn == 0)
440bd389b36SDavid du Colombier 			break;
441bd389b36SDavid du Colombier 		for(;;) {
442bd389b36SDavid du Colombier 			l = m->line;
443bd389b36SDavid du Colombier 			if(args.uflag && ok && kcmp(ok, l->key) == 0) {
444bd389b36SDavid du Colombier 				free(l->key);
445bd389b36SDavid du Colombier 			} else {
446*219b2ee8SDavid du Colombier 				lineout(b, l);
447bd389b36SDavid du Colombier 				if(ok)
448bd389b36SDavid du Colombier 					free(ok);
449bd389b36SDavid du Colombier 				ok = l->key;
450bd389b36SDavid du Colombier 				free(l);
451bd389b36SDavid du Colombier 			}
452bd389b36SDavid du Colombier 
453bd389b36SDavid du Colombier 			l = newline(&m->b);
454bd389b36SDavid du Colombier 			if(l == 0) {
455bd389b36SDavid du Colombier 				nn--;
456bd389b36SDavid du Colombier 				mmp[0] = mmp[nn];
457bd389b36SDavid du Colombier 				break;
458bd389b36SDavid du Colombier 			}
459bd389b36SDavid du Colombier 			m->line = l;
460bd389b36SDavid du Colombier 			m->key = l->key;
461bd389b36SDavid du Colombier 			if(nn > 1 && kcmp(mmp[0]->key, mmp[1]->key) > 0)
462bd389b36SDavid du Colombier 				break;
463bd389b36SDavid du Colombier 		}
464bd389b36SDavid du Colombier 	}
465bd389b36SDavid du Colombier 	if(ok)
466bd389b36SDavid du Colombier 		free(ok);
467bd389b36SDavid du Colombier 
468bd389b36SDavid du Colombier 	m = mp;
469bd389b36SDavid du Colombier 	for(i=0; i<n; i++,m++) {
470*219b2ee8SDavid du Colombier 		Bterm(&m->b);
471bd389b36SDavid du Colombier 		close(m->fd);
472bd389b36SDavid du Colombier 	}
473bd389b36SDavid du Colombier 
474bd389b36SDavid du Colombier 	free(mp);
475bd389b36SDavid du Colombier 	free(mmp);
476bd389b36SDavid du Colombier }
477bd389b36SDavid du Colombier 
478bd389b36SDavid du Colombier int
479bd389b36SDavid du Colombier kcmp(Key *ka, Key *kb)
480bd389b36SDavid du Colombier {
481bd389b36SDavid du Colombier 	int n, m;
482bd389b36SDavid du Colombier 
483bd389b36SDavid du Colombier 	/*
484bd389b36SDavid du Colombier 	 * set n to length of smaller key
485bd389b36SDavid du Colombier 	 */
486bd389b36SDavid du Colombier 	n = ka->klen;
487bd389b36SDavid du Colombier 	m = kb->klen;
488bd389b36SDavid du Colombier 	if(n > m)
489bd389b36SDavid du Colombier 		n = m;
490bd389b36SDavid du Colombier 	return memcmp(ka->key, kb->key, n);
491bd389b36SDavid du Colombier }
492bd389b36SDavid du Colombier 
493bd389b36SDavid du Colombier void
494bd389b36SDavid du Colombier printout(Biobuf *b)
495bd389b36SDavid du Colombier {
496bd389b36SDavid du Colombier 	long n;
497bd389b36SDavid du Colombier 	Line **lp, *l;
498bd389b36SDavid du Colombier 	Key *ok;
499bd389b36SDavid du Colombier 
500bd389b36SDavid du Colombier 	sort4(args.linep, args.nline);
501bd389b36SDavid du Colombier 	lp = args.linep;
502bd389b36SDavid du Colombier 	ok = 0;
503bd389b36SDavid du Colombier 	for(n=args.nline; n>0; n--) {
504bd389b36SDavid du Colombier 		l = *lp++;
505bd389b36SDavid du Colombier 		if(args.uflag && ok && kcmp(ok, l->key) == 0)
506bd389b36SDavid du Colombier 			continue;
507*219b2ee8SDavid du Colombier 		lineout(b, l);
508bd389b36SDavid du Colombier 		ok = l->key;
509bd389b36SDavid du Colombier 	}
510bd389b36SDavid du Colombier }
511bd389b36SDavid du Colombier 
512bd389b36SDavid du Colombier void
513bd389b36SDavid du Colombier setfield(int n, int c)
514bd389b36SDavid du Colombier {
515bd389b36SDavid du Colombier 	Field *f;
516bd389b36SDavid du Colombier 
517bd389b36SDavid du Colombier 	f = &args.field[n];
518bd389b36SDavid du Colombier 	switch(c) {
519bd389b36SDavid du Colombier 	default:
520bd389b36SDavid du Colombier 		fprint(2, "sort: unknown option: field.%C\n", c);
521bd389b36SDavid du Colombier 		done("option");
522bd389b36SDavid du Colombier 	case 'b':	/* skip blanks */
523bd389b36SDavid du Colombier 		f->flags |= Bflag;
524bd389b36SDavid du Colombier 		break;
525bd389b36SDavid du Colombier 	case 'd':	/* directory order */
526bd389b36SDavid du Colombier 		f->flags |= Dflag;
527bd389b36SDavid du Colombier 		break;
528bd389b36SDavid du Colombier 	case 'f':	/* fold case */
529bd389b36SDavid du Colombier 		f->flags |= Fflag;
530bd389b36SDavid du Colombier 		break;
531bd389b36SDavid du Colombier 	case 'g':	/* floating point -n case */
532bd389b36SDavid du Colombier 		f->flags |= Gflag;
533bd389b36SDavid du Colombier 		break;
534bd389b36SDavid du Colombier 	case 'i':	/* ignore non-ascii */
535bd389b36SDavid du Colombier 		f->flags |= Iflag;
536bd389b36SDavid du Colombier 		break;
537bd389b36SDavid du Colombier 	case 'M':	/* month */
538bd389b36SDavid du Colombier 		f->flags |= Mflag;
539bd389b36SDavid du Colombier 		break;
540bd389b36SDavid du Colombier 	case 'n':	/* numbers */
541bd389b36SDavid du Colombier 		f->flags |= Nflag;
542bd389b36SDavid du Colombier 		break;
543bd389b36SDavid du Colombier 	case 'r':	/* reverse */
544bd389b36SDavid du Colombier 		f->flags |= Rflag;
545bd389b36SDavid du Colombier 		break;
546bd389b36SDavid du Colombier 	case 'w':	/* ignore white */
547bd389b36SDavid du Colombier 		f->flags |= Wflag;
548bd389b36SDavid du Colombier 		break;
549bd389b36SDavid du Colombier 	}
550bd389b36SDavid du Colombier }
551bd389b36SDavid du Colombier 
552bd389b36SDavid du Colombier void
553bd389b36SDavid du Colombier dofield(char *s, int *n1, int *n2, int off1, int off2)
554bd389b36SDavid du Colombier {
555bd389b36SDavid du Colombier 	int c, n;
556bd389b36SDavid du Colombier 
557bd389b36SDavid du Colombier 	c = *s++;
558bd389b36SDavid du Colombier 	if(c >= '0' && c <= '9') {
559bd389b36SDavid du Colombier 		n = 0;
560bd389b36SDavid du Colombier 		while(c >= '0' && c <= '9') {
561bd389b36SDavid du Colombier 			n = n*10 + (c-'0');
562bd389b36SDavid du Colombier 			c = *s++;
563bd389b36SDavid du Colombier 		}
564bd389b36SDavid du Colombier 		n -= off1;	/* posix committee: rot in hell */
565bd389b36SDavid du Colombier 		if(n < 0) {
566bd389b36SDavid du Colombier 			fprint(2, "sort: field offset must be positive\n");
567bd389b36SDavid du Colombier 			done("option");
568bd389b36SDavid du Colombier 		}
569bd389b36SDavid du Colombier 		*n1 = n;
570bd389b36SDavid du Colombier 	}
571bd389b36SDavid du Colombier 	if(c == '.') {
572bd389b36SDavid du Colombier 		c = *s++;
573bd389b36SDavid du Colombier 		if(c >= '0' && c <= '9') {
574bd389b36SDavid du Colombier 			n = 0;
575bd389b36SDavid du Colombier 			while(c >= '0' && c <= '9') {
576bd389b36SDavid du Colombier 				n = n*10 + (c-'0');
577bd389b36SDavid du Colombier 				c = *s++;
578bd389b36SDavid du Colombier 			}
579bd389b36SDavid du Colombier 			n -= off2;
580bd389b36SDavid du Colombier 			if(n < 0) {
581bd389b36SDavid du Colombier 				fprint(2, "sort: character offset must be positive\n");
582bd389b36SDavid du Colombier 				done("option");
583bd389b36SDavid du Colombier 			}
584bd389b36SDavid du Colombier 			*n2 = n;
585bd389b36SDavid du Colombier 		}
586bd389b36SDavid du Colombier 	}
587bd389b36SDavid du Colombier 	while(c != 0) {
588bd389b36SDavid du Colombier 		setfield(args.nfield, c);
589bd389b36SDavid du Colombier 		c = *s++;
590bd389b36SDavid du Colombier 	}
591bd389b36SDavid du Colombier }
592bd389b36SDavid du Colombier 
593bd389b36SDavid du Colombier void
594bd389b36SDavid du Colombier printargs(void)
595bd389b36SDavid du Colombier {
596bd389b36SDavid du Colombier 	int i, n;
597bd389b36SDavid du Colombier 	Field *f;
598bd389b36SDavid du Colombier 	char *prefix;
599bd389b36SDavid du Colombier 
600bd389b36SDavid du Colombier 	fprint(2, "sort");
601bd389b36SDavid du Colombier 	for(i=0; i<=args.nfield; i++) {
602bd389b36SDavid du Colombier 		f = &args.field[i];
603bd389b36SDavid du Colombier 		prefix = " -";
604bd389b36SDavid du Colombier 		if(i) {
605bd389b36SDavid du Colombier 			n = f->beg1;
606bd389b36SDavid du Colombier 			if(n >= 0)
607bd389b36SDavid du Colombier 				fprint(2, " +%d", n);
608bd389b36SDavid du Colombier 			else
609bd389b36SDavid du Colombier 				fprint(2, " +*");
610bd389b36SDavid du Colombier 			n = f->beg2;
611bd389b36SDavid du Colombier 			if(n >= 0)
612bd389b36SDavid du Colombier 				fprint(2, ".%d", n);
613bd389b36SDavid du Colombier 			else
614bd389b36SDavid du Colombier 				fprint(2, ".*");
615bd389b36SDavid du Colombier 
616bd389b36SDavid du Colombier 			if(f->flags & B1flag)
617bd389b36SDavid du Colombier 				fprint(2, "b");
618bd389b36SDavid du Colombier 
619bd389b36SDavid du Colombier 			n = f->end1;
620bd389b36SDavid du Colombier 			if(n >= 0)
621bd389b36SDavid du Colombier 				fprint(2, " -%d", n);
622bd389b36SDavid du Colombier 			else
623bd389b36SDavid du Colombier 				fprint(2, " -*");
624bd389b36SDavid du Colombier 			n = f->end2;
625bd389b36SDavid du Colombier 			if(n >= 0)
626bd389b36SDavid du Colombier 				fprint(2, ".%d", n);
627bd389b36SDavid du Colombier 			else
628bd389b36SDavid du Colombier 				fprint(2, ".*");
629bd389b36SDavid du Colombier 			prefix = "";
630bd389b36SDavid du Colombier 		}
631bd389b36SDavid du Colombier 		if(f->flags & Bflag)
632bd389b36SDavid du Colombier 			fprint(2, "%sb", prefix);
633bd389b36SDavid du Colombier 		if(f->flags & Dflag)
634bd389b36SDavid du Colombier 			fprint(2, "%sd", prefix);
635bd389b36SDavid du Colombier 		if(f->flags & Fflag)
636bd389b36SDavid du Colombier 			fprint(2, "%sf", prefix);
637bd389b36SDavid du Colombier 		if(f->flags & Gflag)
638bd389b36SDavid du Colombier 			fprint(2, "%sg", prefix);
639bd389b36SDavid du Colombier 		if(f->flags & Iflag)
640bd389b36SDavid du Colombier 			fprint(2, "%si", prefix);
641bd389b36SDavid du Colombier 		if(f->flags & Mflag)
642bd389b36SDavid du Colombier 			fprint(2, "%sM", prefix);
643bd389b36SDavid du Colombier 		if(f->flags & Nflag)
644bd389b36SDavid du Colombier 			fprint(2, "%sn", prefix);
645bd389b36SDavid du Colombier 		if(f->flags & Rflag)
646bd389b36SDavid du Colombier 			fprint(2, "%sr", prefix);
647bd389b36SDavid du Colombier 		if(f->flags & Wflag)
648bd389b36SDavid du Colombier 			fprint(2, "%sw", prefix);
649bd389b36SDavid du Colombier 	}
650bd389b36SDavid du Colombier 	if(args.cflag)
651bd389b36SDavid du Colombier 		fprint(2, " -c");
652bd389b36SDavid du Colombier 	if(args.uflag)
653bd389b36SDavid du Colombier 		fprint(2, " -u");
654bd389b36SDavid du Colombier 	if(args.ofile)
655bd389b36SDavid du Colombier 		fprint(2, " -o %s", args.ofile);
656bd389b36SDavid du Colombier 	fprint(2, "\n");
657bd389b36SDavid du Colombier }
658bd389b36SDavid du Colombier 
659bd389b36SDavid du Colombier void
660bd389b36SDavid du Colombier newfield(void)
661bd389b36SDavid du Colombier {
662bd389b36SDavid du Colombier 	int n;
663bd389b36SDavid du Colombier 	Field *f;
664bd389b36SDavid du Colombier 
665bd389b36SDavid du Colombier 	n = args.nfield + 1;
666bd389b36SDavid du Colombier 	if(n >= Nfield) {
667bd389b36SDavid du Colombier 		fprint(2, "sort: too many fields specified\n");
668bd389b36SDavid du Colombier 		done("option");
669bd389b36SDavid du Colombier 	}
670bd389b36SDavid du Colombier 	args.nfield = n;
671bd389b36SDavid du Colombier 	f = &args.field[n];
672bd389b36SDavid du Colombier 	f->beg1 = -1;
673bd389b36SDavid du Colombier 	f->beg2 = -1;
674bd389b36SDavid du Colombier 	f->end1 = -1;
675bd389b36SDavid du Colombier 	f->end2 = -1;
676bd389b36SDavid du Colombier }
677bd389b36SDavid du Colombier 
678bd389b36SDavid du Colombier void
679bd389b36SDavid du Colombier doargs(int argc, char *argv[])
680bd389b36SDavid du Colombier {
681bd389b36SDavid du Colombier 	int i, c, hadplus;
682bd389b36SDavid du Colombier 	char *s, *p, *q;
683bd389b36SDavid du Colombier 	Field *f;
684bd389b36SDavid du Colombier 
685bd389b36SDavid du Colombier 	hadplus = 0;
686bd389b36SDavid du Colombier 	for(i=1; i<argc; i++) {
687bd389b36SDavid du Colombier 		s = argv[i];
688bd389b36SDavid du Colombier 		c = *s++;
689bd389b36SDavid du Colombier 		if(c == '-') {
690bd389b36SDavid du Colombier 			c = *s;
691bd389b36SDavid du Colombier 			if(c == 0)		/* forced end of arg marker */
692bd389b36SDavid du Colombier 				break;
693bd389b36SDavid du Colombier 			argv[i] = 0;		/* clobber args processed */
694bd389b36SDavid du Colombier 			if(c == '.' || (c >= '0' && c <= '9')) {
695bd389b36SDavid du Colombier 				if(!hadplus)
696bd389b36SDavid du Colombier 					newfield();
697bd389b36SDavid du Colombier 				f = &args.field[args.nfield];
698bd389b36SDavid du Colombier 				dofield(s, &f->end1, &f->end2, 0, 0);
699bd389b36SDavid du Colombier 				hadplus = 0;
700bd389b36SDavid du Colombier 				continue;
701bd389b36SDavid du Colombier 			}
702bd389b36SDavid du Colombier 
703bd389b36SDavid du Colombier 			while(c = *s++)
704bd389b36SDavid du Colombier 			switch(c) {
705bd389b36SDavid du Colombier 			case '-':	/* end of options */
706bd389b36SDavid du Colombier 				i = argc;
707bd389b36SDavid du Colombier 				continue;
708bd389b36SDavid du Colombier 			case 'T':	/* temp directory */
709bd389b36SDavid du Colombier 				if(*s == 0) {
710bd389b36SDavid du Colombier 					i++;
711bd389b36SDavid du Colombier 					if(i < argc) {
712bd389b36SDavid du Colombier 						args.tname = argv[i];
713bd389b36SDavid du Colombier 						argv[i] = 0;
714bd389b36SDavid du Colombier 					}
715bd389b36SDavid du Colombier 				} else
716bd389b36SDavid du Colombier 					args.tname = s;
717bd389b36SDavid du Colombier 				s = strchr(s, 0);
718bd389b36SDavid du Colombier 				break;
719bd389b36SDavid du Colombier 			case 'o':	/* output file */
720bd389b36SDavid du Colombier 				if(*s == 0) {
721bd389b36SDavid du Colombier 					i++;
722bd389b36SDavid du Colombier 					if(i < argc) {
723bd389b36SDavid du Colombier 						args.ofile = argv[i];
724bd389b36SDavid du Colombier 						argv[i] = 0;
725bd389b36SDavid du Colombier 					}
726bd389b36SDavid du Colombier 				} else
727bd389b36SDavid du Colombier 					args.ofile = s;
728bd389b36SDavid du Colombier 				s = strchr(s, 0);
729bd389b36SDavid du Colombier 				break;
730bd389b36SDavid du Colombier 			case 'k':	/* posix key (what were they thinking?) */
731bd389b36SDavid du Colombier 				p = 0;
732bd389b36SDavid du Colombier 				if(*s == 0) {
733bd389b36SDavid du Colombier 					i++;
734bd389b36SDavid du Colombier 					if(i < argc) {
735bd389b36SDavid du Colombier 						p = argv[i];
736bd389b36SDavid du Colombier 						argv[i] = 0;
737bd389b36SDavid du Colombier 					}
738bd389b36SDavid du Colombier 				} else
739bd389b36SDavid du Colombier 					p = s;
740bd389b36SDavid du Colombier 				s = strchr(s, 0);
741bd389b36SDavid du Colombier 				if(p == 0)
742bd389b36SDavid du Colombier 					break;
743bd389b36SDavid du Colombier 
744bd389b36SDavid du Colombier 				newfield();
745bd389b36SDavid du Colombier 				q = strchr(p, ',');
746bd389b36SDavid du Colombier 				if(q)
747bd389b36SDavid du Colombier 					*q++ = 0;
748bd389b36SDavid du Colombier 				f = &args.field[args.nfield];
749bd389b36SDavid du Colombier 				dofield(p, &f->beg1, &f->beg2, 1, 1);
750bd389b36SDavid du Colombier 				if(f->flags & Bflag) {
751bd389b36SDavid du Colombier 					f->flags |= B1flag;
752bd389b36SDavid du Colombier 					f->flags &= ~Bflag;
753bd389b36SDavid du Colombier 				}
754bd389b36SDavid du Colombier 				if(q) {
755bd389b36SDavid du Colombier 					dofield(q, &f->end1, &f->end2, 1, 0);
756bd389b36SDavid du Colombier 					if(f->end2 <= 0)
757bd389b36SDavid du Colombier 						f->end1++;
758bd389b36SDavid du Colombier 				}
759bd389b36SDavid du Colombier 				hadplus = 0;
760bd389b36SDavid du Colombier 				break;
761bd389b36SDavid du Colombier 			case 't':	/* tab character */
762bd389b36SDavid du Colombier 				if(*s == 0) {
763bd389b36SDavid du Colombier 					i++;
764bd389b36SDavid du Colombier 					if(i < argc) {
765bd389b36SDavid du Colombier 						chartorune(&args.tabchar, argv[i]);
766bd389b36SDavid du Colombier 						argv[i] = 0;
767bd389b36SDavid du Colombier 					}
768bd389b36SDavid du Colombier 				} else
769bd389b36SDavid du Colombier 					s += chartorune(&args.tabchar, s);
770bd389b36SDavid du Colombier 				if(args.tabchar == '\n') {
771bd389b36SDavid du Colombier 					fprint(2, "aw come on, rob\n");
772bd389b36SDavid du Colombier 					done("rob");
773bd389b36SDavid du Colombier 				}
774bd389b36SDavid du Colombier 				break;
775bd389b36SDavid du Colombier 			case 'c':	/* check order */
776bd389b36SDavid du Colombier 				args.cflag = 1;
777bd389b36SDavid du Colombier 				break;
778bd389b36SDavid du Colombier 			case 'u':	/* unique */
779bd389b36SDavid du Colombier 				args.uflag = 1;
780bd389b36SDavid du Colombier 				break;
781bd389b36SDavid du Colombier 			case 'v':	/* debugging noise */
782bd389b36SDavid du Colombier 				args.vflag = 1;
783bd389b36SDavid du Colombier 				break;
784bd389b36SDavid du Colombier 
785bd389b36SDavid du Colombier 			case 'M':	/* month */
786bd389b36SDavid du Colombier 			case 'b':	/* skip blanks */
787bd389b36SDavid du Colombier 			case 'd':	/* directory order */
788bd389b36SDavid du Colombier 			case 'f':	/* fold case */
789bd389b36SDavid du Colombier 			case 'g':	/* floating numbers */
790bd389b36SDavid du Colombier 			case 'i':	/* ignore non-ascii */
791bd389b36SDavid du Colombier 			case 'n':	/* numbers */
792bd389b36SDavid du Colombier 			case 'r':	/* reverse */
793bd389b36SDavid du Colombier 			case 'w':	/* ignore white */
794bd389b36SDavid du Colombier 				if(args.nfield > 0)
795bd389b36SDavid du Colombier 					fprint(2, "sort: global field set after -k\n");
796bd389b36SDavid du Colombier 				setfield(0, c);
797bd389b36SDavid du Colombier 				break;
798bd389b36SDavid du Colombier 			case 'm':
799*219b2ee8SDavid du Colombier 				/* option m silently ignored but required by posix */
800bd389b36SDavid du Colombier 				break;
801bd389b36SDavid du Colombier 			default:
802bd389b36SDavid du Colombier 				fprint(2, "sort: unknown option: -%C\n", c);
803bd389b36SDavid du Colombier 				done("option");
804bd389b36SDavid du Colombier 			}
805bd389b36SDavid du Colombier 			continue;
806bd389b36SDavid du Colombier 		}
807bd389b36SDavid du Colombier 		if(c == '+') {
808bd389b36SDavid du Colombier 			argv[i] = 0;		/* clobber args processed */
809bd389b36SDavid du Colombier 			c = *s;
810bd389b36SDavid du Colombier 			if(c == '.' || (c >= '0' && c <= '9')) {
811bd389b36SDavid du Colombier 				newfield();
812bd389b36SDavid du Colombier 				f = &args.field[args.nfield];
813bd389b36SDavid du Colombier 				dofield(s, &f->beg1, &f->beg2, 0, 0);
814bd389b36SDavid du Colombier 				if(f->flags & Bflag) {
815bd389b36SDavid du Colombier 					f->flags |= B1flag;
816bd389b36SDavid du Colombier 					f->flags &= ~Bflag;
817bd389b36SDavid du Colombier 				}
818bd389b36SDavid du Colombier 				hadplus = 1;
819bd389b36SDavid du Colombier 				continue;
820bd389b36SDavid du Colombier 			}
821bd389b36SDavid du Colombier 			fprint(2, "sort: unknown option: +%C\n", c);
822bd389b36SDavid du Colombier 			done("option");
823bd389b36SDavid du Colombier 		}
824bd389b36SDavid du Colombier 		args.nfile++;
825bd389b36SDavid du Colombier 	}
826bd389b36SDavid du Colombier 
827bd389b36SDavid du Colombier 	for(i=0; i<=args.nfield; i++) {
828bd389b36SDavid du Colombier 		f = &args.field[i];
829bd389b36SDavid du Colombier 
830bd389b36SDavid du Colombier 		/*
831bd389b36SDavid du Colombier 		 * global options apply to fields that
832bd389b36SDavid du Colombier 		 * specify no options
833bd389b36SDavid du Colombier 		 */
834bd389b36SDavid du Colombier 		if(f->flags == 0) {
835bd389b36SDavid du Colombier 			f->flags = args.field[0].flags;
836bd389b36SDavid du Colombier 			if(args.field[0].flags & Bflag)
837bd389b36SDavid du Colombier 				f->flags |= B1flag;
838bd389b36SDavid du Colombier 		}
839bd389b36SDavid du Colombier 
840bd389b36SDavid du Colombier 
841bd389b36SDavid du Colombier 		/*
842bd389b36SDavid du Colombier 		 * build buildkey specification
843bd389b36SDavid du Colombier 		 */
844bd389b36SDavid du Colombier 		switch(f->flags & ~(Bflag|B1flag)) {
845bd389b36SDavid du Colombier 		default:
846bd389b36SDavid du Colombier 			fprint(2, "sort: illegal combination of flags: %lx\n", f->flags);
847bd389b36SDavid du Colombier 			done("option");
848bd389b36SDavid du Colombier 		case 0:
849bd389b36SDavid du Colombier 			f->dokey = dokey_;
850bd389b36SDavid du Colombier 			break;
851bd389b36SDavid du Colombier 		case Rflag:
852bd389b36SDavid du Colombier 			f->dokey = dokey_r;
853bd389b36SDavid du Colombier 			break;
854bd389b36SDavid du Colombier 		case Gflag:
855bd389b36SDavid du Colombier 		case Nflag:
856bd389b36SDavid du Colombier 		case Gflag|Nflag:
857bd389b36SDavid du Colombier 		case Gflag|Rflag:
858bd389b36SDavid du Colombier 		case Nflag|Rflag:
859bd389b36SDavid du Colombier 		case Gflag|Nflag|Rflag:
860bd389b36SDavid du Colombier 			f->dokey = dokey_gn;
861bd389b36SDavid du Colombier 			break;
862bd389b36SDavid du Colombier 		case Mflag:
863bd389b36SDavid du Colombier 		case Mflag|Rflag:
864bd389b36SDavid du Colombier 			f->dokey = dokey_m;
865bd389b36SDavid du Colombier 			makemapm(f);
866bd389b36SDavid du Colombier 			break;
867bd389b36SDavid du Colombier 		case Dflag:
868bd389b36SDavid du Colombier 		case Dflag|Fflag:
869bd389b36SDavid du Colombier 		case Dflag|Fflag|Iflag:
870bd389b36SDavid du Colombier 		case Dflag|Fflag|Iflag|Rflag:
871bd389b36SDavid du Colombier 		case Dflag|Fflag|Iflag|Rflag|Wflag:
872bd389b36SDavid du Colombier 		case Dflag|Fflag|Iflag|Wflag:
873bd389b36SDavid du Colombier 		case Dflag|Fflag|Rflag:
874bd389b36SDavid du Colombier 		case Dflag|Fflag|Rflag|Wflag:
875bd389b36SDavid du Colombier 		case Dflag|Fflag|Wflag:
876bd389b36SDavid du Colombier 		case Dflag|Iflag:
877bd389b36SDavid du Colombier 		case Dflag|Iflag|Rflag:
878bd389b36SDavid du Colombier 		case Dflag|Iflag|Rflag|Wflag:
879bd389b36SDavid du Colombier 		case Dflag|Iflag|Wflag:
880bd389b36SDavid du Colombier 		case Dflag|Rflag:
881bd389b36SDavid du Colombier 		case Dflag|Rflag|Wflag:
882bd389b36SDavid du Colombier 		case Dflag|Wflag:
883bd389b36SDavid du Colombier 		case Fflag:
884bd389b36SDavid du Colombier 		case Fflag|Iflag:
885bd389b36SDavid du Colombier 		case Fflag|Iflag|Rflag:
886bd389b36SDavid du Colombier 		case Fflag|Iflag|Rflag|Wflag:
887bd389b36SDavid du Colombier 		case Fflag|Iflag|Wflag:
888bd389b36SDavid du Colombier 		case Fflag|Rflag:
889bd389b36SDavid du Colombier 		case Fflag|Rflag|Wflag:
890bd389b36SDavid du Colombier 		case Fflag|Wflag:
891bd389b36SDavid du Colombier 		case Iflag:
892bd389b36SDavid du Colombier 		case Iflag|Rflag:
893bd389b36SDavid du Colombier 		case Iflag|Rflag|Wflag:
894bd389b36SDavid du Colombier 		case Iflag|Wflag:
895bd389b36SDavid du Colombier 		case Wflag:
896bd389b36SDavid du Colombier 			f->dokey = dokey_dfi;
897bd389b36SDavid du Colombier 			makemapd(f);
898bd389b36SDavid du Colombier 			break;
899bd389b36SDavid du Colombier 		}
900bd389b36SDavid du Colombier 	}
901bd389b36SDavid du Colombier 
902bd389b36SDavid du Colombier 	/*
903bd389b36SDavid du Colombier 	 * random spot checks
904bd389b36SDavid du Colombier 	 */
905bd389b36SDavid du Colombier 	if(args.nfile > 1 && args.cflag) {
906bd389b36SDavid du Colombier 		fprint(2, "sort: -c can have at most one input file\n");
907bd389b36SDavid du Colombier 		done("option");
908bd389b36SDavid du Colombier 	}
909bd389b36SDavid du Colombier 	return;
910bd389b36SDavid du Colombier }
911bd389b36SDavid du Colombier 
912bd389b36SDavid du Colombier uchar*
913bd389b36SDavid du Colombier skip(uchar *l, int n1, int n2, int bflag, int endfield)
914bd389b36SDavid du Colombier {
915bd389b36SDavid du Colombier 	int i, c, tc;
916bd389b36SDavid du Colombier 	Rune r;
917bd389b36SDavid du Colombier 
918bd389b36SDavid du Colombier 	if(endfield && n1 < 0)
919bd389b36SDavid du Colombier 		return 0;
920bd389b36SDavid du Colombier 
921bd389b36SDavid du Colombier 	c = *l++;
922bd389b36SDavid du Colombier 	tc = args.tabchar;
923bd389b36SDavid du Colombier 	if(tc) {
924bd389b36SDavid du Colombier 		if(tc < Runeself) {
925bd389b36SDavid du Colombier 			for(i=n1; i>0; i--) {
926bd389b36SDavid du Colombier 				while(c != tc) {
927bd389b36SDavid du Colombier 					if(c == '\n')
928bd389b36SDavid du Colombier 						return 0;
929bd389b36SDavid du Colombier 					c = *l++;
930bd389b36SDavid du Colombier 				}
931bd389b36SDavid du Colombier 				if(!(endfield && i == 1))
932bd389b36SDavid du Colombier 					c = *l++;
933bd389b36SDavid du Colombier 			}
934bd389b36SDavid du Colombier 		} else {
935bd389b36SDavid du Colombier 			l--;
936bd389b36SDavid du Colombier 			l += chartorune(&r, (char*)l);
937bd389b36SDavid du Colombier 			for(i=n1; i>0; i--) {
938bd389b36SDavid du Colombier 				while(r != tc) {
939bd389b36SDavid du Colombier 					if(r == '\n')
940bd389b36SDavid du Colombier 						return 0;
941bd389b36SDavid du Colombier 					l += chartorune(&r, (char*)l);
942bd389b36SDavid du Colombier 				}
943bd389b36SDavid du Colombier 				if(!(endfield && i == 1))
944bd389b36SDavid du Colombier 					l += chartorune(&r, (char*)l);
945bd389b36SDavid du Colombier 			}
946bd389b36SDavid du Colombier 			c = r;
947bd389b36SDavid du Colombier 		}
948bd389b36SDavid du Colombier 	} else {
949bd389b36SDavid du Colombier 		for(i=n1; i>0; i--) {
950bd389b36SDavid du Colombier 			while(c == ' ' || c == '\t')
951bd389b36SDavid du Colombier 				c = *l++;
952bd389b36SDavid du Colombier 			while(c != ' ' && c != '\t') {
953bd389b36SDavid du Colombier 				if(c == '\n')
954bd389b36SDavid du Colombier 					return 0;
955bd389b36SDavid du Colombier 				c = *l++;
956bd389b36SDavid du Colombier 			}
957bd389b36SDavid du Colombier 		}
958bd389b36SDavid du Colombier 	}
959bd389b36SDavid du Colombier 
960bd389b36SDavid du Colombier 	if(bflag)
961bd389b36SDavid du Colombier 		while(c == ' ' || c == '\t')
962bd389b36SDavid du Colombier 			c = *l++;
963bd389b36SDavid du Colombier 
964bd389b36SDavid du Colombier 	l--;
965bd389b36SDavid du Colombier 	for(i=n2; i>0; i--) {
966bd389b36SDavid du Colombier 		c = *l;
967bd389b36SDavid du Colombier 		if(c < Runeself) {
968bd389b36SDavid du Colombier 			if(c == '\n')
969bd389b36SDavid du Colombier 				return 0;
970bd389b36SDavid du Colombier 			l++;
971bd389b36SDavid du Colombier 			continue;
972bd389b36SDavid du Colombier 		}
973bd389b36SDavid du Colombier 		l += chartorune(&r, (char*)l);
974bd389b36SDavid du Colombier 	}
975bd389b36SDavid du Colombier 	return l;
976bd389b36SDavid du Colombier }
977bd389b36SDavid du Colombier 
978bd389b36SDavid du Colombier void
979bd389b36SDavid du Colombier dokey_gn(Key *k, uchar *lp, uchar *lpe, Field *f)
980bd389b36SDavid du Colombier {
981bd389b36SDavid du Colombier 	uchar *kp;
982bd389b36SDavid du Colombier 	int c, cl, dp;
983bd389b36SDavid du Colombier 	int state, nzero, exp, expsign, rflag;
984bd389b36SDavid du Colombier 
985bd389b36SDavid du Colombier 	cl = k->klen + 3;
986bd389b36SDavid du Colombier 	kp = k->key + cl;	/* skip place for sign, exponent[2] */
987bd389b36SDavid du Colombier 
988bd389b36SDavid du Colombier 	nzero = 0;		/* number of trailing zeros */
989bd389b36SDavid du Colombier 	exp = 0;		/* value of the exponent */
990bd389b36SDavid du Colombier 	expsign = 0;		/* sign of the exponent */
991bd389b36SDavid du Colombier 	dp = 0x4040;		/* location of decimal point */
992bd389b36SDavid du Colombier 	rflag = f->flags&Rflag;	/* xor of rflag and - sign */
993bd389b36SDavid du Colombier 	state = NSstart;
994bd389b36SDavid du Colombier 
995bd389b36SDavid du Colombier 	for(;; lp++) {
996bd389b36SDavid du Colombier 		if(lp >= lpe)
997bd389b36SDavid du Colombier 			break;
998bd389b36SDavid du Colombier 		c = *lp;
999bd389b36SDavid du Colombier 
1000bd389b36SDavid du Colombier 		if(c == ' ' || c == '\t') {
1001bd389b36SDavid du Colombier 			switch(state) {
1002bd389b36SDavid du Colombier 			case NSstart:
1003bd389b36SDavid du Colombier 			case NSsign:
1004bd389b36SDavid du Colombier 				continue;
1005bd389b36SDavid du Colombier 			}
1006bd389b36SDavid du Colombier 			break;
1007bd389b36SDavid du Colombier 		}
1008bd389b36SDavid du Colombier 		if(c == '+' || c == '-') {
1009bd389b36SDavid du Colombier 			switch(state) {
1010bd389b36SDavid du Colombier 			case NSstart:
1011bd389b36SDavid du Colombier 				state = NSsign;
1012bd389b36SDavid du Colombier 				if(c == '-')
1013bd389b36SDavid du Colombier 					rflag = !rflag;
1014bd389b36SDavid du Colombier 				continue;
1015bd389b36SDavid du Colombier 			case NSexp:
1016bd389b36SDavid du Colombier 				state = NSexpsign;
1017bd389b36SDavid du Colombier 				if(c == '-')
1018bd389b36SDavid du Colombier 					expsign = 1;
1019bd389b36SDavid du Colombier 				continue;
1020bd389b36SDavid du Colombier 			}
1021bd389b36SDavid du Colombier 			break;
1022bd389b36SDavid du Colombier 		}
1023bd389b36SDavid du Colombier 		if(c == '0') {
1024bd389b36SDavid du Colombier 			switch(state) {
1025bd389b36SDavid du Colombier 			case NSdigit:
1026bd389b36SDavid du Colombier 				if(rflag)
1027bd389b36SDavid du Colombier 					c = ~c;
1028bd389b36SDavid du Colombier 				*kp++ = c;
1029bd389b36SDavid du Colombier 				cl++;
1030bd389b36SDavid du Colombier 				nzero++;
1031bd389b36SDavid du Colombier 				dp++;
1032bd389b36SDavid du Colombier 				state = NSdigit;
1033bd389b36SDavid du Colombier 				continue;
1034bd389b36SDavid du Colombier 			case NSfract:
1035bd389b36SDavid du Colombier 				if(rflag)
1036bd389b36SDavid du Colombier 					c = ~c;
1037bd389b36SDavid du Colombier 				*kp++ = c;
1038bd389b36SDavid du Colombier 				cl++;
1039bd389b36SDavid du Colombier 				nzero++;
1040bd389b36SDavid du Colombier 				state = NSfract;
1041bd389b36SDavid du Colombier 				continue;
1042bd389b36SDavid du Colombier 			case NSstart:
1043bd389b36SDavid du Colombier 			case NSsign:
1044bd389b36SDavid du Colombier 			case NSzero:
1045bd389b36SDavid du Colombier 				state = NSzero;
1046bd389b36SDavid du Colombier 				continue;
1047bd389b36SDavid du Colombier 			case NSzerofract:
1048bd389b36SDavid du Colombier 			case NSpoint:
1049bd389b36SDavid du Colombier 				dp--;
1050bd389b36SDavid du Colombier 				state = NSzerofract;
1051bd389b36SDavid du Colombier 				continue;
1052bd389b36SDavid du Colombier 			case NSexpsign:
1053bd389b36SDavid du Colombier 			case NSexp:
1054bd389b36SDavid du Colombier 			case NSexpdigit:
1055bd389b36SDavid du Colombier 				exp = exp*10 + (c - '0');
1056bd389b36SDavid du Colombier 				state = NSexpdigit;
1057bd389b36SDavid du Colombier 				continue;
1058bd389b36SDavid du Colombier 			}
1059bd389b36SDavid du Colombier 			break;
1060bd389b36SDavid du Colombier 		}
1061bd389b36SDavid du Colombier 		if(c >= '1' && c <= '9') {
1062bd389b36SDavid du Colombier 			switch(state) {
1063bd389b36SDavid du Colombier 			case NSzero:
1064bd389b36SDavid du Colombier 			case NSstart:
1065bd389b36SDavid du Colombier 			case NSsign:
1066bd389b36SDavid du Colombier 			case NSdigit:
1067bd389b36SDavid du Colombier 				if(rflag)
1068bd389b36SDavid du Colombier 					c = ~c;
1069bd389b36SDavid du Colombier 				*kp++ = c;
1070bd389b36SDavid du Colombier 				cl++;
1071bd389b36SDavid du Colombier 				nzero = 0;
1072bd389b36SDavid du Colombier 				dp++;
1073bd389b36SDavid du Colombier 				state = NSdigit;
1074bd389b36SDavid du Colombier 				continue;
1075bd389b36SDavid du Colombier 			case NSzerofract:
1076bd389b36SDavid du Colombier 			case NSpoint:
1077bd389b36SDavid du Colombier 			case NSfract:
1078bd389b36SDavid du Colombier 				if(rflag)
1079bd389b36SDavid du Colombier 					c = ~c;
1080bd389b36SDavid du Colombier 				*kp++ = c;
1081bd389b36SDavid du Colombier 				cl++;
1082bd389b36SDavid du Colombier 				nzero = 0;
1083bd389b36SDavid du Colombier 				state = NSfract;
1084bd389b36SDavid du Colombier 				continue;
1085bd389b36SDavid du Colombier 			case NSexpsign:
1086bd389b36SDavid du Colombier 			case NSexp:
1087bd389b36SDavid du Colombier 			case NSexpdigit:
1088bd389b36SDavid du Colombier 				exp = exp*10 + (c - '0');
1089bd389b36SDavid du Colombier 				state = NSexpdigit;
1090bd389b36SDavid du Colombier 				continue;
1091bd389b36SDavid du Colombier 			}
1092bd389b36SDavid du Colombier 			break;
1093bd389b36SDavid du Colombier 		}
1094bd389b36SDavid du Colombier 		if(c == '.') {
1095bd389b36SDavid du Colombier 			switch(state) {
1096bd389b36SDavid du Colombier 			case NSstart:
1097bd389b36SDavid du Colombier 			case NSsign:
1098bd389b36SDavid du Colombier 				state = NSpoint;
1099bd389b36SDavid du Colombier 				continue;
1100bd389b36SDavid du Colombier 			case NSzero:
1101bd389b36SDavid du Colombier 				state = NSzerofract;
1102bd389b36SDavid du Colombier 				continue;
1103bd389b36SDavid du Colombier 			case NSdigit:
1104bd389b36SDavid du Colombier 				state = NSfract;
1105bd389b36SDavid du Colombier 				continue;
1106bd389b36SDavid du Colombier 			}
1107bd389b36SDavid du Colombier 			break;
1108bd389b36SDavid du Colombier 		}
1109bd389b36SDavid du Colombier 		if((f->flags & Gflag) && (c == 'e' || c == 'E')) {
1110bd389b36SDavid du Colombier 			switch(state) {
1111bd389b36SDavid du Colombier 			case NSdigit:
1112bd389b36SDavid du Colombier 			case NSfract:
1113bd389b36SDavid du Colombier 				state = NSexp;
1114bd389b36SDavid du Colombier 				continue;
1115bd389b36SDavid du Colombier 			}
1116bd389b36SDavid du Colombier 			break;
1117bd389b36SDavid du Colombier 		}
1118bd389b36SDavid du Colombier 		break;
1119bd389b36SDavid du Colombier 	}
1120bd389b36SDavid du Colombier 
1121bd389b36SDavid du Colombier 	switch(state) {
1122bd389b36SDavid du Colombier 	/*
1123bd389b36SDavid du Colombier 	 * result is zero
1124bd389b36SDavid du Colombier 	 */
1125bd389b36SDavid du Colombier 	case NSstart:
1126bd389b36SDavid du Colombier 	case NSsign:
1127bd389b36SDavid du Colombier 	case NSzero:
1128bd389b36SDavid du Colombier 	case NSzerofract:
1129bd389b36SDavid du Colombier 	case NSpoint:
1130bd389b36SDavid du Colombier 		kp = k->key + k->klen;
1131bd389b36SDavid du Colombier 		k->klen += 2;
1132bd389b36SDavid du Colombier 		kp[0] = 0x20;	/* between + and - */
1133bd389b36SDavid du Colombier 		kp[1] = 0;
1134bd389b36SDavid du Colombier 		return;
1135bd389b36SDavid du Colombier 	/*
1136bd389b36SDavid du Colombier 	 * result has exponent
1137bd389b36SDavid du Colombier 	 */
1138bd389b36SDavid du Colombier 	case NSexpsign:
1139bd389b36SDavid du Colombier 	case NSexp:
1140bd389b36SDavid du Colombier 	case NSexpdigit:
1141bd389b36SDavid du Colombier 		if(expsign)
1142bd389b36SDavid du Colombier 			exp = -exp;
1143bd389b36SDavid du Colombier 		dp += exp;
1144bd389b36SDavid du Colombier 
1145bd389b36SDavid du Colombier 	/*
1146bd389b36SDavid du Colombier 	 * result is fixed point number
1147bd389b36SDavid du Colombier 	 */
1148bd389b36SDavid du Colombier 	case NSdigit:
1149bd389b36SDavid du Colombier 	case NSfract:
1150bd389b36SDavid du Colombier 		kp -= nzero;
1151bd389b36SDavid du Colombier 		cl -= nzero;
1152bd389b36SDavid du Colombier 		break;
1153bd389b36SDavid du Colombier 	}
1154bd389b36SDavid du Colombier 
1155bd389b36SDavid du Colombier 	/*
1156bd389b36SDavid du Colombier 	 * end of number
1157bd389b36SDavid du Colombier 	 */
1158bd389b36SDavid du Colombier 	c = 0;
1159bd389b36SDavid du Colombier 	if(rflag)
1160bd389b36SDavid du Colombier 		c = ~c;
1161bd389b36SDavid du Colombier 	*kp = c;
1162bd389b36SDavid du Colombier 
1163bd389b36SDavid du Colombier 	/*
1164bd389b36SDavid du Colombier 	 * sign and exponent
1165bd389b36SDavid du Colombier 	 */
1166bd389b36SDavid du Colombier 	c = 0x30;
1167bd389b36SDavid du Colombier 	if(rflag) {
1168bd389b36SDavid du Colombier 		c = 0x10;
1169bd389b36SDavid du Colombier 		dp = ~dp;
1170bd389b36SDavid du Colombier 	}
1171bd389b36SDavid du Colombier 	kp = k->key + k->klen;
1172bd389b36SDavid du Colombier 	kp[0] = c;
1173bd389b36SDavid du Colombier 	kp[1] = (dp >> 8);
1174bd389b36SDavid du Colombier 	kp[2] = dp;
1175bd389b36SDavid du Colombier 	k->klen = cl+1;
1176bd389b36SDavid du Colombier }
1177bd389b36SDavid du Colombier 
1178bd389b36SDavid du Colombier void
1179bd389b36SDavid du Colombier dokey_m(Key *k, uchar *lp, uchar *lpe, Field *f)
1180bd389b36SDavid du Colombier {
1181bd389b36SDavid du Colombier 	uchar *kp;
1182bd389b36SDavid du Colombier 	Rune r, place[3];
1183bd389b36SDavid du Colombier 	int c, cl, pc;
1184bd389b36SDavid du Colombier 	int rflag;
1185bd389b36SDavid du Colombier 
1186bd389b36SDavid du Colombier 	rflag = f->flags&Rflag;
1187bd389b36SDavid du Colombier 	pc = 0;
1188bd389b36SDavid du Colombier 
1189bd389b36SDavid du Colombier 	cl = k->klen;
1190bd389b36SDavid du Colombier 	kp = k->key + cl;
1191bd389b36SDavid du Colombier 
1192bd389b36SDavid du Colombier 	for(;;) {
1193bd389b36SDavid du Colombier 		/*
1194bd389b36SDavid du Colombier 		 * get the character
1195bd389b36SDavid du Colombier 		 */
1196bd389b36SDavid du Colombier 		if(lp >= lpe)
1197bd389b36SDavid du Colombier 			break;
1198bd389b36SDavid du Colombier 		c = *lp;
1199bd389b36SDavid du Colombier 		if(c >= Runeself) {
1200bd389b36SDavid du Colombier 			lp += chartorune(&r, (char*)lp);
1201bd389b36SDavid du Colombier 			c = r;
1202bd389b36SDavid du Colombier 		} else
1203bd389b36SDavid du Colombier 			lp++;
1204bd389b36SDavid du Colombier 
1205bd389b36SDavid du Colombier 		if(c < nelem(f->mapto)) {
1206bd389b36SDavid du Colombier 			c = f->mapto[c];
1207bd389b36SDavid du Colombier 			if(c == 0)
1208bd389b36SDavid du Colombier 				continue;
1209bd389b36SDavid du Colombier 		}
1210bd389b36SDavid du Colombier 		place[pc++] = c;
1211bd389b36SDavid du Colombier 		if(pc < 3)
1212bd389b36SDavid du Colombier 			continue;
1213bd389b36SDavid du Colombier 		for(c=11; c>=0; c--)
1214bd389b36SDavid du Colombier 			if(memcmp(month[c], place, sizeof(place)) == 0)
1215bd389b36SDavid du Colombier 				break;
1216bd389b36SDavid du Colombier 		c += 10;
1217bd389b36SDavid du Colombier 		if(rflag)
1218bd389b36SDavid du Colombier 			c = ~c;
1219bd389b36SDavid du Colombier 		*kp++ = c;
1220bd389b36SDavid du Colombier 		cl++;
1221bd389b36SDavid du Colombier 		break;
1222bd389b36SDavid du Colombier 	}
1223bd389b36SDavid du Colombier 
1224bd389b36SDavid du Colombier 	c = 0;
1225bd389b36SDavid du Colombier 	if(rflag)
1226bd389b36SDavid du Colombier 		c = ~c;
1227bd389b36SDavid du Colombier 	*kp = c;
1228bd389b36SDavid du Colombier 	k->klen = cl+1;
1229bd389b36SDavid du Colombier }
1230bd389b36SDavid du Colombier 
1231bd389b36SDavid du Colombier void
1232bd389b36SDavid du Colombier dokey_dfi(Key *k, uchar *lp, uchar *lpe, Field *f)
1233bd389b36SDavid du Colombier {
1234bd389b36SDavid du Colombier 	uchar *kp;
1235bd389b36SDavid du Colombier 	Rune r;
1236bd389b36SDavid du Colombier 	int c, cl, n, rflag;
1237bd389b36SDavid du Colombier 
1238bd389b36SDavid du Colombier 	cl = k->klen;
1239bd389b36SDavid du Colombier 	kp = k->key + cl;
1240bd389b36SDavid du Colombier 	rflag = f->flags & Rflag;
1241bd389b36SDavid du Colombier 
1242bd389b36SDavid du Colombier 	for(;;) {
1243bd389b36SDavid du Colombier 		/*
1244bd389b36SDavid du Colombier 		 * get the character
1245bd389b36SDavid du Colombier 		 */
1246bd389b36SDavid du Colombier 		if(lp >= lpe)
1247bd389b36SDavid du Colombier 			break;
1248bd389b36SDavid du Colombier 		c = *lp;
1249bd389b36SDavid du Colombier 		if(c >= Runeself) {
1250bd389b36SDavid du Colombier 			lp += chartorune(&r, (char*)lp);
1251bd389b36SDavid du Colombier 			c = r;
1252bd389b36SDavid du Colombier 		} else
1253bd389b36SDavid du Colombier 			lp++;
1254bd389b36SDavid du Colombier 
1255bd389b36SDavid du Colombier 		/*
1256bd389b36SDavid du Colombier 		 * do the various mappings.
1257bd389b36SDavid du Colombier 		 * the common case is handled
1258bd389b36SDavid du Colombier 		 * completely by the table.
1259bd389b36SDavid du Colombier 		 */
1260bd389b36SDavid du Colombier 		if(c != 0 && c < Runeself) {
1261bd389b36SDavid du Colombier 			c = f->mapto[c];
1262bd389b36SDavid du Colombier 			if(c) {
1263bd389b36SDavid du Colombier 				*kp++ = c;
1264bd389b36SDavid du Colombier 				cl++;
1265bd389b36SDavid du Colombier 			}
1266bd389b36SDavid du Colombier 			continue;
1267bd389b36SDavid du Colombier 		}
1268bd389b36SDavid du Colombier 
1269bd389b36SDavid du Colombier 		/*
1270bd389b36SDavid du Colombier 		 * for characters out of range,
1271bd389b36SDavid du Colombier 		 * the table does not do Rflag.
1272bd389b36SDavid du Colombier 		 * ignore is based on mapto[255]
1273bd389b36SDavid du Colombier 		 */
1274bd389b36SDavid du Colombier 		if(c != 0 && c < nelem(f->mapto)) {
1275bd389b36SDavid du Colombier 			c = f->mapto[c];
1276bd389b36SDavid du Colombier 			if(c == 0)
1277bd389b36SDavid du Colombier 				continue;
1278bd389b36SDavid du Colombier 		} else
1279bd389b36SDavid du Colombier 			if(f->mapto[nelem(f->mapto)-1] == 0)
1280bd389b36SDavid du Colombier 				continue;
1281bd389b36SDavid du Colombier 
1282bd389b36SDavid du Colombier 		/*
1283bd389b36SDavid du Colombier 		 * put it in the key
1284bd389b36SDavid du Colombier 		 */
1285bd389b36SDavid du Colombier 		r = c;
1286bd389b36SDavid du Colombier 		n = runetochar((char*)kp, &r);
1287bd389b36SDavid du Colombier 		kp += n;
1288bd389b36SDavid du Colombier 		cl += n;
1289bd389b36SDavid du Colombier 		if(rflag)
1290bd389b36SDavid du Colombier 			while(n > 0) {
1291bd389b36SDavid du Colombier 				kp[-n] = ~kp[-n];
1292bd389b36SDavid du Colombier 				n--;
1293bd389b36SDavid du Colombier 			}
1294bd389b36SDavid du Colombier 	}
1295bd389b36SDavid du Colombier 
1296bd389b36SDavid du Colombier 	/*
1297bd389b36SDavid du Colombier 	 * end of key
1298bd389b36SDavid du Colombier 	 */
1299bd389b36SDavid du Colombier 	k->klen = cl+1;
1300bd389b36SDavid du Colombier 	if(rflag) {
1301bd389b36SDavid du Colombier 		*kp = ~0;
1302bd389b36SDavid du Colombier 		return;
1303bd389b36SDavid du Colombier 	}
1304bd389b36SDavid du Colombier 	*kp = 0;
1305bd389b36SDavid du Colombier }
1306bd389b36SDavid du Colombier 
1307bd389b36SDavid du Colombier void
1308bd389b36SDavid du Colombier dokey_r(Key *k, uchar *lp, uchar *lpe, Field *f)
1309bd389b36SDavid du Colombier {
1310bd389b36SDavid du Colombier 	int cl, n;
1311bd389b36SDavid du Colombier 	uchar *kp;
1312bd389b36SDavid du Colombier 
1313bd389b36SDavid du Colombier 	USED(f);
1314bd389b36SDavid du Colombier 	n = lpe - lp;
1315bd389b36SDavid du Colombier 	if(n < 0)
1316bd389b36SDavid du Colombier 		n = 0;
1317bd389b36SDavid du Colombier 	cl = k->klen;
1318bd389b36SDavid du Colombier 	kp = k->key + cl;
1319bd389b36SDavid du Colombier 	k->klen = cl+n+1;
1320bd389b36SDavid du Colombier 
1321bd389b36SDavid du Colombier 	lpe -= 3;
1322bd389b36SDavid du Colombier 	while(lp < lpe) {
1323bd389b36SDavid du Colombier 		kp[0] = ~lp[0];
1324bd389b36SDavid du Colombier 		kp[1] = ~lp[1];
1325bd389b36SDavid du Colombier 		kp[2] = ~lp[2];
1326bd389b36SDavid du Colombier 		kp[3] = ~lp[3];
1327bd389b36SDavid du Colombier 		kp += 4;
1328bd389b36SDavid du Colombier 		lp += 4;
1329bd389b36SDavid du Colombier 	}
1330bd389b36SDavid du Colombier 
1331bd389b36SDavid du Colombier 	lpe += 3;
1332bd389b36SDavid du Colombier 	while(lp < lpe)
1333bd389b36SDavid du Colombier 		*kp++ = ~*lp++;
1334bd389b36SDavid du Colombier 	*kp = ~0;
1335bd389b36SDavid du Colombier }
1336bd389b36SDavid du Colombier 
1337bd389b36SDavid du Colombier void
1338bd389b36SDavid du Colombier dokey_(Key *k, uchar *lp, uchar *lpe, Field *f)
1339bd389b36SDavid du Colombier {
1340bd389b36SDavid du Colombier 	int n, cl;
1341bd389b36SDavid du Colombier 	uchar *kp;
1342bd389b36SDavid du Colombier 
1343bd389b36SDavid du Colombier 	USED(f);
1344bd389b36SDavid du Colombier 	n = lpe - lp;
1345bd389b36SDavid du Colombier 	if(n < 0)
1346bd389b36SDavid du Colombier 		n = 0;
1347bd389b36SDavid du Colombier 	cl = k->klen;
1348bd389b36SDavid du Colombier 	kp = k->key + cl;
1349bd389b36SDavid du Colombier 	k->klen = cl+n+1;
1350bd389b36SDavid du Colombier 	memmove(kp, lp, n);
1351bd389b36SDavid du Colombier 	kp[n] = 0;
1352bd389b36SDavid du Colombier }
1353bd389b36SDavid du Colombier 
1354bd389b36SDavid du Colombier void
1355bd389b36SDavid du Colombier buildkey(Line *l)
1356bd389b36SDavid du Colombier {
1357bd389b36SDavid du Colombier 	Key *k;
1358bd389b36SDavid du Colombier 	uchar *lp, *lpe;
1359bd389b36SDavid du Colombier 	int ll, kl, cl, i, n;
1360bd389b36SDavid du Colombier 	Field *f;
1361bd389b36SDavid du Colombier 
1362bd389b36SDavid du Colombier 	ll = l->llen - 1;
1363bd389b36SDavid du Colombier 	kl = 0;			/* allocated length */
1364bd389b36SDavid du Colombier 	cl = 0;			/* current length */
1365bd389b36SDavid du Colombier 	k = 0;
1366bd389b36SDavid du Colombier 
1367bd389b36SDavid du Colombier 	for(i=1; i<=args.nfield; i++) {
1368bd389b36SDavid du Colombier 		f = &args.field[i];
1369bd389b36SDavid du Colombier 		lp = skip(l->line, f->beg1, f->beg2, f->flags&B1flag, 0);
1370bd389b36SDavid du Colombier 		if(lp == 0)
1371bd389b36SDavid du Colombier 			lp = l->line + ll;
1372bd389b36SDavid du Colombier 		lpe = skip(l->line, f->end1, f->end2, f->flags&Bflag, 1);
1373bd389b36SDavid du Colombier 		if(lpe == 0)
1374bd389b36SDavid du Colombier 			lpe = l->line + ll;
1375bd389b36SDavid du Colombier 		n = (lpe - lp) + 1;
1376bd389b36SDavid du Colombier 		if(n <= 0)
1377bd389b36SDavid du Colombier 			n = 1;
1378bd389b36SDavid du Colombier 		if(cl+(n+4) > kl) {
1379bd389b36SDavid du Colombier 			kl = cl+(n+4);
1380bd389b36SDavid du Colombier 			k = realloc(k, sizeof(Key) +
1381bd389b36SDavid du Colombier 				(kl-1)*sizeof(k->key[0]));
1382bd389b36SDavid du Colombier 			if(k == 0)
1383bd389b36SDavid du Colombier 				nomem();
1384bd389b36SDavid du Colombier 		}
1385bd389b36SDavid du Colombier 		k->klen = cl;
1386bd389b36SDavid du Colombier 		(*f->dokey)(k, lp, lpe, f);
1387bd389b36SDavid du Colombier 		cl = k->klen;
1388bd389b36SDavid du Colombier 	}
1389bd389b36SDavid du Colombier 
1390bd389b36SDavid du Colombier 	/*
1391bd389b36SDavid du Colombier 	 * global comparisons
1392bd389b36SDavid du Colombier 	 */
1393bd389b36SDavid du Colombier 	if(!(args.uflag && cl > 0)) {
1394bd389b36SDavid du Colombier 		f = &args.field[0];
1395bd389b36SDavid du Colombier 		if(cl+(ll+4) > kl) {
1396bd389b36SDavid du Colombier 			kl = cl+(ll+4);
1397bd389b36SDavid du Colombier 			k = realloc(k, sizeof(Key) +
1398bd389b36SDavid du Colombier 				(kl-1)*sizeof(k->key[0]));
1399bd389b36SDavid du Colombier 			if(k == 0)
1400bd389b36SDavid du Colombier 				nomem();
1401bd389b36SDavid du Colombier 		}
1402bd389b36SDavid du Colombier 		k->klen = cl;
1403bd389b36SDavid du Colombier 		(*f->dokey)(k, l->line, l->line+ll, f);
1404bd389b36SDavid du Colombier 		cl = k->klen;
1405bd389b36SDavid du Colombier 	}
1406bd389b36SDavid du Colombier 
1407bd389b36SDavid du Colombier 	l->key = k;
1408bd389b36SDavid du Colombier 	k->klen = cl;
1409bd389b36SDavid du Colombier 
1410bd389b36SDavid du Colombier 	if(args.vflag) {
1411bd389b36SDavid du Colombier 		fprint(2, "%.*s", l->llen, l->line);
1412bd389b36SDavid du Colombier 		for(i=0; i<k->klen; i++) {
1413bd389b36SDavid du Colombier 			fprint(2, " %.2x", k->key[i]);
1414bd389b36SDavid du Colombier 			if(k->key[i] == 0x00 || k->key[i] == 0xff)
1415bd389b36SDavid du Colombier 				fprint(2, "\n");
1416bd389b36SDavid du Colombier 		}
1417bd389b36SDavid du Colombier 	}
1418bd389b36SDavid du Colombier }
1419bd389b36SDavid du Colombier 
1420bd389b36SDavid du Colombier void
1421bd389b36SDavid du Colombier makemapm(Field *f)
1422bd389b36SDavid du Colombier {
1423bd389b36SDavid du Colombier 	int i, c;
1424bd389b36SDavid du Colombier 
1425bd389b36SDavid du Colombier 	for(i=0; i<nelem(f->mapto); i++) {
1426bd389b36SDavid du Colombier 		c = 1;
1427bd389b36SDavid du Colombier 		if(i == ' ' || i == '\t')
1428bd389b36SDavid du Colombier 			c = 0;
1429bd389b36SDavid du Colombier 		if(i >= 'a' && i <= 'z')
1430bd389b36SDavid du Colombier 			c = i + ('A' - 'a');
1431bd389b36SDavid du Colombier 		if(i >= 'A' && i <= 'Z')
1432bd389b36SDavid du Colombier 			c = i;
1433bd389b36SDavid du Colombier 		f->mapto[i] = c;
1434bd389b36SDavid du Colombier 		if(args.vflag) {
1435bd389b36SDavid du Colombier 			if((i & 15) == 0)
1436bd389b36SDavid du Colombier 				fprint(2, "	");
1437bd389b36SDavid du Colombier 			fprint(2, " %.2x", c);
1438bd389b36SDavid du Colombier 			if((i & 15) == 15)
1439bd389b36SDavid du Colombier 				fprint(2, "\n");
1440bd389b36SDavid du Colombier 		}
1441bd389b36SDavid du Colombier 	}
1442bd389b36SDavid du Colombier }
1443bd389b36SDavid du Colombier 
1444bd389b36SDavid du Colombier void
1445bd389b36SDavid du Colombier makemapd(Field *f)
1446bd389b36SDavid du Colombier {
1447bd389b36SDavid du Colombier 	int i, j, c;
1448bd389b36SDavid du Colombier 
1449bd389b36SDavid du Colombier 	for(i=0; i<nelem(f->mapto); i++) {
1450bd389b36SDavid du Colombier 		c = i;
1451bd389b36SDavid du Colombier 		if(f->flags & Iflag)
1452bd389b36SDavid du Colombier 			if(c < 040 || c > 0176)
1453bd389b36SDavid du Colombier 				c = -1;
1454bd389b36SDavid du Colombier 		if((f->flags & Wflag) && c >= 0)
1455bd389b36SDavid du Colombier 			if(c == ' ' || c == '\t')
1456bd389b36SDavid du Colombier 				c = -1;
1457bd389b36SDavid du Colombier 		if((f->flags & Dflag) && c >= 0)
1458bd389b36SDavid du Colombier 			if(!(c == ' ' || c == '\t' ||
1459bd389b36SDavid du Colombier 			    (c >= 'a' && c <= 'z') ||
1460bd389b36SDavid du Colombier 			    (c >= 'A' && c <= 'Z') ||
1461bd389b36SDavid du Colombier 			    (c >= '0' && c <= '9'))) {
1462bd389b36SDavid du Colombier 				for(j=0; latinmap[j]; j+=3)
1463bd389b36SDavid du Colombier 					if(c == latinmap[j+0] ||
1464bd389b36SDavid du Colombier 					   c == latinmap[j+1])
1465bd389b36SDavid du Colombier 						break;
1466bd389b36SDavid du Colombier 				if(latinmap[j] == 0)
1467bd389b36SDavid du Colombier 					c = -1;
1468bd389b36SDavid du Colombier 			}
1469bd389b36SDavid du Colombier 		if((f->flags & Fflag) && c >= 0) {
1470bd389b36SDavid du Colombier 			if(c >= 'a' && c <= 'z')
1471bd389b36SDavid du Colombier 				c += 'A' - 'a';
1472bd389b36SDavid du Colombier 			for(j=0; latinmap[j]; j+=3)
1473bd389b36SDavid du Colombier 				if(c == latinmap[j+0] ||
1474bd389b36SDavid du Colombier 				   c == latinmap[j+1]) {
1475bd389b36SDavid du Colombier 					c = latinmap[j+2];
1476bd389b36SDavid du Colombier 					break;
1477bd389b36SDavid du Colombier 				}
1478bd389b36SDavid du Colombier 		}
1479bd389b36SDavid du Colombier 		if((f->flags & Rflag) && c >= 0 && i > 0 && i < Runeself)
1480bd389b36SDavid du Colombier 			c = ~c & 0xff;
1481bd389b36SDavid du Colombier 		if(c < 0)
1482bd389b36SDavid du Colombier 			c = 0;
1483bd389b36SDavid du Colombier 		f->mapto[i] = c;
1484bd389b36SDavid du Colombier 		if(args.vflag) {
1485bd389b36SDavid du Colombier 			if((i & 15) == 0)
1486bd389b36SDavid du Colombier 				fprint(2, "	");
1487bd389b36SDavid du Colombier 			fprint(2, " %.2x", c);
1488bd389b36SDavid du Colombier 			if((i & 15) == 15)
1489bd389b36SDavid du Colombier 				fprint(2, "\n");
1490bd389b36SDavid du Colombier 		}
1491bd389b36SDavid du Colombier 	}
1492bd389b36SDavid du Colombier }
1493bd389b36SDavid du Colombier 
1494bd389b36SDavid du Colombier int	latinmap[] =
1495bd389b36SDavid du Colombier {
1496bd389b36SDavid du Colombier /*	lcase	ucase	fold	*/
1497bd389b36SDavid du Colombier 	L'à',	L'À',	L'A',
1498bd389b36SDavid du Colombier 	L'á',	L'Á',	L'A',
1499bd389b36SDavid du Colombier 	L'â',	L'Â',	L'A',
1500bd389b36SDavid du Colombier 	L'ä',	L'Ä',	L'A',
1501bd389b36SDavid du Colombier 	L'ã',	L'Ã',	L'A',
1502bd389b36SDavid du Colombier 	L'å',	L'Å',	L'A',
1503bd389b36SDavid du Colombier 	L'è',	L'È',	L'E',
1504bd389b36SDavid du Colombier 	L'é',	L'É',	L'E',
1505bd389b36SDavid du Colombier 	L'ê',	L'Ê',	L'E',
1506bd389b36SDavid du Colombier 	L'ë',	L'Ë',	L'E',
1507bd389b36SDavid du Colombier 	L'ì',	L'Ì',	L'I',
1508bd389b36SDavid du Colombier 	L'í',	L'Í',	L'I',
1509bd389b36SDavid du Colombier 	L'î',	L'Î',	L'I',
1510bd389b36SDavid du Colombier 	L'ï',	L'Ï',	L'I',
1511bd389b36SDavid du Colombier 	L'ò',	L'Ò',	L'O',
1512bd389b36SDavid du Colombier 	L'ó',	L'Ó',	L'O',
1513bd389b36SDavid du Colombier 	L'ô',	L'Ô',	L'O',
1514bd389b36SDavid du Colombier 	L'ö',	L'Ö',	L'O',
1515bd389b36SDavid du Colombier 	L'õ',	L'Õ',	L'O',
1516bd389b36SDavid du Colombier 	L'ø',	L'Ø',	L'O',
1517bd389b36SDavid du Colombier 	L'ù',	L'Ù',	L'U',
1518bd389b36SDavid du Colombier 	L'ú',	L'Ú',	L'U',
1519bd389b36SDavid du Colombier 	L'û',	L'Û',	L'U',
1520bd389b36SDavid du Colombier 	L'ü',	L'Ü',	L'U',
1521bd389b36SDavid du Colombier 	L'æ',	L'Æ',	L'A',
1522bd389b36SDavid du Colombier 	L'ð',	L'Ð',	L'D',
1523bd389b36SDavid du Colombier 	L'ñ',	L'Ñ',	L'N',
1524bd389b36SDavid du Colombier 	L'ý',	L'Ý',	L'Y',
1525bd389b36SDavid du Colombier 	L'ç',	L'Ç',	L'C',
1526bd389b36SDavid du Colombier 	0,
1527bd389b36SDavid du Colombier };
1528bd389b36SDavid du Colombier 
1529bd389b36SDavid du Colombier Rune*	month[12] =
1530bd389b36SDavid du Colombier {
1531bd389b36SDavid du Colombier 	L"JAN",
1532bd389b36SDavid du Colombier 	L"FEB",
1533bd389b36SDavid du Colombier 	L"MAR",
1534bd389b36SDavid du Colombier 	L"APR",
1535bd389b36SDavid du Colombier 	L"MAY",
1536bd389b36SDavid du Colombier 	L"JUN",
1537bd389b36SDavid du Colombier 	L"JUL",
1538bd389b36SDavid du Colombier 	L"AUG",
1539bd389b36SDavid du Colombier 	L"SEP",
1540bd389b36SDavid du Colombier 	L"OCT",
1541bd389b36SDavid du Colombier 	L"NOV",
1542bd389b36SDavid du Colombier 	L"DEC",
1543bd389b36SDavid du Colombier };
1544bd389b36SDavid du Colombier 
1545bd389b36SDavid du Colombier /************** radix sort ***********/
1546bd389b36SDavid du Colombier 
1547bd389b36SDavid du Colombier enum
1548bd389b36SDavid du Colombier {
1549bd389b36SDavid du Colombier 	Threshold	= 14,
1550bd389b36SDavid du Colombier };
1551bd389b36SDavid du Colombier 
1552bd389b36SDavid du Colombier void	rsort4(Key***, ulong, int);
1553bd389b36SDavid du Colombier void	bsort4(Key***, ulong, int);
1554bd389b36SDavid du Colombier 
1555bd389b36SDavid du Colombier void
1556bd389b36SDavid du Colombier sort4(void *a, ulong n)
1557bd389b36SDavid du Colombier {
1558bd389b36SDavid du Colombier 	if(n > Threshold)
1559bd389b36SDavid du Colombier 		rsort4((Key***)a, n, 0);
1560bd389b36SDavid du Colombier 	else
1561bd389b36SDavid du Colombier 		bsort4((Key***)a, n, 0);
1562bd389b36SDavid du Colombier }
1563bd389b36SDavid du Colombier 
1564bd389b36SDavid du Colombier void
1565bd389b36SDavid du Colombier rsort4(Key ***a, ulong n, int b)
1566bd389b36SDavid du Colombier {
1567bd389b36SDavid du Colombier 	Key ***ea, ***t, ***u, **t1, **u1, *k;
1568bd389b36SDavid du Colombier 	Key ***part[257];
1569bd389b36SDavid du Colombier 	static long count[257];
1570bd389b36SDavid du Colombier 	long clist[257+257], *cp, *cp1;
1571bd389b36SDavid du Colombier 	int c, lowc, higc;
1572bd389b36SDavid du Colombier 
1573bd389b36SDavid du Colombier 	/*
1574bd389b36SDavid du Colombier 	 * pass 1 over all keys,
1575bd389b36SDavid du Colombier 	 * count the number of each key[b].
1576bd389b36SDavid du Colombier 	 * find low count and high count.
1577bd389b36SDavid du Colombier 	 */
1578bd389b36SDavid du Colombier 	lowc = 256;
1579bd389b36SDavid du Colombier 	higc = 0;
1580bd389b36SDavid du Colombier 	ea = a+n;
1581bd389b36SDavid du Colombier 	for(t=a; t<ea; t++) {
1582bd389b36SDavid du Colombier 		k = **t;
1583bd389b36SDavid du Colombier 		n = k->klen;
1584bd389b36SDavid du Colombier 		if(b >= n) {
1585bd389b36SDavid du Colombier 			count[256]++;
1586bd389b36SDavid du Colombier 			continue;
1587bd389b36SDavid du Colombier 		}
1588bd389b36SDavid du Colombier 		c = k->key[b];
1589bd389b36SDavid du Colombier 		n = count[c]++;
1590bd389b36SDavid du Colombier 		if(n == 0) {
1591bd389b36SDavid du Colombier 			if(c < lowc)
1592bd389b36SDavid du Colombier 				lowc = c;
1593bd389b36SDavid du Colombier 			if(c > higc)
1594bd389b36SDavid du Colombier 				higc = c;
1595bd389b36SDavid du Colombier 		}
1596bd389b36SDavid du Colombier 	}
1597bd389b36SDavid du Colombier 
1598bd389b36SDavid du Colombier 	/*
1599bd389b36SDavid du Colombier 	 * pass 2 over all counts,
1600bd389b36SDavid du Colombier 	 * put partition pointers in part[c].
1601bd389b36SDavid du Colombier 	 * save compacted indexes and counts
1602bd389b36SDavid du Colombier 	 * in clist[].
1603bd389b36SDavid du Colombier 	 */
1604bd389b36SDavid du Colombier 	t = a;
1605bd389b36SDavid du Colombier 	n = count[256];
1606bd389b36SDavid du Colombier 	clist[0] = n;
1607bd389b36SDavid du Colombier 	part[256] = t;
1608bd389b36SDavid du Colombier 	t += n;
1609bd389b36SDavid du Colombier 
1610bd389b36SDavid du Colombier 	cp1 = clist+1;
1611bd389b36SDavid du Colombier 	cp = count+lowc;
1612bd389b36SDavid du Colombier 	for(c=lowc; c<=higc; c++,cp++) {
1613bd389b36SDavid du Colombier 		n = *cp;
1614bd389b36SDavid du Colombier 		if(n) {
1615bd389b36SDavid du Colombier 			cp1[0] = n;
1616bd389b36SDavid du Colombier 			cp1[1] = c;
1617bd389b36SDavid du Colombier 			cp1 += 2;
1618bd389b36SDavid du Colombier 			part[c] = t;
1619bd389b36SDavid du Colombier 			t += n;
1620bd389b36SDavid du Colombier 		}
1621bd389b36SDavid du Colombier 	}
1622bd389b36SDavid du Colombier 	*cp1 = 0;
1623bd389b36SDavid du Colombier 
1624bd389b36SDavid du Colombier 	/*
1625bd389b36SDavid du Colombier 	 * pass 3 over all counts.
1626bd389b36SDavid du Colombier 	 * chase lowest pointer in each partition
1627bd389b36SDavid du Colombier 	 * around a permutation until it comes
1628bd389b36SDavid du Colombier 	 * back and is stored where it started.
1629bd389b36SDavid du Colombier 	 * static array, count[], should be
1630bd389b36SDavid du Colombier 	 * reduced to zero entries except maybe
1631bd389b36SDavid du Colombier 	 * count[256].
1632bd389b36SDavid du Colombier 	 */
1633bd389b36SDavid du Colombier 	for(cp1=clist+1; cp1[0]; cp1+=2) {
1634bd389b36SDavid du Colombier 		c = cp1[1];
1635bd389b36SDavid du Colombier 		cp = count+c;
1636bd389b36SDavid du Colombier 		while(*cp) {
1637bd389b36SDavid du Colombier 			t1 = *part[c];
1638bd389b36SDavid du Colombier 			for(;;) {
1639bd389b36SDavid du Colombier 				k = *t1;
1640bd389b36SDavid du Colombier 				n = 256;
1641bd389b36SDavid du Colombier 				if(b < k->klen)
1642bd389b36SDavid du Colombier 					n = k->key[b];
1643bd389b36SDavid du Colombier 				u = part[n]++;
1644bd389b36SDavid du Colombier 				count[n]--;
1645bd389b36SDavid du Colombier 				u1 = *u;
1646bd389b36SDavid du Colombier 				*u = t1;
1647bd389b36SDavid du Colombier 				if(n == c)
1648bd389b36SDavid du Colombier 					break;
1649bd389b36SDavid du Colombier 				t1 = u1;
1650bd389b36SDavid du Colombier 			}
1651bd389b36SDavid du Colombier 		}
1652bd389b36SDavid du Colombier 	}
1653bd389b36SDavid du Colombier 
1654bd389b36SDavid du Colombier 	/*
1655bd389b36SDavid du Colombier 	 * pass 4 over all partitions.
1656bd389b36SDavid du Colombier 	 * call recursively.
1657bd389b36SDavid du Colombier 	 */
1658bd389b36SDavid du Colombier 	b++;
1659bd389b36SDavid du Colombier 	t = a + clist[0];
1660bd389b36SDavid du Colombier 	count[256] = 0;
1661bd389b36SDavid du Colombier 	for(cp1=clist+1; n=cp1[0]; cp1+=2) {
1662bd389b36SDavid du Colombier 		if(n > Threshold)
1663bd389b36SDavid du Colombier 			rsort4(t, n, b);
1664bd389b36SDavid du Colombier 		else
1665bd389b36SDavid du Colombier 		if(n > 1)
1666bd389b36SDavid du Colombier 			bsort4(t, n, b);
1667bd389b36SDavid du Colombier 		t += n;
1668bd389b36SDavid du Colombier 	}
1669bd389b36SDavid du Colombier }
1670bd389b36SDavid du Colombier 
1671bd389b36SDavid du Colombier /*
1672bd389b36SDavid du Colombier  * bubble sort to pick up
1673bd389b36SDavid du Colombier  * the pieces.
1674bd389b36SDavid du Colombier  */
1675bd389b36SDavid du Colombier void
1676bd389b36SDavid du Colombier bsort4(Key ***a, ulong n, int b)
1677bd389b36SDavid du Colombier {
1678bd389b36SDavid du Colombier 	Key ***i, ***j, ***k, ***l, **t;
1679bd389b36SDavid du Colombier 	Key *ka, *kb;
1680bd389b36SDavid du Colombier 	int n1, n2;
1681bd389b36SDavid du Colombier 
1682bd389b36SDavid du Colombier 	l = a+n;
1683bd389b36SDavid du Colombier 	j = a;
1684bd389b36SDavid du Colombier 
1685bd389b36SDavid du Colombier loop:
1686bd389b36SDavid du Colombier 	i = j;
1687bd389b36SDavid du Colombier 	j++;
1688bd389b36SDavid du Colombier 	if(j >= l)
1689bd389b36SDavid du Colombier 		return;
1690bd389b36SDavid du Colombier 
1691bd389b36SDavid du Colombier 	ka = **i;
1692bd389b36SDavid du Colombier 	kb = **j;
1693bd389b36SDavid du Colombier 	n1 = ka->klen - b;
1694bd389b36SDavid du Colombier 	n2 = kb->klen - b;
1695bd389b36SDavid du Colombier 	if(n1 > n2)
1696bd389b36SDavid du Colombier 		n1 = n2;
1697bd389b36SDavid du Colombier 	if(n1 <= 0)
1698bd389b36SDavid du Colombier 		goto loop;
1699bd389b36SDavid du Colombier 	n2 = ka->key[b] - kb->key[b];
1700bd389b36SDavid du Colombier 	if(n2 == 0)
1701bd389b36SDavid du Colombier 		n2 = memcmp(ka->key+b, kb->key+b, n1);
1702bd389b36SDavid du Colombier 	if(n2 <= 0)
1703bd389b36SDavid du Colombier 		goto loop;
1704bd389b36SDavid du Colombier 
1705bd389b36SDavid du Colombier 	for(;;) {
1706bd389b36SDavid du Colombier 		k = i+1;
1707bd389b36SDavid du Colombier 
1708bd389b36SDavid du Colombier 		t = *k;
1709bd389b36SDavid du Colombier 		*k = *i;
1710bd389b36SDavid du Colombier 		*i = t;
1711bd389b36SDavid du Colombier 
1712bd389b36SDavid du Colombier 		if(i <= a)
1713bd389b36SDavid du Colombier 			goto loop;
1714bd389b36SDavid du Colombier 
1715bd389b36SDavid du Colombier 		i--;
1716bd389b36SDavid du Colombier 		ka = **i;
1717bd389b36SDavid du Colombier 		kb = *t;
1718bd389b36SDavid du Colombier 		n1 = ka->klen - b;
1719bd389b36SDavid du Colombier 		n2 = kb->klen - b;
1720bd389b36SDavid du Colombier 		if(n1 > n2)
1721bd389b36SDavid du Colombier 			n1 = n2;
1722bd389b36SDavid du Colombier 		if(n1 <= 0)
1723bd389b36SDavid du Colombier 			goto loop;
1724bd389b36SDavid du Colombier 		n2 = ka->key[b] - kb->key[b];
1725bd389b36SDavid du Colombier 		if(n2 == 0)
1726bd389b36SDavid du Colombier 			n2 = memcmp(ka->key+b, kb->key+b, n1);
1727bd389b36SDavid du Colombier 		if(n2 <= 0)
1728bd389b36SDavid du Colombier 			goto loop;
1729bd389b36SDavid du Colombier 	}
1730bd389b36SDavid du Colombier }
1731