[BACK]Return to tags.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/tags.c, Revision 1.19

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