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