xref: /illumos-gate/usr/src/cmd/diff/diff.c (revision 247ccfcd)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  *	diff - differential file comparison
42  *
43  *	Uses an algorithm  which finds
44  *	a pair of longest identical subsequences in the two
45  *	files.
46  *
47  *	The major goal is to generate the match vector J.
48  *	J[i] is the index of the line in file1 corresponding
49  *	to line i file0. J[i] = 0 if there is no
50  *	such line in file1.
51  *
52  *	Lines are hashed so as to work in core. All potential
53  *	matches are located by sorting the lines of each file
54  *	on the hash (called value). In particular, this
55  *	collects the equivalence classes in file1 together.
56  *	Subroutine equiv  replaces the value of each line in
57  *	file0 by the index of the first element of its
58  *	matching equivalence in (the reordered) file1.
59  *	To save space equiv squeezes file1 into a single
60  *	array member in which the equivalence classes
61  *	are simply concatenated, except that their first
62  *	members are flagged by changing sign.
63  *
64  *	Next the indices that point into member are unsorted into
65  *	array class according to the original order of file0.
66  *
67  *	The cleverness lies in routine stone. This marches
68  *	through the lines of file0, developing a vector klist
69  *	of "k-candidates". At step i a k-candidate is a matched
70  *	pair of lines x,y (x in file0 y in file1) such that
71  *	there is a common subsequence of lenght k
72  *	between the first i lines of file0 and the first y
73  *	lines of file1, but there is no such subsequence for
74  *	any smaller y. x is the earliest possible mate to y
75  *	that occurs in such a subsequence.
76  *
77  *	Whenever any of the members of the equivalence class of
78  *	lines in file1 matable to a line in file0 has serial number
79  *	less than the y of some k-candidate, that k-candidate
80  *	with the smallest such y is replaced. The new
81  *	k-candidate is chained (via pred) to the current
82  *	k-1 candidate so that the actual subsequence can
83  *	be recovered. When a member has serial number greater
84  *	that the y of all k-candidates, the klist is extended.
85  *	At the end, the longest subsequence is pulled out
86  *	and placed in the array J by unravel.
87  *
88  *	With J in hand, the matches there recorded are
89  *	checked against reality to assure that no spurious
90  *	matches have crept in due to hashing. If they have,
91  *	they are broken, and "jackpot " is recorded--a harmless
92  *	matter except that a true match for a spuriously
93  *	mated line may now be unnecessarily reported as a change.
94  *
95  *	Much of the complexity of the program comes simply
96  *	from trying to minimize core utilization and
97  *	maximize the range of doable problems by dynamically
98  *	allocating what is needed and reusing what is not.
99  *	The core requirements for problems larger than somewhat
100  *	are (in words) 2*length(file0) + length(file1) +
101  *	3*(number of k-candidates installed),  typically about
102  *	6n words for files of length n.
103  */
104 #include <stdio.h>
105 #include <wchar.h>
106 #include <ctype.h>
107 #include <stdlib.h>
108 #include <limits.h>
109 #include <sys/types.h>
110 #include <sys/stat.h>
111 #include <sys/wait.h>
112 #include <unistd.h>
113 #include <signal.h>
114 #include <fcntl.h>
115 #include <dirent.h>
116 #include <locale.h>
117 #include <stdarg.h>
118 #include <errno.h>
119 #include <string.h>
120 #include "diff.h"
121 
122 #define	CHRTRAN(x)	(iflag ? (iswupper(x) ? towlower(x) : (x)) : (x))
123 #define	NCCHRTRAN(x)	(iswupper(x) ? towlower(x) : (x))
124 #define	max(a, b)	((a) < (b) ? (b) : (a))
125 #define	min(a, b)	((a) > (b) ? (b) : (a))
126 
127 int pref, suff;		/* length of prefix and suffix */
128 int *class;		/* will be overlaid on file[0] */
129 int *member;		/* will be overlaid on file[1] */
130 int *klist;		/* will be overlaid on file[0] after class */
131 struct cand *clist;	/* merely a free storage pot for candidates */
132 int clen = 0;
133 int *J;			/* will be overlaid on class */
134 long *ixold;		/* will be overlaid on klist */
135 long *ixnew;		/* will be overlaid on file[1] */
136 
137 static int	mbcurmax;
138 
139 static void error(const char *);
140 static void unravel(int);
141 static void	check(void);
142 static void	output(void);
143 static void	change(int, int, int, int);
144 static void	range(int, int, char *);
145 static void	fetch(long *, int, int, int, char *, int);
146 static void	dump_context_vec(void);
147 static void	diffdir(char **);
148 static void	setfile(char **, char **, char *);
149 static void	scanpr(struct dir *, int, char *, char *,
150 	char *, char *, char *);
151 static void	only(struct dir *, int);
152 static void	sort(struct line *, int);
153 static void	unsort(struct line *, int, int *);
154 static void	filename(char **, char **, struct stat *, char **);
155 static void	prepare(int, char *);
156 static void	prune(void);
157 static void	equiv(struct line *, int, struct line *, int, int *);
158 static void	done(void);
159 static void	noroom(void);
160 static void	usage(void);
161 static void	initbuf(FILE *, int, long);
162 static void	resetbuf(int);
163 
164 static int	stone(int *, int, int *, int *);
165 static int	newcand(int, int, int);
166 static int	search(int *, int, int);
167 static int	skipline(int);
168 static int	readhash(FILE *, int, char *);
169 static int	entcmp(struct dir *, struct dir *);
170 static int	compare(struct dir *);
171 static int	calldiff(char *);
172 static int	binary(int);
173 static int	filebinary(FILE *);
174 static int	isbinary(char *, int);
175 static int	useless(char *);
176 static char	*copytemp(char *);
177 static char *pfiletype(mode_t);
178 static struct dir *setupdir(char *);
179 static wint_t	getbufwchar(int, int *);
180 static wint_t	wcput(wint_t);
181 static long	ftellbuf(int);
182 
183 
184 /*
185  * error message string constants
186  */
187 #define	BAD_MB_ERR	"invalid multibyte character encountered"
188 #define	NO_PROCS_ERR	"no more processes"
189 #define	NO_MEM_ERR	"out of memory"
190 
191 static void *
192 talloc(size_t n)
193 {
194 	void *p;
195 	p = malloc(n);
196 	if (p == NULL)
197 		noroom();
198 	return (p);
199 }
200 
201 static void *
202 ralloc(void *p, size_t n)	/* compacting reallocation */
203 {
204 	void	*q;
205 #if 0
206 	free(p);
207 #endif
208 	q = realloc(p, n);
209 	if (q == NULL)
210 		noroom();
211 	return (q);
212 }
213 
214 
215 int
216 main(int argc, char **argv)
217 {
218 	int k;
219 	char *argp;
220 	int flag;			/* option flag read by getopt() */
221 	int i, j;
222 	char buf1[BUFSIZ], buf2[BUFSIZ];
223 
224 
225 	(void) setlocale(LC_ALL, "");
226 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
227 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
228 #endif
229 	(void) textdomain(TEXT_DOMAIN);
230 
231 	mbcurmax = MB_CUR_MAX;
232 
233 	diffargv = argv;
234 	whichtemp = 0;
235 	while ((flag = getopt(argc, argv, "bitwcuefhnlrsC:D:S:U:")) != EOF) {
236 		switch (flag) {
237 		case 'D':
238 			opt = D_IFDEF;
239 			wantelses = 1;
240 			ifdef1 = "";
241 			ifdef2 = optarg;
242 			break;
243 
244 		case 'b':
245 			bflag = 1;
246 			break;
247 
248 		case 'C':
249 		case 'U':
250 			opt = D_CONTEXT;
251 			argp = optarg;
252 			context = 0;
253 			while (*argp >= '0' && *argp <= '9')
254 				context *= 10, context += *argp++ - '0';
255 			if (*argp)
256 				error(gettext("use [ -C num | -U num ]"));
257 			if (flag == 'U')
258 				uflag++;
259 			else
260 				uflag = 0;
261 			break;
262 
263 		case 'c':
264 		case 'u':
265 			opt = D_CONTEXT;
266 			context = 3;
267 			if (flag == 'u')
268 				uflag++;
269 			else
270 				uflag = 0;
271 			break;
272 
273 		case 'e':
274 			opt = D_EDIT;
275 			break;
276 
277 		case 'f':
278 			opt = D_REVERSE;
279 			break;
280 
281 		case 'h':
282 			hflag++;
283 			break;
284 
285 		case 'i':
286 			iflag = 1;
287 			break;
288 
289 		case 'l':
290 			lflag = 1;
291 			break;
292 
293 		case 'n':
294 			opt = D_NREVERSE;
295 			break;
296 
297 		case 'r':
298 			rflag = 1;
299 			break;
300 
301 		case 'S':
302 			(void) strcpy(start, optarg);
303 			break;
304 
305 		case 's':
306 			sflag = 1;
307 			break;
308 
309 		case 't':
310 			tflag = 1;
311 			break;
312 
313 		case 'w':
314 			wflag = 1;
315 			break;
316 
317 		case '?':
318 			usage();
319 			break;
320 
321 		default:
322 			/* Not sure how it would get here, but just in case */
323 			(void) fprintf(stderr, "diff: ");
324 			(void) fprintf(stderr,
325 			    gettext("invalid option -%c\n"), flag);
326 			usage();
327 		}
328 	}
329 
330 	argc -= optind;
331 	argv = &argv[optind];
332 
333 	if (opt != D_CONTEXT && uflag)
334 		uflag = 0;
335 
336 	if (argc != 2)
337 		error(gettext("two filename arguments required"));
338 
339 	file1 = argv[0];
340 	file2 = argv[1];
341 
342 	if (hflag) {
343 		if (opt) {
344 			error(gettext(
345 			    "-h doesn't support -e, -f, -n, -c, or -I"));
346 		} else {
347 			diffargv[0] = "diffh";
348 			(void) execv(diffh, diffargv);
349 			(void) fprintf(stderr, "diffh: ");
350 			perror(diffh);
351 			status = 2;
352 			done();
353 		}
354 
355 	}
356 
357 	if (strcmp(file1, "-") == 0) {
358 		if (fstat(fileno(stdin), &stb1) == 0)
359 			stb1.st_mode = S_IFREG;
360 		else {
361 			(void) fprintf(stderr, "diff: ");
362 			perror("stdin");
363 			done();
364 		}
365 	} else if (stat(file1, &stb1) < 0) {
366 		(void) fprintf(stderr, "diff: ");
367 		perror(file1);
368 		done();
369 	}
370 
371 	if (strcmp(file2, "-") == 0) {
372 		if (strcmp(file1, "-") == 0)
373 			error(gettext("cannot specify - -"));
374 		else {
375 			if (fstat(fileno(stdin), &stb2) == 0)
376 				stb2.st_mode = S_IFREG;
377 			else {
378 				(void) fprintf(stderr, "diff: ");
379 				perror("stdin");
380 				done();
381 			}
382 		}
383 	} else if (stat(file2, &stb2) < 0) {
384 		(void) fprintf(stderr, "diff: ");
385 		perror(file2);
386 		done();
387 	}
388 
389 	if ((stb1.st_mode & S_IFMT) == S_IFDIR &&
390 	    (stb2.st_mode & S_IFMT) == S_IFDIR) {
391 		diffdir(argv);
392 		done();
393 	}
394 
395 	filename(&file1, &file2, &stb1, &input_file1);
396 	filename(&file2, &file1, &stb2, &input_file2);
397 	if ((input[0] = fopen(file1, "r")) == NULL) {
398 		(void) fprintf(stderr, "diff: ");
399 		perror(file1);
400 		status = 2;
401 		done();
402 	}
403 	initbuf(input[0], 0, 0);
404 
405 	if ((input[1] = fopen(file2, "r")) == NULL) {
406 		(void) fprintf(stderr, "diff: ");
407 		perror(file2);
408 		status = 2;
409 		done();
410 	}
411 	initbuf(input[1], 1, 0);
412 
413 	if (stb1.st_size != stb2.st_size)
414 		goto notsame;
415 
416 	for (;;) {
417 		i = fread(buf1, 1, BUFSIZ, input[0]);
418 		j = fread(buf2, 1, BUFSIZ, input[1]);
419 		if (ferror(input[0]) || ferror(input[1])) {
420 			(void) fprintf(stderr, "diff: ");
421 			(void) fprintf(stderr, gettext("Error reading "));
422 			perror(ferror(input[0])? file1:file2);
423 			(void) fclose(input[0]);
424 			(void) fclose(input[1]);
425 			status = 2;
426 			done();
427 		}
428 		if (i != j)
429 			goto notsame;
430 		if (i == 0 && j == 0) {
431 			/* files are the same; diff -D needs to print one */
432 			if (opt == D_IFDEF) {
433 				rewind(input[0]);
434 				while (i = fread(buf1, 1, BUFSIZ, input[0]))
435 					(void) fwrite(buf1, 1, i, stdout);
436 			}
437 			(void) fclose(input[0]);
438 			(void) fclose(input[1]);
439 			status = 0;
440 			goto same;		/* files don't differ */
441 		}
442 		for (j = 0; j < i; j++)
443 			if (buf1[j] != buf2[j])
444 				goto notsame;
445 	}
446 
447 notsame:
448 	status = 1;
449 	if (filebinary(input[0]) || filebinary(input[1])) {
450 		if (ferror(input[0]) || ferror(input[1])) {
451 			(void) fprintf(stderr, "diff: ");
452 			(void) fprintf(stderr, gettext("Error reading "));
453 			perror(ferror(input[0])? file1:file2);
454 			(void) fclose(input[0]);
455 			(void) fclose(input[1]);
456 			status = 2;
457 			done();
458 		}
459 		(void) printf(gettext("Binary files %s and %s differ\n"),
460 		    file1, file2);
461 		(void) fclose(input[0]);
462 		(void) fclose(input[1]);
463 		done();
464 	}
465 	prepare(0, file1);
466 	prepare(1, file2);
467 	prune();
468 	sort(sfile[0], slen[0]);
469 	sort(sfile[1], slen[1]);
470 
471 	member = (int *)file[1];
472 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
473 	member = (int *)ralloc((void *)member, (slen[1] + 2) * sizeof (int));
474 
475 	class = (int *)file[0];
476 	unsort(sfile[0], slen[0], class);
477 	class = (int *)ralloc((void *)class, (slen[0] + 2) * sizeof (int));
478 
479 	klist = (int *)talloc((slen[0] + 2) * sizeof (int));
480 	clist = (struct cand *)talloc(sizeof (cand));
481 	k = stone(class, slen[0], member, klist);
482 	free((void *)member);
483 	free((void *)class);
484 
485 	J = (int *)talloc((len[0] + 2) * sizeof (int));
486 	unravel(klist[k]);
487 	free((char *)clist);
488 	free((char *)klist);
489 
490 	ixold = (long *)talloc((len[0] + 2) * sizeof (long));
491 	ixnew = (long *)talloc((len[1] + 2) * sizeof (long));
492 	check();
493 	output();
494 	status = anychange;
495 
496 same:
497 	if (opt == D_CONTEXT && anychange == 0)
498 		(void) printf(gettext("No differences encountered\n"));
499 	done();
500 	/*NOTREACHED*/
501 	return (0);
502 }
503 
504 static int
505 stone(int *a, int n, int *b, int *c)
506 {
507 	int i, k, y;
508 	int j, l;
509 	int oldc, tc;
510 	int oldl;
511 
512 	k = 0;
513 	c[0] = newcand(0, 0, 0);
514 	for (i = 1; i <= n; i++) {
515 		j = a[i];
516 		if (j == 0)
517 			continue;
518 		y = -b[j];
519 		oldl = 0;
520 		oldc = c[0];
521 		do {
522 			if (y <= clist[oldc].y)
523 				continue;
524 			l = search(c, k, y);
525 			if (l != oldl+1)
526 				oldc = c[l-1];
527 			if (l <= k) {
528 				if (clist[c[l]].y <= y)
529 					continue;
530 				tc = c[l];
531 				c[l] = newcand(i, y, oldc);
532 				oldc = tc;
533 				oldl = l;
534 			} else {
535 				c[l] = newcand(i, y, oldc);
536 				k++;
537 				break;
538 			}
539 		} while ((y = b[++j]) > 0);
540 	}
541 	return (k);
542 }
543 
544 static int
545 newcand(int x, int y, int pred)
546 {
547 	struct cand *q;
548 
549 	clist = (struct cand *)ralloc((void *)clist, ++clen * sizeof (cand));
550 	q = clist + clen -1;
551 	q->x = x;
552 	q->y = y;
553 	q->pred = pred;
554 	return (clen - 1);
555 }
556 
557 static int
558 search(int *c, int k, int y)
559 {
560 	int i, j, l;
561 	int t;
562 
563 	if (clist[c[k]].y < y)	/* quick look for typical case */
564 		return (k + 1);
565 	i = 0;
566 	j = k+1;
567 	while ((l = (i + j) / 2) > i) {
568 		t = clist[c[l]].y;
569 		if (t > y)
570 			j = l;
571 		else if (t < y)
572 			i = l;
573 		else
574 			return (l);
575 	}
576 	return (l + 1);
577 }
578 
579 static void
580 unravel(int p)
581 {
582 	int i;
583 	struct cand *q;
584 
585 	for (i = 0; i <= len[0]; i++)
586 		J[i] = i <= pref ? i :
587 		    i > len[0] - suff ? i + len[1] - len[0]: 0;
588 	for (q = clist + p; q->y != 0; q = clist + q->pred)
589 		J[q->x + pref] = q->y + pref;
590 }
591 
592 /*
593  * check does double duty:
594  * 1. ferret out any fortuitous correspondences due to confounding by
595  * hashing (which result in "jackpot")
596  * 2. collect random access indexes to the two files
597  */
598 
599 static void
600 check(void)
601 {
602 	wint_t	c, d;
603 	int i, j;
604 	/* int jackpot; */
605 	int	mlen;
606 	long ctold, ctnew;
607 
608 	resetbuf(0);
609 	resetbuf(1);
610 
611 	j = 1;
612 	ixold[0] = ixnew[0] = 0;
613 	/* jackpot = 0; */
614 
615 	/*
616 	 * ctold and ctnew are byte positions within the file (suitable for
617 	 * lseek()).  After we get a character with getwc(), instead of
618 	 * just incrementing the byte position by 1, we have to determine
619 	 * how many bytes the character actually is.  This is the reason for
620 	 * the wctomb() calls here and in skipline().
621 	 */
622 	ctold = ctnew = 0;
623 	for (i = 1; i <= len[0]; i++) {
624 		if (J[i] == 0) {
625 			ixold[i] = ctold += skipline(0);
626 			continue;
627 		}
628 		while (j < J[i]) {
629 			ixnew[j] = ctnew += skipline(1);
630 			j++;
631 		}
632 		if (bflag || wflag || iflag) {
633 			for (;;) {
634 				c = getbufwchar(0, &mlen);
635 				ctold += mlen;
636 				d = getbufwchar(1, &mlen);
637 				ctnew += mlen;
638 
639 				if (bflag && iswspace(c) && iswspace(d)) {
640 					while (iswspace(c)) {
641 						if (c == '\n' || c == WEOF)
642 							break;
643 
644 						c = getbufwchar(0, &mlen);
645 						ctold += mlen;
646 					}
647 					while (iswspace(d)) {
648 						if (d == '\n' || d == WEOF)
649 							break;
650 
651 						d = getbufwchar(1, &mlen);
652 						ctnew += mlen;
653 					}
654 				} else if (wflag) {
655 					while (iswspace(c) && c != '\n') {
656 						c = getbufwchar(0, &mlen);
657 						ctold += mlen;
658 					}
659 					while (iswspace(d) && d != '\n') {
660 						d = getbufwchar(1, &mlen);
661 						ctnew += mlen;
662 					}
663 				}
664 				if (c == WEOF || d == WEOF) {
665 					if (c != d) {
666 						/* jackpot++; */
667 						J[i] = 0;
668 						if (c != '\n' && c != WEOF)
669 							ctold += skipline(0);
670 						if (d != '\n' && d != WEOF)
671 							ctnew += skipline(1);
672 						break;
673 					}
674 					break;
675 				} else {
676 					if (CHRTRAN(c) != CHRTRAN(d)) {
677 						/* jackpot++; */
678 						J[i] = 0;
679 						if (c != '\n')
680 							ctold += skipline(0);
681 						if (d != '\n')
682 							ctnew += skipline(1);
683 						break;
684 					}
685 					if (c == '\n')
686 						break;
687 				}
688 			}
689 		} else {
690 			for (;;) {
691 				c = getbufwchar(0, &mlen);
692 				ctold += mlen;
693 				d = getbufwchar(1, &mlen);
694 				ctnew += mlen;
695 				if (c != d) {
696 					/* jackpot++; */
697 					J[i] = 0;
698 					if (c != '\n' && c != WEOF)
699 						ctold += skipline(0);
700 					if (d != '\n' && d != WEOF)
701 						ctnew += skipline(1);
702 					break;
703 				}
704 				if (c == '\n' || c == WEOF)
705 					break;
706 			}
707 		}
708 		ixold[i] = ctold;
709 		ixnew[j] = ctnew;
710 		j++;
711 	}
712 	for (; j <= len[1]; j++) {
713 		ixnew[j] = ctnew += skipline(1);
714 	}
715 
716 /*	if(jackpot)			*/
717 /*		fprintf(stderr, "diff: jackpot\n");	*/
718 }
719 
720 static int
721 skipline(int f)
722 {
723 	int i;
724 	wint_t c;
725 	int	mlen;
726 
727 	for (i = 1; c = getbufwchar(f, &mlen); ) {
728 		if (c == '\n' || c == WEOF)
729 			return (i);
730 		i += mlen;
731 	}
732 	return (i);
733 }
734 
735 static void
736 output(void)
737 {
738 	int m;
739 	wint_t	wc;
740 	int i0, i1, j1;
741 	int j0;
742 	int	mlen;
743 
744 	resetbuf(0);
745 	resetbuf(1);
746 
747 	m = len[0];
748 	J[0] = 0;
749 	J[m + 1] = len[1] + 1;
750 	if (opt != D_EDIT)
751 		for (i0 = 1; i0 <= m; i0 = i1+1) {
752 			while (i0 <= m && J[i0] == J[i0 - 1] + 1)
753 				i0++;
754 			j0 = J[i0 - 1] + 1;
755 			i1 = i0 - 1;
756 			while (i1 < m && J[i1 + 1] == 0)
757 				i1++;
758 			j1 = J[i1 + 1] - 1;
759 			J[i1] = j1;
760 			change(i0, i1, j0, j1);
761 		} else for (i0 = m; i0 >= 1; i0 = i1 - 1) {
762 			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
763 				i0--;
764 			j0 = J[i0 + 1] - 1;
765 			i1 = i0 + 1;
766 			while (i1 > 1 && J[i1 - 1] == 0)
767 				i1--;
768 			j1 = J[i1 - 1] + 1;
769 			J[i1] = j1;
770 			change(i1, i0, j1, j0);
771 		}
772 	if (m == 0)
773 		change(1, 0, 1, len[1]);
774 	if (opt == D_IFDEF) {
775 		for (;;) {
776 			wc = getbufwchar(0, &mlen);
777 			if (wc == WEOF)
778 				return;
779 			(void) wcput(wc);
780 		}
781 	}
782 	if (anychange && opt == D_CONTEXT)
783 		dump_context_vec();
784 }
785 
786 
787 /*
788  * indicate that there is a difference between lines a and b of the from file
789  * to get to lines c to d of the to file.
790  * If a is greater then b then there are no lines in the from file involved
791  * and this means that there were lines appended (beginning at b).
792  * If c is greater than d then there are lines missing from the to file.
793  */
794 static void
795 change(int a, int b, int c, int d)
796 {
797 	char	time_buf[BUFSIZ];
798 	char	*dcmsg;
799 
800 	if (opt != D_IFDEF && a > b && c > d)
801 		return;
802 	if (anychange == 0) {
803 		anychange = 1;
804 		if (opt == D_CONTEXT) {
805 			/*
806 			 * TRANSLATION_NOTE_FOR_DC
807 			 * This message is the format of file
808 			 * timestamps written with the -C and
809 			 * -c options.
810 			 * %a -- locale's abbreviated weekday name
811 			 * %b -- locale's abbreviated month name
812 			 * %e -- day of month [1,31]
813 			 * %T -- Time as %H:%M:%S
814 			 * %Y -- Year, including the century
815 			 */
816 			dcmsg = dcgettext(NULL, "%a %b %e %T %Y", LC_TIME);
817 			(void) cftime(time_buf, dcmsg, &stb1.st_mtime);
818 			if (uflag)
819 				(void) printf("--- %s	%s\n", input_file1,
820 				    time_buf);
821 			else
822 				(void) printf("*** %s	%s\n", input_file1,
823 				    time_buf);
824 			(void) cftime(time_buf, dcmsg, &stb2.st_mtime);
825 			if (uflag)
826 				(void) printf("+++ %s	%s\n", input_file2,
827 				    time_buf);
828 			else
829 				(void) printf("--- %s	%s\n", input_file2,
830 				    time_buf);
831 
832 			context_vec_start = malloc(MAX_CONTEXT *
833 			    sizeof (struct context_vec));
834 			if (context_vec_start == NULL)
835 				error(gettext(NO_MEM_ERR));
836 
837 			context_vec_end = context_vec_start + (MAX_CONTEXT - 1);
838 			context_vec_ptr = context_vec_start - 1;
839 		}
840 	}
841 
842 	if (opt == D_CONTEXT) {
843 		/*
844 		 * if this new change is within 'context' lines of
845 		 * the previous change, just add it to the change
846 		 * record.  If the record is full or if this
847 		 * change is more than 'context' lines from the previous
848 		 * change, dump the record, reset it & add the new change.
849 		 */
850 		if (context_vec_ptr >= context_vec_end ||
851 		    (context_vec_ptr >= context_vec_start &&
852 		    a > (context_vec_ptr->b + 2 * context) &&
853 		    c > (context_vec_ptr->d + 2 * context)))
854 			dump_context_vec();
855 
856 		context_vec_ptr++;
857 		context_vec_ptr->a = a;
858 		context_vec_ptr->b = b;
859 		context_vec_ptr->c = c;
860 		context_vec_ptr->d = d;
861 		return;
862 	}
863 
864 	switch (opt) {
865 	case D_NORMAL:
866 	case D_EDIT:
867 		range(a, b, ",");
868 		(void) putchar(a > b ? 'a' : c > d ? 'd' : 'c');
869 		if (opt == D_NORMAL) range(c, d, ",");
870 		(void) printf("\n");
871 		break;
872 	case D_REVERSE:
873 		(void) putchar(a > b ? 'a' : c > d ? 'd' : 'c');
874 		range(a, b, " ");
875 		(void) printf("\n");
876 		break;
877 	case D_NREVERSE:
878 		if (a > b)
879 			(void) printf("a%d %d\n", b, d - c + 1);
880 		else {
881 			(void) printf("d%d %d\n", a, b - a + 1);
882 			if (!(c > d))
883 				/* add changed lines */
884 				(void) printf("a%d %d\n", b, d - c + 1);
885 		}
886 		break;
887 	}
888 	if (opt == D_NORMAL || opt == D_IFDEF) {
889 		fetch(ixold, a, b, 0, "< ", 1);
890 		if (a <= b && c <= d && opt == D_NORMAL)
891 			(void) prints("---\n");
892 	}
893 	fetch(ixnew, c, d, 1, opt == D_NORMAL?"> ":empty, 0);
894 	if ((opt == D_EDIT || opt == D_REVERSE) && c <= d)
895 		(void) prints(".\n");
896 	if (inifdef) {
897 		(void) fprintf(stdout, "#endif /* %s */\n", endifname);
898 		inifdef = 0;
899 	}
900 }
901 
902 static void
903 range(int a, int b, char *separator)
904 {
905 	(void) printf("%d", a > b ? b : a);
906 	if (a < b) {
907 		(void) printf("%s%d", separator, b);
908 	}
909 }
910 
911 static void
912 fetch(long *f, int a, int b, int filen, char *s, int oldfile)
913 {
914 	int i;
915 	int col;
916 	int nc;
917 	int mlen = 0;
918 	wint_t	ch;
919 	FILE	*lb;
920 
921 	lb = input[filen];
922 	/*
923 	 * When doing #ifdef's, copy down to current line
924 	 * if this is the first file, so that stuff makes it to output.
925 	 */
926 	if (opt == D_IFDEF && oldfile) {
927 		long curpos = ftellbuf(filen);
928 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
929 		nc = f[(a > b) ? b : (a - 1) ] - curpos;
930 		for (i = 0; i < nc; i += mlen) {
931 			ch = getbufwchar(filen, &mlen);
932 			if (ch == WEOF) {
933 				(void) putchar('\n');
934 				break;
935 			} else {
936 				(void) wcput(ch);
937 			}
938 		}
939 	}
940 	if (a > b)
941 		return;
942 	if (opt == D_IFDEF) {
943 		int oneflag = (*ifdef1 != '\0') != (*ifdef2 != '\0');
944 		if (inifdef)
945 			(void) fprintf(stdout, "#else /* %s%s */\n",
946 			    oneflag && oldfile == 1 ? "!" : "", ifdef2);
947 		else {
948 			if (oneflag) {
949 				/* There was only one ifdef given */
950 				endifname = ifdef2;
951 				if (oldfile)
952 					(void) fprintf(stdout,
953 					    "#ifndef %s\n", endifname);
954 				else
955 					(void) fprintf(stdout,
956 					    "#ifdef %s\n", endifname);
957 			} else {
958 				endifname = oldfile ? ifdef1 : ifdef2;
959 				(void) fprintf(stdout,
960 				    "#ifdef %s\n", endifname);
961 			}
962 		}
963 		inifdef = 1 + oldfile;
964 	}
965 
966 	for (i = a; i <= b; i++) {
967 		(void) fseek(lb, f[i - 1], SEEK_SET);
968 		initbuf(lb, filen, f[i - 1]);
969 		if (opt != D_IFDEF)
970 			(void) prints(s);
971 		col = 0;
972 		while (ch = getbufwchar(filen, &mlen)) {
973 			if (ch != '\n' && ch != WEOF) {
974 				if (ch == '\t' && tflag)
975 					do {
976 						(void) putchar(' ');
977 					} while (++col & 7);
978 				else {
979 					(void) wcput(ch);
980 					col++;
981 				}
982 			} else
983 				break;
984 		}
985 		(void) putchar('\n');
986 	}
987 }
988 
989 /*
990  * hashing has the effect of
991  * arranging line in 7-bit bytes and then
992  * summing 1-s complement in 16-bit hunks
993  */
994 
995 static int
996 readhash(FILE *f, int filen, char *str)
997 {
998 	long sum;
999 	unsigned int	shift;
1000 	int space;
1001 	int t;
1002 	wint_t	wt;
1003 	int	mlen;
1004 
1005 	sum = 1;
1006 	space = 0;
1007 	if (!bflag && !wflag) {
1008 		if (iflag)
1009 			if (mbcurmax == 1) {
1010 				/* In this case, diff doesn't have to take */
1011 				/* care of multibyte characters. */
1012 				for (shift = 0; (t = getc(f)) != '\n';
1013 				    shift += 7) {
1014 					if (t == EOF) {
1015 						if (shift) {
1016 							(void) fprintf(stderr,
1017 	gettext("Warning: missing newline at end of file %s\n"), str);
1018 							break;
1019 						} else
1020 							return (0);
1021 					}
1022 					sum += (isupper(t) ? tolower(t) : t) <<
1023 					    (shift &= HALFMASK);
1024 				}
1025 			} else {
1026 				/* In this case, diff needs to take care of */
1027 				/* multibyte characters. */
1028 				for (shift = 0;
1029 				    (wt = getbufwchar(filen, &mlen)) != '\n';
1030 				    shift += 7) {
1031 					if (wt == WEOF) {
1032 						if (shift) {
1033 							(void) fprintf(stderr,
1034 	gettext("Warning: missing newline at end of file %s\n"), str);
1035 							break;
1036 						} else
1037 							return (0);
1038 					}
1039 					sum += NCCHRTRAN(wt) <<
1040 					    (shift &= HALFMASK);
1041 				}
1042 			}
1043 		else
1044 			/* In this case, diff doesn't have to take care of */
1045 			/* multibyte characters. */
1046 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
1047 				if (t == EOF) {
1048 					if (shift) {
1049 						(void) fprintf(stderr,
1050 	gettext("Warning: missing newline at end of file %s\n"), str);
1051 						break;
1052 					} else
1053 						return (0);
1054 				}
1055 				sum += (long)t << (shift &= HALFMASK);
1056 			}
1057 	} else {
1058 		/* In this case, diff needs to take care of */
1059 		/* multibyte characters. */
1060 		for (shift = 0; ; ) {
1061 			wt = getbufwchar(filen, &mlen);
1062 
1063 			if (wt != '\n' && iswspace(wt)) {
1064 				space++;
1065 				continue;
1066 			} else {
1067 				switch (wt) {
1068 				case WEOF:
1069 					if (shift) {
1070 						(void) fprintf(stderr,
1071 	gettext("Warning: missing newline at end of file %s\n"), str);
1072 						break;
1073 					} else
1074 						return (0);
1075 				default:
1076 					if (space && !wflag) {
1077 						shift += 7;
1078 						space = 0;
1079 					}
1080 					sum += CHRTRAN(wt) <<
1081 					    (shift &= HALFMASK);
1082 					shift += 7;
1083 					continue;
1084 				case L'\n':
1085 					break;
1086 				}
1087 			}
1088 			break;
1089 		}
1090 	}
1091 	return (sum);
1092 }
1093 
1094 
1095 /* dump accumulated "context" diff changes */
1096 static void
1097 dump_context_vec(void)
1098 {
1099 	int	a, b = 0, c, d = 0;
1100 	char	ch;
1101 	struct	context_vec *cvp = context_vec_start;
1102 	int	lowa, upb, lowc, upd;
1103 	int	do_output;
1104 
1105 	if (cvp > context_vec_ptr)
1106 		return;
1107 
1108 	lowa = max(1, cvp->a - context);
1109 	upb  = min(len[0], context_vec_ptr->b + context);
1110 	lowc = max(1, cvp->c - context);
1111 	upd  = min(len[1], context_vec_ptr->d + context);
1112 
1113 	if (uflag) {
1114 		(void) printf("@@ -%d,%d +%d,%d @@\n",
1115 		    lowa, upb - lowa + 1,
1116 		    lowc, upd - lowc + 1);
1117 	} else {
1118 		(void) printf("***************\n*** ");
1119 		range(lowa, upb, ",");
1120 		(void) printf(" ****\n");
1121 	}
1122 
1123 	/*
1124 	 * output changes to the "old" file.  The first loop suppresses
1125 	 * output if there were no changes to the "old" file (we'll see
1126 	 * the "old" lines as context in the "new" list).
1127 	 */
1128 	if (uflag)
1129 		do_output = 1;
1130 	else
1131 		for (do_output = 0; cvp <= context_vec_ptr; cvp++)
1132 			if (cvp->a <= cvp->b) {
1133 				cvp = context_vec_start;
1134 				do_output++;
1135 				break;
1136 			}
1137 
1138 	if (do_output) {
1139 		while (cvp <= context_vec_ptr) {
1140 			a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d;
1141 
1142 			if (a <= b && c <= d)
1143 				ch = 'c';
1144 			else
1145 				ch = (a <= b) ? 'd' : 'a';
1146 
1147 			if (ch == 'a') {
1148 				/* The last argument should not affect */
1149 				/* the behavior of fetch() */
1150 				fetch(ixold, lowa, b, 0, uflag ? " " : "  ", 1);
1151 				if (uflag)
1152 					fetch(ixnew, c, d, 1, "+", 0);
1153 			} else if (ch == 'd') {
1154 				fetch(ixold, lowa, a - 1, 0, uflag ? " " :
1155 				    "  ", 1);
1156 				fetch(ixold, a, b, 0, uflag ? "-" : "- ", 1);
1157 			} else {
1158 				/* The last argument should not affect */
1159 				/* the behavior of fetch() */
1160 				fetch(ixold, lowa, a-1, 0, uflag ? " " : "  ",
1161 				    1);
1162 				if (uflag) {
1163 					fetch(ixold, a, b, 0, "-", 1);
1164 					fetch(ixnew, c, d, 1, "+", 0);
1165 				} else
1166 					fetch(ixold, a, b, 0, "! ", 1);
1167 			}
1168 			lowa = b + 1;
1169 			cvp++;
1170 		}
1171 		/* The last argument should not affect the behavior */
1172 		/* of fetch() */
1173 		fetch(ixold, b+1, upb, 0, uflag ? " " : "  ", 1);
1174 	}
1175 
1176 	if (uflag) {
1177 		context_vec_ptr = context_vec_start - 1;
1178 		return;
1179 	}
1180 
1181 	/* output changes to the "new" file */
1182 	(void) printf("--- ");
1183 	range(lowc, upd, ",");
1184 	(void) printf(" ----\n");
1185 
1186 	do_output = 0;
1187 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1188 		if (cvp->c <= cvp->d) {
1189 			cvp = context_vec_start;
1190 			do_output++;
1191 			break;
1192 		}
1193 
1194 	if (do_output) {
1195 		while (cvp <= context_vec_ptr) {
1196 			a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d;
1197 
1198 			if (a <= b && c <= d)
1199 				ch = 'c';
1200 			else
1201 				ch = (a <= b) ? 'd' : 'a';
1202 
1203 			if (ch == 'd')
1204 				/* The last argument should not affect */
1205 				/* the behavior of fetch() */
1206 				fetch(ixnew, lowc, d, 1, "  ", 0);
1207 			else {
1208 				/* The last argument should not affect */
1209 				/* the behavior of fetch() */
1210 				fetch(ixnew, lowc, c - 1, 1, "  ", 0);
1211 				fetch(ixnew, c, d, 1,
1212 				    ch == 'c' ? "! " : "+ ", 0);
1213 			}
1214 			lowc = d + 1;
1215 			cvp++;
1216 		}
1217 		/* The last argument should not affect the behavior */
1218 		/* of fetch() */
1219 		fetch(ixnew, d + 1, upd, 1, "  ", 0);
1220 	}
1221 	context_vec_ptr = context_vec_start - 1;
1222 }
1223 
1224 
1225 
1226 /*
1227  * diff - directory comparison
1228  */
1229 
1230 struct	dir *setupdir();
1231 int	header;
1232 char	title[2 * BUFSIZ], *etitle;
1233 
1234 static void
1235 diffdir(char **argv)
1236 {
1237 	struct dir *d1, *d2;
1238 	struct dir *dir1, *dir2;
1239 	int i;
1240 	int cmp;
1241 	int result, dirstatus;
1242 
1243 	if (opt == D_IFDEF)
1244 		error(gettext("cannot specify -D with directories"));
1245 
1246 	if (opt == D_EDIT && (sflag || lflag)) {
1247 		(void) fprintf(stderr, "diff: ");
1248 		(void) fprintf(stderr, gettext(
1249 		    "warning: should not give -s or -l with -e\n"));
1250 	}
1251 	dirstatus = 0;
1252 	title[0] = 0;
1253 	(void) strcpy(title, "diff ");
1254 	for (i = 1; diffargv[i + 2]; i++) {
1255 		if (strcmp(diffargv[i], "-") == 0) {
1256 			continue;	/* Skip -S and its argument */
1257 		}
1258 		(void) strcat(title, diffargv[i]);
1259 		(void) strcat(title, " ");
1260 	}
1261 	for (etitle = title; *etitle; etitle++)
1262 		;
1263 	setfile(&file1, &efile1, file1);
1264 	setfile(&file2, &efile2, file2);
1265 	argv[0] = file1;
1266 	argv[1] = file2;
1267 	dir1 = setupdir(file1);
1268 	dir2 = setupdir(file2);
1269 	d1 = dir1; d2 = dir2;
1270 	while (d1->d_entry != 0 || d2->d_entry != 0) {
1271 		if (d1->d_entry && useless(d1->d_entry)) {
1272 			d1++;
1273 			continue;
1274 		}
1275 		if (d2->d_entry && useless(d2->d_entry)) {
1276 			d2++;
1277 			continue;
1278 		}
1279 		if (d1->d_entry == 0)
1280 			cmp = 1;
1281 		else if (d2->d_entry == 0)
1282 			cmp = -1;
1283 		else
1284 			cmp = strcmp(d1->d_entry, d2->d_entry);
1285 		if (cmp < 0) {
1286 			if (lflag)
1287 				d1->d_flags |= ONLY;
1288 			else if (opt == 0 || opt == 2)
1289 				only(d1, 1);
1290 			d1++;
1291 			if (dirstatus == 0)
1292 				dirstatus = 1;
1293 		} else if (cmp == 0) {
1294 			result = compare(d1);
1295 			if (result > dirstatus)
1296 				dirstatus = result;
1297 			d1++;
1298 			d2++;
1299 		} else {
1300 			if (lflag)
1301 				d2->d_flags |= ONLY;
1302 			else if (opt == 0 || opt == 2)
1303 				only(d2, 2);
1304 			d2++;
1305 			if (dirstatus == 0)
1306 				dirstatus = 1;
1307 		}
1308 	}
1309 	if (lflag) {
1310 		scanpr(dir1, ONLY,
1311 		    gettext("Only in %.*s"), file1, efile1, 0, 0);
1312 		scanpr(dir2, ONLY,
1313 		    gettext("Only in %.*s"), file2, efile2, 0, 0);
1314 		scanpr(dir1, SAME,
1315 		    gettext("Common identical files in %.*s and %.*s"),
1316 		    file1, efile1, file2, efile2);
1317 		scanpr(dir1, DIFFER,
1318 		    gettext("Binary files which differ in %.*s and %.*s"),
1319 		    file1, efile1, file2, efile2);
1320 		scanpr(dir1, DIRECT,
1321 		    gettext("Common subdirectories of %.*s and %.*s"),
1322 		    file1, efile1, file2, efile2);
1323 	}
1324 	if (rflag) {
1325 		if (header && lflag)
1326 			(void) printf("\f");
1327 		for (d1 = dir1; d1->d_entry; d1++)  {
1328 			if ((d1->d_flags & DIRECT) == 0)
1329 				continue;
1330 			(void) strcpy(efile1, d1->d_entry);
1331 			(void) strcpy(efile2, d1->d_entry);
1332 			result = calldiff((char *)0);
1333 			if (result > dirstatus)
1334 				dirstatus = result;
1335 		}
1336 	}
1337 	status = dirstatus;
1338 }
1339 
1340 static void
1341 setfile(char **fpp, char **epp, char *file)
1342 {
1343 	char *cp;
1344 
1345 	*fpp = (char *)malloc(BUFSIZ);
1346 	if (*fpp == 0) {
1347 		(void) fprintf(stderr, "diff: ");
1348 		(void) fprintf(stderr, gettext("out of memory\n"));
1349 		exit(1);
1350 	}
1351 	(void) strcpy(*fpp, file);
1352 	for (cp = *fpp; *cp; cp++)
1353 		continue;
1354 	*cp++ = '/';
1355 	*cp = 0;
1356 	*epp = cp;
1357 }
1358 
1359 static void
1360 scanpr(struct dir *dp, int test, char *title, char *file1, char *efile1,
1361     char *file2, char *efile2)
1362 {
1363 	int titled = 0;
1364 
1365 	for (; dp->d_entry; dp++) {
1366 		if ((dp->d_flags & test) == 0)
1367 			continue;
1368 		if (titled == 0) {
1369 			if (header == 0)
1370 				header = 1;
1371 			else
1372 				(void) printf("\n");
1373 			(void) printf(title,
1374 			    efile1 - file1 - 1, file1,
1375 			    efile2 - file2 - 1, file2);
1376 			(void) printf(":\n");
1377 			titled = 1;
1378 		}
1379 		(void) printf("\t%s\n", dp->d_entry);
1380 	}
1381 }
1382 
1383 static void
1384 only(struct dir *dp, int which)
1385 {
1386 	char *file = which == 1 ? file1 : file2;
1387 	char *efile = which == 1 ? efile1 : efile2;
1388 
1389 	(void) printf(gettext("Only in %.*s: %s\n"), efile - file - 1, file,
1390 	    dp->d_entry);
1391 }
1392 
1393 int	entcmp();
1394 
1395 static struct dir *
1396 setupdir(char *cp)
1397 {
1398 	struct dir *dp = 0, *ep;
1399 	struct dirent64 *rp;
1400 	int nitems;
1401 	int size;
1402 	DIR *dirp;
1403 
1404 	dirp = opendir(cp);
1405 	if (dirp == NULL) {
1406 		(void) fprintf(stderr, "diff: ");
1407 		perror(cp);
1408 		done();
1409 	}
1410 	nitems = 0;
1411 	dp = (struct dir *)malloc(sizeof (struct dir));
1412 	if (dp == 0)
1413 		error(gettext(NO_MEM_ERR));
1414 
1415 	while (rp = readdir64(dirp)) {
1416 		ep = &dp[nitems++];
1417 		ep->d_reclen = rp->d_reclen;
1418 		ep->d_entry = 0;
1419 		ep->d_flags = 0;
1420 		size = strlen(rp->d_name);
1421 		if (size > 0) {
1422 			ep->d_entry = (char *)malloc(size + 1);
1423 			if (ep->d_entry == 0)
1424 				error(gettext(NO_MEM_ERR));
1425 
1426 			(void) strcpy(ep->d_entry, rp->d_name);
1427 		}
1428 		dp = (struct dir *)realloc((char *)dp,
1429 		    (nitems + 1) * sizeof (struct dir));
1430 		if (dp == 0)
1431 			error(gettext(NO_MEM_ERR));
1432 	}
1433 	dp[nitems].d_entry = 0;		/* delimiter */
1434 	(void) closedir(dirp);
1435 	qsort(dp, nitems, sizeof (struct dir),
1436 	    (int (*)(const void *, const void *))entcmp);
1437 	return (dp);
1438 }
1439 
1440 static int
1441 entcmp(struct dir *d1, struct dir *d2)
1442 {
1443 	return (strcmp(d1->d_entry, d2->d_entry));
1444 }
1445 
1446 static int
1447 compare(struct dir *dp)
1448 {
1449 	int i, j;
1450 	int f1 = -1, f2 = -1;
1451 	mode_t fmt1, fmt2;
1452 	struct stat stb1, stb2;
1453 	char buf1[BUFSIZ], buf2[BUFSIZ];
1454 	int result;
1455 
1456 	(void) strcpy(efile1, dp->d_entry);
1457 	(void) strcpy(efile2, dp->d_entry);
1458 
1459 	if (stat(file1, &stb1) == -1) {
1460 		(void) fprintf(stderr, "diff: ");
1461 		perror(file1);
1462 		return (2);
1463 	}
1464 	if (stat(file2, &stb2) == -1) {
1465 		(void) fprintf(stderr, "diff: ");
1466 		perror(file2);
1467 		return (2);
1468 	}
1469 
1470 	fmt1 = stb1.st_mode & S_IFMT;
1471 	fmt2 = stb2.st_mode & S_IFMT;
1472 
1473 	if (fmt1 == S_IFREG) {
1474 		f1 = open(file1, O_RDONLY);
1475 		if (f1 < 0) {
1476 			(void) fprintf(stderr, "diff: ");
1477 			perror(file1);
1478 			return (2);
1479 		}
1480 	}
1481 
1482 	if (fmt2 == S_IFREG) {
1483 		f2 = open(file2, O_RDONLY);
1484 		if (f2 < 0) {
1485 			(void) fprintf(stderr, "diff: ");
1486 			perror(file2);
1487 			(void) close(f1);
1488 			return (2);
1489 		}
1490 	}
1491 
1492 	if (fmt1 != S_IFREG || fmt2 != S_IFREG) {
1493 		if (fmt1 == fmt2) {
1494 			switch (fmt1) {
1495 
1496 			case S_IFDIR:
1497 				dp->d_flags = DIRECT;
1498 				if (lflag || opt == D_EDIT)
1499 					goto closem;
1500 				(void) printf(gettext(
1501 				    "Common subdirectories: %s and %s\n"),
1502 				    file1, file2);
1503 				goto closem;
1504 
1505 			case S_IFCHR:
1506 			case S_IFBLK:
1507 				if (stb1.st_rdev == stb2.st_rdev)
1508 					goto same;
1509 				(void) printf(gettext(
1510 				    "Special files %s and %s differ\n"),
1511 				    file1, file2);
1512 				break;
1513 
1514 			case S_IFLNK:
1515 				if ((i = readlink(file1, buf1, BUFSIZ)) == -1) {
1516 					(void) fprintf(stderr, gettext(
1517 					    "diff: cannot read link\n"));
1518 					return (2);
1519 				}
1520 
1521 				if ((j = readlink(file2, buf2, BUFSIZ)) == -1) {
1522 					(void) fprintf(stderr, gettext(
1523 					    "diff: cannot read link\n"));
1524 					return (2);
1525 				}
1526 
1527 				if (i == j) {
1528 					if (strncmp(buf1, buf2, i) == 0)
1529 						goto same;
1530 				}
1531 
1532 				(void) printf(gettext(
1533 				    "Symbolic links %s and %s differ\n"),
1534 				    file1, file2);
1535 				break;
1536 
1537 			case S_IFIFO:
1538 				if (stb1.st_ino == stb2.st_ino)
1539 					goto same;
1540 				(void) printf(gettext(
1541 				    "Named pipes %s and %s differ\n"),
1542 				    file1, file2);
1543 				break;
1544 			}
1545 		} else {
1546 			if (lflag)
1547 				dp->d_flags |= DIFFER;
1548 			else if (opt == D_NORMAL || opt == D_CONTEXT) {
1549 /*
1550  * TRANSLATION_NOTE
1551  * The second and fourth parameters will take the gettext'ed string
1552  * of one of the following:
1553  * a directory
1554  * a character special file
1555  * a block special file
1556  * a plain file
1557  * a named pipe
1558  * a socket
1559  * a door
1560  * an event port
1561  * an unknown type
1562  */
1563 				(void) printf(gettext(
1564 				    "File %s is %s while file %s is %s\n"),
1565 				    file1, pfiletype(fmt1),
1566 				    file2, pfiletype(fmt2));
1567 			}
1568 		}
1569 		(void) close(f1); (void) close(f2);
1570 		return (1);
1571 	}
1572 	if (stb1.st_size != stb2.st_size)
1573 		goto notsame;
1574 	for (;;) {
1575 		i = read(f1, buf1, BUFSIZ);
1576 		j = read(f2, buf2, BUFSIZ);
1577 		if (i < 0 || j < 0) {
1578 			(void) fprintf(stderr, "diff: ");
1579 			(void) fprintf(stderr, gettext("Error reading "));
1580 			perror(i < 0 ? file1: file2);
1581 			(void) close(f1); (void) close(f2);
1582 			return (2);
1583 		}
1584 		if (i != j)
1585 			goto notsame;
1586 		if (i == 0 && j == 0)
1587 			goto same;
1588 		for (j = 0; j < i; j++)
1589 			if (buf1[j] != buf2[j])
1590 				goto notsame;
1591 	}
1592 same:
1593 	if (sflag == 0)
1594 		goto closem;
1595 	if (lflag)
1596 		dp->d_flags = SAME;
1597 	else
1598 		(void) printf(gettext("Files %s and %s are identical\n"),
1599 		    file1, file2);
1600 
1601 closem:
1602 	(void) close(f1); (void) close(f2);
1603 	return (0);
1604 
1605 notsame:
1606 	if (binary(f1) || binary(f2)) {
1607 		if (lflag)
1608 			dp->d_flags |= DIFFER;
1609 		else if (opt == D_NORMAL || opt == D_CONTEXT)
1610 			(void) printf(
1611 			    gettext("Binary files %s and %s differ\n"),
1612 			    file1, file2);
1613 		(void) close(f1); (void) close(f2);
1614 		return (1);
1615 	}
1616 	(void) close(f1); (void) close(f2);
1617 	anychange = 1;
1618 	if (lflag) {
1619 		result = calldiff(title);
1620 	} else {
1621 		if (opt == D_EDIT)
1622 			(void) printf("ed - %s << '-*-END-*-'\n", dp->d_entry);
1623 		else
1624 			(void) printf("%s%s %s\n", title, file1, file2);
1625 		result = calldiff((char *)0);
1626 		if (opt == D_EDIT)
1627 			(void) printf("w\nq\n-*-END-*-\n");
1628 	}
1629 	return (result);
1630 }
1631 
1632 char	*prargs[] = { "pr", "-h", 0, 0, 0 };
1633 
1634 static int
1635 calldiff(char *wantpr)
1636 {
1637 	pid_t pid;
1638 	int diffstatus, pv[2];
1639 
1640 	prargs[2] = wantpr;
1641 	(void) fflush(stdout);
1642 	if (wantpr) {
1643 		(void) sprintf(etitle, "%s %s", file1, file2);
1644 		(void) pipe(pv);
1645 		pid = fork();
1646 		if (pid == (pid_t)-1)
1647 			error(gettext(NO_PROCS_ERR));
1648 
1649 		if (pid == 0) {
1650 			(void) close(0);
1651 			(void) dup(pv[0]);
1652 			(void) close(pv[0]);
1653 			(void) close(pv[1]);
1654 			(void) execv(pr+5, prargs);
1655 			(void) execv(pr, prargs);
1656 			perror(pr);
1657 			done();
1658 		}
1659 	}
1660 	pid = fork();
1661 	if (pid == (pid_t)-1)
1662 		error(gettext(NO_PROCS_ERR));
1663 
1664 	if (pid == 0) {
1665 		if (wantpr) {
1666 			(void) close(1);
1667 			(void) dup(pv[1]);
1668 			(void) close(pv[0]);
1669 			(void) close(pv[1]);
1670 		}
1671 		(void) execv(diff+5, diffargv);
1672 		(void) execv(diff, diffargv);
1673 		perror(diff);
1674 		done();
1675 	}
1676 	if (wantpr)	{
1677 		(void) close(pv[0]);
1678 		(void) close(pv[1]);
1679 	}
1680 	while (wait(&diffstatus) != pid)
1681 		continue;
1682 	while (wait((int *)0) != (pid_t)-1)
1683 		continue;
1684 	if ((diffstatus&0177) != 0)
1685 		return (2);
1686 	else
1687 		return ((diffstatus>>8) & 0377);
1688 }
1689 
1690 static char *
1691 pfiletype(mode_t fmt)
1692 {
1693 /*
1694  * TRANSLATION_NOTE
1695  * The following 9 messages will be used in the second and
1696  * the fourth parameters of the message
1697  * "File %s is %s while file %s is %s\n"
1698  */
1699 	switch (fmt) {
1700 
1701 	case S_IFDIR:
1702 		return (gettext("a directory"));
1703 		break;
1704 
1705 	case S_IFCHR:
1706 		return (gettext("a character special file"));
1707 		break;
1708 
1709 	case S_IFBLK:
1710 		return (gettext("a block special file"));
1711 		break;
1712 
1713 	case S_IFREG:
1714 		return (gettext("a plain file"));
1715 		break;
1716 
1717 	case S_IFIFO:
1718 		return (gettext("a named pipe"));
1719 		break;
1720 
1721 	case S_IFSOCK:
1722 		return (gettext("a socket"));
1723 		break;
1724 
1725 	case S_IFDOOR:
1726 		return (gettext("a door"));
1727 		break;
1728 
1729 	case S_IFPORT:
1730 		return (gettext("an event port"));
1731 		break;
1732 
1733 	default:
1734 		return (gettext("an unknown type"));
1735 		break;
1736 	}
1737 }
1738 
1739 static int
1740 binary(int f)
1741 {
1742 	char buf[BUFSIZ];
1743 	int cnt;
1744 
1745 	(void) lseek(f, (long)0, SEEK_SET);
1746 	cnt = read(f, buf, BUFSIZ);
1747 	if (cnt < 0)
1748 		return (1);
1749 	return (isbinary(buf, cnt));
1750 }
1751 
1752 static int
1753 filebinary(FILE *f)
1754 {
1755 	char buf[BUFSIZ];
1756 	int cnt;
1757 
1758 	(void) fseek(f, (long)0, SEEK_SET);
1759 	cnt = fread(buf, 1, BUFSIZ, f);
1760 	if (ferror(f))
1761 		return (1);
1762 	return (isbinary(buf, cnt));
1763 }
1764 
1765 
1766 /*
1767  * We consider a "binary" file to be one that:
1768  * contains a null character ("diff" doesn't handle them correctly, and
1769  *    neither do many other UNIX text-processing commands).
1770  * Characters with their 8th bit set do NOT make a file binary; they may be
1771  * legitimate text characters, or parts of same.
1772  */
1773 static int
1774 isbinary(char *buf, int cnt)
1775 {
1776 	char *cp;
1777 
1778 	cp = buf;
1779 	while (--cnt >= 0)
1780 		if (*cp++ == '\0')
1781 			return (1);
1782 	return (0);
1783 }
1784 
1785 
1786 /*
1787  * THIS IS CRUDE.
1788  */
1789 static int
1790 useless(char *cp)
1791 {
1792 
1793 	if (cp[0] == '.') {
1794 		if (cp[1] == '\0')
1795 			return (1);	/* directory "." */
1796 		if (cp[1] == '.' && cp[2] == '\0')
1797 			return (1);	/* directory ".." */
1798 	}
1799 	if (start && strcmp(start, cp) > 0)
1800 		return (1);
1801 	return (0);
1802 }
1803 
1804 
1805 void
1806 sort(struct line *a, int n)	/* shellsort CACM #201 */
1807 {
1808 	struct line w;
1809 	int j, m;
1810 	struct line *ai;
1811 	struct line *aim;
1812 	int k;
1813 
1814 	for (j = 1, m = 0; j <= n; j *= 2)
1815 		m = 2 * j - 1;
1816 	for (m /= 2; m != 0; m /= 2) {
1817 		k = n - m;
1818 		for (j = 1; j <= k; j++) {
1819 			for (ai = &a[j]; ai > a; ai -= m) {
1820 				aim = &ai[m];
1821 				if (aim < ai)
1822 					break;	/* wraparound */
1823 				if (aim->value > ai[0].value ||
1824 				    aim->value == ai[0].value &&
1825 				    aim->serial > ai[0].serial)
1826 					break;
1827 				w.value = ai[0].value;
1828 				ai[0].value = aim->value;
1829 				aim->value = w.value;
1830 				w.serial = ai[0].serial;
1831 				ai[0].serial = aim->serial;
1832 				aim->serial = w.serial;
1833 			}
1834 		}
1835 	}
1836 }
1837 
1838 static void
1839 unsort(struct line *f, int l, int *b)
1840 {
1841 	int *a;
1842 	int i;
1843 
1844 	a = (int *)talloc((l + 1) * sizeof (int));
1845 	for (i = 1; i <= l; i++)
1846 		a[f[i].serial] = f[i].value;
1847 	for (i = 1; i <= l; i++)
1848 		b[i] = a[i];
1849 	free((char *)a);
1850 }
1851 
1852 static void
1853 filename(char **pa1, char **pa2, struct stat *st, char **ifile)
1854 {
1855 	char *a1, *b1, *a2;
1856 
1857 	a1 = *pa1;
1858 	a2 = *pa2;
1859 
1860 	if (strcmp(*pa1, "-") == 0)
1861 		*ifile = strdup("-");
1862 	else
1863 		*ifile = strdup(*pa1);
1864 
1865 	if (*ifile == (char *)NULL) {
1866 		(void) fprintf(stderr, gettext(
1867 		    "no more memory - try again later\n"));
1868 		status = 2;
1869 		done();
1870 	}
1871 
1872 	if ((st->st_mode & S_IFMT) == S_IFDIR) {
1873 		b1 = *pa1 = (char *)malloc(PATH_MAX);
1874 		while (*b1++ = *a1++)
1875 			;
1876 		b1[-1] = '/';
1877 		a1 = b1;
1878 		while (*a1++ = *a2++)
1879 			if (*a2 && *a2 != '/' && a2[-1] == '/')
1880 				a1 = b1;
1881 		*ifile = strdup(*pa1);
1882 
1883 		if (*ifile == (char *)NULL) {
1884 			(void) fprintf(stderr, gettext(
1885 			    "no more memory - try again later\n"));
1886 			status = 2;
1887 			done();
1888 		}
1889 
1890 		if (stat(*pa1, st) < 0) {
1891 			(void) fprintf(stderr, "diff: ");
1892 			perror(*pa1);
1893 			done();
1894 		}
1895 	} else if ((st->st_mode & S_IFMT) == S_IFCHR)
1896 		*pa1 = copytemp(a1);
1897 	else if (a1[0] == '-' && a1[1] == 0) {
1898 		*pa1 = copytemp(a1);	/* hack! */
1899 		if (stat(*pa1, st) < 0) {
1900 			(void) fprintf(stderr, "diff: ");
1901 			perror(*pa1);
1902 			done();
1903 		}
1904 	}
1905 }
1906 
1907 static char *
1908 copytemp(char *fn)
1909 {
1910 	int ifd, ofd;	/* input and output file descriptors */
1911 	int i;
1912 	char template[13];	/* template for temp file name */
1913 	char buf[BUFSIZ];
1914 
1915 	/*
1916 	 * a "-" file is interpreted as fd 0 for pre-/dev/fd systems
1917 	 * ... let's hope this goes away soon!
1918 	 */
1919 	if ((ifd = (strcmp(fn, "-") ? open(fn, 0) : 0)) < 0) {
1920 		(void) fprintf(stderr, "diff: ");
1921 		(void) fprintf(stderr, gettext("cannot open %s\n"), fn);
1922 		done();
1923 	}
1924 	(void) signal(SIGHUP, (void (*)(int))done);
1925 	(void) signal(SIGINT, (void (*)(int))done);
1926 	(void) signal(SIGPIPE, (void (*)(int))done);
1927 	(void) signal(SIGTERM, (void (*)(int))done);
1928 	(void) strcpy(template, "/tmp/dXXXXXX");
1929 	if ((ofd = mkstemp(template)) < 0) {
1930 		(void) fprintf(stderr, "diff: ");
1931 		(void) fprintf(stderr, gettext("cannot create %s\n"), template);
1932 		done();
1933 	}
1934 	(void) strcpy(tempfile[whichtemp++], template);
1935 	while ((i = read(ifd, buf, BUFSIZ)) > 0)
1936 		if (write(ofd, buf, i) != i) {
1937 			(void) fprintf(stderr, "diff: ");
1938 			(void) fprintf(stderr,
1939 			    gettext("write failed %s\n"), template);
1940 			done();
1941 		}
1942 	(void) close(ifd); (void) close(ofd);
1943 	return (tempfile[whichtemp-1]);
1944 }
1945 
1946 static void
1947 prepare(int i, char *arg)
1948 {
1949 	struct line *p;
1950 	int j, h;
1951 
1952 	(void) fseek(input[i], (long)0, SEEK_SET);
1953 	p = (struct line *)talloc(3 * sizeof (line));
1954 	for (j = 0; h = readhash(input[i], i, arg); ) {
1955 		p = (struct line *)ralloc((void *)p, (++j + 3) * sizeof (line));
1956 		p[j].value = h;
1957 	}
1958 	len[i] = j;
1959 	file[i] = p;
1960 }
1961 
1962 static void
1963 prune(void)
1964 {
1965 	int i, j;
1966 
1967 	for (pref = 0; pref < len[0] && pref < len[1] &&
1968 	    file[0][pref + 1].value == file[1][pref + 1].value;
1969 	    pref++)
1970 		;
1971 	for (suff = 0; (suff < len[0] - pref) &&
1972 	    (suff < len[1] - pref) &&
1973 	    (file[0][len[0] - suff].value == file[1][len[1] - suff].value);
1974 	    suff++)
1975 		;
1976 
1977 	/* decremnt suff by 2 iff suff >= 2, ensure that suff is never < 0 */
1978 	if (suff >= 2)
1979 		suff -= 2;
1980 
1981 	for (j = 0; j < 2; j++) {
1982 		sfile[j] = file[j] + pref;
1983 		slen[j] = len[j] - pref - suff;
1984 		for (i = 0; i <= slen[j]; i++)
1985 			sfile[j][i].serial = i;
1986 	}
1987 }
1988 
1989 static void
1990 equiv(struct line *a, int n, struct line *b, int m, int *c)
1991 {
1992 	int i, j;
1993 	i = j = 1;
1994 	while (i <= n && j <= m) {
1995 		if (a[i].value < b[j].value)
1996 			a[i++].value = 0;
1997 		else if (a[i].value == b[j].value)
1998 			a[i++].value = j;
1999 		else
2000 			j++;
2001 	}
2002 	while (i <= n)
2003 		a[i++].value = 0;
2004 	b[m+1].value = 0;	j = 0;
2005 	while (++j <= m) {
2006 		c[j] = -b[j].serial;
2007 		while (b[j + 1].value == b[j].value) {
2008 			j++;
2009 			c[j] = b[j].serial;
2010 		}
2011 	}
2012 	c[j] = -1;
2013 }
2014 
2015 static void
2016 done(void)
2017 {
2018 	if (whichtemp) (void) unlink(tempfile[0]);
2019 	if (whichtemp == 2) (void) unlink(tempfile[1]);
2020 	exit(status);
2021 }
2022 
2023 static void
2024 noroom(void)
2025 {
2026 	(void) fprintf(stderr, "diff: ");
2027 	(void) fprintf(stderr, gettext("files too big, try -h\n"));
2028 	done();
2029 }
2030 
2031 static void
2032 error(const char *s)
2033 {
2034 	(void) fprintf(stderr, "diff: ");
2035 	(void) fprintf(stderr, s);
2036 	(void) fprintf(stderr, "\n");
2037 	done();
2038 }
2039 
2040 static void
2041 usage(void)
2042 {
2043 	(void) fprintf(stderr, gettext(
2044 	    "usage: diff [-bitw] [-c | -e | -f | -h | -n | -u] file1 "
2045 	    "file2\n"
2046 	    "       diff [-bitw] [-C number | -U number] file1 file2\n"
2047 	    "       diff [-bitw] [-D string] file1 file2\n"
2048 	    "       diff [-bitw] [-c | -e | -f | -h | -n | -u] [-l] [-r] "
2049 	    "[-s] [-S name] directory1 directory2\n"));
2050 	status = 2;
2051 	done();
2052 }
2053 
2054 #define	NW	1024
2055 struct buff	{
2056 	FILE	*iop;	/* I/O stream */
2057 	char	buf[NW + MB_LEN_MAX];	/* buffer */
2058 	char	*ptr;	/* current pointer in the buffer */
2059 	int	buffered;	/* if non-zero, buffer has data */
2060 	long	offset;	/* offset in the file */
2061 };
2062 
2063 static struct buff bufwchar[2];
2064 
2065 /*
2066  *	Initializes the buff structure for specified
2067  *	I/O stream.  Also sets the specified offset
2068  */
2069 static void
2070 initbuf(FILE *iop, int filen, long offset)
2071 {
2072 	bufwchar[filen].iop = iop;
2073 	bufwchar[filen].ptr = NULL;
2074 	bufwchar[filen].buffered = 0;
2075 	bufwchar[filen].offset = offset;
2076 }
2077 
2078 /*
2079  *	Reset a buff structure, and rewind the associated file.
2080  */
2081 static void
2082 resetbuf(int filen)
2083 {
2084 	bufwchar[filen].ptr = NULL;
2085 	bufwchar[filen].buffered = bufwchar[filen].offset = 0;
2086 	rewind(bufwchar[filen].iop);
2087 }
2088 
2089 
2090 /*
2091  *	Returns the current offset in the file
2092  */
2093 static long
2094 ftellbuf(int filen)
2095 {
2096 	return (bufwchar[filen].offset);
2097 }
2098 
2099 static wint_t
2100 wcput(wint_t wc)
2101 {
2102 	char	mbs[MB_LEN_MAX];
2103 	unsigned char	*p;
2104 	int	n;
2105 
2106 	n = wctomb(mbs, (wchar_t)wc);
2107 	if (n > 0) {
2108 		p = (unsigned char *)mbs;
2109 		while (n--) {
2110 			(void) putc((*p++), stdout);
2111 		}
2112 		return (wc);
2113 	} else if (n < 0) {
2114 		(void) putc((int)(wc & 0xff), stdout);
2115 		return (wc & 0xff);
2116 	} else {
2117 		/* this should not happen */
2118 		return (WEOF);
2119 	}
2120 }
2121 
2122 /*
2123  *	Reads one wide-character from the file associated with filen.
2124  *	If multibyte locales, the input is buffered.
2125  *
2126  *	Input:	filen	the file number (0 or 1)
2127  *	Output:	*len	number of bytes to make wide-character
2128  *	Return:			wide-character
2129  */
2130 static wint_t
2131 getbufwchar(int filen, int *len)
2132 {
2133 
2134 	int	i, num, clen;
2135 	wchar_t	wc;
2136 	size_t	mxlen;
2137 
2138 	if (mbcurmax == 1) {
2139 		/* If sigle byte locale, use getc() */
2140 		int	ch;
2141 
2142 		ch = getc(bufwchar[filen].iop);
2143 		bufwchar[filen].offset++;
2144 		*len = 1;
2145 
2146 		if (isascii(ch) || (ch == EOF)) {
2147 			return ((wint_t)ch);
2148 		} else {
2149 			wchar_t	wc;
2150 			char	str[2] = {0, 0};
2151 
2152 			str[0] = (char)ch;
2153 			if (mbtowc(&wc, str, 1) > 0) {
2154 				return ((wint_t)wc);
2155 			} else {
2156 				return ((wint_t)ch);
2157 			}
2158 		}
2159 	} else {
2160 		mxlen = mbcurmax;
2161 	}
2162 
2163 	if (bufwchar[filen].buffered == 0) {
2164 		/* Not buffered */
2165 		bufwchar[filen].ptr = &(bufwchar[filen].buf[MB_LEN_MAX]);
2166 		num = fread((void *)bufwchar[filen].ptr,
2167 		    sizeof (char), NW, bufwchar[filen].iop);
2168 		if (ferror(bufwchar[filen].iop)) {
2169 			(void) fprintf(stderr, "diff: ");
2170 			(void) fprintf(stderr, gettext("Error reading "));
2171 			perror((filen == 0) ? file1 : file2);
2172 			status = 2;
2173 			done();
2174 		}
2175 		if (num == 0)
2176 			return (WEOF);
2177 		bufwchar[filen].buffered = num;
2178 	}
2179 
2180 	if (bufwchar[filen].buffered < mbcurmax) {
2181 		for (i = 0; i < bufwchar[filen].buffered; i++) {
2182 			bufwchar[filen].buf[MB_LEN_MAX -
2183 			    (bufwchar[filen].buffered - i)] =
2184 			    *(bufwchar[filen].ptr + i);
2185 		}
2186 		bufwchar[filen].ptr = &(bufwchar[filen].buf[MB_LEN_MAX]);
2187 		num = fread((void *)bufwchar[filen].ptr,
2188 		    sizeof (char), NW, bufwchar[filen].iop);
2189 		if (ferror(bufwchar[filen].iop)) {
2190 			(void) fprintf(stderr, "diff: ");
2191 			(void) fprintf(stderr, gettext("Error reading "));
2192 			perror((filen == 0) ? file1 : file2);
2193 			status = 2;
2194 			done();
2195 		}
2196 		bufwchar[filen].ptr = &(bufwchar[filen].buf[MB_LEN_MAX -
2197 		    bufwchar[filen].buffered]);
2198 		bufwchar[filen].buffered += num;
2199 		if (bufwchar[filen].buffered < mbcurmax) {
2200 			mxlen = bufwchar[filen].buffered;
2201 		}
2202 	}
2203 
2204 	clen = mbtowc(&wc, bufwchar[filen].ptr, mxlen);
2205 	if (clen <= 0) {
2206 		(bufwchar[filen].buffered)--;
2207 		*len = 1;
2208 		(bufwchar[filen].offset)++;
2209 		wc = (wchar_t)((unsigned char)*bufwchar[filen].ptr++);
2210 		return ((wint_t)wc);
2211 	} else {
2212 		bufwchar[filen].buffered -= clen;
2213 		bufwchar[filen].ptr += clen;
2214 		bufwchar[filen].offset += clen;
2215 		*len = clen;
2216 		return ((wint_t)wc);
2217 	}
2218 }
2219