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