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