/* */
This source file includes following definitions.
- start_sort_process
- terminate_sort_process
- start_sort_process
- terminate_sort_process
- start_sort_process
- terminate_sort_process
- dbop_open
- dbop_get
- dbop_put
- dbop_put_withlen
- dbop_delete
- dbop_update
- dbop_first
- dbop_next
- dbop_unread
- dbop_lastdat
- dbop_getflag
- dbop_getoption
- dbop_putoption
- dbop_getversion
- dbop_putversion
- dbop_close
1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006,
3 * 2009, 2010
4 * Tama Communications Corporation
5 *
6 * This file is part of GNU GLOBAL.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <assert.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #ifdef STDC_HEADERS
31 #include <stdlib.h>
32 #endif
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #else
36 #include <strings.h>
37 #endif
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include <errno.h>
42
43 #include "char.h"
44 #include "checkalloc.h"
45 #include "dbop.h"
46 #include "die.h"
47 #include "env.h"
48 #include "locatestring.h"
49 #include "strbuf.h"
50 #include "strlimcpy.h"
51 #include "test.h"
52
53 /**
54 * Though the prefix of the key of meta record is currently only a @CODE{' '} (blank),
55 * this will be enhanced in the future.
56 */
57 #define ismeta(p) (*((char *)(p)) <= ' ')
58
59 /**
60 * Stuff for #DBOP_SORTED_WRITE
61 */
62 #define SORT_SEP '\t'
63
64 /**
65 * Two functions required for sorted writing.
66 *
67 * @fn static void start_sort_process(DBOP *dbop)
68 * (1) start_sort_process: start sort process for sorted writing
69 *
70 * @param[in] dbop DBOP descriptor
71 *
72 * @fn static void terminate_sort_process(DBOP *dbop)
73 * (2) terminate_sort_process: terminate sort process
74 *
75 * @param[in] dbop DBOP descriptor
76 */
77 static void start_sort_process(DBOP *);
78 static void terminate_sort_process(DBOP *);
79 static char *sortnotfound = "POSIX sort program not found. If available, the program will be speed up.\nPlease see ./configure --help.";
80 /*
81 * 1. DJGPP
82 */
83 #if defined(__DJGPP__)
84 /*
85 * Just ignored. DJGPP version doesn't use sorted writing.
86 */
87 static void
88 start_sort_process(DBOP *dbop) {
89 return;
90 }
91 static void
92 terminate_sort_process(DBOP *dbop) {
93 return;
94 }
95 /*
96 * 2. WIN32
97 */
98 #elif defined(_WIN32) && !defined(__CYGWIN__)
99 #define WIN32_LEAN_AND_MEAN
100 #include <windows.h>
101 /*
102 * sort is included with the binary distribution
103 */
104 static char argv[] = "sort -k 1,1";
105 static void
106 start_sort_process(DBOP *dbop) {
107 HANDLE opipe[2], ipipe[2];
108 SECURITY_ATTRIBUTES sa;
109 STARTUPINFO si;
110 PROCESS_INFORMATION pi;
111 const char* lc_all;
112 char sort[MAX_PATH];
113 char* path;
114 static int informed;
115
116 if (!strcmp(POSIX_SORT, "no"))
117 return;
118 if (informed)
119 return;
120 /*
121 * force using sort in the same directory as the program, to avoid
122 * using the Windows one
123 */
124 path = strrchr(_pgmptr, '\\');
125 sprintf(sort, "%.*s\\sort.exe", path - _pgmptr, _pgmptr);
126 if (!test("fx", sort)) {
127 warning(sortnotfound);
128 informed = 1;
129 return;
130 }
131
132 sa.nLength = sizeof(sa);
133 sa.bInheritHandle = TRUE;
134 sa.lpSecurityDescriptor = NULL;
135 if (!CreatePipe(&opipe[0], &opipe[1], &sa, 0) ||
136 !CreatePipe(&ipipe[0], &ipipe[1], &sa, 0))
137 die("CreatePipe failed.");
138 SetHandleInformation(opipe[1], HANDLE_FLAG_INHERIT, 0);
139 SetHandleInformation(ipipe[0], HANDLE_FLAG_INHERIT, 0);
140 ZeroMemory(&si, sizeof(si));
141 si.cb = sizeof(si);
142 si.hStdInput = opipe[0];
143 si.hStdOutput = ipipe[1];
144 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
145 si.dwFlags = STARTF_USESTDHANDLES;
146 lc_all = getenv("LC_ALL");
147 if (lc_all == NULL)
148 lc_all = "";
149 set_env("LC_ALL", "C");
150 CreateProcess(sort, argv, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
151 set_env("LC_ALL", lc_all);
152 CloseHandle(opipe[0]);
153 CloseHandle(ipipe[1]);
154 CloseHandle(pi.hThread);
155 dbop->pid = pi.hProcess;
156 dbop->sortout = fdopen(_open_osfhandle((long)opipe[1], _O_WRONLY), "w");
157 dbop->sortin = fdopen(_open_osfhandle((long)ipipe[0], _O_RDONLY), "r");
158 if (dbop->sortout == NULL || dbop->sortin == NULL)
159 die("fdopen failed.");
160 }
161 static void
162 terminate_sort_process(DBOP *dbop) {
163 WaitForSingleObject(dbop->pid, INFINITE);
164 CloseHandle(dbop->pid);
165 }
166 /*
167 * 3. UNIX and CYGWIN
168 */
169 #else
170 #include <sys/wait.h>
171 /*
172 * Though it doesn't understand why, GNU sort with no option is faster
173 * than 'sort -k 1,1'. But we should use '-k 1,1' here not to rely on
174 * a specific command.
175 */
176 static char *argv[] = {
177 POSIX_SORT,
178 "-k",
179 "1,1",
180 NULL
181 };
182 static void
183 start_sort_process(DBOP *dbop) {
184 int opipe[2], ipipe[2];
185
186 if (!strcmp(POSIX_SORT, "no"))
187 return;
188 if (!test("fx", POSIX_SORT)) {
189 static int informed;
190
191 if (!informed) {
192 warning(sortnotfound);
193 informed = 1;
194 }
195 return;
196 }
197 /*
198 * Setup pipe for two way communication
199 *
200 * Parent(gtags) Child(sort)
201 * ---------------------------------------------------
202 * (dbop->sortout) opipe[1] =====> opipe[0] (stdin)
203 * (dbop->sortin) ipipe[0] <===== ipipe[1] (stdout)
204 */
205 if (pipe(opipe) < 0 || pipe(ipipe) < 0)
206 die("pipe(2) failed.");
207 dbop->pid = fork();
208 if (dbop->pid == 0) {
209 /* child process */
210 close(opipe[1]);
211 close(ipipe[0]);
212 if (dup2(opipe[0], 0) < 0 || dup2(ipipe[1], 1) < 0)
213 die("dup2(2) failed.");
214 close(opipe[0]);
215 close(ipipe[1]);
216 /*
217 * Use C locale in order to avoid the degradation of performance
218 * by internationalized sort command.
219 */
220 set_env("LC_ALL", "C");
221 execvp(POSIX_SORT, argv);
222 } else if (dbop->pid < 0)
223 die("fork(2) failed.");
224 /* parent process */
225 close(opipe[0]);
226 close(ipipe[1]);
227 fcntl(ipipe[0], F_SETFD, FD_CLOEXEC);
228 fcntl(opipe[1], F_SETFD, FD_CLOEXEC);
229 dbop->sortout = fdopen(opipe[1], "w");
230 dbop->sortin = fdopen(ipipe[0], "r");
231 if (dbop->sortout == NULL || dbop->sortin == NULL)
232 die("fdopen(3) failed.");
233 }
234 static void
235 terminate_sort_process(DBOP *dbop) {
236 while (waitpid(dbop->pid, NULL, 0) < 0 && errno == EINTR)
237 ;
238 }
239 #endif
240
241 /**
242 * dbop_open: open db database.
243 *
244 * @param[in] path database name
245 * @param[in] mode 0: read only, 1: create, 2: modify
246 * @param[in] perm file permission
247 * @param[in] flags
248 * #DBOP_DUP: allow duplicate records. <br>
249 * #DBOP_SORTED_WRITE: use sorted writing. This requires @NAME{POSIX} sort.
250 * @return descripter for @NAME{dbop_xxx()}
251 *
252 * Sorted wirting is fast because all writing is done by not insertion but addition.
253 */
254 DBOP *
255 dbop_open(const char *path, int mode, int perm, int flags)
256 {
257 DB *db;
258 int rw = 0;
259 DBOP *dbop;
260 BTREEINFO info;
261
262 /*
263 * setup arguments.
264 */
265 switch (mode) {
266 case 0:
267 rw = O_RDONLY;
268 break;
269 case 1:
270 rw = O_RDWR|O_CREAT|O_TRUNC;
271 break;
272 case 2:
273 rw = O_RDWR;
274 break;
275 default:
276 assert(0);
277 }
278 memset(&info, 0, sizeof(info));
279 if (flags & DBOP_DUP)
280 info.flags |= R_DUP;
281 info.psize = DBOP_PAGESIZE;
282 /*
283 * Decide cache size. The default value is 5MB.
284 * See libutil/gparam.h for the details.
285 */
286 info.cachesize = GTAGSCACHE;
287 if (getenv("GTAGSCACHE") != NULL)
288 info.cachesize = atoi(getenv("GTAGSCACHE"));
289 if (info.cachesize < GTAGSMINCACHE)
290 info.cachesize = GTAGSMINCACHE;
291
292 /*
293 * if unlink do job normally, those who already open tag file can use
294 * it until closing.
295 */
296 if (path != NULL && mode == 1 && test("f", path))
297 (void)unlink(path);
298 db = dbopen(path, rw, 0600, DB_BTREE, &info);
299 if (!db)
300 return NULL;
301 dbop = (DBOP *)check_calloc(sizeof(DBOP), 1);
302 if (path == NULL)
303 dbop->dbname[0] = '\0';
304 else
305 strlimcpy(dbop->dbname, path, sizeof(dbop->dbname));
306 dbop->db = db;
307 dbop->openflags = flags;
308 dbop->perm = (mode == 1) ? perm : 0;
309 dbop->lastdat = NULL;
310 dbop->lastsize = 0;
311 dbop->sortout = NULL;
312 dbop->sortin = NULL;
313 /*
314 * Setup sorted writing.
315 */
316 if (mode != 0 && dbop->openflags & DBOP_SORTED_WRITE)
317 start_sort_process(dbop);
318 return dbop;
319 }
320 /**
321 * dbop_get: get data by a key.
322 *
323 * @param[in] dbop descripter
324 * @param[in] name name
325 * @return pointer to data
326 */
327 const char *
328 dbop_get(DBOP *dbop, const char *name)
329 {
330 DB *db = dbop->db;
331 DBT key, dat;
332 int status;
333
334 key.data = (char *)name;
335 key.size = strlen(name)+1;
336
337 status = (*db->get)(db, &key, &dat, 0);
338 dbop->lastdat = (char *)dat.data;
339 dbop->lastsize = dat.size;
340 switch (status) {
341 case RET_SUCCESS:
342 break;
343 case RET_ERROR:
344 die("dbop_get failed.");
345 case RET_SPECIAL:
346 return (NULL);
347 }
348 return (dat.data);
349 }
350 /**
351 * dbop_put: put data by a key.
352 *
353 * @param[in] dbop descripter
354 * @param[in] name key
355 * @param[in] data data
356 */
357 void
358 dbop_put(DBOP *dbop, const char *name, const char *data)
359 {
360 DB *db = dbop->db;
361 DBT key, dat;
362 int status;
363 int len;
364
365 if (!(len = strlen(name)))
366 die("primary key size == 0.");
367 if (len > MAXKEYLEN)
368 die("primary key too long.");
369 /* sorted writing */
370 if (dbop->sortout != NULL) {
371 fputs(name, dbop->sortout);
372 putc(SORT_SEP, dbop->sortout);
373 fputs(data, dbop->sortout);
374 putc('\n', dbop->sortout);
375 return;
376 }
377 key.data = (char *)name;
378 key.size = strlen(name)+1;
379 dat.data = (char *)data;
380 dat.size = strlen(data)+1;
381
382 status = (*db->put)(db, &key, &dat, 0);
383 switch (status) {
384 case RET_SUCCESS:
385 break;
386 case RET_ERROR:
387 case RET_SPECIAL:
388 die(dbop->put_errmsg ? dbop->put_errmsg : "dbop_put failed.");
389 }
390 }
391 /**
392 * dbop_put_withlen: put data by a key.
393 *
394 * @param[in] dbop descripter
395 * @param[in] name key
396 * @param[in] data data
397 * @param[in] length length of @a data
398 *
399 * @note This function doesn't support sorted writing.
400 */
401 void
402 dbop_put_withlen(DBOP *dbop, const char *name, const char *data, int length)
403 {
404 DB *db = dbop->db;
405 DBT key, dat;
406 int status;
407 int len;
408
409 if (!(len = strlen(name)))
410 die("primary key size == 0.");
411 if (len > MAXKEYLEN)
412 die("primary key too long.");
413 key.data = (char *)name;
414 key.size = strlen(name)+1;
415 dat.data = (char *)data;
416 dat.size = length;
417
418 status = (*db->put)(db, &key, &dat, 0);
419 switch (status) {
420 case RET_SUCCESS:
421 break;
422 case RET_ERROR:
423 case RET_SPECIAL:
424 die(dbop->put_errmsg ? dbop->put_errmsg : "dbop_put_withlen failed.");
425 }
426 }
427 /**
428 * dbop_delete: delete record by path name.
429 *
430 * @param[in] dbop descripter
431 * @param[in] path path name
432 */
433 void
434 dbop_delete(DBOP *dbop, const char *path)
435 {
436 DB *db = dbop->db;
437 DBT key;
438 int status;
439
440 if (path) {
441 key.data = (char *)path;
442 key.size = strlen(path)+1;
443 status = (*db->del)(db, &key, 0);
444 } else
445 status = (*db->del)(db, &key, R_CURSOR);
446 if (status == RET_ERROR)
447 die("dbop_delete failed.");
448 }
449 /**
450 * dbop_update: update record.
451 *
452 * @param[in] dbop descripter
453 * @param[in] key key
454 * @param[in] dat data
455 */
456 void
457 dbop_update(DBOP *dbop, const char *key, const char *dat)
458 {
459 dbop_put(dbop, key, dat);
460 }
461 /**
462 * dbop_first: get first record.
463 *
464 * @param[in] dbop dbop descripter
465 * @param[in] name key value or prefix <br>
466 * !=NULL: indexed read by key <br>
467 * ==NULL: sequential read
468 * @param[in] preg compiled regular expression if any.
469 * @param[in] flags following dbop_next call take over this. <br>
470 * #DBOP_KEY: read key part <br>
471 * #DBOP_PREFIX: prefix read; only valied when sequential read
472 * @return data
473 */
474 const char *
475 dbop_first(DBOP *dbop, const char *name, regex_t *preg, int flags)
476 {
477 DB *db = dbop->db;
478 DBT key, dat;
479 int status;
480
481 dbop->preg = preg;
482 if (flags & DBOP_PREFIX && !name)
483 flags &= ~DBOP_PREFIX;
484 if (name) {
485 if (strlen(name) > MAXKEYLEN)
486 die("primary key too long.");
487 strlimcpy(dbop->key, name, sizeof(dbop->key));
488 key.data = (char *)name;
489 key.size = strlen(name);
490 /*
491 * includes NULL character unless prefix read.
492 */
493 if (!(flags & DBOP_PREFIX))
494 key.size++;
495 dbop->keylen = key.size;
496 for (status = (*db->seq)(db, &key, &dat, R_CURSOR);
497 status == RET_SUCCESS;
498 status = (*db->seq)(db, &key, &dat, R_NEXT)) {
499 if (flags & DBOP_PREFIX) {
500 if (strncmp((char *)key.data, dbop->key, dbop->keylen))
501 return NULL;
502 } else {
503 if (strcmp((char *)key.data, dbop->key))
504 return NULL;
505 }
506 if (preg && regexec(preg, (char *)key.data, 0, 0, 0) != 0)
507 continue;
508 break;
509 }
510 } else {
511 dbop->keylen = dbop->key[0] = 0;
512 for (status = (*db->seq)(db, &key, &dat, R_FIRST);
513 status == RET_SUCCESS;
514 status = (*db->seq)(db, &key, &dat, R_NEXT)) {
515 /* skip meta records */
516 if (ismeta(key.data) && !(dbop->openflags & DBOP_RAW))
517 continue;
518 if (preg && regexec(preg, (char *)key.data, 0, 0, 0) != 0)
519 continue;
520 break;
521 }
522 }
523 dbop->lastdat = (char *)dat.data;
524 dbop->lastsize = dat.size;
525 dbop->lastkey = (char *)key.data;
526 dbop->lastkeysize = key.size;
527 switch (status) {
528 case RET_SUCCESS:
529 break;
530 case RET_ERROR:
531 die("dbop_first failed.");
532 case RET_SPECIAL:
533 return (NULL);
534 }
535 dbop->ioflags = flags;
536 if (flags & DBOP_KEY) {
537 strlimcpy(dbop->prev, (char *)key.data, sizeof(dbop->prev));
538 return (char *)key.data;
539 }
540 return ((char *)dat.data);
541 }
542 /**
543 * dbop_next: get next record.
544 *
545 * @param[in] dbop dbop descripter
546 * @return data
547 *
548 * @note dbop_next() always skip meta records.
549 */
550 const char *
551 dbop_next(DBOP *dbop)
552 {
553 DB *db = dbop->db;
554 int flags = dbop->ioflags;
555 DBT key, dat;
556 int status;
557
558 if (dbop->unread) {
559 dbop->unread = 0;
560 return dbop->lastdat;
561 }
562 while ((status = (*db->seq)(db, &key, &dat, R_NEXT)) == RET_SUCCESS) {
563 assert(dat.data != NULL);
564 /* skip meta records */
565 if (!(dbop->openflags & DBOP_RAW)) {
566 if (flags & DBOP_KEY && ismeta(key.data))
567 continue;
568 else if (ismeta(dat.data))
569 continue;
570 }
571 if (flags & DBOP_KEY) {
572 if (!strcmp(dbop->prev, (char *)key.data))
573 continue;
574 if (strlen((char *)key.data) > MAXKEYLEN)
575 die("primary key too long.");
576 strlimcpy(dbop->prev, (char *)key.data, sizeof(dbop->prev));
577 }
578 dbop->lastdat = (char *)dat.data;
579 dbop->lastsize = dat.size;
580 dbop->lastkey = (char *)key.data;
581 dbop->lastkeysize = key.size;
582 if (flags & DBOP_PREFIX) {
583 if (strncmp((char *)key.data, dbop->key, dbop->keylen))
584 return NULL;
585 } else if (dbop->keylen) {
586 if (strcmp((char *)key.data, dbop->key))
587 return NULL;
588 }
589 if (dbop->preg && regexec(dbop->preg, (char *)key.data, 0, 0, 0) != 0)
590 continue;
591 return (flags & DBOP_KEY) ? (char *)key.data : (char *)dat.data;
592 }
593 if (status == RET_ERROR)
594 die("dbop_next failed.");
595 return NULL;
596 }
597 /**
598 * dbop_unread: unread record to read again.
599 *
600 * @param[in] dbop dbop descripter
601 *
602 * @note dbop_next() will read this record later.
603 */
604 void
605 dbop_unread(DBOP *dbop)
606 {
607 dbop->unread = 1;
608 }
609 /**
610 * dbop_lastdat: get last data
611 *
612 * @param[in] dbop dbop descripter
613 * @param[out] size
614 * @return last data
615 */
616 const char *
617 dbop_lastdat(DBOP *dbop, int *size)
618 {
619 if (size)
620 *size = dbop->lastsize;
621 return dbop->lastdat;
622 }
623 /**
624 * get_flag: get flag value
625 */
626 const char *
627 dbop_getflag(DBOP *dbop)
628 {
629 int size;
630 const char *dat = dbop_lastdat(dbop, &size);
631 const char *flag = "";
632 /*
633 * Dat format is like follows.
634 * dat 'xxxxxxx\0ffff\0'
635 * (data) (flag)
636 */
637 if (dat) {
638 int i = strlen(dat) + 1;
639 if (size > i)
640 flag = dat + i;
641 }
642 return flag;
643 }
644 /**
645 * dbop_getoption: get option
646 */
647 const char *
648 dbop_getoption(DBOP *dbop, const char *key)
649 {
650 static char buf[1024];
651 const char *p;
652
653 if ((p = dbop_get(dbop, key)) == NULL)
654 return NULL;
655 if (dbop->lastsize <= strlen(key))
656 die("illegal format (dbop_getoption).");
657 for (p += strlen(key); *p && isspace((unsigned char)*p); p++)
658 ;
659 strlimcpy(buf, p, sizeof(buf));
660 return buf;
661 }
662 /**
663 * dbop_putoption: put option
664 */
665 void
666 dbop_putoption(DBOP *dbop, const char *key, const char *string)
667 {
668 char buf[1024];
669
670 if (string)
671 snprintf(buf, sizeof(buf), "%s %s", key, string);
672 else
673 snprintf(buf, sizeof(buf), "%s", key);
674 dbop_put(dbop, key, buf);
675 }
676 /**
677 * dbop_getversion: get format version
678 */
679 int
680 dbop_getversion(DBOP *dbop)
681 {
682 int format_version = 1; /* default format version */
683 const char *p;
684
685 if ((p = dbop_getoption(dbop, VERSIONKEY)) != NULL)
686 format_version = atoi(p);
687 return format_version;
688 }
689 /**
690 * dbop_putversion: put format version
691 */
692 void
693 dbop_putversion(DBOP *dbop, int version)
694 {
695 char number[32];
696
697 snprintf(number, sizeof(number), "%d", version);
698 dbop_putoption(dbop, VERSIONKEY, number);
699 }
700 /**
701 * dbop_close: close db
702 *
703 * @param[in] dbop dbop descripter
704 */
705 void
706 dbop_close(DBOP *dbop)
707 {
708 DB *db = dbop->db;
709
710 /*
711 * Load sorted tag records and write them to the tag file.
712 */
713 if (dbop->sortout != NULL) {
714 STRBUF *sb = strbuf_open(256);
715 char *p;
716
717 /*
718 * End of the former stage of sorted writing.
719 * fclose() and sortout = NULL is important.
720 *
721 * fclose(): enables reading from sortin descriptor.
722 * sortout = NULL: makes the following dbop_put write to the tag file directly.
723 */
724 fclose(dbop->sortout);
725 dbop->sortout = NULL;
726 /*
727 * The last stage of sorted writing.
728 */
729 while (strbuf_fgets(sb, dbop->sortin, STRBUF_NOCRLF)) {
730 for (p = strbuf_value(sb); *p && *p != SORT_SEP; p++)
731 ;
732 if (!*p)
733 die("unexpected end of record.");
734 *p++ = '\0';
735 dbop_put(dbop, strbuf_value(sb), p);
736 }
737 fclose(dbop->sortin);
738 strbuf_close(sb);
739 terminate_sort_process(dbop);
740 }
741 #ifdef USE_DB185_COMPAT
742 (void)db->close(db);
743 #else
744 /*
745 * If dbname = NULL, omit writing to the disk in __bt_close().
746 */
747 (void)db->close(db, dbop->dbname[0] == '\0' ? 1 : 0);
748 #endif
749 if (dbop->dbname[0] != '\0') {
750 if (dbop->perm && chmod(dbop->dbname, dbop->perm) < 0)
751 die("chmod(2) failed.");
752 }
753 (void)free(dbop);
754 }
/* */