1 static char *sccsid = "@(#)touch.c 1.3 (Berkeley) 01/22/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 writetouched(); 258 } 259 260 boolean preview(name, nerrors, files, ix) 261 char *name; 262 int nerrors; 263 Eptr **files; 264 int ix; 265 { 266 int back; 267 reg Eptr *erpp; 268 269 if (!oktotouch(name)) 270 return(false); 271 if (nerrors <= 0) 272 return(false); 273 back = false; 274 if(query){ 275 switch(inquire(terse 276 ? "Preview? " 277 : "Do you want to preview the errors first? ")){ 278 case Q_YES: 279 case Q_yes: 280 back = true; 281 EITERATE(erpp, files, ix){ 282 errorprint(stdout, *erpp, TRUE); 283 } 284 if (!terse) 285 fprintf(stdout, "\n"); 286 default: 287 break; 288 } 289 } 290 return(back); 291 } 292 293 int settotouch(name) 294 char *name; 295 { 296 int dest = TOSTDOUT; 297 298 if (query){ 299 switch(touchstatus = inquire(terse 300 ? "Touch? " 301 : "Do you want to touch file \"%s\"? ", 302 name)){ 303 case Q_NO: 304 case Q_no: 305 return(dest); 306 default: 307 break; 308 } 309 } 310 311 switch(probethisfile(name)){ 312 case F_NOTREAD: 313 dest = TOSTDOUT; 314 fprintf(stdout, terse 315 ? "\"%s\" unreadable\n" 316 : "File \"%s\" is unreadable\n", 317 name); 318 break; 319 case F_NOTWRITE: 320 dest = TOSTDOUT; 321 fprintf(stdout, terse 322 ? "\"%s\" unwritable\n" 323 : "File \"%s\" is unwritable\n", 324 name); 325 break; 326 case F_NOTEXIST: 327 dest = TOSTDOUT; 328 fprintf(stdout, terse 329 ? "\"%s\" not found\n" 330 : "Can't find file \"%s\" to insert error messages into.\n", 331 name); 332 break; 333 default: 334 dest = edit(name) ? TOSTDOUT : TOTHEFILE; 335 break; 336 } 337 return(dest); 338 } 339 340 diverterrors(name, dest, files, ix, previewed, nterrors) 341 char *name; 342 int dest; 343 Eptr **files; 344 int ix; 345 boolean previewed; 346 int nterrors; 347 { 348 int nerrors; 349 reg Eptr *erpp; 350 reg Eptr errorp; 351 352 nerrors = files[ix+1] - files[ix]; 353 354 if ( (nerrors != nterrors) 355 && (!previewed) ){ 356 fprintf(stdout, terse 357 ? "Uninserted errors\n" 358 : ">>Uninserted errors for file \"%s\" follow.\n", 359 name); 360 } 361 362 EITERATE(erpp, files, ix){ 363 errorp = *erpp; 364 if (errorp->error_e_class != C_TRUE){ 365 if (previewed || touchstatus == Q_NO) 366 continue; 367 errorprint(stdout, errorp, TRUE); 368 continue; 369 } 370 switch (dest){ 371 case TOSTDOUT: 372 if (previewed || touchstatus == Q_NO) 373 continue; 374 errorprint(stdout,errorp, TRUE); 375 break; 376 case TOTHEFILE: 377 insert(errorp->error_line); 378 text(errorp, FALSE); 379 break; 380 } 381 } 382 } 383 384 int oktotouch(filename) 385 char *filename; 386 { 387 extern char *suffixlist; 388 reg char *src; 389 reg char *pat; 390 char *osrc; 391 392 pat = suffixlist; 393 if (pat == 0) 394 return(0); 395 if (*pat == '*') 396 return(1); 397 while (*pat++ != '.') 398 continue; 399 --pat; /* point to the period */ 400 401 for (src = &filename[strlen(filename)], --src; 402 (src > filename) && (*src != '.'); --src) 403 continue; 404 if (*src != '.') 405 return(0); 406 407 for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){ 408 for (; *src /* not at end of the source */ 409 && *pat /* not off end of pattern */ 410 && *pat != '.' /* not off end of sub pattern */ 411 && *pat != '*' /* not wild card */ 412 && *src == *pat; /* and equal... */ 413 src++, pat++) 414 continue; 415 if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*')) 416 return(1); 417 if (*src != 0 && *pat == '*') 418 return(1); 419 while (*pat && *pat != '.') 420 pat++; 421 if (! *pat) 422 return(0); 423 } 424 return(0); 425 } 426 /* 427 * Construct an execv argument 428 * We need 1 argument for the editor's name 429 * We need 1 argument for the initial search string 430 * We need n_pissed_on arguments for the file names 431 * We need 1 argument that is a null for execv. 432 * The caller fills in the editor's name. 433 * We fill in the initial search string. 434 * We fill in the arguments, and the null. 435 */ 436 execvarg(n_pissed_on, r_argc, r_argv) 437 int n_pissed_on; 438 int *r_argc; 439 char ***r_argv; 440 { 441 Eptr p; 442 char *sep; 443 int fi; 444 445 (*r_argv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *)); 446 (*r_argc) = n_pissed_on + 2; 447 (*r_argv)[1] = "+1;/###/"; 448 n_pissed_on = 2; 449 if (!terse){ 450 fprintf(stdout, "You touched file(s):"); 451 sep = " "; 452 } 453 FILEITERATE(fi, 1){ 454 if (!touchedfiles[fi]) 455 continue; 456 p = *(files[fi]); 457 if (!terse){ 458 fprintf(stdout,"%s\"%s\"", sep, p->error_text[0]); 459 sep = ", "; 460 } 461 (*r_argv)[n_pissed_on++] = p->error_text[0]; 462 } 463 if (!terse) 464 fprintf(stdout, "\n"); 465 (*r_argv)[n_pissed_on] = 0; 466 } 467 468 FILE *o_touchedfile; /* the old file */ 469 FILE *n_touchedfile; /* the new file */ 470 char *o_name; 471 char n_name[32]; 472 char *canon_name = "ErrorXXXXXX"; 473 int o_lineno; 474 int n_lineno; 475 boolean tempfileopen = FALSE; 476 /* 477 * open the file; guaranteed to be both readable and writable 478 * Well, if it isn't, then return TRUE if something failed 479 */ 480 boolean edit(name) 481 char *name; 482 { 483 o_name = name; 484 if ( (o_touchedfile = fopen(name, "r")) == NULL){ 485 fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n", 486 processname, name); 487 return(TRUE); 488 } 489 (void)strcpy(n_name, canon_name); 490 (void)mktemp(n_name); 491 if ( (n_touchedfile = fopen(n_name, "w")) == NULL){ 492 fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n", 493 processname, name); 494 return(TRUE); 495 } 496 tempfileopen = TRUE; 497 n_lineno = 0; 498 o_lineno = 0; 499 return(FALSE); 500 } 501 /* 502 * Position to the line (before, after) the line given by place 503 */ 504 char edbuf[BUFSIZ]; 505 insert(place) 506 int place; 507 { 508 --place; /* always insert messages before the offending line*/ 509 for(; o_lineno < place; o_lineno++, n_lineno++){ 510 if(fgets(edbuf, BUFSIZ, o_touchedfile) == NULL) 511 return; 512 fputs(edbuf, n_touchedfile); 513 } 514 } 515 516 text(p, use_all) 517 reg Eptr p; 518 boolean use_all; 519 { 520 int offset = use_all ? 0 : 2; 521 522 fputs(lang_table[p->error_language].lang_incomment, n_touchedfile); 523 fprintf(n_touchedfile, "%d [%s] ", 524 p->error_line, 525 lang_table[p->error_language].lang_name); 526 wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset); 527 fputs(lang_table[p->error_language].lang_outcomment,n_touchedfile); 528 n_lineno++; 529 } 530 531 writetouched() 532 { 533 int nread; 534 535 while((nread = fread(edbuf, 1, sizeof(edbuf), o_touchedfile)) != NULL){ 536 fwrite(edbuf, 1, nread, n_touchedfile); 537 } 538 fclose(n_touchedfile); 539 fclose(o_touchedfile); 540 unlink(o_name); 541 link(n_name, o_name); 542 unlink(n_name); 543 tempfileopen = FALSE; 544 } 545 546 onintr() 547 { 548 switch(inquire(terse 549 ? "\nContinue? " 550 : "\nInterrupt: Do you want to continue? ")){ 551 case Q_YES: 552 case Q_yes: 553 signal(SIGINT, onintr); 554 return; 555 default: 556 if (tempfileopen) 557 writetouched(); 558 exit(1); 559 } 560 /*NOTREACHED*/ 561 } 562 563 errorprint(place, errorp, print_all) 564 FILE *place; 565 Eptr errorp; 566 boolean print_all; 567 { 568 int offset = print_all ? 0 : 2; 569 570 if (errorp->error_e_class == C_IGNORE) 571 return; 572 fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name); 573 wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset); 574 putc('\n', place); 575 } 576 577 int inquire(fmt, a1, a2) 578 char *fmt; 579 /*VARARGS1*/ 580 { 581 char buffer[128]; 582 for(;;){ 583 do{ 584 fflush(stdout); 585 fprintf(stderr, fmt, a1, a2); 586 fflush(stderr); 587 } while (fgets(buffer, 127, queryfile) == NULL); 588 switch(buffer[0]){ 589 case 'Y': return(Q_YES); 590 case 'y': return(Q_yes); 591 case 'N': return(Q_NO); 592 case 'n': return(Q_no); 593 default: fprintf(stderr, "Yes or No only!\n"); 594 } 595 } 596 } 597 598 int probethisfile(name) 599 char *name; 600 { 601 struct stat statbuf; 602 if (stat(name, &statbuf) < 0) 603 return(F_NOTEXIST); 604 if((statbuf.st_mode & S_IREAD) == 0) 605 return(F_NOTREAD); 606 if((statbuf.st_mode & S_IWRITE) == 0) 607 return(F_NOTWRITE); 608 return(F_TOUCHIT); 609 } 610