xref: /illumos-gate/usr/src/cmd/diff/diffh.c (revision b58c9703c68c2d59709d0d83191ecab329860b31)
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 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <ctype.h>
37 #include <locale.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <limits.h>
41 #include <stdarg.h>
42 
43 #define	C		3
44 #define	RANGE		30
45 #define	LEN		255
46 #define	INF		16384
47 
48 char *text[2][RANGE];
49 long lineno[2] = {1, 1};	/* no. of 1st stored line in each file */
50 int ntext[2];		/* number of stored lines in each */
51 long n0, n1;		/* scan pointer in each */
52 int bflag;
53 int debug = 0;
54 FILE *file[2];
55 static int diffFound = 0;
56 
57 static char *getl(int f, long n);
58 static void clrl(int f, long n);
59 static void movstr(char *s, char *t);
60 static int easysynch(void);
61 static int output(int a, int b);
62 static void change(long a, int b, long c, int d, char *s);
63 static void range(long a, int b);
64 static int cmp(char *s, char *t);
65 static FILE *dopen(char *f1, char *f2);
66 static void progerr(char *s);
67 static void error(char *err, ...);
68 static int hardsynch(void);
69 
70 	/* return pointer to line n of file f */
71 static char *
72 getl(int f, long n)
73 {
74 	char *t;
75 	int delta, nt;
76 
77 again:
78 	delta = n - lineno[f];
79 	nt = ntext[f];
80 	if (delta < 0)
81 		progerr("1");
82 	if (delta < nt)
83 		return (text[f][delta]);
84 	if (delta > nt)
85 		progerr("2");
86 	if (nt >= RANGE)
87 		progerr("3");
88 	if (feof(file[f]))
89 		return (NULL);
90 	t = text[f][nt];
91 	if (t == 0) {
92 		t = text[f][nt] = (char *)malloc(LEN+1);
93 		if (t == NULL)
94 			if (hardsynch())
95 				goto again;
96 			else
97 				progerr("5");
98 	}
99 	t = fgets(t, LEN, file[f]);
100 	if (t != NULL)
101 		ntext[f]++;
102 	return (t);
103 }
104 
105 	/* remove thru line n of file f from storage */
106 static void
107 clrl(int f, long n)
108 {
109 	int i, j;
110 
111 	j = n-lineno[f]+1;
112 	for (i = 0; i+j < ntext[f]; i++)
113 		movstr(text[f][i+j], text[f][i]);
114 	lineno[f] = n+1;
115 	ntext[f] -= j;
116 }
117 
118 static void
119 movstr(char *s, char *t)
120 {
121 	while (*t++ = *s++)
122 		continue;
123 }
124 
125 int
126 main(int argc, char **argv)
127 {
128 	char *s0, *s1;
129 
130 	if ((argc > 1) && (*argv[1] == '-')) {
131 		argc--;
132 		argv++;
133 		while (*++argv[0])
134 			if (*argv[0] == 'b')
135 				bflag++;
136 	}
137 
138 	(void) setlocale(LC_ALL, "");
139 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
140 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
141 #endif
142 	(void) textdomain(TEXT_DOMAIN);
143 
144 	if (argc != 3)
145 		error(gettext("must have 2 file arguments"));
146 	file[0] = dopen(argv[1], argv[2]);
147 	file[1] = dopen(argv[2], argv[1]);
148 	for (;;) {
149 		s0 = getl(0, ++n0);
150 		s1 = getl(1, ++n1);
151 		if (s0 == NULL || s1 == NULL)
152 			break;
153 		if (cmp(s0, s1) != 0) {
154 			if (!easysynch() && !hardsynch())
155 				progerr("5");
156 		} else {
157 			clrl(0, n0);
158 			clrl(1, n1);
159 		}
160 	}
161 	/* diff is expected to return 1 if the files differ */
162 	if (s0 == NULL && s1 == NULL)
163 		return (diffFound);
164 	if (s0 == NULL) {
165 		(void) output(-1, INF);
166 		return (1);
167 	}
168 	if (s1 == NULL) {
169 		(void) output(INF, -1);
170 		return (1);
171 	}
172 	/* NOTREACHED */
173 	return (0);
174 }
175 
176 	/* synch on C successive matches */
177 static int
178 easysynch()
179 {
180 	int i, j;
181 	int k, m;
182 	char *s0, *s1;
183 
184 	for (i = j = 1; i < RANGE && j < RANGE; i++, j++) {
185 		s0 = getl(0, n0+i);
186 		if (s0 == NULL)
187 			return (output(INF, INF));
188 		for (k = C-1; k < j; k++) {
189 			for (m = 0; m < C; m++)
190 				if (cmp(getl(0, n0+i-m),
191 					getl(1, n1+k-m)) != 0)
192 					goto cont1;
193 			return (output(i-C, k-C));
194 cont1:
195 			;
196 		}
197 		s1 = getl(1, n1+j);
198 		if (s1 == NULL)
199 			return (output(INF, INF));
200 		for (k = C-1; k <= i; k++) {
201 			for (m = 0; m < C; m++)
202 				if (cmp(getl(0, n0+k-m),
203 					getl(1, n1+j-m)) != 0)
204 					goto cont2;
205 			return (output(k-C, j-C));
206 cont2:
207 			;
208 		}
209 	}
210 	return (0);
211 }
212 
213 static int
214 output(int a, int b)
215 {
216 	int i;
217 	char *s;
218 
219 	if (a < 0)
220 		change(n0-1, 0, n1, b, "a");
221 	else if (b < 0)
222 		change(n0, a, n1-1, 0, "d");
223 	else
224 		change(n0, a, n1, b, "c");
225 	for (i = 0; i <= a; i++) {
226 		s = getl(0, n0+i);
227 		if (s == NULL)
228 			break;
229 		(void) printf("< %s", s);
230 		clrl(0, n0+i);
231 	}
232 	n0 += i-1;
233 	if (a >= 0 && b >= 0)
234 		(void) printf("---\n");
235 	for (i = 0; i <= b; i++) {
236 		s = getl(1, n1+i);
237 		if (s == NULL)
238 			break;
239 		(void) printf("> %s", s);
240 		clrl(1, n1+i);
241 	}
242 	diffFound = 1;
243 	n1 += i-1;
244 	return (1);
245 }
246 
247 static void
248 change(long a, int b, long c, int d, char *s)
249 {
250 	range(a, b);
251 	(void) printf("%s", s);
252 	range(c, d);
253 	(void) printf("\n");
254 }
255 
256 static void
257 range(long a, int b)
258 {
259 	if (b == INF)
260 		(void) printf("%ld,$", a);
261 	else if (b == 0)
262 		(void) printf("%ld", a);
263 	else
264 		(void) printf("%ld,%ld", a, a+b);
265 }
266 
267 static int
268 cmp(char *s, char *t)
269 {
270 	if (debug)
271 		(void) printf("%s:%s\n", s, t);
272 	for (;;) {
273 		if (bflag && isspace(*s) && isspace(*t)) {
274 			while (isspace(*++s))
275 				;
276 			while (isspace(*++t))
277 				;
278 		}
279 		if (*s != *t || *s == 0)
280 			break;
281 		s++;
282 		t++;
283 	}
284 	return (*s-*t);
285 }
286 
287 static FILE *
288 dopen(char *f1, char *f2)
289 {
290 	FILE *f;
291 	char b[PATH_MAX], *bptr, *eptr;
292 	struct stat statbuf;
293 
294 	if (cmp(f1, "-") == 0) {
295 		if (cmp(f2, "-") == 0)
296 			error(gettext("can't do - -"));
297 		else {
298 			if (fstat(fileno(stdin), &statbuf) == -1)
299 				error(gettext("can't access stdin"));
300 			else
301 				return (stdin);
302 		}
303 	}
304 	if (stat(f1, &statbuf) == -1)
305 		error(gettext("can't access %s"), f1);
306 	if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
307 		for (bptr = b; *bptr = *f1++; bptr++)
308 			;
309 		*bptr++ = '/';
310 		for (eptr = f2; *eptr; eptr++)
311 			if (*eptr == '/' && eptr[1] != 0 && eptr[1] != '/')
312 				f2 = eptr+1;
313 		while (*bptr++ = *f2++)
314 			;
315 		f1 = b;
316 	}
317 	f = fopen(f1, "r");
318 	if (f == NULL)
319 		error(gettext("can't open %s"), f1);
320 	return (f);
321 }
322 
323 
324 static void
325 progerr(char *s)
326 {
327 	error(gettext("program error %s"), s);
328 }
329 
330 static void
331 error(char *err, ...)
332 {
333 	va_list	ap;
334 
335 	va_start(ap, err);
336 	(void) fprintf(stderr, "diffh: ");
337 	(void) vfprintf(stderr, err, ap);
338 	(void) fprintf(stderr, "\n");
339 	va_end(ap);
340 	exit(2);
341 }
342 
343 	/* stub for resychronization beyond limits of text buf */
344 static int
345 hardsynch()
346 {
347 	change(n0, INF, n1, INF, "c");
348 	(void) printf(gettext("---change record omitted\n"));
349 	error(gettext("can't resynchronize"));
350 	return (0);
351 }
352