xref: /csrg-svn/usr.bin/error/touch.c (revision 1460)
1 static	char *sccsid = "@(#)touch.c	1.1 (Berkeley) 10/16/80";
2 #include <stdio.h>
3 #include <ctype.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <signal.h>
7 #include "error.h"
8 
9 findfiles(nerrors, errors, r_nfiles, r_files)
10 	int	nerrors;
11 	struct	error_desc	**errors;
12 	int	*r_nfiles;
13 	struct	error_desc	****r_files;
14 {
15 			int	nfiles;
16 	struct	error_desc	***files;
17 
18 			char	*currentfilename;
19 	register	int	errorindex;
20 			int	fileindex;
21 	register	struct	error_desc	*errorp;
22 	/*
23 	 *	First, go through and count all of the filenames
24 	 */
25 	for (errorp = errors[errorindex = 0],nfiles = 0, currentfilename = "\1";
26 	     errorindex < nerrors;
27 	     errorp = errors[++errorindex]){
28 		if (SORTABLE(errorp->error_e_class)){
29 			if (strcmp(errorp->error_text[0],currentfilename) != 0){
30 				nfiles++;
31 				currentfilename = errorp->error_text[0];
32 			}
33 		}
34 	}
35 	files = (struct error_desc ***)Calloc(nfiles + 3,
36 		sizeof (struct error_desc**));
37 	touchedfiles = (boolean	*)Calloc(nfiles+3, sizeof(boolean));
38 	/*
39 	 *	Now, go through and partition off the error messages
40 	 *	into those that are synchronization, discarded or
41 	 *	not specific to any file, and those that were
42 	 *	nulled or true errors.
43 	 */
44 	files[0] = &errors[0];
45 	for (errorp = errors[errorindex = 0], fileindex = 0;
46 	     (errorindex < nerrors) &&
47 		(NOTSORTABLE(errorp->error_e_class));
48 	     errorp = errors[++errorindex]){
49 		continue;
50 	}
51 	/*
52 	 *	Now, go through and partition off all error messages
53 	 *	for a given file.
54 	 */
55 	files[1] = &errors[errorindex];
56 	touchedfiles[0] = touchedfiles[1] = FALSE;
57 	for (errorp = errors[errorindex], currentfilename = "\1", fileindex = 1;
58 	     errorindex < nerrors; errorp = errors[++errorindex]){
59 		if ( (errorp->error_e_class == C_NULLED) || (errorp->error_e_class == C_TRUE) ){
60 			if (strcmp(errorp->error_text[0],currentfilename) != 0){
61 				currentfilename = errorp->error_text[0];
62 				touchedfiles[fileindex] = FALSE;
63 				files[fileindex++] = &errors[errorindex];
64 			}
65 		}
66 	}
67 	files[fileindex] = &errors[nerrors];
68 	*r_nfiles = nfiles;
69 	*r_files = files;
70 }
71 
72 char	*class_table[] = {
73 	/*C_UNKNOWN	0	*/	"Unknown",
74 	/*C_IGNORE	1	*/	"ignore",
75 	/*C_SYNC	2	*/	"synchronization",
76 	/*C_DISCARD	3	*/	"discarded",
77 	/*C_NONSPEC	4	*/	"non specific",
78 	/*C_THISFILE	5	*/	"specific to this file",
79 	/*C_NULLED	6	*/	"nulled",
80 	/*C_TRUE	7	*/	"true",
81 	/*C_DUPL	8	*/	"duplicated"
82 };
83 
84 int	class_count[C_LAST - C_FIRST] = {0};
85 
86 filenames(nfiles, files)
87 	int	nfiles;
88 	struct	error_desc	***files;
89 {
90 	register	int	fileindex;
91 	register	struct	error_desc	*errorp;
92 	register	struct	error_desc	**erpp;
93 			char	*sep = " ";
94 	register	int	errortype;
95 	extern		char	*class_table[];
96 			int	someerrors = 0;
97 
98 	/*
99 	 *	first, go through and simply dump out errors that
100 	 *	don't pertain to any file
101 	 */
102 	if (files[1] - files[0] > 0){
103 	    for(errortype = C_UNKNOWN; NOTSORTABLE(errortype); errortype++){
104 		if (class_count[errortype] > 0){
105 			if (errortype > C_SYNC)
106 				someerrors++;
107 			fprintf(stdout, "\n\t%d %s errors follow:\n",
108 				class_count[errortype], class_table[errortype]);
109 			for (errorp = *(erpp = files[0]);
110 			     erpp < files[1];
111 			     errorp = (*++erpp)){
112 				if (errorp->error_e_class == errortype)
113 					errorprint(stdout, errorp, TRUE);
114 			}
115 		}
116 	    }
117 	}
118 	if (nfiles){
119 		someerrors++;
120 		fprintf(stdout, "%d files contain errors:", nfiles);
121 		for (fileindex = 1; fileindex <= nfiles; fileindex++){
122 			fprintf(stdout, "%s\"%s\" (%d)",
123 				sep, (*files[fileindex])->error_text[0],
124 				files[fileindex+1] - files[fileindex]);
125 			sep = ", ";
126 		}
127 		fprintf(stdout, "\n");
128 	}
129 	if (!someerrors)
130 		fprintf(stdout, "No errors.\n");
131 }
132 
133 extern	boolean	notouch;
134 
135 boolean touchfiles(nfiles, files, r_edargc, r_edargv)
136 	int	nfiles;
137 	struct	error_desc	***files;
138 	int	*r_edargc;
139 	char	***r_edargv;
140 {
141 			char	*currentfilename;
142 	register	struct	error_desc	*errorp;
143 	register	int	fileindex;
144 	register	struct	error_desc	**erpp;
145 			int		ntrueerrors;
146 			int		errordest;	/* where errors go*/
147 			char		*sep;
148 			boolean		scribbled;
149 			int		n_pissed_on;	/* how many files touched*/
150 	for (fileindex = 1; fileindex <= nfiles; fileindex++){
151 		fprintf(stdout, "\nFile \"%s\" has %d total error messages.\n",
152 			currentfilename = (*files[fileindex])->error_text[0],
153 			files[fileindex+1] - files[fileindex]);
154 		/*
155 		 *	First, iterate through all error messages in this file
156 		 *	to see how many of the error messages really will
157 		 *	get inserted into the file.
158 		 */
159 		for (erpp = files[fileindex], ntrueerrors = 0;
160 		     erpp < files[fileindex+1];
161 		     erpp++){
162 			errorp = *erpp;
163 			if (errorp->error_e_class == C_TRUE)
164 				ntrueerrors++;
165 		}
166 		fprintf(stdout,"\t%d of these errors can be inserted into the file.\n",
167 			ntrueerrors);
168 
169 		/*
170 		 *	What does the operator want?
171 		 */
172 		errordest = TOSTDOUT;
173 		if (oktotouch(currentfilename) && (ntrueerrors > 0) ){
174 			if (query && inquire("Do you want to preview the errors first?")){
175 				for (erpp = files[fileindex];
176 				     erpp < files[fileindex + 1];
177 				     erpp++){
178 					errorprint(stdout, *erpp, TRUE);
179 				}
180 				fprintf(stdout, "\n");
181 			}
182 			if (   !query
183 			    || inquire("Do you want to touch file \"%s\"? ",
184 					currentfilename)
185 			){
186 				errordest = TOTHEFILE;
187 				if (!probethisfile(currentfilename)){
188 					errordest = TOSTDOUT;
189 					fprintf(stdout,
190 					 "Can't find file \"%s\" to insert error messages into.\n",
191 						currentfilename);
192 				} else {
193 					if (edit(currentfilename))
194 						errordest = TOSTDOUT;
195 					else
196 						touchedfiles[fileindex] = TRUE;
197 				}
198 			}
199 		}
200 		/*
201 		 *	go through and print each error message,
202 		 *	diverting to the right place
203 		 */
204 		if ( (files[fileindex+1] - files[fileindex]) != ntrueerrors)
205 			fprintf(stdout,
206 			    ">>Uninserted error messages for file \"%s\" follow.\n",
207 			    currentfilename);
208 		for (erpp = files[fileindex];erpp < files[fileindex+1];erpp++){
209 			errorp = *erpp;
210 			if (errorp->error_e_class == C_TRUE){
211 				switch (errordest){
212 				  case TOSTDOUT:
213 					errorprint(stdout, errorp, TRUE);
214 					  break;
215 				  case TOTHEFILE:
216 					insert(errorp->error_line);
217 					text(errorp, FALSE);
218 					break;
219 				}	/* switch */
220 			} else {
221 				errorprint(stdout, errorp, TRUE);
222 			}
223 		}	/* end of walking through all errors*/
224 		if (errordest == TOTHEFILE){
225 			writetouched();
226 		}
227 	}	/* end of walking through all files*/
228 	scribbled = FALSE;
229 	for (n_pissed_on = 0, fileindex = 1; fileindex <= nfiles; fileindex++){
230 		scribbled |= touchedfiles[fileindex];
231 		n_pissed_on++;
232 	}
233 	if (scribbled){
234 		/*
235 		 *	Construct an execv argument
236 		 *	We need 1 argument for the editor's name
237 		 *	We need 1 argument for the initial search string
238 		 *	We need n_pissed_on arguments for the file names
239 		 *	We need 1 argument that is a null for execv.
240 		 *	The caller fills in the editor's name.
241 		 *	We fill in the initial search string.
242 		 *	We fill in the arguments, and the null.
243 		 */
244 		(*r_edargv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *));
245 		(*r_edargc) =  n_pissed_on + 2;
246 		(*r_edargv)[1] = "+/###/";
247 		n_pissed_on = 2;
248 		fprintf(stdout, "You touched file(s):");
249 		sep = " ";
250 		for (fileindex = 1; fileindex <= nfiles; fileindex++){
251 			if (!touchedfiles[fileindex])
252 				continue;
253 			errorp = *(files[fileindex]);
254 			fprintf(stdout,"%s\"%s\"", sep, errorp->error_text[0]);
255 			sep = ", ";
256 			(*r_edargv)[n_pissed_on++] = errorp->error_text[0];
257 		}
258 		fprintf(stdout, "\n");
259 		(*r_edargv)[n_pissed_on] = 0;
260 		return(TRUE);
261 	} else {
262 		fprintf(stdout, "You didn't touch any files.\n");
263 		return(FALSE);
264 	}
265 
266 }	/* end of touchfiles*/
267 int	oktotouch(filename)
268 	char	*filename;
269 {
270 	extern		char	*suffixlist;
271 	register	char	*src;
272 	register	char	*pat;
273 			char	*osrc;
274 
275 	pat = suffixlist;
276 	if (pat == 0)
277 		return(0);
278 	if (*pat == '*')
279 		return(1);
280 	while (*pat++ != '.')
281 		continue;
282 	--pat;		/* point to the period */
283 
284 	for (src = &filename[strlen(filename)], --src;
285 	     (src > filename) && (*src != '.'); --src)
286 		continue;
287 	if (*src != '.')
288 		return(0);
289 
290 	for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){
291 		for (;   *src			/* not at end of the source */
292 		      && *pat			/* not off end of pattern */
293 		      && *pat != '.'		/* not off end of sub pattern */
294 		      && *pat != '*'		/* not wild card */
295 		      && *src == *pat;		/* and equal... */
296 		      src++, pat++)
297 			continue;
298 		if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
299 			return(1);
300 		if (*src != 0 && *pat == '*')
301 			return(1);
302 		while (*pat && *pat != '.')
303 			pat++;
304 		if (! *pat)
305 			return(0);
306 	}
307 	return(0);
308 }
309 
310 FILE	*o_touchedfile;	/* the old file */
311 FILE	*n_touchedfile;	/* the new file */
312 char	*o_name;
313 char	n_name[32];
314 char	*canon_name = "ErrorXXXXXX";
315 int	o_lineno;
316 int	n_lineno;
317 boolean	tempfileopen = FALSE;
318 /*
319  *	open the file; guaranteed to be both readable and writable
320  *	Well, if it isn't, then return TRUE if something failed
321  */
322 boolean edit(name)
323 	char	*name;
324 {
325 	o_name = name;
326 	if ( (o_touchedfile = fopen(name, "r")) == NULL){
327 		fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n",
328 			processname, name);
329 		return(TRUE);
330 	}
331 	strcpy(n_name, canon_name);
332 	mktemp(n_name);
333 	if ( (n_touchedfile = fopen(n_name, "w")) == NULL){
334 		fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n",
335 			processname, name);
336 		return(TRUE);
337 	}
338 	tempfileopen = TRUE;
339 	n_lineno = 0;
340 	o_lineno = 0;
341 	return(FALSE);
342 }
343 /*
344  *	Position to the line (before, after) the line given by place
345  */
346 char	edbuffer[BUFSIZ];
347 insert(place)
348 	int	place;
349 {
350 	--place;	/* always insert messages before the offending line*/
351 	for(; o_lineno < place; o_lineno++, n_lineno++){
352 		if(fgets(edbuffer, BUFSIZ, o_touchedfile) == NULL)
353 			return;
354 		fputs(edbuffer, n_touchedfile);
355 	}
356 }
357 
358 text(errorp, use_all)
359 	register	struct	error_desc	*errorp;
360 	boolean	use_all;
361 {
362 	int	offset = use_all ? 0 : 2;
363 	fputs(lang_table[errorp->error_language].lang_incomment, n_touchedfile);
364 	fprintf(n_touchedfile, "%d [%s] ",
365 		errorp->error_line,
366 		lang_table[errorp->error_language].lang_name);
367 	wordvprint(n_touchedfile,
368 		errorp->error_lgtext-offset, errorp->error_text+offset);
369 	fputs(lang_table[errorp->error_language].lang_outcomment,n_touchedfile);
370 	n_lineno++;
371 }
372 
373 writetouched()
374 {
375 	int	bytes_read;
376 	for(; (bytes_read = fread(edbuffer, 1, sizeof(edbuffer), o_touchedfile))!= NULL; ){
377 		fwrite(edbuffer, 1, bytes_read, n_touchedfile);
378 	}
379 	fclose(n_touchedfile);
380 	fclose(o_touchedfile);
381 	unlink(o_name);
382 	link(n_name, o_name);
383 	unlink(n_name);
384 	tempfileopen = FALSE;
385 }
386 onintr()
387 {
388 	if (inquire("\nInterrupt: Do you want to continue?")){
389 		signal(SIGINT, onintr);
390 		return;
391 	}
392 	if (tempfileopen)
393 		writetouched();
394 	exit(1);
395 }
396 errorprint(place, errorp, print_all)
397 	FILE	*place;
398 	struct	error_desc	*errorp;
399 	boolean	print_all;
400 {
401 	int	offset = print_all ? 0 : 2;
402 
403 	if (errorp->error_e_class == C_IGNORE)
404 		return;
405 	fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name);
406 	wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset);
407 	putc('\n', place);
408 }
409 
410 boolean inquire(fmt, a1, a2)
411 	char	*fmt;
412 	/*VARARGS1*/
413 {
414 	char	buffer[128];
415 	char	ch;
416 	for(;;){
417 		do{
418 			fflush(stdout);
419 			fprintf(stderr, fmt, a1, a2);
420 			fflush(stderr);
421 		} while (fgets(buffer, 127, queryfile) == NULL);
422 		ch = buffer[0];
423 		if (ch == 'Y' || ch == 'y')
424 			return(TRUE);
425 		if (ch == 'N' || ch == 'n')
426 			return(FALSE);
427 		fprintf(stderr, "Yes or No only!\n");
428 	}
429 }
430 
431 boolean probethisfile(currentfilename)
432 	char	*currentfilename;
433 {
434 	struct stat statbuf;
435 	if (stat(currentfilename, &statbuf) != 0)
436 		return(FALSE);
437 	if ( (statbuf.st_mode&S_IREAD) && (statbuf.st_mode&S_IWRITE))
438 		return(TRUE);
439 	return(FALSE);
440 }
441