/* */
This source file includes following definitions.
- gpath_open
- gpath_put
- gpath_path2fid
- gpath_fid2path
- gpath_delete
- gpath_nextkey
- gpath_close
- gfind_open
- gfind_read
- gfind_close
1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006
3 * Tama Communications Corporation
4 *
5 * This file is part of GNU GLOBAL.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <assert.h>
25 #ifdef STDC_HEADERS
26 #include <stdlib.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #include <strings.h>
32 #endif
33
34 #include "checkalloc.h"
35 #include "die.h"
36 #include "dbop.h"
37 #include "gtagsop.h"
38 #include "makepath.h"
39 #include "gpathop.h"
40 #include "strbuf.h"
41 #include "strlimcpy.h"
42
43 static DBOP *dbop;
44 static int _nextkey;
45 static int _mode;
46 static int opened;
47 static int created;
48
49 /**
50 * @file
51 * @NAME{GPATH} format version
52 *
53 * -# @XREF{gtags,1} bury version number in #GPATH.
54 * -# @XREF{global,1} pick up the version number from #GPATH. If the number
55 * is not acceptable version number then @NAME{global} give up work any more
56 * and display error message. <br>
57 * -# If version number is not found then it assumes version 1.
58 * -# @NAME{GPATH} version is independent with the other tag files.
59 *
60 * @par [History of format version]
61 *
62 * @NAME{GLOBAL-4.8.7} no idea about format version. <br>
63 * @NAME{GLOBAL-5.0} understand format version. <br>
64 * support format version 2.
65 *
66 * - Format version 1
67 * @par
68 *
69 * #GPATH has only source files.
70 *
71 * @par
72 * @code{.txt}
73 * key data
74 * --------------------
75 * ./aaa.c\0 11\0
76 * @endcode
77 *
78 * - Format version 2
79 * @par
80 *
81 * #GPATH has not only source files but also other files like @FILE{README}.
82 * You can distinguish them by the flag following data value.
83 * At present, the flag value is only @CODE{'o'} (other files).
84 *
85 * @par
86 * @code{.txt}
87 * key data
88 * --------------------
89 * ./aaa.c\0 11\0
90 * ./README\0 12\0o\0 <=== 'o' means other files.
91 * @endcode
92 */
93 static int support_version = 2; /**< acceptable format version */
94 static int create_version = 2; /**< format version of newly created tag file */
95 /**
96 * gpath_open: open gpath tag file
97 *
98 * @param[in] dbpath @VAR{GTAGSDBPATH}
99 * @param[in] mode 0: read only <br>
100 * 1: create <br>
101 * 2: modify
102 * @return 0: normal <br>
103 * -1: error
104 */
105 int
106 gpath_open(const char *dbpath, int mode)
107 {
108 if (opened > 0) {
109 if (mode != _mode)
110 die("duplicate open with different mode.");
111 opened++;
112 return 0;
113 }
114 /*
115 * We create GPATH just first time.
116 */
117 _mode = mode;
118 if (mode == 1 && created)
119 mode = 0;
120 dbop = dbop_open(makepath(dbpath, dbname(GPATH), NULL), mode, 0644, 0);
121 if (dbop == NULL)
122 return -1;
123 if (mode == 1) {
124 dbop_putversion(dbop, create_version);
125 _nextkey = 1;
126
127 } else {
128 int format_version;
129 const char *path = dbop_get(dbop, NEXTKEY);
130
131 if (path == NULL)
132 die("nextkey not found in GPATH.");
133 _nextkey = atoi(path);
134 format_version = dbop_getversion(dbop);
135 if (format_version > support_version)
136 die("GPATH seems new format. Please install the latest GLOBAL.");
137 else if (format_version < support_version)
138 die("GPATH seems older format. Please remake tag files.");
139 }
140 opened++;
141 return 0;
142 }
143 /**
144 * gpath_put: put path name
145 *
146 * @param[in] path path name
147 * @param[in] type path type <br>
148 * #GPATH_SOURCE: source file <br>
149 * #GPATH_OTHER: other file
150 */
151 void
152 gpath_put(const char *path, int type)
153 {
154 char fid[MAXFIDLEN];
155 STATIC_STRBUF(sb);
156
157 assert(opened > 0);
158 if (_mode == 1 && created)
159 return;
160 if (dbop_get(dbop, path) != NULL)
161 return;
162 /*
163 * generate new file id for the path.
164 */
165 snprintf(fid, sizeof(fid), "%d", _nextkey++);
166 /*
167 * path => fid mapping.
168 */
169 strbuf_clear(sb);
170 strbuf_puts0(sb, fid);
171 if (type == GPATH_OTHER)
172 strbuf_puts0(sb, "o");
173 dbop_put_withlen(dbop, path, strbuf_value(sb), strbuf_getlen(sb));
174 /*
175 * fid => path mapping.
176 */
177 strbuf_clear(sb);
178 strbuf_puts0(sb, path);
179 if (type == GPATH_OTHER)
180 strbuf_puts0(sb, "o");
181 dbop_put_withlen(dbop, fid, strbuf_value(sb), strbuf_getlen(sb));
182 }
183 /**
184 * gpath_path2fid: convert path into id
185 *
186 * @param[in] path path name
187 * @param[out] type path type <br>
188 * #GPATH_SOURCE: source file <br>
189 * #GPATH_OTHER: other file
190 * @return file id
191 */
192 const char *
193 gpath_path2fid(const char *path, int *type)
194 {
195 const char *fid = dbop_get(dbop, path);
196 assert(opened > 0);
197 if (fid && type) {
198 const char *flag = dbop_getflag(dbop);
199 *type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE;
200
201 }
202 return fid;
203 }
204 /**
205 * gpath_fid2path: convert id into path
206 *
207 * @param[in] fid file id
208 * @param[out] type path type <br>
209 * #GPATH_SOURCE: source file <br>
210 * #GPATH_OTHER: other file
211 * @return path name
212 */
213 const char *
214 gpath_fid2path(const char *fid, int *type)
215 {
216 const char *path = dbop_get(dbop, fid);
217 assert(opened > 0);
218 if (path && type) {
219 const char *flag = dbop_getflag(dbop);
220 *type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE;
221 }
222 return path;
223 }
224 /**
225 * gpath_delete: delete specified path record
226 *
227 * @param[in] path path name
228 */
229 void
230 gpath_delete(const char *path)
231 {
232 const char *fid;
233
234 assert(opened > 0);
235 assert(_mode == 2);
236 assert(path[0] == '.' && path[1] == '/');
237 fid = dbop_get(dbop, path);
238 if (fid == NULL)
239 return;
240 dbop_delete(dbop, fid);
241 dbop_delete(dbop, path);
242 }
243 /**
244 * gpath_nextkey: return next key
245 *
246 * @return next id
247 */
248 int
249 gpath_nextkey(void)
250 {
251 assert(_mode != 1);
252 return _nextkey;
253 }
254 /**
255 * gpath_close: close gpath tag file
256 */
257 void
258 gpath_close(void)
259 {
260 char fid[MAXFIDLEN];
261
262 assert(opened > 0);
263 if (--opened > 0)
264 return;
265 if (_mode == 1 && created) {
266 dbop_close(dbop);
267 return;
268 }
269 if (_mode == 1 || _mode == 2) {
270 snprintf(fid, sizeof(fid), "%d", _nextkey);
271 dbop_update(dbop, NEXTKEY, fid);
272 }
273 dbop_close(dbop);
274 if (_mode == 1)
275 created = 1;
276 }
277
278 /**
279 * @fn GFIND *gfind_open(const char *dbpath, const char *local, int target)
280 *
281 * @remark gfind iterator using #GPATH.
282 *
283 * @par
284 * @NAME{gfind_xxx()} does almost same with @NAME{find_xxx()} but much faster,
285 * because @NAME{gfind_xxx()} use #GPATH (file index). <br>
286 * If #GPATH exist then you should use this.
287 */
288
289 /**
290 * gfind_open: start iterator using #GPATH.
291 *
292 * @param[in] dbpath dbpath
293 * @param[in] local local prefix <br>
294 * if @VAR{NULL} specified, it assumes @FILE{./};
295 * @param[in] target #GPATH_SOURCE: only source file <br>
296 * #GPATH_OTHER: only other file <br>
297 * #GPATH_BOTH: source file + other file
298 * @return #GFIND structure
299 */
300 GFIND *
301 gfind_open(const char *dbpath, const char *local, int target)
302 {
303 GFIND *gfind = (GFIND *)check_calloc(sizeof(GFIND), 1);
304
305 gfind->dbop = dbop_open(makepath(dbpath, dbname(GPATH), NULL), 0, 0, 0);
306 if (gfind->dbop == NULL)
307 die("GPATH not found.");
308 gfind->path = NULL;
309 gfind->prefix = check_strdup(local ? local : "./");
310 gfind->first = 1;
311 gfind->eod = 0;
312 gfind->target = target;
313 gfind->type = GPATH_SOURCE;
314 gfind->version = dbop_getversion(gfind->dbop);
315 if (gfind->version > support_version)
316 die("GPATH seems new format. Please install the latest GLOBAL.");
317 else if (gfind->version < support_version)
318 die("GPATH seems older format. Please remake tag files.");
319 return gfind;
320 }
321 /**
322 * gfind_read: read path using #GPATH.
323 *
324 * @param[in] gfind #GFIND structure
325 * @return path
326 */
327 const char *
328 gfind_read(GFIND *gfind)
329 {
330 const char *flag;
331
332 gfind->type = GPATH_SOURCE;
333 if (gfind->eod)
334 return NULL;
335 for (;;) {
336 if (gfind->first) {
337 gfind->first = 0;
338 gfind->path = dbop_first(gfind->dbop, gfind->prefix, NULL, DBOP_KEY | DBOP_PREFIX);
339 } else {
340 gfind->path = dbop_next(gfind->dbop);
341 }
342 if (gfind->path == NULL) {
343 gfind->eod = 1;
344 break;
345 }
346 /*
347 * if gfind->target == 0, return only source files.
348 * *flag == 'o' means 'other files' like README.
349 */
350 flag = dbop_getflag(gfind->dbop);
351 gfind->type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE;
352 if (gfind->type & gfind->target)
353 break;
354 }
355 return gfind->path;
356 }
357 /**
358 * gfind_close: close iterator.
359 */
360 void
361 gfind_close(GFIND *gfind)
362 {
363 dbop_close(gfind->dbop);
364 free((void *)gfind->prefix);
365 free(gfind);
366 }
/* */