xref: /illumos-gate/usr/src/cmd/diff/diff.c (revision 78eb75ca)
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 #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 int
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 	return (0);
504 }
505 
506 static int
507 stone(int *a, int n, int *b, int *c)
508 {
509 	int i, k, y;
510 	int j, l;
511 	int oldc, tc;
512 	int oldl;
513 
514 	k = 0;
515 	c[0] = newcand(0, 0, 0);
516 	for (i = 1; i <= n; i++) {
517 		j = a[i];
518 		if (j == 0)
519 			continue;
520 		y = -b[j];
521 		oldl = 0;
522 		oldc = c[0];
523 		do {
524 			if (y <= clist[oldc].y)
525 				continue;
526 			l = search(c, k, y);
527 			if (l != oldl+1)
528 				oldc = c[l-1];
529 			if (l <= k) {
530 				if (clist[c[l]].y <= y)
531 					continue;
532 				tc = c[l];
533 				c[l] = newcand(i, y, oldc);
534 				oldc = tc;
535 				oldl = l;
536 			} else {
537 				c[l] = newcand(i, y, oldc);
538 				k++;
539 				break;
540 			}
541 		} while ((y = b[++j]) > 0);
542 	}
543 	return (k);
544 }
545 
546 static int
547 newcand(int x, int y, int pred)
548 {
549 	struct cand *q;
550 
551 	clist = (struct cand *)ralloc((void *)clist, ++clen * sizeof (cand));
552 	q = clist + clen -1;
553 	q->x = x;
554 	q->y = y;
555 	q->pred = pred;
556 	return (clen - 1);
557 }
558 
559 static int
560 search(int *c, int k, int y)
561 {
562 	int i, j, l;
563 	int t;
564 
565 	if (clist[c[k]].y < y)	/* quick look for typical case */
566 		return (k + 1);
567 	i = 0;
568 	j = k+1;
569 	while ((l = (i + j) / 2) > i) {
570 		t = clist[c[l]].y;
571 		if (t > y)
572 			j = l;
573 		else if (t < y)
574 			i = l;
575 		else
576 			return (l);
577 	}
578 	return (l + 1);
579 }
580 
581 static void
582 unravel(int p)
583 {
584 	int i;
585 	struct cand *q;
586 
587 	for (i = 0; i <= len[0]; i++)
588 		J[i] = i <= pref ? i :
589 			i > len[0] - suff ? i + len[1] - len[0]:
590 			0;
591 	for (q = clist + p; q->y != 0; q = clist + q->pred)
592 		J[q->x + pref] = q->y + pref;
593 }
594 
595 /*
596  * check does double duty:
597  * 1. ferret out any fortuitous correspondences due to confounding by
598  * hashing (which result in "jackpot")
599  * 2. collect random access indexes to the two files
600  */
601 
602 static void
603 check(void)
604 {
605 	wint_t	c, d;
606 	int i, j;
607 	/* int jackpot; */
608 	int	mlen;
609 	long ctold, ctnew;
610 
611 	resetbuf(0);
612 	resetbuf(1);
613 
614 	j = 1;
615 	ixold[0] = ixnew[0] = 0;
616 	/* jackpot = 0; */
617 
618 	/*
619 	 * ctold and ctnew are byte positions within the file (suitable for
620 	 * lseek()).  After we get a character with getwc(), instead of
621 	 * just incrementing the byte position by 1, we have to determine
622 	 * how many bytes the character actually is.  This is the reason for
623 	 * the wctomb() calls here and in skipline().
624 	 */
625 	ctold = ctnew = 0;
626 	for (i = 1; i <= len[0]; i++) {
627 		if (J[i] == 0) {
628 			ixold[i] = ctold += skipline(0);
629 			continue;
630 		}
631 		while (j < J[i]) {
632 			ixnew[j] = ctnew += skipline(1);
633 			j++;
634 		}
635 		if (bflag || wflag || iflag) {
636 			for (;;) {
637 				c = getbufwchar(0, &mlen);
638 				ctold += mlen;
639 				d = getbufwchar(1, &mlen);
640 				ctnew += mlen;
641 
642 				if (bflag && iswspace(c) && iswspace(d)) {
643 					while (iswspace(c)) {
644 						if (c == '\n' || c == WEOF)
645 							break;
646 
647 						c = getbufwchar(0, &mlen);
648 						ctold += mlen;
649 					}
650 					while (iswspace(d)) {
651 						if (d == '\n' || d == WEOF)
652 							break;
653 
654 						d = getbufwchar(1, &mlen);
655 						ctnew += mlen;
656 					}
657 				} else if (wflag) {
658 					while (iswspace(c) && c != '\n') {
659 						c = getbufwchar(0, &mlen);
660 						ctold += mlen;
661 					}
662 					while (iswspace(d) && d != '\n') {
663 						d = getbufwchar(1, &mlen);
664 						ctnew += mlen;
665 					}
666 				}
667 				if (c == WEOF || d == WEOF) {
668 					if (c != d) {
669 						/* jackpot++; */
670 						J[i] = 0;
671 						if (c != '\n' && c != WEOF)
672 							ctold += skipline(0);
673 						if (d != '\n' && d != WEOF)
674 							ctnew += skipline(1);
675 						break;
676 					}
677 					break;
678 				} else {
679 					if (CHRTRAN(c) != CHRTRAN(d)) {
680 						/* jackpot++; */
681 						J[i] = 0;
682 						if (c != '\n')
683 							ctold += skipline(0);
684 						if (d != '\n')
685 							ctnew += skipline(1);
686 						break;
687 					}
688 					if (c == '\n')
689 						break;
690 				}
691 			}
692 		} else {
693 			for (;;) {
694 				c = getbufwchar(0, &mlen);
695 				ctold += mlen;
696 				d = getbufwchar(1, &mlen);
697 				ctnew += mlen;
698 				if (c != d) {
699 					/* jackpot++; */
700 					J[i] = 0;
701 					if (c != '\n' && c != WEOF)
702 						ctold += skipline(0);
703 					if (d != '\n' && d != WEOF)
704 						ctnew += skipline(1);
705 					break;
706 				}
707 				if (c == '\n' || c == WEOF)
708 					break;
709 			}
710 		}
711 		ixold[i] = ctold;
712 		ixnew[j] = ctnew;
713 		j++;
714 	}
715 	for (; j <= len[1]; j++) {
716 		ixnew[j] = ctnew += skipline(1);
717 	}
718 
719 /*	if(jackpot)			*/
720 /*		fprintf(stderr, "diff: jackpot\n");	*/
721 }
722 
723 static int
724 skipline(int f)
725 {
726 	int i;
727 	wint_t c;
728 	int	mlen;
729 
730 	for (i = 1; c = getbufwchar(f, &mlen); ) {
731 		if (c == '\n' || c == WEOF)
732 			return (i);
733 		i += mlen;
734 	}
735 	return (i);
736 }
737 
738 static void
739 output(void)
740 {
741 	int m;
742 	wint_t	wc;
743 	int i0, i1, j1;
744 	int j0;
745 	int	mlen;
746 
747 	resetbuf(0);
748 	resetbuf(1);
749 
750 	m = len[0];
751 	J[0] = 0;
752 	J[m + 1] = len[1] + 1;
753 	if (opt != D_EDIT)
754 		for (i0 = 1; i0 <= m; i0 = i1+1) {
755 			while (i0 <= m && J[i0] == J[i0 - 1] + 1)
756 				i0++;
757 			j0 = J[i0 - 1] + 1;
758 			i1 = i0 - 1;
759 			while (i1 < m && J[i1 + 1] == 0)
760 				i1++;
761 			j1 = J[i1 + 1] - 1;
762 			J[i1] = j1;
763 			change(i0, i1, j0, j1);
764 		} else for (i0 = m; i0 >= 1; i0 = i1 - 1) {
765 			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
766 				i0--;
767 			j0 = J[i0 + 1] - 1;
768 			i1 = i0 + 1;
769 			while (i1 > 1 && J[i1 - 1] == 0)
770 				i1--;
771 			j1 = J[i1 - 1] + 1;
772 			J[i1] = j1;
773 			change(i1, i0, j1, j0);
774 		}
775 	if (m == 0)
776 		change(1, 0, 1, len[1]);
777 	if (opt == D_IFDEF) {
778 		for (;;) {
779 			wc = getbufwchar(0, &mlen);
780 			if (wc == WEOF)
781 				return;
782 			(void) wcput(wc);
783 		}
784 	}
785 	if (anychange && opt == D_CONTEXT)
786 		dump_context_vec();
787 }
788 
789 
790 /*
791  * indicate that there is a difference between lines a and b of the from file
792  * to get to lines c to d of the to file.
793  * If a is greater then b then there are no lines in the from file involved
794  * and this means that there were lines appended (beginning at b).
795  * If c is greater than d then there are lines missing from the to file.
796  */
797 static void
798 change(int a, int b, int c, int d)
799 {
800 	char	time_buf[BUFSIZ];
801 	char	*dcmsg;
802 
803 	if (opt != D_IFDEF && a > b && c > d)
804 		return;
805 	if (anychange == 0) {
806 		anychange = 1;
807 		if (opt == D_CONTEXT) {
808 			/*
809 			 * TRANSLATION_NOTE_FOR_DC
810 			 * This message is the format of file
811 			 * timestamps written with the -C and
812 			 * -c options.
813 			 * %a -- locale's abbreviated weekday name
814 			 * %b -- locale's abbreviated month name
815 			 * %e -- day of month [1,31]
816 			 * %T -- Time as %H:%M:%S
817 			 * %Y -- Year, including the century
818 			 */
819 			dcmsg = dcgettext(NULL, "%a %b %e %T %Y", LC_TIME);
820 			(void) cftime(time_buf, dcmsg, &stb1.st_mtime);
821 			if (uflag)
822 				(void) printf("--- %s	%s\n", input_file1,
823 				    time_buf);
824 			else
825 				(void) printf("*** %s	%s\n", input_file1,
826 				    time_buf);
827 			(void) cftime(time_buf, dcmsg, &stb2.st_mtime);
828 			if (uflag)
829 				(void) printf("+++ %s	%s\n", input_file2,
830 				    time_buf);
831 			else
832 				(void) printf("--- %s	%s\n", input_file2,
833 				    time_buf);
834 
835 			context_vec_start = (struct context_vec *)
836 					    malloc(MAX_CONTEXT *
837 					    sizeof (struct context_vec));
838 			if (context_vec_start == NULL)
839 				error(gettext(NO_MEM_ERR));
840 
841 			context_vec_end = context_vec_start + (MAX_CONTEXT - 1);
842 			context_vec_ptr = context_vec_start - 1;
843 		}
844 	}
845 
846 	if (opt == D_CONTEXT) {
847 		/*
848 		 * if this new change is within 'context' lines of
849 		 * the previous change, just add it to the change
850 		 * record.  If the record is full or if this
851 		 * change is more than 'context' lines from the previous
852 		 * change, dump the record, reset it & add the new change.
853 		 */
854 		if (context_vec_ptr >= context_vec_end ||
855 		    (context_vec_ptr >= context_vec_start &&
856 		    a > (context_vec_ptr->b + 2 * context) &&
857 		    c > (context_vec_ptr->d + 2 * context)))
858 			dump_context_vec();
859 
860 		context_vec_ptr++;
861 		context_vec_ptr->a = a;
862 		context_vec_ptr->b = b;
863 		context_vec_ptr->c = c;
864 		context_vec_ptr->d = d;
865 		return;
866 	}
867 
868 	switch (opt) {
869 	case D_NORMAL:
870 	case D_EDIT:
871 		range(a, b, ",");
872 		(void) putchar(a > b ? 'a' : c > d ? 'd' : 'c');
873 		if (opt == D_NORMAL) range(c, d, ",");
874 		(void) printf("\n");
875 		break;
876 	case D_REVERSE:
877 		(void) putchar(a > b ? 'a' : c > d ? 'd' : 'c');
878 		range(a, b, " ");
879 		(void) printf("\n");
880 		break;
881 	case D_NREVERSE:
882 		if (a > b)
883 			(void) printf("a%d %d\n", b, d - c + 1);
884 		else {
885 			(void) printf("d%d %d\n", a, b - a + 1);
886 			if (!(c > d))
887 				/* add changed lines */
888 				(void) printf("a%d %d\n", b, d - c + 1);
889 		}
890 		break;
891 	}
892 	if (opt == D_NORMAL || opt == D_IFDEF) {
893 		fetch(ixold, a, b, 0, "< ", 1);
894 		if (a <= b && c <= d && opt == D_NORMAL)
895 			(void) prints("---\n");
896 	}
897 	fetch(ixnew, c, d, 1, opt == D_NORMAL?"> ":empty, 0);
898 	if ((opt == D_EDIT || opt == D_REVERSE) && c <= d)
899 		(void) prints(".\n");
900 	if (inifdef) {
901 		(void) fprintf(stdout, "#endif /* %s */\n", endifname);
902 		inifdef = 0;
903 	}
904 }
905 
906 static void
907 range(int a, int b, char *separator)
908 {
909 	(void) printf("%d", a > b ? b : a);
910 	if (a < b) {
911 		(void) printf("%s%d", separator, b);
912 	}
913 }
914 
915 static void
916 fetch(long *f, int a, int b, int filen, char *s, int oldfile)
917 {
918 	int i;
919 	int col;
920 	int nc;
921 	int mlen = 0;
922 	wint_t	ch;
923 	FILE	*lb;
924 
925 	lb = input[filen];
926 	/*
927 	 * When doing #ifdef's, copy down to current line
928 	 * if this is the first file, so that stuff makes it to output.
929 	 */
930 	if (opt == D_IFDEF && oldfile) {
931 		long curpos = ftellbuf(filen);
932 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
933 		nc = f[(a > b) ? b : (a - 1) ] - curpos;
934 		for (i = 0; i < nc; i += mlen) {
935 			ch = getbufwchar(filen, &mlen);
936 			if (ch == WEOF) {
937 				(void) putchar('\n');
938 				break;
939 			} else {
940 				(void) wcput(ch);
941 			}
942 		}
943 	}
944 	if (a > b)
945 		return;
946 	if (opt == D_IFDEF) {
947 		int oneflag = (*ifdef1 != '\0') != (*ifdef2 != '\0');
948 		if (inifdef)
949 			(void) fprintf(stdout, "#else /* %s%s */\n",
950 			    oneflag && oldfile == 1 ? "!" : "", ifdef2);
951 		else {
952 			if (oneflag) {
953 				/* There was only one ifdef given */
954 				endifname = ifdef2;
955 				if (oldfile)
956 					(void) fprintf(stdout,
957 					    "#ifndef %s\n", endifname);
958 				else
959 					(void) fprintf(stdout,
960 					    "#ifdef %s\n", endifname);
961 			} else {
962 				endifname = oldfile ? ifdef1 : ifdef2;
963 				(void) fprintf(stdout,
964 					"#ifdef %s\n", endifname);
965 			}
966 		}
967 		inifdef = 1 + oldfile;
968 	}
969 
970 	for (i = a; i <= b; i++) {
971 		(void) fseek(lb, f[i - 1], SEEK_SET);
972 		initbuf(lb, filen, f[i - 1]);
973 		if (opt != D_IFDEF)
974 			(void) prints(s);
975 		col = 0;
976 		while (ch = getbufwchar(filen, &mlen)) {
977 			if (ch != '\n' && ch != WEOF) {
978 				if (ch == '\t' && tflag)
979 					do
980 						(void) putchar(' ');
981 					while (++col & 7);
982 				else {
983 					(void) wcput(ch);
984 					col++;
985 				}
986 			} else
987 				break;
988 		}
989 		(void) putchar('\n');
990 	}
991 }
992 
993 /*
994  * hashing has the effect of
995  * arranging line in 7-bit bytes and then
996  * summing 1-s complement in 16-bit hunks
997  */
998 
999 static int
1000 readhash(FILE *f, int filen, char *str)
1001 {
1002 	long sum;
1003 	unsigned int	shift;
1004 	int space;
1005 	int t;
1006 	wint_t	wt;
1007 	int	mlen;
1008 
1009 	sum = 1;
1010 	space = 0;
1011 	if (!bflag && !wflag) {
1012 		if (iflag)
1013 			if (mbcurmax == 1) {
1014 				/* In this case, diff doesn't have to take */
1015 				/* care of multibyte characters. */
1016 				for (shift = 0; (t = getc(f)) != '\n';
1017 					shift += 7) {
1018 					if (t == EOF) {
1019 						if (shift) {
1020 							(void) fprintf(stderr,
1021 	gettext("Warning: missing newline at end of file %s\n"), str);
1022 							break;
1023 						} else
1024 							return (0);
1025 					}
1026 					sum += (isupper(t) ? tolower(t) : t) <<
1027 						(shift &= HALFMASK);
1028 				}
1029 			} else {
1030 				/* In this case, diff needs to take care of */
1031 				/* multibyte characters. */
1032 				for (shift = 0;
1033 				(wt = getbufwchar(filen, &mlen)) != '\n';
1034 					shift += 7) {
1035 					if (wt == WEOF) {
1036 						if (shift) {
1037 							(void) fprintf(stderr,
1038 	gettext("Warning: missing newline at end of file %s\n"), str);
1039 							break;
1040 						} else
1041 							return (0);
1042 					}
1043 					sum += NCCHRTRAN(wt) <<
1044 						(shift &= HALFMASK);
1045 				}
1046 			}
1047 		else
1048 			/* In this case, diff doesn't have to take care of */
1049 			/* multibyte characters. */
1050 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
1051 				if (t == EOF) {
1052 					if (shift) {
1053 						(void) fprintf(stderr,
1054 	gettext("Warning: missing newline at end of file %s\n"), str);
1055 						break;
1056 					} else
1057 						return (0);
1058 				}
1059 				sum += (long)t << (shift &= HALFMASK);
1060 			}
1061 	} else {
1062 		/* In this case, diff needs to take care of */
1063 		/* multibyte characters. */
1064 		for (shift = 0; ; ) {
1065 			wt = getbufwchar(filen, &mlen);
1066 
1067 			if (wt != '\n' && iswspace(wt)) {
1068 				space++;
1069 				continue;
1070 			} else {
1071 				switch (wt) {
1072 				case WEOF:
1073 					if (shift) {
1074 						(void) fprintf(stderr,
1075 	gettext("Warning: missing newline at end of file %s\n"), str);
1076 						break;
1077 					} else
1078 						return (0);
1079 				default:
1080 					if (space && !wflag) {
1081 						shift += 7;
1082 						space = 0;
1083 					}
1084 					sum += CHRTRAN(wt) <<
1085 						(shift &= HALFMASK);
1086 					shift += 7;
1087 					continue;
1088 				case L'\n':
1089 					break;
1090 				}
1091 			}
1092 			break;
1093 		}
1094 	}
1095 	return (sum);
1096 }
1097 
1098 
1099 /* dump accumulated "context" diff changes */
1100 static void
1101 dump_context_vec(void)
1102 {
1103 	int	a, b, c, d;
1104 	char	ch;
1105 	struct	context_vec *cvp = context_vec_start;
1106 	int	lowa, upb, lowc, upd;
1107 	int	do_output;
1108 
1109 	if (cvp > context_vec_ptr)
1110 		return;
1111 
1112 	lowa = max(1, cvp->a - context);
1113 	upb  = min(len[0], context_vec_ptr->b + context);
1114 	lowc = max(1, cvp->c - context);
1115 	upd  = min(len[1], context_vec_ptr->d + context);
1116 
1117 	if (uflag) {
1118 		(void) printf("@@ -%d,%d +%d,%d @@\n",
1119 		    lowa, upb - lowa + 1,
1120 		    lowc, upd - lowc + 1);
1121 	} else {
1122 		(void) printf("***************\n*** ");
1123 		range(lowa, upb, ",");
1124 		(void) printf(" ****\n");
1125 	}
1126 
1127 	/*
1128 	 * output changes to the "old" file.  The first loop suppresses
1129 	 * output if there were no changes to the "old" file (we'll see
1130 	 * the "old" lines as context in the "new" list).
1131 	 */
1132 	if (uflag)
1133 		do_output = 1;
1134 	else
1135 		for (do_output = 0; cvp <= context_vec_ptr; cvp++)
1136 			if (cvp->a <= cvp->b) {
1137 				cvp = context_vec_start;
1138 				do_output++;
1139 				break;
1140 			}
1141 
1142 	if (do_output) {
1143 		while (cvp <= context_vec_ptr) {
1144 			a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d;
1145 
1146 			if (a <= b && c <= d)
1147 				ch = 'c';
1148 			else
1149 				ch = (a <= b) ? 'd' : 'a';
1150 
1151 			if (ch == 'a') {
1152 				/* The last argument should not affect */
1153 				/* the behavior of fetch() */
1154 				fetch(ixold, lowa, b, 0, uflag ? " " : "  ", 1);
1155 				if (uflag)
1156 					fetch(ixnew, c, d, 1, "+", 0);
1157 			} else if (ch == 'd') {
1158 				fetch(ixold, lowa, a - 1, 0, uflag ? " " :
1159 					    "  ", 1);
1160 				fetch(ixold, a, b, 0, uflag ? "-" : "- ", 1);
1161 			} else {
1162 				/* The last argument should not affect */
1163 				/* the behavior of fetch() */
1164 				fetch(ixold, lowa, a-1, 0, uflag ? " " : "  ",
1165 				    1);
1166 				if (uflag) {
1167 					fetch(ixold, a, b, 0, "-", 1);
1168 					fetch(ixnew, c, d, 1, "+", 0);
1169 				} else
1170 					fetch(ixold, a, b, 0, "! ", 1);
1171 			}
1172 			lowa = b + 1;
1173 			cvp++;
1174 		}
1175 		/* The last argument should not affect the behavior */
1176 		/* of fetch() */
1177 		fetch(ixold, b+1, upb, 0, uflag ? " " : "  ", 1);
1178 	}
1179 
1180 	if (uflag) {
1181 		context_vec_ptr = context_vec_start - 1;
1182 		return;
1183 	}
1184 
1185 	/* output changes to the "new" file */
1186 	(void) printf("--- ");
1187 	range(lowc, upd, ",");
1188 	(void) printf(" ----\n");
1189 
1190 	do_output = 0;
1191 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1192 		if (cvp->c <= cvp->d) {
1193 			cvp = context_vec_start;
1194 			do_output++;
1195 			break;
1196 		}
1197 
1198 	if (do_output) {
1199 		while (cvp <= context_vec_ptr) {
1200 			a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d;
1201 
1202 			if (a <= b && c <= d)
1203 				ch = 'c';
1204 			else
1205 				ch = (a <= b) ? 'd' : 'a';
1206 
1207 			if (ch == 'd')
1208 				/* The last argument should not affect */
1209 				/* the behavior of fetch() */
1210 				fetch(ixnew, lowc, d, 1, "  ", 0);
1211 			else {
1212 				/* The last argument should not affect */
1213 				/* the behavior of fetch() */
1214 				fetch(ixnew, lowc, c - 1, 1, "  ", 0);
1215 				fetch(ixnew, c, d, 1,
1216 				    ch == 'c' ? "! " : "+ ", 0);
1217 			}
1218 			lowc = d + 1;
1219 			cvp++;
1220 		}
1221 		/* The last argument should not affect the behavior */
1222 		/* of fetch() */
1223 		fetch(ixnew, d + 1, upd, 1, "  ", 0);
1224 	}
1225 	context_vec_ptr = context_vec_start - 1;
1226 }
1227 
1228 
1229 
1230 /*
1231  * diff - directory comparison
1232  */
1233 
1234 struct	dir *setupdir();
1235 int	header;
1236 char	title[2 * BUFSIZ], *etitle;
1237 
1238 static void
1239 diffdir(char **argv)
1240 {
1241 	struct dir *d1, *d2;
1242 	struct dir *dir1, *dir2;
1243 	int i;
1244 	int cmp;
1245 	int result, dirstatus;
1246 
1247 	if (opt == D_IFDEF)
1248 		error(gettext("cannot specify -D with directories"));
1249 
1250 	if (opt == D_EDIT && (sflag || lflag)) {
1251 		(void) fprintf(stderr, "diff: ");
1252 		(void) fprintf(stderr, gettext(
1253 			"warning: should not give -s or -l with -e\n"));
1254 	}
1255 	dirstatus = 0;
1256 	title[0] = 0;
1257 	(void) strcpy(title, "diff ");
1258 	for (i = 1; diffargv[i + 2]; i++) {
1259 		if (strcmp(diffargv[i], "-") == 0) {
1260 			continue;	/* Skip -S and its argument */
1261 		}
1262 		(void) strcat(title, diffargv[i]);
1263 		(void) strcat(title, " ");
1264 	}
1265 	for (etitle = title; *etitle; etitle++)
1266 		;
1267 	setfile(&file1, &efile1, file1);
1268 	setfile(&file2, &efile2, file2);
1269 	argv[0] = file1;
1270 	argv[1] = file2;
1271 	dir1 = setupdir(file1);
1272 	dir2 = setupdir(file2);
1273 	d1 = dir1; d2 = dir2;
1274 	while (d1->d_entry != 0 || d2->d_entry != 0) {
1275 		if (d1->d_entry && useless(d1->d_entry)) {
1276 			d1++;
1277 			continue;
1278 		}
1279 		if (d2->d_entry && useless(d2->d_entry)) {
1280 			d2++;
1281 			continue;
1282 		}
1283 		if (d1->d_entry == 0)
1284 			cmp = 1;
1285 		else if (d2->d_entry == 0)
1286 			cmp = -1;
1287 		else
1288 			cmp = strcmp(d1->d_entry, d2->d_entry);
1289 		if (cmp < 0) {
1290 			if (lflag)
1291 				d1->d_flags |= ONLY;
1292 			else if (opt == 0 || opt == 2)
1293 				only(d1, 1);
1294 			d1++;
1295 			if (dirstatus == 0)
1296 				dirstatus = 1;
1297 		} else if (cmp == 0) {
1298 			result = compare(d1);
1299 			if (result > dirstatus)
1300 				dirstatus = result;
1301 			d1++;
1302 			d2++;
1303 		} else {
1304 			if (lflag)
1305 				d2->d_flags |= ONLY;
1306 			else if (opt == 0 || opt == 2)
1307 				only(d2, 2);
1308 			d2++;
1309 			if (dirstatus == 0)
1310 				dirstatus = 1;
1311 		}
1312 	}
1313 	if (lflag) {
1314 		scanpr(dir1, ONLY,
1315 			gettext("Only in %.*s"), file1, efile1, 0, 0);
1316 		scanpr(dir2, ONLY,
1317 			gettext("Only in %.*s"), file2, efile2, 0, 0);
1318 		scanpr(dir1, SAME,
1319 		    gettext("Common identical files in %.*s and %.*s"),
1320 		    file1, efile1, file2, efile2);
1321 		scanpr(dir1, DIFFER,
1322 		    gettext("Binary files which differ in %.*s and %.*s"),
1323 		    file1, efile1, file2, efile2);
1324 		scanpr(dir1, DIRECT,
1325 		    gettext("Common subdirectories of %.*s and %.*s"),
1326 		    file1, efile1, file2, efile2);
1327 	}
1328 	if (rflag) {
1329 		if (header && lflag)
1330 			(void) printf("\f");
1331 		for (d1 = dir1; d1->d_entry; d1++)  {
1332 			if ((d1->d_flags & DIRECT) == 0)
1333 				continue;
1334 			(void) strcpy(efile1, d1->d_entry);
1335 			(void) strcpy(efile2, d1->d_entry);
1336 			result = calldiff((char *)0);
1337 			if (result > dirstatus)
1338 				dirstatus = result;
1339 		}
1340 	}
1341 	status = dirstatus;
1342 }
1343 
1344 static void
1345 setfile(char **fpp, char **epp, char *file)
1346 {
1347 	char *cp;
1348 
1349 	*fpp = (char *)malloc(BUFSIZ);
1350 	if (*fpp == 0) {
1351 		(void) fprintf(stderr, "diff: ");
1352 		(void) fprintf(stderr, gettext("out of memory\n"));
1353 		exit(1);
1354 	}
1355 	(void) strcpy(*fpp, file);
1356 	for (cp = *fpp; *cp; cp++)
1357 		continue;
1358 	*cp++ = '/';
1359 	*cp = 0;
1360 	*epp = cp;
1361 }
1362 
1363 static void
1364 scanpr(struct dir *dp, int test,
1365 	char *title, char *file1, char *efile1, char *file2, char *efile2)
1366 {
1367 	int titled = 0;
1368 
1369 	for (; dp->d_entry; dp++) {
1370 		if ((dp->d_flags & test) == 0)
1371 			continue;
1372 		if (titled == 0) {
1373 			if (header == 0)
1374 				header = 1;
1375 			else
1376 				(void) printf("\n");
1377 			(void) printf(title,
1378 			    efile1 - file1 - 1, file1,
1379 			    efile2 - file2 - 1, file2);
1380 			(void) printf(":\n");
1381 			titled = 1;
1382 		}
1383 		(void) printf("\t%s\n", dp->d_entry);
1384 	}
1385 }
1386 
1387 static void
1388 only(struct dir *dp, int which)
1389 {
1390 	char *file = which == 1 ? file1 : file2;
1391 	char *efile = which == 1 ? efile1 : efile2;
1392 
1393 	(void) printf(gettext("Only in %.*s: %s\n"), efile - file - 1, file,
1394 	    dp->d_entry);
1395 }
1396 
1397 int	entcmp();
1398 
1399 static struct dir *
1400 setupdir(char *cp)
1401 {
1402 	struct dir *dp = 0, *ep;
1403 	struct dirent64 *rp;
1404 	int nitems;
1405 	int size;
1406 	DIR *dirp;
1407 
1408 	dirp = opendir(cp);
1409 	if (dirp == NULL) {
1410 		(void) fprintf(stderr, "diff: ");
1411 		perror(cp);
1412 		done();
1413 	}
1414 	nitems = 0;
1415 	dp = (struct dir *)malloc(sizeof (struct dir));
1416 	if (dp == 0)
1417 		error(gettext(NO_MEM_ERR));
1418 
1419 	while (rp = readdir64(dirp)) {
1420 		ep = &dp[nitems++];
1421 		ep->d_reclen = rp->d_reclen;
1422 		ep->d_entry = 0;
1423 		ep->d_flags = 0;
1424 		size = strlen(rp->d_name);
1425 		if (size > 0) {
1426 			ep->d_entry = (char *)malloc(size + 1);
1427 			if (ep->d_entry == 0)
1428 				error(gettext(NO_MEM_ERR));
1429 
1430 			(void) strcpy(ep->d_entry, rp->d_name);
1431 		}
1432 		dp = (struct dir *)realloc((char *)dp,
1433 			(nitems + 1) * sizeof (struct dir));
1434 		if (dp == 0)
1435 			error(gettext(NO_MEM_ERR));
1436 	}
1437 	dp[nitems].d_entry = 0;		/* delimiter */
1438 	(void) closedir(dirp);
1439 	qsort(dp, nitems, sizeof (struct dir),
1440 		(int (*)(const void *, const void *))entcmp);
1441 	return (dp);
1442 }
1443 
1444 static int
1445 entcmp(struct dir *d1, struct dir *d2)
1446 {
1447 	return (strcmp(d1->d_entry, d2->d_entry));
1448 }
1449 
1450 static int
1451 compare(struct dir *dp)
1452 {
1453 	int i, j;
1454 	int f1 = -1, f2 = -1;
1455 	mode_t fmt1, fmt2;
1456 	struct stat stb1, stb2;
1457 	char buf1[BUFSIZ], buf2[BUFSIZ];
1458 	int result;
1459 
1460 	(void) strcpy(efile1, dp->d_entry);
1461 	(void) strcpy(efile2, dp->d_entry);
1462 
1463 	if (stat(file1, &stb1) == -1) {
1464 		(void) fprintf(stderr, "diff: ");
1465 		perror(file1);
1466 		return (2);
1467 	}
1468 	if (stat(file2, &stb2) == -1) {
1469 		(void) fprintf(stderr, "diff: ");
1470 		perror(file2);
1471 		return (2);
1472 	}
1473 
1474 	fmt1 = stb1.st_mode & S_IFMT;
1475 	fmt2 = stb2.st_mode & S_IFMT;
1476 
1477 	if (fmt1 == S_IFREG) {
1478 		f1 = open(file1, O_RDONLY);
1479 		if (f1 < 0) {
1480 			(void) fprintf(stderr, "diff: ");
1481 			perror(file1);
1482 			return (2);
1483 		}
1484 	}
1485 
1486 	if (fmt2 == S_IFREG) {
1487 		f2 = open(file2, O_RDONLY);
1488 		if (f2 < 0) {
1489 			(void) fprintf(stderr, "diff: ");
1490 			perror(file2);
1491 			(void) close(f1);
1492 			return (2);
1493 		}
1494 	}
1495 
1496 	if (fmt1 != S_IFREG || fmt2 != S_IFREG) {
1497 		if (fmt1 == fmt2) {
1498 			switch (fmt1) {
1499 
1500 			case S_IFDIR:
1501 				dp->d_flags = DIRECT;
1502 				if (lflag || opt == D_EDIT)
1503 					goto closem;
1504 				(void) printf(gettext(
1505 				    "Common subdirectories: %s and %s\n"),
1506 				    file1, file2);
1507 				goto closem;
1508 
1509 			case S_IFCHR:
1510 			case S_IFBLK:
1511 				if (stb1.st_rdev == stb2.st_rdev)
1512 					goto same;
1513 				(void) printf(gettext(
1514 				    "Special files %s and %s differ\n"),
1515 				    file1, file2);
1516 				break;
1517 
1518 			case S_IFLNK:
1519 				if ((i = readlink(file1, buf1, BUFSIZ)) == -1) {
1520 					(void) fprintf(stderr, gettext(
1521 					    "diff: cannot read link\n"));
1522 					return (2);
1523 				}
1524 
1525 				if ((j = readlink(file2, buf2, BUFSIZ)) == -1) {
1526 					(void) fprintf(stderr, gettext(
1527 					    "diff: cannot read link\n"));
1528 					return (2);
1529 				}
1530 
1531 				if (i == j) {
1532 					if (strncmp(buf1, buf2, i) == 0)
1533 						goto same;
1534 				}
1535 
1536 				(void) printf(gettext(
1537 				    "Symbolic links %s and %s differ\n"),
1538 				    file1, file2);
1539 				break;
1540 
1541 			case S_IFIFO:
1542 				if (stb1.st_ino == stb2.st_ino)
1543 					goto same;
1544 				(void) printf(gettext(
1545 				    "Named pipes %s and %s differ\n"),
1546 				    file1, file2);
1547 				break;
1548 			}
1549 		} else {
1550 			if (lflag)
1551 				dp->d_flags |= DIFFER;
1552 			else if (opt == D_NORMAL || opt == D_CONTEXT) {
1553 /*
1554  * TRANSLATION_NOTE
1555  * The second and fourth parameters will take the gettext'ed string
1556  * of one of the following:
1557  * a directory
1558  * a character special file
1559  * a block special file
1560  * a plain file
1561  * a named pipe
1562  * a socket
1563  * a door
1564  * an event port
1565  * an unknown type
1566  */
1567 				(void) printf(
1568 gettext("File %s is %s while file %s is %s\n"),
1569 					file1, pfiletype(fmt1),
1570 					file2, pfiletype(fmt2));
1571 			}
1572 		}
1573 		(void) close(f1); (void) close(f2);
1574 		return (1);
1575 	}
1576 	if (stb1.st_size != stb2.st_size)
1577 		goto notsame;
1578 	for (;;) {
1579 		i = read(f1, buf1, BUFSIZ);
1580 		j = read(f2, buf2, BUFSIZ);
1581 		if (i < 0 || j < 0) {
1582 			(void) fprintf(stderr, "diff: ");
1583 			(void) fprintf(stderr, gettext("Error reading "));
1584 			perror(i < 0 ? file1: file2);
1585 			(void) close(f1); (void) close(f2);
1586 			return (2);
1587 		}
1588 		if (i != j)
1589 			goto notsame;
1590 		if (i == 0 && j == 0)
1591 			goto same;
1592 		for (j = 0; j < i; j++)
1593 			if (buf1[j] != buf2[j])
1594 				goto notsame;
1595 	}
1596 same:
1597 	if (sflag == 0)
1598 		goto closem;
1599 	if (lflag)
1600 		dp->d_flags = SAME;
1601 	else
1602 		(void) printf(gettext("Files %s and %s are identical\n"),
1603 			file1, file2);
1604 
1605 closem:
1606 	(void) close(f1); (void) close(f2);
1607 	return (0);
1608 
1609 notsame:
1610 	if (binary(f1) || binary(f2)) {
1611 		if (lflag)
1612 			dp->d_flags |= DIFFER;
1613 		else if (opt == D_NORMAL || opt == D_CONTEXT)
1614 			(void) printf(
1615 				gettext("Binary files %s and %s differ\n"),
1616 			    file1, file2);
1617 		(void) close(f1); (void) close(f2);
1618 		return (1);
1619 	}
1620 	(void) close(f1); (void) close(f2);
1621 	anychange = 1;
1622 	if (lflag) {
1623 		result = calldiff(title);
1624 	} else {
1625 		if (opt == D_EDIT)
1626 			(void) printf("ed - %s << '-*-END-*-'\n", dp->d_entry);
1627 		else
1628 			(void) printf("%s%s %s\n", title, file1, file2);
1629 		result = calldiff((char *)0);
1630 		if (opt == D_EDIT)
1631 			(void) printf("w\nq\n-*-END-*-\n");
1632 	}
1633 	return (result);
1634 }
1635 
1636 char	*prargs[] = { "pr", "-h", 0, 0, 0 };
1637 
1638 static int
1639 calldiff(char *wantpr)
1640 {
1641 	pid_t pid;
1642 	int diffstatus, pv[2];
1643 
1644 	prargs[2] = wantpr;
1645 	(void) fflush(stdout);
1646 	if (wantpr) {
1647 		(void) sprintf(etitle, "%s %s", file1, file2);
1648 		(void) pipe(pv);
1649 		pid = fork();
1650 		if (pid == (pid_t)-1)
1651 			error(gettext(NO_PROCS_ERR));
1652 
1653 		if (pid == 0) {
1654 			(void) close(0);
1655 			(void) dup(pv[0]);
1656 			(void) close(pv[0]);
1657 			(void) close(pv[1]);
1658 			(void) execv(pr+5, prargs);
1659 			(void) execv(pr, prargs);
1660 			perror(pr);
1661 			done();
1662 		}
1663 	}
1664 	pid = fork();
1665 	if (pid == (pid_t)-1)
1666 		error(gettext(NO_PROCS_ERR));
1667 
1668 	if (pid == 0) {
1669 		if (wantpr) {
1670 			(void) close(1);
1671 			(void) dup(pv[1]);
1672 			(void) close(pv[0]);
1673 			(void) close(pv[1]);
1674 		}
1675 		(void) execv(diff+5, diffargv);
1676 		(void) execv(diff, diffargv);
1677 		perror(diff);
1678 		done();
1679 	}
1680 	if (wantpr)	{
1681 		(void) close(pv[0]);
1682 		(void) close(pv[1]);
1683 	}
1684 	while (wait(&diffstatus) != pid)
1685 		continue;
1686 	while (wait((int *)0) != (pid_t)-1)
1687 		continue;
1688 	if ((diffstatus&0177) != 0)
1689 		return (2);
1690 	else
1691 		return ((diffstatus>>8) & 0377);
1692 }
1693 
1694 static char *
1695 pfiletype(mode_t fmt)
1696 {
1697 /*
1698  * TRANSLATION_NOTE
1699  * The following 9 messages will be used in the second and
1700  * the fourth parameters of the message
1701  * "File %s is %s while file %s is %s\n"
1702  */
1703 	switch (fmt) {
1704 
1705 	case S_IFDIR:
1706 		return (gettext("a directory"));
1707 		break;
1708 
1709 	case S_IFCHR:
1710 		return (gettext("a character special file"));
1711 		break;
1712 
1713 	case S_IFBLK:
1714 		return (gettext("a block special file"));
1715 		break;
1716 
1717 	case S_IFREG:
1718 		return (gettext("a plain file"));
1719 		break;
1720 
1721 	case S_IFIFO:
1722 		return (gettext("a named pipe"));
1723 		break;
1724 
1725 	case S_IFSOCK:
1726 		return (gettext("a socket"));
1727 		break;
1728 
1729 	case S_IFDOOR:
1730 		return (gettext("a door"));
1731 		break;
1732 
1733 	case S_IFPORT:
1734 		return (gettext("an event port"));
1735 		break;
1736 
1737 	default:
1738 		return (gettext("an unknown type"));
1739 		break;
1740 	}
1741 }
1742 
1743 static int
1744 binary(int f)
1745 {
1746 	char buf[BUFSIZ];
1747 	int cnt;
1748 
1749 	(void) lseek(f, (long)0, SEEK_SET);
1750 	cnt = read(f, buf, BUFSIZ);
1751 	if (cnt < 0)
1752 		return (1);
1753 	return (isbinary(buf, cnt));
1754 }
1755 
1756 static int
1757 filebinary(FILE *f)
1758 {
1759 	char buf[BUFSIZ];
1760 	int cnt;
1761 
1762 	(void) fseek(f, (long)0, SEEK_SET);
1763 	cnt = fread(buf, 1, BUFSIZ, f);
1764 	if (ferror(f))
1765 		return (1);
1766 	return (isbinary(buf, cnt));
1767 }
1768 
1769 
1770 /*
1771  * We consider a "binary" file to be one that:
1772  * contains a null character ("diff" doesn't handle them correctly, and
1773  *    neither do many other UNIX text-processing commands).
1774  * Characters with their 8th bit set do NOT make a file binary; they may be
1775  * legitimate text characters, or parts of same.
1776  */
1777 static int
1778 isbinary(char *buf, int cnt)
1779 {
1780 	char *cp;
1781 
1782 	cp = buf;
1783 	while (--cnt >= 0)
1784 		if (*cp++ == '\0')
1785 			return (1);
1786 	return (0);
1787 }
1788 
1789 
1790 /*
1791  * THIS IS CRUDE.
1792  */
1793 static int
1794 useless(char *cp)
1795 {
1796 
1797 	if (cp[0] == '.') {
1798 		if (cp[1] == '\0')
1799 			return (1);	/* directory "." */
1800 		if (cp[1] == '.' && cp[2] == '\0')
1801 			return (1);	/* directory ".." */
1802 	}
1803 	if (start && strcmp(start, cp) > 0)
1804 		return (1);
1805 	return (0);
1806 }
1807 
1808 
1809 void
1810 sort(struct line *a, int n)	/* shellsort CACM #201 */
1811 {
1812 	struct line w;
1813 	int j, m;
1814 	struct line *ai;
1815 	struct line *aim;
1816 	int k;
1817 
1818 	for (j = 1, m = 0; j <= n; j *= 2)
1819 		m = 2 * j - 1;
1820 	for (m /= 2; m != 0; m /= 2) {
1821 		k = n - m;
1822 		for (j = 1; j <= k; j++) {
1823 			for (ai = &a[j]; ai > a; ai -= m) {
1824 				aim = &ai[m];
1825 				if (aim < ai)
1826 					break;	/* wraparound */
1827 				if (aim->value > ai[0].value ||
1828 				    aim->value == ai[0].value &&
1829 				    aim->serial > ai[0].serial)
1830 					break;
1831 				w.value = ai[0].value;
1832 				ai[0].value = aim->value;
1833 				aim->value = w.value;
1834 				w.serial = ai[0].serial;
1835 				ai[0].serial = aim->serial;
1836 				aim->serial = w.serial;
1837 			}
1838 		}
1839 	}
1840 }
1841 
1842 static void
1843 unsort(struct line *f, int l, int *b)
1844 {
1845 	int *a;
1846 	int i;
1847 
1848 	a = (int *)talloc((l + 1) * sizeof (int));
1849 	for (i = 1; i <= l; i++)
1850 		a[f[i].serial] = f[i].value;
1851 	for (i = 1; i <= l; i++)
1852 		b[i] = a[i];
1853 	free((char *)a);
1854 }
1855 
1856 static void
1857 filename(char **pa1, char **pa2, struct stat *st, char **ifile)
1858 {
1859 	char *a1, *b1, *a2;
1860 
1861 	a1 = *pa1;
1862 	a2 = *pa2;
1863 
1864 	if (strcmp(*pa1, "-") == 0)
1865 		*ifile = strdup("-");
1866 	else
1867 		*ifile = strdup(*pa1);
1868 
1869 	if (*ifile == (char *)NULL) {
1870 		(void) fprintf(stderr, gettext(
1871 			"no more memory - try again later\n"));
1872 		status = 2;
1873 		done();
1874 	}
1875 
1876 	if ((st->st_mode & S_IFMT) == S_IFDIR) {
1877 		b1 = *pa1 = (char *)malloc(PATH_MAX);
1878 		while (*b1++ = *a1++)
1879 			;
1880 		b1[-1] = '/';
1881 		a1 = b1;
1882 		while (*a1++ = *a2++)
1883 			if (*a2 && *a2 != '/' && a2[-1] == '/')
1884 				a1 = b1;
1885 		*ifile = strdup(*pa1);
1886 
1887 		if (*ifile == (char *)NULL) {
1888 			(void) fprintf(stderr, gettext(
1889 				"no more memory - try again later\n"));
1890 			status = 2;
1891 			done();
1892 		}
1893 
1894 		if (stat(*pa1, st) < 0) {
1895 			(void) fprintf(stderr, "diff: ");
1896 			perror(*pa1);
1897 			done();
1898 		}
1899 	} else if ((st->st_mode & S_IFMT) == S_IFCHR)
1900 		*pa1 = copytemp(a1);
1901 	else if (a1[0] == '-' && a1[1] == 0) {
1902 		*pa1 = copytemp(a1);	/* hack! */
1903 		if (stat(*pa1, st) < 0) {
1904 			(void) fprintf(stderr, "diff: ");
1905 			perror(*pa1);
1906 			done();
1907 		}
1908 	}
1909 }
1910 
1911 static char *
1912 copytemp(char *fn)
1913 {
1914 	int ifd, ofd;	/* input and output file descriptors */
1915 	int i;
1916 	char template[13];	/* template for temp file name */
1917 	char buf[BUFSIZ];
1918 
1919 	/*
1920 	 * a "-" file is interpreted as fd 0 for pre-/dev/fd systems
1921 	 * ... let's hope this goes away soon!
1922 	 */
1923 	if ((ifd = (strcmp(fn, "-") ? open(fn, 0) : 0)) < 0) {
1924 		(void) fprintf(stderr, "diff: ");
1925 		(void) fprintf(stderr, gettext("cannot open %s\n"), fn);
1926 		done();
1927 	}
1928 	(void) signal(SIGHUP, (void (*)(int))done);
1929 	(void) signal(SIGINT, (void (*)(int))done);
1930 	(void) signal(SIGPIPE, (void (*)(int))done);
1931 	(void) signal(SIGTERM, (void (*)(int))done);
1932 	(void) strcpy(template, "/tmp/dXXXXXX");
1933 	if ((ofd = mkstemp(template)) < 0) {
1934 		(void) fprintf(stderr, "diff: ");
1935 		(void) fprintf(stderr, gettext("cannot create %s\n"), template);
1936 		done();
1937 	}
1938 	(void) strcpy(tempfile[whichtemp++], template);
1939 	while ((i = read(ifd, buf, BUFSIZ)) > 0)
1940 		if (write(ofd, buf, i) != i) {
1941 			(void) fprintf(stderr, "diff: ");
1942 			(void) fprintf(stderr,
1943 				gettext("write failed %s\n"), template);
1944 			done();
1945 		}
1946 	(void) close(ifd); (void) close(ofd);
1947 	return (tempfile[whichtemp-1]);
1948 }
1949 
1950 static void
1951 prepare(int i, char *arg)
1952 {
1953 	struct line *p;
1954 	int j, h;
1955 
1956 	(void) fseek(input[i], (long)0, SEEK_SET);
1957 	p = (struct line *)talloc(3 * sizeof (line));
1958 	for (j = 0; h = readhash(input[i], i, arg); ) {
1959 		p = (struct line *)ralloc((void *)p, (++j + 3) * sizeof (line));
1960 		p[j].value = h;
1961 	}
1962 	len[i] = j;
1963 	file[i] = p;
1964 }
1965 
1966 static void
1967 prune(void)
1968 {
1969 	int i, j;
1970 
1971 	for (pref = 0; pref < len[0] && pref < len[1] &&
1972 			file[0][pref + 1].value == file[1][pref + 1].value;
1973 	    pref++)
1974 		;
1975 	for (suff = 0; (suff < len[0] - pref) &&
1976 			(suff < len[1] - pref) &&
1977 			(file[0][len[0] - suff].value ==
1978 			file[1][len[1] - suff].value);
1979 	    suff++)
1980 		;
1981 
1982 	/* decremnt suff by 2 iff suff >= 2, ensure that suff is never < 0 */
1983 	if (suff >= 2)
1984 		suff -= 2;
1985 
1986 	for (j = 0; j < 2; j++) {
1987 		sfile[j] = file[j] + pref;
1988 		slen[j] = len[j] - pref - suff;
1989 		for (i = 0; i <= slen[j]; i++)
1990 			sfile[j][i].serial = i;
1991 	}
1992 }
1993 
1994 static void
1995 equiv(struct line *a, int n, struct line *b, int m, int *c)
1996 {
1997 	int i, j;
1998 	i = j = 1;
1999 	while (i <= n && j <= m) {
2000 		if (a[i].value < b[j].value)
2001 			a[i++].value = 0;
2002 		else if (a[i].value == b[j].value)
2003 			a[i++].value = j;
2004 		else
2005 			j++;
2006 	}
2007 	while (i <= n)
2008 		a[i++].value = 0;
2009 	b[m+1].value = 0;	j = 0;
2010 	while (++j <= m) {
2011 		c[j] = -b[j].serial;
2012 		while (b[j + 1].value == b[j].value) {
2013 			j++;
2014 			c[j] = b[j].serial;
2015 		}
2016 	}
2017 	c[j] = -1;
2018 }
2019 
2020 static void
2021 done(void)
2022 {
2023 	if (whichtemp) (void) unlink(tempfile[0]);
2024 	if (whichtemp == 2) (void) unlink(tempfile[1]);
2025 	exit(status);
2026 }
2027 
2028 static void
2029 noroom(void)
2030 {
2031 	(void) fprintf(stderr, "diff: ");
2032 	(void) fprintf(stderr, gettext("files too big, try -h\n"));
2033 	done();
2034 }
2035 
2036 static void
2037 error(const char *s)
2038 {
2039 	(void) fprintf(stderr, "diff: ");
2040 	(void) fprintf(stderr, s);
2041 	(void) fprintf(stderr, "\n");
2042 	done();
2043 }
2044 
2045 static void
2046 usage(void)
2047 {
2048 	(void) fprintf(stderr, gettext(
2049 		"usage: diff [-bitw] [-c | -e | -f | -h | -n | -u] file1 "
2050 			"file2\n"
2051 		"       diff [-bitw] [-C number | -U number] file1 file2\n"
2052 		"       diff [-bitw] [-D string] file1 file2\n"
2053 		"       diff [-bitw] [-c | -e | -f | -h | -n | -u] [-l] [-r] "
2054 			"[-s] [-S name] directory1 directory2\n"));
2055 	status = 2;
2056 	done();
2057 }
2058 
2059 #define	NW	1024
2060 struct buff	{
2061 	FILE	*iop;	/* I/O stream */
2062 	char	buf[NW + MB_LEN_MAX];	/* buffer */
2063 	char	*ptr;	/* current pointer in the buffer */
2064 	int	buffered;	/* if non-zero, buffer has data */
2065 	long	offset;	/* offset in the file */
2066 };
2067 
2068 static struct buff bufwchar[2];
2069 
2070 /*
2071  *	Initializes the buff structure for specified
2072  *	I/O stream.  Also sets the specified offset
2073  */
2074 static void
2075 initbuf(FILE *iop, int filen, long offset)
2076 {
2077 	bufwchar[filen].iop = iop;
2078 	bufwchar[filen].ptr = NULL;
2079 	bufwchar[filen].buffered = 0;
2080 	bufwchar[filen].offset = offset;
2081 }
2082 
2083 /*
2084  * 	Reset a buff structure, and rewind the associated file.
2085  */
2086 static void
2087 resetbuf(int filen)
2088 {
2089 	bufwchar[filen].ptr = NULL;
2090 	bufwchar[filen].buffered = bufwchar[filen].offset = 0;
2091 	rewind(bufwchar[filen].iop);
2092 }
2093 
2094 
2095 /*
2096  *	Returns the current offset in the file
2097  */
2098 static long
2099 ftellbuf(int filen)
2100 {
2101 	return (bufwchar[filen].offset);
2102 }
2103 
2104 static wint_t
2105 wcput(wint_t wc)
2106 {
2107 	char	mbs[MB_LEN_MAX];
2108 	unsigned char	*p;
2109 	int	n;
2110 
2111 	n = wctomb(mbs, (wchar_t)wc);
2112 	if (n > 0) {
2113 		p = (unsigned char *)mbs;
2114 		while (n--) {
2115 			(void) putc((*p++), stdout);
2116 		}
2117 		return (wc);
2118 	} else if (n < 0) {
2119 		(void) putc((int)(wc & 0xff), stdout);
2120 		return (wc & 0xff);
2121 	} else {
2122 		/* this should not happen */
2123 		return (WEOF);
2124 	}
2125 }
2126 
2127 /*
2128  *	Reads one wide-character from the file associated with filen.
2129  *	If multibyte locales, the input is buffered.
2130  *
2131  *	Input:	filen	the file number (0 or 1)
2132  *	Output:	*len	number of bytes to make wide-character
2133  *	Return:			wide-character
2134  */
2135 static wint_t
2136 getbufwchar(int filen, int *len)
2137 {
2138 
2139 	int	i, num, clen;
2140 	wchar_t	wc;
2141 	size_t	mxlen;
2142 
2143 	if (mbcurmax == 1) {
2144 		/* If sigle byte locale, use getc() */
2145 		int	ch;
2146 
2147 		ch = getc(bufwchar[filen].iop);
2148 		bufwchar[filen].offset++;
2149 		*len = 1;
2150 
2151 		if (isascii(ch) || (ch == EOF)) {
2152 			return ((wint_t)ch);
2153 		} else {
2154 			wchar_t	wc;
2155 			char	str[2] = {0, 0};
2156 
2157 			str[0] = (char)ch;
2158 			if (mbtowc(&wc, str, 1) > 0) {
2159 				return ((wint_t)wc);
2160 			} else {
2161 				return ((wint_t)ch);
2162 			}
2163 		}
2164 	} else {
2165 		mxlen = mbcurmax;
2166 	}
2167 
2168 	if (bufwchar[filen].buffered == 0) {
2169 		/* Not buffered */
2170 		bufwchar[filen].ptr = &(bufwchar[filen].buf[MB_LEN_MAX]);
2171 		num = fread((void *)bufwchar[filen].ptr,
2172 			sizeof (char), NW, bufwchar[filen].iop);
2173 		if (ferror(bufwchar[filen].iop)) {
2174 			(void) fprintf(stderr, "diff: ");
2175 			(void) fprintf(stderr, gettext("Error reading "));
2176 			perror((filen == 0) ? file1 : file2);
2177 			status = 2;
2178 			done();
2179 		}
2180 		if (num == 0)
2181 			return (WEOF);
2182 		bufwchar[filen].buffered = num;
2183 	}
2184 
2185 	if (bufwchar[filen].buffered < mbcurmax) {
2186 		for (i = 0; i < bufwchar[filen].buffered; i++) {
2187 			bufwchar[filen].buf[MB_LEN_MAX -
2188 				(bufwchar[filen].buffered - i)] =
2189 				*(bufwchar[filen].ptr + i);
2190 		}
2191 		bufwchar[filen].ptr = &(bufwchar[filen].buf[MB_LEN_MAX]);
2192 		num = fread((void *)bufwchar[filen].ptr,
2193 			sizeof (char), NW, bufwchar[filen].iop);
2194 		if (ferror(bufwchar[filen].iop)) {
2195 			(void) fprintf(stderr, "diff: ");
2196 			(void) fprintf(stderr, gettext("Error reading "));
2197 			perror((filen == 0) ? file1 : file2);
2198 			status = 2;
2199 			done();
2200 		}
2201 		bufwchar[filen].ptr = &(bufwchar[filen].buf[MB_LEN_MAX -
2202 				bufwchar[filen].buffered]);
2203 		bufwchar[filen].buffered += num;
2204 		if (bufwchar[filen].buffered < mbcurmax) {
2205 			mxlen = bufwchar[filen].buffered;
2206 		}
2207 	}
2208 
2209 	clen = mbtowc(&wc, bufwchar[filen].ptr, mxlen);
2210 	if (clen <= 0) {
2211 		(bufwchar[filen].buffered)--;
2212 		*len = 1;
2213 		(bufwchar[filen].offset)++;
2214 		wc = (wchar_t)((unsigned char)*bufwchar[filen].ptr++);
2215 		return ((wint_t)wc);
2216 	} else {
2217 		bufwchar[filen].buffered -= clen;
2218 		bufwchar[filen].ptr += clen;
2219 		bufwchar[filen].offset += clen;
2220 		*len = clen;
2221 		return ((wint_t)wc);
2222 	}
2223 }
2224