1 1.32 rillig /* $NetBSD: touch.c,v 1.32 2023/08/26 14:59:44 rillig Exp $ */ 2 1.3 jtc 3 1.1 cgd /* 4 1.3 jtc * Copyright (c) 1980, 1993 5 1.3 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.14 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.6 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.3 jtc #if 0 35 1.3 jtc static char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93"; 36 1.3 jtc #endif 37 1.32 rillig __RCSID("$NetBSD: touch.c,v 1.32 2023/08/26 14:59:44 rillig Exp $"); 38 1.1 cgd #endif /* not lint */ 39 1.1 cgd 40 1.9 kleink #include <sys/param.h> 41 1.1 cgd #include <sys/stat.h> 42 1.6 lukem #include <ctype.h> 43 1.1 cgd #include <signal.h> 44 1.1 cgd #include <stdio.h> 45 1.1 cgd #include <stdlib.h> 46 1.1 cgd #include <string.h> 47 1.6 lukem #include <unistd.h> 48 1.16 lukem #include <util.h> 49 1.6 lukem #include <stdarg.h> 50 1.25 christos #include <err.h> 51 1.1 cgd #include "error.h" 52 1.1 cgd #include "pathnames.h" 53 1.1 cgd 54 1.1 cgd /* 55 1.18 dholland * Iterate through errors 56 1.1 cgd */ 57 1.1 cgd #define EITERATE(p, fv, i) for (p = fv[i]; p < fv[i+1]; p++) 58 1.20 dholland #define ECITERATE(ei, p, lb, errs, nerrs) \ 59 1.20 dholland for (ei = lb; p = errs[ei],ei < nerrs; ei++) 60 1.20 dholland 61 1.20 dholland #define FILEITERATE(fi, lb, num) \ 62 1.20 dholland for (fi = lb; fi <= num; fi++) 63 1.1 cgd 64 1.17 dholland static int touchstatus = Q_YES; 65 1.1 cgd 66 1.17 dholland /* 67 1.18 dholland * codes for probethisfile to return 68 1.17 dholland */ 69 1.18 dholland #define F_NOTEXIST 1 70 1.18 dholland #define F_NOTREAD 2 71 1.18 dholland #define F_NOTWRITE 3 72 1.18 dholland #define F_TOUCHIT 4 73 1.17 dholland 74 1.17 dholland static int countfiles(Eptr *); 75 1.31 rillig static bool nopertain(Eptr **); 76 1.19 dholland static void hackfile(const char *, Eptr **, int, int); 77 1.31 rillig static bool preview(int, Eptr **, int); 78 1.19 dholland static int settotouch(const char *); 79 1.31 rillig static void diverterrors(const char *, int, Eptr **, int, bool, int); 80 1.31 rillig static bool oktotouch(const char *); 81 1.17 dholland static void execvarg(int, int *, char ***); 82 1.31 rillig static bool edit(const char *); 83 1.17 dholland static void insert(int); 84 1.31 rillig static void text(Eptr, bool); 85 1.31 rillig static bool writetouched(bool); 86 1.31 rillig static bool mustoverwrite(FILE *, FILE *); 87 1.31 rillig static bool mustwrite(const char *, size_t, FILE *); 88 1.31 rillig static void errorprint(FILE *, Eptr, bool); 89 1.19 dholland static int probethisfile(const char *); 90 1.17 dholland 91 1.23 christos static const char * 92 1.23 christos makename(const char *name, size_t level) 93 1.23 christos { 94 1.23 christos const char *p; 95 1.23 christos 96 1.24 christos if (level == 0) 97 1.23 christos return name; 98 1.23 christos 99 1.23 christos if (*name == '/') { 100 1.23 christos name++; 101 1.23 christos if (level-- == 0) 102 1.23 christos return name; 103 1.23 christos } 104 1.23 christos 105 1.23 christos while (level-- != 0 && (p = strchr(name, '/')) != NULL) 106 1.23 christos name = p + 1; 107 1.23 christos 108 1.23 christos return name; 109 1.23 christos } 110 1.6 lukem void 111 1.20 dholland findfiles(int my_nerrors, Eptr *my_errors, int *r_nfiles, Eptr ***r_files) 112 1.1 cgd { 113 1.20 dholland int my_nfiles; 114 1.20 dholland Eptr **my_files; 115 1.19 dholland const char *name; 116 1.18 dholland int ei; 117 1.18 dholland int fi; 118 1.18 dholland Eptr errorp; 119 1.1 cgd 120 1.20 dholland my_nfiles = countfiles(my_errors); 121 1.1 cgd 122 1.21 dholland my_files = Calloc(my_nfiles + 3, sizeof (Eptr*)); 123 1.31 rillig touchedfiles = Calloc(my_nfiles+3, sizeof(touchedfiles[0])); 124 1.1 cgd /* 125 1.18 dholland * Now, partition off the error messages 126 1.18 dholland * into those that are synchronization, discarded or 127 1.18 dholland * not specific to any file, and those that were 128 1.18 dholland * nulled or true errors. 129 1.1 cgd */ 130 1.20 dholland my_files[0] = &my_errors[0]; 131 1.20 dholland ECITERATE(ei, errorp, 0, my_errors, my_nerrors) { 132 1.1 cgd if ( ! (NOTSORTABLE(errorp->error_e_class))) 133 1.1 cgd break; 134 1.1 cgd } 135 1.1 cgd /* 136 1.18 dholland * Now, and partition off all error messages 137 1.18 dholland * for a given file. 138 1.1 cgd */ 139 1.20 dholland my_files[1] = &my_errors[ei]; 140 1.32 rillig touchedfiles[0] = false; 141 1.32 rillig touchedfiles[1] = false; 142 1.1 cgd name = "\1"; 143 1.1 cgd fi = 1; 144 1.20 dholland ECITERATE(ei, errorp, ei, my_errors, my_nerrors) { 145 1.23 christos const char *fname = makename(errorp->error_text[0], filelevel); 146 1.21 dholland if (errorp->error_e_class == C_NULLED 147 1.21 dholland || errorp->error_e_class == C_TRUE) { 148 1.23 christos if (strcmp(fname, name) != 0) { 149 1.23 christos name = fname; 150 1.21 dholland touchedfiles[fi] = false; 151 1.20 dholland my_files[fi] = &my_errors[ei]; 152 1.1 cgd fi++; 153 1.1 cgd } 154 1.1 cgd } 155 1.1 cgd } 156 1.20 dholland my_files[fi] = &my_errors[my_nerrors]; 157 1.20 dholland *r_nfiles = my_nfiles; 158 1.20 dholland *r_files = my_files; 159 1.1 cgd } 160 1.1 cgd 161 1.17 dholland static int 162 1.13 wiz countfiles(Eptr *errors) 163 1.1 cgd { 164 1.19 dholland const char *name; 165 1.18 dholland int ei; 166 1.18 dholland Eptr errorp; 167 1.20 dholland int my_nfiles; 168 1.1 cgd 169 1.20 dholland my_nfiles = 0; 170 1.1 cgd name = "\1"; 171 1.20 dholland ECITERATE(ei, errorp, 0, errors, nerrors) { 172 1.18 dholland if (SORTABLE(errorp->error_e_class)) { 173 1.23 christos const char *fname = makename(errorp->error_text[0], 174 1.23 christos filelevel); 175 1.23 christos if (strcmp(fname, name) != 0) { 176 1.20 dholland my_nfiles++; 177 1.24 christos name = fname; 178 1.1 cgd } 179 1.1 cgd } 180 1.1 cgd } 181 1.30 rillig return my_nfiles; 182 1.1 cgd } 183 1.6 lukem 184 1.22 dholland const char *class_table[] = { 185 1.1 cgd /*C_UNKNOWN 0 */ "Unknown", 186 1.1 cgd /*C_IGNORE 1 */ "ignore", 187 1.1 cgd /*C_SYNC 2 */ "synchronization", 188 1.1 cgd /*C_DISCARD 3 */ "discarded", 189 1.1 cgd /*C_NONSPEC 4 */ "non specific", 190 1.1 cgd /*C_THISFILE 5 */ "specific to this file", 191 1.1 cgd /*C_NULLED 6 */ "nulled", 192 1.1 cgd /*C_TRUE 7 */ "true", 193 1.1 cgd /*C_DUPL 8 */ "duplicated" 194 1.1 cgd }; 195 1.1 cgd 196 1.18 dholland int class_count[C_LAST - C_FIRST] = {0}; 197 1.1 cgd 198 1.6 lukem void 199 1.20 dholland filenames(int my_nfiles, Eptr **my_files) 200 1.1 cgd { 201 1.18 dholland int fi; 202 1.19 dholland const char *sep = " "; 203 1.31 rillig bool someerrors; 204 1.1 cgd 205 1.1 cgd /* 206 1.18 dholland * first, simply dump out errors that 207 1.18 dholland * don't pertain to any file 208 1.1 cgd */ 209 1.20 dholland someerrors = nopertain(my_files); 210 1.1 cgd 211 1.31 rillig if (my_nfiles > 0) { 212 1.31 rillig someerrors = true; 213 1.10 is if (terse) 214 1.20 dholland fprintf(stdout, "%d file%s", my_nfiles, plural(my_nfiles)); 215 1.18 dholland else 216 1.10 is fprintf(stdout, "%d file%s contain%s errors", 217 1.20 dholland my_nfiles, plural(my_nfiles), verbform(my_nfiles)); 218 1.18 dholland if (!terse) { 219 1.20 dholland FILEITERATE(fi, 1, my_nfiles) { 220 1.24 christos const char *fname = makename( 221 1.24 christos (*my_files[fi])->error_text[0], filelevel); 222 1.1 cgd fprintf(stdout, "%s\"%s\" (%d)", 223 1.24 christos sep, fname, 224 1.20 dholland (int)(my_files[fi+1] - my_files[fi])); 225 1.1 cgd sep = ", "; 226 1.1 cgd } 227 1.1 cgd } 228 1.1 cgd fprintf(stdout, "\n"); 229 1.1 cgd } 230 1.1 cgd if (!someerrors) 231 1.1 cgd fprintf(stdout, "No errors.\n"); 232 1.1 cgd } 233 1.1 cgd 234 1.1 cgd /* 235 1.18 dholland * Dump out errors that don't pertain to any file 236 1.1 cgd */ 237 1.31 rillig static bool 238 1.20 dholland nopertain(Eptr **my_files) 239 1.1 cgd { 240 1.18 dholland int type; 241 1.31 rillig bool someerrors = false; 242 1.18 dholland Eptr *erpp; 243 1.18 dholland Eptr errorp; 244 1.1 cgd 245 1.20 dholland if (my_files[1] - my_files[0] <= 0) 246 1.31 rillig return false; 247 1.18 dholland for (type = C_UNKNOWN; NOTSORTABLE(type); type++) { 248 1.1 cgd if (class_count[type] <= 0) 249 1.1 cgd continue; 250 1.1 cgd if (type > C_SYNC) 251 1.31 rillig someerrors = true; 252 1.18 dholland if (terse) { 253 1.1 cgd fprintf(stdout, "\t%d %s errors NOT PRINTED\n", 254 1.1 cgd class_count[type], class_table[type]); 255 1.1 cgd } else { 256 1.1 cgd fprintf(stdout, "\n\t%d %s errors follow\n", 257 1.1 cgd class_count[type], class_table[type]); 258 1.20 dholland EITERATE(erpp, my_files, 0) { 259 1.1 cgd errorp = *erpp; 260 1.18 dholland if (errorp->error_e_class == type) { 261 1.21 dholland errorprint(stdout, errorp, true); 262 1.1 cgd } 263 1.1 cgd } 264 1.1 cgd } 265 1.1 cgd } 266 1.30 rillig return someerrors; 267 1.1 cgd } 268 1.1 cgd 269 1.23 christos 270 1.21 dholland bool 271 1.20 dholland touchfiles(int my_nfiles, Eptr **my_files, int *r_edargc, char ***r_edargv) 272 1.1 cgd { 273 1.19 dholland const char *name; 274 1.18 dholland Eptr errorp; 275 1.18 dholland int fi; 276 1.18 dholland Eptr *erpp; 277 1.18 dholland int ntrueerrors; 278 1.31 rillig bool scribbled; 279 1.18 dholland int n_pissed_on; /* # of file touched*/ 280 1.18 dholland int spread; 281 1.1 cgd 282 1.20 dholland FILEITERATE(fi, 1, my_nfiles) { 283 1.23 christos name = makename((*my_files[fi])->error_text[0], filelevel); 284 1.29 rillig spread = (int)(my_files[fi+1] - my_files[fi]); 285 1.23 christos 286 1.1 cgd fprintf(stdout, terse 287 1.1 cgd ? "\"%s\" has %d error%s, " 288 1.1 cgd : "\nFile \"%s\" has %d error%s.\n" 289 1.1 cgd , name ,spread ,plural(spread)); 290 1.1 cgd /* 291 1.18 dholland * First, iterate through all error messages in this file 292 1.18 dholland * to see how many of the error messages really will 293 1.18 dholland * get inserted into the file. 294 1.1 cgd */ 295 1.1 cgd ntrueerrors = 0; 296 1.20 dholland EITERATE(erpp, my_files, fi) { 297 1.1 cgd errorp = *erpp; 298 1.1 cgd if (errorp->error_e_class == C_TRUE) 299 1.1 cgd ntrueerrors++; 300 1.1 cgd } 301 1.1 cgd fprintf(stdout, terse 302 1.1 cgd ? "insert %d\n" 303 1.1 cgd : "\t%d of these errors can be inserted into the file.\n", 304 1.1 cgd ntrueerrors); 305 1.1 cgd 306 1.20 dholland hackfile(name, my_files, fi, ntrueerrors); 307 1.1 cgd } 308 1.21 dholland scribbled = false; 309 1.1 cgd n_pissed_on = 0; 310 1.20 dholland FILEITERATE(fi, 1, my_nfiles) { 311 1.1 cgd scribbled |= touchedfiles[fi]; 312 1.1 cgd n_pissed_on++; 313 1.1 cgd } 314 1.18 dholland if (scribbled) { 315 1.1 cgd /* 316 1.18 dholland * Construct an execv argument 317 1.1 cgd */ 318 1.1 cgd execvarg(n_pissed_on, r_edargc, r_edargv); 319 1.21 dholland return true; 320 1.1 cgd } else { 321 1.1 cgd if (!terse) 322 1.1 cgd fprintf(stdout, "You didn't touch any files.\n"); 323 1.21 dholland return false; 324 1.1 cgd } 325 1.1 cgd } 326 1.1 cgd 327 1.17 dholland static void 328 1.20 dholland hackfile(const char *name, Eptr **my_files, int ix, int my_nerrors) 329 1.1 cgd { 330 1.31 rillig bool previewed; 331 1.18 dholland int errordest; /* where errors go */ 332 1.1 cgd 333 1.1 cgd if (!oktotouch(name)) { 334 1.21 dholland previewed = false; 335 1.1 cgd errordest = TOSTDOUT; 336 1.1 cgd } else { 337 1.29 rillig previewed = preview(my_nerrors, my_files, ix); 338 1.1 cgd errordest = settotouch(name); 339 1.1 cgd } 340 1.1 cgd 341 1.1 cgd if (errordest != TOSTDOUT) 342 1.21 dholland touchedfiles[ix] = true; 343 1.1 cgd 344 1.21 dholland if (previewed && errordest == TOSTDOUT) 345 1.1 cgd return; 346 1.1 cgd 347 1.20 dholland diverterrors(name, errordest, my_files, ix, previewed, my_nerrors); 348 1.1 cgd 349 1.18 dholland if (errordest == TOTHEFILE) { 350 1.1 cgd /* 351 1.18 dholland * overwrite the original file 352 1.1 cgd */ 353 1.31 rillig writetouched(true); 354 1.1 cgd } 355 1.1 cgd } 356 1.1 cgd 357 1.31 rillig static bool 358 1.29 rillig preview(int my_nerrors, Eptr **my_files, int ix) 359 1.1 cgd { 360 1.31 rillig bool back; 361 1.18 dholland Eptr *erpp; 362 1.1 cgd 363 1.20 dholland if (my_nerrors <= 0) 364 1.21 dholland return false; 365 1.21 dholland back = false; 366 1.18 dholland if (query) { 367 1.29 rillig int answer = inquire(terse 368 1.1 cgd ? "Preview? " 369 1.29 rillig : "Do you want to preview the errors first? "); 370 1.29 rillig if (answer == Q_YES || answer == Q_yes) { 371 1.21 dholland back = true; 372 1.20 dholland EITERATE(erpp, my_files, ix) { 373 1.21 dholland errorprint(stdout, *erpp, true); 374 1.1 cgd } 375 1.1 cgd if (!terse) 376 1.1 cgd fprintf(stdout, "\n"); 377 1.1 cgd } 378 1.1 cgd } 379 1.30 rillig return back; 380 1.1 cgd } 381 1.1 cgd 382 1.17 dholland static int 383 1.19 dholland settotouch(const char *name) 384 1.1 cgd { 385 1.18 dholland int dest = TOSTDOUT; 386 1.1 cgd 387 1.18 dholland if (query) { 388 1.27 joerg int reply; 389 1.27 joerg if (terse) 390 1.27 joerg reply = inquire("Touch? "); 391 1.27 joerg else 392 1.27 joerg reply = inquire("Do you want to touch file \"%s\"? ", 393 1.27 joerg name); 394 1.27 joerg switch (reply) { 395 1.1 cgd case Q_NO: 396 1.1 cgd case Q_no: 397 1.15 lukem case Q_error: 398 1.15 lukem touchstatus = Q_NO; 399 1.30 rillig return dest; 400 1.1 cgd default: 401 1.15 lukem touchstatus = Q_YES; 402 1.1 cgd break; 403 1.1 cgd } 404 1.1 cgd } 405 1.1 cgd 406 1.18 dholland switch (probethisfile(name)) { 407 1.1 cgd case F_NOTREAD: 408 1.1 cgd dest = TOSTDOUT; 409 1.1 cgd fprintf(stdout, terse 410 1.1 cgd ? "\"%s\" unreadable\n" 411 1.1 cgd : "File \"%s\" is unreadable\n", 412 1.1 cgd name); 413 1.1 cgd break; 414 1.1 cgd case F_NOTWRITE: 415 1.1 cgd dest = TOSTDOUT; 416 1.1 cgd fprintf(stdout, terse 417 1.1 cgd ? "\"%s\" unwritable\n" 418 1.1 cgd : "File \"%s\" is unwritable\n", 419 1.1 cgd name); 420 1.1 cgd break; 421 1.1 cgd case F_NOTEXIST: 422 1.1 cgd dest = TOSTDOUT; 423 1.1 cgd fprintf(stdout, terse 424 1.1 cgd ? "\"%s\" not found\n" 425 1.1 cgd : "Can't find file \"%s\" to insert error messages into.\n", 426 1.1 cgd name); 427 1.1 cgd break; 428 1.1 cgd default: 429 1.1 cgd dest = edit(name) ? TOSTDOUT : TOTHEFILE; 430 1.1 cgd break; 431 1.1 cgd } 432 1.30 rillig return dest; 433 1.1 cgd } 434 1.1 cgd 435 1.17 dholland static void 436 1.20 dholland diverterrors(const char *name, int dest, Eptr **my_files, int ix, 437 1.31 rillig bool previewed, int nterrors) 438 1.1 cgd { 439 1.20 dholland int my_nerrors; 440 1.18 dholland Eptr *erpp; 441 1.18 dholland Eptr errorp; 442 1.1 cgd 443 1.29 rillig my_nerrors = (int)(my_files[ix+1] - my_files[ix]); 444 1.1 cgd 445 1.21 dholland if (my_nerrors != nterrors && !previewed) { 446 1.26 joerg if (terse) 447 1.26 joerg printf("Uninserted errors\n"); 448 1.26 joerg else 449 1.26 joerg printf(">>Uninserted errors for file \"%s\" follow.\n", 450 1.26 joerg name); 451 1.1 cgd } 452 1.1 cgd 453 1.20 dholland EITERATE(erpp, my_files, ix) { 454 1.1 cgd errorp = *erpp; 455 1.18 dholland if (errorp->error_e_class != C_TRUE) { 456 1.1 cgd if (previewed || touchstatus == Q_NO) 457 1.1 cgd continue; 458 1.21 dholland errorprint(stdout, errorp, true); 459 1.1 cgd continue; 460 1.1 cgd } 461 1.18 dholland switch (dest) { 462 1.1 cgd case TOSTDOUT: 463 1.1 cgd if (previewed || touchstatus == Q_NO) 464 1.1 cgd continue; 465 1.21 dholland errorprint(stdout,errorp, true); 466 1.1 cgd break; 467 1.1 cgd case TOTHEFILE: 468 1.1 cgd insert(errorp->error_line); 469 1.21 dholland text(errorp, false); 470 1.1 cgd break; 471 1.1 cgd } 472 1.1 cgd } 473 1.1 cgd } 474 1.1 cgd 475 1.31 rillig static bool 476 1.19 dholland oktotouch(const char *filename) 477 1.1 cgd { 478 1.19 dholland const char *src; 479 1.22 dholland const char *pat; 480 1.19 dholland const char *osrc; 481 1.1 cgd 482 1.1 cgd pat = suffixlist; 483 1.1 cgd if (pat == 0) 484 1.31 rillig return false; 485 1.1 cgd if (*pat == '*') 486 1.31 rillig return true; 487 1.1 cgd while (*pat++ != '.') 488 1.1 cgd continue; 489 1.1 cgd --pat; /* point to the period */ 490 1.1 cgd 491 1.1 cgd for (src = &filename[strlen(filename)], --src; 492 1.21 dholland src > filename && *src != '.'; --src) 493 1.1 cgd continue; 494 1.1 cgd if (*src != '.') 495 1.31 rillig return false; 496 1.1 cgd 497 1.31 rillig for (src++, pat++, osrc = src; 498 1.31 rillig *src != '\0' && *pat != '\0'; src = osrc, pat++) { 499 1.31 rillig for (; *src != '\0' /* not at end of the source */ 500 1.31 rillig && *pat != '\0' /* not off end of pattern */ 501 1.1 cgd && *pat != '.' /* not off end of sub pattern */ 502 1.1 cgd && *pat != '*' /* not wild card */ 503 1.1 cgd && *src == *pat; /* and equal... */ 504 1.1 cgd src++, pat++) 505 1.1 cgd continue; 506 1.32 rillig if (*src == '\0' 507 1.32 rillig && (*pat == '\0' || *pat == '.' || *pat == '*')) 508 1.31 rillig return true; 509 1.32 rillig if (*src != '\0' && *pat == '*') 510 1.31 rillig return true; 511 1.31 rillig while (*pat != '\0' && *pat != '.') 512 1.1 cgd pat++; 513 1.31 rillig if (*pat == '\0') 514 1.31 rillig return false; 515 1.1 cgd } 516 1.31 rillig return false; 517 1.1 cgd } 518 1.6 lukem 519 1.1 cgd /* 520 1.18 dholland * Construct an execv argument 521 1.18 dholland * We need 1 argument for the editor's name 522 1.18 dholland * We need 1 argument for the initial search string 523 1.18 dholland * We need n_pissed_on arguments for the file names 524 1.18 dholland * We need 1 argument that is a null for execv. 525 1.18 dholland * The caller fills in the editor's name. 526 1.18 dholland * We fill in the initial search string. 527 1.18 dholland * We fill in the arguments, and the null. 528 1.1 cgd */ 529 1.17 dholland static void 530 1.13 wiz execvarg(int n_pissed_on, int *r_argc, char ***r_argv) 531 1.1 cgd { 532 1.18 dholland Eptr p; 533 1.23 christos const char *sep, *name; 534 1.18 dholland int fi; 535 1.1 cgd 536 1.6 lukem sep = NULL; 537 1.21 dholland (*r_argv) = Calloc(n_pissed_on + 3, sizeof(char *)); 538 1.1 cgd (*r_argc) = n_pissed_on + 2; 539 1.22 dholland (*r_argv)[1] = Strdup("+1;/###/"); /* XXX leaked */ 540 1.1 cgd n_pissed_on = 2; 541 1.18 dholland if (!terse) { 542 1.1 cgd fprintf(stdout, "You touched file(s):"); 543 1.1 cgd sep = " "; 544 1.1 cgd } 545 1.20 dholland FILEITERATE(fi, 1, nfiles) { 546 1.1 cgd if (!touchedfiles[fi]) 547 1.1 cgd continue; 548 1.1 cgd p = *(files[fi]); 549 1.23 christos name = makename(p->error_text[0], filelevel); 550 1.18 dholland if (!terse) { 551 1.23 christos fprintf(stdout,"%s\"%s\"", sep, name); 552 1.1 cgd sep = ", "; 553 1.1 cgd } 554 1.23 christos (*r_argv)[n_pissed_on++] = __UNCONST(name); 555 1.1 cgd } 556 1.1 cgd if (!terse) 557 1.1 cgd fprintf(stdout, "\n"); 558 1.32 rillig (*r_argv)[n_pissed_on] = NULL; 559 1.1 cgd } 560 1.1 cgd 561 1.17 dholland static FILE *o_touchedfile; /* the old file */ 562 1.17 dholland static FILE *n_touchedfile; /* the new file */ 563 1.19 dholland static const char *o_name; 564 1.17 dholland static char n_name[MAXPATHLEN]; 565 1.17 dholland static int o_lineno; 566 1.17 dholland static int n_lineno; 567 1.31 rillig static bool tempfileopen = false; 568 1.18 dholland 569 1.1 cgd /* 570 1.18 dholland * open the file; guaranteed to be both readable and writable 571 1.18 dholland * Well, if it isn't, then return TRUE if something failed 572 1.1 cgd */ 573 1.31 rillig static bool 574 1.19 dholland edit(const char *name) 575 1.1 cgd { 576 1.4 lukem int fd; 577 1.9 kleink const char *tmpdir; 578 1.4 lukem 579 1.1 cgd o_name = name; 580 1.18 dholland if ((o_touchedfile = fopen(name, "r")) == NULL) { 581 1.25 christos warn("Can't open file `%s' to touch (read)", name); 582 1.21 dholland return true; 583 1.1 cgd } 584 1.9 kleink if ((tmpdir = getenv("TMPDIR")) == NULL) 585 1.9 kleink tmpdir = _PATH_TMP; 586 1.9 kleink (void)snprintf(n_name, sizeof (n_name), "%s/%s", tmpdir, TMPFILE); 587 1.4 lukem fd = -1; 588 1.4 lukem if ((fd = mkstemp(n_name)) == -1 || 589 1.4 lukem (n_touchedfile = fdopen(fd, "w")) == NULL) { 590 1.25 christos warn("Can't open file `%s' to touch (write)", name); 591 1.4 lukem if (fd != -1) 592 1.4 lukem close(fd); 593 1.21 dholland return true; 594 1.1 cgd } 595 1.21 dholland tempfileopen = true; 596 1.1 cgd n_lineno = 0; 597 1.1 cgd o_lineno = 0; 598 1.21 dholland return false; 599 1.1 cgd } 600 1.6 lukem 601 1.1 cgd /* 602 1.18 dholland * Position to the line (before, after) the line given by place 603 1.1 cgd */ 604 1.17 dholland static char edbuf[BUFSIZ]; 605 1.6 lukem 606 1.17 dholland static void 607 1.13 wiz insert(int place) 608 1.1 cgd { 609 1.18 dholland --place; /* always insert messages before the offending line */ 610 1.18 dholland for (; o_lineno < place; o_lineno++, n_lineno++) { 611 1.18 dholland if (fgets(edbuf, BUFSIZ, o_touchedfile) == NULL) 612 1.1 cgd return; 613 1.1 cgd fputs(edbuf, n_touchedfile); 614 1.1 cgd } 615 1.1 cgd } 616 1.1 cgd 617 1.17 dholland static void 618 1.31 rillig text(Eptr p, bool use_all) 619 1.1 cgd { 620 1.18 dholland int offset = use_all ? 0 : 2; 621 1.1 cgd 622 1.1 cgd fputs(lang_table[p->error_language].lang_incomment, n_touchedfile); 623 1.1 cgd fprintf(n_touchedfile, "%d [%s] ", 624 1.1 cgd p->error_line, 625 1.1 cgd lang_table[p->error_language].lang_name); 626 1.1 cgd wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset); 627 1.18 dholland fputs(lang_table[p->error_language].lang_outcomment, n_touchedfile); 628 1.1 cgd n_lineno++; 629 1.1 cgd } 630 1.1 cgd 631 1.1 cgd /* 632 1.18 dholland * write the touched file to its temporary copy, 633 1.18 dholland * then bring the temporary in over the local file 634 1.1 cgd */ 635 1.31 rillig static bool 636 1.31 rillig writetouched(bool overwrite) 637 1.1 cgd { 638 1.29 rillig size_t nread; 639 1.18 dholland FILE *localfile; 640 1.20 dholland FILE *temp; 641 1.31 rillig bool botch; 642 1.31 rillig bool oktorm; 643 1.1 cgd 644 1.31 rillig botch = false; 645 1.31 rillig oktorm = true; 646 1.5 pk while ((nread = fread(edbuf, 1, sizeof(edbuf), o_touchedfile)) != 0) { 647 1.18 dholland if (nread != fwrite(edbuf, 1, nread, n_touchedfile)) { 648 1.1 cgd /* 649 1.18 dholland * Catastrophe in temporary area: file system full? 650 1.1 cgd */ 651 1.31 rillig botch = true; 652 1.25 christos warn("write failure: No errors inserted in `%s'", 653 1.25 christos o_name); 654 1.1 cgd } 655 1.1 cgd } 656 1.1 cgd fclose(n_touchedfile); 657 1.1 cgd fclose(o_touchedfile); 658 1.18 dholland 659 1.1 cgd /* 660 1.18 dholland * Now, copy the temp file back over the original 661 1.18 dholland * file, thus preserving links, etc 662 1.1 cgd */ 663 1.31 rillig if (!botch && overwrite) { 664 1.1 cgd localfile = NULL; 665 1.20 dholland temp = NULL; 666 1.18 dholland if ((localfile = fopen(o_name, "w")) == NULL) { 667 1.25 christos warn("Can't open file `%s' to overwrite", o_name); 668 1.31 rillig botch = true; 669 1.1 cgd } 670 1.20 dholland if ((temp = fopen(n_name, "r")) == NULL) { 671 1.25 christos warn("Can't open file `%s' to read", n_name); 672 1.31 rillig botch = true; 673 1.1 cgd } 674 1.1 cgd if (!botch) 675 1.20 dholland oktorm = mustoverwrite(localfile, temp); 676 1.1 cgd if (localfile != NULL) 677 1.1 cgd fclose(localfile); 678 1.20 dholland if (temp != NULL) 679 1.20 dholland fclose(temp); 680 1.1 cgd } 681 1.31 rillig if (!oktorm) 682 1.25 christos errx(1, "Catastrophe: A copy of `%s': was saved in `%s'", 683 1.25 christos o_name, n_name); 684 1.1 cgd /* 685 1.18 dholland * Kiss the temp file good bye 686 1.1 cgd */ 687 1.1 cgd unlink(n_name); 688 1.21 dholland tempfileopen = false; 689 1.21 dholland return true; 690 1.1 cgd } 691 1.6 lukem 692 1.1 cgd /* 693 1.31 rillig * return whether the tmpfile can be removed after writing it out 694 1.1 cgd */ 695 1.31 rillig static bool 696 1.20 dholland mustoverwrite(FILE *preciousfile, FILE *temp) 697 1.1 cgd { 698 1.29 rillig size_t nread; 699 1.1 cgd 700 1.20 dholland while ((nread = fread(edbuf, 1, sizeof(edbuf), temp)) != 0) { 701 1.31 rillig if (!mustwrite(edbuf, nread, preciousfile)) 702 1.31 rillig return false; 703 1.1 cgd } 704 1.31 rillig return true; 705 1.1 cgd } 706 1.18 dholland 707 1.1 cgd /* 708 1.31 rillig * return false on catastrophe 709 1.1 cgd */ 710 1.31 rillig static bool 711 1.29 rillig mustwrite(const char *base, size_t n, FILE *preciousfile) 712 1.1 cgd { 713 1.29 rillig size_t nwrote; 714 1.1 cgd 715 1.29 rillig if (n == 0) 716 1.31 rillig return true; 717 1.1 cgd nwrote = fwrite(base, 1, n, preciousfile); 718 1.1 cgd if (nwrote == n) 719 1.31 rillig return true; 720 1.25 christos warn("write failed"); 721 1.18 dholland switch (inquire(terse 722 1.1 cgd ? "Botch overwriting: retry? " 723 1.18 dholland : "Botch overwriting the source file: retry? ")) { 724 1.1 cgd case Q_YES: 725 1.1 cgd case Q_yes: 726 1.1 cgd mustwrite(base + nwrote, n - nwrote, preciousfile); 727 1.31 rillig return true; 728 1.1 cgd case Q_NO: 729 1.1 cgd case Q_no: 730 1.18 dholland switch (inquire("Are you sure? ")) { 731 1.15 lukem case Q_error: 732 1.1 cgd case Q_YES: 733 1.1 cgd case Q_yes: 734 1.31 rillig return false; 735 1.1 cgd case Q_NO: 736 1.1 cgd case Q_no: 737 1.1 cgd mustwrite(base + nwrote, n - nwrote, preciousfile); 738 1.31 rillig return true; 739 1.28 christos default: 740 1.28 christos abort(); 741 1.1 cgd } 742 1.29 rillig /* FALLTHROUGH */ 743 1.15 lukem case Q_error: 744 1.1 cgd default: 745 1.31 rillig return false; 746 1.1 cgd } 747 1.1 cgd } 748 1.1 cgd 749 1.1 cgd void 750 1.16 lukem onintr(int sig) 751 1.1 cgd { 752 1.18 dholland switch (inquire(terse 753 1.1 cgd ? "\nContinue? " 754 1.18 dholland : "\nInterrupt: Do you want to continue? ")) { 755 1.1 cgd case Q_YES: 756 1.1 cgd case Q_yes: 757 1.16 lukem signal(sig, onintr); 758 1.1 cgd return; 759 1.15 lukem case Q_error: 760 1.1 cgd default: 761 1.18 dholland if (tempfileopen) { 762 1.1 cgd /* 763 1.18 dholland * Don't overwrite the original file! 764 1.1 cgd */ 765 1.31 rillig writetouched(false); 766 1.1 cgd } 767 1.16 lukem (void)raise_default_signal(sig); 768 1.16 lukem _exit(127); 769 1.1 cgd } 770 1.1 cgd /*NOTREACHED*/ 771 1.1 cgd } 772 1.1 cgd 773 1.17 dholland static void 774 1.31 rillig errorprint(FILE *place, Eptr errorp, bool print_all) 775 1.1 cgd { 776 1.18 dholland int offset = print_all ? 0 : 2; 777 1.1 cgd 778 1.1 cgd if (errorp->error_e_class == C_IGNORE) 779 1.1 cgd return; 780 1.1 cgd fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name); 781 1.1 cgd wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset); 782 1.1 cgd putc('\n', place); 783 1.1 cgd } 784 1.1 cgd 785 1.6 lukem int 786 1.19 dholland inquire(const char *fmt, ...) 787 1.1 cgd { 788 1.6 lukem va_list ap; 789 1.18 dholland char buffer[128]; 790 1.1 cgd 791 1.1 cgd if (queryfile == NULL) 792 1.30 rillig return Q_error; 793 1.18 dholland for (;;) { 794 1.15 lukem fflush(stdout); 795 1.15 lukem va_start(ap, fmt); 796 1.15 lukem vfprintf(stderr, fmt, ap); 797 1.15 lukem va_end(ap); 798 1.15 lukem fflush(stderr); 799 1.15 lukem if (fgets(buffer, 127, queryfile) == NULL) 800 1.30 rillig return Q_error; 801 1.18 dholland switch (buffer[0]) { 802 1.30 rillig case 'Y': return Q_YES; 803 1.30 rillig case 'y': return Q_yes; 804 1.30 rillig case 'N': return Q_NO; 805 1.30 rillig case 'n': return Q_no; 806 1.18 dholland default: fprintf(stderr, "Yes or No only!\n"); 807 1.1 cgd } 808 1.1 cgd } 809 1.1 cgd } 810 1.1 cgd 811 1.17 dholland static int 812 1.19 dholland probethisfile(const char *name) 813 1.1 cgd { 814 1.1 cgd struct stat statbuf; 815 1.18 dholland 816 1.1 cgd if (stat(name, &statbuf) < 0) 817 1.30 rillig return F_NOTEXIST; 818 1.18 dholland if ((statbuf.st_mode & S_IREAD) == 0) 819 1.30 rillig return F_NOTREAD; 820 1.18 dholland if ((statbuf.st_mode & S_IWRITE) == 0) 821 1.30 rillig return F_NOTWRITE; 822 1.30 rillig return F_TOUCHIT; 823 1.1 cgd } 824