1 static char *sccsid = "@(#)touch.c 1.2 (Berkeley) 10/16/80"; 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 findfiles(nerrors, errors, r_nfiles, r_files) 10 int nerrors; 11 struct error_desc **errors; 12 int *r_nfiles; 13 struct error_desc ****r_files; 14 { 15 int nfiles; 16 struct error_desc ***files; 17 18 char *currentfilename; 19 register int errorindex; 20 int fileindex; 21 register struct error_desc *errorp; 22 /* 23 * First, go through and count all of the filenames 24 */ 25 for (errorp = errors[errorindex = 0],nfiles = 0, currentfilename = "\1"; 26 errorindex < nerrors; 27 errorp = errors[++errorindex]){ 28 if (SORTABLE(errorp->error_e_class)){ 29 if (strcmp(errorp->error_text[0],currentfilename) != 0){ 30 nfiles++; 31 currentfilename = errorp->error_text[0]; 32 } 33 } 34 } 35 files = (struct error_desc ***)Calloc(nfiles + 3, 36 sizeof (struct error_desc**)); 37 touchedfiles = (boolean *)Calloc(nfiles+3, sizeof(boolean)); 38 /* 39 * Now, go through and partition off the error messages 40 * into those that are synchronization, discarded or 41 * not specific to any file, and those that were 42 * nulled or true errors. 43 */ 44 files[0] = &errors[0]; 45 for (errorp = errors[errorindex = 0], fileindex = 0; 46 (errorindex < nerrors) && 47 (NOTSORTABLE(errorp->error_e_class)); 48 errorp = errors[++errorindex]){ 49 continue; 50 } 51 /* 52 * Now, go through and partition off all error messages 53 * for a given file. 54 */ 55 files[1] = &errors[errorindex]; 56 touchedfiles[0] = touchedfiles[1] = FALSE; 57 for (errorp = errors[errorindex], currentfilename = "\1", fileindex = 1; 58 errorindex < nerrors; errorp = errors[++errorindex]){ 59 if ( (errorp->error_e_class == C_NULLED) || (errorp->error_e_class == C_TRUE) ){ 60 if (strcmp(errorp->error_text[0],currentfilename) != 0){ 61 currentfilename = errorp->error_text[0]; 62 touchedfiles[fileindex] = FALSE; 63 files[fileindex++] = &errors[errorindex]; 64 } 65 } 66 } 67 files[fileindex] = &errors[nerrors]; 68 *r_nfiles = nfiles; 69 *r_files = files; 70 } 71 72 char *class_table[] = { 73 /*C_UNKNOWN 0 */ "Unknown", 74 /*C_IGNORE 1 */ "ignore", 75 /*C_SYNC 2 */ "synchronization", 76 /*C_DISCARD 3 */ "discarded", 77 /*C_NONSPEC 4 */ "non specific", 78 /*C_THISFILE 5 */ "specific to this file", 79 /*C_NULLED 6 */ "nulled", 80 /*C_TRUE 7 */ "true", 81 /*C_DUPL 8 */ "duplicated" 82 }; 83 84 int class_count[C_LAST - C_FIRST] = {0}; 85 86 filenames(nfiles, files) 87 int nfiles; 88 struct error_desc ***files; 89 { 90 register int fileindex; 91 register struct error_desc *errorp; 92 register struct error_desc **erpp; 93 char *sep = " "; 94 register int errortype; 95 extern char *class_table[]; 96 int someerrors = 0; 97 98 /* 99 * first, go through and simply dump out errors that 100 * don't pertain to any file 101 */ 102 if (files[1] - files[0] > 0){ 103 for(errortype = C_UNKNOWN; NOTSORTABLE(errortype); errortype++){ 104 if (class_count[errortype] > 0){ 105 if (errortype > C_SYNC) 106 someerrors++; 107 fprintf(stdout, "\n\t%d %s errors follow:\n", 108 class_count[errortype], class_table[errortype]); 109 for (errorp = *(erpp = files[0]); 110 erpp < files[1]; 111 errorp = (*++erpp)){ 112 if (errorp->error_e_class == errortype) 113 errorprint(stdout, errorp, TRUE); 114 } 115 } 116 } 117 } 118 if (nfiles){ 119 someerrors++; 120 fprintf(stdout, "%d files contain errors:", nfiles); 121 for (fileindex = 1; fileindex <= nfiles; fileindex++){ 122 fprintf(stdout, "%s\"%s\" (%d)", 123 sep, (*files[fileindex])->error_text[0], 124 files[fileindex+1] - files[fileindex]); 125 sep = ", "; 126 } 127 fprintf(stdout, "\n"); 128 } 129 if (!someerrors) 130 fprintf(stdout, "No errors.\n"); 131 } 132 133 extern boolean notouch; 134 135 boolean touchfiles(nfiles, files, r_edargc, r_edargv) 136 int nfiles; 137 struct error_desc ***files; 138 int *r_edargc; 139 char ***r_edargv; 140 { 141 char *currentfilename; 142 register struct error_desc *errorp; 143 register int fileindex; 144 register struct error_desc **erpp; 145 int ntrueerrors; 146 int errordest; /* where errors go*/ 147 char *sep; 148 boolean scribbled; 149 int n_pissed_on; /* # of file touched*/ 150 int previewed; 151 152 for (fileindex = 1; fileindex <= nfiles; fileindex++){ 153 fprintf(stdout, "\nFile \"%s\" has %d total error messages.\n", 154 currentfilename = (*files[fileindex])->error_text[0], 155 files[fileindex+1] - files[fileindex]); 156 /* 157 * First, iterate through all error messages in this file 158 * to see how many of the error messages really will 159 * get inserted into the file. 160 */ 161 for (erpp = files[fileindex], ntrueerrors = 0; 162 erpp < files[fileindex+1]; 163 erpp++){ 164 errorp = *erpp; 165 if (errorp->error_e_class == C_TRUE) 166 ntrueerrors++; 167 } 168 fprintf(stdout,"\t%d of these errors can be inserted into the file.\n", 169 ntrueerrors); 170 171 /* 172 * What does the operator want? 173 */ 174 previewed = 0; 175 errordest = TOSTDOUT; 176 if (oktotouch(currentfilename) && (ntrueerrors > 0) ){ 177 if (query && inquire("Do you want to preview the errors first?")){ 178 previewed = 1; 179 for (erpp = files[fileindex]; 180 erpp < files[fileindex + 1]; 181 erpp++){ 182 errorprint(stdout, *erpp, TRUE); 183 } 184 fprintf(stdout, "\n"); 185 } 186 if ( !query 187 || inquire("Do you want to touch file \"%s\"? ", 188 currentfilename) 189 ){ 190 errordest = TOTHEFILE; 191 if (!probethisfile(currentfilename)){ 192 errordest = TOSTDOUT; 193 fprintf(stdout, 194 "Can't find file \"%s\" to insert error messages into.\n", 195 currentfilename); 196 } else { 197 if (edit(currentfilename)) 198 errordest = TOSTDOUT; 199 else 200 touchedfiles[fileindex] = TRUE; 201 } 202 } 203 } 204 if (previewed && (errordest == TOSTDOUT)) 205 continue; /* with the next file */ 206 /* 207 * go through and print each error message, 208 * diverting to the right place 209 */ 210 if ( (files[fileindex+1] - files[fileindex]) != ntrueerrors) 211 if (!previewed) fprintf(stdout, 212 ">>Uninserted error messages for file \"%s\" follow.\n", 213 currentfilename); 214 for (erpp = files[fileindex];erpp < files[fileindex+1];erpp++){ 215 errorp = *erpp; 216 if (errorp->error_e_class == C_TRUE){ 217 switch (errordest){ 218 case TOSTDOUT: 219 if (!previewed) 220 errorprint(stdout,errorp, TRUE); 221 break; 222 case TOTHEFILE: 223 insert(errorp->error_line); 224 text(errorp, FALSE); 225 break; 226 } /* switch */ 227 } else { 228 if (!previewed) 229 errorprint(stdout, errorp, TRUE); 230 } 231 } /* end of walking through all errors*/ 232 if (errordest == TOTHEFILE){ 233 writetouched(); 234 } 235 } /* end of walking through all files*/ 236 scribbled = FALSE; 237 for (n_pissed_on = 0, fileindex = 1; fileindex <= nfiles; fileindex++){ 238 scribbled |= touchedfiles[fileindex]; 239 n_pissed_on++; 240 } 241 if (scribbled){ 242 /* 243 * Construct an execv argument 244 * We need 1 argument for the editor's name 245 * We need 1 argument for the initial search string 246 * We need n_pissed_on arguments for the file names 247 * We need 1 argument that is a null for execv. 248 * The caller fills in the editor's name. 249 * We fill in the initial search string. 250 * We fill in the arguments, and the null. 251 */ 252 (*r_edargv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *)); 253 (*r_edargc) = n_pissed_on + 2; 254 (*r_edargv)[1] = "+/###/"; 255 n_pissed_on = 2; 256 fprintf(stdout, "You touched file(s):"); 257 sep = " "; 258 for (fileindex = 1; fileindex <= nfiles; fileindex++){ 259 if (!touchedfiles[fileindex]) 260 continue; 261 errorp = *(files[fileindex]); 262 fprintf(stdout,"%s\"%s\"", sep, errorp->error_text[0]); 263 sep = ", "; 264 (*r_edargv)[n_pissed_on++] = errorp->error_text[0]; 265 } 266 fprintf(stdout, "\n"); 267 (*r_edargv)[n_pissed_on] = 0; 268 return(TRUE); 269 } else { 270 fprintf(stdout, "You didn't touch any files.\n"); 271 return(FALSE); 272 } 273 274 } /* end of touchfiles*/ 275 int oktotouch(filename) 276 char *filename; 277 { 278 extern char *suffixlist; 279 register char *src; 280 register char *pat; 281 char *osrc; 282 283 pat = suffixlist; 284 if (pat == 0) 285 return(0); 286 if (*pat == '*') 287 return(1); 288 while (*pat++ != '.') 289 continue; 290 --pat; /* point to the period */ 291 292 for (src = &filename[strlen(filename)], --src; 293 (src > filename) && (*src != '.'); --src) 294 continue; 295 if (*src != '.') 296 return(0); 297 298 for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){ 299 for (; *src /* not at end of the source */ 300 && *pat /* not off end of pattern */ 301 && *pat != '.' /* not off end of sub pattern */ 302 && *pat != '*' /* not wild card */ 303 && *src == *pat; /* and equal... */ 304 src++, pat++) 305 continue; 306 if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*')) 307 return(1); 308 if (*src != 0 && *pat == '*') 309 return(1); 310 while (*pat && *pat != '.') 311 pat++; 312 if (! *pat) 313 return(0); 314 } 315 return(0); 316 } 317 318 FILE *o_touchedfile; /* the old file */ 319 FILE *n_touchedfile; /* the new file */ 320 char *o_name; 321 char n_name[32]; 322 char *canon_name = "ErrorXXXXXX"; 323 int o_lineno; 324 int n_lineno; 325 boolean tempfileopen = FALSE; 326 /* 327 * open the file; guaranteed to be both readable and writable 328 * Well, if it isn't, then return TRUE if something failed 329 */ 330 boolean edit(name) 331 char *name; 332 { 333 o_name = name; 334 if ( (o_touchedfile = fopen(name, "r")) == NULL){ 335 fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n", 336 processname, name); 337 return(TRUE); 338 } 339 strcpy(n_name, canon_name); 340 mktemp(n_name); 341 if ( (n_touchedfile = fopen(n_name, "w")) == NULL){ 342 fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n", 343 processname, name); 344 return(TRUE); 345 } 346 tempfileopen = TRUE; 347 n_lineno = 0; 348 o_lineno = 0; 349 return(FALSE); 350 } 351 /* 352 * Position to the line (before, after) the line given by place 353 */ 354 char edbuffer[BUFSIZ]; 355 insert(place) 356 int place; 357 { 358 --place; /* always insert messages before the offending line*/ 359 for(; o_lineno < place; o_lineno++, n_lineno++){ 360 if(fgets(edbuffer, BUFSIZ, o_touchedfile) == NULL) 361 return; 362 fputs(edbuffer, n_touchedfile); 363 } 364 } 365 366 text(errorp, use_all) 367 register struct error_desc *errorp; 368 boolean use_all; 369 { 370 int offset = use_all ? 0 : 2; 371 fputs(lang_table[errorp->error_language].lang_incomment, n_touchedfile); 372 fprintf(n_touchedfile, "%d [%s] ", 373 errorp->error_line, 374 lang_table[errorp->error_language].lang_name); 375 wordvprint(n_touchedfile, 376 errorp->error_lgtext-offset, errorp->error_text+offset); 377 fputs(lang_table[errorp->error_language].lang_outcomment,n_touchedfile); 378 n_lineno++; 379 } 380 381 writetouched() 382 { 383 int bytes_read; 384 for(; (bytes_read = fread(edbuffer, 1, sizeof(edbuffer), o_touchedfile))!= NULL; ){ 385 fwrite(edbuffer, 1, bytes_read, n_touchedfile); 386 } 387 fclose(n_touchedfile); 388 fclose(o_touchedfile); 389 unlink(o_name); 390 link(n_name, o_name); 391 unlink(n_name); 392 tempfileopen = FALSE; 393 } 394 onintr() 395 { 396 if (inquire("\nInterrupt: Do you want to continue?")){ 397 signal(SIGINT, onintr); 398 return; 399 } 400 if (tempfileopen) 401 writetouched(); 402 exit(1); 403 } 404 errorprint(place, errorp, print_all) 405 FILE *place; 406 struct error_desc *errorp; 407 boolean print_all; 408 { 409 int offset = print_all ? 0 : 2; 410 411 if (errorp->error_e_class == C_IGNORE) 412 return; 413 fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name); 414 wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset); 415 putc('\n', place); 416 } 417 418 boolean inquire(fmt, a1, a2) 419 char *fmt; 420 /*VARARGS1*/ 421 { 422 char buffer[128]; 423 char ch; 424 for(;;){ 425 do{ 426 fflush(stdout); 427 fprintf(stderr, fmt, a1, a2); 428 fflush(stderr); 429 } while (fgets(buffer, 127, queryfile) == NULL); 430 ch = buffer[0]; 431 if (ch == 'Y' || ch == 'y') 432 return(TRUE); 433 if (ch == 'N' || ch == 'n') 434 return(FALSE); 435 fprintf(stderr, "Yes or No only!\n"); 436 } 437 } 438 439 boolean probethisfile(currentfilename) 440 char *currentfilename; 441 { 442 struct stat statbuf; 443 if (stat(currentfilename, &statbuf) != 0) 444 return(FALSE); 445 if ( (statbuf.st_mode&S_IREAD) && (statbuf.st_mode&S_IWRITE)) 446 return(TRUE); 447 return(FALSE); 448 } 449