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