xref: /original-bsd/local/local.cmd/chgbars.c (revision 5133e8a4)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)chgbars.c	5.1 (Berkeley) 05/25/87";
9 #endif not lint
10 
11 /*
12  * Derived from original program by A. Dain Samples.
13  *
14  * The program chgbars will accept the output from a diff comparison of
15  * two versions of a file.  It will then read the new version of the file
16  * and insert the appropriate troff commands to put change bars in the
17  * right margin. Typing 'chgbars' without any arguments will give you
18  * some documentation and an example.
19  *
20  * Caveat: If you make a change inside an equation or table, the
21  * preprocessors eqn and tbl may not like what chgbars does to the file.
22  * You may have to go into the output from chgbars to remove or rearrange
23  * some of the lines of the form '.mc |  \"open' or '.mc  \"close' in
24  * order to get through tbl or eqn.
25  *
26  * Unfortunately, users of RCS will be disappointed: one cannot use rcsdiff.
27  * Rcsdiff compares the files in the wrong order.
28  *
29  * There is a relatively easy way to do the job with the tools sed and awk.
30  * However, sed does not allow enough commands to process large documents.
31  * In the true spirit of a filter/tool, chgbars is limited only by the
32  * amount of memory on the machine, and is fast and useful.
33  *
34  * The modifications necessary are outlined:
35  *
36  * FORM OF DIFF OUTPUT	COMMENT			NEW SED COMMANDS
37  * ================================================================
38  * 100a			means those lines	100a\\
39  * L1			were deleted from	.mc |\\
40  * L2			oldfile			.mc
41  * :
42  * Ln
43  * .
44  * ----------------------------------------------------------------
45  * 100c
46  * L1
47  * L2
48  * :
49  * Ln
50  * .
51  *	means that line 100 in newfile replaced all those lines in oldfile.
52  *		100a
53  *		.mc
54  *		99 a
55  *		.mc |
56  * ----------------------------------------------------------------
57  * 100d			that line was added	100a\\
58  *			to oldfile		.mc
59  *						99a\\
60  *						.mc |
61  * ----------------------------------------------------------------
62  * 100,200d		those lines were	200a\\
63  *			added to oldfile		.mc
64  *						99a\\
65  *						.mc |
66  * ----------------------------------------------------------------
67  * 100,200c
68  * L1
69  * L2
70  * :
71  * Ln
72  * .
73  *	means lines 100 to 200 of newfile replaced all the following lines
74  *	in oldfile.
75  *		200a
76  *		.mc
77  *		99a
78  *		.mc |
79  * ----------------------------------------------------------------
80  */
81 
82 #include <stdio.h>
83 #include <strings.h>
84 #include <ctype.h>
85 
86 #define dbg(s)  /* fprintf(stderr,"s\n") */
87 #define none 0
88 #define open 1
89 #define close 2
90 #define both 3
91 
92 char (*action)[];
93 FILE *file1, *file2;
94 char linebuf[1024];
95 char nextch;
96 int num1, num2, t, line;
97 char lineact;
98 
99 #ifndef lint
100 char copyright[] =
101 "@(#) Copyright (c) 1986 Regents of the University of California.\n\
102  All rights reserved.\n";
103 #endif not lint
104 
105 main(argc, argv)
106 	int argc;
107 	char *argv[];
108 {
109 	register char  *p;
110 	register int    i;
111 
112 	if (argc < 2)
113 		usage();
114 	/* open $1 */
115 	if (strcmp(argv[1], "-") == 0) {
116 		file1 = stdin;
117 		if (argc <= 2)
118 			usage();
119 	} else if ((file1 = fopen(argv[1], "r")) == NULL) {
120 		fprintf(stderr, "error: can't open %s\n", argv[1]);
121 		exit(1);
122 	}
123 	/*
124 	 * read each entry setting the appropriate entry in action[]
125 	 *
126 	 * get the first line number: since diff -e puts the numbers out
127 	 * in reverse order, this tells us how big to make the file
128 	 */
129 	readline();
130 	if (lineact == 'a')
131 		t = num1;
132 	else
133 		t = num2;
134 	action = (char (*)[]) malloc(t + 1);
135 	for (p = (char *) action, i = 0; i < t; i++)
136 		*p++ = (char) none;
137 	while (!feof(file1)) {
138 		if (lineact == 'a') {
139 			(*action)[num1] = both;
140 			skiptilldot();
141 		} else {
142 			(*action)[num1 - 1] = open;
143 			(*action)[num2] = close;
144 			if (lineact == 'c')
145 				skiptilldot();
146 			else
147 				skiptilleol();
148 		}
149 		readline();
150 	}
151 	fclose(file1);
152 	/* open $2 */
153 	if (argc == 2)
154 		file2 = stdin;
155 	else
156 		file2 = fopen(argv[2], "r");
157 	if (file2 == NULL) {
158 		fprintf(stderr, "can't open %2\n", argv[2]);
159 		exit(1);
160 	}
161 	line = 0;
162 	while (!feof(file2)) {
163 		if (line != 0)
164 			fputs(linebuf, stdout);
165 		if (line <= t) {
166 			switch ((*action)[line]) {
167 			case open:
168 				printf(".mc |   \\\"open\n");
169 				break;
170 			case close:
171 				printf(".mc     \\\"close\n");
172 				break;
173 			case both:
174 				printf(".mc |   \\\"both\n");
175 				printf(".mc\n");
176 				break;
177 			default:;
178 			}
179 		}
180 		fgets(linebuf, sizeof(linebuf), file2);
181 		line++;
182 	}
183 	if (line <= t) {
184 		fprintf(stderr, "oops: number of lines read does not match\n");
185 		fprintf(stderr, "number of lines expected\n");
186 		exit(1);
187 	}
188 	exit(0);
189 }
190 
191 usage()
192 {
193 
194 	fprintf(stderr, "Usage:\n");
195 	fprintf(stderr, "\tchgbars diff.out\n");
196 	fprintf(stderr, "\t\t\treads the output from diff, and expects the\n");
197 	fprintf(stderr, "\t\t\tfile to be modified on stdin\n");
198 	fprintf(stderr, "\tchgbars diff.out file.tbm\n");
199 	fprintf(stderr, "\t\t\tboth the output from diff and the file to be\n");
200 	fprintf(stderr, "\t\t\tmodified are stated explicitly\n");
201 	fprintf(stderr, "\tchgbars - file.tbm\n");
202 	fprintf(stderr, "\t\t\treads the output from diff on stdin\n");
203 	fprintf(stderr, "\nE.g.\n");
204 	fprintf(stderr, "diff -b -e newfile oldfile | chgbars - newfile | vtroff -ms\n");
205 	fprintf(stderr, "\t(note the order of the files in the diff command!)\n");
206 	fprintf(stderr, "\n\nBe forewarned: chgbars does not know about tables or equations.\n");
207 	fprintf(stderr, "If any part of a table or equation is changed, chgbars will insert the\n");
208 	fprintf(stderr, ".mc commands, whether tbl or eqn likes it or not.\n");
209 	fprintf(stderr, "This means that you may have to do some hand editing of the output of\n");
210 	fprintf(stderr, "chgbars to make it acceptable to one or both of these preprocessors.\n");
211 
212 	exit(1);
213 }
214 
215 readnum()
216 {
217 	int num;
218 
219 	dbg(readnum);
220 	num = 0;
221 	nextch = getc(file1);
222 	while (isdigit(nextch)) {
223 		num = num * 10 + nextch - '0';
224 		nextch = getc(file1);
225 	}
226 	return num;
227 }
228 
229 readline()
230 {
231 
232 	dbg(readline);
233 	num1 = readnum();
234 	if (nextch == ',')
235 		num2 = readnum();
236 	else
237 		num2 = num1;
238 	lineact = nextch;
239 }
240 
241 skiptilleol()
242 {
243 
244 	dbg(skiptilleol);
245 	while (nextch != '\n')
246 		nextch = getc(file1);
247 }
248 
249 skiptilldot()
250 {
251 
252 	dbg(skiptilldot);
253 	do {
254 		if (fgets(linebuf, sizeof(linebuf), file1) == NULL) {
255 			fprintf(stderr, "error reading file1\n");
256 			exit(1);
257 		}
258 	} while (strcmp(linebuf, ".\n") != 0);
259 }
260