1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * 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 5.7 (Berkeley) 2/26/91"; 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 } 769