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