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