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
ex_tag_first(SCR * sp,CHAR_T * tagarg)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
ex_tag_push(SCR * sp,EXCMD * cmdp)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
ex_tag_next(SCR * sp,EXCMD * cmdp)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
ex_tag_prev(SCR * sp,EXCMD * cmdp)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
ex_tag_nswitch(SCR * sp,TAG * tp,int force)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
ex_tag_Nswitch(SCR * sp,TAG * tp,int force)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
ex_tag_pop(SCR * sp,EXCMD * cmdp)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
ex_tag_top(SCR * sp,EXCMD * cmdp)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
tag_pop(SCR * sp,TAGQ * dtqp,int force)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
ex_tag_display(SCR * sp)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
ex_tag_copy(SCR * orig,SCR * sp)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
tagf_copy(SCR * sp,TAGF * otfp,TAGF ** tfpp)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
tagq_copy(SCR * sp,TAGQ * otqp,TAGQ ** tqpp)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
tag_copy(SCR * sp,TAG * otp,TAG ** tpp)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
tagf_free(SCR * sp,TAGF * tfp)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
tagq_free(SCR * sp,TAGQ * tqp)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
tagq_push(SCR * sp,TAGQ * tqp,int new_screen,int force)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
tag_msg(SCR * sp,tagmsg_t msg,char * tag)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
ex_tagf_alloc(SCR * sp,char * str)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
ex_tag_free(SCR * sp)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
ctag_search(SCR * sp,CHAR_T * search,size_t slen,char * tag)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 *
ctag_slist(SCR * sp,CHAR_T * tag)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
ctag_sfile(SCR * sp,TAGF * tfp,TAGQ * tqp,char * tname)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
ctag_file(SCR * sp,TAGF * tfp,char * name,char ** dirp,size_t * dlenp)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 *
binary_search(char * string,char * front,char * back)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 *
linear_search(char * string,char * front,char * back,long tl)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
compare(char * s1,char * s2,char * back)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