## root/libutil/gpathop.c

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

## DEFINITIONS

This source file includes following definitions.

1. gpath_open
2. gpath_put
3. gpath_path2fid
4. gpath_fid2path
5. gpath_delete
6. gpath_nextkey
7. gpath_close
8. gfind_open
9. gfind_read
10. gfind_close

    /*
     * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006
     *      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 <assert.h>
    #ifdef STDC_HEADERS
    #include <stdlib.h>
    #endif
    #ifdef HAVE_STRING_H
    #include <string.h>
    #else
    #include <strings.h>
    #endif

    #include "checkalloc.h"
    #include "die.h"
    #include "dbop.h"
    #include "gtagsop.h"
    #include "makepath.h"
    #include "gpathop.h"
    #include "strbuf.h"
    #include "strlimcpy.h"

    static DBOP *dbop;
    static int _nextkey;
    static int _mode;
    static int opened;
    static int created;

    /*
     * GPATH format version
     *
     * 1. Gtags(1) bury version number in GPATH.
     * 2. Global(1) pick up the version number from GPATH. If the number
     *    is not acceptable version number then global give up work any more
     *    and display error message.
     * 3. If version number is not found then it assumes version 1.
     * 4. GPATH version is independent with the other tag files.
     *
     * [History of format version]
     *
     * GLOBAL-4.8.7         no idea about format version.
     * GLOBAL-5.0           understand format version.
     *                      support format version 2.
     *
     * - Format version 1
     *
     * GPATH has only source files.
     *
     *      key             data
     *      --------------------
     *      ./aaa.c\0       11\0
     *
     * - Format version 2
     *
     * GPATH has not only source files but also other files like README.
     * You can distinguish them by the flag following data value.
     * At present, the flag value is only 'o'(other files).
     *
     *      key             data
     *      --------------------
     *      ./aaa.c\0       11\0
     *      ./README\0      12\0o\0         <=== 'o' means other files.
     */
    static int support_version = 2; /* acceptable format version   */
    static int create_version = 2;  /* format version of newly created tag file */
    /*
     * gpath_open: open gpath tag file
     *
     *      i)      dbpath  GTAGSDBPATH
     *      i)      mode    0: read only
     *                      1: create
     *                      2: modify
     *      r)              0: normal
     *                      -1: error
     */
    int
    gpath_open(const char *dbpath, int mode)
    {
            if (opened > 0) {
                    if (mode != _mode)
                            die("duplicate open with different mode.");
                    opened++;
                    return 0;
            }
            /*
             * We create GPATH just first time.
             */
            _mode = mode;
            if (mode == 1 && created)
                    mode = 0;
            dbop = dbop_open(makepath(dbpath, dbname(GPATH), NULL), mode, 0644, 0);
            if (dbop == NULL)
                    return -1;
            if (mode == 1) {
                    dbop_putversion(dbop, create_version);
                    _nextkey = 1;

            } else {
                    int format_version;
                    const char *path = dbop_get(dbop, NEXTKEY);

                    if (path == NULL)
                            die("nextkey not found in GPATH.");
                    _nextkey = atoi(path);
                    format_version = dbop_getversion(dbop);
                    if (format_version > support_version)
                            die("GPATH seems new format. Please install the latest GLOBAL.");
                    else if (format_version < support_version)
                            die("GPATH seems older format. Please remake tag files.");
            }
            opened++;
            return 0;
    }
    /*
     * gpath_put: put path name
     *
     *      i)      path    path name
     *      i)      type    path type
     *                      GPATH_SOURCE: source file
     *                      GPATH_OTHER: other file
     */
    void
    gpath_put(const char *path, int type)
    {
            char fid[32];
            STATIC_STRBUF(sb);

            assert(opened > 0);
            if (_mode == 1 && created)
                    return;
            if (dbop_get(dbop, path) != NULL)
                    return;
            /*
             * generate new file id for the path.
             */
            snprintf(fid, sizeof(fid), "%d", _nextkey++);
            /*
             * path => fid mapping.
             */
            strbuf_clear(sb);
            strbuf_puts0(sb, fid);
            if (type == GPATH_OTHER)
                    strbuf_puts0(sb, "o");
            dbop_put_withlen(dbop, path, strbuf_value(sb), strbuf_getlen(sb));
            /*
             * fid => path mapping.
             */
            strbuf_clear(sb);
            strbuf_puts0(sb, path);
            if (type == GPATH_OTHER)
                    strbuf_puts0(sb, "o");
            dbop_put_withlen(dbop, fid, strbuf_value(sb), strbuf_getlen(sb));
    }
    /*
     * gpath_path2fid: convert path into id
     *
     *      i)      path    path name
     *      o)      type    path type
     *                      GPATH_SOURCE: source file
     *                      GPATH_OTHER: other file
     *      r)              file id
     */
    const char *
    gpath_path2fid(const char *path, int *type)
    {
            const char *fid = dbop_get(dbop, path);
            assert(opened > 0);
            if (fid && type) {
                    const char *flag = dbop_getflag(dbop);
                    *type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE;

            }
            return fid;
    }
    /*
     * gpath_fid2path: convert id into path
     *
     *      i)      fid     file id
     *      o)      type    path type
     *                      GPATH_SOURCE: source file
     *                      GPATH_OTHER: other file
     *      r)              path name
     */
    const char *
    gpath_fid2path(const char *fid, int *type)
    {
            const char *path = dbop_get(dbop, fid);
            assert(opened > 0);
            if (path && type) {
                    const char *flag = dbop_getflag(dbop);
                    *type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE;
            }
            return path;
    }
    /*
     * gpath_delete: delete specified path record
     *
     *      i)      path    path name
     */
    void
    gpath_delete(const char *path)
    {
            const char *fid;

            assert(opened > 0);
            assert(_mode == 2);
            assert(path[0] == '.' && path[1] == '/');
            fid = dbop_get(dbop, path);
            if (fid == NULL)
                    return;
            dbop_delete(dbop, fid);
            dbop_delete(dbop, path);
    }
    /*
     * gpath_nextkey: return next key
     *
     *      r)              next id
     */
    int
    gpath_nextkey(void)
    {
            assert(_mode != 1);
            return _nextkey;
    }
    /*
     * gpath_close: close gpath tag file
     */
    void
    gpath_close(void)
    {
            char fid[32];

            assert(opened > 0);
            if (--opened > 0)
                    return;
            if (_mode == 1 && created) {
                    dbop_close(dbop);
                    return;
            }
            if (_mode == 1 || _mode == 2) {
                    snprintf(fid, sizeof(fid), "%d", _nextkey);
                    dbop_update(dbop, NEXTKEY, fid);
            }
            dbop_close(dbop);
            if (_mode == 1)
                    created = 1;
    }

    /*
     * gfind iterator using GPATH.
     *
     * gfind_xxx() does almost same with find_xxx() but much faster,
     * because gfind_xxx() use GPATH (file index).
     * If GPATH exist then you should use this.
     */

    /*
     * gfind_open: start iterator using GPATH.
     *
     *      i)      dbpath  dbpath
     *      i)      local   local prefix
     *                      if NULL specified, it assumes "./";
     *      i)      target  GPATH_SOURCE: only source file
     *                      GPATH_OTHER: only other file
     *                      GPATH_BOTH: source file + other file
     *      r)              GFIND structure
     */
    GFIND *
    gfind_open(const char *dbpath, const char *local, int target)
    {
            GFIND *gfind = (GFIND *)check_calloc(sizeof(GFIND), 1);

            gfind->dbop = dbop_open(makepath(dbpath, dbname(GPATH), NULL), 0, 0, 0);
            if (gfind->dbop == NULL)
                    die("GPATH not found.");
            gfind->path = NULL;
            gfind->prefix = check_strdup(local ? local : "./");
            gfind->first = 1;
            gfind->eod = 0;
            gfind->target = target;
            gfind->type = GPATH_SOURCE;
            gfind->version = dbop_getversion(gfind->dbop);
            if (gfind->version > support_version)
                    die("GPATH seems new format. Please install the latest GLOBAL.");
            else if (gfind->version < support_version)
                    die("GPATH seems older format. Please remake tag files.");
            return gfind;
    }
    /*
     * gfind_read: read path using GPATH.
     *
     *      i)      gfind   GFIND structure
     *      r)              path
     */
    const char *
    gfind_read(GFIND *gfind)
    {
            const char *flag;

            gfind->type = GPATH_SOURCE;
            if (gfind->eod)
                    return NULL;
            for (;;) {
                    if (gfind->first) {
                            gfind->first = 0;
                            gfind->path = dbop_first(gfind->dbop, gfind->prefix, NULL, DBOP_KEY | DBOP_PREFIX);
                    } else {
                            gfind->path = dbop_next(gfind->dbop);
                    }
                    if (gfind->path == NULL) {
                            gfind->eod = 1;
                            break;
                    }
                    /*
                     * if gfind->target == 0, return only source files.
                     * *flag == 'o' means 'other files' like README.
                     */
                    flag = dbop_getflag(gfind->dbop);
                    gfind->type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE;
                    if (gfind->type & gfind->target)
                            break;
            }
            return gfind->path;
    }
    /*
     * gfind_close: close iterator.
     */
    void
    gfind_close(GFIND *gfind)
    {
            dbop_close(gfind->dbop);
            free((void *)gfind->prefix);
            free(gfind);
    }

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