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