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[] = "@(#)errortouch.c 5.1 (Berkeley) 5/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: }