1 1.3 christos /* $NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $ */ 2 1.1 pgoyette 3 1.1 pgoyette /*- 4 1.1 pgoyette * Copyright (c) 1992, 1993, 1994 5 1.1 pgoyette * The Regents of the University of California. All rights reserved. 6 1.1 pgoyette * 7 1.1 pgoyette * Redistribution and use in source and binary forms, with or without 8 1.1 pgoyette * modification, are permitted provided that the following conditions 9 1.1 pgoyette * are met: 10 1.1 pgoyette * 1. Redistributions of source code must retain the above copyright 11 1.1 pgoyette * notice, this list of conditions and the following disclaimer. 12 1.1 pgoyette * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 pgoyette * notice, this list of conditions and the following disclaimer in the 14 1.1 pgoyette * documentation and/or other materials provided with the distribution. 15 1.1 pgoyette * 3. Neither the name of the University nor the names of its contributors 16 1.1 pgoyette * may be used to endorse or promote products derived from this software 17 1.1 pgoyette * without specific prior written permission. 18 1.1 pgoyette * 19 1.1 pgoyette * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 pgoyette * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 pgoyette * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 pgoyette * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 pgoyette * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 pgoyette * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 pgoyette * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 pgoyette * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 pgoyette * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 pgoyette * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 pgoyette * SUCH DAMAGE. 30 1.1 pgoyette */ 31 1.1 pgoyette 32 1.1 pgoyette #include <sys/cdefs.h> 33 1.1 pgoyette #ifndef lint 34 1.1 pgoyette __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\ 35 1.1 pgoyette The Regents of the University of California. All rights reserved."); 36 1.1 pgoyette #endif /* not lint */ 37 1.1 pgoyette 38 1.1 pgoyette #ifndef lint 39 1.1 pgoyette #if 0 40 1.1 pgoyette static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94"; 41 1.1 pgoyette #else 42 1.3 christos __RCSID("$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $"); 43 1.1 pgoyette #endif 44 1.1 pgoyette #endif /* not lint */ 45 1.1 pgoyette 46 1.1 pgoyette #include <sys/param.h> 47 1.1 pgoyette #include <sys/stat.h> 48 1.1 pgoyette 49 1.1 pgoyette #include <ctype.h> 50 1.1 pgoyette #include <errno.h> 51 1.1 pgoyette #include <fcntl.h> 52 1.1 pgoyette #include <limits.h> 53 1.1 pgoyette #include <stdio.h> 54 1.1 pgoyette #include <stdlib.h> 55 1.1 pgoyette #include <string.h> 56 1.1 pgoyette #include <stdbool.h> 57 1.1 pgoyette #include <unistd.h> 58 1.1 pgoyette #include <err.h> 59 1.1 pgoyette #include <db.h> 60 1.2 christos #include "btree.h" 61 1.1 pgoyette 62 1.1 pgoyette enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; 63 1.1 pgoyette 64 1.1 pgoyette static void compare(DBT *, DBT *); 65 1.1 pgoyette static DBTYPE dbtype(const char *); 66 1.2 christos static void dump(DB *, int, int); 67 1.1 pgoyette static void get(DB *, DBT *); 68 1.1 pgoyette static void getdata(DB *, DBT *, DBT *); 69 1.1 pgoyette static void put(DB *, DBT *, DBT *); 70 1.1 pgoyette static void rem(DB *, DBT *); 71 1.1 pgoyette static const char *sflags(int); 72 1.1 pgoyette static void synk(DB *); 73 1.1 pgoyette static void *rfile(char *, size_t *); 74 1.1 pgoyette static void seq(DB *, DBT *); 75 1.1 pgoyette static u_int setflags(char *); 76 1.1 pgoyette static void *setinfo(DBTYPE, char *); 77 1.2 christos static void unlinkpg(DB *); 78 1.1 pgoyette static void usage(void) __attribute__((__noreturn__)); 79 1.1 pgoyette static void *xcopy(void *, size_t); 80 1.1 pgoyette static void chkcmd(enum S); 81 1.1 pgoyette static void chkdata(enum S); 82 1.1 pgoyette static void chkkey(enum S); 83 1.1 pgoyette 84 1.1 pgoyette #ifdef STATISTICS 85 1.1 pgoyette extern void __bt_stat(DB *); 86 1.1 pgoyette #endif 87 1.2 christos extern int __bt_relink(BTREE *, PAGE *); 88 1.1 pgoyette 89 1.1 pgoyette static DBTYPE type; /* Database type. */ 90 1.1 pgoyette static void *infop; /* Iflags. */ 91 1.1 pgoyette static size_t lineno; /* Current line in test script. */ 92 1.1 pgoyette static u_int flags; /* Current DB flags. */ 93 1.1 pgoyette static int ofd = STDOUT_FILENO; /* Standard output fd. */ 94 1.1 pgoyette 95 1.1 pgoyette static DB *XXdbp; /* Global for gdb. */ 96 1.1 pgoyette static size_t XXlineno; /* Fast breakpoint for gdb. */ 97 1.1 pgoyette 98 1.1 pgoyette int 99 1.1 pgoyette main(int argc, char *argv[]) 100 1.1 pgoyette { 101 1.1 pgoyette extern int optind; 102 1.1 pgoyette extern char *optarg; 103 1.1 pgoyette enum S command = COMMAND, state; 104 1.1 pgoyette DB *dbp; 105 1.1 pgoyette DBT data, key, keydata; 106 1.1 pgoyette size_t len; 107 1.1 pgoyette int ch, oflags, sflag; 108 1.1 pgoyette char *fname, *infoarg, *p, *t, buf[8 * 1024]; 109 1.1 pgoyette bool unlink_dbfile; 110 1.1 pgoyette 111 1.1 pgoyette infoarg = NULL; 112 1.1 pgoyette fname = NULL; 113 1.1 pgoyette unlink_dbfile = false; 114 1.1 pgoyette oflags = O_CREAT | O_RDWR; 115 1.1 pgoyette sflag = 0; 116 1.1 pgoyette while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1) 117 1.1 pgoyette switch (ch) { 118 1.1 pgoyette case 'f': 119 1.1 pgoyette fname = optarg; 120 1.1 pgoyette break; 121 1.1 pgoyette case 'i': 122 1.1 pgoyette infoarg = optarg; 123 1.1 pgoyette break; 124 1.1 pgoyette case 'l': 125 1.1 pgoyette oflags |= DB_LOCK; 126 1.1 pgoyette break; 127 1.1 pgoyette case 'o': 128 1.1 pgoyette if ((ofd = open(optarg, 129 1.1 pgoyette O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) 130 1.1 pgoyette err(1, "Cannot create `%s'", optarg); 131 1.1 pgoyette break; 132 1.1 pgoyette case 's': 133 1.1 pgoyette sflag = 1; 134 1.1 pgoyette break; 135 1.1 pgoyette case '?': 136 1.1 pgoyette default: 137 1.1 pgoyette usage(); 138 1.1 pgoyette } 139 1.1 pgoyette argc -= optind; 140 1.1 pgoyette argv += optind; 141 1.1 pgoyette 142 1.1 pgoyette if (argc != 2) 143 1.1 pgoyette usage(); 144 1.1 pgoyette 145 1.1 pgoyette /* Set the type. */ 146 1.1 pgoyette type = dbtype(*argv++); 147 1.1 pgoyette 148 1.1 pgoyette /* Open the descriptor file. */ 149 1.1 pgoyette if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL) 150 1.1 pgoyette err(1, "Cannot reopen `%s'", *argv); 151 1.1 pgoyette 152 1.1 pgoyette /* Set up the db structure as necessary. */ 153 1.1 pgoyette if (infoarg == NULL) 154 1.1 pgoyette infop = NULL; 155 1.1 pgoyette else 156 1.1 pgoyette for (p = strtok(infoarg, ",\t "); p != NULL; 157 1.1 pgoyette p = strtok(0, ",\t ")) 158 1.1 pgoyette if (*p != '\0') 159 1.1 pgoyette infop = setinfo(type, p); 160 1.1 pgoyette 161 1.1 pgoyette /* 162 1.1 pgoyette * Open the DB. Delete any preexisting copy, you almost never 163 1.1 pgoyette * want it around, and it often screws up tests. 164 1.1 pgoyette */ 165 1.1 pgoyette if (fname == NULL) { 166 1.1 pgoyette const char *q = getenv("TMPDIR"); 167 1.1 pgoyette if (q == NULL) 168 1.1 pgoyette q = "/var/tmp"; 169 1.1 pgoyette (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q); 170 1.1 pgoyette fname = buf; 171 1.1 pgoyette (void)unlink(buf); 172 1.1 pgoyette unlink_dbfile = true; 173 1.1 pgoyette } else if (!sflag) 174 1.1 pgoyette (void)unlink(fname); 175 1.1 pgoyette 176 1.1 pgoyette if ((dbp = dbopen(fname, 177 1.1 pgoyette oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL) 178 1.1 pgoyette err(1, "Cannot dbopen `%s'", fname); 179 1.1 pgoyette XXdbp = dbp; 180 1.1 pgoyette if (unlink_dbfile) 181 1.1 pgoyette (void)unlink(fname); 182 1.1 pgoyette 183 1.1 pgoyette state = COMMAND; 184 1.1 pgoyette for (lineno = 1; 185 1.1 pgoyette (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { 186 1.1 pgoyette /* Delete the newline, displaying the key/data is easier. */ 187 1.1 pgoyette if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL) 188 1.1 pgoyette *t = '\0'; 189 1.1 pgoyette if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) || 190 1.1 pgoyette *p == '#') 191 1.1 pgoyette continue; 192 1.1 pgoyette 193 1.1 pgoyette /* Convenient gdb break point. */ 194 1.1 pgoyette if (XXlineno == lineno) 195 1.1 pgoyette XXlineno = 1; 196 1.1 pgoyette switch (*p) { 197 1.1 pgoyette case 'c': /* compare */ 198 1.1 pgoyette chkcmd(state); 199 1.1 pgoyette state = KEY; 200 1.1 pgoyette command = COMPARE; 201 1.1 pgoyette break; 202 1.1 pgoyette case 'e': /* echo */ 203 1.1 pgoyette chkcmd(state); 204 1.1 pgoyette /* Don't display the newline, if CR at EOL. */ 205 1.1 pgoyette if (p[len - 2] == '\r') 206 1.1 pgoyette --len; 207 1.1 pgoyette if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 || 208 1.1 pgoyette write(ofd, "\n", 1) != 1) 209 1.1 pgoyette err(1, "write failed"); 210 1.1 pgoyette break; 211 1.1 pgoyette case 'g': /* get */ 212 1.1 pgoyette chkcmd(state); 213 1.1 pgoyette state = KEY; 214 1.1 pgoyette command = GET; 215 1.1 pgoyette break; 216 1.1 pgoyette case 'p': /* put */ 217 1.1 pgoyette chkcmd(state); 218 1.1 pgoyette state = KEY; 219 1.1 pgoyette command = PUT; 220 1.1 pgoyette break; 221 1.1 pgoyette case 'r': /* remove */ 222 1.1 pgoyette chkcmd(state); 223 1.1 pgoyette if (flags == R_CURSOR) { 224 1.1 pgoyette rem(dbp, &key); 225 1.1 pgoyette state = COMMAND; 226 1.1 pgoyette } else { 227 1.1 pgoyette state = KEY; 228 1.1 pgoyette command = REMOVE; 229 1.1 pgoyette } 230 1.1 pgoyette break; 231 1.1 pgoyette case 'S': /* sync */ 232 1.1 pgoyette chkcmd(state); 233 1.1 pgoyette synk(dbp); 234 1.1 pgoyette state = COMMAND; 235 1.1 pgoyette break; 236 1.1 pgoyette case 's': /* seq */ 237 1.1 pgoyette chkcmd(state); 238 1.1 pgoyette if (flags == R_CURSOR) { 239 1.1 pgoyette state = KEY; 240 1.1 pgoyette command = SEQ; 241 1.1 pgoyette } else 242 1.1 pgoyette seq(dbp, &key); 243 1.1 pgoyette break; 244 1.1 pgoyette case 'f': 245 1.1 pgoyette flags = setflags(p + 1); 246 1.1 pgoyette break; 247 1.1 pgoyette case 'D': /* data file */ 248 1.1 pgoyette chkdata(state); 249 1.1 pgoyette data.data = rfile(p + 1, &data.size); 250 1.1 pgoyette goto ldata; 251 1.1 pgoyette case 'd': /* data */ 252 1.1 pgoyette chkdata(state); 253 1.1 pgoyette data.data = xcopy(p + 1, len - 1); 254 1.1 pgoyette data.size = len - 1; 255 1.1 pgoyette ldata: switch (command) { 256 1.1 pgoyette case COMPARE: 257 1.1 pgoyette compare(&keydata, &data); 258 1.1 pgoyette break; 259 1.1 pgoyette case PUT: 260 1.1 pgoyette put(dbp, &key, &data); 261 1.1 pgoyette break; 262 1.1 pgoyette default: 263 1.1 pgoyette errx(1, "line %zu: command doesn't take data", 264 1.1 pgoyette lineno); 265 1.1 pgoyette } 266 1.1 pgoyette if (type != DB_RECNO) 267 1.1 pgoyette free(key.data); 268 1.1 pgoyette free(data.data); 269 1.1 pgoyette state = COMMAND; 270 1.1 pgoyette break; 271 1.1 pgoyette case 'K': /* key file */ 272 1.1 pgoyette chkkey(state); 273 1.1 pgoyette if (type == DB_RECNO) 274 1.1 pgoyette errx(1, "line %zu: 'K' not available for recno", 275 1.1 pgoyette lineno); 276 1.1 pgoyette key.data = rfile(p + 1, &key.size); 277 1.1 pgoyette goto lkey; 278 1.1 pgoyette case 'k': /* key */ 279 1.1 pgoyette chkkey(state); 280 1.1 pgoyette if (type == DB_RECNO) { 281 1.1 pgoyette static recno_t recno; 282 1.1 pgoyette recno = atoi(p + 1); 283 1.1 pgoyette key.data = &recno; 284 1.1 pgoyette key.size = sizeof(recno); 285 1.1 pgoyette } else { 286 1.1 pgoyette key.data = xcopy(p + 1, len - 1); 287 1.1 pgoyette key.size = len - 1; 288 1.1 pgoyette } 289 1.1 pgoyette lkey: switch (command) { 290 1.1 pgoyette case COMPARE: 291 1.1 pgoyette getdata(dbp, &key, &keydata); 292 1.1 pgoyette state = DATA; 293 1.1 pgoyette break; 294 1.1 pgoyette case GET: 295 1.1 pgoyette get(dbp, &key); 296 1.1 pgoyette if (type != DB_RECNO) 297 1.1 pgoyette free(key.data); 298 1.1 pgoyette state = COMMAND; 299 1.1 pgoyette break; 300 1.1 pgoyette case PUT: 301 1.1 pgoyette state = DATA; 302 1.1 pgoyette break; 303 1.1 pgoyette case REMOVE: 304 1.1 pgoyette rem(dbp, &key); 305 1.1 pgoyette if ((type != DB_RECNO) && (flags != R_CURSOR)) 306 1.1 pgoyette free(key.data); 307 1.1 pgoyette state = COMMAND; 308 1.1 pgoyette break; 309 1.1 pgoyette case SEQ: 310 1.1 pgoyette seq(dbp, &key); 311 1.1 pgoyette if ((type != DB_RECNO) && (flags != R_CURSOR)) 312 1.1 pgoyette free(key.data); 313 1.1 pgoyette state = COMMAND; 314 1.1 pgoyette break; 315 1.1 pgoyette default: 316 1.1 pgoyette errx(1, "line %zu: command doesn't take a key", 317 1.1 pgoyette lineno); 318 1.1 pgoyette } 319 1.1 pgoyette break; 320 1.1 pgoyette case 'o': 321 1.2 christos dump(dbp, p[1] == 'r', 0); 322 1.2 christos break; 323 1.2 christos case 'O': 324 1.2 christos dump(dbp, p[1] == 'r', 1); 325 1.2 christos break; 326 1.2 christos case 'u': 327 1.2 christos unlinkpg(dbp); 328 1.1 pgoyette break; 329 1.1 pgoyette default: 330 1.1 pgoyette errx(1, "line %zu: %s: unknown command character", 331 1.1 pgoyette lineno, p); 332 1.1 pgoyette } 333 1.1 pgoyette } 334 1.1 pgoyette #ifdef STATISTICS 335 1.1 pgoyette /* 336 1.1 pgoyette * -l must be used (DB_LOCK must be set) for this to be 337 1.1 pgoyette * used, otherwise a page will be locked and it will fail. 338 1.1 pgoyette */ 339 1.1 pgoyette if (type == DB_BTREE && oflags & DB_LOCK) 340 1.1 pgoyette __bt_stat(dbp); 341 1.1 pgoyette #endif 342 1.1 pgoyette if ((*dbp->close)(dbp)) 343 1.1 pgoyette err(1, "db->close failed"); 344 1.1 pgoyette (void)close(ofd); 345 1.1 pgoyette return 0; 346 1.1 pgoyette } 347 1.1 pgoyette 348 1.1 pgoyette #define NOOVERWRITE "put failed, would overwrite key\n" 349 1.1 pgoyette 350 1.1 pgoyette static void 351 1.1 pgoyette compare(DBT *db1, DBT *db2) 352 1.1 pgoyette { 353 1.1 pgoyette size_t len; 354 1.1 pgoyette u_char *p1, *p2; 355 1.1 pgoyette 356 1.1 pgoyette if (db1->size != db2->size) 357 1.1 pgoyette printf("compare failed: key->data len %zu != data len %zu\n", 358 1.1 pgoyette db1->size, db2->size); 359 1.1 pgoyette 360 1.1 pgoyette len = MIN(db1->size, db2->size); 361 1.1 pgoyette for (p1 = db1->data, p2 = db2->data; len--;) 362 1.1 pgoyette if (*p1++ != *p2++) { 363 1.1 pgoyette printf("compare failed at offset %lu\n", 364 1.1 pgoyette (unsigned long)(p1 - (u_char *)db1->data)); 365 1.1 pgoyette break; 366 1.1 pgoyette } 367 1.1 pgoyette } 368 1.1 pgoyette 369 1.1 pgoyette static void 370 1.1 pgoyette get(DB *dbp, DBT *kp) 371 1.1 pgoyette { 372 1.1 pgoyette DBT data; 373 1.1 pgoyette 374 1.1 pgoyette switch ((*dbp->get)(dbp, kp, &data, flags)) { 375 1.1 pgoyette case 0: 376 1.1 pgoyette (void)write(ofd, data.data, data.size); 377 1.1 pgoyette if (ofd == STDOUT_FILENO) 378 1.1 pgoyette (void)write(ofd, "\n", 1); 379 1.1 pgoyette break; 380 1.1 pgoyette case -1: 381 1.1 pgoyette err(1, "line %zu: get failed", lineno); 382 1.1 pgoyette /* NOTREACHED */ 383 1.1 pgoyette case 1: 384 1.1 pgoyette #define NOSUCHKEY "get failed, no such key\n" 385 1.1 pgoyette if (ofd != STDOUT_FILENO) 386 1.1 pgoyette (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 387 1.1 pgoyette else 388 1.1 pgoyette (void)fprintf(stderr, "%zu: %.*s: %s", 389 1.1 pgoyette lineno, (int)MIN(kp->size, 20), 390 1.1 pgoyette (const char *)kp->data, 391 1.1 pgoyette NOSUCHKEY); 392 1.1 pgoyette #undef NOSUCHKEY 393 1.1 pgoyette break; 394 1.1 pgoyette } 395 1.1 pgoyette } 396 1.1 pgoyette 397 1.1 pgoyette static void 398 1.1 pgoyette getdata(DB *dbp, DBT *kp, DBT *dp) 399 1.1 pgoyette { 400 1.1 pgoyette switch ((*dbp->get)(dbp, kp, dp, flags)) { 401 1.1 pgoyette case 0: 402 1.1 pgoyette return; 403 1.1 pgoyette case -1: 404 1.1 pgoyette err(1, "line %zu: getdata failed", lineno); 405 1.1 pgoyette /* NOTREACHED */ 406 1.1 pgoyette case 1: 407 1.1 pgoyette errx(1, "line %zu: getdata failed, no such key", lineno); 408 1.1 pgoyette /* NOTREACHED */ 409 1.1 pgoyette } 410 1.1 pgoyette } 411 1.1 pgoyette 412 1.1 pgoyette static void 413 1.1 pgoyette put(DB *dbp, DBT *kp, DBT *dp) 414 1.1 pgoyette { 415 1.1 pgoyette switch ((*dbp->put)(dbp, kp, dp, flags)) { 416 1.1 pgoyette case 0: 417 1.1 pgoyette break; 418 1.1 pgoyette case -1: 419 1.1 pgoyette err(1, "line %zu: put failed", lineno); 420 1.1 pgoyette /* NOTREACHED */ 421 1.1 pgoyette case 1: 422 1.1 pgoyette (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); 423 1.1 pgoyette break; 424 1.1 pgoyette } 425 1.1 pgoyette } 426 1.1 pgoyette 427 1.1 pgoyette static void 428 1.1 pgoyette rem(DB *dbp, DBT *kp) 429 1.1 pgoyette { 430 1.1 pgoyette switch ((*dbp->del)(dbp, kp, flags)) { 431 1.1 pgoyette case 0: 432 1.1 pgoyette break; 433 1.1 pgoyette case -1: 434 1.1 pgoyette err(1, "line %zu: rem failed", lineno); 435 1.1 pgoyette /* NOTREACHED */ 436 1.1 pgoyette case 1: 437 1.1 pgoyette #define NOSUCHKEY "rem failed, no such key\n" 438 1.1 pgoyette if (ofd != STDOUT_FILENO) 439 1.1 pgoyette (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 440 1.1 pgoyette else if (flags != R_CURSOR) 441 1.1 pgoyette (void)fprintf(stderr, "%zu: %.*s: %s", 442 1.1 pgoyette lineno, (int)MIN(kp->size, 20), 443 1.1 pgoyette (const char *)kp->data, NOSUCHKEY); 444 1.1 pgoyette else 445 1.1 pgoyette (void)fprintf(stderr, 446 1.1 pgoyette "%zu: rem of cursor failed\n", lineno); 447 1.1 pgoyette #undef NOSUCHKEY 448 1.1 pgoyette break; 449 1.1 pgoyette } 450 1.1 pgoyette } 451 1.1 pgoyette 452 1.1 pgoyette static void 453 1.1 pgoyette synk(DB *dbp) 454 1.1 pgoyette { 455 1.1 pgoyette switch ((*dbp->sync)(dbp, flags)) { 456 1.1 pgoyette case 0: 457 1.1 pgoyette break; 458 1.1 pgoyette case -1: 459 1.1 pgoyette err(1, "line %zu: synk failed", lineno); 460 1.1 pgoyette /* NOTREACHED */ 461 1.1 pgoyette } 462 1.1 pgoyette } 463 1.1 pgoyette 464 1.1 pgoyette static void 465 1.1 pgoyette seq(DB *dbp, DBT *kp) 466 1.1 pgoyette { 467 1.1 pgoyette DBT data; 468 1.1 pgoyette 469 1.1 pgoyette switch (dbp->seq(dbp, kp, &data, flags)) { 470 1.1 pgoyette case 0: 471 1.1 pgoyette (void)write(ofd, data.data, data.size); 472 1.1 pgoyette if (ofd == STDOUT_FILENO) 473 1.1 pgoyette (void)write(ofd, "\n", 1); 474 1.1 pgoyette break; 475 1.1 pgoyette case -1: 476 1.1 pgoyette err(1, "line %zu: seq failed", lineno); 477 1.1 pgoyette /* NOTREACHED */ 478 1.1 pgoyette case 1: 479 1.1 pgoyette #define NOSUCHKEY "seq failed, no such key\n" 480 1.1 pgoyette if (ofd != STDOUT_FILENO) 481 1.1 pgoyette (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 482 1.1 pgoyette else if (flags == R_CURSOR) 483 1.1 pgoyette (void)fprintf(stderr, "%zu: %.*s: %s", 484 1.1 pgoyette lineno, (int)MIN(kp->size, 20), 485 1.1 pgoyette (const char *)kp->data, NOSUCHKEY); 486 1.1 pgoyette else 487 1.1 pgoyette (void)fprintf(stderr, 488 1.1 pgoyette "%zu: seq (%s) failed\n", lineno, sflags(flags)); 489 1.1 pgoyette #undef NOSUCHKEY 490 1.1 pgoyette break; 491 1.1 pgoyette } 492 1.1 pgoyette } 493 1.1 pgoyette 494 1.1 pgoyette static void 495 1.2 christos dump(DB *dbp, int rev, int recurse) 496 1.1 pgoyette { 497 1.1 pgoyette DBT key, data; 498 1.1 pgoyette int xflags, nflags; 499 1.1 pgoyette 500 1.1 pgoyette if (rev) { 501 1.1 pgoyette xflags = R_LAST; 502 1.2 christos nflags = recurse ? R_RPREV : R_PREV; 503 1.1 pgoyette } else { 504 1.1 pgoyette xflags = R_FIRST; 505 1.2 christos nflags = recurse ? R_RNEXT : R_NEXT; 506 1.1 pgoyette } 507 1.1 pgoyette for (;; xflags = nflags) 508 1.1 pgoyette switch (dbp->seq(dbp, &key, &data, xflags)) { 509 1.1 pgoyette case 0: 510 1.1 pgoyette (void)write(ofd, data.data, data.size); 511 1.1 pgoyette if (ofd == STDOUT_FILENO) 512 1.1 pgoyette (void)write(ofd, "\n", 1); 513 1.1 pgoyette break; 514 1.1 pgoyette case 1: 515 1.1 pgoyette goto done; 516 1.1 pgoyette case -1: 517 1.1 pgoyette err(1, "line %zu: (dump) seq failed", lineno); 518 1.1 pgoyette /* NOTREACHED */ 519 1.1 pgoyette } 520 1.1 pgoyette done: return; 521 1.1 pgoyette } 522 1.1 pgoyette 523 1.2 christos void 524 1.2 christos unlinkpg(DB *dbp) 525 1.2 christos { 526 1.2 christos BTREE *t = dbp->internal; 527 1.2 christos PAGE *h = NULL; 528 1.2 christos pgno_t pg; 529 1.2 christos 530 1.2 christos for (pg = P_ROOT; pg < t->bt_mp->npages; 531 1.2 christos mpool_put(t->bt_mp, h, 0), pg++) { 532 1.3 christos if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 533 1.2 christos break; 534 1.2 christos /* Look for a nonempty leaf page that has both left 535 1.2 christos * and right siblings. */ 536 1.2 christos if (h->prevpg == P_INVALID || h->nextpg == P_INVALID) 537 1.2 christos continue; 538 1.2 christos if (NEXTINDEX(h) == 0) 539 1.2 christos continue; 540 1.2 christos if ((h->flags & (P_BLEAF | P_RLEAF))) 541 1.2 christos break; 542 1.2 christos } 543 1.2 christos if (h == NULL || pg == t->bt_mp->npages) { 544 1.2 christos errx(1, "%s: no appropriate page found", __func__); 545 1.2 christos return; 546 1.2 christos } 547 1.2 christos if (__bt_relink(t, h) != 0) { 548 1.2 christos perror("unlinkpg"); 549 1.2 christos goto cleanup; 550 1.2 christos } 551 1.2 christos h->prevpg = P_INVALID; 552 1.2 christos h->nextpg = P_INVALID; 553 1.2 christos cleanup: 554 1.2 christos mpool_put(t->bt_mp, h, MPOOL_DIRTY); 555 1.2 christos } 556 1.2 christos 557 1.1 pgoyette static u_int 558 1.1 pgoyette setflags(char *s) 559 1.1 pgoyette { 560 1.1 pgoyette char *p; 561 1.1 pgoyette 562 1.1 pgoyette for (; isspace((unsigned char)*s); ++s); 563 1.1 pgoyette if (*s == '\n' || *s == '\0') 564 1.1 pgoyette return 0; 565 1.1 pgoyette if ((p = strchr(s, '\n')) != NULL) 566 1.1 pgoyette *p = '\0'; 567 1.1 pgoyette if (!strcmp(s, "R_CURSOR")) return R_CURSOR; 568 1.1 pgoyette if (!strcmp(s, "R_FIRST")) return R_FIRST; 569 1.1 pgoyette if (!strcmp(s, "R_IAFTER")) return R_IAFTER; 570 1.1 pgoyette if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE; 571 1.1 pgoyette if (!strcmp(s, "R_LAST")) return R_LAST; 572 1.1 pgoyette if (!strcmp(s, "R_NEXT")) return R_NEXT; 573 1.1 pgoyette if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE; 574 1.1 pgoyette if (!strcmp(s, "R_PREV")) return R_PREV; 575 1.1 pgoyette if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR; 576 1.1 pgoyette 577 1.1 pgoyette errx(1, "line %zu: %s: unknown flag", lineno, s); 578 1.1 pgoyette /* NOTREACHED */ 579 1.1 pgoyette } 580 1.1 pgoyette 581 1.1 pgoyette static const char * 582 1.1 pgoyette sflags(int xflags) 583 1.1 pgoyette { 584 1.1 pgoyette switch (xflags) { 585 1.1 pgoyette case R_CURSOR: return "R_CURSOR"; 586 1.1 pgoyette case R_FIRST: return "R_FIRST"; 587 1.1 pgoyette case R_IAFTER: return "R_IAFTER"; 588 1.1 pgoyette case R_IBEFORE: return "R_IBEFORE"; 589 1.1 pgoyette case R_LAST: return "R_LAST"; 590 1.1 pgoyette case R_NEXT: return "R_NEXT"; 591 1.1 pgoyette case R_NOOVERWRITE: return "R_NOOVERWRITE"; 592 1.1 pgoyette case R_PREV: return "R_PREV"; 593 1.1 pgoyette case R_SETCURSOR: return "R_SETCURSOR"; 594 1.1 pgoyette } 595 1.1 pgoyette 596 1.1 pgoyette return "UNKNOWN!"; 597 1.1 pgoyette } 598 1.1 pgoyette 599 1.1 pgoyette static DBTYPE 600 1.1 pgoyette dbtype(const char *s) 601 1.1 pgoyette { 602 1.1 pgoyette if (!strcmp(s, "btree")) 603 1.1 pgoyette return DB_BTREE; 604 1.1 pgoyette if (!strcmp(s, "hash")) 605 1.1 pgoyette return DB_HASH; 606 1.1 pgoyette if (!strcmp(s, "recno")) 607 1.1 pgoyette return DB_RECNO; 608 1.1 pgoyette errx(1, "%s: unknown type (use btree, hash or recno)", s); 609 1.1 pgoyette /* NOTREACHED */ 610 1.1 pgoyette } 611 1.1 pgoyette 612 1.1 pgoyette static void * 613 1.1 pgoyette setinfo(DBTYPE dtype, char *s) 614 1.1 pgoyette { 615 1.1 pgoyette static BTREEINFO ib; 616 1.1 pgoyette static HASHINFO ih; 617 1.1 pgoyette static RECNOINFO rh; 618 1.1 pgoyette char *eq; 619 1.1 pgoyette 620 1.1 pgoyette if ((eq = strchr(s, '=')) == NULL) 621 1.1 pgoyette errx(1, "%s: illegal structure set statement", s); 622 1.1 pgoyette *eq++ = '\0'; 623 1.1 pgoyette if (!isdigit((unsigned char)*eq)) 624 1.1 pgoyette errx(1, "%s: structure set statement must be a number", s); 625 1.1 pgoyette 626 1.1 pgoyette switch (dtype) { 627 1.1 pgoyette case DB_BTREE: 628 1.1 pgoyette if (!strcmp("flags", s)) { 629 1.1 pgoyette ib.flags = atoi(eq); 630 1.1 pgoyette return &ib; 631 1.1 pgoyette } 632 1.1 pgoyette if (!strcmp("cachesize", s)) { 633 1.1 pgoyette ib.cachesize = atoi(eq); 634 1.1 pgoyette return &ib; 635 1.1 pgoyette } 636 1.1 pgoyette if (!strcmp("maxkeypage", s)) { 637 1.1 pgoyette ib.maxkeypage = atoi(eq); 638 1.1 pgoyette return &ib; 639 1.1 pgoyette } 640 1.1 pgoyette if (!strcmp("minkeypage", s)) { 641 1.1 pgoyette ib.minkeypage = atoi(eq); 642 1.1 pgoyette return &ib; 643 1.1 pgoyette } 644 1.1 pgoyette if (!strcmp("lorder", s)) { 645 1.1 pgoyette ib.lorder = atoi(eq); 646 1.1 pgoyette return &ib; 647 1.1 pgoyette } 648 1.1 pgoyette if (!strcmp("psize", s)) { 649 1.1 pgoyette ib.psize = atoi(eq); 650 1.1 pgoyette return &ib; 651 1.1 pgoyette } 652 1.1 pgoyette break; 653 1.1 pgoyette case DB_HASH: 654 1.1 pgoyette if (!strcmp("bsize", s)) { 655 1.1 pgoyette ih.bsize = atoi(eq); 656 1.1 pgoyette return &ih; 657 1.1 pgoyette } 658 1.1 pgoyette if (!strcmp("ffactor", s)) { 659 1.1 pgoyette ih.ffactor = atoi(eq); 660 1.1 pgoyette return &ih; 661 1.1 pgoyette } 662 1.1 pgoyette if (!strcmp("nelem", s)) { 663 1.1 pgoyette ih.nelem = atoi(eq); 664 1.1 pgoyette return &ih; 665 1.1 pgoyette } 666 1.1 pgoyette if (!strcmp("cachesize", s)) { 667 1.1 pgoyette ih.cachesize = atoi(eq); 668 1.1 pgoyette return &ih; 669 1.1 pgoyette } 670 1.1 pgoyette if (!strcmp("lorder", s)) { 671 1.1 pgoyette ih.lorder = atoi(eq); 672 1.1 pgoyette return &ih; 673 1.1 pgoyette } 674 1.1 pgoyette break; 675 1.1 pgoyette case DB_RECNO: 676 1.1 pgoyette if (!strcmp("flags", s)) { 677 1.1 pgoyette rh.flags = atoi(eq); 678 1.1 pgoyette return &rh; 679 1.1 pgoyette } 680 1.1 pgoyette if (!strcmp("cachesize", s)) { 681 1.1 pgoyette rh.cachesize = atoi(eq); 682 1.1 pgoyette return &rh; 683 1.1 pgoyette } 684 1.1 pgoyette if (!strcmp("lorder", s)) { 685 1.1 pgoyette rh.lorder = atoi(eq); 686 1.1 pgoyette return &rh; 687 1.1 pgoyette } 688 1.1 pgoyette if (!strcmp("reclen", s)) { 689 1.1 pgoyette rh.reclen = atoi(eq); 690 1.1 pgoyette return &rh; 691 1.1 pgoyette } 692 1.1 pgoyette if (!strcmp("bval", s)) { 693 1.1 pgoyette rh.bval = atoi(eq); 694 1.1 pgoyette return &rh; 695 1.1 pgoyette } 696 1.1 pgoyette if (!strcmp("psize", s)) { 697 1.1 pgoyette rh.psize = atoi(eq); 698 1.1 pgoyette return &rh; 699 1.1 pgoyette } 700 1.1 pgoyette break; 701 1.1 pgoyette } 702 1.1 pgoyette errx(1, "%s: unknown structure value", s); 703 1.1 pgoyette /* NOTREACHED */ 704 1.1 pgoyette } 705 1.1 pgoyette 706 1.1 pgoyette static void * 707 1.1 pgoyette rfile(char *name, size_t *lenp) 708 1.1 pgoyette { 709 1.1 pgoyette struct stat sb; 710 1.1 pgoyette void *p; 711 1.1 pgoyette int fd; 712 1.1 pgoyette char *np; 713 1.1 pgoyette 714 1.1 pgoyette for (; isspace((unsigned char)*name); ++name) 715 1.1 pgoyette continue; 716 1.1 pgoyette if ((np = strchr(name, '\n')) != NULL) 717 1.1 pgoyette *np = '\0'; 718 1.1 pgoyette if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1) 719 1.1 pgoyette err(1, "Cannot open `%s'", name); 720 1.1 pgoyette #ifdef NOT_PORTABLE 721 1.1 pgoyette if (sb.st_size > (off_t)SIZE_T_MAX) { 722 1.1 pgoyette errno = E2BIG; 723 1.1 pgoyette err("Cannot process `%s'", name); 724 1.1 pgoyette } 725 1.1 pgoyette #endif 726 1.1 pgoyette if ((p = malloc((size_t)sb.st_size)) == NULL) 727 1.1 pgoyette err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size); 728 1.1 pgoyette if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size) 729 1.1 pgoyette err(1, "read failed"); 730 1.1 pgoyette *lenp = (size_t)sb.st_size; 731 1.1 pgoyette (void)close(fd); 732 1.1 pgoyette return p; 733 1.1 pgoyette } 734 1.1 pgoyette 735 1.1 pgoyette static void * 736 1.1 pgoyette xcopy(void *text, size_t len) 737 1.1 pgoyette { 738 1.1 pgoyette void *p; 739 1.1 pgoyette 740 1.1 pgoyette if ((p = malloc(len)) == NULL) 741 1.1 pgoyette err(1, "Cannot allocate %zu bytes", len); 742 1.1 pgoyette (void)memmove(p, text, len); 743 1.1 pgoyette return p; 744 1.1 pgoyette } 745 1.1 pgoyette 746 1.1 pgoyette static void 747 1.1 pgoyette chkcmd(enum S state) 748 1.1 pgoyette { 749 1.1 pgoyette if (state != COMMAND) 750 1.1 pgoyette errx(1, "line %zu: not expecting command", lineno); 751 1.1 pgoyette } 752 1.1 pgoyette 753 1.1 pgoyette static void 754 1.1 pgoyette chkdata(enum S state) 755 1.1 pgoyette { 756 1.1 pgoyette if (state != DATA) 757 1.1 pgoyette errx(1, "line %zu: not expecting data", lineno); 758 1.1 pgoyette } 759 1.1 pgoyette 760 1.1 pgoyette static void 761 1.1 pgoyette chkkey(enum S state) 762 1.1 pgoyette { 763 1.1 pgoyette if (state != KEY) 764 1.1 pgoyette errx(1, "line %zu: not expecting a key", lineno); 765 1.1 pgoyette } 766 1.1 pgoyette 767 1.1 pgoyette static void 768 1.1 pgoyette usage(void) 769 1.1 pgoyette { 770 1.1 pgoyette (void)fprintf(stderr, 771 1.2 christos "Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] " 772 1.2 christos "type script\n", getprogname()); 773 1.1 pgoyette exit(1); 774 1.1 pgoyette } 775