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