Annotation of src/usr.bin/error/touch.c, Revision 1.1.1.1
1.1 deraadt 1: /* $NetBSD: touch.c,v 1.3 1995/09/02 06:15:54 jtc Exp $ */
2:
3: /*
4: * Copyright (c) 1980, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: #if 0
38: static char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93";
39: #endif
40: static char rcsid[] = "$NetBSD: touch.c,v 1.3 1995/09/02 06:15:54 jtc Exp $";
41: #endif /* not lint */
42:
43: #include <sys/types.h>
44: #include <sys/stat.h>
45: #include <signal.h>
46: #include <unistd.h>
47: #include <stdio.h>
48: #include <ctype.h>
49: #include <stdlib.h>
50: #include <string.h>
51: #include "error.h"
52: #include "pathnames.h"
53:
54: /*
55: * Iterate through errors
56: */
57: #define EITERATE(p, fv, i) for (p = fv[i]; p < fv[i+1]; p++)
58: #define ECITERATE(ei, p, lb) for (ei = lb; p = errors[ei],ei < nerrors; ei++)
59:
60: #define FILEITERATE(fi, lb) for (fi = lb; fi <= nfiles; fi++)
61: int touchstatus = Q_YES;
62:
63: findfiles(nerrors, errors, r_nfiles, r_files)
64: int nerrors;
65: Eptr *errors;
66: int *r_nfiles;
67: Eptr ***r_files;
68: {
69: int nfiles;
70: Eptr **files;
71:
72: char *name;
73: reg int ei;
74: int fi;
75: reg Eptr errorp;
76:
77: nfiles = countfiles(errors);
78:
79: files = (Eptr**)Calloc(nfiles + 3, sizeof (Eptr*));
80: touchedfiles = (boolean *)Calloc(nfiles+3, sizeof(boolean));
81: /*
82: * Now, partition off the error messages
83: * into those that are synchronization, discarded or
84: * not specific to any file, and those that were
85: * nulled or true errors.
86: */
87: files[0] = &errors[0];
88: ECITERATE(ei, errorp, 0){
89: if ( ! (NOTSORTABLE(errorp->error_e_class)))
90: break;
91: }
92: /*
93: * Now, and partition off all error messages
94: * for a given file.
95: */
96: files[1] = &errors[ei];
97: touchedfiles[0] = touchedfiles[1] = FALSE;
98: name = "\1";
99: fi = 1;
100: ECITERATE(ei, errorp, ei){
101: if ( (errorp->error_e_class == C_NULLED)
102: || (errorp->error_e_class == C_TRUE) ){
103: if (strcmp(errorp->error_text[0], name) != 0){
104: name = errorp->error_text[0];
105: touchedfiles[fi] = FALSE;
106: files[fi] = &errors[ei];
107: fi++;
108: }
109: }
110: }
111: files[fi] = &errors[nerrors];
112: *r_nfiles = nfiles;
113: *r_files = files;
114: }
115:
116: int countfiles(errors)
117: Eptr *errors;
118: {
119: char *name;
120: int ei;
121: reg Eptr errorp;
122:
123: int nfiles;
124: nfiles = 0;
125: name = "\1";
126: ECITERATE(ei, errorp, 0){
127: if (SORTABLE(errorp->error_e_class)){
128: if (strcmp(errorp->error_text[0],name) != 0){
129: nfiles++;
130: name = errorp->error_text[0];
131: }
132: }
133: }
134: return(nfiles);
135: }
136: char *class_table[] = {
137: /*C_UNKNOWN 0 */ "Unknown",
138: /*C_IGNORE 1 */ "ignore",
139: /*C_SYNC 2 */ "synchronization",
140: /*C_DISCARD 3 */ "discarded",
141: /*C_NONSPEC 4 */ "non specific",
142: /*C_THISFILE 5 */ "specific to this file",
143: /*C_NULLED 6 */ "nulled",
144: /*C_TRUE 7 */ "true",
145: /*C_DUPL 8 */ "duplicated"
146: };
147:
148: int class_count[C_LAST - C_FIRST] = {0};
149:
150: filenames(nfiles, files)
151: int nfiles;
152: Eptr **files;
153: {
154: reg int fi;
155: char *sep = " ";
156: extern char *class_table[];
157: int someerrors;
158:
159: /*
160: * first, simply dump out errors that
161: * don't pertain to any file
162: */
163: someerrors = nopertain(files);
164:
165: if (nfiles){
166: someerrors++;
167: fprintf(stdout, terse
168: ? "%d file%s"
169: : "%d file%s contain%s errors",
170: nfiles, plural(nfiles), verbform(nfiles));
171: if (!terse){
172: FILEITERATE(fi, 1){
173: fprintf(stdout, "%s\"%s\" (%d)",
174: sep, (*files[fi])->error_text[0],
175: files[fi+1] - files[fi]);
176: sep = ", ";
177: }
178: }
179: fprintf(stdout, "\n");
180: }
181: if (!someerrors)
182: fprintf(stdout, "No errors.\n");
183: }
184:
185: /*
186: * Dump out errors that don't pertain to any file
187: */
188: int nopertain(files)
189: Eptr **files;
190: {
191: int type;
192: int someerrors = 0;
193: reg Eptr *erpp;
194: reg Eptr errorp;
195:
196: if (files[1] - files[0] <= 0)
197: return(0);
198: for(type = C_UNKNOWN; NOTSORTABLE(type); type++){
199: if (class_count[type] <= 0)
200: continue;
201: if (type > C_SYNC)
202: someerrors++;
203: if (terse){
204: fprintf(stdout, "\t%d %s errors NOT PRINTED\n",
205: class_count[type], class_table[type]);
206: } else {
207: fprintf(stdout, "\n\t%d %s errors follow\n",
208: class_count[type], class_table[type]);
209: EITERATE(erpp, files, 0){
210: errorp = *erpp;
211: if (errorp->error_e_class == type){
212: errorprint(stdout, errorp, TRUE);
213: }
214: }
215: }
216: }
217: return(someerrors);
218: }
219:
220: extern boolean notouch;
221:
222: boolean touchfiles(nfiles, files, r_edargc, r_edargv)
223: int nfiles;
224: Eptr **files;
225: int *r_edargc;
226: char ***r_edargv;
227: {
228: char *name;
229: reg Eptr errorp;
230: reg int fi;
231: reg Eptr *erpp;
232: int ntrueerrors;
233: boolean scribbled;
234: int n_pissed_on; /* # of file touched*/
235: int spread;
236:
237: FILEITERATE(fi, 1){
238: name = (*files[fi])->error_text[0];
239: spread = files[fi+1] - files[fi];
240: fprintf(stdout, terse
241: ? "\"%s\" has %d error%s, "
242: : "\nFile \"%s\" has %d error%s.\n"
243: , name ,spread ,plural(spread));
244: /*
245: * First, iterate through all error messages in this file
246: * to see how many of the error messages really will
247: * get inserted into the file.
248: */
249: ntrueerrors = 0;
250: EITERATE(erpp, files, fi){
251: errorp = *erpp;
252: if (errorp->error_e_class == C_TRUE)
253: ntrueerrors++;
254: }
255: fprintf(stdout, terse
256: ? "insert %d\n"
257: : "\t%d of these errors can be inserted into the file.\n",
258: ntrueerrors);
259:
260: hackfile(name, files, fi, ntrueerrors);
261: }
262: scribbled = FALSE;
263: n_pissed_on = 0;
264: FILEITERATE(fi, 1){
265: scribbled |= touchedfiles[fi];
266: n_pissed_on++;
267: }
268: if (scribbled){
269: /*
270: * Construct an execv argument
271: */
272: execvarg(n_pissed_on, r_edargc, r_edargv);
273: return(TRUE);
274: } else {
275: if (!terse)
276: fprintf(stdout, "You didn't touch any files.\n");
277: return(FALSE);
278: }
279: }
280:
281: hackfile(name, files, ix, nerrors)
282: char *name;
283: Eptr **files;
284: int ix;
285: {
286: boolean previewed;
287: int errordest; /* where errors go*/
288:
289: if (!oktotouch(name)) {
290: previewed = FALSE;
291: errordest = TOSTDOUT;
292: } else {
293: previewed = preview(name, nerrors, files, ix);
294: errordest = settotouch(name);
295: }
296:
297: if (errordest != TOSTDOUT)
298: touchedfiles[ix] = TRUE;
299:
300: if (previewed && (errordest == TOSTDOUT))
301: return;
302:
303: diverterrors(name, errordest, files, ix, previewed, nerrors);
304:
305: if (errordest == TOTHEFILE){
306: /*
307: * overwrite the original file
308: */
309: writetouched(1);
310: }
311: }
312:
313: boolean preview(name, nerrors, files, ix)
314: char *name;
315: int nerrors;
316: Eptr **files;
317: int ix;
318: {
319: int back;
320: reg Eptr *erpp;
321:
322: if (nerrors <= 0)
323: return(FALSE);
324: back = FALSE;
325: if(query){
326: switch(inquire(terse
327: ? "Preview? "
328: : "Do you want to preview the errors first? ")){
329: case Q_YES:
330: case Q_yes:
331: back = TRUE;
332: EITERATE(erpp, files, ix){
333: errorprint(stdout, *erpp, TRUE);
334: }
335: if (!terse)
336: fprintf(stdout, "\n");
337: default:
338: break;
339: }
340: }
341: return(back);
342: }
343:
344: int settotouch(name)
345: char *name;
346: {
347: int dest = TOSTDOUT;
348:
349: if (query){
350: switch(touchstatus = inquire(terse
351: ? "Touch? "
352: : "Do you want to touch file \"%s\"? ",
353: name)){
354: case Q_NO:
355: case Q_no:
356: return(dest);
357: default:
358: break;
359: }
360: }
361:
362: switch(probethisfile(name)){
363: case F_NOTREAD:
364: dest = TOSTDOUT;
365: fprintf(stdout, terse
366: ? "\"%s\" unreadable\n"
367: : "File \"%s\" is unreadable\n",
368: name);
369: break;
370: case F_NOTWRITE:
371: dest = TOSTDOUT;
372: fprintf(stdout, terse
373: ? "\"%s\" unwritable\n"
374: : "File \"%s\" is unwritable\n",
375: name);
376: break;
377: case F_NOTEXIST:
378: dest = TOSTDOUT;
379: fprintf(stdout, terse
380: ? "\"%s\" not found\n"
381: : "Can't find file \"%s\" to insert error messages into.\n",
382: name);
383: break;
384: default:
385: dest = edit(name) ? TOSTDOUT : TOTHEFILE;
386: break;
387: }
388: return(dest);
389: }
390:
391: diverterrors(name, dest, files, ix, previewed, nterrors)
392: char *name;
393: int dest;
394: Eptr **files;
395: int ix;
396: boolean previewed;
397: int nterrors;
398: {
399: int nerrors;
400: reg Eptr *erpp;
401: reg Eptr errorp;
402:
403: nerrors = files[ix+1] - files[ix];
404:
405: if ( (nerrors != nterrors)
406: && (!previewed) ){
407: fprintf(stdout, terse
408: ? "Uninserted errors\n"
409: : ">>Uninserted errors for file \"%s\" follow.\n",
410: name);
411: }
412:
413: EITERATE(erpp, files, ix){
414: errorp = *erpp;
415: if (errorp->error_e_class != C_TRUE){
416: if (previewed || touchstatus == Q_NO)
417: continue;
418: errorprint(stdout, errorp, TRUE);
419: continue;
420: }
421: switch (dest){
422: case TOSTDOUT:
423: if (previewed || touchstatus == Q_NO)
424: continue;
425: errorprint(stdout,errorp, TRUE);
426: break;
427: case TOTHEFILE:
428: insert(errorp->error_line);
429: text(errorp, FALSE);
430: break;
431: }
432: }
433: }
434:
435: int oktotouch(filename)
436: char *filename;
437: {
438: extern char *suffixlist;
439: reg char *src;
440: reg char *pat;
441: char *osrc;
442:
443: pat = suffixlist;
444: if (pat == 0)
445: return(0);
446: if (*pat == '*')
447: return(1);
448: while (*pat++ != '.')
449: continue;
450: --pat; /* point to the period */
451:
452: for (src = &filename[strlen(filename)], --src;
453: (src > filename) && (*src != '.'); --src)
454: continue;
455: if (*src != '.')
456: return(0);
457:
458: for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){
459: for (; *src /* not at end of the source */
460: && *pat /* not off end of pattern */
461: && *pat != '.' /* not off end of sub pattern */
462: && *pat != '*' /* not wild card */
463: && *src == *pat; /* and equal... */
464: src++, pat++)
465: continue;
466: if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
467: return(1);
468: if (*src != 0 && *pat == '*')
469: return(1);
470: while (*pat && *pat != '.')
471: pat++;
472: if (! *pat)
473: return(0);
474: }
475: return(0);
476: }
477: /*
478: * Construct an execv argument
479: * We need 1 argument for the editor's name
480: * We need 1 argument for the initial search string
481: * We need n_pissed_on arguments for the file names
482: * We need 1 argument that is a null for execv.
483: * The caller fills in the editor's name.
484: * We fill in the initial search string.
485: * We fill in the arguments, and the null.
486: */
487: execvarg(n_pissed_on, r_argc, r_argv)
488: int n_pissed_on;
489: int *r_argc;
490: char ***r_argv;
491: {
492: Eptr p;
493: char *sep;
494: int fi;
495:
496: (*r_argv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *));
497: (*r_argc) = n_pissed_on + 2;
498: (*r_argv)[1] = "+1;/###/";
499: n_pissed_on = 2;
500: if (!terse){
501: fprintf(stdout, "You touched file(s):");
502: sep = " ";
503: }
504: FILEITERATE(fi, 1){
505: if (!touchedfiles[fi])
506: continue;
507: p = *(files[fi]);
508: if (!terse){
509: fprintf(stdout,"%s\"%s\"", sep, p->error_text[0]);
510: sep = ", ";
511: }
512: (*r_argv)[n_pissed_on++] = p->error_text[0];
513: }
514: if (!terse)
515: fprintf(stdout, "\n");
516: (*r_argv)[n_pissed_on] = 0;
517: }
518:
519: FILE *o_touchedfile; /* the old file */
520: FILE *n_touchedfile; /* the new file */
521: char *o_name;
522: char n_name[64];
523: char *canon_name = _PATH_TMP;
524: int o_lineno;
525: int n_lineno;
526: boolean tempfileopen = FALSE;
527: /*
528: * open the file; guaranteed to be both readable and writable
529: * Well, if it isn't, then return TRUE if something failed
530: */
531: boolean edit(name)
532: char *name;
533: {
534: o_name = name;
535: if ( (o_touchedfile = fopen(name, "r")) == NULL){
536: fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n",
537: processname, name);
538: return(TRUE);
539: }
540: (void)strcpy(n_name, canon_name);
541: (void)mktemp(n_name);
542: if ( (n_touchedfile = fopen(n_name, "w")) == NULL){
543: fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n",
544: processname, name);
545: return(TRUE);
546: }
547: tempfileopen = TRUE;
548: n_lineno = 0;
549: o_lineno = 0;
550: return(FALSE);
551: }
552: /*
553: * Position to the line (before, after) the line given by place
554: */
555: char edbuf[BUFSIZ];
556: insert(place)
557: int place;
558: {
559: --place; /* always insert messages before the offending line*/
560: for(; o_lineno < place; o_lineno++, n_lineno++){
561: if(fgets(edbuf, BUFSIZ, o_touchedfile) == NULL)
562: return;
563: fputs(edbuf, n_touchedfile);
564: }
565: }
566:
567: text(p, use_all)
568: reg Eptr p;
569: boolean use_all;
570: {
571: int offset = use_all ? 0 : 2;
572:
573: fputs(lang_table[p->error_language].lang_incomment, n_touchedfile);
574: fprintf(n_touchedfile, "%d [%s] ",
575: p->error_line,
576: lang_table[p->error_language].lang_name);
577: wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset);
578: fputs(lang_table[p->error_language].lang_outcomment,n_touchedfile);
579: n_lineno++;
580: }
581:
582: /*
583: * write the touched file to its temporary copy,
584: * then bring the temporary in over the local file
585: */
586: writetouched(overwrite)
587: int overwrite;
588: {
589: reg int nread;
590: reg FILE *localfile;
591: reg FILE *tmpfile;
592: int botch;
593: int oktorm;
594:
595: botch = 0;
596: oktorm = 1;
597: while((nread = fread(edbuf, 1, sizeof(edbuf), o_touchedfile)) != NULL){
598: if (nread != fwrite(edbuf, 1, nread, n_touchedfile)){
599: /*
600: * Catastrophe in temporary area: file system full?
601: */
602: botch = 1;
603: fprintf(stderr,
604: "%s: write failure: No errors inserted in \"%s\"\n",
605: processname, o_name);
606: }
607: }
608: fclose(n_touchedfile);
609: fclose(o_touchedfile);
610: /*
611: * Now, copy the temp file back over the original
612: * file, thus preserving links, etc
613: */
614: if (botch == 0 && overwrite){
615: botch = 0;
616: localfile = NULL;
617: tmpfile = NULL;
618: if ((localfile = fopen(o_name, "w")) == NULL){
619: fprintf(stderr,
620: "%s: Can't open file \"%s\" to overwrite.\n",
621: processname, o_name);
622: botch++;
623: }
624: if ((tmpfile = fopen(n_name, "r")) == NULL){
625: fprintf(stderr, "%s: Can't open file \"%s\" to read.\n",
626: processname, n_name);
627: botch++;
628: }
629: if (!botch)
630: oktorm = mustoverwrite(localfile, tmpfile);
631: if (localfile != NULL)
632: fclose(localfile);
633: if (tmpfile != NULL)
634: fclose(tmpfile);
635: }
636: if (oktorm == 0){
637: fprintf(stderr, "%s: Catastrophe: A copy of \"%s\": was saved in \"%s\"\n",
638: processname, o_name, n_name);
639: exit(1);
640: }
641: /*
642: * Kiss the temp file good bye
643: */
644: unlink(n_name);
645: tempfileopen = FALSE;
646: return(TRUE);
647: }
648: /*
649: * return 1 if the tmpfile can be removed after writing it out
650: */
651: int mustoverwrite(preciousfile, tmpfile)
652: FILE *preciousfile;
653: FILE *tmpfile;
654: {
655: int nread;
656:
657: while((nread = fread(edbuf, 1, sizeof(edbuf), tmpfile)) != NULL){
658: if (mustwrite(edbuf, nread, preciousfile) == 0)
659: return(0);
660: }
661: return(1);
662: }
663: /*
664: * return 0 on catastrophe
665: */
666: mustwrite(base, n, preciousfile)
667: char *base;
668: int n;
669: FILE *preciousfile;
670: {
671: int nwrote;
672:
673: if (n <= 0)
674: return(1);
675: nwrote = fwrite(base, 1, n, preciousfile);
676: if (nwrote == n)
677: return(1);
678: perror(processname);
679: switch(inquire(terse
680: ? "Botch overwriting: retry? "
681: : "Botch overwriting the source file: retry? ")){
682: case Q_YES:
683: case Q_yes:
684: mustwrite(base + nwrote, n - nwrote, preciousfile);
685: return(1);
686: case Q_NO:
687: case Q_no:
688: switch(inquire("Are you sure? ")){
689: case Q_YES:
690: case Q_yes:
691: return(0);
692: case Q_NO:
693: case Q_no:
694: mustwrite(base + nwrote, n - nwrote, preciousfile);
695: return(1);
696: }
697: default:
698: return(0);
699: }
700: }
701:
702: void
703: onintr()
704: {
705: switch(inquire(terse
706: ? "\nContinue? "
707: : "\nInterrupt: Do you want to continue? ")){
708: case Q_YES:
709: case Q_yes:
710: signal(SIGINT, onintr);
711: return;
712: default:
713: if (tempfileopen){
714: /*
715: * Don't overwrite the original file!
716: */
717: writetouched(0);
718: }
719: exit(1);
720: }
721: /*NOTREACHED*/
722: }
723:
724: errorprint(place, errorp, print_all)
725: FILE *place;
726: Eptr errorp;
727: boolean print_all;
728: {
729: int offset = print_all ? 0 : 2;
730:
731: if (errorp->error_e_class == C_IGNORE)
732: return;
733: fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name);
734: wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset);
735: putc('\n', place);
736: }
737:
738: int inquire(fmt, a1, a2)
739: char *fmt;
740: /*VARARGS1*/
741: {
742: char buffer[128];
743:
744: if (queryfile == NULL)
745: return(0);
746: for(;;){
747: do{
748: fflush(stdout);
749: fprintf(stderr, fmt, a1, a2);
750: fflush(stderr);
751: } while (fgets(buffer, 127, queryfile) == NULL);
752: switch(buffer[0]){
753: case 'Y': return(Q_YES);
754: case 'y': return(Q_yes);
755: case 'N': return(Q_NO);
756: case 'n': return(Q_no);
757: default: fprintf(stderr, "Yes or No only!\n");
758: }
759: }
760: }
761:
762: int probethisfile(name)
763: char *name;
764: {
765: struct stat statbuf;
766: if (stat(name, &statbuf) < 0)
767: return(F_NOTEXIST);
768: if((statbuf.st_mode & S_IREAD) == 0)
769: return(F_NOTREAD);
770: if((statbuf.st_mode & S_IWRITE) == 0)
771: return(F_NOTWRITE);
772: return(F_TOUCHIT);
773: }