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