/* * 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #ifdef STDC_HEADERS #include #endif #ifdef HAVE_STRING_H #include #else #include #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); }