1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <signal.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include "error.h"
35 
36 FILE	*errorfile;
37 FILE	*queryfile;
38 int	nerrors = 0;
39 Eptr	er_head;
40 Eptr	*errors;
41 
42 int	nfiles = 0;
43 Eptr	**files;	/* array of pointers into errors */
44 int	language = INCC;
45 
46 char	*currentfilename = "????";
47 char	*processname;
48 char	im_on[] = "/dev/tty";	/* my tty name */
49 
50 boolean	query = FALSE;		/* query the operator if touch files */
51 boolean	notouch = FALSE;	/* don't touch ANY files */
52 boolean	terse	= FALSE;	/* Terse output */
53 
54 char	*suffixlist = ".*";	/* initially, can touch any file */
55 
56 static void try(char *name, int argc, char **argv);
57 static void forkvi(int argc, char **argv);
58 static int errorsort(const void *arg1, const void *arg2);
59 
60 
61 /*
62  *	error [-I ignorename] [-n] [-q] [-t suffixlist] [-s] [-v] [infile]
63  *
64  *	-T:	terse output
65  *
66  *	-I:	the following name, `ignorename' contains a list of
67  *		function names that are not to be treated as hard errors.
68  *		Default: ~/.errorsrc
69  *
70  *	-n:	don't touch ANY files!
71  *
72  *	-q:	The user is to be queried before touching each
73  *		file; if not specified, all files with hard, non
74  *		ignorable errors are touched (assuming they can be).
75  *
76  *	-t:	touch only files ending with the list of suffices, each
77  *		suffix preceded by a dot.
78  *		eg, -t .c.y.l
79  *		will touch only files ending with .c, .y or .l
80  *
81  *	-s:	print a summary of the error's categories.
82  *
83  *	-v:	after touching all files, overlay vi(1), ex(1) or ed(1)
84  *		on top of error, entered in the first file with
85  *		an error in it, with the appropriate editor
86  *		set up to use the "next" command to get the other
87  *		files containing errors.
88  *
89  *	-p:	(obsolete: for older versions of pi without bug
90  *		fix regarding printing out the name of the main file
91  *		with an error in it)
92  *		Take the following argument and use it as the name of
93  *		the pascal source file, suffix .p
94  *
95  *	-E:	show the errors in sorted order; intended for
96  *		debugging.
97  *
98  *	-S:	show the errors in unsorted order
99  *		(as they come from the error file)
100  *
101  *	infile:	The error messages come from this file.
102  *		Default: stdin
103  */
104 int
main(int argc,char * argv[])105 main(int argc, char *argv[])
106 {
107 	char	*cp;
108 	char	*ignorename = 0;
109 	int	ed_argc;
110 	char	**ed_argv;		/* return from touchfiles */
111 	boolean	show_errors = FALSE;
112 	boolean	Show_Errors = FALSE;
113 	boolean	pr_summary = FALSE;
114 	boolean	edit_files = FALSE;
115 
116 	processname = argv[0];
117 
118 	errorfile = stdin;
119 	if (argc > 1) {
120 		for (; (argc > 1) && (argv[1][0] == '-'); argc--, argv++) {
121 			for (cp = argv[1] + 1; *cp; cp++) {
122 				switch (*cp) {
123 				default:
124 					(void) fprintf(stderr,
125 					    "%s: -%c: Unknown flag\n",
126 					    processname, *cp);
127 					break;
128 				case 'n':
129 					notouch = TRUE;
130 					break;
131 				case 'q':
132 					query = TRUE;
133 					break;
134 				case 'S':
135 					Show_Errors = TRUE;
136 					break;
137 				case 's':
138 					pr_summary = TRUE;
139 					break;
140 				case 'v':
141 					edit_files = TRUE;
142 					break;
143 				case 'T':
144 					terse = TRUE;
145 					break;
146 				case 't':
147 					*cp-- = 0;
148 					argv++;
149 					argc--;
150 					if (argc > 1) {
151 						suffixlist = argv[1];
152 					}
153 					break;
154 				case 'I':	/* ignore file name */
155 					*cp-- = 0;
156 					argv++;
157 					argc--;
158 					if (argc > 1)
159 						ignorename = argv[1];
160 					break;
161 				}
162 			}
163 		}
164 	}
165 	if (notouch)
166 		suffixlist = 0;
167 	if (argc > 1) {
168 		if (argc > 3) {
169 			(void) fprintf(stderr,
170 			    "%s: Only takes 0 or 1 arguments\n",
171 			    processname);
172 			exit(3);
173 		}
174 		if ((errorfile = fopen(argv[1], "r")) == NULL) {
175 			(void) fprintf(stderr,
176 			    "%s: %s: No such file or directory for "
177 			    "reading errors.\n", processname, argv[1]);
178 			exit(4);
179 		}
180 	}
181 	if ((queryfile = fopen(im_on, "r")) == NULL) {
182 		if (query) {
183 			(void) fprintf(stderr,
184 			    "%s: Can't open \"%s\" to query the user.\n",
185 			    processname, im_on);
186 			exit(9);
187 		}
188 	}
189 	if (signal(SIGINT, onintr) == SIG_IGN)
190 		(void) signal(SIGINT, SIG_IGN);
191 	if (signal(SIGTERM, onintr) == SIG_IGN)
192 		(void) signal(SIGTERM, SIG_IGN);
193 	getignored(ignorename);
194 	eaterrors(&nerrors, &errors);
195 	if (Show_Errors)
196 		printerrors(TRUE, nerrors, errors);
197 	qsort(errors, nerrors, sizeof (Eptr), errorsort);
198 	if (show_errors)
199 		printerrors(FALSE, nerrors, errors);
200 	findfiles(nerrors, errors, &nfiles, &files);
201 	if (pr_summary) {
202 		if (nunknown)
203 			(void) fprintf(stdout,
204 			    "%d Errors are unclassifiable.\n",
205 			    nunknown);
206 		if (nignore)
207 			(void) fprintf(stdout,
208 			    "%d Errors are classifiable, but totally "
209 			    "discarded.\n", nignore);
210 		if (nsyncerrors)
211 			(void) fprintf(stdout,
212 			    "%d Errors are synchronization errors.\n",
213 			    nsyncerrors);
214 		if (nignore)
215 			(void) fprintf(stdout,
216 			    "%d Errors are discarded because they "
217 			    "refer to sacrosanct files.\n", ndiscard);
218 		if (nnulled)
219 			(void) fprintf(stdout,
220 			    "%d Errors are nulled because they refer "
221 			    "to specific functions.\n", nnulled);
222 		if (nnonspec)
223 			(void) fprintf(stdout,
224 			    "%d Errors are not specific to any file.\n",
225 			    nnonspec);
226 		if (nthisfile)
227 			(void) fprintf(stdout,
228 			    "%d Errors are specific to a given file, "
229 			    "but not to a line.\n", nthisfile);
230 		if (ntrue)
231 			(void) fprintf(stdout,
232 			    "%d Errors are true errors, and can be "
233 			    "inserted into the files.\n", ntrue);
234 	}
235 	filenames(nfiles, files);
236 	(void) fflush(stdout);
237 	if (touchfiles(nfiles, files, &ed_argc, &ed_argv) && edit_files)
238 		forkvi(ed_argc, ed_argv);
239 	return (0);
240 }
241 
242 static void
forkvi(int argc,char ** argv)243 forkvi(int argc, char **argv)
244 {
245 	if (query) {
246 		switch (inquire(terse
247 		    ? "Edit? "
248 		    : "Do you still want to edit the files you touched? ")) {
249 		case Q_NO:
250 		case Q_no:
251 			return;
252 		default:
253 			break;
254 		}
255 	}
256 	/*
257 	 *	ed_agument's first argument is
258 	 *	a vi/ex compatabile search argument
259 	 *	to find the first occurance of ###
260 	 */
261 	try("vi", argc, argv);
262 	try("ex", argc, argv);
263 	try("ed", argc-1, argv+1);
264 	(void) fprintf(stdout, "Can't find any editors.\n");
265 }
266 
267 static void
try(char * name,int argc,char ** argv)268 try(char *name, int argc, char **argv)
269 {
270 	argv[0] = name;
271 	wordvprint(stdout, argc, argv);
272 	(void) fprintf(stdout, "\n");
273 	(void) fflush(stderr);
274 	(void) fflush(stdout);
275 	(void) sleep(2);
276 	if (freopen(im_on, "r", stdin) == NULL)
277 		return;
278 	if (freopen(im_on, "w", stdout) == NULL)
279 		return;
280 	(void) execvp(name, argv);
281 }
282 
283 static int
errorsort(const void * arg1,const void * arg2)284 errorsort(const void *arg1, const void *arg2)
285 {
286 	Eptr	*epp1 = (Eptr *)arg1;
287 	Eptr	*epp2 = (Eptr *)arg2;
288 	Eptr	ep1, ep2;
289 	int	order;
290 
291 	/*
292 	 *	Sort by:
293 	 *	1)	synchronization, non specific, discarded errors first;
294 	 *	2)	nulled and true errors last
295 	 *		a)	grouped by similar file names
296 	 *			1)	grouped in ascending line number
297 	 */
298 	ep1 = *epp1; ep2 = *epp2;
299 	if (ep1 == 0 || ep2 == 0)
300 		return (0);
301 	if ((NOTSORTABLE(ep1->error_e_class)) ^
302 	    (NOTSORTABLE(ep2->error_e_class))) {
303 		return (NOTSORTABLE(ep1->error_e_class) ? -1 : 1);
304 	}
305 	if (NOTSORTABLE(ep1->error_e_class))	/* then both are */
306 		return (ep1->error_no - ep2->error_no);
307 	order = strcmp(ep1->error_text[0], ep2->error_text[0]);
308 	if (order == 0) {
309 		return (ep1->error_line - ep2->error_line);
310 	}
311 	return (order);
312 }
313