summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2018-05-23 16:18:14 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2018-05-23 16:18:14 (GMT)
commit7edbff3372dfd387e4f6da45f43ba8ada6dfbe43 (patch) (side-by-side diff)
treed3049ab8c07773c82104e720f92be555ae276179
parent008b71a4d58ad33cf5a41e2aa55b9393e8420531 (diff)
downloadgdbm-7edbff3372dfd387e4f6da45f43ba8ada6dfbe43.tar.gz
gdbm-7edbff3372dfd387e4f6da45f43ba8ada6dfbe43.tar.bz2
gdbmtool: accept commands from command line as well as from file
Commands can be supplied to gdbmtool in three ways: 1. From a file, using the --file (-f) option: gdbmtool -f comfile 2. From the command line, if first argument is the database name: gdbmtool test.db count \; fetch mykey \; avail Note the use of semicolon to delimit commands. 3. From the interactive shell, if neither of the above is used. * src/Makefile.am: Add new sources. * src/gdbmtool.c: Use new stream functions for input. * src/gdbmtool.h (setsource): Remove proto. (instream_t): New data type. (instream_name, instream_read) (instream_close, instream_interactive) (instream_eq): New functions. (instream_stdin_create) (instream_argv_create) (instream_file_create) (interactive, input_context_push): New protos. * src/gram.y: Accept ; in place of newline. * src/input-argv.c: New file. * src/input-file.c: New file. * src/input-rl.c: Rewrite to provide instream_t API. * src/input-std.c: Likewise. * src/lex.l: Rewrite. * tests/.gitignore: Update. * tests/Makefile.am: Add new tests. Incorporate DejaGNU tests. * tests/config/default.exp: New file. * tests/gdbmtool/base.exp: New file. * tests/gdbmtool00.at: New file. * tests/gdbmtool01.at: New file. * tests/gdbmtool02.at: New file. * tests/testsuite.at: Include new tests.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--doc/gdbm.texi1
-rw-r--r--src/Makefile.am4
-rw-r--r--src/gdbmtool.c99
-rw-r--r--src/gdbmtool.h57
-rw-r--r--src/gram.y28
-rw-r--r--src/input-argv.c106
-rw-r--r--src/input-file.c88
-rw-r--r--src/input-rl.c153
-rw-r--r--src/input-std.c45
-rw-r--r--src/lex.l315
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/Makefile.am37
-rw-r--r--tests/config/default.exp221
-rw-r--r--tests/gdbmtool/base.exp9
-rw-r--r--tests/gdbmtool00.at37
-rw-r--r--tests/gdbmtool01.at37
-rw-r--r--tests/gdbmtool02.at31
-rw-r--r--tests/testsuite.at5
18 files changed, 995 insertions, 281 deletions
diff --git a/doc/gdbm.texi b/doc/gdbm.texi
index 2aac1c3..0876f8d 100644
--- a/doc/gdbm.texi
+++ b/doc/gdbm.texi
@@ -11,6 +11,7 @@
* GDBM: (gdbm). The GNU database manager.
* gdbm_dump: (gdbm) gdbm_dump. Dump the GDBM database into a flat file.
* gdbm_load: (gdbm) gdbm_load. Load the database from a flat file.
+* gdbmtool: (gdbm) gdbmtool. Examine and modify a GDBM database.
@end direntry
@end ifinfo
diff --git a/src/Makefile.am b/src/Makefile.am
index aff6982..766dba8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -96,6 +96,8 @@ gdbmtool_LDADD = \
gdbmtool_SOURCES = \
datconv.c\
gram.y\
+ input-argv.c\
+ input-file.c\
lex.l\
gdbmtool.h\
gdbmtool.c\
@@ -109,7 +111,7 @@ else
endif
AM_YFLAGS = -dtv
-#AM_LFLAGS = -d
+AM_LFLAGS = -d
gdbm_load_LDADD = ./libgdbmapp.a ./libgdbm.la
gdbm_dump_LDADD = ./libgdbmapp.a ./libgdbm.la
diff --git a/src/gdbmtool.c b/src/gdbmtool.c
index b322260..c522ad0 100644
--- a/src/gdbmtool.c
+++ b/src/gdbmtool.c
@@ -78,7 +78,7 @@ opendb (char *dbname)
if (open_mode == GDBM_NEWDB)
{
- if (interactive && variable_is_true ("confirm") &&
+ if (interactive () && variable_is_true ("confirm") &&
access (dbname, F_OK) == 0)
{
if (!getyn (_("database %s already exists; overwrite"), dbname))
@@ -1106,9 +1106,10 @@ void
source_handler (struct handler_param *param)
{
char *fname = tildexpand (PARAM_STRING (param, 0));
- if (setsource (fname, 0) == 0)
- yyparse ();
+ instream_t istr = instream_file_create (fname);
free (fname);
+ if (input_context_push (istr) == 0)
+ yyparse ();
}
@@ -1456,7 +1457,7 @@ command_lookup (const char *str, struct locus *loc, struct command **pcmd)
break;
case fcom_found:
- if (!interactive)
+ if (!interactive ())
{
state = fcom_abort;
found = NULL;
@@ -1479,9 +1480,8 @@ command_lookup (const char *str, struct locus *loc, struct command **pcmd)
}
if (state == fcom_init)
- lerror (loc,
- interactive ? _("Invalid command. Try ? for help.") :
- _("Unknown command"));
+ lerror (loc, interactive () ? _("Invalid command. Try ? for help.") :
+ _("Unknown command"));
if (!found)
return T_BOGUS;
@@ -1490,7 +1490,12 @@ command_lookup (const char *str, struct locus *loc, struct command **pcmd)
}
char *parseopt_program_doc = N_("examine and/or modify a GDBM database");
-char *parseopt_program_args = N_("DBFILE");
+char *parseopt_program_args = N_("DBFILE [COMMAND [ARG ...]]");
+
+enum {
+ OPT_LEX_TRACE = 256,
+ OPT_GRAM_TRACE
+};
struct gdbm_option optab[] = {
{ 'b', "block-size", N_("SIZE"), N_("set block size") },
@@ -1504,6 +1509,8 @@ struct gdbm_option optab[] = {
{ 'r', "read-only", NULL, N_("open database in read-only mode") },
{ 's', "synchronize", NULL, N_("synchronize to disk after each write") },
{ 'q', "quiet", NULL, N_("don't print initial banner") },
+ { OPT_LEX_TRACE, "lex-trace", NULL, N_("enable lexical analyzer traces") },
+ { OPT_GRAM_TRACE, "gram-trace", NULL, N_("enable grammar traces") },
{ 0 }
};
@@ -1786,7 +1793,7 @@ static struct gdbmarglist last_args;
void
run_last_command (void)
{
- if (interactive)
+ if (interactive ())
{
if (last_cmd)
{
@@ -1846,7 +1853,7 @@ run_command (struct command *cmd, struct gdbmarglist *arglist)
/* Optional argument */
break;
- if (!interactive)
+ if (!interactive ())
{
terror (_("%s: not enough arguments"), cmd->name);
return 1;
@@ -1896,7 +1903,7 @@ run_command (struct command *cmd, struct gdbmarglist *arglist)
pagfp = NULL;
expected_lines = 0;
- expected_lines_ptr = (interactive && pager) ? &expected_lines : NULL;
+ expected_lines_ptr = (interactive () && pager) ? &expected_lines : NULL;
if (!(cmd->begin && cmd->begin (&param, expected_lines_ptr)))
{
if (pager && expected_lines > get_screen_lines ())
@@ -1938,12 +1945,13 @@ run_command (struct command *cmd, struct gdbmarglist *arglist)
}
static void
-source_rcfile ()
+source_rcfile (void)
{
+ instream_t istr = NULL;
+
if (access (GDBMTOOLRC, R_OK) == 0)
{
- if (setsource (GDBMTOOLRC, 0) == 0)
- yyparse ();
+ istr = instream_file_create (GDBMTOOLRC);
}
else
{
@@ -1962,11 +1970,17 @@ source_rcfile ()
fname = mkfilename (p, GDBMTOOLRC, NULL);
if (access (fname, R_OK) == 0)
{
- if (setsource (fname, 0) == 0)
- yyparse ();
+ istr = instream_file_create (GDBMTOOLRC);
}
free (fname);
}
+
+ if (istr)
+ {
+ if (input_context_push (istr))
+ exit (EXIT_FATAL);
+ yyparse ();
+ }
}
#if GDBM_DEBUG_ENABLE
@@ -1984,12 +1998,12 @@ debug_printer (char const *fmt, ...)
int
main (int argc, char *argv[])
{
- int intr;
int opt;
int bv;
int norc = 0;
int res;
- char *source = "-";
+ char *source = NULL;
+ instream_t input = NULL;
set_progname (argv[0]);
#if GDBM_DEBUG_ENABLE
@@ -2005,8 +2019,6 @@ main (int argc, char *argv[])
sort_commands ();
/* Initialize variables. */
- intr = isatty (0);
- interactive = intr; /* Used early by input_init */
dsdef[DS_KEY] = dsegm_new_field (datadef_lookup ("string"), NULL, 1);
dsdef[DS_CONTENT] = dsegm_new_field (datadef_lookup ("string"), NULL, 1);
@@ -2014,6 +2026,7 @@ main (int argc, char *argv[])
variable_set ("pager", VART_STRING, getenv ("PAGER"));
input_init ();
+ lex_trace (0);
for (opt = parseopt_first (argc, argv, optab);
opt != EOF;
@@ -2022,7 +2035,6 @@ main (int argc, char *argv[])
{
case 'f':
source = optarg;
- intr = 0;
break;
case 'l':
@@ -2068,6 +2080,14 @@ main (int argc, char *argv[])
bv = 1;
variable_set ("quiet", VART_BOOL, &bv);
break;
+
+ case OPT_LEX_TRACE:
+ lex_trace (1);
+ break;
+
+ case OPT_GRAM_TRACE:
+ gram_trace (1);
+ break;
default:
terror (_("unknown option; try `%s -h' for more info"),
@@ -2078,14 +2098,30 @@ main (int argc, char *argv[])
argc -= optind;
argv += optind;
- if (argc > 1)
+ if (source && strcmp (source, "-"))
{
- terror (_("too many arguments"));
- exit (EXIT_USAGE);
+ input = instream_file_create (source);
+ if (!input)
+ exit (1);
+ }
+
+ if (argc >= 1)
+ {
+ file_name = estrdup (argv[0]);
+ argc--;
+ argv++;
+ if (argc)
+ {
+ if (input)
+ {
+ terror (_("--file and and command cannot be used together"));
+ exit (EXIT_USAGE);
+ }
+ input = instream_argv_create (argc, argv);
+ if (!input)
+ exit (1);
+ }
}
-
- if (argc == 1)
- file_name = estrdup (argv[0]);
signal (SIGPIPE, SIG_IGN);
@@ -2093,13 +2129,16 @@ main (int argc, char *argv[])
argmax = 0;
if (!norc)
- source_rcfile ();
+ source_rcfile ();
+
+ if (!input)
+ input = instream_stdin_create ();
/* Welcome message. */
- if (intr && !variable_is_true ("quiet"))
+ if (instream_interactive (input) && !variable_is_true ("quiet"))
printf (_("\nWelcome to the gdbm tool. Type ? for help.\n\n"));
- if (setsource (source, intr))
+ if (input_context_push (input))
exit (EXIT_FATAL);
res = yyparse ();
input_done ();
diff --git a/src/gdbmtool.h b/src/gdbmtool.h
index 8a7725c..c472276 100644
--- a/src/gdbmtool.h
+++ b/src/gdbmtool.h
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
+#include <limits.h>
#ifndef GDBM_ARG_UNUSED
# define GDBM_ARG_UNUSED __attribute__ ((__unused__))
@@ -107,19 +108,67 @@ void terror (const char *fmt, ...)
char *make_prompt (void);
-int setsource (const char *filename, int intr);
-
extern char *file_name;
-extern int interactive;
extern int open_mode;
#define GDBMTOOLRC ".gdbmtoolrc"
#define GDBMTOOL_DEFFILE "junk.gdbm"
-ssize_t input_read (FILE *fp, char *buf, size_t size);
+typedef struct instream *instream_t;
+
+struct instream
+{
+ char *in_name; /* Input stream name */
+ int in_inter; /* True if this is an interactive stream */
+ ssize_t (*in_read) (instream_t, char*, size_t);
+ /* Read from stream */
+ void (*in_close) (instream_t);
+ /* Close the stream */
+ int (*in_eq) (instream_t, instream_t);
+ /* Return true if both streams refer to the
+ same input */
+};
+
+static inline char const *
+instream_name (instream_t in)
+{
+ return in->in_name;
+}
+
+static inline ssize_t
+instream_read (instream_t in, char *buf, size_t size)
+{
+ return in->in_read (in, buf, size);
+}
+
+static inline void
+instream_close (instream_t in)
+{
+ in->in_close (in);
+}
+
+static inline int
+instream_interactive (instream_t in)
+{
+ return in->in_inter;
+}
+
+static inline int
+instream_eq (instream_t a, instream_t b)
+{
+ return a->in_eq (a, b);
+}
+
void input_init (void);
void input_done (void);
+instream_t instream_stdin_create (void);
+instream_t instream_argv_create (int argc, char **argv);
+instream_t instream_file_create (char const *name);
+
+int interactive (void);
+int input_context_push (instream_t);
+
struct handler_param;
void input_history_handler (struct handler_param *param);
int input_history_begin (struct handler_param *param, size_t *exp_count);
diff --git a/src/gram.y b/src/gram.y
index 57d90c5..5372795 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -71,20 +71,20 @@ stmtlist : stmt
| stmtlist stmt
;
-stmt : /* empty */ '\n'
+stmt : /* empty */ eol
{
run_last_command ();
}
- | T_CMD arglist '\n'
+ | T_CMD arglist eol
{
- if (run_command ($1, &$2) && !interactive)
+ if (run_command ($1, &$2) && !interactive ())
exit (EXIT_USAGE);
}
- | set '\n'
- | defn '\n'
- | T_BOGUS '\n'
+ | set eol
+ | defn eol
+ | T_BOGUS eol
{
- if (interactive)
+ if (interactive ())
{
yyclearin;
yyerrok;
@@ -92,9 +92,9 @@ stmt : /* empty */ '\n'
else
YYERROR;
}
- | error { end_def(); } '\n'
+ | error { end_def(); } eol
{
- if (interactive)
+ if (interactive ())
{
yyclearin;
yyerrok;
@@ -104,6 +104,10 @@ stmt : /* empty */ '\n'
}
;
+eol : '\n'
+ | ';'
+ ;
+
arglist : /* empty */
{
gdbmarglist_init (&$$, NULL);
@@ -367,3 +371,9 @@ yyerror (char const *s)
terror ("%s", s);
return 0;
}
+
+void
+gram_trace (int n)
+{
+ yydebug = 1;
+}
diff --git a/src/input-argv.c b/src/input-argv.c
new file mode 100644
index 0000000..278398c
--- a/dev/null
+++ b/src/input-argv.c
@@ -0,0 +1,106 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+
+ GDBM is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GDBM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbmtool.h"
+#include <stdlib.h>
+
+struct instream_argv
+{
+ struct instream base; /* Base structure */
+ int argc; /* Number of arguments */
+ char **argv; /* Vector of arguments */
+ int idx; /* Index of the current argument */
+ char *cur; /* Current position in argv[idx] */
+ int delim; /* True if cur points to a delimiter */
+};
+
+static ssize_t
+instream_argv_read (instream_t istr, char *buf, size_t size)
+{
+ size_t len, total = 0;
+ struct instream_argv *i = (struct instream_argv*)istr;
+
+ while (total < size)
+ {
+ if (*i->cur == 0)
+ {
+ if (i->idx == i->argc)
+ {
+ if (!i->delim)
+ {
+ i->cur = "\n";
+ i->delim = 1;
+ }
+ else
+ break;
+ }
+ else if (!i->delim)
+ {
+ i->cur = " ";
+ i->delim = 1;
+ }
+ else
+ {
+ i->cur = i->argv[i->idx++];
+ i->delim = 0;
+ }
+ }
+
+ len = strlen (i->cur);
+ if (len > size - total)
+ len = size - total;
+ memcpy (buf + total, i->cur, len);
+ i->cur += len;
+ total += len;
+ }
+ return total;
+}
+
+static void
+instream_argv_close (instream_t istr)
+{
+ struct instream_argv *i = (struct instream_argv *)istr;
+ free (i);
+}
+
+static int
+instream_argv_eq (instream_t a, instream_t b)
+{
+ return 0;
+}
+
+instream_t
+instream_argv_create (int argc, char **argv)
+{
+ struct instream_argv *istr;
+
+ istr = emalloc (sizeof *istr);
+ istr->base.in_name = "argv";
+ istr->base.in_inter = 0;
+ istr->base.in_read = instream_argv_read;
+ istr->base.in_close = instream_argv_close;
+ istr->base.in_eq = instream_argv_eq;
+
+ istr->argc = argc;
+ istr->argv = argv;
+ istr->idx = 0;
+ istr->cur = "";
+ istr->delim = 1;
+
+ return (instream_t) istr;
+}
+
+
diff --git a/src/input-file.c b/src/input-file.c
new file mode 100644
index 0000000..5cbb030
--- a/dev/null
+++ b/src/input-file.c
@@ -0,0 +1,88 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+
+ GDBM is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GDBM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbmtool.h"
+
+struct instream_file
+{
+ struct instream base; /* Base structure */
+ FILE *fp; /* Opened file */
+ dev_t dev; /* Device number */
+ ino_t ino; /* Inode number */
+};
+
+static ssize_t
+instream_file_read (instream_t istr, char *buf, size_t size)
+{
+ struct instream_file *file = (struct instream_file *)istr;
+ return fread (buf, 1, size, file->fp);
+}
+
+static void
+instream_file_close (instream_t istr)
+{
+ struct instream_file *file = (struct instream_file *)istr;
+ fclose (file->fp);
+ free (file->base.in_name);
+ free (file);
+}
+
+static int
+instream_file_eq (instream_t a, instream_t b)
+{
+ struct instream_file *file_a = (struct instream_file *)a;
+ struct instream_file *file_b = (struct instream_file *)b;
+ return file_a->dev == file_b->dev && file_a->ino == file_b->ino;
+}
+
+instream_t
+instream_file_create (char const *name)
+{
+ struct instream_file *istr;
+ struct stat st;
+ FILE *fp;
+
+ if (stat (name, &st))
+ {
+ terror (_("cannot open `%s': %s"), name, strerror (errno));
+ return NULL;
+ }
+ else if (!S_ISREG (st.st_mode))
+ {
+ terror (_("%s is not a regular file"), name);
+ return NULL;
+ }
+
+ fp = fopen (name, "r");
+ if (!fp)
+ {
+ terror (_("cannot open %s for reading: %s"), name,
+ strerror (errno));
+ return NULL;
+ }
+
+ istr = emalloc (sizeof *istr);
+ istr->base.in_name = estrdup (name);
+ istr->base.in_inter = 0;
+ istr->base.in_read = instream_file_read;
+ istr->base.in_close = instream_file_close;
+ istr->base.in_eq = instream_file_eq;
+ istr->fp = fp;
+ istr->dev = st.st_dev;
+ istr->ino = st.st_ino;
+
+ return (instream_t) istr;
+}
diff --git a/src/input-rl.c b/src/input-rl.c
index d09feff..695d351 100644
--- a/src/input-rl.c
+++ b/src/input-rl.c
@@ -62,64 +62,6 @@ retrieve_history (char *str)
return 0;
}
-ssize_t
-input_read (FILE *fp, char *buf, size_t size)
-{
- static char *input_line;
- static size_t input_length;
- static size_t input_off;
-#define input_ptr() (input_line + input_off)
-#define input_size() (input_length - input_off)
-
- if (interactive)
- {
- size_t len = input_size ();
- if (!len)
- {
- if (input_line)
- {
- newline:
- free (input_line);
- input_line = NULL;
- buf[0] = '\n';
- return 1;
- }
- else
- {
- char *prompt;
- again:
- prompt = make_prompt ();
- input_line = readline (prompt);
- free (prompt);
- if (!input_line)
- return 0;
- input_length = strlen (input_line);
- input_off = 0;
- if (input_length)
- {
- if (retrieve_history (input_line))
- {
- free (input_line);
- goto again;
- }
- }
- else
- goto newline;
- len = input_size ();
- add_history (input_line);
- }
- }
-
- if (len > size)
- len = size;
- memcpy (buf, input_ptr (), len);
- input_off += len;
-
- return len;
- }
- return fread (buf, 1, size, fp);
-}
-
struct history_param
{
int from;
@@ -180,7 +122,7 @@ input_history_handler (struct handler_param *param)
#define HISTFILE_SUFFIX "_history"
static char *
-get_history_file_name ()
+get_history_file_name (void)
{
static char *filename = NULL;
@@ -222,14 +164,99 @@ input_init (void)
rl_readline_name = (char *) progname;
rl_attempted_completion_function = shell_completion;
rl_pre_input_hook = pre_input;
- if (interactive)
- read_history (get_history_file_name ());
+ read_history (get_history_file_name ());
}
void
input_done (void)
{
- if (interactive)
- write_history (get_history_file_name ());
+ write_history (get_history_file_name ());
+}
+
+static void
+instream_stdin_close (instream_t istr)
+{
+ free (istr);
+}
+
+static ssize_t
+stdin_read_readline (instream_t istr, char *buf, size_t size)
+{
+ static char *input_line;
+ static size_t input_length;
+ static size_t input_off;
+#define input_ptr() (input_line + input_off)
+#define input_size() (input_length - input_off)
+ size_t len = input_size ();
+ if (!len)
+ {
+ if (input_line)
+ {
+ newline:
+ free (input_line);
+ input_line = NULL;
+ buf[0] = '\n';
+ return 1;
+ }
+ else
+ {
+ char *prompt;
+ again:
+ prompt = make_prompt ();
+ input_line = readline (prompt);
+ free (prompt);
+ if (!input_line)
+ return 0;
+ input_length = strlen (input_line);
+ input_off = 0;
+ if (input_length)
+ {
+ if (retrieve_history (input_line))
+ {
+ free (input_line);
+ goto again;
+ }
+ }
+ else
+ goto newline;
+ len = input_size ();
+ add_history (input_line);
+ }
+ }
+
+ if (len > size)
+ len = size;
+ memcpy (buf, input_ptr (), len);
+ input_off += len;
+
+ return len;
+}
+
+static ssize_t
+instream_stdin_read (instream_t istr, char *buf, size_t size)
+{
+ if (istr->in_inter)
+ return stdin_read_readline (istr, buf, size);
+ return fread (buf, 1, size, stdin);
+}
+
+static int
+instream_stdin_eq (instream_t a, instream_t b)
+{
+ return 0;
}
+instream_t
+instream_stdin_create (void)
+{
+ struct instream *istr;
+
+ istr = emalloc (sizeof *istr);
+ istr->in_name = "stdin";
+ istr->in_inter = isatty (fileno (stdin));
+ istr->in_read = instream_stdin_read;
+ istr->in_close = instream_stdin_close;
+ istr->in_eq = instream_stdin_eq;
+
+ return istr;
+}
diff --git a/src/input-std.c b/src/input-std.c
index e4f883d..45ae506 100644
--- a/src/input-std.c
+++ b/src/input-std.c
@@ -16,17 +16,42 @@
#include "gdbmtool.h"
-ssize_t
-input_read (FILE *fp, char *buf, size_t size)
+static ssize_t
+instream_stdin_read (instream_t istr, char *buf, size_t size)
{
- if (interactive)
- {
- print_prompt_at_bol ();
- if (fgets (buf, size, fp) == NULL)
- return 0;
- return strlen (buf);
- }
- return fread (buf, 1, size, fp);
+ if (istr->in_inter)
+ print_prompt_at_bol ();
+ if (fgets (buf, size, stdin) == NULL)
+ return 0;
+ return strlen (buf);
+}
+
+static void
+instream_stdin_close (instream_t istr)
+{
+ free (istr);
+}
+
+static int
+instream_stdin_eq (instream_t a, instream_t b)
+{
+ return 0;
+}
+
+instream_t
+instream_stdin_create (void)
+{
+ struct instream_file *istr;
+
+ istr = emalloc (sizeof *istr);
+ istr->base.in_name = "stdin";
+ istr->base.in_inter = isatty (fileno (stdin));
+ istr->base.in_read = instream_stdin_read;
+ istr->base.in_close = instream_stdin_close;
+ istr->base.in_eq = instream_stdin_eq;
+ istr->fp = stdin;
+
+ return istr;
}
void
diff --git a/src/lex.l b/src/lex.l
index a2a8ee8..0318031 100644
--- a/src/lex.l
+++ b/src/lex.l
@@ -19,14 +19,23 @@
#include "gdbmtool.h"
#include "gram.h"
-struct point point;
-
+struct context /* Input context */
+{
+ struct context *parent; /* Pointer to the parent context */
+ struct locus locus; /* Locus */
+ struct point point;
+ YY_BUFFER_STATE buf; /* Buffer */
+ instream_t input;
+};
+
+static struct context *context_tos;
+
/* Advance locus to the next line */
void
-advance_line ()
+advance_line (void)
{
- ++point.line;
- point.col = 0;
+ ++context_tos->point.line;
+ context_tos->point.col = 0;
}
#define YY_USER_ACTION \
@@ -34,11 +43,11 @@ advance_line ()
{ \
if (YYSTATE == 0) \
{ \
- yylloc.beg = point; \
+ yylloc.beg = context_tos->point; \
yylloc.beg.col++; \
} \
- point.col += yyleng; \
- yylloc.end = point; \
+ context_tos->point.col += yyleng; \
+ yylloc.end = context_tos->point; \
} \
while (0);
@@ -46,7 +55,10 @@ advance_line ()
#define YY_INPUT(buf,result,max_size) \
do \
{ \
- result = input_read (yyin, buf, max_size); \
+ if (context_tos) \
+ result = instream_read (context_tos->input, buf, max_size); \
+ else \
+ result = 0; \
} \
while (0);
@@ -56,146 +68,112 @@ void string_addc (int c);
char *string_end (void);
int unescape (int c);
-struct context /* Input context */
+int
+interactive (void)
{
- struct context *parent; /* Pointer to the parent context */
- struct locus locus; /* Locus */
- struct point point;
- int interactive;
- ino_t ino; /* Inode number */
- dev_t dev; /* Device number */
- FILE *file; /* Input file */
- YY_BUFFER_STATE buf; /* Buffer */
-};
+ return context_tos && instream_interactive (context_tos->input);
+}
+
+static struct context *
+input_context_lookup (instream_t istr)
+{
+ struct context *cp;
-static struct context *context_tos;
-static ino_t ino;
-static dev_t dev;
-int interactive; /* Are we running in interactive mode? */
-static int initialized;
+ for (cp = context_tos; cp; cp = cp->parent)
+ if (instream_eq (cp->input, istr))
+ break;
+ return cp;
+}
-static void
-context_push ()
+int
+input_context_push (instream_t input)
{
- struct context *cp = ecalloc (1, sizeof (*cp));
+ struct context *cp;
+
+ cp = input_context_lookup (input);
+ if (cp)
+ {
+ terror (_("recursive sourcing"));
+ if (cp->parent)
+ lerror (&cp->locus, _("%s already sourced here"),
+ instream_name (input));
+ return 1;
+ }
+ yy_switch_to_buffer (yy_create_buffer (NULL, YY_BUF_SIZE));
+
+ /* Create new context */
+
+ cp = ecalloc (1, sizeof (*cp));
cp->locus = yylloc;
- cp->point = point;
- cp->interactive = interactive;
- cp->ino = ino;
- cp->dev = dev;
- cp->file = yyin;
+ cp->point.file = estrdup (instream_name (input));
+ cp->point.line = 1;
+ cp->point.col = 0;
+
+ cp->input = input;
cp->buf = YY_CURRENT_BUFFER;
cp->parent = context_tos;
context_tos = cp;
+
+ return 0;
}
+void
+lex_trace (int n)
+{
+ yy_flex_debug = n;
+}
+
int
-context_pop ()
+input_context_pop (void)
{
- struct context *cp = context_tos;
+ struct context *cp;
- fclose (yyin);
- yyin = NULL;
- free (point.file);
- point.file = NULL;
+ if (!context_tos)
+ return 1;
+ instream_close (context_tos->input);
+ free (context_tos->point.file);
memset (&yylloc, 0, sizeof (yylloc));
-
+ cp = context_tos->parent;
+ free (context_tos);
+ context_tos = cp;
if (!cp)
return 1;
- context_tos = cp->parent;
-
yylloc = cp->locus;
- point = cp->point;
- interactive = cp->interactive;
- ino = cp->ino;
- dev = cp->dev;
- yyin = cp->file;
yy_delete_buffer (YY_CURRENT_BUFFER);
yy_switch_to_buffer (cp->buf);
return 0;
}
-static struct context *
-findctx (struct stat *st)
-{
- struct context *cp;
-
- for (cp = context_tos; cp; cp = cp->parent)
- if (cp->dev == st->st_dev && cp->ino == st->st_ino)
- break;
- return cp;
-}
-
-int
-setsource (const char *name, int intr)
+static int
+t_num (int base)
{
- struct stat st;
- struct context *cp;
- FILE *fp;
-
- if (strcmp (name, "-") == 0)
+ long n;
+ errno = 0;
+ n = strtol (yytext, NULL, base);
+ if (errno)
{
- fp = stdin;
- name = "stdin";
+ lerror (&yylloc, "%s", strerror (errno));
+ return T_BOGUS;
}
- else
+ if (n < INT_MIN || n > INT_MAX)
{
- if (stat (name, &st))
- {
- terror (_("cannot open `%s': %s"), name, strerror (errno));
- return -1;
- }
- else if (!S_ISREG (st.st_mode))
- {
- terror (_("%s is not a regular file"), name);
- return -1;
- }
-
- cp = findctx (&st);
- if (cp)
- {
- terror (_("recursive sourcing"));
- if (cp->parent)
- lerror (&cp->locus, _("%s already sourced here"), name);
- return 1;
- }
-
- fp = fopen (name, "r");
- if (!fp)
- {
- terror (_("cannot open %s for reading: %s"), name,
- strerror (errno));
- return 1;
- }
+ lerror (&yylloc, "value out of range");
+ return T_BOGUS;
}
-
- if (yyin)
- context_push ();
-
- yyin = fp;
- yy_switch_to_buffer (yy_create_buffer (yyin, YY_BUF_SIZE));
-
- interactive = intr;
- dev = st.st_dev;
- ino = st.st_ino;
-
- point.file = estrdup (name);
- point.line = 1;
- point.col = 0;
-
- initialized = 1;
-
- return 0;
+ yylval.num = n;
+ return T_NUM;
}
+
%}
%option noinput
%option nounput
+%option nodefault
-%x STR MLSTR DEF
+%x CMD STR MLSTR DEF
WS [ \t][ \t]*
IDENT [a-zA-Z_][a-zA-Z_0-9-]*
@@ -238,67 +216,82 @@ O [0-7]
REJECT;
}
if (file)
- point.file = file;
- point.line = line;
- point.col = 0;
+ context_tos->point.file = file;
+ context_tos->point.line = line;
+ context_tos->point.col = 0;
}
#.*\n advance_line ();
#.* /* end-of-file comment */;
-<DEF>off { return T_OFF; }
-<DEF>pad { return T_PAD; }
-<DEF>0[xX]{X}{X}* { yylval.num = strtoul (yytext, NULL, 16);
- return T_NUM; };
-<DEF>0{O}{O}* { yylval.num = strtoul (yytext, NULL, 8);
- return T_NUM; };
-<DEF>0|{P} { yylval.num = strtoul (yytext, NULL, 10);
- return T_NUM; };
-^[ \t]*\? { return command_lookup ("help", &yylloc, &yylval.cmd); }
-^[ \t]*{IDENT} { char *p = yytext + strspn (yytext, " \t");
- return command_lookup (p, &yylloc, &yylval.cmd);
- }
-<DEF>{IDENT} { if ((yylval.type = datadef_lookup (yytext)))
- return T_TYPE;
- else
- {
- yylval.string = estrdup (yytext);
- return T_IDENT;
- }
- }
-{IDENT} { yylval.string = estrdup (yytext);
- return T_IDENT;
- }
-<INITIAL,DEF>[^ \"\t\n\[\]{},=]+ { yylval.string = estrdup (yytext);
- return T_WORD; }
-\"[^\\\"\n]*\" { yylval.string = emalloc (yyleng - 1);
+<INITIAL>{
+\? { BEGIN (CMD);
+ return command_lookup ("help", &yylloc, &yylval.cmd); }
+{IDENT} { BEGIN (CMD);
+ return command_lookup (yytext, &yylloc, &yylval.cmd); }
+{WS} ;
+}
+
+<DEF>{
+off { return T_OFF; }
+pad { return T_PAD; }
+0[xX]{X}{X}* { return t_num (8); };
+0{O}{O}* { return t_num (16); };
+0|{P} { return t_num (10); };
+{IDENT} { if ((yylval.type = datadef_lookup (yytext)))
+ return T_TYPE;
+ else
+ {
+ yylval.string = estrdup (yytext);
+ return T_IDENT;
+ }
+ }
+[^ \"\t\n;\[\]{},=]+ { yylval.string = estrdup (yytext); return T_WORD; }
+\n { advance_line (); }
+{WS} ;
+. return yytext[0];
+}
+
+<CMD>{
+{IDENT} { yylval.string = estrdup (yytext); return T_IDENT; }
+[^ \"\t\n;\[\]{},=]+ { yylval.string = estrdup (yytext); return T_WORD; }
+\"[^\\\"\n]*\" {
+ yylval.string = emalloc (yyleng - 1);
memcpy (yylval.string, yytext+1, yyleng-2);
yylval.string[yyleng-2] = 0;
return T_WORD; }
-\"[^\\\"\n]*\\$ { string_begin ();
+\"[^\\\"\n]*\\$ {
+ string_begin ();
string_add (yytext + 1, yyleng - 2);
BEGIN (MLSTR); }
-\"[^\\\"\n]*\\. { string_begin ();
+\"[^\\\"\n]*\\. {
+ string_begin ();
string_add (yytext + 1, yyleng - 3);
string_addc (unescape (yytext[yyleng-1]));
BEGIN (STR); }
-<STR,MLSTR>[^\\\"\n]*\" { if (yyleng > 1)
- string_add (yytext, yyleng - 1);
- yylval.string = string_end ();
- BEGIN (INITIAL);
- return T_WORD; }
-<STR,MLSTR>[^\\\"\n]*\\$ { string_add (yytext, yyleng - 1); }
-<STR,MLSTR>[^\\\"\n]*\\. { string_add (yytext, yyleng - 2);
- string_addc (unescape (yytext[yyleng-1])); }
-<INITIAL,DEF>{WS} ;
-<DEF>\n { advance_line (); }
-\n { advance_line (); return '\n'; }
-<INITIAL,DEF>. return yytext[0];
+; { BEGIN (INITIAL); return ';'; }
+{WS} ;
+}
+
+<STR,MLSTR>{
+[^\\\"\n]*\" { if (yyleng > 1)
+ string_add (yytext, yyleng - 1);
+ yylval.string = string_end ();
+ BEGIN (CMD);
+ return T_WORD; }
+[^\\\"\n]*\\$ { string_add (yytext, yyleng - 1); }
+[^\\\"\n]*\\. { string_add (yytext, yyleng - 2);
+ string_addc (unescape (yytext[yyleng-1])); }
+}
+
+<*>\n { BEGIN (INITIAL); advance_line (); return '\n'; }
+
+<INITIAL,CMD,DEF>. return yytext[0];
%%
int
-yywrap ()
+yywrap (void)
{
- return context_pop ();
+ return input_context_pop ();
}
void
@@ -310,7 +303,7 @@ begin_def (void)
void
end_def (void)
{
- BEGIN (INITIAL);
+ BEGIN (CMD);
}
void
@@ -427,9 +420,9 @@ escape (int c)
void
vlerror (struct locus *loc, const char *fmt, va_list ap)
{
- if (!interactive)
+ if (!interactive ())
fprintf (stderr, "%s: ", progname);
- if (initialized && loc && loc->beg.file)
+ if (loc && loc->beg.file)
{
YY_LOCATION_PRINT (stderr, *loc);
fprintf (stderr, ": ");
diff --git a/tests/.gitignore b/tests/.gitignore
index a339f8d..7aef06b 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -24,4 +24,5 @@ num2word
package.m4
testsuite
testsuite.dir
-testsuite.log
+*.log
+*.sum
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b85110d..409ce0b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,7 +22,6 @@ EXTRA_DIST = \
DISTCLEANFILES = atconfig
MAINTAINERCLEANFILES = Makefile.in $(TESTSUITE)
-
## ------------ ##
## package.m4. ##
## ------------ ##
@@ -73,6 +72,9 @@ TESTSUITE_AT = \
delete00.at\
delete01.at\
delete02.at\
+ gdbmtool00.at\
+ gdbmtool01.at\
+ gdbmtool02.at\
fetch00.at\
fetch01.at\
setopt00.at\
@@ -135,4 +137,35 @@ dtfetch_LDADD = ../src/libgdbm.la ../compat/libgdbm_compat.la
dtdel_LDADD = ../src/libgdbm.la ../compat/libgdbm_compat.la
d_creat_ce_LDADD = ../src/libgdbm.la ../compat/libgdbm_compat.la
-
+## -------------------- ##
+## DejaGNU Test suite. ##
+## -------------------- ##
+AUTOMAKE_OPTIONS = dejagnu
+DEJATOOL = gdbmtool
+RUNTESTFLAGS =
+CLEANFILES = *.log
+
+site.exp: Makefile
+ @echo 'Making a new site.exp file...'
+ @test ! -f site.bak || rm -f site.bak
+ @echo '## these variables are automatically generated by make ##' > $@-t
+ @echo '# Do not edit here. If you wish to override these values' >> $@-t
+ @echo '# edit the last section' >> $@-t
+ @echo 'set tool $(DEJATOOL)' >> $@-t
+ @echo "set top_srcdir `cd $(top_srcdir); pwd`" >> $@-t
+ @echo "set top_builddir `cd $(top_builddir); pwd`" >> $@-t
+ @echo "set srcdir `cd $(srcdir); pwd`" >> $@-t
+ @echo 'set objdir' `pwd` >> $@-t
+ @echo 'set host_alias "$(host_alias)"' >> $@-t
+ @echo 'set host_triplet $(host_triplet)' >> $@-t
+ @echo 'set target_alias "$(target_alias)"' >> $@-t
+ @echo 'set target_triplet $(target_triplet)' >> $@-t
+ @echo 'set build_alias "$(build_alias)"' >> $@-t
+ @echo 'set build_triplet $(build_triplet)' >> $@-t
+ @echo '## All variables above are generated by configure. Do Not Edit ##' >> $@-t
+ @test ! -f site.exp || sed '1,/^## All variables above are.*##/ d' site.exp >> $@-t
+ @test ! -f site.exp || mv site.exp site.bak
+ @mv $@-t site.exp
+
+EXTRA_DIST += \
+ gdbmtool/base.exp
diff --git a/tests/config/default.exp b/tests/config/default.exp
new file mode 100644
index 0000000..7802ea6
--- a/dev/null
+++ b/tests/config/default.exp
@@ -0,0 +1,221 @@
+verbose "STARTED" 1
+
+# Make sure we use POSIX locale
+set env(LC_ALL) "C"
+
+set gdbmtool_prompt "gdbmtool> "
+
+proc gdbmtool_start {args} {
+ global gdbmtool_spawn_id
+ global top_builddir
+ global gdbmtool_prompt
+
+ set cmd "$top_builddir/src/gdbmtool -q $args"
+ verbose "running $cmd" 1
+
+ set gdbmtool_spawn_id [remote_spawn host $cmd]
+ if { $gdbmtool_spawn_id < 0 || $gdbmtool_spawn_id == "" } {
+ perror "Spawning $cmd failed."
+ return 1;
+ }
+
+ remote_expect host 60 {
+ -re "\[\r\n\]?${gdbmtool_prompt}$" {
+ verbose "gdbmtool initialized."
+ }
+ default {
+ perror "gdbmtool not initialized"
+ return 1
+ }
+ }
+ return 0
+}
+
+proc gdbmtool_stop {} {
+ verbose "Stopping gdbmtool"
+ gdbmtool_command "quit"
+ remote_close host
+}
+
+proc gdbmtool_send {string} {
+ return [remote_send host "$string"]
+}
+
+proc gdbmtool_command {cmd} {
+ set res [gdbmtool_send "$cmd\n"]
+ remote_expect host 60 {
+ -ex "\r\n" { }
+ default {
+ perror "gdbmtool_command for target failed";
+ return -1
+ }
+ }
+ verbose "RESULT: $res" 2
+ return $res
+}
+
+proc gdbmtool_test { command expcode } {
+ gdbmtool_command $command
+ uplevel remote_expect host 60 $expcode
+# set code [catch \
+# {uplevel remote_expect host 60 $expcode} string];
+}
+
+# mu_test COMMAND PATTERN
+# COMMAND - Command to send to the program
+# PATTERN - A list of strings to expect in return
+# Return value:
+# -3 - eof
+# -2 - timeout
+# -1 - generic failure
+# 1 - test fails
+# 0 - test succeeds
+proc seq_test { args } {
+ set command [lindex $args 0]
+ set pattern [lindex $args 1]
+
+ set result -1
+ if { "${command}" != "" } {
+ set res [gdbmtool_command "${command}"]
+ if { $res != "" } {
+ return $result;
+ }
+ }
+
+ global timeout;
+ if [info exists timeout] {
+ set tmt $timeout;
+ } else {
+ set tmt 60;
+ }
+
+ set result 0
+ for {set i 0} {$result == 0 && $i < [llength $pattern]} {incr i} {
+ set regexp 0
+ switch -regexp -- "[lindex ${pattern} $i]" {
+ ^-re.*$ { set regexp 1; incr i }
+ ^-- { incr i }
+ }
+
+ regsub "\[ \t\]*$" [lindex ${pattern} $i] "" pat
+ verbose "i=$i, pat=$pat" 2
+
+ if {$regexp} {
+ verbose "REGEX for $pat / [llength $pat] " 3
+ remote_expect host $tmt {
+ -re "$pat\[ \r\t\]*\r\n" { }
+ default {
+ set result 1
+ break
+ }
+ timeout {
+ set result -2
+ break
+ }
+ eof {
+ set result -3
+ break
+ }
+ }
+ } else {
+ remote_expect host $tmt {
+ -ex "$pat" {
+ # go on
+ }
+ default {
+ set result 1
+ break
+ }
+ timeout {
+ set result -2
+ break
+ }
+ eof {
+ set result -3
+ break
+ }
+ }
+
+ if {$result == 0} {
+ remote_expect host $tmt {
+ -re "\[ \t]*\r\n" { }
+ default { set result 1 }
+ timeout { set result -2 }
+ eof { set result -3 }
+ }
+ }
+ }
+ }
+ return $result
+}
+
+# mail_test [-message MESSAGE][-default (FAIL|XFAIL)][-noprompt]
+# COMMAND PATTERN [PATTERN...]
+# COMMAND - Command to send to mail.
+# PATTERN - Sequence to expect in return.
+# MESSAGE - [optional] message to output
+proc gdbmtool_test { args } {
+ global gdbmtool_prompt
+ global suppress_flag;
+ upvar timeout timeout
+
+ set default ""
+ set message ""
+ set wait_for_prompt 1
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ set a [lindex $args $i]
+ if {"$a" == "-default"} {
+ set default [lindex $args [expr $i + 1]]
+ incr i
+ } elseif {"$a" == "-message"} {
+ set message [lindex $args [expr $i + 1]]
+ incr i
+ } elseif {"$a" == "-noprompt"} {
+ set wait_for_prompt 0
+ } else {
+ set args [lrange $args $i end]
+ break
+ }
+ }
+
+ if {"$message" == ""} {
+ set message [lindex $args 0]
+ }
+
+ verbose "Message is \"$message\"" 2
+
+ set command [lindex $args 0]
+ set pattern [lrange $args 1 end]
+
+ set result [seq_test $command $pattern]
+ if {$wait_for_prompt} {
+ remote_expect host 30 {
+ -re "\[\r\n\]?${gdbmtool_prompt}$" {}
+ default {
+ perror "gdbmtool not initialized"
+ return 1
+ }
+ }
+ }
+
+ if {$result == 0} {
+ pass "$message"
+ } elseif {$result == 1} {
+ if { "$default" == "" || "$default" != "FAIL" } {
+ fail "$message"
+ } else {
+ xfail "$message"
+ set result 0
+ }
+ } elseif {$result == -2} {
+ fail "$message (timeout)"
+ } elseif {$result == -3} {
+ fail "$message (eof)"
+ } else {
+ fail "$message"
+ }
+ return $result
+}
+
+
+
diff --git a/tests/gdbmtool/base.exp b/tests/gdbmtool/base.exp
new file mode 100644
index 0000000..6a831ac
--- a/dev/null
+++ b/tests/gdbmtool/base.exp
@@ -0,0 +1,9 @@
+gdbmtool_start
+gdbmtool_test "status" \
+ "No database name" \
+ "Database is not open" \
+ "define key string" \
+ "define content string"
+gdbmtool_test "version" \
+ -re "^GDBM version .*"
+gdbmtool_stop
diff --git a/tests/gdbmtool00.at b/tests/gdbmtool00.at
new file mode 100644
index 0000000..baec9a4
--- a/dev/null
+++ b/tests/gdbmtool00.at
@@ -0,0 +1,37 @@
+# This file is part of GDBM. -*- autoconf -*-
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# GDBM is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GDBM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+AT_SETUP([Input from file])
+AT_KEYWORDS([gdbmtool])
+
+AT_CHECK([
+
+AT_DATA([input],[
+status
+version
+quit
+])
+
+gdbmtool -f input | sed 's/^GDBM version.*/GDBM version/'
+],
+[0],
+[No database name
+Database is not open
+define key string
+define content string
+GDBM version
+])
+AT_CLEANUP
diff --git a/tests/gdbmtool01.at b/tests/gdbmtool01.at
new file mode 100644
index 0000000..91203f6
--- a/dev/null
+++ b/tests/gdbmtool01.at
@@ -0,0 +1,37 @@
+# This file is part of GDBM. -*- autoconf -*-
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# GDBM is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GDBM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+AT_SETUP([Input from stdin])
+AT_KEYWORDS([gdbmtool])
+
+AT_CHECK([
+
+AT_DATA([input],[
+status
+version
+quit
+])
+
+gdbmtool < input | sed 's/^GDBM version.*/GDBM version/'
+],
+[0],
+[No database name
+Database is not open
+define key string
+define content string
+GDBM version
+])
+AT_CLEANUP
diff --git a/tests/gdbmtool02.at b/tests/gdbmtool02.at
new file mode 100644
index 0000000..0c2d35f
--- a/dev/null
+++ b/tests/gdbmtool02.at
@@ -0,0 +1,31 @@
+# This file is part of GDBM. -*- autoconf -*-
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# GDBM is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GDBM is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+AT_SETUP([Input from command line])
+AT_KEYWORDS([gdbmtool])
+
+AT_CHECK([
+gtload t.db < /dev/null
+gdbmtool t.db status \; version | sed 's/^GDBM version.*/GDBM version/'
+],
+[0],
+[Database file: t.db
+Database is not open
+define key string
+define content string
+GDBM version
+])
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 9488245..176a2d6 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -86,4 +86,9 @@ m4_include([dump02.at])
m4_include([dump03.at])
m4_include([dump04.at])
+AT_BANNER([gdbmtool])
+m4_include([gdbmtool00.at])
+m4_include([gdbmtool01.at])
+m4_include([gdbmtool02.at])
+
# End of testsuite.at

Return to:

Send suggestions and report system problems to the System administrator.