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
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.
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]
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
31 #pragma ident "%Z%%M% %I% %E% SMI"
35 #include <sys/types.h>
46 * This program segments two files into pieces of <= seglim lines
47 * (which is passed as a third argument or defaulted to some number)
48 * and then executes diff upon the pieces. The output of
49 * 'diff' is then processed to make it look as if 'diff' had
50 * processed the files whole. The reason for all this is that seglim
51 * is a reasonable upper limit on the size of files that diff can
53 * NOTE -- by segmenting the files in this manner, it cannot be
54 * guaranteed that the 'diffing' of the segments will generate
55 * a minimal set of differences.
56 * This process is most definitely not equivalent to 'diffing'
57 * the files whole, assuming 'diff' could handle such large files.
59 * 'diff' is executed by a child process, generated by forking,
60 * and communicates with this program through pipes.
63 static char Error
[128];
65 static int seglim
; /* limit of size of file segment to be generated */
67 static char diff
[] = "/usr/bin/diff";
68 static char tempskel
[] = "/tmp/bdXXXXXX"; /* used to generate temp file names */
69 static char tempfile
[32];
70 static char otmp
[32], ntmp
[32];
72 static int fatal_num
= 1; /* exit number for fatal exit */
73 static offset_t linenum
;
74 static size_t obufsiz
, nbufsiz
, dbufsiz
;
75 static char *readline(char **, size_t *, FILE *);
76 static void addgen(char **, size_t *, FILE *);
77 static void delgen(char **, size_t *, FILE *);
78 static void fixnum(char *);
79 static void fatal(char *);
80 static void setsig(void);
81 static void setsig1(int);
82 static char *satoi(char *, offset_t
*);
83 static FILE *maket(char *);
88 main(int argc
, char *argv
[])
90 FILE *poldfile
, *pnewfile
;
91 char *oline
, *nline
, *diffline
;
96 FILE *poldtemp
, *pnewtemp
, *pipeinp
;
101 * Set flags for 'fatal' so that it will clean up,
102 * produce a message, and terminate.
104 fflags
= FTLMSG
| FTLCLN
| FTLEXIT
;
108 if (argc
< 3 || argc
> 5)
111 if (strcmp(argv
[1], "-") == 0 && strcmp(argv
[2], "-") == 0)
112 fatal("both files standard input");
113 if (strcmp(argv
[1], "-") == 0)
116 if ((poldfile
= fopen(argv
[1], "r")) == NULL
) {
117 (void) snprintf(Error
, sizeof (Error
),
118 "Can not open '%s'", argv
[1]);
121 if (strcmp(argv
[2], "-") == 0)
124 if ((pnewfile
= fopen(argv
[2], "r")) == NULL
) {
125 (void) snprintf(Error
, sizeof (Error
),
126 "Can not open '%s'", argv
[2]);
133 if (argv
[3][0] == '-' && argv
[3][1] == 's')
136 if ((seglim
= atoi(argv
[3])) == 0)
137 fatal("non-numeric limit");
138 if (argc
== 5 && argv
[4][0] == '-' &&
146 /* Allocate the buffers and initialize their lengths */
152 if ((oline
= (char *)malloc(obufsiz
)) == NULL
||
153 (nline
= (char *)malloc(nbufsiz
)) == NULL
||
154 (diffline
= (char *)malloc(dbufsiz
)) == NULL
)
155 fatal("Out of memory");
158 * The following while-loop will prevent any lines
159 * common to the beginning of both files from being
160 * sent to 'diff'. Since the running time of 'diff' is
161 * non-linear, this will help improve performance.
162 * If, during this process, both files reach EOF, then
163 * the files are equal and the program will terminate.
164 * If either file reaches EOF before the other, the
165 * program will generate the appropriate 'diff' output
166 * itself, since this can be easily determined and will
167 * avoid executing 'diff' completely.
170 olp
= readline(&oline
, &obufsiz
, poldfile
);
171 nlp
= readline(&nline
, &nbufsiz
, pnewfile
);
173 if (!olp
&& !nlp
) /* EOF found on both: files equal */
178 * The entire old file is a prefix of the
179 * new file. Generate the appropriate "append"
180 * 'diff'-like output, which is of the form:
182 * where 'n' represents a line-number.
184 addgen(&nline
, &nbufsiz
, pnewfile
);
189 * The entire new file is a prefix of the
190 * old file. Generate the appropriate "delete"
191 * 'diff'-like output, which is of the form:
193 * where 'n' represents a line-number.
195 delgen(&oline
, &obufsiz
, poldfile
);
198 if (strcmp(olp
, nlp
) == 0)
205 * Here, first 'linenum' lines are equal.
206 * The following while-loop segments both files into
207 * seglim segments, forks and executes 'diff' on the
208 * segments, and processes the resulting output of
209 * 'diff', which is read from a pipe.
212 /* If both files are at EOF, everything is done. */
213 if (!olp
&& !nlp
) /* finished */
218 * Generate appropriate "append"
219 * output without executing 'diff'.
221 addgen(&nline
, &nbufsiz
, pnewfile
);
226 * Generate appropriate "delete"
227 * output without executing 'diff'.
229 delgen(&oline
, &obufsiz
, poldfile
);
233 * Create a temporary file to hold a segment
234 * from the old file, and write it.
236 poldtemp
= maket(otmp
);
238 while (olp
&& otcnt
< seglim
) {
239 (void) fputs(oline
, poldtemp
);
240 if (ferror(poldtemp
) != 0) {
242 fatal("Can not write to temporary file");
244 olp
= readline(&oline
, &obufsiz
, poldfile
);
247 (void) fclose(poldtemp
);
250 * Create a temporary file to hold a segment
251 * from the new file, and write it.
253 pnewtemp
= maket(ntmp
);
255 while (nlp
&& ntcnt
< seglim
) {
256 (void) fputs(nline
, pnewtemp
);
257 if (ferror(pnewtemp
) != 0) {
259 fatal("Can not write to temporary file");
261 nlp
= readline(&nline
, &nbufsiz
, pnewfile
);
264 (void) fclose(pnewtemp
);
266 /* Create pipes and fork. */
267 if ((pipe(pfd
)) == -1)
268 fatal("Can not create pipe");
269 if ((i
= fork()) < (pid_t
)0) {
270 (void) close(pfd
[0]);
271 (void) close(pfd
[1]);
272 fatal("Can not fork, try again");
273 } else if (i
== (pid_t
)0) { /* child process */
274 (void) close(pfd
[0]);
277 (void) close(pfd
[1]);
279 /* Execute 'diff' on the segment files. */
280 (void) execlp(diff
, diff
, otmp
, ntmp
, 0);
283 * Exit code here must be > 1.
284 * Parent process treats exit code of 1 from the child
285 * as non-error because the child process "diff" exits
286 * with a status of 1 when a difference is encountered.
287 * The error here is a true error--the parent process
288 * needs to detect it and exit with a non-zero status.
291 (void) snprintf(Error
, sizeof (Error
),
292 "Can not execute '%s'", diff
);
295 } else { /* parent process */
296 (void) close(pfd
[1]);
297 pipeinp
= fdopen(pfd
[0], "r");
299 /* Process 'diff' output. */
300 while ((dp
= readline(&diffline
, &dbufsiz
, pipeinp
))) {
304 (void) printf("%s", diffline
);
307 (void) fclose(pipeinp
);
310 (void) wait(&status
);
312 (void) snprintf(Error
, sizeof (Error
),
313 "'%s' failed", diff
);
319 /* Remove temporary files. */
325 /* Routine to save remainder of a file. */
327 saverest(char **linep
, size_t *bufsizp
, FILE *iptr
)
332 temptr
= maket(tempfile
);
337 (void) fputs(*linep
, temptr
);
339 lp
= readline(linep
, bufsizp
, iptr
);
341 (void) fclose(temptr
);
344 /* Routine to write out data saved by 'saverest' and to remove the file. */
346 putsave(char **linep
, size_t *bufsizp
, char type
)
350 if ((temptr
= fopen(tempfile
, "r")) == NULL
) {
351 (void) snprintf(Error
, sizeof (Error
),
352 "Can not open tempfile ('%s')", tempfile
); fatal(Error
);
355 while (readline(linep
, bufsizp
, temptr
))
356 (void) printf("%c %s", type
, *linep
);
358 (void) fclose(temptr
);
360 (void) unlink(tempfile
);
376 (void) printf("%c", *lp
);
381 lp
= satoi(lp
, &num
);
383 (void) printf("%lld", num
);
389 addgen(char **lpp
, size_t *bufsizp
, FILE *fp
)
392 (void) printf("%llda%lld", linenum
, linenum
+1);
394 /* Save lines of new file. */
395 oldline
= linenum
+ 1;
396 saverest(lpp
, bufsizp
, fp
);
398 if (oldline
< linenum
)
399 (void) printf(",%lld\n", linenum
);
403 /* Output saved lines, as 'diff' would. */
404 putsave(lpp
, bufsizp
, '>');
410 delgen(char **lpp
, size_t *bufsizp
, FILE *fp
)
414 (void) printf("%lld", linenum
+1);
417 /* Save lines of old file. */
418 saverest(lpp
, bufsizp
, fp
);
420 if (savenum
+1 != linenum
)
421 (void) printf(",%lldd%lld\n", linenum
, savenum
);
423 (void) printf("d%lld\n", savenum
);
425 /* Output saved lines, as 'diff' would. */
426 putsave(lpp
, bufsizp
, '<');
434 (void) unlink(tempfile
);
445 (void) strcpy(file
, tempskel
);
446 if ((fd
= mkstemp(file
)) == -1 ||
447 (iop
= fdopen(fd
, "w+")) == NULL
) {
448 (void) snprintf(Error
, sizeof (Error
),
449 "Can not open/create temp file ('%s')", file
);
458 * General purpose error handler.
460 * The argument to fatal is a pointer to an error message string.
461 * The action of this routine is driven completely from
462 * the "fflags" global word (see <fatal.h>).
464 * The FTLMSG bit controls the writing of the error
465 * message on file descriptor 2. A newline is written
466 * after the user supplied message.
468 * If the FTLCLN bit is on, clean_up is called.
472 (void) fprintf(stderr
, "%s: %s\n", prognam
, msg
);
475 if (fflags
& FTLEXIT
)
482 * General-purpose signal setting routine.
483 * All non-ignored, non-caught signals are caught.
484 * If a signal other than hangup, interrupt, or quit is caught,
485 * a "user-oriented" message is printed on file descriptor 2.
486 * If hangup, interrupt or quit is caught, that signal
488 * Termination is like that of "fatal",
495 for (j
= 1; j
< ONSIG
; j
++) {
496 act
= signal(j
, setsig1
);
501 (void) signal(j
, act
);
509 (void) signal(sig
, SIG_IGN
);
515 satoi(char *p
, offset_t
*ip
)
521 sum
= sum
* 10 + (*p
++ - '0');
527 * Read a line of data from a file. If the current buffer is not large enough
528 * to contain the line, double the size of the buffer and continue reading.
529 * Loop until either the entire line is read or until there is no more space
534 readline(char **bufferp
, size_t *bufsizp
, FILE *filep
)
537 size_t newsize
; /* number of bytes to make buffer */
540 (*bufferp
)[*bufsizp
- 1] = '\t'; /* arbitrary non-zero character */
541 (*bufferp
)[*bufsizp
- 2] = ' '; /* arbitrary non-newline char */
542 bufp
= fgets(*bufferp
, *bufsizp
, filep
);
545 while ((*bufferp
)[*bufsizp
-1] == '\0' &&
546 (*bufferp
)[*bufsizp
- 2] != '\n' &&
547 strlen(*bufferp
) == *bufsizp
- 1) {
548 newsize
= 2 * (*bufsizp
);
549 bufp
= (char *)realloc((void *)*bufferp
, newsize
);
551 fatal("Out of memory");
555 (*bufferp
)[*bufsizp
- 1] = '\t';
556 (*bufferp
)[*bufsizp
- 2] = ' ';
557 bufp
= fgets(*bufferp
+ oldsize
-1, oldsize
+ 1, filep
);
559 if (filep
->_flag
& _IOEOF
) {