Bug Summary

File:src/usr.bin/vi/build/../ex/ex_tag.c
Warning:line 860, column 3
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

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