## root/libutil/dbop.c

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

## DEFINITIONS

This source file includes following definitions.

1. dbop_open
2. dbop_get
3. dbop_put
4. dbop_put_withlen
5. dbop_delete
6. dbop_update
7. dbop_first
8. dbop_next
9. dbop_unread
10. dbop_lastdat
11. dbop_getflag
12. dbop_getoption
13. dbop_putoption
14. dbop_getversion
15. dbop_putversion
16. dbop_close

    /*
     * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 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 <sys/types.h>
    #include <sys/stat.h>

    #include <assert.h>
    #include <ctype.h>
    #include <fcntl.h>
    #ifdef STDC_HEADERS
    #include <stdlib.h>
    #endif
    #ifdef HAVE_STRING_H
    #include <string.h>
    #else
    #include <strings.h>
    #endif
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif

    #include "char.h"
    #include "checkalloc.h"
    #include "dbop.h"
    #include "die.h"
    #include "locatestring.h"
    #include "strbuf.h"
    #include "strlimcpy.h"
    #include "test.h"

    int print_statistics = 0;

    #define ismeta(p)       (*((char *)(p)) == ' ')

    /*
     * dbop_open: open db database.
     *
     *      i)      path    database name
     *      i)      mode    0: read only, 1: create, 2: modify
     *      i)      perm    file permission
     *      i)      flags
     *                      DBOP_DUP: allow duplicate records.
     *                      DBOP_REMOVE: remove on closed.
     *      r)              descripter for dbop_xxx()
     */
    DBOP *
    dbop_open(const char *path, int mode, int perm, int flags)
    {
            DB *db;
            int rw = 0;
            DBOP *dbop;
            BTREEINFO info;

            /*
             * setup arguments.
             */
            switch (mode) {
            case 0:
                    rw = O_RDONLY;
                    break;
            case 1:
                    rw = O_RDWR|O_CREAT|O_TRUNC;
                    break;
            case 2:
                    rw = O_RDWR;
                    break;
            default:
                    assert(0);
            }
            memset(&info, 0, sizeof(info));
            if (flags & DBOP_DUP)
                    info.flags |= R_DUP;
            info.psize = DBOP_PAGESIZE;
            /*
             * Decide cache size. The default value is 5MB.
             * See libutil/gparam.h for the details.
             */
            info.cachesize = GTAGSCACHE;
            if (getenv("GTAGSCACHE") != NULL)
                    info.cachesize = atoi(getenv("GTAGSCACHE"));
            if (info.cachesize < GTAGSMINCACHE)
                    info.cachesize = GTAGSMINCACHE;

            /*
             * if unlink do job normally, those who already open tag file can use
             * it until closing.
             */
            if (path != NULL && mode == 1 && test("f", path))
                    (void)unlink(path);
            db = dbopen(path, rw, 0600, DB_BTREE, &info);
            if (!db)
                    return NULL;
            dbop = (DBOP *)check_calloc(sizeof(DBOP), 1);
            if (path == NULL)
                    dbop->dbname[0] = '\0';
            else
                    strlimcpy(dbop->dbname, path, sizeof(dbop->dbname));
            dbop->db        = db;
            dbop->openflags = flags;
            dbop->perm      = (mode == 1) ? perm : 0;
            dbop->lastdat   = NULL;
            dbop->lastsize  = 0;

            return dbop;
    }
    /*
     * dbop_get: get data by a key.
     *
     *      i)      dbop    descripter
     *      i)      name    name
     *      r)              pointer to data
     */
    const char *
    dbop_get(DBOP *dbop, const char *name)
    {
            DB *db = dbop->db;
            DBT key, dat;
            int status;

            key.data = (char *)name;
            key.size = strlen(name)+1;

            status = (*db->get)(db, &key, &dat, 0);
            dbop->lastdat = (char *)dat.data;
            dbop->lastsize = dat.size;
            switch (status) {
            case RET_SUCCESS:
                    break;
            case RET_ERROR:
                    die("cannot read from database.");
            case RET_SPECIAL:
                    return (NULL);
            }
            return (dat.data);
    }
    /*
     * dbop_put: put data by a key.
     *
     *      i)      dbop    descripter
     *      i)      name    key
     *      i)      data    data
     */
    void
    dbop_put(DBOP *dbop, const char *name, const char *data)
    {
            DB *db = dbop->db;
            DBT key, dat;
            int status;
            int len;

            if (!(len = strlen(name)))
                    die("primary key size == 0.");
            if (len > MAXKEYLEN)
                    die("primary key too long.");
            key.data = (char *)name;
            key.size = strlen(name)+1;
            dat.data = (char *)data;
            dat.size = strlen(data)+1;

            status = (*db->put)(db, &key, &dat, 0);
            switch (status) {
            case RET_SUCCESS:
                    break;
            case RET_ERROR:
            case RET_SPECIAL:
                    die("cannot write to database.");
            }
    }
    /*
     * dbop_put_withlen: put data by a key.
     *
     *      i)      dbop    descripter
     *      i)      name    key
     *      i)      data    data
     *      i)      length  length of data
     */
    void
    dbop_put_withlen(DBOP *dbop, const char *name, const char *data, int length)
    {
            DB *db = dbop->db;
            DBT key, dat;
            int status;
            int len;

            if (!(len = strlen(name)))
                    die("primary key size == 0.");
            if (len > MAXKEYLEN)
                    die("primary key too long.");
            key.data = (char *)name;
            key.size = strlen(name)+1;
            dat.data = (char *)data;
            dat.size = length;

            status = (*db->put)(db, &key, &dat, 0);
            switch (status) {
            case RET_SUCCESS:
                    break;
            case RET_ERROR:
            case RET_SPECIAL:
                    die("cannot write to database.");
            }
    }
    /*
     * dbop_delete: delete record by path name.
     *
     *      i)      dbop    descripter
     *      i)      path    path name
     */
    void
    dbop_delete(DBOP *dbop, const char *path)
    {
            DB *db = dbop->db;
            DBT key;
            int status;

            if (path) {
                    key.data = (char *)path;
                    key.size = strlen(path)+1;
                    status = (*db->del)(db, &key, 0);
            } else
                    status = (*db->del)(db, &key, R_CURSOR);
            if (status == RET_ERROR)
                    die("cannot delete record.");
    }
    /*
     * dbop_update: update record.
     *
     *      i)      dbop    descripter
     *      i)      key     key
     *      i)      dat     data
     */
    void
    dbop_update(DBOP *dbop, const char *key, const char *dat)
    {
            dbop_put(dbop, key, dat);
    }
    /*
     * dbop_first: get first record.
     *
     *      i)      dbop    dbop descripter
     *      i)      name    key value or prefix
     *                      !=NULL: indexed read by key
     *                      ==NULL: sequential read
     *      i)      preg    compiled regular expression if any.
     *      i)      flags   following dbop_next call take over this.
     *                      DBOP_KEY        read key part
     *                      DBOP_PREFIX     prefix read
     *                                      only valied when sequential read
     *      r)              data
     */
    const char *
    dbop_first(DBOP *dbop, const char *name, regex_t *preg, int flags)
    {
            DB *db = dbop->db;
            DBT key, dat;
            int status;

            dbop->preg = preg;
            if (flags & DBOP_PREFIX && !name)
                    flags &= ~DBOP_PREFIX;
            if (name) {
                    if (strlen(name) > MAXKEYLEN)
                            die("primary key too long.");
                    strlimcpy(dbop->key, name, sizeof(dbop->key));
                    key.data = (char *)name;
                    key.size = strlen(name);
                    /*
                     * includes NULL character unless prefix read.
                     */
                    if (!(flags & DBOP_PREFIX))
                            key.size++;
                    dbop->keylen = key.size;
                    for (status = (*db->seq)(db, &key, &dat, R_CURSOR);
                            status == RET_SUCCESS;
                            status = (*db->seq)(db, &key, &dat, R_NEXT)) {
                            if (flags & DBOP_PREFIX) {
                                    if (strncmp((char *)key.data, dbop->key, dbop->keylen))
                                            return NULL;
                            } else {
                                    if (strcmp((char *)key.data, dbop->key))
                                            return NULL;
                            }
                            if (preg && regexec(preg, (char *)key.data, 0, 0, 0) != 0)
                                    continue;
                            break;
                    }
            } else {
                    dbop->keylen = dbop->key[0] = 0;
                    for (status = (*db->seq)(db, &key, &dat, R_FIRST);
                            status == RET_SUCCESS;
                            status = (*db->seq)(db, &key, &dat, R_NEXT)) {
                            /* skip meta records */
                            if (ismeta(key.data))
                                    continue;
                            if (preg && regexec(preg, (char *)key.data, 0, 0, 0) != 0)
                                    continue;
                            break;
                    }
            }
            dbop->lastdat = (char *)dat.data;
            dbop->lastsize = dat.size;
            dbop->lastkey = (char *)key.data;
            dbop->lastkeysize = key.size;
            switch (status) {
            case RET_SUCCESS:
                    break;
            case RET_ERROR:
                    die("dbop_first failed.");
            case RET_SPECIAL:
                    return (NULL);
            }
            dbop->ioflags = flags;
            if (flags & DBOP_KEY) {
                    strlimcpy(dbop->prev, (char *)key.data, sizeof(dbop->prev));
                    return (char *)key.data;
            }
            return ((char *)dat.data);
    }
    /*
     * dbop_next: get next record.
     *
     *      i)      dbop    dbop descripter
     *      r)              data
     *
     * Db_next always skip meta records.
     */
    const char *
    dbop_next(DBOP *dbop)
    {
            DB *db = dbop->db;
            int flags = dbop->ioflags;
            DBT key, dat;
            int status;

            if (dbop->unread) {
                    dbop->unread = 0;
                    return dbop->lastdat;
            }
            while ((status = (*db->seq)(db, &key, &dat, R_NEXT)) == RET_SUCCESS) {
                    assert(dat.data != NULL);
                    /* skip meta records */
                    if (flags & DBOP_KEY && ismeta(key.data))
                            continue;
                    else if (ismeta(dat.data))
                            continue;
                    if (flags & DBOP_KEY) {
                            if (!strcmp(dbop->prev, (char *)key.data))
                                    continue;
                            if (strlen((char *)key.data) > MAXKEYLEN)
                                    die("primary key too long.");
                            strlimcpy(dbop->prev, (char *)key.data, sizeof(dbop->prev));
                    }
                    dbop->lastdat   = (char *)dat.data;
                    dbop->lastsize  = dat.size;
                    dbop->lastkey = (char *)key.data;
                    dbop->lastkeysize = key.size;
                    if (flags & DBOP_PREFIX) {
                            if (strncmp((char *)key.data, dbop->key, dbop->keylen))
                                    return NULL;
                    } else if (dbop->keylen) {
                            if (strcmp((char *)key.data, dbop->key))
                                    return NULL;
                    }
                    if (dbop->preg && regexec(dbop->preg, (char *)key.data, 0, 0, 0) != 0)
                            continue;
                    return (flags & DBOP_KEY) ? (char *)key.data : (char *)dat.data;
            }
            if (status == RET_ERROR)
                    die("dbop_next failed.");
            return NULL;
    }
    /*
     * dbop_unread: unread record to read again.
     *
     *      i)      dbop    dbop descripter
     *
     * Dbop_next will read this record later.
     */
    void
    dbop_unread(DBOP *dbop)
    {
            dbop->unread = 1;
    }
    /*
     * dbop_lastdat: get last data
     *
     *      i)      dbop    dbop descripter
     *      r)              last data
     */
    const char *
    dbop_lastdat(DBOP *dbop, int *size)
    {
            if (size)
                    *size = dbop->lastsize;
            return dbop->lastdat;
    }
    /*
     * get_flag: get flag value
     */
    const char *
    dbop_getflag(DBOP *dbop)
    {
            int size;
            const char *dat = dbop_lastdat(dbop, &size);
            const char *flag = "";
            /*
             * Dat format is like follows.
             * dat 'xxxxxxx\0ffff\0'
             *      (data)   (flag)
             */
            if (dat) {
                    int i = strlen(dat) + 1;
                    if (size > i)
                            flag = dat + i;
            }
            return flag;
    }
    /*
     * dbop_getoption: get option
     */
    const char *
    dbop_getoption(DBOP *dbop, const char *key)
    {
            static char buf[1024];
            const char *p;

            if ((p = dbop_get(dbop, key)) == NULL)
                    return NULL;
            if (dbop->lastsize <= strlen(key))
                    die("illegal format (dbop_getoption).");
            for (p += strlen(key); *p && isspace((unsigned char)*p); p++)
                    ;
            strlimcpy(buf, p, sizeof(buf));
            return buf;
    }
    /*
     * dbop_putoption: put option
     */
    void
    dbop_putoption(DBOP *dbop, const char *key, const char *string)
    {
            char buf[1024];

            if (string)
                    snprintf(buf, sizeof(buf), "%s %s", key, string);
            else
                    snprintf(buf, sizeof(buf), "%s", key);
            dbop_put(dbop, key, buf);
    }
    /*
     * dbop_getversion: get format version
     */
    int
    dbop_getversion(DBOP *dbop)
    {
            int format_version = 1;                 /* default format version */
            const char *p;

            if ((p = dbop_getoption(dbop, VERSIONKEY)) != NULL)
                    format_version = atoi(p);
            return format_version;
    }
    /*
     * dbop_putversion: put format version
     */
    void
    dbop_putversion(DBOP *dbop, int version)
    {
            char number[32];

            snprintf(number, sizeof(number), "%d", version);
            dbop_putoption(dbop, VERSIONKEY, number);
    }
    /*
     * dbop_close: close db
     *
     *      i)      dbop    dbop descripter
     */
    void
    dbop_close(DBOP *dbop)
    {
            DB *db = dbop->db;

    #ifdef USE_DB185_COMPAT
            (void)db->close(db);
    #else
            /*
             * If DBOP_REMOVE is specified, omit writing to the disk in __bt_close().
             */
            (void)db->close(db, (dbop->openflags & DBOP_REMOVE || dbop->dbname[0] == '\0') ? 1 : 0);
    #endif
            if (dbop->dbname[0] != '\0') {
                    if (dbop->openflags & DBOP_REMOVE)
                            (void)unlink(dbop->dbname);
                    else if (dbop->perm && chmod(dbop->dbname, dbop->perm) < 0)
                            die("cannot change file mode.");
            }
            (void)free(dbop);
    }

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