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