1 /* $NetBSD: ex_tag.c,v 1.12 2014/08/22 21:28:20 aymeric Exp $ */ 2 /*- 3 * Copyright (c) 1992, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1992, 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * David Hitz of Auspex Systems, Inc. 10 * 11 * See the LICENSE file for redistribution information. 12 */ 13 14 #include "config.h" 15 16 #include <sys/cdefs.h> 17 #if 0 18 #ifndef lint 19 static const char sccsid[] = "Id: ex_tag.c,v 10.50 2004/03/16 14:09:11 skimo Exp (Berkeley) Date: 2004/03/16 14:09:11 "; 20 #endif /* not lint */ 21 #else 22 __RCSID("$NetBSD: ex_tag.c,v 1.12 2014/08/22 21:28:20 aymeric Exp $"); 23 #endif 24 25 #include <sys/param.h> 26 #include <sys/types.h> /* XXX: param.h may not have included types.h */ 27 28 #ifdef HAVE_SYS_MMAN_H 29 #include <sys/mman.h> 30 #endif 31 32 #include <sys/queue.h> 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 36 #include <bitstring.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <stddef.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "../common/common.h" 48 #include "../vi/vi.h" 49 #include "tag.h" 50 51 static char *binary_search __P((char *, char *, char *)); 52 static int compare __P((char *, char *, char *)); 53 static void ctag_file __P((SCR *, TAGF *, char *, char **, size_t *)); 54 static int ctag_search __P((SCR *, CHAR_T *, size_t, char *)); 55 #ifdef GTAGS 56 static int getentry __P((char *, char **, char **, char **)); 57 static TAGQ *gtag_slist __P((SCR *, CHAR_T *, int)); 58 #endif 59 static int ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *)); 60 static TAGQ *ctag_slist __P((SCR *, CHAR_T *)); 61 static char *linear_search __P((char *, char *, char *, unsigned long)); 62 static int tag_copy __P((SCR *, TAG *, TAG **)); 63 static int tag_pop __P((SCR *, TAGQ *, int)); 64 static int tagf_copy __P((SCR *, TAGF *, TAGF **)); 65 static int tagf_free __P((SCR *, TAGF *)); 66 static int tagq_copy __P((SCR *, TAGQ *, TAGQ **)); 67 68 /* 69 * ex_tag_first -- 70 * The tag code can be entered from main, e.g., "vi -t tag". 71 * 72 * PUBLIC: int ex_tag_first __P((SCR *, const CHAR_T *)); 73 */ 74 int 75 ex_tag_first(SCR *sp, const CHAR_T *tagarg) 76 { 77 EXCMD cmd; 78 79 /* Build an argument for the ex :tag command. */ 80 ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, OOBLNO, 0); 81 argv_exp0(sp, &cmd, tagarg, STRLEN(tagarg)); 82 83 /* 84 * XXX 85 * Historic vi went ahead and created a temporary file when it failed 86 * to find the tag. We match historic practice, but don't distinguish 87 * between real error and failure to find the tag. 88 */ 89 if (ex_tag_push(sp, &cmd)) 90 return (0); 91 92 /* Display tags in the center of the screen. */ 93 F_CLR(sp, SC_SCR_TOP); 94 F_SET(sp, SC_SCR_CENTER); 95 96 return (0); 97 } 98 99 #ifdef GTAGS 100 /* 101 * ex_rtag_push -- ^] 102 * :rtag[!] [string] 103 * 104 * Enter a new TAGQ context based on a ctag string. 105 * 106 * PUBLIC: int ex_rtag_push __P((SCR *, EXCMD *)); 107 */ 108 int 109 ex_rtag_push(SCR *sp, EXCMD *cmdp) 110 { 111 F_SET(cmdp, E_REFERENCE); 112 return ex_tag_push(sp, cmdp); 113 } 114 #endif 115 116 /* 117 * ex_tag_push -- ^] 118 * :tag[!] [string] 119 * 120 * Enter a new TAGQ context based on a ctag string. 121 * 122 * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *)); 123 */ 124 int 125 ex_tag_push(SCR *sp, EXCMD *cmdp) 126 { 127 EX_PRIVATE *exp; 128 TAGQ *tqp; 129 unsigned long tl; 130 131 exp = EXP(sp); 132 switch (cmdp->argc) { 133 case 1: 134 if (exp->tag_last != NULL) 135 free(exp->tag_last); 136 137 if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp, 138 cmdp->argv[0]->len)) == NULL) { 139 msgq(sp, M_SYSERR, NULL); 140 return (1); 141 } 142 143 /* Taglength may limit the number of characters. */ 144 if ((tl = 145 O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl) 146 exp->tag_last[tl] = '\0'; 147 break; 148 case 0: 149 if (exp->tag_last == NULL) { 150 msgq(sp, M_ERR, "158|No previous tag entered"); 151 return (1); 152 } 153 break; 154 default: 155 abort(); 156 } 157 158 /* Get the tag information. */ 159 #ifdef GTAGS 160 if (O_ISSET(sp, O_GTAGSMODE)) { 161 if ((tqp = gtag_slist(sp, exp->tag_last, 162 F_ISSET(cmdp, E_REFERENCE))) == NULL) 163 return (1); 164 } else 165 #endif 166 if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL) 167 return (1); 168 169 if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN), 170 FL_ISSET(cmdp->iflags, E_C_FORCE))) 171 return 1; 172 173 return 0; 174 } 175 176 /* 177 * ex_tag_next -- 178 * Switch context to the next TAG. 179 * 180 * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *)); 181 */ 182 int 183 ex_tag_next(SCR *sp, EXCMD *cmdp) 184 { 185 EX_PRIVATE *exp; 186 TAG *tp; 187 TAGQ *tqp; 188 const char *np; 189 size_t nlen; 190 191 exp = EXP(sp); 192 if ((tqp = TAILQ_FIRST(&exp->tq)) == NULL) { 193 tag_msg(sp, TAG_EMPTY, NULL); 194 return (1); 195 } 196 if ((tp = TAILQ_NEXT(tqp->current, q)) == NULL) { 197 msgq(sp, M_ERR, "282|Already at the last tag of this group"); 198 return (1); 199 } 200 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) 201 return (1); 202 tqp->current = tp; 203 204 if (F_ISSET(tqp, TAG_CSCOPE)) 205 (void)cscope_search(sp, tqp, tp); 206 else 207 (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); 208 if (tqp->current->msg) { 209 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, 210 np, nlen); 211 msgq(sp, M_INFO, "%s", np); 212 } 213 return (0); 214 } 215 216 /* 217 * ex_tag_prev -- 218 * Switch context to the next TAG. 219 * 220 * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *)); 221 */ 222 int 223 ex_tag_prev(SCR *sp, EXCMD *cmdp) 224 { 225 EX_PRIVATE *exp; 226 TAG *tp; 227 TAGQ *tqp; 228 const char *np; 229 size_t nlen; 230 231 exp = EXP(sp); 232 if ((tqp = TAILQ_FIRST(&exp->tq)) == NULL) { 233 tag_msg(sp, TAG_EMPTY, NULL); 234 return (0); 235 } 236 if ((tp = TAILQ_PREV(tqp->current, _tagqh, q)) == NULL) { 237 msgq(sp, M_ERR, "255|Already at the first tag of this group"); 238 return (1); 239 } 240 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) 241 return (1); 242 tqp->current = tp; 243 244 if (F_ISSET(tqp, TAG_CSCOPE)) 245 (void)cscope_search(sp, tqp, tp); 246 else 247 (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); 248 if (tqp->current->msg) { 249 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, 250 np, nlen); 251 msgq(sp, M_INFO, "%s", np); 252 } 253 return (0); 254 } 255 256 /* 257 * ex_tag_nswitch -- 258 * Switch context to the specified TAG. 259 * 260 * PUBLIC: int ex_tag_nswitch __P((SCR *, TAG *, int)); 261 */ 262 int 263 ex_tag_nswitch(SCR *sp, TAG *tp, int force) 264 { 265 /* Get a file structure. */ 266 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL) 267 return (1); 268 269 /* If not changing files, return, we're done. */ 270 if (tp->frp == sp->frp) 271 return (0); 272 273 /* Check for permission to leave. */ 274 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE)) 275 return (1); 276 277 /* Initialize the new file. */ 278 if (file_init(sp, tp->frp, NULL, FS_SETALT)) 279 return (1); 280 281 /* Display tags in the center of the screen. */ 282 F_CLR(sp, SC_SCR_TOP); 283 F_SET(sp, SC_SCR_CENTER); 284 285 /* Switch. */ 286 F_SET(sp, SC_FSWITCH); 287 return (0); 288 } 289 290 /* 291 * ex_tag_Nswitch -- 292 * Switch context to the specified TAG in a new screen. 293 * 294 * PUBLIC: int ex_tag_Nswitch __P((SCR *, TAG *, int)); 295 */ 296 int 297 ex_tag_Nswitch(SCR *sp, TAG *tp, int force) 298 { 299 SCR *new; 300 301 /* Get a file structure. */ 302 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL) 303 return (1); 304 305 /* Get a new screen. */ 306 if (screen_init(sp->gp, sp, &new)) 307 return (1); 308 if (vs_split(sp, new, 0)) { 309 (void)file_end(new, new->ep, 1); 310 (void)screen_fini(new); 311 return (1); 312 } 313 314 /* Get a backing file. */ 315 if (tp->frp == sp->frp) { 316 /* Copy file state. */ 317 new->ep = sp->ep; 318 ++new->ep->refcnt; 319 TAILQ_INSERT_HEAD(&new->ep->scrq, new, eq); 320 321 new->frp = tp->frp; 322 new->frp->flags = sp->frp->flags; 323 } else if (file_init(new, tp->frp, NULL, force)) { 324 (void)vs_discard(new, NULL); 325 (void)screen_end(new); 326 return (1); 327 } 328 329 /* Create the argument list. */ 330 new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name); 331 332 /* Display tags in the center of the screen. */ 333 F_CLR(new, SC_SCR_TOP); 334 F_SET(new, SC_SCR_CENTER); 335 336 /* Switch. */ 337 sp->nextdisp = new; 338 F_SET(sp, SC_SSWITCH); 339 340 return (0); 341 } 342 343 /* 344 * ex_tag_pop -- ^T 345 * :tagp[op][!] [number | file] 346 * 347 * Pop to a previous TAGQ context. 348 * 349 * PUBLIC: int ex_tag_pop __P((SCR *, EXCMD *)); 350 */ 351 int 352 ex_tag_pop(SCR *sp, EXCMD *cmdp) 353 { 354 EX_PRIVATE *exp; 355 TAGQ *tqp, *dtqp = NULL; 356 size_t arglen; 357 long off; 358 const char *arg; 359 char *p, *t; 360 size_t nlen; 361 362 /* Check for an empty stack. */ 363 exp = EXP(sp); 364 if (TAILQ_EMPTY(&exp->tq)) { 365 tag_msg(sp, TAG_EMPTY, NULL); 366 return (1); 367 } 368 369 /* Find the last TAG structure that we're going to DISCARD! */ 370 switch (cmdp->argc) { 371 case 0: /* Pop one tag. */ 372 dtqp = TAILQ_FIRST(&exp->tq); 373 break; 374 case 1: /* Name or number. */ 375 INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len+1, 376 arg, nlen); 377 off = strtol(arg, &p, 10); 378 if (*p != '\0') 379 goto filearg; 380 381 /* Number: pop that many queue entries. */ 382 if (off < 1) 383 return (0); 384 TAILQ_FOREACH(tqp, &exp->tq, q) 385 if (--off <= 1) 386 break; 387 if (tqp == NULL) { 388 msgq(sp, M_ERR, 389 "159|Less than %s entries on the tags stack; use :display t[ags]", 390 arg); 391 return (1); 392 } 393 dtqp = tqp; 394 break; 395 396 /* File argument: pop to that queue entry. */ 397 filearg: arglen = strlen(arg); 398 for (tqp = TAILQ_FIRST(&exp->tq); 399 tqp != NULL; 400 dtqp = tqp, tqp = TAILQ_NEXT(tqp, q)) { 401 /* Don't pop to the current file. */ 402 if (tqp == TAILQ_FIRST(&exp->tq)) 403 continue; 404 p = tqp->current->frp->name; 405 if ((t = strrchr(p, '/')) == NULL) 406 t = p; 407 else 408 ++t; 409 if (!strncmp(arg, t, arglen)) 410 break; 411 } 412 if (tqp == NULL) { 413 msgq_str(sp, M_ERR, arg, 414 "160|No file %s on the tags stack to return to; use :display t[ags]"); 415 return (1); 416 } 417 break; 418 default: 419 abort(); 420 } 421 422 return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE))); 423 } 424 425 /* 426 * ex_tag_top -- :tagt[op][!] 427 * Clear the tag stack. 428 * 429 * PUBLIC: int ex_tag_top __P((SCR *, EXCMD *)); 430 */ 431 int 432 ex_tag_top(SCR *sp, EXCMD *cmdp) 433 { 434 EX_PRIVATE *exp; 435 TAGQ *tqp; 436 437 exp = EXP(sp); 438 439 /* Check for an empty stack. */ 440 if (TAILQ_EMPTY(&exp->tq)) { 441 tag_msg(sp, TAG_EMPTY, NULL); 442 return (1); 443 } 444 445 /* Return to the oldest information. */ 446 tqp = TAILQ_LAST(&exp->tq, _tqh); 447 tqp = TAILQ_PREV(tqp, _tqh, q); 448 return tag_pop(sp, tqp, FL_ISSET(cmdp->iflags, E_C_FORCE)); 449 } 450 451 /* 452 * tag_pop -- 453 * Pop up to and including the specified TAGQ context. 454 */ 455 static int 456 tag_pop(SCR *sp, TAGQ *dtqp, int force) 457 { 458 EX_PRIVATE *exp; 459 TAG *tp; 460 TAGQ *tqp; 461 462 exp = EXP(sp); 463 464 /* 465 * Update the cursor from the saved TAG information of the TAG 466 * structure we're moving to. 467 */ 468 tp = TAILQ_NEXT(dtqp, q)->current; 469 if (tp->frp == sp->frp) { 470 sp->lno = tp->lno; 471 sp->cno = tp->cno; 472 } else { 473 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE)) 474 return (1); 475 476 tp->frp->lno = tp->lno; 477 tp->frp->cno = tp->cno; 478 F_SET(sp->frp, FR_CURSORSET); 479 if (file_init(sp, tp->frp, NULL, FS_SETALT)) 480 return (1); 481 482 F_SET(sp, SC_FSWITCH); 483 } 484 485 /* Pop entries off the queue up to and including dtqp. */ 486 do { 487 tqp = TAILQ_FIRST(&exp->tq); 488 if (tagq_free(sp, tqp)) 489 return (0); 490 } while (tqp != dtqp); 491 492 /* 493 * If only a single tag left, we've returned to the first tag point, 494 * and the stack is now empty. 495 */ 496 if (TAILQ_NEXT(TAILQ_FIRST(&exp->tq), q) == NULL) 497 tagq_free(sp, TAILQ_FIRST(&exp->tq)); 498 499 return (0); 500 } 501 502 /* 503 * ex_tag_display -- 504 * Display the list of tags. 505 * 506 * PUBLIC: int ex_tag_display __P((SCR *)); 507 */ 508 int 509 ex_tag_display(SCR *sp) 510 { 511 EX_PRIVATE *exp; 512 TAG *tp; 513 TAGQ *tqp; 514 int cnt; 515 size_t len; 516 char *p; 517 518 exp = EXP(sp); 519 if (TAILQ_EMPTY(&exp->tq)) { 520 tag_msg(sp, TAG_EMPTY, NULL); 521 return (0); 522 } 523 524 /* 525 * We give the file name 20 columns and the search string the rest. 526 * If there's not enough room, we don't do anything special, it's 527 * not worth the effort, it just makes the display more confusing. 528 * 529 * We also assume that characters in file names map 1-1 to printing 530 * characters. This might not be true, but I don't think it's worth 531 * fixing. (The obvious fix is to pass the filenames through the 532 * msg_print function.) 533 */ 534 #define L_NAME 30 /* Name. */ 535 #define L_SLOP 4 /* Leading number plus trailing *. */ 536 #define L_SPACE 5 /* Spaces after name, before tag. */ 537 #define L_TAG 20 /* Tag. */ 538 if (sp->cols <= L_NAME + L_SLOP) { 539 msgq(sp, M_ERR, "292|Display too small."); 540 return (0); 541 } 542 543 /* 544 * Display the list of tags for each queue entry. The first entry 545 * is numbered, and the current tag entry has an asterisk appended. 546 */ 547 for (cnt = 1, tqp = TAILQ_FIRST(&exp->tq); !INTERRUPTED(sp) && 548 tqp != NULL; ++cnt, tqp = TAILQ_NEXT(tqp, q)) 549 TAILQ_FOREACH(tp, &tqp->tagq, q) { 550 if (tp == TAILQ_FIRST(&tqp->tagq)) 551 (void)ex_printf(sp, "%2d ", cnt); 552 else 553 (void)ex_printf(sp, " "); 554 p = tp->frp == NULL ? tp->fname : tp->frp->name; 555 if ((len = strlen(p)) > L_NAME) { 556 len = len - (L_NAME - 4); 557 (void)ex_printf(sp, " ... %*.*s", 558 L_NAME - 4, L_NAME - 4, p + len); 559 } else 560 (void)ex_printf(sp, 561 " %*.*s", L_NAME, L_NAME, p); 562 if (tqp->current == tp) 563 (void)ex_printf(sp, "*"); 564 565 if (tp == TAILQ_FIRST(&tqp->tagq) && tqp->tag != NULL && 566 (sp->cols - L_NAME) >= L_TAG + L_SPACE) { 567 len = strlen(tqp->tag); 568 if (len > sp->cols - (L_NAME + L_SPACE)) 569 len = sp->cols - (L_NAME + L_SPACE); 570 (void)ex_printf(sp, "%s%.*s", 571 tqp->current == tp ? " " : " ", 572 (int)len, tqp->tag); 573 } 574 (void)ex_printf(sp, "\n"); 575 } 576 return (0); 577 } 578 579 /* 580 * ex_tag_copy -- 581 * Copy a screen's tag structures. 582 * 583 * PUBLIC: int ex_tag_copy __P((SCR *, SCR *)); 584 */ 585 int 586 ex_tag_copy(SCR *orig, SCR *sp) 587 { 588 EX_PRIVATE *oexp, *nexp; 589 TAGQ *aqp, *tqp; 590 TAG *ap, *tp; 591 TAGF *atfp, *tfp; 592 593 oexp = EXP(orig); 594 nexp = EXP(sp); 595 596 /* Copy tag queue and tags stack. */ 597 TAILQ_FOREACH(aqp, &oexp->tq, q) { 598 if (tagq_copy(sp, aqp, &tqp)) 599 return (1); 600 TAILQ_FOREACH(ap, &aqp->tagq, q) { 601 if (tag_copy(sp, ap, &tp)) 602 return (1); 603 /* Set the current pointer. */ 604 if (aqp->current == ap) 605 tqp->current = tp; 606 TAILQ_INSERT_TAIL(&tqp->tagq, tp, q); 607 } 608 TAILQ_INSERT_TAIL(&nexp->tq, tqp, q); 609 F_SET(tqp, TAG_IS_LINKED); 610 } 611 612 /* Copy list of tag files. */ 613 614 TAILQ_FOREACH(atfp, &oexp->tagfq, q) { 615 if (tagf_copy(sp, atfp, &tfp)) 616 return (1); 617 TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q); 618 } 619 620 /* Copy the last tag. */ 621 if (oexp->tag_last != NULL && 622 (nexp->tag_last = v_wstrdup(sp, oexp->tag_last, 623 STRLEN(oexp->tag_last))) == NULL) { 624 msgq(sp, M_SYSERR, NULL); 625 return (1); 626 } 627 return (0); 628 } 629 630 /* 631 * tagf_copy -- 632 * Copy a TAGF structure and return it in new memory. 633 */ 634 static int 635 tagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp) 636 { 637 TAGF *tfp; 638 639 MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF)); 640 *tfp = *otfp; 641 642 /* XXX: Allocate as part of the TAGF structure!!! */ 643 if ((tfp->name = strdup(otfp->name)) == NULL) 644 return (1); 645 646 *tfpp = tfp; 647 return (0); 648 } 649 650 /* 651 * tagq_copy -- 652 * Copy a TAGQ structure and return it in new memory. 653 */ 654 static int 655 tagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp) 656 { 657 TAGQ *tqp; 658 size_t len; 659 660 len = sizeof(TAGQ); 661 if (otqp->tag != NULL) 662 len += otqp->tlen + 1; 663 MALLOC_RET(sp, tqp, TAGQ *, len); 664 memcpy(tqp, otqp, len); 665 666 TAILQ_INIT(&tqp->tagq); 667 tqp->current = NULL; 668 if (otqp->tag != NULL) 669 tqp->tag = tqp->buf; 670 671 *tqpp = tqp; 672 return (0); 673 } 674 675 /* 676 * tag_copy -- 677 * Copy a TAG structure and return it in new memory. 678 */ 679 static int 680 tag_copy(SCR *sp, TAG *otp, TAG **tpp) 681 { 682 TAG *tp; 683 size_t len; 684 685 len = sizeof(TAG); 686 if (otp->fname != NULL) 687 len += otp->fnlen + 1; 688 if (otp->search != NULL) 689 len += otp->slen + 1; 690 if (otp->msg != NULL) 691 len += otp->mlen + 1; 692 MALLOC_RET(sp, tp, TAG *, len); 693 memcpy(tp, otp, len); 694 695 if (otp->fname != NULL) 696 tp->fname = (char *)tp->buf; 697 if (otp->search != NULL) 698 tp->search = tp->buf + (otp->search - otp->buf); 699 if (otp->msg != NULL) 700 tp->msg = tp->buf + (otp->msg - otp->buf); 701 702 *tpp = tp; 703 return (0); 704 } 705 706 /* 707 * tagf_free -- 708 * Free a TAGF structure. 709 */ 710 static int 711 tagf_free(SCR *sp, TAGF *tfp) 712 { 713 EX_PRIVATE *exp; 714 715 exp = EXP(sp); 716 TAILQ_REMOVE(&exp->tagfq, tfp, q); 717 free(tfp->name); 718 free(tfp); 719 return (0); 720 } 721 722 /* 723 * tagq_free -- 724 * Free a TAGQ structure (and associated TAG structures). 725 * 726 * PUBLIC: int tagq_free __P((SCR *, TAGQ *)); 727 */ 728 int 729 tagq_free(SCR *sp, TAGQ *tqp) 730 { 731 EX_PRIVATE *exp; 732 TAG *tp; 733 734 exp = EXP(sp); 735 while ((tp = TAILQ_FIRST(&tqp->tagq)) != NULL) { 736 TAILQ_REMOVE(&tqp->tagq, tp, q); 737 free(tp); 738 } 739 /* 740 * !!! 741 * If allocated and then the user failed to switch files, the TAGQ 742 * structure was never attached to any list. 743 */ 744 if (F_ISSET(tqp, TAG_IS_LINKED)) 745 TAILQ_REMOVE(&exp->tq, tqp, q); 746 free(tqp); 747 return (0); 748 } 749 750 /* 751 * PUBLIC: int tagq_push __P((SCR*, TAGQ*, int, int )); 752 */ 753 int 754 tagq_push(SCR *sp, TAGQ *tqp, int new_screen, int force) 755 { 756 EX_PRIVATE *exp; 757 FREF *frp; 758 TAG *rtp; 759 TAGQ *rtqp; 760 db_recno_t lno; 761 size_t cno; 762 int istmp; 763 const char *np; 764 size_t nlen; 765 766 exp = EXP(sp); 767 768 /* 769 * Allocate all necessary memory before swapping screens. Initialize 770 * flags so we know what to free. 771 */ 772 rtp = NULL; 773 rtqp = NULL; 774 if (TAILQ_EMPTY(&exp->tq)) { 775 /* Initialize the `local context' tag queue structure. */ 776 CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ)); 777 TAILQ_INIT(&rtqp->tagq); 778 779 /* Initialize and link in its tag structure. */ 780 CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG)); 781 TAILQ_INSERT_HEAD(&rtqp->tagq, rtp, q); 782 rtqp->current = rtp; 783 } 784 785 /* 786 * Stick the current context information in a convenient place, we're 787 * about to lose it. Note, if we're called on editor startup, there 788 * will be no FREF structure. 789 */ 790 frp = sp->frp; 791 lno = sp->lno; 792 cno = sp->cno; 793 istmp = frp == NULL || (F_ISSET(frp, FR_TMPFILE) && !new_screen); 794 795 /* Try to switch to the tag. */ 796 if (new_screen) { 797 if (ex_tag_Nswitch(sp, TAILQ_FIRST(&tqp->tagq), force)) 798 goto err; 799 800 /* Everything else gets done in the new screen. */ 801 sp = sp->nextdisp; 802 exp = EXP(sp); 803 } else 804 if (ex_tag_nswitch(sp, TAILQ_FIRST(&tqp->tagq), force)) 805 goto err; 806 807 /* 808 * If this is the first tag, put a `current location' queue entry 809 * in place, so we can pop all the way back to the current mark. 810 * Note, it doesn't point to much of anything, it's a placeholder. 811 */ 812 if (TAILQ_EMPTY(&exp->tq)) { 813 TAILQ_INSERT_HEAD(&exp->tq, rtqp, q); 814 F_SET(rtqp, TAG_IS_LINKED); 815 } else { 816 free(rtqp); 817 rtqp = TAILQ_FIRST(&exp->tq); 818 } 819 820 /* Link the new TAGQ structure into place. */ 821 TAILQ_INSERT_HEAD(&exp->tq, tqp, q); 822 F_SET(tqp, TAG_IS_LINKED); 823 824 (void)ctag_search(sp, 825 tqp->current->search, tqp->current->slen, tqp->tag); 826 if (tqp->current->msg) { 827 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, 828 np, nlen); 829 msgq(sp, M_INFO, "%s", np); 830 } 831 832 /* 833 * Move the current context from the temporary save area into the 834 * right structure. 835 * 836 * If we were in a temporary file, we don't have a context to which 837 * we can return, so just make it be the same as what we're moving 838 * to. It will be a little odd that ^T doesn't change anything, but 839 * I don't think it's a big deal. 840 */ 841 if (istmp) { 842 rtqp->current->frp = sp->frp; 843 rtqp->current->lno = sp->lno; 844 rtqp->current->cno = sp->cno; 845 } else { 846 rtqp->current->frp = frp; 847 rtqp->current->lno = lno; 848 rtqp->current->cno = cno; 849 } 850 return (0); 851 852 err: 853 alloc_err: 854 if (rtqp != NULL) 855 free(rtqp); 856 if (rtp != NULL) 857 free(rtp); 858 tagq_free(sp, tqp); 859 return (1); 860 } 861 862 /* 863 * tag_msg 864 * A few common messages. 865 * 866 * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *)); 867 */ 868 void 869 tag_msg(SCR *sp, tagmsg_t msg, char *tag) 870 { 871 switch (msg) { 872 case TAG_BADLNO: 873 msgq_str(sp, M_ERR, tag, 874 "164|%s: the tag's line number is past the end of the file"); 875 break; 876 case TAG_EMPTY: 877 msgq(sp, M_INFO, "165|The tags stack is empty"); 878 break; 879 case TAG_SEARCH: 880 msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found"); 881 break; 882 default: 883 abort(); 884 } 885 } 886 887 /* 888 * ex_tagf_alloc -- 889 * Create a new list of ctag files. 890 * 891 * PUBLIC: int ex_tagf_alloc __P((SCR *, const char *)); 892 */ 893 int 894 ex_tagf_alloc(SCR *sp, const char *str) 895 { 896 EX_PRIVATE *exp; 897 TAGF *tfp; 898 size_t len; 899 const char *p, *t; 900 901 /* Free current queue. */ 902 exp = EXP(sp); 903 while ((tfp = TAILQ_FIRST(&exp->tagfq)) != NULL) 904 tagf_free(sp, tfp); 905 906 /* Create new queue. */ 907 for (p = t = str;; ++p) { 908 if (*p == '\0' || isblank((unsigned char)*p)) { 909 if ((len = p - t) > 1) { 910 MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF)); 911 MALLOC(sp, tfp->name, char *, len + 1); 912 if (tfp->name == NULL) { 913 free(tfp); 914 return (1); 915 } 916 memcpy(tfp->name, t, len); 917 tfp->name[len] = '\0'; 918 tfp->flags = 0; 919 TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q); 920 } 921 t = p + 1; 922 } 923 if (*p == '\0') 924 break; 925 } 926 return (0); 927 } 928 /* Free previous queue. */ 929 /* 930 * ex_tag_free -- 931 * Free the ex tag information. 932 * 933 * PUBLIC: int ex_tag_free __P((SCR *)); 934 */ 935 int 936 ex_tag_free(SCR *sp) 937 { 938 EX_PRIVATE *exp; 939 TAGF *tfp; 940 TAGQ *tqp; 941 942 /* Free up tag information. */ 943 exp = EXP(sp); 944 while ((tqp = TAILQ_FIRST(&exp->tq)) != NULL) 945 tagq_free(sp, tqp); 946 while ((tfp = TAILQ_FIRST(&exp->tagfq)) != NULL) 947 tagf_free(sp, tfp); 948 if (exp->tag_last != NULL) 949 free(exp->tag_last); 950 return (0); 951 } 952 953 /* 954 * ctag_search -- 955 * Search a file for a tag. 956 */ 957 static int 958 ctag_search(SCR *sp, CHAR_T *search, size_t slen, char *tag) 959 { 960 MARK m; 961 char *p; 962 const char *np; 963 size_t nlen; 964 965 /* 966 * !!! 967 * The historic tags file format (from a long, long time ago...) 968 * used a line number, not a search string. I got complaints, so 969 * people are still using the format. POSIX 1003.2 permits it. 970 */ 971 if (ISDIGIT((UCHAR_T)search[0])) { 972 INT2CHAR(sp, search, slen+1, np, nlen); 973 m.lno = atoi(np); 974 if (!db_exist(sp, m.lno)) { 975 tag_msg(sp, TAG_BADLNO, tag); 976 return (1); 977 } 978 } else { 979 /* 980 * Search for the tag; cheap fallback for C functions 981 * if the name is the same but the arguments have changed. 982 */ 983 m.lno = 1; 984 m.cno = 0; 985 if (f_search(sp, &m, &m, 986 search, slen, NULL, 987 SEARCH_FIRST | SEARCH_TAG | SEARCH_PARSE)) { 988 INT2CHAR(sp, search, slen, np, nlen); 989 if ((p = strrchr(np, '(')) != NULL) { 990 slen = p - np; 991 if (f_search(sp, &m, &m, search, slen, 992 NULL, SEARCH_FIRST | SEARCH_TAG)) 993 goto notfound; 994 } else { 995 notfound: tag_msg(sp, TAG_SEARCH, tag); 996 return (1); 997 } 998 } 999 /* 1000 * !!! 1001 * Historically, tags set the search direction if it wasn't 1002 * already set. 1003 */ 1004 if (sp->searchdir == NOTSET) 1005 sp->searchdir = FORWARD; 1006 } 1007 1008 /* 1009 * !!! 1010 * Tags move to the first non-blank, NOT the search pattern start. 1011 */ 1012 sp->lno = m.lno; 1013 sp->cno = 0; 1014 (void)nonblank(sp, sp->lno, &sp->cno); 1015 return (0); 1016 } 1017 1018 #ifdef GTAGS 1019 /* 1020 * getentry -- 1021 * get tag information from current line. 1022 * 1023 * gtags temporary file format. 1024 * <tag> <lineno> <file> <image> 1025 * 1026 * sample. 1027 * +------------------------------------------------ 1028 * |main 30 main.c main(argc, argv) 1029 * |func 21 subr.c func(arg) 1030 */ 1031 static int 1032 getentry(char *buf, char **tag, char **file, char **line) 1033 { 1034 char *p = buf; 1035 1036 for (*tag = p; *p && !isspace((unsigned char)*p); p++) /* tag name */ 1037 ; 1038 if (*p == 0) 1039 goto err; 1040 *p++ = 0; 1041 for (; *p && isspace((unsigned char)*p); p++) /* (skip blanks) */ 1042 ; 1043 if (*p == 0) 1044 goto err; 1045 *line = p; /* line no */ 1046 for (*line = p; *p && !isspace((unsigned char)*p); p++) 1047 ; 1048 if (*p == 0) 1049 goto err; 1050 *p++ = 0; 1051 for (; *p && isspace((unsigned char)*p); p++) /* (skip blanks) */ 1052 ; 1053 if (*p == 0) 1054 goto err; 1055 *file = p; /* file name */ 1056 for (*file = p; *p && !isspace((unsigned char)*p); p++) 1057 ; 1058 if (*p == 0) 1059 goto err; 1060 *p = 0; 1061 1062 /* value check */ 1063 if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0) 1064 return 1; /* OK */ 1065 err: 1066 return 0; /* ERROR */ 1067 } 1068 1069 /* 1070 * gtag_slist -- 1071 * Search the list of tags files for a tag, and return tag queue. 1072 */ 1073 static TAGQ * 1074 gtag_slist(SCR *sp, CHAR_T *tag, int ref) 1075 { 1076 TAGQ *tqp; 1077 size_t len, nlen, slen, wlen; 1078 TAG *tp; 1079 const char *np; 1080 char *name, *file, *search; 1081 char command[BUFSIZ]; 1082 char buf[BUFSIZ]; 1083 const CHAR_T *wp; 1084 FILE *fp; 1085 1086 /* Allocate and initialize the tag queue structure. */ 1087 INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen); 1088 len = nlen - 1; 1089 CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1); 1090 TAILQ_INIT(&tqp->tagq); 1091 tqp->tag = tqp->buf; 1092 memcpy(tqp->tag, np, (tqp->tlen = len) + 1); 1093 1094 /* 1095 * Find the tag, only display missing file messages once, and 1096 * then only if we didn't find the tag. 1097 */ 1098 snprintf(command, sizeof(command), "global -%s '%s'", ref ? "rx" : "x", 1099 np); 1100 if ((fp = popen(command, "r")) != NULL) { 1101 while (fgets(buf, sizeof(buf), fp)) { 1102 if (buf[strlen(buf)-1] == '\n') /* chop(buf) */ 1103 buf[strlen(buf)-1] = 0; 1104 else 1105 while (fgetc(fp) != '\n') 1106 ; 1107 if (getentry(buf, &name, &file, &search) == 0) { 1108 break; 1109 } 1110 slen = strlen(search); 1111 CALLOC_GOTO(sp, tp, 1112 TAG *, 1, sizeof(TAG) + strlen(file) + 1 + 1113 (slen + 1) * sizeof(CHAR_T)); 1114 tp->fname = (char *)tp->buf; 1115 strcpy(tp->fname, file); 1116 tp->fnlen = strlen(file); 1117 tp->search = (CHAR_T *)(tp->fname + tp->fnlen + 1); 1118 CHAR2INT(sp, search, slen + 1, wp, wlen); 1119 MEMCPYW(tp->search, wp, (tp->slen = slen) + 1); 1120 TAILQ_INSERT_TAIL(&tqp->tagq, tp, q); 1121 } 1122 pclose(fp); 1123 } 1124 1125 /* Check to see if we found anything. */ 1126 if (TAILQ_EMPTY(&tqp->tagq)) { 1127 msgq_str(sp, M_ERR, np, "162|%s: tag not found"); 1128 free(tqp); 1129 return (NULL); 1130 } 1131 1132 tqp->current = TAILQ_FIRST(&tqp->tagq); 1133 return (tqp); 1134 1135 alloc_err: 1136 free(tqp); 1137 return (NULL); 1138 } 1139 #endif 1140 1141 /* 1142 * ctag_slist -- 1143 * Search the list of tags files for a tag, and return tag queue. 1144 */ 1145 static TAGQ * 1146 ctag_slist(SCR *sp, CHAR_T *tag) 1147 { 1148 EX_PRIVATE *exp; 1149 TAGF *tfp; 1150 TAGQ *tqp; 1151 size_t len; 1152 int echk; 1153 const char *np; 1154 size_t nlen; 1155 1156 exp = EXP(sp); 1157 1158 /* Allocate and initialize the tag queue structure. */ 1159 INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen); 1160 len = nlen - 1; 1161 CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1); 1162 TAILQ_INIT(&tqp->tagq); 1163 tqp->tag = tqp->buf; 1164 memcpy(tqp->tag, np, (tqp->tlen = len) + 1); 1165 1166 /* 1167 * Find the tag, only display missing file messages once, and 1168 * then only if we didn't find the tag. 1169 */ 1170 echk = 0; 1171 TAILQ_FOREACH(tfp, &exp->tagfq, q) 1172 if (ctag_sfile(sp, tfp, tqp, tqp->tag)) { 1173 echk = 1; 1174 F_SET(tfp, TAGF_ERR); 1175 } else 1176 F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN); 1177 1178 /* Check to see if we found anything. */ 1179 if (TAILQ_EMPTY(&tqp->tagq)) { 1180 msgq_str(sp, M_ERR, tqp->tag, "162|%s: tag not found"); 1181 if (echk) 1182 TAILQ_FOREACH(tfp, &exp->tagfq, q) 1183 if (F_ISSET(tfp, TAGF_ERR) && 1184 !F_ISSET(tfp, TAGF_ERR_WARN)) { 1185 errno = tfp->errnum; 1186 msgq_str(sp, M_SYSERR, tfp->name, "%s"); 1187 F_SET(tfp, TAGF_ERR_WARN); 1188 } 1189 free(tqp); 1190 return (NULL); 1191 } 1192 1193 tqp->current = TAILQ_FIRST(&tqp->tagq); 1194 return (tqp); 1195 1196 alloc_err: 1197 return (NULL); 1198 } 1199 1200 /* 1201 * ctag_sfile -- 1202 * Search a tags file for a tag, adding any found to the tag queue. 1203 */ 1204 static int 1205 ctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname) 1206 { 1207 struct stat sb; 1208 TAG *tp; 1209 size_t dlen, nlen = 0, slen; 1210 int fd, i, nf1, nf2; 1211 char *back, *front, *map, *p, *search, *t; 1212 char *cname = NULL, *dname = NULL, *name = NULL; 1213 const CHAR_T *wp; 1214 size_t wlen; 1215 unsigned long tl; 1216 1217 if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) { 1218 tfp->errnum = errno; 1219 return (1); 1220 } 1221 1222 /* 1223 * XXX 1224 * Some old BSD systems require MAP_FILE as an argument when mapping 1225 * regular files. 1226 */ 1227 #ifndef MAP_FILE 1228 #define MAP_FILE 0 1229 #endif 1230 /* 1231 * XXX 1232 * We'd like to test if the file is too big to mmap. Since we don't 1233 * know what size or type off_t's or size_t's are, what the largest 1234 * unsigned integral type is, or what random insanity the local C 1235 * compiler will perpetrate, doing the comparison in a portable way 1236 * is flatly impossible. Hope mmap fails if the file is too large. 1237 */ 1238 if (fstat(fd, &sb) != 0 || 1239 (map = mmap(NULL, (size_t)sb.st_size, PROT_READ | PROT_WRITE, 1240 MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) { 1241 tfp->errnum = errno; 1242 (void)close(fd); 1243 return (1); 1244 } 1245 1246 tl = O_VAL(sp, O_TAGLENGTH); 1247 front = map; 1248 back = front + sb.st_size; 1249 front = binary_search(tname, front, back); 1250 front = linear_search(tname, front, back, tl); 1251 if (front == NULL) 1252 goto done; 1253 1254 /* 1255 * Initialize and link in the tag structure(s). The historic ctags 1256 * file format only permitted a single tag location per tag. The 1257 * obvious extension to permit multiple tags locations per tag is to 1258 * output multiple records in the standard format. Unfortunately, 1259 * this won't work correctly with historic ex/vi implementations, 1260 * because their binary search assumes that there's only one record 1261 * per tag, and so will use a random tag entry if there si more than 1262 * one. This code handles either format. 1263 * 1264 * The tags file is in the following format: 1265 * 1266 * <tag> <filename> <line number> | <pattern> 1267 * 1268 * Figure out how long everything is so we can allocate in one swell 1269 * foop, but discard anything that looks wrong. 1270 */ 1271 for (;;) { 1272 /* Nul-terminate the end of the line. */ 1273 for (p = front; p < back && *p != '\n'; ++p); 1274 if (p == back || *p != '\n') 1275 break; 1276 *p = '\0'; 1277 1278 /* Update the pointers for the next time. */ 1279 t = p + 1; 1280 p = front; 1281 front = t; 1282 1283 /* Break the line into tokens. */ 1284 for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i) 1285 switch (i) { 1286 case 0: /* Tag. */ 1287 cname = t; 1288 break; 1289 case 1: /* Filename. */ 1290 name = t; 1291 nlen = strlen(name); 1292 break; 1293 } 1294 1295 /* Check for corruption. */ 1296 if (i != 2 || p == NULL || t == NULL) 1297 goto corrupt; 1298 1299 /* The rest of the string is the search pattern. */ 1300 search = p; 1301 if ((slen = strlen(p)) == 0) { 1302 corrupt: p = msg_print(sp, tname, &nf1); 1303 t = msg_print(sp, tfp->name, &nf2); 1304 msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t); 1305 if (nf1) 1306 FREE_SPACE(sp, p, 0); 1307 if (nf2) 1308 FREE_SPACE(sp, t, 0); 1309 continue; 1310 } 1311 1312 /* Check for passing the last entry. */ 1313 if (tl ? strncmp(tname, cname, tl) : strcmp(tname, cname)) 1314 break; 1315 1316 /* Resolve the file name. */ 1317 ctag_file(sp, tfp, name, &dname, &dlen); 1318 1319 CALLOC_GOTO(sp, tp, 1320 TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + 1321 (slen + 1) * sizeof(CHAR_T)); 1322 tp->fname = (char *)tp->buf; 1323 if (dlen != 0) { 1324 memcpy(tp->fname, dname, dlen); 1325 tp->fname[dlen] = '/'; 1326 ++dlen; 1327 } 1328 memcpy(tp->fname + dlen, name, nlen + 1); 1329 tp->fnlen = dlen + nlen; 1330 tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1); 1331 CHAR2INT(sp, search, slen + 1, wp, wlen); 1332 MEMCPYW(tp->search, wp, (tp->slen = slen) + 1); 1333 TAILQ_INSERT_TAIL(&tqp->tagq, tp, q); 1334 } 1335 1336 alloc_err: 1337 done: if (munmap(map, (size_t)sb.st_size)) 1338 msgq(sp, M_SYSERR, "munmap"); 1339 if (close(fd)) 1340 msgq(sp, M_SYSERR, "close"); 1341 return (0); 1342 } 1343 1344 /* 1345 * ctag_file -- 1346 * Search for the right path to this file. 1347 */ 1348 static void 1349 ctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp) 1350 { 1351 struct stat sb; 1352 char *p, buf[MAXPATHLEN]; 1353 1354 /* 1355 * !!! 1356 * If the tag file path is a relative path, see if it exists. If it 1357 * doesn't, look relative to the tags file path. It's okay for a tag 1358 * file to not exist, and historically, vi simply displayed a "new" 1359 * file. However, if the path exists relative to the tag file, it's 1360 * pretty clear what's happening, so we may as well get it right. 1361 */ 1362 *dlenp = 0; 1363 if (name[0] != '/' && 1364 stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) { 1365 *p = '\0'; 1366 (void)snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name); 1367 if (stat(buf, &sb) == 0) { 1368 *dirp = tfp->name; 1369 *dlenp = strlen(*dirp); 1370 } 1371 *p = '/'; 1372 } 1373 } 1374 1375 /* 1376 * Binary search for "string" in memory between "front" and "back". 1377 * 1378 * This routine is expected to return a pointer to the start of a line at 1379 * *or before* the first word matching "string". Relaxing the constraint 1380 * this way simplifies the algorithm. 1381 * 1382 * Invariants: 1383 * front points to the beginning of a line at or before the first 1384 * matching string. 1385 * 1386 * back points to the beginning of a line at or after the first 1387 * matching line. 1388 * 1389 * Base of the Invariants. 1390 * front = NULL; 1391 * back = EOF; 1392 * 1393 * Advancing the Invariants: 1394 * 1395 * p = first newline after halfway point from front to back. 1396 * 1397 * If the string at "p" is not greater than the string to match, 1398 * p is the new front. Otherwise it is the new back. 1399 * 1400 * Termination: 1401 * 1402 * The definition of the routine allows it return at any point, 1403 * since front is always at or before the line to print. 1404 * 1405 * In fact, it returns when the chosen "p" equals "back". This 1406 * implies that there exists a string is least half as long as 1407 * (back - front), which in turn implies that a linear search will 1408 * be no more expensive than the cost of simply printing a string or two. 1409 * 1410 * Trying to continue with binary search at this point would be 1411 * more trouble than it's worth. 1412 */ 1413 #define EQUAL 0 1414 #define GREATER 1 1415 #define LESS (-1) 1416 1417 #define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n') continue; 1418 1419 static char * 1420 binary_search(register char *string, register char *front, register char *back) 1421 { 1422 register char *p; 1423 1424 p = front + (back - front) / 2; 1425 SKIP_PAST_NEWLINE(p, back); 1426 1427 while (p != back) { 1428 if (compare(string, p, back) == GREATER) 1429 front = p; 1430 else 1431 back = p; 1432 p = front + (back - front) / 2; 1433 SKIP_PAST_NEWLINE(p, back); 1434 } 1435 return (front); 1436 } 1437 1438 /* 1439 * Find the first line that starts with string, linearly searching from front 1440 * to back. 1441 * 1442 * Return NULL for no such line. 1443 * 1444 * This routine assumes: 1445 * 1446 * o front points at the first character in a line. 1447 * o front is before or at the first line to be printed. 1448 */ 1449 static char * 1450 linear_search(char *string, char *front, char *back, unsigned long tl) 1451 { 1452 char *end; 1453 while (front < back) { 1454 end = tl && (unsigned long)(back-front) > tl ? front+tl : back; 1455 switch (compare(string, front, end)) { 1456 case EQUAL: /* Found it. */ 1457 return (front); 1458 case LESS: /* No such string. */ 1459 return (NULL); 1460 case GREATER: /* Keep going. */ 1461 break; 1462 } 1463 SKIP_PAST_NEWLINE(front, back); 1464 } 1465 return (NULL); 1466 } 1467 1468 /* 1469 * Return LESS, GREATER, or EQUAL depending on how the string1 compares 1470 * with string2 (s1 ??? s2). 1471 * 1472 * o Matches up to len(s1) are EQUAL. 1473 * o Matches up to len(s2) are GREATER. 1474 * 1475 * The string "s1" is null terminated. The string s2 is '\t', space, (or 1476 * "back") terminated. 1477 * 1478 * !!! 1479 * Reasonably modern ctags programs use tabs as separators, not spaces. 1480 * However, historic programs did use spaces, and, I got complaints. 1481 */ 1482 static int 1483 compare(register char *s1, register char *s2, register char *back) 1484 { 1485 for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2) 1486 if (*s1 != *s2) 1487 return (*s1 < *s2 ? LESS : GREATER); 1488 return (*s1 ? GREATER : s2 < back && 1489 (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL); 1490 } 1491