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