1: static char *sccsid = "@(#)errortouch.c 1.5 (Berkeley) 11/20/82"; 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 seen; 242: int errordest; /* where errors go*/ 243: 244: seen = preview(name, nerrors, files, ix); 245: 246: errordest = settotouch(name); 247: 248: if (errordest != TOSTDOUT) 249: touchedfiles[ix] = TRUE; 250: 251: if (seen && (errordest == TOSTDOUT)) 252: return; 253: 254: diverterrors(name, errordest, files, ix, seen, nerrors); 255: 256: if (errordest == TOTHEFILE){ 257: /* 258: * overwrite the original file 259: */ 260: writetouched(1); 261: } 262: } 263: 264: boolean preview(name, nerrors, files, ix) 265: char *name; 266: int nerrors; 267: Eptr **files; 268: int ix; 269: { 270: int back; 271: reg Eptr *erpp; 272: 273: if (!oktotouch(name)) 274: return(false); 275: if (nerrors <= 0) 276: return(false); 277: back = false; 278: if(query){ 279: switch(inquire(terse 280: ? "Preview? " 281: : "Do you want to preview the errors first? ")){ 282: case Q_YES: 283: case Q_yes: 284: back = true; 285: EITERATE(erpp, files, ix){ 286: errorprint(stdout, *erpp, TRUE); 287: } 288: if (!terse) 289: fprintf(stdout, "\n"); 290: default: 291: break; 292: } 293: } 294: return(back); 295: } 296: 297: int settotouch(name) 298: char *name; 299: { 300: int dest = TOSTDOUT; 301: 302: if (query){ 303: switch(touchstatus = inquire(terse 304: ? "Touch? " 305: : "Do you want to touch file \"%s\"? ", 306: name)){ 307: case Q_NO: 308: case Q_no: 309: return(dest); 310: default: 311: break; 312: } 313: } 314: 315: switch(probethisfile(name)){ 316: case F_NOTREAD: 317: dest = TOSTDOUT; 318: fprintf(stdout, terse 319: ? "\"%s\" unreadable\n" 320: : "File \"%s\" is unreadable\n", 321: name); 322: break; 323: case F_NOTWRITE: 324: dest = TOSTDOUT; 325: fprintf(stdout, terse 326: ? "\"%s\" unwritable\n" 327: : "File \"%s\" is unwritable\n", 328: name); 329: break; 330: case F_NOTEXIST: 331: dest = TOSTDOUT; 332: fprintf(stdout, terse 333: ? "\"%s\" not found\n" 334: : "Can't find file \"%s\" to insert error messages into.\n", 335: name); 336: break; 337: default: 338: dest = edit(name) ? TOSTDOUT : TOTHEFILE; 339: break; 340: } 341: return(dest); 342: } 343: 344: diverterrors(name, dest, files, ix, seen, nterrors) 345: char *name; 346: int dest; 347: Eptr **files; 348: int ix; 349: boolean seen; 350: int nterrors; 351: { 352: int nerrors; 353: reg Eptr *erpp; 354: reg Eptr errorp; 355: 356: nerrors = files[ix+1] - files[ix]; 357: 358: if ( (nerrors != nterrors) 359: && (!seen) ){ 360: fprintf(stdout, terse 361: ? "Uninserted errors\n" 362: : ">>Uninserted errors for file \"%s\" follow.\n", 363: name); 364: } 365: 366: EITERATE(erpp, files, ix){ 367: errorp = *erpp; 368: if (errorp->error_e_class != C_TRUE){ 369: if (seen || touchstatus == Q_NO) 370: continue; 371: errorprint(stdout, errorp, TRUE); 372: continue; 373: } 374: switch (dest){ 375: case TOSTDOUT: 376: if (seen || touchstatus == Q_NO) 377: continue; 378: errorprint(stdout,errorp, TRUE); 379: break; 380: case TOTHEFILE: 381: insert(errorp->error_line); 382: text(errorp, FALSE); 383: break; 384: } 385: } 386: } 387: 388: int oktotouch(filename) 389: char *filename; 390: { 391: extern char *suffixlist; 392: reg char *src; 393: reg char *pat; 394: char *osrc; 395: 396: pat = suffixlist; 397: if (pat == 0) 398: return(0); 399: if (*pat == '*') 400: return(1); 401: while (*pat++ != '.') 402: continue; 403: --pat; /* point to the period */ 404: 405: for (src = &filename[strlen(filename)], --src; 406: (src > filename) && (*src != '.'); --src) 407: continue; 408: if (*src != '.') 409: return(0); 410: 411: for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){ 412: for (; *src /* not at end of the source */ 413: && *pat /* not off end of pattern */ 414: && *pat != '.' /* not off end of sub pattern */ 415: && *pat != '*' /* not wild card */ 416: && *src == *pat; /* and equal... */ 417: src++, pat++) 418: continue; 419: if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*')) 420: return(1); 421: if (*src != 0 && *pat == '*') 422: return(1); 423: while (*pat && *pat != '.') 424: pat++; 425: if (! *pat) 426: return(0); 427: } 428: return(0); 429: } 430: /* 431: * Construct an execv argument 432: * We need 1 argument for the editor's name 433: * We need 1 argument for the initial search string 434: * We need n_pissed_on arguments for the file names 435: * We need 1 argument that is a null for execv. 436: * The caller fills in the editor's name. 437: * We fill in the initial search string. 438: * We fill in the arguments, and the null. 439: */ 440: execvarg(n_pissed_on, r_argc, r_argv) 441: int n_pissed_on; 442: int *r_argc; 443: char ***r_argv; 444: { 445: Eptr p; 446: char *sep; 447: int fi; 448: 449: (*r_argv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *)); 450: (*r_argc) = n_pissed_on + 2; 451: (*r_argv)[1] = "+1;/###/"; 452: n_pissed_on = 2; 453: if (!terse){ 454: fprintf(stdout, "You touched file(s):"); 455: sep = " "; 456: } 457: FILEITERATE(fi, 1){ 458: if (!touchedfiles[fi]) 459: continue; 460: p = *(files[fi]); 461: if (!terse){ 462: fprintf(stdout,"%s\"%s\"", sep, p->error_text[0]); 463: sep = ", "; 464: } 465: (*r_argv)[n_pissed_on++] = p->error_text[0]; 466: } 467: if (!terse) 468: fprintf(stdout, "\n"); 469: (*r_argv)[n_pissed_on] = 0; 470: } 471: 472: FILE *o_touchedfile; /* the old file */ 473: FILE *n_touchedfile; /* the new file */ 474: char *o_name; 475: char n_name[64]; 476: char *canon_name = "/tmp/ErrorXXXXXX"; 477: int o_lineno; 478: int n_lineno; 479: boolean tempfileopen = FALSE; 480: /* 481: * open the file; guaranteed to be both readable and writable 482: * Well, if it isn't, then return TRUE if something failed 483: */ 484: boolean edit(name) 485: char *name; 486: { 487: o_name = name; 488: if ( (o_touchedfile = fopen(name, "r")) == NULL){ 489: fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n", 490: processname, name); 491: return(TRUE); 492: } 493: (void)strcpy(n_name, canon_name); 494: (void)mktemp(n_name); 495: if ( (n_touchedfile = fopen(n_name, "w")) == NULL){ 496: fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n", 497: processname, name); 498: return(TRUE); 499: } 500: tempfileopen = TRUE; 501: n_lineno = 0; 502: o_lineno = 0; 503: return(FALSE); 504: } 505: /* 506: * Position to the line (before, after) the line given by place 507: */ 508: char edbuf[BUFSIZ]; 509: insert(place) 510: int place; 511: { 512: --place; /* always insert messages before the offending line*/ 513: for(; o_lineno < place; o_lineno++, n_lineno++){ 514: if(fgets(edbuf, BUFSIZ, o_touchedfile) == NULL) 515: return; 516: fputs(edbuf, n_touchedfile); 517: } 518: } 519: 520: text(p, use_all) 521: reg Eptr p; 522: boolean use_all; 523: { 524: int offset = use_all ? 0 : 2; 525: 526: fputs(lang_table[p->error_language].lang_incomment, n_touchedfile); 527: fprintf(n_touchedfile, "%d [%s] ", 528: p->error_line, 529: lang_table[p->error_language].lang_name); 530: wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset); 531: fputs(lang_table[p->error_language].lang_outcomment,n_touchedfile); 532: n_lineno++; 533: } 534: 535: /* 536: * write the touched file to its temporary copy, 537: * then bring the temporary in over the local file 538: */ 539: writetouched(overwrite) 540: int overwrite; 541: { 542: reg int nread; 543: reg FILE *localfile; 544: reg FILE *tmpfile; 545: int botch; 546: int oktorm; 547: 548: botch = 0; 549: oktorm = 1; 550: while((nread = fread(edbuf, 1, sizeof(edbuf), o_touchedfile)) != NULL){ 551: if (nread != fwrite(edbuf, 1, nread, n_touchedfile)){ 552: /* 553: * Catastrophe in temporary area: file system full? 554: */ 555: botch = 1; 556: fprintf(stderr, 557: "%s: write failure: No errors inserted in \"%s\"\n", 558: processname, o_name); 559: } 560: } 561: fclose(n_touchedfile); 562: fclose(o_touchedfile); 563: /* 564: * Now, copy the temp file back over the original 565: * file, thus preserving links, etc 566: */ 567: if (botch == 0 && overwrite){ 568: botch = 0; 569: localfile = NULL; 570: tmpfile = NULL; 571: if ((localfile = fopen(o_name, "w")) == NULL){ 572: fprintf(stderr, 573: "%s: Can't open file \"%s\" to overwrite.\n", 574: processname, o_name); 575: botch++; 576: } 577: if ((tmpfile = fopen(n_name, "r")) == NULL){ 578: fprintf(stderr, "%s: Can't open file \"%s\" to read.\n", 579: processname, n_name); 580: botch++; 581: } 582: if (!botch) 583: oktorm = mustoverwrite(localfile, tmpfile); 584: if (localfile != NULL) 585: fclose(localfile); 586: if (tmpfile != NULL) 587: fclose(tmpfile); 588: } 589: if (oktorm == 0){ 590: fprintf(stderr, "%s: Catastrophe: A copy of \"%s\: was saved in \"%s\"\n", 591: processname, o_name, n_name); 592: exit(1); 593: } 594: /* 595: * Kiss the temp file good bye 596: */ 597: unlink(n_name); 598: tempfileopen = FALSE; 599: return(TRUE); 600: } 601: /* 602: * return 1 if the tmpfile can be removed after writing it out 603: */ 604: int mustoverwrite(preciousfile, tmpfile) 605: FILE *preciousfile; 606: FILE *tmpfile; 607: { 608: int nread; 609: 610: while((nread = fread(edbuf, 1, sizeof(edbuf), tmpfile)) != NULL){ 611: if (mustwrite(edbuf, nread, preciousfile) == 0) 612: return(0); 613: } 614: return(1); 615: } 616: /* 617: * return 0 on catastrophe 618: */ 619: mustwrite(base, n, preciousfile) 620: char *base; 621: int n; 622: FILE *preciousfile; 623: { 624: int nwrote; 625: 626: if (n <= 0) 627: return(1); 628: nwrote = fwrite(base, 1, n, preciousfile); 629: if (nwrote == n) 630: return(1); 631: perror(processname); 632: switch(inquire(terse 633: ? "Botch overwriting: retry? " 634: : "Botch overwriting the source file: retry? ")){ 635: case Q_YES: 636: case Q_yes: 637: mustwrite(base + nwrote, n - nwrote, preciousfile); 638: return(1); 639: case Q_NO: 640: case Q_no: 641: switch(inquire("Are you sure? ")){ 642: case Q_YES: 643: case Q_yes: 644: return(0); 645: case Q_NO: 646: case Q_no: 647: mustwrite(base + nwrote, n - nwrote, preciousfile); 648: return(1); 649: } 650: } 651: } 652: 653: onintr() 654: { 655: switch(inquire(terse 656: ? "\nContinue? " 657: : "\nInterrupt: Do you want to continue? ")){ 658: case Q_YES: 659: case Q_yes: 660: signal(SIGINT, onintr); 661: return; 662: default: 663: if (tempfileopen){ 664: /* 665: * Don't overwrite the original file! 666: */ 667: writetouched(0); 668: } 669: exit(1); 670: } 671: /*NOTREACHED*/ 672: } 673: 674: errorprint(place, errorp, print_all) 675: FILE *place; 676: Eptr errorp; 677: boolean print_all; 678: { 679: int offset = print_all ? 0 : 2; 680: 681: if (errorp->error_e_class == C_IGNORE) 682: return; 683: fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name); 684: wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset); 685: putc('\n', place); 686: } 687: 688: int inquire(fmt, a1, a2) 689: char *fmt; 690: /*VARARGS1*/ 691: { 692: char buffer[128]; 693: for(;;){ 694: do{ 695: fflush(stdout); 696: fprintf(stderr, fmt, a1, a2); 697: fflush(stderr); 698: } while (fgets(buffer, 127, queryfile) == NULL); 699: switch(buffer[0]){ 700: case 'Y': return(Q_YES); 701: case 'y': return(Q_yes); 702: case 'N': return(Q_NO); 703: case 'n': return(Q_no); 704: default: fprintf(stderr, "Yes or No only!\n"); 705: } 706: } 707: } 708: 709: int probethisfile(name) 710: char *name; 711: { 712: struct stat statbuf; 713: if (stat(name, &statbuf) < 0) 714: return(F_NOTEXIST); 715: if((statbuf.st_mode & S_IREAD) == 0) 716: return(F_NOTREAD); 717: if((statbuf.st_mode & S_IWRITE) == 0) 718: return(F_NOTWRITE); 719: return(F_TOUCHIT); 720: }