Bug Summary

File:src/usr.bin/mg/tags.c
Warning:line 394, column 2
Potential leak of memory pointed to by 't'

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 tags.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/mg/obj -resource-dir /usr/local/lib/clang/13.0.0 -D REGEX -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/mg/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/mg/tags.c
1/* $OpenBSD: tags.c,v 1.16 2017/08/06 04:39:45 bcallah Exp $ */
2
3/*
4 * This file is in the public domain.
5 *
6 * Author: Sunil Nimmagadda <sunil@openbsd.org>
7 */
8
9#include <sys/queue.h>
10#include <sys/stat.h>
11#include <sys/tree.h>
12#include <sys/types.h>
13#include <ctype.h>
14#include <err.h>
15#include <errno(*__errno()).h>
16#include <signal.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <util.h>
22
23#include "def.h"
24
25struct ctag;
26
27static int addctag(char *);
28static int atbow(void);
29void closetags(void);
30static int ctagcmp(struct ctag *, struct ctag *);
31static int loadbuffer(char *);
32static int loadtags(const char *);
33static int pushtag(char *);
34static int searchpat(char *);
35static struct ctag *searchtag(char *);
36static char *strip(char *, size_t);
37static void unloadtags(void);
38
39#define DEFAULTFN"tags" "tags"
40
41char *tagsfn = NULL((void *)0);
42int loaded = FALSE0;
43
44/* ctags(1) entries are parsed and maintained in a tree. */
45struct ctag {
46 RB_ENTRY(ctag)struct { struct ctag *rbe_left; struct ctag *rbe_right; struct
ctag *rbe_parent; int rbe_color; }
entry;
47 char *tag;
48 char *fname;
49 char *pat;
50};
51RB_HEAD(tagtree, ctag)struct tagtree { struct ctag *rbh_root; } tags = RB_INITIALIZER(&tags){ ((void *)0) };
52RB_GENERATE(tagtree, ctag, entry, ctagcmp)void tagtree_RB_INSERT_COLOR(struct tagtree *head, struct ctag
*elm) { struct ctag *parent, *gparent, *tmp; while ((parent =
(elm)->entry.rbe_parent) && (parent)->entry.rbe_color
== 1) { gparent = (parent)->entry.rbe_parent; if (parent ==
(gparent)->entry.rbe_left) { tmp = (gparent)->entry.rbe_right
; if (tmp && (tmp)->entry.rbe_color == 1) { (tmp)->
entry.rbe_color = 0; do { (parent)->entry.rbe_color = 0; (
gparent)->entry.rbe_color = 1; } while (0); elm = gparent;
continue; } if ((parent)->entry.rbe_right == elm) { do { (
tmp) = (parent)->entry.rbe_right; if (((parent)->entry.
rbe_right = (tmp)->entry.rbe_left)) { ((tmp)->entry.rbe_left
)->entry.rbe_parent = (parent); } do {} while (0); if (((tmp
)->entry.rbe_parent = (parent)->entry.rbe_parent)) { if
((parent) == ((parent)->entry.rbe_parent)->entry.rbe_left
) ((parent)->entry.rbe_parent)->entry.rbe_left = (tmp);
else ((parent)->entry.rbe_parent)->entry.rbe_right = (
tmp); } else (head)->rbh_root = (tmp); (tmp)->entry.rbe_left
= (parent); (parent)->entry.rbe_parent = (tmp); do {} while
(0); if (((tmp)->entry.rbe_parent)) do {} while (0); } while
(0); tmp = parent; parent = elm; elm = tmp; } do { (parent)->
entry.rbe_color = 0; (gparent)->entry.rbe_color = 1; } while
(0); do { (tmp) = (gparent)->entry.rbe_left; if (((gparent
)->entry.rbe_left = (tmp)->entry.rbe_right)) { ((tmp)->
entry.rbe_right)->entry.rbe_parent = (gparent); } do {} while
(0); if (((tmp)->entry.rbe_parent = (gparent)->entry.rbe_parent
)) { if ((gparent) == ((gparent)->entry.rbe_parent)->entry
.rbe_left) ((gparent)->entry.rbe_parent)->entry.rbe_left
= (tmp); else ((gparent)->entry.rbe_parent)->entry.rbe_right
= (tmp); } else (head)->rbh_root = (tmp); (tmp)->entry
.rbe_right = (gparent); (gparent)->entry.rbe_parent = (tmp
); do {} while (0); if (((tmp)->entry.rbe_parent)) do {} while
(0); } while (0); } else { tmp = (gparent)->entry.rbe_left
; if (tmp && (tmp)->entry.rbe_color == 1) { (tmp)->
entry.rbe_color = 0; do { (parent)->entry.rbe_color = 0; (
gparent)->entry.rbe_color = 1; } while (0); elm = gparent;
continue; } if ((parent)->entry.rbe_left == elm) { do { (
tmp) = (parent)->entry.rbe_left; if (((parent)->entry.rbe_left
= (tmp)->entry.rbe_right)) { ((tmp)->entry.rbe_right)->
entry.rbe_parent = (parent); } do {} while (0); if (((tmp)->
entry.rbe_parent = (parent)->entry.rbe_parent)) { if ((parent
) == ((parent)->entry.rbe_parent)->entry.rbe_left) ((parent
)->entry.rbe_parent)->entry.rbe_left = (tmp); else ((parent
)->entry.rbe_parent)->entry.rbe_right = (tmp); } else (
head)->rbh_root = (tmp); (tmp)->entry.rbe_right = (parent
); (parent)->entry.rbe_parent = (tmp); do {} while (0); if
(((tmp)->entry.rbe_parent)) do {} while (0); } while (0);
tmp = parent; parent = elm; elm = tmp; } do { (parent)->entry
.rbe_color = 0; (gparent)->entry.rbe_color = 1; } while (0
); do { (tmp) = (gparent)->entry.rbe_right; if (((gparent)
->entry.rbe_right = (tmp)->entry.rbe_left)) { ((tmp)->
entry.rbe_left)->entry.rbe_parent = (gparent); } do {} while
(0); if (((tmp)->entry.rbe_parent = (gparent)->entry.rbe_parent
)) { if ((gparent) == ((gparent)->entry.rbe_parent)->entry
.rbe_left) ((gparent)->entry.rbe_parent)->entry.rbe_left
= (tmp); else ((gparent)->entry.rbe_parent)->entry.rbe_right
= (tmp); } else (head)->rbh_root = (tmp); (tmp)->entry
.rbe_left = (gparent); (gparent)->entry.rbe_parent = (tmp)
; do {} while (0); if (((tmp)->entry.rbe_parent)) do {} while
(0); } while (0); } } (head->rbh_root)->entry.rbe_color
= 0; } void tagtree_RB_REMOVE_COLOR(struct tagtree *head, struct
ctag *parent, struct ctag *elm) { struct ctag *tmp; while ((
elm == ((void *)0) || (elm)->entry.rbe_color == 0) &&
elm != (head)->rbh_root) { if ((parent)->entry.rbe_left
== elm) { tmp = (parent)->entry.rbe_right; if ((tmp)->
entry.rbe_color == 1) { do { (tmp)->entry.rbe_color = 0; (
parent)->entry.rbe_color = 1; } while (0); do { (tmp) = (parent
)->entry.rbe_right; if (((parent)->entry.rbe_right = (tmp
)->entry.rbe_left)) { ((tmp)->entry.rbe_left)->entry
.rbe_parent = (parent); } do {} while (0); if (((tmp)->entry
.rbe_parent = (parent)->entry.rbe_parent)) { if ((parent) ==
((parent)->entry.rbe_parent)->entry.rbe_left) ((parent
)->entry.rbe_parent)->entry.rbe_left = (tmp); else ((parent
)->entry.rbe_parent)->entry.rbe_right = (tmp); } else (
head)->rbh_root = (tmp); (tmp)->entry.rbe_left = (parent
); (parent)->entry.rbe_parent = (tmp); do {} while (0); if
(((tmp)->entry.rbe_parent)) do {} while (0); } while (0);
tmp = (parent)->entry.rbe_right; } if (((tmp)->entry.rbe_left
== ((void *)0) || ((tmp)->entry.rbe_left)->entry.rbe_color
== 0) && ((tmp)->entry.rbe_right == ((void *)0) ||
((tmp)->entry.rbe_right)->entry.rbe_color == 0)) { (tmp
)->entry.rbe_color = 1; elm = parent; parent = (elm)->entry
.rbe_parent; } else { if ((tmp)->entry.rbe_right == ((void
*)0) || ((tmp)->entry.rbe_right)->entry.rbe_color == 0
) { struct ctag *oleft; if ((oleft = (tmp)->entry.rbe_left
)) (oleft)->entry.rbe_color = 0; (tmp)->entry.rbe_color
= 1; do { (oleft) = (tmp)->entry.rbe_left; if (((tmp)->
entry.rbe_left = (oleft)->entry.rbe_right)) { ((oleft)->
entry.rbe_right)->entry.rbe_parent = (tmp); } do {} while (
0); if (((oleft)->entry.rbe_parent = (tmp)->entry.rbe_parent
)) { if ((tmp) == ((tmp)->entry.rbe_parent)->entry.rbe_left
) ((tmp)->entry.rbe_parent)->entry.rbe_left = (oleft); else
((tmp)->entry.rbe_parent)->entry.rbe_right = (oleft); }
else (head)->rbh_root = (oleft); (oleft)->entry.rbe_right
= (tmp); (tmp)->entry.rbe_parent = (oleft); do {} while (
0); if (((oleft)->entry.rbe_parent)) do {} while (0); } while
(0); tmp = (parent)->entry.rbe_right; } (tmp)->entry.rbe_color
= (parent)->entry.rbe_color; (parent)->entry.rbe_color
= 0; if ((tmp)->entry.rbe_right) ((tmp)->entry.rbe_right
)->entry.rbe_color = 0; do { (tmp) = (parent)->entry.rbe_right
; if (((parent)->entry.rbe_right = (tmp)->entry.rbe_left
)) { ((tmp)->entry.rbe_left)->entry.rbe_parent = (parent
); } do {} while (0); if (((tmp)->entry.rbe_parent = (parent
)->entry.rbe_parent)) { if ((parent) == ((parent)->entry
.rbe_parent)->entry.rbe_left) ((parent)->entry.rbe_parent
)->entry.rbe_left = (tmp); else ((parent)->entry.rbe_parent
)->entry.rbe_right = (tmp); } else (head)->rbh_root = (
tmp); (tmp)->entry.rbe_left = (parent); (parent)->entry
.rbe_parent = (tmp); do {} while (0); if (((tmp)->entry.rbe_parent
)) do {} while (0); } while (0); elm = (head)->rbh_root; break
; } } else { tmp = (parent)->entry.rbe_left; if ((tmp)->
entry.rbe_color == 1) { do { (tmp)->entry.rbe_color = 0; (
parent)->entry.rbe_color = 1; } while (0); do { (tmp) = (parent
)->entry.rbe_left; if (((parent)->entry.rbe_left = (tmp
)->entry.rbe_right)) { ((tmp)->entry.rbe_right)->entry
.rbe_parent = (parent); } do {} while (0); if (((tmp)->entry
.rbe_parent = (parent)->entry.rbe_parent)) { if ((parent) ==
((parent)->entry.rbe_parent)->entry.rbe_left) ((parent
)->entry.rbe_parent)->entry.rbe_left = (tmp); else ((parent
)->entry.rbe_parent)->entry.rbe_right = (tmp); } else (
head)->rbh_root = (tmp); (tmp)->entry.rbe_right = (parent
); (parent)->entry.rbe_parent = (tmp); do {} while (0); if
(((tmp)->entry.rbe_parent)) do {} while (0); } while (0);
tmp = (parent)->entry.rbe_left; } if (((tmp)->entry.rbe_left
== ((void *)0) || ((tmp)->entry.rbe_left)->entry.rbe_color
== 0) && ((tmp)->entry.rbe_right == ((void *)0) ||
((tmp)->entry.rbe_right)->entry.rbe_color == 0)) { (tmp
)->entry.rbe_color = 1; elm = parent; parent = (elm)->entry
.rbe_parent; } else { if ((tmp)->entry.rbe_left == ((void *
)0) || ((tmp)->entry.rbe_left)->entry.rbe_color == 0) {
struct ctag *oright; if ((oright = (tmp)->entry.rbe_right
)) (oright)->entry.rbe_color = 0; (tmp)->entry.rbe_color
= 1; do { (oright) = (tmp)->entry.rbe_right; if (((tmp)->
entry.rbe_right = (oright)->entry.rbe_left)) { ((oright)->
entry.rbe_left)->entry.rbe_parent = (tmp); } do {} while (
0); if (((oright)->entry.rbe_parent = (tmp)->entry.rbe_parent
)) { if ((tmp) == ((tmp)->entry.rbe_parent)->entry.rbe_left
) ((tmp)->entry.rbe_parent)->entry.rbe_left = (oright);
else ((tmp)->entry.rbe_parent)->entry.rbe_right = (oright
); } else (head)->rbh_root = (oright); (oright)->entry.
rbe_left = (tmp); (tmp)->entry.rbe_parent = (oright); do {
} while (0); if (((oright)->entry.rbe_parent)) do {} while
(0); } while (0); tmp = (parent)->entry.rbe_left; } (tmp)
->entry.rbe_color = (parent)->entry.rbe_color; (parent)
->entry.rbe_color = 0; if ((tmp)->entry.rbe_left) ((tmp
)->entry.rbe_left)->entry.rbe_color = 0; do { (tmp) = (
parent)->entry.rbe_left; if (((parent)->entry.rbe_left =
(tmp)->entry.rbe_right)) { ((tmp)->entry.rbe_right)->
entry.rbe_parent = (parent); } do {} while (0); if (((tmp)->
entry.rbe_parent = (parent)->entry.rbe_parent)) { if ((parent
) == ((parent)->entry.rbe_parent)->entry.rbe_left) ((parent
)->entry.rbe_parent)->entry.rbe_left = (tmp); else ((parent
)->entry.rbe_parent)->entry.rbe_right = (tmp); } else (
head)->rbh_root = (tmp); (tmp)->entry.rbe_right = (parent
); (parent)->entry.rbe_parent = (tmp); do {} while (0); if
(((tmp)->entry.rbe_parent)) do {} while (0); } while (0);
elm = (head)->rbh_root; break; } } } if (elm) (elm)->entry
.rbe_color = 0; } struct ctag * tagtree_RB_REMOVE(struct tagtree
*head, struct ctag *elm) { struct ctag *child, *parent, *old
= elm; int color; if ((elm)->entry.rbe_left == ((void *)0
)) child = (elm)->entry.rbe_right; else if ((elm)->entry
.rbe_right == ((void *)0)) child = (elm)->entry.rbe_left; else
{ struct ctag *left; elm = (elm)->entry.rbe_right; while (
(left = (elm)->entry.rbe_left)) elm = left; child = (elm)->
entry.rbe_right; parent = (elm)->entry.rbe_parent; color =
(elm)->entry.rbe_color; if (child) (child)->entry.rbe_parent
= parent; if (parent) { if ((parent)->entry.rbe_left == elm
) (parent)->entry.rbe_left = child; else (parent)->entry
.rbe_right = child; do {} while (0); } else (head)->rbh_root
= child; if ((elm)->entry.rbe_parent == old) parent = elm
; (elm)->entry = (old)->entry; if ((old)->entry.rbe_parent
) { if (((old)->entry.rbe_parent)->entry.rbe_left == old
) ((old)->entry.rbe_parent)->entry.rbe_left = elm; else
((old)->entry.rbe_parent)->entry.rbe_right = elm; do {
} while (0); } else (head)->rbh_root = elm; ((old)->entry
.rbe_left)->entry.rbe_parent = elm; if ((old)->entry.rbe_right
) ((old)->entry.rbe_right)->entry.rbe_parent = elm; if (
parent) { left = parent; do { do {} while (0); } while ((left
= (left)->entry.rbe_parent)); } goto color; } parent = (elm
)->entry.rbe_parent; color = (elm)->entry.rbe_color; if
(child) (child)->entry.rbe_parent = parent; if (parent) {
if ((parent)->entry.rbe_left == elm) (parent)->entry.rbe_left
= child; else (parent)->entry.rbe_right = child; do {} while
(0); } else (head)->rbh_root = child; color: if (color ==
0) tagtree_RB_REMOVE_COLOR(head, parent, child); return (old
); } struct ctag * tagtree_RB_INSERT(struct tagtree *head, struct
ctag *elm) { struct ctag *tmp; struct ctag *parent = ((void *
)0); int comp = 0; tmp = (head)->rbh_root; while (tmp) { parent
= tmp; comp = (ctagcmp)(elm, parent); if (comp < 0) tmp =
(tmp)->entry.rbe_left; else if (comp > 0) tmp = (tmp)->
entry.rbe_right; else return (tmp); } do { (elm)->entry.rbe_parent
= parent; (elm)->entry.rbe_left = (elm)->entry.rbe_right
= ((void *)0); (elm)->entry.rbe_color = 1; } while (0); if
(parent != ((void *)0)) { if (comp < 0) (parent)->entry
.rbe_left = elm; else (parent)->entry.rbe_right = elm; do {
} while (0); } else (head)->rbh_root = elm; tagtree_RB_INSERT_COLOR
(head, elm); return (((void *)0)); } struct ctag * tagtree_RB_FIND
(struct tagtree *head, struct ctag *elm) { struct ctag *tmp =
(head)->rbh_root; int comp; while (tmp) { comp = ctagcmp(
elm, tmp); if (comp < 0) tmp = (tmp)->entry.rbe_left; else
if (comp > 0) tmp = (tmp)->entry.rbe_right; else return
(tmp); } return (((void *)0)); } struct ctag * tagtree_RB_NFIND
(struct tagtree *head, struct ctag *elm) { struct ctag *tmp =
(head)->rbh_root; struct ctag *res = ((void *)0); int comp
; while (tmp) { comp = ctagcmp(elm, tmp); if (comp < 0) { res
= tmp; tmp = (tmp)->entry.rbe_left; } else if (comp > 0
) tmp = (tmp)->entry.rbe_right; else return (tmp); } return
(res); } struct ctag * tagtree_RB_NEXT(struct ctag *elm) { if
((elm)->entry.rbe_right) { elm = (elm)->entry.rbe_right
; while ((elm)->entry.rbe_left) elm = (elm)->entry.rbe_left
; } else { if ((elm)->entry.rbe_parent && (elm == (
(elm)->entry.rbe_parent)->entry.rbe_left)) elm = (elm)->
entry.rbe_parent; else { while ((elm)->entry.rbe_parent &&
(elm == ((elm)->entry.rbe_parent)->entry.rbe_right)) elm
= (elm)->entry.rbe_parent; elm = (elm)->entry.rbe_parent
; } } return (elm); } struct ctag * tagtree_RB_PREV(struct ctag
*elm) { if ((elm)->entry.rbe_left) { elm = (elm)->entry
.rbe_left; while ((elm)->entry.rbe_right) elm = (elm)->
entry.rbe_right; } else { if ((elm)->entry.rbe_parent &&
(elm == ((elm)->entry.rbe_parent)->entry.rbe_right)) elm
= (elm)->entry.rbe_parent; else { while ((elm)->entry.
rbe_parent && (elm == ((elm)->entry.rbe_parent)->
entry.rbe_left)) elm = (elm)->entry.rbe_parent; elm = (elm
)->entry.rbe_parent; } } return (elm); } struct ctag * tagtree_RB_MINMAX
(struct tagtree *head, int val) { struct ctag *tmp = (head)->
rbh_root; struct ctag *parent = ((void *)0); while (tmp) { parent
= tmp; if (val < 0) tmp = (tmp)->entry.rbe_left; else tmp
= (tmp)->entry.rbe_right; } return (parent); }
;
53
54struct tagpos {
55 SLIST_ENTRY(tagpos)struct { struct tagpos *sle_next; } entry;
56 int doto;
57 int dotline;
58 char *bname;
59};
60SLIST_HEAD(tagstack, tagpos)struct tagstack { struct tagpos *slh_first; } shead = SLIST_HEAD_INITIALIZER(shead){ ((void *)0) };
61
62int
63ctagcmp(struct ctag *s, struct ctag *t)
64{
65 return strcmp(s->tag, t->tag);
66}
67
68/*
69 * Record the filename that contain tags to be used while loading them
70 * on first use. If a filename is already recorded, ask user to retain
71 * already loaded tags (if any) and unload them if user chooses not to.
72 */
73/* ARGSUSED */
74int
75tagsvisit(int f, int n)
76{
77 char fname[NFILEN1024], *bufp, *temp;
78 struct stat sb;
79
80 if (getbufcwd(fname, sizeof(fname)) == FALSE0)
81 fname[0] = '\0';
82
83 if (strlcat(fname, DEFAULTFN"tags", sizeof(fname)) >= sizeof(fname)) {
84 dobeep();
85 ewprintf("Filename too long");
86 return (FALSE0);
87 }
88
89 bufp = eread("Visit tags table (default %s): ", fname,
90 NFILEN1024, EFFILE0x0004 | EFCR0x0010 | EFNEW0x0008 | EFDEF0x0020, DEFAULTFN"tags");
91 if (bufp == NULL((void *)0))
92 return (ABORT2);
93
94 if (stat(bufp, &sb) == -1) {
95 dobeep();
96 ewprintf("stat: %s", strerror(errno(*__errno())));
97 return (FALSE0);
98 } else if (S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000) == 0) {
99 dobeep();
100 ewprintf("Not a regular file");
101 return (FALSE0);
102 } else if (access(bufp, R_OK0x04) == -1) {
103 dobeep();
104 ewprintf("Cannot access file %s", bufp);
105 return (FALSE0);
106 }
107
108 if (tagsfn == NULL((void *)0)) {
109 if (bufp[0] == '\0') {
110 if ((tagsfn = strdup(fname)) == NULL((void *)0)) {
111 dobeep();
112 ewprintf("Out of memory");
113 return (FALSE0);
114 }
115 } else {
116 /* bufp points to local variable, so duplicate. */
117 if ((tagsfn = strdup(bufp)) == NULL((void *)0)) {
118 dobeep();
119 ewprintf("Out of memory");
120 return (FALSE0);
121 }
122 }
123 } else {
124 if ((temp = strdup(bufp)) == NULL((void *)0)) {
125 dobeep();
126 ewprintf("Out of memory");
127 return (FALSE0);
128 }
129 free(tagsfn);
130 tagsfn = temp;
131 if (eyorn("Keep current list of tags table also") == FALSE0) {
132 ewprintf("Starting a new list of tags table");
133 unloadtags();
134 }
135 loaded = FALSE0;
136 }
137 return (TRUE1);
138}
139
140/*
141 * Ask user for a tag while treating word at dot as default. Visit tags
142 * file if not yet done, load tags and jump to definition of the tag.
143 */
144int
145findtag(int f, int n)
146{
147 char utok[MAX_TOKEN64], dtok[MAX_TOKEN64];
148 char *tok, *bufp;
149 int ret;
150
151 if (curtoken(f, n, dtok) == FALSE0) {
1
Taking true branch
152 dtok[0] = '\0';
153 bufp = eread("Find tag: ", utok, MAX_TOKEN64, EFNUL0x0040 | EFNEW0x0008);
154 } else
155 bufp = eread("Find tag (default %s): ", utok, MAX_TOKEN64,
156 EFNUL0x0040 | EFNEW0x0008, dtok);
157
158 if (bufp == NULL((void *)0))
2
Assuming 'bufp' is not equal to NULL
3
Taking false branch
159 return (ABORT2);
160 else if (bufp[0] == '\0')
4
Assuming the condition is false
5
Taking false branch
161 tok = dtok;
162 else
163 tok = utok;
164
165 if (tok[0] == '\0') {
6
Assuming the condition is false
7
Taking false branch
166 dobeep();
167 ewprintf("There is no default tag");
168 return (FALSE0);
169 }
170
171 if (tagsfn == NULL((void *)0))
8
Assuming 'tagsfn' is not equal to NULL
9
Taking false branch
172 if ((ret = tagsvisit(f, n)) != TRUE1)
173 return (ret);
174 if (!loaded) {
10
Assuming 'loaded' is 0
11
Taking true branch
175 if (loadtags(tagsfn) == FALSE0) {
12
Calling 'loadtags'
176 free(tagsfn);
177 tagsfn = NULL((void *)0);
178 return (FALSE0);
179 }
180 loaded = TRUE1;
181 }
182 return pushtag(tok);
183}
184
185/*
186 * Free tags tree.
187 */
188void
189unloadtags(void)
190{
191 struct ctag *var, *nxt;
192
193 for (var = RB_MIN(tagtree, &tags)tagtree_RB_MINMAX(&tags, -1); var != NULL((void *)0); var = nxt) {
194 nxt = RB_NEXT(tagtree, &tags, var)tagtree_RB_NEXT(var);
195 RB_REMOVE(tagtree, &tags, var)tagtree_RB_REMOVE(&tags, var);
196 /* line parsed with fparseln needs to be freed */
197 free(var->tag);
198 free(var);
199 }
200}
201
202/*
203 * Lookup tag passed in tree and if found, push current location and
204 * buffername onto stack, load the file with tag definition into a new
205 * buffer and position dot at the pattern.
206 */
207/*ARGSUSED */
208int
209pushtag(char *tok)
210{
211 struct ctag *res;
212 struct tagpos *s;
213 char bname[NFILEN1024];
214 int doto, dotline;
215
216 if ((res = searchtag(tok)) == NULL((void *)0))
217 return (FALSE0);
218
219 doto = curwp->w_doto;
220 dotline = curwp->w_dotline;
221 /* record absolute filenames. Fixes issues when mg's cwd is not the
222 * same as buffer's directory.
223 */
224 if (strlcpy(bname, curbp->b_cwd, sizeof(bname)) >= sizeof(bname)) {
225 dobeep();
226 ewprintf("filename too long");
227 return (FALSE0);
228 }
229 if (strlcat(bname, curbp->b_bnameb_list.l_name, sizeof(bname)) >= sizeof(bname)) {
230 dobeep();
231 ewprintf("filename too long");
232 return (FALSE0);
233 }
234
235 if (loadbuffer(res->fname) == FALSE0)
236 return (FALSE0);
237
238 if (searchpat(res->pat) == TRUE1) {
239 if ((s = malloc(sizeof(struct tagpos))) == NULL((void *)0)) {
240 dobeep();
241 ewprintf("Out of memory");
242 return (FALSE0);
243 }
244 if ((s->bname = strdup(bname)) == NULL((void *)0)) {
245 dobeep();
246 ewprintf("Out of memory");
247 free(s);
248 return (FALSE0);
249 }
250 s->doto = doto;
251 s->dotline = dotline;
252 SLIST_INSERT_HEAD(&shead, s, entry)do { (s)->entry.sle_next = (&shead)->slh_first; (&
shead)->slh_first = (s); } while (0)
;
253 return (TRUE1);
254 } else {
255 dobeep();
256 ewprintf("%s: pattern not found", res->tag);
257 return (FALSE0);
258 }
259 /* NOTREACHED */
260 return (FALSE0);
261}
262
263/*
264 * If tag stack is not empty pop stack and jump to recorded buffer, dot.
265 */
266/* ARGSUSED */
267int
268poptag(int f, int n)
269{
270 struct line *dotp;
271 struct tagpos *s;
272
273 if (SLIST_EMPTY(&shead)(((&shead)->slh_first) == ((void *)0))) {
274 dobeep();
275 ewprintf("No previous location for find-tag invocation");
276 return (FALSE0);
277 }
278 s = SLIST_FIRST(&shead)((&shead)->slh_first);
279 SLIST_REMOVE_HEAD(&shead, entry)do { (&shead)->slh_first = (&shead)->slh_first->
entry.sle_next; } while (0)
;
280 if (loadbuffer(s->bname) == FALSE0)
281 return (FALSE0);
282 curwp->w_dotline = s->dotline;
283 curwp->w_doto = s->doto;
284
285 /* storing of dotp in tagpos wouldn't work out in cases when
286 * that buffer is killed by user(dangling pointer). Explicitly
287 * traverse till dotline for correct handling.
288 */
289 dotp = curwp->w_bufp->b_headp;
290 while (s->dotline--)
291 dotp = dotp->l_fp;
292
293 curwp->w_dotp = dotp;
294 free(s->bname);
295 free(s);
296 return (TRUE1);
297}
298
299/*
300 * Parse the tags file and construct the tags tree. Remove escape
301 * characters while parsing the file.
302 */
303int
304loadtags(const char *fn)
305{
306 char *l;
307 FILE *fd;
308
309 if ((fd = fopen(fn, "r")) == NULL((void *)0)) {
13
Assuming the condition is false
14
Taking false branch
310 dobeep();
311 ewprintf("Unable to open tags file: %s", fn);
312 return (FALSE0);
313 }
314 while ((l = fparseln(fd, NULL((void *)0), NULL((void *)0), "\\\\\0",
15
Assuming the condition is true
16
Loop condition is true. Entering loop body
315 FPARSELN_UNESCCONT0x02 | FPARSELN_UNESCREST0x08)) != NULL((void *)0)) {
316 if (addctag(l) == FALSE0) {
17
Calling 'addctag'
317 fclose(fd);
318 return (FALSE0);
319 }
320 }
321 fclose(fd);
322 return (TRUE1);
323}
324
325/*
326 * Cleanup and destroy tree and stack.
327 */
328void
329closetags(void)
330{
331 struct tagpos *s;
332
333 while (!SLIST_EMPTY(&shead)(((&shead)->slh_first) == ((void *)0))) {
334 s = SLIST_FIRST(&shead)((&shead)->slh_first);
335 SLIST_REMOVE_HEAD(&shead, entry)do { (&shead)->slh_first = (&shead)->slh_first->
entry.sle_next; } while (0)
;
336 free(s->bname);
337 free(s);
338 }
339 unloadtags();
340 free(tagsfn);
341}
342
343/*
344 * Strip away any special characters in pattern.
345 * The pattern in ctags isn't a true regular expression. Its of the form
346 * /^xxx$/ or ?^xxx$? and in some cases the "$" would be missing. Strip
347 * the leading and trailing special characters so the pattern matching
348 * would be a simple string compare. Escape character is taken care by
349 * fparseln.
350 */
351char *
352strip(char *s, size_t len)
353{
354 /* first strip trailing special chars */
355 s[len - 1] = '\0';
356 if (s[len - 2] == '$')
357 s[len - 2] = '\0';
358
359 /* then strip leading special chars */
360 s++;
361 if (*s == '^')
362 s++;
363
364 return s;
365}
366
367/*
368 * tags line is of the format "<tag>\t<filename>\t<pattern>". Split them
369 * by replacing '\t' with '\0'. This wouldn't alter the size of malloc'ed
370 * l, and can be freed during cleanup.
371 */
372int
373addctag(char *l)
374{
375 struct ctag *t;
376
377 if ((t = malloc(sizeof(struct ctag))) == NULL((void *)0)) {
18
Memory is allocated
19
Assuming the condition is false
20
Taking false branch
378 dobeep();
379 ewprintf("Out of memory");
380 return (FALSE0);
381 }
382 t->tag = l;
383 if ((l = strchr(l, '\t')) == NULL((void *)0))
21
Assuming the condition is false
22
Taking false branch
384 goto cleanup;
385 *l++ = '\0';
386 t->fname = l;
387 if ((l = strchr(l, '\t')) == NULL((void *)0))
23
Assuming the condition is false
24
Taking false branch
388 goto cleanup;
389 *l++ = '\0';
390 if (*l == '\0')
25
Assuming the condition is false
26
Taking false branch
391 goto cleanup;
392 t->pat = strip(l, strlen(l));
393 RB_INSERT(tagtree, &tags, t)tagtree_RB_INSERT(&tags, t);
394 return (TRUE1);
27
Potential leak of memory pointed to by 't'
395cleanup:
396 free(t);
397 free(l);
398 return (FALSE0);
399}
400
401/*
402 * Search through each line of buffer for pattern.
403 */
404int
405searchpat(char *s_pat)
406{
407 struct line *lp;
408 int dotline;
409 size_t plen;
410
411 plen = strlen(s_pat);
412 dotline = 1;
413 lp = lforw(curbp->b_headp)((curbp->b_headp)->l_fp);
414 while (lp != curbp->b_headp) {
415 if (ltext(lp)((lp)->l_text) != NULL((void *)0) && plen <= llength(lp)((lp)->l_used) &&
416 (strncmp(s_pat, ltext(lp)((lp)->l_text), plen) == 0)) {
417 curwp->w_doto = 0;
418 curwp->w_dotp = lp;
419 curwp->w_dotline = dotline;
420 return (TRUE1);
421 } else {
422 lp = lforw(lp)((lp)->l_fp);
423 dotline++;
424 }
425 }
426 return (FALSE0);
427}
428
429/*
430 * Return TRUE if dot is at beginning of a word or at beginning
431 * of line, else FALSE.
432 */
433int
434atbow(void)
435{
436 if (curwp->w_doto == 0)
437 return (TRUE1);
438 if (ISWORD(curwp->w_dotp->l_text[curwp->w_doto])((cinfo[((unsigned char) (curwp->w_dotp->l_text[curwp->
w_doto]))]&0x01)!=0)
&&
439 !ISWORD(curwp->w_dotp->l_text[curwp->w_doto - 1])((cinfo[((unsigned char) (curwp->w_dotp->l_text[curwp->
w_doto - 1]))]&0x01)!=0)
)
440 return (TRUE1);
441 return (FALSE0);
442}
443
444/*
445 * Extract the word at dot without changing dot position.
446 */
447int
448curtoken(int f, int n, char *token)
449{
450 struct line *odotp;
451 int odoto, tdoto, odotline, size, r;
452 char c;
453
454 /* Underscore character is to be treated as "inword" while
455 * processing tokens unlike mg's default word traversal. Save
456 * and restore it's cinfo value so that tag matching works for
457 * identifier with underscore.
458 */
459 c = cinfo['_'];
460 cinfo['_'] = _MG_W0x01;
461
462 odotp = curwp->w_dotp;
463 odoto = curwp->w_doto;
464 odotline = curwp->w_dotline;
465
466 /* Move backword unless we are at the beginning of a word or at
467 * beginning of line.
468 */
469 if (!atbow())
470 if ((r = backword(f, n)) == FALSE0)
471 goto cleanup;
472
473 tdoto = curwp->w_doto;
474
475 if ((r = forwword(f, n)) == FALSE0)
476 goto cleanup;
477
478 /* strip away leading whitespace if any like emacs. */
479 while (ltext(curwp->w_dotp)((curwp->w_dotp)->l_text) &&
480 isspace(lgetc(curwp->w_dotp, tdoto)(((unsigned char) ((curwp->w_dotp)->l_text[(tdoto)])))))
481 tdoto++;
482
483 size = curwp->w_doto - tdoto;
484 if (size <= 0 || size >= MAX_TOKEN64 ||
485 ltext(curwp->w_dotp)((curwp->w_dotp)->l_text) == NULL((void *)0)) {
486 r = FALSE0;
487 goto cleanup;
488 }
489 strncpy(token, ltext(curwp->w_dotp)((curwp->w_dotp)->l_text) + tdoto, size);
490 token[size] = '\0';
491 r = TRUE1;
492
493cleanup:
494 cinfo['_'] = c;
495 curwp->w_dotp = odotp;
496 curwp->w_doto = odoto;
497 curwp->w_dotline = odotline;
498 return (r);
499}
500
501/*
502 * Search tagstree for a given token.
503 */
504struct ctag *
505searchtag(char *tok)
506{
507 struct ctag t, *res;
508
509 t.tag = tok;
510 if ((res = RB_FIND(tagtree, &tags, &t)tagtree_RB_FIND(&tags, &t)) == NULL((void *)0)) {
511 dobeep();
512 ewprintf("No tag containing %s", tok);
513 return (NULL((void *)0));
514 }
515 return res;
516}
517
518/*
519 * This is equivalent to filevisit from file.c.
520 * Look around to see if we can find the file in another buffer; if we
521 * can't find it, create a new buffer, read in the text, and switch to
522 * the new buffer. *scratch*, *grep*, *compile* needs to be handled
523 * differently from other buffers which have "filenames".
524 */
525int
526loadbuffer(char *bname)
527{
528 struct buffer *bufp;
529 char *adjf;
530
531 /* check for special buffers which begin with '*' */
532 if (bname[0] == '*') {
533 if ((bufp = bfind(bname, FALSE0)) != NULL((void *)0)) {
534 curbp = bufp;
535 return (showbuffer(bufp, curwp, WFFULL0x08));
536 } else {
537 return (FALSE0);
538 }
539 } else {
540 if ((adjf = adjustname(bname, TRUE1)) == NULL((void *)0))
541 return (FALSE0);
542 if ((bufp = findbuffer(adjf)) == NULL((void *)0))
543 return (FALSE0);
544 }
545 curbp = bufp;
546 if (showbuffer(bufp, curwp, WFFULL0x08) != TRUE1)
547 return (FALSE0);
548 if (bufp->b_fname[0] == '\0') {
549 if (readin(adjf) != TRUE1) {
550 killbuffer(bufp);
551 return (FALSE0);
552 }
553 }
554 return (TRUE1);
555}