xref: /titanic_52/usr/src/cmd/diff/diff.c (revision 965005c81e0f731867d47892b9fb677030b102df)
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