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