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