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