/[LeafOK_CVS]/pvpgn-1.7.4/src/tinycdb/cdb.c
ViewVC logotype

Contents of /pvpgn-1.7.4/src/tinycdb/cdb.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show annotations)
Tue Jun 6 03:41:38 2006 UTC (19 years, 9 months ago) by sysadm
CVS Tags: pvpgn_1-7-4-0_MIL
Branch point for: GNU, MAIN
Content type: text/x-csrc
Initial revision

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

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1