## root/htags/anchor.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. cmp
2. anchor_prepare
3. anchor_load
4. anchor_unload
5. anchor_first
6. anchor_next
7. anchor_get
8. define_line
9. anchor_getlinks
10. anchor_dump

    /*
     * Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
     *      2008    Tama Communications Corporation
     *
     * This file is part of GNU GLOBAL.
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
    #include <stdio.h>
    #ifdef STDC_HEADERS
    #include <stdlib.h>
    #endif
    #ifdef HAVE_STRING_H
    #include <string.h>
    #else
    #include <strings.h>
    #endif
    #ifdef HAVE_CTYPE_H
    #include <ctype.h>
    #endif
    #include "global.h"
    #include "anchor.h"
    #include "htags.h"

    static XARGS *anchor_input[GTAGLIM];
    static struct anchor *table;
    static VARRAY *vb;

    static struct anchor *start;
    static struct anchor *curp;
    static struct anchor *end;
    static struct anchor *CURRENT;

    /* compare routine for qsort(3) */
    static int
    cmp(const void *s1, const void *s2)
    {
            return ((struct anchor *)s1)->lineno - ((struct anchor *)s2)->lineno;
    }
    /*
     * Pointers (as lineno).
     */
    static int FIRST;
    static int LAST;
    static struct anchor *CURRENTDEF;

    /*
     * anchor_prepare: setup input stream.
     *
     *      i)      anchor_stream   file pointer of path list
     */
    void
    anchor_prepare(FILE *anchor_stream)
    {
            /*
             * Option table:
             * We use blank string as null option instead of null string("")
             * not to change the command length. This length influences
             * the number of arguments in the xargs processing.
             */
            char *options[] = {NULL, " ", "r", "s"};
            char comline[MAXFILLEN];
            int db;

            for (db = GTAGS; db < GTAGLIM; db++) {
                    anchor_input[db] = NULL;
                    /*
                     * Htags(1) should not use gtags-parser(1) directly;
                     * it should use global(1) with the -f option instead.
                     * Because gtags-parser is part of the implementation of
                     * gtags(1) and global(1), and htags is only an application
                     * program which uses global(1). If htags depends on
                     * gtags-parser, it will become difficult to change the
                     * implementation of gtags and global.
                     *
                     * Though the output of global -f is not necessarily sorted
                     * by the path, it is guaranteed that the records concerning
                     * the same file are consecutive.
                     */
                    if (gtags_exist[db] == 1) {
                            snprintf(comline, sizeof(comline), "%s -f%s --nofilter=path", global_path, options[db]);
                            anchor_input[db] = xargs_open_with_file(comline, 0, anchor_stream);
                    }
            }
    }
    /*
     * anchor_load: load anchor table
     *
     *      i)      path    path name
     */
    void
    anchor_load(const char *path)
    {
            int db;

            FIRST = LAST = 0;
            end = CURRENT = NULL;

            if (vb == NULL)
                    vb = varray_open(sizeof(struct anchor), 1000);
            else
                    varray_reset(vb);

            for (db = GTAGS; db < GTAGLIM; db++) {
                    XARGS *xp;
                    char *ctags_x;

                    if ((xp = anchor_input[db]) == NULL)
                            continue;
                    /*
                     * Read from input stream until it reaches end of file
                     * or the line of another file appears.
                     */
                    while ((ctags_x = xargs_read(xp)) != NULL) {
                            SPLIT ptable;
                            struct anchor *a;
                            int type;

                            if (split(ctags_x, 4, &ptable) < 4) {
                                    recover(&ptable);
                                    die("too small number of parts in anchor_load().\n'%s'", ctags_x);
                            }
                            if (!locatestring(ptable.part[PART_PATH].start, "./", MATCH_AT_FIRST)) {
                                    recover(&ptable);
                                    die("The output of parser is illegal.\n%s", ctags_x);
                            }
                            if (strcmp(ptable.part[PART_PATH].start, path) != 0) {
                                    recover(&ptable);
                                    xargs_unread(xp);
                                    break;
                            }
                            if (db == GTAGS) {
                                    char *p = ptable.part[PART_LINE].start;

                                    for (; *p && isspace((unsigned char)*p); p++)
                                            ;
                                    if (!*p) {
                                            recover(&ptable);
                                            die("The output of parser is illegal.\n%s", ctags_x);
                                    }
                                    /*
                                     * Function header is applied only to the anchor whoes type is 'D'.
                                     * (D: function, M: macro, T: type)
                                     */
                                    type = 'T';
                                    if (*p == '#')
                                            type = 'M';
                                    else if (locatestring(p, "typedef", MATCH_AT_FIRST))
                                            type = 'T';
                                    else if ((p = locatestring(p, ptable.part[PART_TAG].start, MATCH_FIRST)) != NULL) {
                                            /* skip a tag and the following blanks */
                                            p += strlen(ptable.part[PART_TAG].start);
                                            for (; *p && isspace((unsigned char)*p); p++)
                                                    ;
                                            if (*p == '(')
                                                    type = 'D';
                                    }
                            }  else if (db == GRTAGS)
                                    type = 'R';
                            else
                                    type = 'Y';
                            /* allocate an entry */
                            a = varray_append(vb);
                            a->lineno = atoi(ptable.part[PART_LNO].start);
                            a->type = type;
                            a->done = 0;
                            settag(a, ptable.part[PART_TAG].start);
                            recover(&ptable);
                    }
                    if (ctags_x == NULL) {
                            xargs_close(anchor_input[db]);
                            anchor_input[db] = NULL;
                    }
            }
            if (vb->length == 0) {
                    table = NULL;
            } else {
                    int i, used = vb->length;
                    /*
                     * Sort by lineno.
                     */
                    table = varray_assign(vb, 0, 0);
                    qsort(table, used, sizeof(struct anchor), cmp);
                    /*
                     * Setup some lineno.
                     */
                    for (i = 0; i < used; i++)
                            if (table[i].type == 'D')
                                    break;
                    if (i < used)
                            FIRST = table[i].lineno;
                    for (i = used - 1; i >= 0; i--)
                            if (table[i].type == 'D')
                                    break;
                    if (i >= 0)
                            LAST = table[i].lineno;
            }
            /*
             * Setup loop range.
             */
            start = table;
            curp = NULL;
            end = &table[vb->length];
            /* anchor_dump(stderr, 0);*/
    }
    /*
     * anchor_unload: unload anchor table
     */
    void
    anchor_unload(void)
    {
            struct anchor *a;

            for (a = start; a && a < end; a++) {
                    if (a->reserve) {
                            free(a->reserve);
                            a->reserve = NULL;
                    }
            }
            /* We don't free varray */
            /* varray_close(vb); */
            FIRST = LAST = 0;
            start = curp = end = NULL;
    }
    /*
     * anchor_first: return the first anchor
     */
    struct anchor *
    anchor_first(void)
    {
            if (!start || start == end)
                    return NULL;
            CURRENT = start;
            if (CURRENT->type == 'D')
                    CURRENTDEF = CURRENT;
            return CURRENT;
    }
    /*
     * anchor_next: return the next anchor
     */
    struct anchor *
    anchor_next(void)
    {
            if (!start)
                    return NULL;
            if (++CURRENT >= end)
                    return NULL;
            if (CURRENT->type == 'D')
                    CURRENTDEF = CURRENT;
            return CURRENT;
    }
    /*
     * anchor_get: return the specified anchor
     *
     *      i)      name    name of anchor
     *      i)      length  lenght of the name
     *      i)      type    ==0: not specified
     *                      !=0: D, M, T, R, Y
     */
    struct anchor *
    anchor_get(const char *name, int length, int type, int lineno)
    {
            struct anchor *p = curp ? curp : start;

            if (table == NULL)
                    return NULL;
            if (p->lineno > lineno)
                    return NULL;
            /*
             * set pointer to the top of the cluster.
             */
            for (; p < end && p->lineno < lineno; p++)
                    ;
            if (p >= end || p->lineno != lineno)
                    return NULL;
            curp = p;
            for (; p < end && p->lineno == lineno; p++)
                    if (!p->done && p->length == length && !strcmp(gettag(p), name))
                            if (!type || p->type == type)
                                    return p;
            return NULL;
    }
    /*
     * define_line: check whether or not this is a define line.
     *
     *      i)      lineno  line number
     *      go)     curp    pointer to the current cluster
     *      r)              1: definition, 0: not definition
     */
    int
    define_line(int lineno)
    {
            struct anchor *p = curp ? curp : start;

            if (table == NULL)
                    return 0;
            if (p->lineno > lineno)
                    return 0;
            /*
             * set pointer to the top of the cluster.
             */
            for (; p < end && p->lineno < lineno; p++)
                    ;
            if (p >= end || p->lineno != lineno)
                    return 0;
            curp = p;
            for (; p < end && p->lineno == lineno; p++)
                    if (p->type == 'D')
                            return 1;
            return 0;
    }
    /*
     * anchor_getlinks: return anchor link array
     *              (previous, next, first, last, top, bottom)
     */
    int *
    anchor_getlinks(int lineno)
    {
            static int ref[A_SIZE];
            int i;

            for (i = 0; i < A_SIZE; i++)
                    ref[i] = 0;
            if (lineno >= 1 && start) {
                    struct anchor *c, *p;

                    if (CURRENTDEF == NULL) {
                            for (c = start; c < end; c++)
                                    if (c->lineno == lineno && c->type == 'D')
                                            break;
                            CURRENTDEF = c;
                    } else {
                            for (c = CURRENTDEF; c >= start; c--)
                                    if (c->lineno == lineno && c->type == 'D')
                                            break;
                    }
                    for (p = c - 1; p >= start; p--)
                            if (p->type == 'D') {
                                    ref[A_PREV] = p->lineno;
                                    break;
                            }
                    for (p = c + 1; p < end; p++)
                            if (p->type == 'D') {
                                    ref[A_NEXT] = p->lineno;
                                    break;
                            }
            }
            if (FIRST > 0 && lineno != FIRST)
                    ref[A_FIRST] = FIRST;
            if (LAST > 0 && lineno != LAST)
                    ref[A_LAST] = LAST;
            if (lineno != 0)
                    ref[A_TOP] = -1;
            if (lineno != -1)
                    ref[A_BOTTOM] = -2;
            if (FIRST > 0 && FIRST == LAST) {
                    if (lineno == 0)
                            ref[A_LAST] = 0;
                    if (lineno == -1)
                            ref[A_FIRST] = 0;
            }
            return ref;
    }
    void
    anchor_dump(FILE *op, int lineno)
    {
            struct anchor *a;

            if (op == NULL)
                    op = stderr;
            for (a = start; a < end ; a++)
                    if (lineno == 0 || a->lineno == lineno)
                            fprintf(op, "%d %s(%c)\n",
                                    a->lineno, gettag(a), a->type);
    }

/* [<][>][^][v][top][bottom][index][help] */