touch.c revision 1.3
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 38static char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93"; 39#endif 40static 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++) 61int touchstatus = Q_YES; 62 63findfiles(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 116int 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} 136char *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 148int class_count[C_LAST - C_FIRST] = {0}; 149 150filenames(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 */ 188int 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 220extern boolean notouch; 221 222boolean 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 281hackfile(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 313boolean 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 344int 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 391diverterrors(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 435int 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 */ 487execvarg(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 519FILE *o_touchedfile; /* the old file */ 520FILE *n_touchedfile; /* the new file */ 521char *o_name; 522char n_name[64]; 523char *canon_name = _PATH_TMP; 524int o_lineno; 525int n_lineno; 526boolean 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 */ 531boolean 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 */ 555char edbuf[BUFSIZ]; 556insert(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 567text(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 */ 586writetouched(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 */ 651int 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 */ 666mustwrite(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 702void 703onintr() 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 724errorprint(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 738int 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 762int 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} 774