cdb.c
上传用户:tany51
上传日期:2013-06-12
资源大小:1397k
文件大小:12k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*
  2.  * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
  3.  * Public domain.
  4.  */
  5. #include "common/setup_before.h"
  6. #include <stdio.h>
  7. #ifdef STDC_HEADERS
  8. # include <stdlib.h>
  9. #else
  10. # ifdef HAVE_MALLOC_H
  11. #  include <malloc.h>
  12. # endif
  13. #endif
  14. #ifdef HAVE_STRING_H
  15. # include <string.h>
  16. #else
  17. # ifdef HAVE_STRINGS_H
  18. #  include <strings.h>
  19. # endif
  20. #endif
  21. #include <stdarg.h>
  22. #include "compat/getopt.h"
  23. #include <errno.h>
  24. #include "cdb.h"
  25. #include "common/setup_after.h"
  26. #ifndef EPROTO
  27. # define EPROTO EINVAL
  28. #endif
  29. static char *progname;
  30. #define F_DUPMASK 0x000f
  31. #define F_WARNDUP 0x0100
  32. #define F_ERRDUP 0x0200
  33. #define F_MAP 0x1000 /* map format (or else CDB native format) */
  34. static char *buf;
  35. static unsigned blen;
  36. static void
  37. #ifdef __GNUC__
  38. __attribute__((noreturn,format(printf,2,3)))
  39. #endif
  40. error(int errnum, const char *fmt, ...)
  41. {
  42.   if (fmt) {
  43.     va_list ap;
  44.     fprintf(stderr, "%s: ", progname);
  45.     va_start(ap, fmt);
  46.     vfprintf(stderr, fmt, ap);
  47.     va_end(ap);
  48.   }
  49.   if (errnum)
  50.     fprintf(stderr, ": %sn", strerror(errnum));
  51.   else {
  52.     if (fmt) putc('n', stderr);
  53.     fprintf(stderr, "%s: try `%s -h' for helpn", progname, progname);
  54.   }
  55.   fflush(stderr);
  56.   exit(errnum ? 111 : 2);
  57. }
  58. static void allocbuf(unsigned len) {
  59.   if (blen < len) {
  60.     if (buf) buf = (char*)realloc(buf, len);
  61.     else buf = (char*)malloc(len);
  62.     if (!buf) error(ENOMEM, "unable to allocate %u bytes", len);
  63.     blen = len;
  64.   }
  65. }
  66. static int qmode(char *dbname, char *key, int num, int flags)
  67. {
  68.   struct cdb c;
  69.   struct cdb_find cf;
  70.   FILE *fd;
  71.   int r;
  72.   int n, found;
  73.   fd = fopen(dbname, "rb");
  74.   if (fd == NULL || cdb_init(&c, fd) != 0)
  75.     error(errno, "unable to open database `%s'", dbname);
  76.   r = cdb_findinit(&cf, &c, key, strlen(key));
  77.   if (!r)
  78.     return 100;
  79.   else if (r < 0)
  80.     error(errno, "%s", key);
  81.   n = 0; found = 0;
  82.   while((r = cdb_findnext(&cf)) > 0) {
  83.     ++n;
  84.     if (num && num != n) continue;
  85.     ++found;
  86.     allocbuf(cdb_datalen(&c));
  87.     if (cdb_read(&c, buf, cdb_datalen(&c), cdb_datapos(&c)) != 0)
  88.       error(errno, "unable to read value");
  89.     fwrite(buf, 1, cdb_datalen(&c), stdout);
  90.     if (flags & F_MAP) putchar('n');
  91.     if (num)
  92.       break;
  93.   }
  94.   if (r < 0)
  95.     error(0, "%s", key);
  96.   return found ? 0 : 100;
  97. }
  98. static void
  99. fget(FILE *f, unsigned char *b, unsigned len, unsigned *posp, unsigned limit)
  100. {
  101.   if (posp && limit - *posp < len)
  102.     error(EPROTO, "invalid database format");
  103.   if (fread(b, 1, len, f) != len) {
  104.     if (ferror(f)) error(errno, "unable to read");
  105.     fprintf(stderr, "%s: unable to read: short filen", progname);
  106.     exit(2);
  107.   }
  108.   if (posp) *posp += len;
  109. }
  110. static int
  111. fcpy(FILE *fi, FILE *fo, unsigned len, unsigned *posp, unsigned limit)
  112. {
  113.   while(len > blen) {
  114.     fget(fi, buf, blen, posp, limit);
  115.     if (fo && fwrite(buf, 1, blen, fo) != blen) return -1;
  116.     len -= blen;
  117.   }
  118.   if (len) {
  119.     fget(fi, buf, len, posp, limit);
  120.     if (fo && fwrite(buf, 1, len, fo) != len) return -1;
  121.   }
  122.   return 0;
  123. }
  124. static int
  125. dmode(char *dbname, char mode, int flags)
  126. {
  127.   unsigned eod, klen, vlen;
  128.   unsigned pos = 0;
  129.   FILE *f;
  130.   if (strcmp(dbname, "-") == 0)
  131.     f = stdin;
  132.   else if ((f = fopen(dbname, "rb")) == NULL)
  133.     error(errno, "open %s", dbname);
  134.   allocbuf(2048);
  135.   fget(f, buf, 2048, &pos, 2048);
  136.   eod = cdb_unpack(buf);
  137.   while(pos < eod) {
  138.     fget(f, buf, 8, &pos, eod);
  139.     klen = cdb_unpack(buf);
  140.     vlen = cdb_unpack(buf + 4);
  141.     if (!(flags & F_MAP))
  142.       if (printf(mode == 'd' ? "+%u,%u:" : "+%u:", klen, vlen) < 0) return -1;
  143.     if (fcpy(f, stdout, klen, &pos, eod) != 0) return -1;
  144.     if (mode == 'd')
  145.       if (fputs(flags & F_MAP ? " " : "->", stdout) < 0)
  146.         return -1;
  147.     if (fcpy(f, mode == 'd' ? stdout : NULL, vlen, &pos, eod) != 0)
  148.       return -1;
  149.     if (putc('n', stdout) < 0)
  150.       return -1;
  151.   }
  152.   if (pos != eod)
  153.     error(EPROTO, "invalid cdb file format");
  154.   if (!(flags & F_MAP))
  155.     if (putc('n', stdout) < 0)
  156.       return -1;
  157.   return 0;
  158. }
  159. static int smode(char *dbname) {
  160.   FILE *f;
  161.   unsigned pos, eod;
  162.   unsigned cnt = 0;
  163.   unsigned kmin = 0, kmax = 0, ktot = 0;
  164.   unsigned vmin = 0, vmax = 0, vtot = 0;
  165.   unsigned hmin = 0, hmax = 0, htot = 0, hcnt = 0;
  166. #define NDIST 11
  167.   unsigned dist[NDIST];
  168.   unsigned char toc[2048];
  169.   unsigned k;
  170.   if (strcmp(dbname, "-") == 0)
  171.     f = stdin;
  172.   else if ((f = fopen(dbname, "rb")) == NULL)
  173.     error(errno, "open %s", dbname);
  174.   pos = 0;
  175.   fget(f, toc, 2048, &pos, 2048);
  176.   allocbuf(2048);
  177.   eod = cdb_unpack(toc);
  178.   while(pos < eod) {
  179.     unsigned klen, vlen;
  180.     fget(f, buf, 8, &pos, eod);
  181.     klen = cdb_unpack(buf);
  182.     vlen = cdb_unpack(buf + 4);
  183.     fcpy(f, NULL, klen, &pos, eod);
  184.     fcpy(f, NULL, vlen, &pos, eod);
  185.     ++cnt;
  186.     ktot += klen;
  187.     if (!kmin || kmin > klen) kmin = klen;
  188.     if (kmax < klen) kmax = klen;
  189.     vtot += vlen;
  190.     if (!vmin || vmin > vlen) vmin = vlen;
  191.     if (vmax < vlen) vmax = vlen;
  192.     vlen += klen;
  193.   }
  194.   if (pos != eod) error(EPROTO, "invalid cdb file format");
  195.   for (k = 0; k < NDIST; ++k)
  196.     dist[k] = 0;
  197.   for (k = 0; k < 256; ++k) {
  198.     unsigned i = cdb_unpack(toc + (k << 3));
  199.     unsigned hlen = cdb_unpack(toc + (k << 3) + 4);
  200.     if (i != pos) error(EPROTO, "invalid cdb hash table");
  201.     if (!hlen) continue;
  202.     for (i = 0; i < hlen; ++i) {
  203.       unsigned h;
  204.       fget(f, buf, 8, &pos, 0xffffffff);
  205.       if (!cdb_unpack(buf + 4)) continue;
  206.       h = (cdb_unpack(buf) >> 8) % hlen;
  207.       if (h == i) h = 0;
  208.       else {
  209.         if (h < i) h = i - h;
  210.         else h = hlen - h + i;
  211.         if (h >= NDIST) h = NDIST - 1;
  212.       }
  213.       ++dist[h];
  214.     }
  215.     if (!hmin || hmin > hlen) hmin = hlen;
  216.     if (hmax < hlen) hmax = hlen;
  217.     htot += hlen;
  218.     ++hcnt;
  219.   }
  220.   printf("number of records: %un", cnt);
  221.   printf("key min/avg/max length: %u/%u/%un",
  222.          kmin, cnt ? (ktot + cnt / 2) / cnt : 0, kmax);
  223.   printf("val min/avg/max length: %u/%u/%un",
  224.          vmin, cnt ? (vtot + cnt / 2) / cnt : 0, vmax);
  225.   printf("hash tables/entries/collisions: %u/%u/%un",
  226.          hcnt, htot, cnt - dist[0]);
  227.   printf("hash table min/avg/max length: %u/%u/%un",
  228.          hmin, hcnt ? (htot + hcnt / 2) / hcnt : 0, hmax);
  229.   printf("hash table distances:n");
  230.   for(k = 0; k < NDIST; ++k)
  231.     printf(" %c%u: %6u %2u%%n",
  232.            k == NDIST - 1 ? '>' : 'd', k == NDIST - 1 ? k - 1 : k,
  233.            dist[k], cnt ? dist[k] * 100 / cnt : 0);
  234.   return 0;
  235. }
  236. static void badinput(const char *fn) {
  237.   fprintf(stderr, "%s: %s: bad formatn", progname, fn);
  238.   exit(2);
  239. }
  240. static int getnum(FILE *f, unsigned *np, const char *fn) {
  241.   unsigned n;
  242.   int c = getc(f);
  243.   if (c < '0' || c > '9') badinput(fn);
  244.   n = c - '0';
  245.   while((c = getc(f)) >= '0' && c <= '9') {
  246.     c -= '0';
  247.     if (0xffffffff / 10 - c < n) badinput(fn);
  248.     n = n * 10 + c;
  249.   }
  250.   *np = n;
  251.   return c;
  252. }
  253. static void
  254. addrec(struct cdb_make *cdbmp,
  255.        char *key, unsigned klen,
  256.        char *val, unsigned vlen,
  257.        int flags)
  258. {
  259.   int r = cdb_make_put(cdbmp, key, klen, val, vlen, flags & F_DUPMASK);
  260.   if (r < 0)
  261.     error(errno, "cdb_make_put");
  262.   else if (r && (flags & F_WARNDUP)) {
  263.     fprintf(stderr, "%s: key `", progname);
  264.     fwrite(key, 1, klen, stderr);
  265.     fputs("' duplicatedn", stderr);
  266.     if (flags & F_ERRDUP)
  267.       exit(1);
  268.   }
  269. }
  270. static void
  271. dofile_cdb(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags)
  272. {
  273.   unsigned klen, vlen;
  274.   int c;
  275.   while((c = getc(f)) == '+') {
  276.     if ((c = getnum(f, &klen, fn)) != ',' ||
  277.         (c = getnum(f, &vlen, fn)) != ':' ||
  278.         0xffffffff - klen < vlen)
  279.       badinput(fn);
  280.     allocbuf(klen + vlen);
  281.     fget(f, buf, klen, NULL, 0);
  282.     if (getc(f) != '-' || getc(f) != '>') badinput(fn);
  283.     fget(f, buf + klen, vlen, NULL, 0);
  284.     if (getc(f) != 'n') badinput(fn);
  285.     addrec(cdbmp, buf, klen, buf + klen, vlen, flags);
  286.   }
  287.   if (c != 'n') badinput(fn);
  288. }
  289. static void
  290. dofile_ln(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags)
  291. {
  292.   char *k, *v;
  293.   while(fgets(buf, blen, f) != NULL) {
  294.     unsigned l = 0;
  295.     for (;;) {
  296.       l += strlen(buf + l);
  297.       v = buf + l;
  298.       if (v > buf && v[-1] == 'n') {
  299.         v[-1] = '';
  300.         break;
  301.       }
  302.       if (l < blen)
  303.         allocbuf(l + 512);
  304.       if (!fgets(buf + l, blen - l, f))
  305.         break;
  306.     }
  307.     k = buf;
  308.     while(*k == ' ' || *k == 't') ++k;
  309.     if (!*k || *k == '#')
  310.       continue;
  311.     v = k;
  312.     while(*v && *v != ' ' && *v != 't') ++v;
  313.     if (*v) *v++ = '';
  314.     while(*v == ' ' || *v == 't') ++v;
  315.     addrec(cdbmp, k, strlen(k), v, strlen(v), flags);
  316.   }
  317. }
  318. static void
  319. dofile(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags)
  320. {
  321.   if (flags & F_MAP)
  322.     dofile_ln(cdbmp, f, fn, flags);
  323.   else
  324.     dofile_cdb(cdbmp, f, fn, flags);
  325.   if (ferror(f))
  326.     error(errno, "read error");
  327. }
  328. static int
  329. cmode(char *dbname, char *tmpname, int argc, char **argv, int flags)
  330. {
  331.   struct cdb_make cdb;
  332.   FILE *fd;
  333.   if (!tmpname) {
  334.     tmpname = (char*)malloc(strlen(dbname) + 5);
  335.     if (!tmpname)
  336.       error(ENOMEM, "unable to allocate memory");
  337.     strcat(strcpy(tmpname, dbname), ".tmp");
  338.   }
  339.   fd = fopen(tmpname, "w+b");
  340.   if (fd == 0)
  341.     error(errno, "unable to create %s", tmpname);
  342.   cdb_make_start(&cdb, fd);
  343.   allocbuf(4096);
  344.   if (argc) {
  345.     int i;
  346.     for (i = 0; i < argc; ++i) {
  347.       if (strcmp(argv[i], "-") == 0)
  348.         dofile(&cdb, stdin, "(stdin)", flags);
  349.       else {
  350.         FILE *f = fopen(argv[i], "rb");
  351.         if (!f)
  352.           error(errno, "%s", argv[i]);
  353.         dofile(&cdb, f, argv[i], flags);
  354.         fclose(f);
  355.       }
  356.     }
  357.   }
  358.   else
  359.     dofile(&cdb, stdin, "(stdin)", flags);
  360.   if (cdb_make_finish(&cdb) != 0)
  361.     error(errno, "cdb_make_finish");
  362.   fclose(fd);
  363.   if (rename(tmpname, dbname) != 0)
  364.     error(errno, "rename %s->%s", tmpname, dbname);
  365.   return 0;
  366. }
  367. int main(int argc, char **argv)
  368. {
  369.   int c;
  370.   char mode = 0;
  371.   char *tmpname = NULL;
  372.   int flags = 0;
  373.   int num = 0;
  374.   int r;
  375.   extern char *optarg;
  376.   extern int optind;
  377.   if ((progname = strrchr(argv[0], '/')) != NULL)
  378.     argv[0] = ++progname;
  379.   else
  380.     progname = argv[0];
  381.   if (argc == 1)
  382.     error(0, "no arguments given");
  383.   while((c = getopt(argc, argv, "qdlcsht:n:mwrue")) != EOF)
  384.     switch(c) {
  385.     case 'q': case 'd':  case 'l': case 'c': case 's':
  386.       if (mode && mode != c)
  387.         error(0, "different modes of operation requested");
  388.       mode = c;
  389.       break;
  390.     case 't': tmpname = optarg; break;
  391.     case 'w': flags |= F_WARNDUP; break;
  392.     case 'e': flags |= F_WARNDUP | F_ERRDUP; break;
  393.     case 'r': flags = (flags & ~F_DUPMASK) | CDB_PUT_REPLACE; break;
  394.     case 'u': flags = (flags & ~F_DUPMASK) | CDB_PUT_INSERT; break;
  395.     case 'm': flags |= F_MAP; break;
  396.     case 'n':
  397.       if ((num = atoi(optarg)) <= 0)
  398.         error(0, "invalid record number `%s'", optarg);
  399.       break;
  400.     case 'h':
  401.       printf("
  402. %s: Constant DataBase (CDB) tool. Usage is:n
  403.  query:  %s -q [-m] [-n recno|-a] cdbfile keyn
  404.  dump:   %s -d [-m] [cdbfile|-]n
  405.  list:   %s -l [-m] [cdbfile|-]n
  406.  create: %s -c [-m] [-wrue] [-t tempfile] cdbfile [infile...]n
  407.  stats:  %s -s [cdbfile|-]n
  408.  help:   %s -hn
  409. ", progname, progname, progname, progname, progname, progname, progname);
  410.       return 0;
  411.     default:
  412.       error(0, NULL);
  413.     }
  414.   argv += optind;
  415.   argc -= optind;
  416.   switch(mode) {
  417.     case 'q':
  418.       if (argc < 2) error(0, "no database or key to query specified");
  419.       if (argc > 2) error(0, "extra arguments in command line");
  420.       r = qmode(argv[0], argv[1], num, flags);
  421.       break;
  422.     case 'c':
  423.       if (!argc) error(0, "no database name specified");
  424.       if ((flags & F_WARNDUP) && !(flags & F_DUPMASK))
  425.         flags |= CDB_PUT_WARN;
  426.       r = cmode(argv[0], tmpname, argc - 1, argv + 1, flags);
  427.       break;
  428.     case 'd':
  429.     case 'l':
  430.       if (argc > 1) error(0, "extra arguments for dump/list");
  431.       r = dmode(argc ? argv[0] : "-", mode, flags);
  432.       break;
  433.     case 's':
  434.       if (argc > 1) error(0, "extra argument(s) for stats");
  435.       r = smode(argc ? argv[0] : "-");
  436.       break;
  437.     default:
  438.       error(0, "no -q, -c, -d, -l or -s option specified");
  439.   }
  440.   if (r < 0 || fflush(stdout) < 0)
  441.     error(errno, "unable to write: %d", c);
  442.   return r;
  443. }