root/libutil/dbop.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. start_sort_process
  2. terminate_sort_process
  3. start_sort_process
  4. terminate_sort_process
  5. start_sort_process
  6. terminate_sort_process
  7. dbop_open
  8. dbop_get
  9. dbop_put
  10. dbop_put_withlen
  11. dbop_delete
  12. dbop_update
  13. dbop_first
  14. dbop_next
  15. dbop_unread
  16. dbop_lastdat
  17. dbop_getflag
  18. dbop_getoption
  19. dbop_putoption
  20. dbop_getversion
  21. dbop_putversion
  22. 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 }

/* [previous][next][first][last][top][bottom][index][help] */