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