1 static char *sccsid = "@(#)touch.c 1.4 (Berkeley) 05/04/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 previewed; 242 int errordest; /* where errors go*/ 243 244 previewed = preview(name, nerrors, files, ix); 245 246 errordest = settotouch(name); 247 248 if (errordest != TOSTDOUT) 249 touchedfiles[ix] = TRUE; 250 251 if (previewed && (errordest == TOSTDOUT)) 252 return; 253 254 diverterrors(name, errordest, files, ix, previewed, 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, previewed, nterrors) 345 char *name; 346 int dest; 347 Eptr **files; 348 int ix; 349 boolean previewed; 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 && (!previewed) ){ 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 (previewed || touchstatus == Q_NO) 370 continue; 371 errorprint(stdout, errorp, TRUE); 372 continue; 373 } 374 switch (dest){ 375 case TOSTDOUT: 376 if (previewed || 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 547 while((nread = fread(edbuf, 1, sizeof(edbuf), o_touchedfile)) != NULL){ 548 fwrite(edbuf, 1, nread, n_touchedfile); 549 } 550 fclose(n_touchedfile); 551 fclose(o_touchedfile); 552 /* 553 * Now, copy the temp file back over the original 554 * file, thus preserving links, etc 555 */ 556 if (overwrite){ 557 botch = 0; 558 localfile = NULL; 559 tmpfile = NULL; 560 if ((localfile = fopen(o_name, "w")) == NULL){ 561 fprintf(stderr, 562 "%s: Can't open file \"%s\" to overwrite.\n", 563 processname, o_name); 564 botch++; 565 } 566 if ((tmpfile = fopen(n_name, "r")) == NULL){ 567 fprintf(stderr, "%s: Can't open file \"%s\" to read.\n", 568 processname, n_name); 569 botch++; 570 } 571 if (!botch){ 572 while((nread=fread(edbuf, 1, sizeof(edbuf), tmpfile)) 573 != NULL){ 574 fwrite(edbuf, 1, nread, localfile); 575 } 576 } 577 if (localfile != NULL) 578 fclose(localfile); 579 if (tmpfile != NULL) 580 fclose(tmpfile); 581 } 582 /* 583 * Kiss the temp file good bye 584 */ 585 unlink(n_name); 586 tempfileopen = FALSE; 587 return(TRUE); 588 } 589 590 onintr() 591 { 592 switch(inquire(terse 593 ? "\nContinue? " 594 : "\nInterrupt: Do you want to continue? ")){ 595 case Q_YES: 596 case Q_yes: 597 signal(SIGINT, onintr); 598 return; 599 default: 600 if (tempfileopen){ 601 /* 602 * Don't overwrite the original file! 603 */ 604 writetouched(0); 605 } 606 exit(1); 607 } 608 /*NOTREACHED*/ 609 } 610 611 errorprint(place, errorp, print_all) 612 FILE *place; 613 Eptr errorp; 614 boolean print_all; 615 { 616 int offset = print_all ? 0 : 2; 617 618 if (errorp->error_e_class == C_IGNORE) 619 return; 620 fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name); 621 wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset); 622 putc('\n', place); 623 } 624 625 int inquire(fmt, a1, a2) 626 char *fmt; 627 /*VARARGS1*/ 628 { 629 char buffer[128]; 630 for(;;){ 631 do{ 632 fflush(stdout); 633 fprintf(stderr, fmt, a1, a2); 634 fflush(stderr); 635 } while (fgets(buffer, 127, queryfile) == NULL); 636 switch(buffer[0]){ 637 case 'Y': return(Q_YES); 638 case 'y': return(Q_yes); 639 case 'N': return(Q_NO); 640 case 'n': return(Q_no); 641 default: fprintf(stderr, "Yes or No only!\n"); 642 } 643 } 644 } 645 646 int probethisfile(name) 647 char *name; 648 { 649 struct stat statbuf; 650 if (stat(name, &statbuf) < 0) 651 return(F_NOTEXIST); 652 if((statbuf.st_mode & S_IREAD) == 0) 653 return(F_NOTREAD); 654 if((statbuf.st_mode & S_IWRITE) == 0) 655 return(F_NOTWRITE); 656 return(F_TOUCHIT); 657 } 658