open /dev/tty instead of ttyname(2) for input
[unix-history] / usr / src / usr.bin / error / touch.c
CommitLineData
c99ba90a
BJ
1static char *sccsid = "@(#)touch.c 1.1 (Berkeley) %G%";
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
9findfiles(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
72char *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
84int class_count[C_LAST - C_FIRST] = {0};
85
86filenames(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
133extern boolean notouch;
134
135boolean 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*/
267int 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
310FILE *o_touchedfile; /* the old file */
311FILE *n_touchedfile; /* the new file */
312char *o_name;
313char n_name[32];
314char *canon_name = "ErrorXXXXXX";
315int o_lineno;
316int n_lineno;
317boolean 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 */
322boolean 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 */
346char edbuffer[BUFSIZ];
347insert(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
358text(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
373writetouched()
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}
386onintr()
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}
396errorprint(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
410boolean 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
431boolean 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}