xref: /illumos-gate/usr/src/cmd/diff/diff.c (revision 148fd93e57d3d5813d90f1291e6bd30de45c7723)
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