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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 #pragma ident "%Z%%M% %I% %E% SMI"
34 * sdiff [-l] [-s] [-w #] [-o output] file1 file2
35 * does side by side diff listing
36 * -l leftside only for identical lines
37 * -s silent; only print differences
38 * -w # width of output
39 * -o output interactive creation of new output commands:
40 * s silent; do not print identical lines
42 * l copy left side to output
43 * r copy right side to output
44 * e l call ed with left side
45 * e r call ed with right side
46 * e b call ed with cat of left and right
47 * e call ed with empty file
51 * cmd decode diff commands
52 * put1 output left side
53 * put2 output right side
54 * putmid output gutter
55 * putline output n chars to indicated file
56 * getlen calculate length of strings with tabs
57 * cmdin read and process interactive cmds
58 * cpp copy from file to file
59 * edit call ed with file
65 #include <sys/types.h>
79 #define WLEN (WGUTTER * 2 + WGUTTER + 2)
82 static const char twoblanks
[3] = " ";
84 static const char *DIFF
= "diff -b ";
85 static char diffcmd
[BMAX
];
86 static char inbuf
[10];
88 static int llen
= 130; /* Default maximum line length written out */
89 static int hlen
; /* Half line length with space for gutter */
90 static int len1
; /* Calculated length of left side */
91 static int nchars
; /* Number of characters in left side - */
92 /* used for tab expansion */
93 static char change
= ' ';
94 static int leftonly
= 0; /* if set print left side only for */
96 static int silent
= 0; /* if set do not print identical lines */
97 static int midflg
= 0; /* set after middle was output */
98 static int rcode
= 0; /* return code */
107 static FILE *diffdes
;
119 static FILE *tempdes
;
122 /* decoded diff cmd- left side from to; right side from, to */
124 static int from1
, to1
, from2
, to2
;
126 static int num1
, num2
; /* line count for left side file and right */
127 static int tempfd
= -1;
129 static char *filename(char *, char *);
130 static char *fgetline(FILE *);
131 static int put1(void);
132 static int put2(void);
133 static void putline(FILE *, char *, int);
134 static int cmd(char *);
135 static int getlen(int, char *);
136 static void putmid(int);
137 static void error(char *, char *);
138 static void onintr(void);
139 static void sremove(void);
140 static void cmdin(void);
141 static void cpp(char *, FILE *, FILE *);
142 static void edit(char *);
145 main(int argc
, char **argv
)
153 if (signal(SIGHUP
, SIG_IGN
) != SIG_IGN
)
154 (void) signal((int)SIGHUP
, (void (*)(int))onintr
);
155 if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
156 (void) signal((int)SIGINT
, (void (*)(int))onintr
);
157 if (signal(SIGPIPE
, SIG_IGN
) != SIG_IGN
)
158 (void) signal((int)SIGPIPE
, (void (*)(int))onintr
);
159 if (signal(SIGTERM
, SIG_IGN
) != SIG_IGN
)
160 (void) signal((int)SIGTERM
, (void (*)(int))onintr
);
162 (void) setlocale(LC_ALL
, "");
163 #if !defined(TEXT_DOMAIN)
164 #define TEXT_DOMAIN "SYS_TEST"
166 (void) textdomain(TEXT_DOMAIN
);
168 while (--argc
> 1 && **++argv
== '-') {
172 /* -w# instead of -w # */
177 llen
= atoi(*++argv
);
180 error(gettext("Wrong line length %s"), *argv
);
198 error(gettext("Illegal argument: %s"), *argv
);
202 (void) fprintf(stderr
, gettext(
203 "Usage: sdiff [-l] [-s] [-o output] [-w #] file1 file2\n"));
209 file1
= filename(file1
, file2
);
210 file2
= filename(file2
, file1
);
211 hlen
= (llen
- WGUTTER
+1)/2;
213 if ((fdes1
= fopen(file1
, "r")) == NULL
)
214 error(gettext("Cannot open: %s"), file1
);
216 if ((fdes2
= fopen(file2
, "r")) == NULL
)
217 error(gettext("Cannot open: %s"), file2
);
221 temp
= strdup("/tmp/sdiffXXXXXX");
222 tempfd
= mkstemp(temp
);
225 "Cannot open/create temp %s"), temp
);
230 ltemp
= strdup("/tmp/sdifflXXXXXX");
231 if ((lfd
= mkstemp(ltemp
)) == -1 ||
232 (left
= fdopen(lfd
, "w")) == NULL
)
234 "Cannot open/create temp %s"),
236 rtemp
= strdup("/tmp/sdiffrXXXXXX");
237 if ((rfd
= mkstemp(rtemp
)) == -1 ||
238 (right
= fdopen(rfd
, "w")) == NULL
)
240 "Cannot open/create temp file %s"),
242 if ((odes
= fopen(ofile
, "w")) == NULL
)
243 error(gettext("Cannot open output %s"), ofile
);
245 /* Call DIFF command */
246 (void) strcpy(diffcmd
, DIFF
);
247 (void) strcat(diffcmd
, file1
);
248 (void) strcat(diffcmd
, " ");
249 (void) strcat(diffcmd
, file2
);
250 diffdes
= popen(diffcmd
, "r");
255 * Read in diff output and decode commands
256 * "change" is used to determine character to put in gutter
257 * num1 and num2 counts the number of lines in file1 and 2
261 while ((bp
= fgetline(diffdes
)) != NULL
) {
266 * handles all diff output that is not cmd
267 * lines starting with <, >, ., ---
272 /* Catch up to from1 and from2 */
276 n
= n1
> n2
? n2
: n1
;
277 if (com
== 'c' && n
> 0)
280 (void) fputs(bp
, stdout
);
285 (void) putc('\n', stdout
);
289 /* Process diff cmd */
296 (void) putc('\n', stdout
);
305 (void) putc('\n', stdout
);
313 n
= n1
> n2
? n2
: n1
;
318 (void) putc('\n', stdout
);
325 (void) putc('\n', stdout
);
332 (void) putc('\n', stdout
);
338 (void) fprintf(stderr
, gettext(
339 "%c: cmd not found\n"), cmd
);
343 if (oflag
== 1 && com
!= 0) {
345 if ((left
= fopen(ltemp
, "w")) == NULL
)
347 "main: Cannot open temp %s"), ltemp
);
348 if ((right
= fopen(rtemp
, "w")) == NULL
)
350 "main: Cannot open temp %s"), rtemp
);
353 /* put out remainder of input files */
358 (void) putc('\n', stdout
);
370 /* len1 = length of left side */
371 /* nchars = num of chars including tabs */
376 if ((bp
= fgetline(fdes1
)) != NULL
) {
377 len1
= getlen(0, bp
);
378 if ((!silent
|| change
!= ' ') && len1
!= 0)
379 putline(stdout
, bp
, nchars
);
383 * put left side either to output file
384 * if identical to right
385 * or left temp file if not
389 putline(odes
, bp
, strlen(bp
));
391 putline(left
, bp
, strlen(bp
));
406 if ((bp
= fgetline(fdes2
)) != NULL
) {
407 (void) getlen((hlen
+ WGUTTER
) % 8, bp
);
410 * if the left and right are different they are always
412 * If the left and right are identical
413 * right is only printed if leftonly is not specified
414 * or silent mode is not specified
415 * or the right contains other than white space (len1 !=0)
420 * put right side to right temp file only
421 * because left side was written to output for
426 putline(right
, bp
, strlen(bp
));
430 putline(stdout
, bp
, nchars
);
432 if (!silent
&& !leftonly
&& len1
!= 0) {
435 putline(stdout
, bp
, nchars
);
447 putline(FILE *file
, char *start
, int num
)
457 (void) putc(*cp
++, file
);
461 if ((len
= end
- cp
) > MB_LEN_MAX
)
464 if ((len
= mbtowc(&wc
, cp
, len
)) <= 0) {
465 (void) putc(*cp
++, file
);
469 if ((d_col
= wcwidth(wc
)) <= 0)
472 if ((cp
+ d_col
) > end
)
475 for (i
= 0; i
< len
; i
++)
476 (void) putc(*cp
++, file
);
487 if (*start
== '>' || *start
== '<' || *start
== '-' || *start
== '.')
490 cp
= (unsigned char *)start
;
522 getlen(int startpos
, char *buffer
)
525 * get the length of the string in buffer
526 * expand tabs to next multiple of 8
529 int slen
, tlen
, len
, d_col
;
536 for (cp
= (unsigned char *)buffer
; (*cp
!= '\n') && (*cp
); cp
++) {
539 tlen
+= 8 - (tlen
% 8);
561 if ((len
= mbtowc(&wc
, (char *)cp
, MB_LEN_MAX
)) <= 0) {
573 if ((d_col
= wcwidth(wc
)) <= 0)
586 return (notspace
? tlen
: 0);
595 * len1 set by getlen to the possibly truncated
596 * length of left side
597 * hlen is length of half line
602 for (i
= 0; i
< hlen
- len1
; i
++)
603 (void) putc(' ', stdout
);
605 (void) fputs(twoblanks
, stdout
);
606 (void) putc((int)change
, stdout
);
607 (void) fputs(twoblanks
, stdout
);
611 error(char *s1
, char *s2
)
613 (void) fprintf(stderr
, "sdiff: ");
614 (void) fprintf(stderr
, s1
, s2
);
615 (void) putc('\n', stderr
);
631 (void) unlink(ltemp
);
635 (void) unlink(rtemp
);
651 (void) fclose(right
);
654 (void) putc(PROMPT
, stdout
);
655 if ((cp
= fgets(inbuf
, 10, stdin
)) == NULL
) {
656 (void) putc('\n', stdout
);
676 cpp(ltemp
, left
, odes
);
681 cpp(rtemp
, right
, odes
);
705 if ((tempdes
= fopen(temp
, "w")) == NULL
)
707 "Cannot open temp file %s"),
709 cpp(ltemp
, left
, tempdes
);
710 cpp(rtemp
, right
, tempdes
);
711 (void) fclose(tempdes
);
718 if ((tempdes
= fopen(temp
, "w")) == NULL
)
720 "Cannot open temp file %s"),
722 (void) fclose(tempdes
);
728 (void) fprintf(stderr
, gettext(
729 "Illegal command %s reenter\n"),
734 cpp(ename
, tempdes
, odes
);
738 (void) fprintf(stderr
, gettext(
739 "Illegal command reenter\n"));
746 cpp(char *from
, FILE *fromdes
, FILE *todes
)
748 char tempbuf
[BMAX
+ 1];
750 if ((fromdes
= fopen(from
, "r")) == NULL
)
752 "cpp: Cannot open %s"), from
);
753 while ((fgets(tempbuf
, BMAX
, fromdes
) != NULL
))
754 (void) fputs(tempbuf
, todes
);
755 (void) fclose(fromdes
);
763 void (*oldintr
)(int);
765 switch (pid
= fork()) {
767 error(gettext("Cannot fork"), NULL
);
771 (void) execl("/usr/bin/ed", "ed", file
, NULL
);
774 oldintr
= signal(SIGINT
, SIG_IGN
); /* ignore interrupts in ed */
775 while (pid
!= wait(&i
))
777 /* restore previous interrupt proc */
778 (void) signal(SIGINT
, oldintr
);
782 filename(char *pa1
, char *pa2
)
789 if (stat(a1
, &stbuf
) != -1 && ((stbuf
.st_mode
&S_IFMT
) == S_IFDIR
)) {
790 b1
= pa1
= (char *)malloc(strlen(a1
) + strlen(a2
) + 2);
791 while (*b1
++ = *a1
++);
794 while (*a1
++ = *a2
++)
795 if (*a2
&& *a2
!= '/' && a2
[-1] == '/')
797 } else if (a1
[0] == '-' && a1
[1] == 0 && temp
== 0) {
798 if (fstat(fileno(stdin
), &stbuf
) == -1)
799 error(gettext("Cannot process stdin"), NULL
);
800 pa1
= temp
= strdup("/tmp/sdiffXXXXXX");
801 if ((tempfd
= mkstemp(temp
)) == -1 ||
802 (tempdes
= fdopen(tempfd
, "w")) == NULL
)
803 error(gettext("Cannot open/create temp %s"),
805 while ((c
= getc(stdin
)) != EOF
)
806 (void) putc(c
, tempdes
);
807 (void) fclose(tempdes
);
813 * like fgets, but reads upto and including a newline,
814 * the data is stored in a reusable dynamic buffer that grows to fit
815 * the largest line in the file, the buffer is NULL terminated
816 * returns a pointer to the dynamic buffer.
821 static char *bp
= NULL
;
826 /* allocate it for the first time */
827 bp
= (char *)malloc(BUFSIZ
);
829 error(gettext("fgetline: malloc failed"), NULL
);
833 /* check for error or nothing read */
834 if (fgets(bp
, blen
, fp
) == NULL
)
840 while ((sl
= strlen(bp
)) == blen
-1 && *(bp
+blen
-2) != '\n') {
841 /* still more data, grow the buffer */
843 bp
= (char *)realloc(bp
, blen
);
845 error(gettext("fgetline: realloc failed"), NULL
);
846 /* continue reading and add to end of buffer */
847 (void) fgets(bp
+sl
, blen
-sl
, fp
);