xref: /netbsd-src/usr.bin/error/input.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: input.c,v 1.16 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[] = "@(#)input.c	8.1 (Berkeley) 6/6/93";
36 #endif
37 __RCSID("$NetBSD: input.c,v 1.16 2009/08/13 06:59:37 dholland Exp $");
38 #endif /* not lint */
39 
40 #include <stdio.h>
41 #include <ctype.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include "error.h"
45 
46 int cur_wordc;		/* how long the current error message is */
47 char **cur_wordv;	/* the actual error message */
48 
49 static Errorclass catchall(void);
50 static Errorclass cpp(void);
51 static Errorclass f77(void);
52 static Errorclass lint0(void);
53 static Errorclass lint1(void);
54 static Errorclass lint2(void);
55 static Errorclass lint3(void);
56 static Errorclass make(void);
57 static Errorclass mod2(void);
58 static Errorclass onelong(void);
59 static Errorclass pccccom(void);	/* Portable C Compiler C Compiler */
60 static Errorclass ri(void);
61 static Errorclass richieccom(void);	/* Richie Compiler for 11 */
62 static Errorclass troff(void);
63 
64 /*
65  * Eat all of the lines in the input file, attempting to categorize
66  * them by their various flavors
67  */
68 void
69 eaterrors(int *r_errorc, Eptr **r_errorv)
70 {
71 	Errorclass errorclass = C_SYNC;
72 	char *line;
73 	const char *inbuffer;
74 	size_t inbuflen;
75 
76     for (;;) {
77 	if ((inbuffer = fgetln(errorfile, &inbuflen)) == NULL)
78 		break;
79 	line = Calloc(inbuflen + 1, sizeof(char));
80 	memcpy(line, inbuffer, inbuflen);
81 	line[inbuflen] = '\0';
82 	wordvbuild(line, &cur_wordc, &cur_wordv);
83 
84 	/*
85 	 * for convenience, convert cur_wordv to be 1 based, instead
86 	 * of 0 based.
87 	 */
88 	cur_wordv -= 1;
89 	if (cur_wordc > 0 &&
90 	   ((( errorclass = onelong() ) != C_UNKNOWN)
91 	   || (( errorclass = cpp() ) != C_UNKNOWN)
92 	   || (( errorclass = pccccom() ) != C_UNKNOWN)
93 	   || (( errorclass = richieccom() ) != C_UNKNOWN)
94 	   || (( errorclass = lint0() ) != C_UNKNOWN)
95 	   || (( errorclass = lint1() ) != C_UNKNOWN)
96 	   || (( errorclass = lint2() ) != C_UNKNOWN)
97 	   || (( errorclass = lint3() ) != C_UNKNOWN)
98 	   || (( errorclass = make() ) != C_UNKNOWN)
99 	   || (( errorclass = f77() ) != C_UNKNOWN)
100 	   || ((errorclass = pi() ) != C_UNKNOWN)
101 	   || (( errorclass = ri() )!= C_UNKNOWN)
102 	   || (( errorclass = mod2() )!= C_UNKNOWN)
103 	   || (( errorclass = troff() )!= C_UNKNOWN))
104 	) ;
105 	else
106 		errorclass = catchall();
107 	if (cur_wordc)
108 		erroradd(cur_wordc, cur_wordv+1, errorclass, C_UNKNOWN);
109     }
110 #ifdef FULLDEBUG
111     printf("%d errorentrys\n", nerrors);
112 #endif
113     arrayify(r_errorc, r_errorv, er_head);
114 }
115 
116 /*
117  * create a new error entry, given a zero based array and count
118  */
119 void
120 erroradd(int errorlength, char **errorv, Errorclass errorclass,
121 	 Errorclass errorsubclass)
122 {
123 	Eptr newerror;
124 	const char *cp;
125 
126 	if (errorclass == C_TRUE) {
127 		/* check canonicalization of the second argument*/
128 		for (cp = errorv[1]; *cp && isdigit((unsigned char)*cp); cp++)
129 			continue;
130 		errorclass = (*cp == '\0') ? C_TRUE : C_NONSPEC;
131 #ifdef FULLDEBUG
132 		if (errorclass != C_TRUE)
133 			printf("The 2nd word, \"%s\" is not a number.\n",
134 				errorv[1]);
135 #endif
136 	}
137 	if (errorlength > 0) {
138 		newerror = Calloc(1, sizeof(Edesc));
139 		newerror->error_language = language; /* language is global */
140 		newerror->error_text = errorv;
141 		newerror->error_lgtext = errorlength;
142 		if (errorclass == C_TRUE)
143 			newerror->error_line = atoi(errorv[1]);
144 		newerror->error_e_class = errorclass;
145 		newerror->error_s_class = errorsubclass;
146 		switch (newerror->error_e_class = discardit(newerror)) {
147 			case C_SYNC:		nsyncerrors++; break;
148 			case C_DISCARD: 	ndiscard++; break;
149 			case C_NULLED:		nnulled++; break;
150 			case C_NONSPEC:		nnonspec++; break;
151 			case C_THISFILE: 	nthisfile++; break;
152 			case C_TRUE:		ntrue++; break;
153 			case C_UNKNOWN:		nunknown++; break;
154 			case C_IGNORE:		nignore++; break;
155 		}
156 		newerror->error_next = er_head;
157 		er_head = newerror;
158 		newerror->error_no = nerrors++;
159 	}	/* length > 0 */
160 }
161 
162 static Errorclass
163 onelong(void)
164 {
165 	char **nwordv;
166 
167 	if (cur_wordc == 1 && language != INLD) {
168 		/*
169 		 * We have either:
170 		 *	a) file name from cc
171 		 *	b) Assembler telling world that it is complaining
172 		 *	c) Noise from make ("Stop.")
173 		 *	c) Random noise
174 		 */
175 		cur_wordc = 0;
176 		if (strcmp(cur_wordv[1], "Stop.") == 0) {
177 			language = INMAKE;
178 			return (C_SYNC);
179 		}
180 		if (strcmp(cur_wordv[1], "Assembler:") == 0) {
181 			/* assembler always alerts us to what happened*/
182 			language = INAS;
183 			return (C_SYNC);
184 		} else
185 		if (strcmp(cur_wordv[1], "Undefined:") == 0) {
186 			/* loader complains about unknown symbols*/
187 			language = INLD;
188 			return (C_SYNC);
189 		}
190 		if (lastchar(cur_wordv[1]) == ':') {
191 			/* cc tells us what file we are in */
192 			currentfilename = cur_wordv[1];
193 			(void)substitute(currentfilename, ':', '\0');
194 			language = INCC;
195 			return (C_SYNC);
196 		}
197 	} else
198 	if (cur_wordc == 1 && language == INLD) {
199 		nwordv = Calloc(4, sizeof(char *));
200 		nwordv[0] = Strdup("ld:");	/* XXX leaked */
201 		nwordv[1] = cur_wordv[1];
202 		nwordv[2] = Strdup("is");	/* XXX leaked */
203 		nwordv[3] = Strdup("undefined.");/* XXX leaked */
204 		cur_wordc = 4;
205 		cur_wordv = nwordv - 1;
206 		return (C_NONSPEC);
207 	} else
208 	if (cur_wordc == 1) {
209 		return (C_SYNC);
210 	}
211 	return (C_UNKNOWN);
212 }	/* end of one long */
213 
214 static Errorclass
215 cpp(void)
216 {
217 	/*
218 	 * Now attempt a cpp error message match
219 	 * Examples:
220 	 *	./morse.h: 23: undefined control
221 	 *	morsesend.c: 229: MAGNIBBL: argument mismatch
222 	 *	morsesend.c: 237: MAGNIBBL: argument mismatch
223 	 *	test1.c: 6: undefined control
224 	 */
225 	if (cur_wordc < 3)
226 		return (C_UNKNOWN);
227 	if (language != INLD		/* loader errors have almost same fmt */
228 	    && lastchar(cur_wordv[1]) == ':'
229 	    && isdigit((unsigned char)firstchar(cur_wordv[2]))
230 	    && lastchar(cur_wordv[2]) == ':') {
231 		language = INCPP;
232 		clob_last(cur_wordv[1], '\0');
233 		clob_last(cur_wordv[2], '\0');
234 		return (C_TRUE);
235 	}
236 	return (C_UNKNOWN);
237 }	/*end of cpp*/
238 
239 static Errorclass
240 pccccom(void)
241 {
242 	/*
243 	 * Now attempt a ccom error message match:
244 	 * Examples:
245 	 *	"morsesend.c", line 237: operands of & have incompatible types
246 	 *	"test.c", line 7: warning: old-fashioned initialization: use =
247 	 *	"subdir.d/foo2.h", line 1: illegal initialization
248 	 */
249 	if (cur_wordc < 4)
250 		return (C_UNKNOWN);
251 	if (firstchar(cur_wordv[1]) == '"'
252 	    && lastchar(cur_wordv[1]) == ','
253 	    && next_lastchar(cur_wordv[1]) == '"'
254 	    && strcmp(cur_wordv[2], "line") == 0
255 	    && isdigit((unsigned char)firstchar(cur_wordv[3]))
256 	    && lastchar(cur_wordv[3]) == ':') {
257 		clob_last(cur_wordv[1], '\0');	/* drop last , */
258 		clob_last(cur_wordv[1], '\0');	/* drop last " */
259 		cur_wordv[1]++;			/* drop first " */
260 		clob_last(cur_wordv[3], '\0');	/* drop : on line number */
261 		cur_wordv[2] = cur_wordv[1];	/* overwrite "line" */
262 		cur_wordv++;		/*compensate*/
263 		cur_wordc--;
264 		currentfilename = cur_wordv[1];
265 		language = INCC;
266 		return (C_TRUE);
267 	}
268 	return (C_UNKNOWN);
269 }	/* end of ccom */
270 
271 /*
272  * Do the error message from the Richie C Compiler for the PDP11,
273  * which has this source:
274  *
275  *	if (filename[0])
276  *		fprintf(stderr, "%s:", filename);
277  *	fprintf(stderr, "%d: ", line);
278  *
279  */
280 
281 static Errorclass
282 richieccom(void)
283 {
284 	char *cp;
285 	char **nwordv;
286 	char *file;
287 
288 	if (cur_wordc < 2)
289 		return (C_UNKNOWN);
290 
291 	if (lastchar(cur_wordv[1]) == ':') {
292 		cp = cur_wordv[1] + strlen(cur_wordv[1]) - 1;
293 		while (isdigit((unsigned char)*--cp))
294 			continue;
295 		if (*cp == ':') {
296 			clob_last(cur_wordv[1], '\0');	/* last : */
297 			*cp = '\0';			/* first : */
298 			file = cur_wordv[1];
299 			nwordv = wordvsplice(1, cur_wordc, cur_wordv+1);
300 			nwordv[0] = file;
301 			nwordv[1] = cp + 1;
302 			cur_wordc += 1;
303 			cur_wordv = nwordv - 1;
304 			language = INCC;
305 			currentfilename = cur_wordv[1];
306 			return (C_TRUE);
307 		}
308 	}
309 	return (C_UNKNOWN);
310 }
311 
312 static Errorclass
313 lint0(void)
314 {
315 	char **nwordv;
316 	char *line, *file;
317 
318 	/*
319 	 * Attempt a match for the new lint style normal compiler
320 	 * error messages, of the form
321 	 *
322 	 *	printf("%s(%d): %s\n", filename, linenumber, message);
323 	 */
324 	if (cur_wordc < 2)
325 		return (C_UNKNOWN);
326 
327 	if (lastchar(cur_wordv[1]) == ':'
328 	    && next_lastchar(cur_wordv[1]) == ')') {
329 		clob_last(cur_wordv[1], '\0'); /* colon */
330 		if (persperdexplode(cur_wordv[1], &line, &file)) {
331 			nwordv = wordvsplice(1, cur_wordc, cur_wordv+1);
332 			nwordv[0] = file;	/* file name */
333 			nwordv[1] = line;	/* line number */
334 			cur_wordc += 1;
335 			cur_wordv = nwordv - 1;
336 			language = INLINT;
337 			return (C_TRUE);
338 		}
339 		cur_wordv[1][strlen(cur_wordv[1])] = ':';
340 	}
341 	return (C_UNKNOWN);
342 }
343 
344 static Errorclass
345 lint1(void)
346 {
347 	char *line1 = NULL, *line2 = NULL;
348 	char *file1 = NULL, *file2 = NULL;
349 	char **nwordv1, **nwordv2;
350 
351 	/*
352 	 * Now, attempt a match for the various errors that lint
353 	 * can complain about.
354 	 *
355 	 * Look first for type 1 lint errors
356 	 */
357 	if (cur_wordc > 1 && strcmp(cur_wordv[cur_wordc-1], "::") == 0) {
358 	 /*
359   	  * %.7s, arg. %d used inconsistently %s(%d) :: %s(%d)
360   	  * %.7s value used inconsistently %s(%d) :: %s(%d)
361   	  * %.7s multiply declared %s(%d) :: %s(%d)
362   	  * %.7s value declared inconsistently %s(%d) :: %s(%d)
363   	  * %.7s function value type must be declared before use %s(%d) :: %s(%d)
364 	  */
365 		language = INLINT;
366 		if (cur_wordc > 2
367 		    && persperdexplode(cur_wordv[cur_wordc], &line2, &file2)
368 		    && persperdexplode(cur_wordv[cur_wordc-2], &line1, &file1)) {
369 			nwordv1 = wordvsplice(2, cur_wordc, cur_wordv+1);
370 			nwordv2 = wordvsplice(2, cur_wordc, cur_wordv+1);
371 			nwordv1[0] = file1;
372 			nwordv1[1] = line1;
373 			erroradd(cur_wordc+2, nwordv1, C_TRUE, C_DUPL); /* takes 0 based*/
374 			nwordv2[0] = file2;
375 			nwordv2[1] = line2;
376 			cur_wordc = cur_wordc + 2;
377 			cur_wordv = nwordv2 - 1;	/* 1 based */
378 			return (C_TRUE);
379 		}
380 	}
381 	if (file2)
382 		free(file2);
383 	if (file1)
384 		free(file1);
385 	if (line2)
386 		free(line2);
387 	if (line1)
388 		free(line1);
389 	return (C_UNKNOWN);
390 } /* end of lint 1*/
391 
392 static Errorclass
393 lint2(void)
394 {
395 	char *file;
396 	char *line;
397 	char **nwordv;
398 
399 	/*
400 	 * Look for type 2 lint errors
401 	 *
402 	 *	%.7s used( %s(%d) ), but not defined
403 	 *	%.7s defined( %s(%d) ), but never used
404 	 *	%.7s declared( %s(%d) ), but never used or defined
405 	 *
406 	 *	bufp defined( "./metric.h"(10) ), but never used
407 	 */
408 	if (cur_wordc < 5)
409 		return (C_UNKNOWN);
410 
411 	if (lastchar(cur_wordv[2]) == '(' /* ')' */
412 	    && strcmp(cur_wordv[4], "),") == 0) {
413 		language = INLINT;
414 		if (persperdexplode(cur_wordv[3], &line, &file)) {
415 			nwordv = wordvsplice(2, cur_wordc, cur_wordv+1);
416 			nwordv[0] = file;
417 			nwordv[1] = line;
418 			cur_wordc = cur_wordc + 2;
419 			cur_wordv = nwordv - 1;	/* 1 based */
420 			return (C_TRUE);
421 		}
422 	}
423 	return (C_UNKNOWN);
424 } /* end of lint 2*/
425 
426 #if 0 /* not const-correct */
427 static char *Lint31[4] = {"returns", "value", "which", "is"};
428 static char *Lint32[6] = {"value", "is", "used,", "but", "none", "returned"};
429 #else
430 DECL_STRINGS_4(static, Lint31,
431 	       "returns", "value", "which", "is");
432 DECL_STRINGS_6(static, Lint32,
433 	       "value", "is", "used,", "but", "none", "returned");
434 #endif
435 
436 static Errorclass
437 lint3(void)
438 {
439 	if (cur_wordc < 3)
440 		return (C_UNKNOWN);
441 	if (wordvcmp(cur_wordv+2, 4, Lint31) == 0
442 	    || wordvcmp(cur_wordv+2, 6, Lint32) == 0) {
443 		language = INLINT;
444 		return (C_NONSPEC);
445 	}
446 	return (C_UNKNOWN);
447 }
448 
449 /*
450  * Special word vectors for use by F77 recognition
451  */
452 #if 0 /* not const-correct */
453 static char *F77_fatal[3] = {"Compiler", "error", "line"};
454 static char *F77_error[3] = {"Error", "on", "line"};
455 static char *F77_warning[3] = {"Warning", "on", "line"};
456 static char *F77_no_ass[3] = {"Error.","No","assembly."};
457 #else
458 DECL_STRINGS_3(static, F77_fatal, "Compiler", "error", "line");
459 DECL_STRINGS_3(static, F77_error, "Error", "on", "line");
460 DECL_STRINGS_3(static, F77_warning, "Warning", "on", "line");
461 DECL_STRINGS_3(static, F77_no_ass, "Error.", "No", "assembly.");
462 #endif
463 
464 static Errorclass
465 f77(void)
466 {
467 	char **nwordv;
468 
469 	/*
470 	 * look for f77 errors:
471 	 * Error messages from /usr/src/cmd/f77/error.c, with
472 	 * these printf formats:
473 	 *
474 	 *	Compiler error line %d of %s: %s
475 	 *	Error on line %d of %s: %s
476 	 *	Warning on line %d of %s: %s
477 	 *	Error.  No assembly.
478 	 */
479 	if (cur_wordc == 3 && wordvcmp(cur_wordv+1, 3, F77_no_ass) == 0) {
480 		cur_wordc = 0;
481 		return (C_SYNC);
482 	}
483 	if (cur_wordc < 6)
484 		return (C_UNKNOWN);
485 	if (lastchar(cur_wordv[6]) == ':'
486 	    && (
487 		wordvcmp(cur_wordv+1, 3, F77_fatal) == 0
488 		|| wordvcmp(cur_wordv+1, 3, F77_error) == 0
489 		|| wordvcmp(cur_wordv+1, 3, F77_warning) == 0
490 	       )
491 	) {
492 		language = INF77;
493 		nwordv = wordvsplice(2, cur_wordc, cur_wordv+1);
494 		nwordv[0] = cur_wordv[6];
495 		clob_last(nwordv[0],'\0');
496 		nwordv[1] = cur_wordv[4];
497 		cur_wordc += 2;
498 		cur_wordv = nwordv - 1;	/* 1 based */
499 		return (C_TRUE);
500 	}
501 	return (C_UNKNOWN);
502 } /* end of f77 */
503 
504 #if 0 /* not const-correct */
505 static char *Make_Croak[3] = {"***", "Error", "code"};
506 static char *Make_NotRemade[5] = {"not", "remade", "because", "of", "errors"};
507 #else
508 DECL_STRINGS_3(static, Make_Croak, "***", "Error", "code");
509 DECL_STRINGS_5(static, Make_NotRemade,
510 	       "not", "remade", "because", "of", "errors");
511 #endif
512 
513 static Errorclass
514 make(void)
515 {
516 	if (wordvcmp(cur_wordv+1, 3, Make_Croak) == 0) {
517 		language = INMAKE;
518 		return (C_SYNC);
519 	}
520 	if (wordvcmp(cur_wordv+2, 5, Make_NotRemade) == 0) {
521 		language = INMAKE;
522 		return (C_SYNC);
523 	}
524 	return (C_UNKNOWN);
525 }
526 
527 static Errorclass
528 ri(void)
529 {
530 /*
531  * Match an error message produced by ri; here is the
532  * procedure yanked from the distributed version of ri
533  * April 24, 1980.
534  *
535  *	serror(str, x1, x2, x3)
536  *		char str[];
537  *		char *x1, *x2, *x3;
538  *	{
539  *		extern int yylineno;
540  *
541  *		putc('"', stdout);
542  *		fputs(srcfile, stdout);
543  *		putc('"', stdout);
544  *		fprintf(stdout, " %d: ", yylineno);
545  *		fprintf(stdout, str, x1, x2, x3);
546  *		fprintf(stdout, "\n");
547  *		synerrs++;
548  *	}
549  */
550 	if (cur_wordc < 3)
551 		return (C_UNKNOWN);
552 	if (firstchar(cur_wordv[1]) == '"'
553 	    && lastchar(cur_wordv[1]) == '"'
554 	    && lastchar(cur_wordv[2]) == ':'
555 	    && isdigit((unsigned char)firstchar(cur_wordv[2]))) {
556 		clob_last(cur_wordv[1], '\0');	/* drop the last " */
557 		cur_wordv[1]++;			/* skip over the first " */
558 		clob_last(cur_wordv[2], '\0');
559 		language = INRI;
560 		return (C_TRUE);
561 	}
562 	return (C_UNKNOWN);
563 }
564 
565 static Errorclass
566 catchall(void)
567 {
568 	/*
569 	 * Catches random things.
570 	 */
571 	language = INUNKNOWN;
572 	return (C_NONSPEC);
573 } /* end of catch all*/
574 
575 static Errorclass
576 troff(void)
577 {
578 	/*
579 	 * troff source error message, from eqn, bib, tbl...
580 	 * Just like pcc ccom, except uses `'
581 	 */
582 	if (cur_wordc < 4)
583 		return (C_UNKNOWN);
584 
585 	if (firstchar(cur_wordv[1]) == '`'
586 	    && lastchar(cur_wordv[1]) == ','
587 	    && next_lastchar(cur_wordv[1]) == '\''
588 	    && strcmp(cur_wordv[2], "line") == 0
589 	    && isdigit((unsigned char)firstchar(cur_wordv[3]))
590 	    && lastchar(cur_wordv[3]) == ':') {
591 		clob_last(cur_wordv[1], '\0');	/* drop last , */
592 		clob_last(cur_wordv[1], '\0');	/* drop last " */
593 		cur_wordv[1]++;			/* drop first " */
594 		clob_last(cur_wordv[3], '\0');	/* drop : on line number */
595 		cur_wordv[2] = cur_wordv[1];	/* overwrite "line" */
596 		cur_wordv++;			/*compensate*/
597 		currentfilename = cur_wordv[1];
598 		language = INTROFF;
599 		return (C_TRUE);
600 	}
601 	return (C_UNKNOWN);
602 }
603 
604 static Errorclass
605 mod2(void)
606 {
607 	/*
608 	 * for decwrl modula2 compiler (powell)
609 	 */
610 	if (cur_wordc < 5)
611 		return (C_UNKNOWN);
612 	if ((strcmp(cur_wordv[1], "!!!") == 0		/* early version */
613 	     || strcmp(cur_wordv[1], "File") == 0)	/* later version */
614 	    && lastchar(cur_wordv[2]) == ','		/* file name */
615 	    && strcmp(cur_wordv[3], "line") == 0
616 	    && isdigit((unsigned char)firstchar(cur_wordv[4]))	/* line number */
617 	    && lastchar(cur_wordv[4]) == ':'	/* line number */
618 	) {
619 		clob_last(cur_wordv[2], '\0');	/* drop last , on file name */
620 		clob_last(cur_wordv[4], '\0');	/* drop last : on line number */
621 		cur_wordv[3] = cur_wordv[2];	/* file name on top of "line" */
622 		cur_wordv += 2;
623 		cur_wordc -= 2;
624 		currentfilename = cur_wordv[1];
625 		language = INMOD2;
626 		return (C_TRUE);
627 	}
628 	return (C_UNKNOWN);
629 }
630