File: | src/usr.bin/less/less/../tags.c |
Warning: | line 78, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (C) 1984-2012 Mark Nudelman | |||
3 | * Modified for use with illumos by Garrett D'Amore. | |||
4 | * Copyright 2014 Garrett D'Amore <garrett@damore.org> | |||
5 | * | |||
6 | * You may distribute under the terms of either the GNU General Public | |||
7 | * License or the Less License, as specified in the README file. | |||
8 | * | |||
9 | * For more information, see the README file. | |||
10 | */ | |||
11 | ||||
12 | #include "less.h" | |||
13 | ||||
14 | #define WHITESP(c)((c) == ' ' || (c) == '\t') ((c) == ' ' || (c) == '\t') | |||
15 | ||||
16 | char *tags = "tags"; | |||
17 | ||||
18 | static int total; | |||
19 | static int curseq; | |||
20 | ||||
21 | extern int linenums; | |||
22 | ||||
23 | enum tag_result { | |||
24 | TAG_FOUND, | |||
25 | TAG_NOFILE, | |||
26 | TAG_NOTAG, | |||
27 | TAG_NOTYPE, | |||
28 | TAG_INTR | |||
29 | }; | |||
30 | ||||
31 | static enum tag_result findctag(char *); | |||
32 | static char *nextctag(void); | |||
33 | static char *prevctag(void); | |||
34 | static off_t ctagsearch(void); | |||
35 | ||||
36 | /* | |||
37 | * The list of tags generated by the last findctag() call. | |||
38 | */ | |||
39 | struct taglist { | |||
40 | struct tag *tl_first; | |||
41 | struct tag *tl_last; | |||
42 | }; | |||
43 | #define TAG_END((struct tag *)&taglist) ((struct tag *)&taglist) | |||
44 | static struct taglist taglist = { TAG_END((struct tag *)&taglist), TAG_END((struct tag *)&taglist) }; | |||
45 | struct tag { | |||
46 | struct tag *next, *prev; /* List links */ | |||
47 | char *tag_file; /* Source file containing the tag */ | |||
48 | off_t tag_linenum; /* Appropriate line number in source file */ | |||
49 | char *tag_pattern; /* Pattern used to find the tag */ | |||
50 | int tag_endline; /* True if the pattern includes '$' */ | |||
51 | }; | |||
52 | static struct tag *curtag; | |||
53 | ||||
54 | #define TAG_INS(tp)(tp)->next = ((struct tag *)&taglist); (tp)->prev = taglist.tl_last; taglist.tl_last->next = (tp); taglist.tl_last = (tp); \ | |||
55 | (tp)->next = TAG_END((struct tag *)&taglist); \ | |||
56 | (tp)->prev = taglist.tl_last; \ | |||
57 | taglist.tl_last->next = (tp); \ | |||
58 | taglist.tl_last = (tp); | |||
59 | ||||
60 | #define TAG_RM(tp)(tp)->next->prev = (tp)->prev; (tp)->prev->next = (tp)->next; \ | |||
61 | (tp)->next->prev = (tp)->prev; \ | |||
62 | (tp)->prev->next = (tp)->next; | |||
63 | ||||
64 | /* | |||
65 | * Delete tag structures. | |||
66 | */ | |||
67 | void | |||
68 | cleantags(void) | |||
69 | { | |||
70 | struct tag *tp; | |||
71 | ||||
72 | /* | |||
73 | * Delete any existing tag list. | |||
74 | * {{ Ideally, we wouldn't do this until after we know that we | |||
75 | * can load some other tag information. }} | |||
76 | */ | |||
77 | while ((tp = taglist.tl_first) != TAG_END((struct tag *)&taglist)) { | |||
78 | TAG_RM(tp)(tp)->next->prev = (tp)->prev; (tp)->prev->next = (tp)->next;; | |||
| ||||
79 | free(tp->tag_file); | |||
80 | free(tp->tag_pattern); | |||
81 | free(tp); | |||
82 | } | |||
83 | curtag = NULL((void *)0); | |||
84 | total = curseq = 0; | |||
85 | } | |||
86 | ||||
87 | /* | |||
88 | * Create a new tag entry. | |||
89 | */ | |||
90 | static struct tag * | |||
91 | maketagent(char *file, off_t linenum, char *pattern, int endline) | |||
92 | { | |||
93 | struct tag *tp; | |||
94 | ||||
95 | tp = ecalloc(sizeof (struct tag), 1); | |||
96 | tp->tag_file = estrdup(file); | |||
97 | tp->tag_linenum = linenum; | |||
98 | tp->tag_endline = endline; | |||
99 | if (pattern == NULL((void *)0)) | |||
100 | tp->tag_pattern = NULL((void *)0); | |||
101 | else | |||
102 | tp->tag_pattern = estrdup(pattern); | |||
103 | return (tp); | |||
104 | } | |||
105 | ||||
106 | /* | |||
107 | * Find tags in tag file. | |||
108 | */ | |||
109 | void | |||
110 | findtag(char *tag) | |||
111 | { | |||
112 | enum tag_result result; | |||
113 | ||||
114 | result = findctag(tag); | |||
| ||||
115 | switch (result) { | |||
116 | case TAG_FOUND: | |||
117 | case TAG_INTR: | |||
118 | break; | |||
119 | case TAG_NOFILE: | |||
120 | error("No tags file", NULL((void *)0)); | |||
121 | break; | |||
122 | case TAG_NOTAG: | |||
123 | error("No such tag in tags file", NULL((void *)0)); | |||
124 | break; | |||
125 | case TAG_NOTYPE: | |||
126 | error("unknown tag type", NULL((void *)0)); | |||
127 | break; | |||
128 | } | |||
129 | } | |||
130 | ||||
131 | /* | |||
132 | * Search for a tag. | |||
133 | */ | |||
134 | off_t | |||
135 | tagsearch(void) | |||
136 | { | |||
137 | if (curtag == NULL((void *)0)) | |||
138 | return (-1); /* No tags loaded! */ | |||
139 | if (curtag->tag_linenum != 0) | |||
140 | return (find_pos(curtag->tag_linenum)); | |||
141 | return (ctagsearch()); | |||
142 | } | |||
143 | ||||
144 | /* | |||
145 | * Go to the next tag. | |||
146 | */ | |||
147 | char * | |||
148 | nexttag(int n) | |||
149 | { | |||
150 | char *tagfile = NULL((void *)0); | |||
151 | ||||
152 | while (n-- > 0) | |||
153 | tagfile = nextctag(); | |||
154 | return (tagfile); | |||
155 | } | |||
156 | ||||
157 | /* | |||
158 | * Go to the previous tag. | |||
159 | */ | |||
160 | char * | |||
161 | prevtag(int n) | |||
162 | { | |||
163 | char *tagfile = NULL((void *)0); | |||
164 | ||||
165 | while (n-- > 0) | |||
166 | tagfile = prevctag(); | |||
167 | return (tagfile); | |||
168 | } | |||
169 | ||||
170 | /* | |||
171 | * Return the total number of tags. | |||
172 | */ | |||
173 | int | |||
174 | ntags(void) | |||
175 | { | |||
176 | return (total); | |||
177 | } | |||
178 | ||||
179 | /* | |||
180 | * Return the sequence number of current tag. | |||
181 | */ | |||
182 | int | |||
183 | curr_tag(void) | |||
184 | { | |||
185 | return (curseq); | |||
186 | } | |||
187 | ||||
188 | /* | |||
189 | * Find tags in the "tags" file. | |||
190 | * Sets curtag to the first tag entry. | |||
191 | */ | |||
192 | static enum tag_result | |||
193 | findctag(char *tag) | |||
194 | { | |||
195 | char *p; | |||
196 | FILE *f; | |||
197 | int taglen; | |||
198 | off_t taglinenum; | |||
199 | char *tagfile; | |||
200 | char *tagpattern; | |||
201 | int tagendline; | |||
202 | int search_char; | |||
203 | int err; | |||
204 | char tline[TAGLINE_SIZE1024]; | |||
205 | struct tag *tp; | |||
206 | ||||
207 | p = shell_unquote(tags); | |||
208 | f = fopen(p, "r"); | |||
209 | free(p); | |||
210 | if (f == NULL((void *)0)) | |||
211 | return (TAG_NOFILE); | |||
212 | ||||
213 | cleantags(); | |||
214 | total = 0; | |||
215 | taglen = strlen(tag); | |||
216 | ||||
217 | /* | |||
218 | * Search the tags file for the desired tag. | |||
219 | */ | |||
220 | while (fgets(tline, sizeof (tline), f) != NULL((void *)0)) { | |||
221 | if (tline[0] == '!') | |||
222 | /* Skip header of extended format. */ | |||
223 | continue; | |||
224 | if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])((tline[taglen]) == ' ' || (tline[taglen]) == '\t')) | |||
225 | continue; | |||
226 | ||||
227 | /* | |||
228 | * Found it. | |||
229 | * The line contains the tag, the filename and the | |||
230 | * location in the file, separated by white space. | |||
231 | * The location is either a decimal line number, | |||
232 | * or a search pattern surrounded by a pair of delimiters. | |||
233 | * Parse the line and extract these parts. | |||
234 | */ | |||
235 | tagpattern = NULL((void *)0); | |||
236 | ||||
237 | /* | |||
238 | * Skip over the whitespace after the tag name. | |||
239 | */ | |||
240 | p = skipsp(tline+taglen); | |||
241 | if (*p == '\0') | |||
242 | /* File name is missing! */ | |||
243 | continue; | |||
244 | ||||
245 | /* | |||
246 | * Save the file name. | |||
247 | * Skip over the whitespace after the file name. | |||
248 | */ | |||
249 | tagfile = p; | |||
250 | while (!WHITESP(*p)((*p) == ' ' || (*p) == '\t') && *p != '\0') | |||
251 | p++; | |||
252 | *p++ = '\0'; | |||
253 | p = skipsp(p); | |||
254 | if (*p == '\0') | |||
255 | /* Pattern is missing! */ | |||
256 | continue; | |||
257 | ||||
258 | /* | |||
259 | * First see if it is a line number. | |||
260 | */ | |||
261 | tagendline = 0; | |||
262 | taglinenum = getnum(&p, 0, &err); | |||
263 | if (err) { | |||
264 | /* | |||
265 | * No, it must be a pattern. | |||
266 | * Delete the initial "^" (if present) and | |||
267 | * the final "$" from the pattern. | |||
268 | * Delete any backslash in the pattern. | |||
269 | */ | |||
270 | taglinenum = 0; | |||
271 | search_char = *p++; | |||
272 | if (*p == '^') | |||
273 | p++; | |||
274 | tagpattern = p; | |||
275 | while (*p != search_char && *p != '\0') { | |||
276 | if (*p == '\\') | |||
277 | p++; | |||
278 | p++; | |||
279 | } | |||
280 | tagendline = (p[-1] == '$'); | |||
281 | if (tagendline) | |||
282 | p--; | |||
283 | *p = '\0'; | |||
284 | } | |||
285 | tp = maketagent(tagfile, taglinenum, tagpattern, tagendline); | |||
286 | TAG_INS(tp)(tp)->next = ((struct tag *)&taglist); (tp)->prev = taglist.tl_last; taglist.tl_last->next = (tp); taglist.tl_last = (tp);; | |||
287 | total++; | |||
288 | } | |||
289 | fclose(f); | |||
290 | if (total == 0) | |||
291 | return (TAG_NOTAG); | |||
292 | curtag = taglist.tl_first; | |||
293 | curseq = 1; | |||
294 | return (TAG_FOUND); | |||
295 | } | |||
296 | ||||
297 | /* | |||
298 | * Edit current tagged file. | |||
299 | */ | |||
300 | int | |||
301 | edit_tagfile(void) | |||
302 | { | |||
303 | if (curtag == NULL((void *)0)) | |||
304 | return (1); | |||
305 | return (edit(curtag->tag_file)); | |||
306 | } | |||
307 | ||||
308 | /* | |||
309 | * Search for a tag. | |||
310 | * This is a stripped-down version of search(). | |||
311 | * We don't use search() for several reasons: | |||
312 | * - We don't want to blow away any search string we may have saved. | |||
313 | * - The various regular-expression functions (from different systems: | |||
314 | * regcmp vs. re_comp) behave differently in the presence of | |||
315 | * parentheses (which are almost always found in a tag). | |||
316 | */ | |||
317 | static off_t | |||
318 | ctagsearch(void) | |||
319 | { | |||
320 | off_t pos, linepos; | |||
321 | off_t linenum; | |||
322 | int len; | |||
323 | char *line; | |||
324 | ||||
325 | pos = ch_zero()(0); | |||
326 | linenum = find_linenum(pos); | |||
327 | ||||
328 | for (;;) { | |||
329 | /* | |||
330 | * Get lines until we find a matching one or | |||
331 | * until we hit end-of-file. | |||
332 | */ | |||
333 | if (abort_sigs()) | |||
334 | return (-1); | |||
335 | ||||
336 | /* | |||
337 | * Read the next line, and save the | |||
338 | * starting position of that line in linepos. | |||
339 | */ | |||
340 | linepos = pos; | |||
341 | pos = forw_raw_line(pos, &line, (int *)NULL((void *)0)); | |||
342 | if (linenum != 0) | |||
343 | linenum++; | |||
344 | ||||
345 | if (pos == -1) { | |||
346 | /* | |||
347 | * We hit EOF without a match. | |||
348 | */ | |||
349 | error("Tag not found", NULL((void *)0)); | |||
350 | return (-1); | |||
351 | } | |||
352 | ||||
353 | /* | |||
354 | * If we're using line numbers, we might as well | |||
355 | * remember the information we have now (the position | |||
356 | * and line number of the current line). | |||
357 | */ | |||
358 | if (linenums) | |||
359 | add_lnum(linenum, pos); | |||
360 | ||||
361 | /* | |||
362 | * Test the line to see if we have a match. | |||
363 | * Use strncmp because the pattern may be | |||
364 | * truncated (in the tags file) if it is too long. | |||
365 | * If tagendline is set, make sure we match all | |||
366 | * the way to end of line (no extra chars after the match). | |||
367 | */ | |||
368 | len = strlen(curtag->tag_pattern); | |||
369 | if (strncmp(curtag->tag_pattern, line, len) == 0 && | |||
370 | (!curtag->tag_endline || line[len] == '\0' || | |||
371 | line[len] == '\r')) { | |||
372 | curtag->tag_linenum = find_linenum(linepos); | |||
373 | break; | |||
374 | } | |||
375 | } | |||
376 | ||||
377 | return (linepos); | |||
378 | } | |||
379 | ||||
380 | static int circular = 0; /* 1: circular tag structure */ | |||
381 | ||||
382 | /* | |||
383 | * Return the filename required for the next tag in the queue that was setup | |||
384 | * by findctag(). The next call to ctagsearch() will try to position at the | |||
385 | * appropriate tag. | |||
386 | */ | |||
387 | static char * | |||
388 | nextctag(void) | |||
389 | { | |||
390 | struct tag *tp; | |||
391 | ||||
392 | if (curtag == NULL((void *)0)) | |||
393 | /* No tag loaded */ | |||
394 | return (NULL((void *)0)); | |||
395 | ||||
396 | tp = curtag->next; | |||
397 | if (tp == TAG_END((struct tag *)&taglist)) { | |||
398 | if (!circular) | |||
399 | return (NULL((void *)0)); | |||
400 | /* Wrapped around to the head of the queue */ | |||
401 | curtag = taglist.tl_first; | |||
402 | curseq = 1; | |||
403 | } else { | |||
404 | curtag = tp; | |||
405 | curseq++; | |||
406 | } | |||
407 | return (curtag->tag_file); | |||
408 | } | |||
409 | ||||
410 | /* | |||
411 | * Return the filename required for the previous ctag in the queue that was | |||
412 | * setup by findctag(). The next call to ctagsearch() will try to position | |||
413 | * at the appropriate tag. | |||
414 | */ | |||
415 | static char * | |||
416 | prevctag(void) | |||
417 | { | |||
418 | struct tag *tp; | |||
419 | ||||
420 | if (curtag == NULL((void *)0)) | |||
421 | /* No tag loaded */ | |||
422 | return (NULL((void *)0)); | |||
423 | ||||
424 | tp = curtag->prev; | |||
425 | if (tp == TAG_END((struct tag *)&taglist)) { | |||
426 | if (!circular) | |||
427 | return (NULL((void *)0)); | |||
428 | /* Wrapped around to the tail of the queue */ | |||
429 | curtag = taglist.tl_last; | |||
430 | curseq = total; | |||
431 | } else { | |||
432 | curtag = tp; | |||
433 | curseq--; | |||
434 | } | |||
435 | return (curtag->tag_file); | |||
436 | } |