[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.20

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.4       millert    22:
                     23: enum tag_result {
                     24:        TAG_FOUND,
                     25:        TAG_NOFILE,
                     26:        TAG_NOTAG,
                     27:        TAG_NOTYPE,
                     28:        TAG_INTR
                     29: };
                     30:
1.15      nicm       31: static enum tag_result findctag(char *);
                     32: static char *nextctag(void);
                     33: static char *prevctag(void);
                     34: static off_t ctagsearch(void);
1.4       millert    35:
                     36: /*
1.15      nicm       37:  * The list of tags generated by the last findctag() call.
1.4       millert    38:  */
                     39: struct taglist {
                     40:        struct tag *tl_first;
                     41:        struct tag *tl_last;
                     42: };
1.12      nicm       43: #define        TAG_END  ((struct tag *)&taglist)
1.4       millert    44: static struct taglist taglist = { TAG_END, TAG_END };
                     45: struct tag {
                     46:        struct tag *next, *prev; /* List links */
                     47:        char *tag_file;         /* Source file containing the tag */
1.17      mmcc       48:        off_t tag_linenum;      /* Appropriate line number in source file */
1.4       millert    49:        char *tag_pattern;      /* Pattern used to find the tag */
1.12      nicm       50:        int tag_endline;        /* True if the pattern includes '$' */
1.4       millert    51: };
                     52: static struct tag *curtag;
                     53:
1.12      nicm       54: #define        TAG_INS(tp) \
1.9       shadchin   55:        (tp)->next = TAG_END; \
                     56:        (tp)->prev = taglist.tl_last; \
                     57:        taglist.tl_last->next = (tp); \
                     58:        taglist.tl_last = (tp);
1.4       millert    59:
1.12      nicm       60: #define        TAG_RM(tp) \
1.4       millert    61:        (tp)->next->prev = (tp)->prev; \
                     62:        (tp)->prev->next = (tp)->next;
                     63:
                     64: /*
                     65:  * Delete tag structures.
                     66:  */
1.12      nicm       67: void
                     68: cleantags(void)
1.4       millert    69: {
1.12      nicm       70:        struct tag *tp;
1.4       millert    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:         */
1.12      nicm       77:        while ((tp = taglist.tl_first) != TAG_END) {
1.4       millert    78:                TAG_RM(tp);
1.19      millert    79:                free(tp->tag_file);
                     80:                free(tp->tag_pattern);
1.4       millert    81:                free(tp);
                     82:        }
                     83:        curtag = NULL;
                     84:        total = curseq = 0;
                     85: }
                     86:
                     87: /*
                     88:  * Create a new tag entry.
                     89:  */
1.12      nicm       90: static struct tag *
1.17      mmcc       91: maketagent(char *file, off_t linenum, char *pattern, int endline)
1.4       millert    92: {
1.12      nicm       93:        struct tag *tp;
1.4       millert    94:
1.12      nicm       95:        tp = ecalloc(sizeof (struct tag), 1);
1.14      tedu       96:        tp->tag_file = estrdup(file);
1.4       millert    97:        tp->tag_linenum = linenum;
                     98:        tp->tag_endline = endline;
                     99:        if (pattern == NULL)
                    100:                tp->tag_pattern = NULL;
                    101:        else
1.14      tedu      102:                tp->tag_pattern = estrdup(pattern);
1.4       millert   103:        return (tp);
                    104: }
                    105:
                    106: /*
                    107:  * Find tags in tag file.
1.1       etheisen  108:  */
1.12      nicm      109: void
                    110: findtag(char *tag)
1.4       millert   111: {
                    112:        enum tag_result result;
                    113:
1.15      nicm      114:        result = findctag(tag);
1.12      nicm      115:        switch (result) {
1.4       millert   116:        case TAG_FOUND:
                    117:        case TAG_INTR:
                    118:                break;
                    119:        case TAG_NOFILE:
1.16      deraadt   120:                error("No tags file", NULL);
1.4       millert   121:                break;
                    122:        case TAG_NOTAG:
1.16      deraadt   123:                error("No such tag in tags file", NULL);
1.4       millert   124:                break;
                    125:        case TAG_NOTYPE:
1.16      deraadt   126:                error("unknown tag type", NULL);
1.4       millert   127:                break;
                    128:        }
                    129: }
                    130:
                    131: /*
                    132:  * Search for a tag.
                    133:  */
1.12      nicm      134: off_t
                    135: tagsearch(void)
1.4       millert   136: {
                    137:        if (curtag == NULL)
1.18      deraadt   138:                return (-1);    /* No tags loaded! */
1.4       millert   139:        if (curtag->tag_linenum != 0)
1.15      nicm      140:                return (find_pos(curtag->tag_linenum));
                    141:        return (ctagsearch());
1.4       millert   142: }
                    143:
                    144: /*
                    145:  * Go to the next tag.
                    146:  */
1.12      nicm      147: char *
                    148: nexttag(int n)
1.4       millert   149: {
1.12      nicm      150:        char *tagfile = NULL;
1.4       millert   151:
                    152:        while (n-- > 0)
1.15      nicm      153:                tagfile = nextctag();
1.12      nicm      154:        return (tagfile);
1.4       millert   155: }
                    156:
                    157: /*
                    158:  * Go to the previous tag.
                    159:  */
1.12      nicm      160: char *
                    161: prevtag(int n)
1.4       millert   162: {
1.12      nicm      163:        char *tagfile = NULL;
1.4       millert   164:
                    165:        while (n-- > 0)
1.15      nicm      166:                tagfile = prevctag();
1.12      nicm      167:        return (tagfile);
1.4       millert   168: }
                    169:
                    170: /*
                    171:  * Return the total number of tags.
                    172:  */
1.12      nicm      173: int
                    174: ntags(void)
1.4       millert   175: {
1.12      nicm      176:        return (total);
1.4       millert   177: }
                    178:
                    179: /*
                    180:  * Return the sequence number of current tag.
                    181:  */
1.12      nicm      182: int
                    183: curr_tag(void)
1.4       millert   184: {
1.12      nicm      185:        return (curseq);
1.4       millert   186: }
                    187:
1.12      nicm      188: /*
1.4       millert   189:  * Find tags in the "tags" file.
                    190:  * Sets curtag to the first tag entry.
                    191:  */
1.12      nicm      192: static enum tag_result
                    193: findctag(char *tag)
1.1       etheisen  194: {
                    195:        char *p;
1.12      nicm      196:        FILE *f;
                    197:        int taglen;
1.17      mmcc      198:        off_t taglinenum;
1.4       millert   199:        char *tagfile;
                    200:        char *tagpattern;
                    201:        int tagendline;
1.1       etheisen  202:        int search_char;
                    203:        int err;
1.4       millert   204:        char tline[TAGLINE_SIZE];
                    205:        struct tag *tp;
1.1       etheisen  206:
1.4       millert   207:        p = shell_unquote(tags);
                    208:        f = fopen(p, "r");
                    209:        free(p);
                    210:        if (f == NULL)
1.12      nicm      211:                return (TAG_NOFILE);
1.1       etheisen  212:
1.4       millert   213:        cleantags();
                    214:        total = 0;
1.1       etheisen  215:        taglen = strlen(tag);
                    216:
                    217:        /*
                    218:         * Search the tags file for the desired tag.
                    219:         */
1.12      nicm      220:        while (fgets(tline, sizeof (tline), f) != NULL) {
1.4       millert   221:                if (tline[0] == '!')
                    222:                        /* Skip header of extended format. */
                    223:                        continue;
1.1       etheisen  224:                if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
                    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.
1.12      nicm      231:                 * The location is either a decimal line number,
1.1       etheisen  232:                 * or a search pattern surrounded by a pair of delimiters.
                    233:                 * Parse the line and extract these parts.
                    234:                 */
1.4       millert   235:                tagpattern = NULL;
1.1       etheisen  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 != '\0')
                    251:                        p++;
                    252:                *p++ = '\0';
                    253:                p = skipsp(p);
                    254:                if (*p == '\0')
                    255:                        /* Pattern is missing! */
                    256:                        continue;
                    257:
                    258:                /*
1.12      nicm      259:                 * First see if it is a line number.
1.1       etheisen  260:                 */
1.4       millert   261:                tagendline = 0;
1.1       etheisen  262:                taglinenum = getnum(&p, 0, &err);
1.12      nicm      263:                if (err) {
1.1       etheisen  264:                        /*
                    265:                         * No, it must be a pattern.
1.12      nicm      266:                         * Delete the initial "^" (if present) and
1.1       etheisen  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++;
1.4       millert   274:                        tagpattern = p;
1.12      nicm      275:                        while (*p != search_char && *p != '\0') {
1.1       etheisen  276:                                if (*p == '\\')
                    277:                                        p++;
1.4       millert   278:                                p++;
1.1       etheisen  279:                        }
1.4       millert   280:                        tagendline = (p[-1] == '$');
                    281:                        if (tagendline)
                    282:                                p--;
                    283:                        *p = '\0';
1.1       etheisen  284:                }
1.12      nicm      285:                tp = maketagent(tagfile, taglinenum, tagpattern, tagendline);
1.4       millert   286:                TAG_INS(tp);
                    287:                total++;
1.1       etheisen  288:        }
                    289:        fclose(f);
1.4       millert   290:        if (total == 0)
1.12      nicm      291:                return (TAG_NOTAG);
1.4       millert   292:        curtag = taglist.tl_first;
                    293:        curseq = 1;
1.12      nicm      294:        return (TAG_FOUND);
1.4       millert   295: }
                    296:
                    297: /*
                    298:  * Edit current tagged file.
                    299:  */
1.12      nicm      300: int
                    301: edit_tagfile(void)
1.4       millert   302: {
                    303:        if (curtag == NULL)
                    304:                return (1);
                    305:        return (edit(curtag->tag_file));
1.1       etheisen  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:
1.12      nicm      314:  *     regcmp vs. re_comp) behave differently in the presence of
1.1       etheisen  315:  *     parentheses (which are almost always found in a tag).
                    316:  */
1.12      nicm      317: static off_t
                    318: ctagsearch(void)
1.1       etheisen  319: {
1.12      nicm      320:        off_t pos, linepos;
1.17      mmcc      321:        off_t linenum;
1.4       millert   322:        int len;
1.1       etheisen  323:        char *line;
                    324:
                    325:        pos = ch_zero();
                    326:        linenum = find_linenum(pos);
                    327:
1.12      nicm      328:        for (;;) {
1.1       etheisen  329:                /*
1.12      nicm      330:                 * Get lines until we find a matching one or
1.1       etheisen  331:                 * until we hit end-of-file.
                    332:                 */
1.20    ! deraadt   333:                if (abort_sigs())
1.12      nicm      334:                        return (-1);
1.1       etheisen  335:
                    336:                /*
1.12      nicm      337:                 * Read the next line, and save the
1.1       etheisen  338:                 * starting position of that line in linepos.
                    339:                 */
                    340:                linepos = pos;
1.9       shadchin  341:                pos = forw_raw_line(pos, &line, (int *)NULL);
1.1       etheisen  342:                if (linenum != 0)
                    343:                        linenum++;
                    344:
1.12      nicm      345:                if (pos == -1) {
1.1       etheisen  346:                        /*
                    347:                         * We hit EOF without a match.
                    348:                         */
1.16      deraadt   349:                        error("Tag not found", NULL);
1.12      nicm      350:                        return (-1);
1.1       etheisen  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.
1.4       millert   365:                 * If tagendline is set, make sure we match all
                    366:                 * the way to end of line (no extra chars after the match).
1.1       etheisen  367:                 */
1.4       millert   368:                len = strlen(curtag->tag_pattern);
                    369:                if (strncmp(curtag->tag_pattern, line, len) == 0 &&
1.12      nicm      370:                    (!curtag->tag_endline || line[len] == '\0' ||
                    371:                    line[len] == '\r')) {
1.4       millert   372:                        curtag->tag_linenum = find_linenum(linepos);
1.1       etheisen  373:                        break;
1.4       millert   374:                }
1.1       etheisen  375:        }
                    376:
                    377:        return (linepos);
                    378: }
                    379:
1.4       millert   380: static int circular = 0;       /* 1: circular tag structure */
                    381:
                    382: /*
1.15      nicm      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
1.4       millert   385:  * appropriate tag.
                    386:  */
1.12      nicm      387: static char *
1.15      nicm      388: nextctag(void)
1.4       millert   389: {
                    390:        struct tag *tp;
                    391:
                    392:        if (curtag == NULL)
                    393:                /* No tag loaded */
1.12      nicm      394:                return (NULL);
1.4       millert   395:
                    396:        tp = curtag->next;
1.12      nicm      397:        if (tp == TAG_END) {
1.4       millert   398:                if (!circular)
1.12      nicm      399:                        return (NULL);
1.4       millert   400:                /* Wrapped around to the head of the queue */
                    401:                curtag = taglist.tl_first;
                    402:                curseq = 1;
1.12      nicm      403:        } else {
1.4       millert   404:                curtag = tp;
                    405:                curseq++;
                    406:        }
                    407:        return (curtag->tag_file);
                    408: }
                    409:
                    410: /*
1.15      nicm      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
1.4       millert   413:  * at the appropriate tag.
                    414:  */
1.12      nicm      415: static char *
1.15      nicm      416: prevctag(void)
1.4       millert   417: {
                    418:        struct tag *tp;
                    419:
                    420:        if (curtag == NULL)
                    421:                /* No tag loaded */
1.12      nicm      422:                return (NULL);
1.4       millert   423:
                    424:        tp = curtag->prev;
1.12      nicm      425:        if (tp == TAG_END) {
1.4       millert   426:                if (!circular)
1.12      nicm      427:                        return (NULL);
1.4       millert   428:                /* Wrapped around to the tail of the queue */
                    429:                curtag = taglist.tl_last;
                    430:                curseq = total;
1.12      nicm      431:        } else {
1.4       millert   432:                curtag = tp;
                    433:                curseq--;
                    434:        }
                    435:        return (curtag->tag_file);
                    436: }