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