| 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 | } |