xref: /original-bsd/usr.bin/error/touch.c (revision 6c57d260)
1 static	char *sccsid = "@(#)touch.c	1.2 (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;	/* # of file touched*/
150 			int		previewed;
151 
152 	for (fileindex = 1; fileindex <= nfiles; fileindex++){
153 		fprintf(stdout, "\nFile \"%s\" has %d total error messages.\n",
154 			currentfilename = (*files[fileindex])->error_text[0],
155 			files[fileindex+1] - files[fileindex]);
156 		/*
157 		 *	First, iterate through all error messages in this file
158 		 *	to see how many of the error messages really will
159 		 *	get inserted into the file.
160 		 */
161 		for (erpp = files[fileindex], ntrueerrors = 0;
162 		     erpp < files[fileindex+1];
163 		     erpp++){
164 			errorp = *erpp;
165 			if (errorp->error_e_class == C_TRUE)
166 				ntrueerrors++;
167 		}
168 		fprintf(stdout,"\t%d of these errors can be inserted into the file.\n",
169 			ntrueerrors);
170 
171 		/*
172 		 *	What does the operator want?
173 		 */
174 		previewed = 0;
175 		errordest = TOSTDOUT;
176 		if (oktotouch(currentfilename) && (ntrueerrors > 0) ){
177 			if (query && inquire("Do you want to preview the errors first?")){
178 				previewed = 1;
179 				for (erpp = files[fileindex];
180 				     erpp < files[fileindex + 1];
181 				     erpp++){
182 					errorprint(stdout, *erpp, TRUE);
183 				}
184 				fprintf(stdout, "\n");
185 			}
186 			if (   !query
187 			    || inquire("Do you want to touch file \"%s\"? ",
188 					currentfilename)
189 			){
190 				errordest = TOTHEFILE;
191 				if (!probethisfile(currentfilename)){
192 					errordest = TOSTDOUT;
193 					fprintf(stdout,
194 					 "Can't find file \"%s\" to insert error messages into.\n",
195 						currentfilename);
196 				} else {
197 					if (edit(currentfilename))
198 						errordest = TOSTDOUT;
199 					else
200 						touchedfiles[fileindex] = TRUE;
201 				}
202 			}
203 		}
204 		if (previewed && (errordest == TOSTDOUT))
205 			continue;		/* with the next file */
206 		/*
207 		 *	go through and print each error message,
208 		 *	diverting to the right place
209 		 */
210 		if ( (files[fileindex+1] - files[fileindex]) != ntrueerrors)
211 			if (!previewed) fprintf(stdout,
212 			    ">>Uninserted error messages for file \"%s\" follow.\n",
213 			    currentfilename);
214 		for (erpp = files[fileindex];erpp < files[fileindex+1];erpp++){
215 			errorp = *erpp;
216 			if (errorp->error_e_class == C_TRUE){
217 				switch (errordest){
218 				  case TOSTDOUT:
219 					if (!previewed)
220 						errorprint(stdout,errorp, TRUE);
221 					break;
222 				  case TOTHEFILE:
223 					insert(errorp->error_line);
224 					text(errorp, FALSE);
225 					break;
226 				}	/* switch */
227 			} else {
228 				if (!previewed)
229 					errorprint(stdout, errorp, TRUE);
230 			}
231 		}	/* end of walking through all errors*/
232 		if (errordest == TOTHEFILE){
233 			writetouched();
234 		}
235 	}	/* end of walking through all files*/
236 	scribbled = FALSE;
237 	for (n_pissed_on = 0, fileindex = 1; fileindex <= nfiles; fileindex++){
238 		scribbled |= touchedfiles[fileindex];
239 		n_pissed_on++;
240 	}
241 	if (scribbled){
242 		/*
243 		 *	Construct an execv argument
244 		 *	We need 1 argument for the editor's name
245 		 *	We need 1 argument for the initial search string
246 		 *	We need n_pissed_on arguments for the file names
247 		 *	We need 1 argument that is a null for execv.
248 		 *	The caller fills in the editor's name.
249 		 *	We fill in the initial search string.
250 		 *	We fill in the arguments, and the null.
251 		 */
252 		(*r_edargv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *));
253 		(*r_edargc) =  n_pissed_on + 2;
254 		(*r_edargv)[1] = "+/###/";
255 		n_pissed_on = 2;
256 		fprintf(stdout, "You touched file(s):");
257 		sep = " ";
258 		for (fileindex = 1; fileindex <= nfiles; fileindex++){
259 			if (!touchedfiles[fileindex])
260 				continue;
261 			errorp = *(files[fileindex]);
262 			fprintf(stdout,"%s\"%s\"", sep, errorp->error_text[0]);
263 			sep = ", ";
264 			(*r_edargv)[n_pissed_on++] = errorp->error_text[0];
265 		}
266 		fprintf(stdout, "\n");
267 		(*r_edargv)[n_pissed_on] = 0;
268 		return(TRUE);
269 	} else {
270 		fprintf(stdout, "You didn't touch any files.\n");
271 		return(FALSE);
272 	}
273 
274 }	/* end of touchfiles*/
275 int	oktotouch(filename)
276 	char	*filename;
277 {
278 	extern		char	*suffixlist;
279 	register	char	*src;
280 	register	char	*pat;
281 			char	*osrc;
282 
283 	pat = suffixlist;
284 	if (pat == 0)
285 		return(0);
286 	if (*pat == '*')
287 		return(1);
288 	while (*pat++ != '.')
289 		continue;
290 	--pat;		/* point to the period */
291 
292 	for (src = &filename[strlen(filename)], --src;
293 	     (src > filename) && (*src != '.'); --src)
294 		continue;
295 	if (*src != '.')
296 		return(0);
297 
298 	for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){
299 		for (;   *src			/* not at end of the source */
300 		      && *pat			/* not off end of pattern */
301 		      && *pat != '.'		/* not off end of sub pattern */
302 		      && *pat != '*'		/* not wild card */
303 		      && *src == *pat;		/* and equal... */
304 		      src++, pat++)
305 			continue;
306 		if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
307 			return(1);
308 		if (*src != 0 && *pat == '*')
309 			return(1);
310 		while (*pat && *pat != '.')
311 			pat++;
312 		if (! *pat)
313 			return(0);
314 	}
315 	return(0);
316 }
317 
318 FILE	*o_touchedfile;	/* the old file */
319 FILE	*n_touchedfile;	/* the new file */
320 char	*o_name;
321 char	n_name[32];
322 char	*canon_name = "ErrorXXXXXX";
323 int	o_lineno;
324 int	n_lineno;
325 boolean	tempfileopen = FALSE;
326 /*
327  *	open the file; guaranteed to be both readable and writable
328  *	Well, if it isn't, then return TRUE if something failed
329  */
330 boolean edit(name)
331 	char	*name;
332 {
333 	o_name = name;
334 	if ( (o_touchedfile = fopen(name, "r")) == NULL){
335 		fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n",
336 			processname, name);
337 		return(TRUE);
338 	}
339 	strcpy(n_name, canon_name);
340 	mktemp(n_name);
341 	if ( (n_touchedfile = fopen(n_name, "w")) == NULL){
342 		fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n",
343 			processname, name);
344 		return(TRUE);
345 	}
346 	tempfileopen = TRUE;
347 	n_lineno = 0;
348 	o_lineno = 0;
349 	return(FALSE);
350 }
351 /*
352  *	Position to the line (before, after) the line given by place
353  */
354 char	edbuffer[BUFSIZ];
355 insert(place)
356 	int	place;
357 {
358 	--place;	/* always insert messages before the offending line*/
359 	for(; o_lineno < place; o_lineno++, n_lineno++){
360 		if(fgets(edbuffer, BUFSIZ, o_touchedfile) == NULL)
361 			return;
362 		fputs(edbuffer, n_touchedfile);
363 	}
364 }
365 
366 text(errorp, use_all)
367 	register	struct	error_desc	*errorp;
368 	boolean	use_all;
369 {
370 	int	offset = use_all ? 0 : 2;
371 	fputs(lang_table[errorp->error_language].lang_incomment, n_touchedfile);
372 	fprintf(n_touchedfile, "%d [%s] ",
373 		errorp->error_line,
374 		lang_table[errorp->error_language].lang_name);
375 	wordvprint(n_touchedfile,
376 		errorp->error_lgtext-offset, errorp->error_text+offset);
377 	fputs(lang_table[errorp->error_language].lang_outcomment,n_touchedfile);
378 	n_lineno++;
379 }
380 
381 writetouched()
382 {
383 	int	bytes_read;
384 	for(; (bytes_read = fread(edbuffer, 1, sizeof(edbuffer), o_touchedfile))!= NULL; ){
385 		fwrite(edbuffer, 1, bytes_read, n_touchedfile);
386 	}
387 	fclose(n_touchedfile);
388 	fclose(o_touchedfile);
389 	unlink(o_name);
390 	link(n_name, o_name);
391 	unlink(n_name);
392 	tempfileopen = FALSE;
393 }
394 onintr()
395 {
396 	if (inquire("\nInterrupt: Do you want to continue?")){
397 		signal(SIGINT, onintr);
398 		return;
399 	}
400 	if (tempfileopen)
401 		writetouched();
402 	exit(1);
403 }
404 errorprint(place, errorp, print_all)
405 	FILE	*place;
406 	struct	error_desc	*errorp;
407 	boolean	print_all;
408 {
409 	int	offset = print_all ? 0 : 2;
410 
411 	if (errorp->error_e_class == C_IGNORE)
412 		return;
413 	fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name);
414 	wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset);
415 	putc('\n', place);
416 }
417 
418 boolean inquire(fmt, a1, a2)
419 	char	*fmt;
420 	/*VARARGS1*/
421 {
422 	char	buffer[128];
423 	char	ch;
424 	for(;;){
425 		do{
426 			fflush(stdout);
427 			fprintf(stderr, fmt, a1, a2);
428 			fflush(stderr);
429 		} while (fgets(buffer, 127, queryfile) == NULL);
430 		ch = buffer[0];
431 		if (ch == 'Y' || ch == 'y')
432 			return(TRUE);
433 		if (ch == 'N' || ch == 'n')
434 			return(FALSE);
435 		fprintf(stderr, "Yes or No only!\n");
436 	}
437 }
438 
439 boolean probethisfile(currentfilename)
440 	char	*currentfilename;
441 {
442 	struct stat statbuf;
443 	if (stat(currentfilename, &statbuf) != 0)
444 		return(FALSE);
445 	if ( (statbuf.st_mode&S_IREAD) && (statbuf.st_mode&S_IWRITE))
446 		return(TRUE);
447 	return(FALSE);
448 }
449