aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-05-23 19:18:14 +0300
committerSergey Poznyakoff <gray@gnu.org>2018-05-23 19:18:14 +0300
commit7edbff3372dfd387e4f6da45f43ba8ada6dfbe43 (patch)
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.
-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
@@ -8,12 +8,13 @@
@ifinfo
@dircategory Programming & development tools
@direntry
* 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
@c @setchapternewpage odd
@comment %**end of header (This is for running Texinfo on a region.)
diff --git a/src/Makefile.am b/src/Makefile.am
index aff6982..766dba8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -93,12 +93,14 @@ gdbmtool_LDADD = \
./libgdbm.la\
@READLINE_LIBS@
gdbmtool_SOURCES = \
datconv.c\
gram.y\
+ input-argv.c\
+ input-file.c\
lex.l\
gdbmtool.h\
gdbmtool.c\
var.c\
util.c
@@ -106,10 +108,10 @@ if GDBM_COND_READLINE
gdbmtool_SOURCES += input-rl.c
else
gdbmtool_SOURCES += input-std.c
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
@@ -75,13 +75,13 @@ opendb (char *dbname)
flags |= GDBM_NOMMAP;
if (variable_is_true ("sync"))
flags |= GDBM_SYNC;
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))
return 1;
}
}
@@ -1103,15 +1103,16 @@ debug_handler (struct handler_param *param)
}
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 ();
}
void help_handler (struct handler_param *param);
int help_begin (struct handler_param *param, size_t *exp_count);
@@ -1453,13 +1454,13 @@ command_lookup (const char *str, struct locus *loc, struct command **pcmd)
case fcom_init:
found = cmd;
state = fcom_found;
break;
case fcom_found:
- if (!interactive)
+ if (!interactive ())
{
state = fcom_abort;
found = NULL;
continue;
}
fprintf (stderr, "ambiguous command: %s\n", str);
@@ -1476,24 +1477,28 @@ command_lookup (const char *str, struct locus *loc, struct command **pcmd)
abort ();
}
}
}
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;
*pcmd = found;
return found->tok;
}
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") },
{ 'c', "cache-size", N_("SIZE"), N_("set cache size") },
{ 'f', "file", N_("FILE"), N_("read commands from FILE") },
{ 'g', NULL, "FILE", NULL, PARSEOPT_HIDDEN },
@@ -1501,12 +1506,14 @@ struct gdbm_option optab[] = {
{ 'm', "no-mmap", NULL, N_("do not use mmap") },
{ 'n', "newdb", NULL, N_("create database") },
{ 'N', "norc", NULL, N_("do not read .gdbmtoolrc file") },
{ '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 }
};
#define ARGINC 16
@@ -1783,13 +1790,13 @@ coerce (struct gdbmarg *arg, struct argdef *def)
static struct command *last_cmd;
static struct gdbmarglist last_args;
void
run_last_command (void)
{
- if (interactive)
+ if (interactive ())
{
if (last_cmd)
{
switch (last_cmd->repeat)
{
case REPEAT_NEVER:
@@ -1843,13 +1850,13 @@ run_command (struct command *cmd, struct gdbmarglist *arglist)
struct gdbmarg *t;
if (*argname == '[')
/* Optional argument */
break;
- if (!interactive)
+ if (!interactive ())
{
terror (_("%s: not enough arguments"), cmd->name);
return 1;
}
printf ("%s? ", argname);
fflush (stdout);
@@ -1893,13 +1900,13 @@ run_command (struct command *cmd, struct gdbmarglist *arglist)
param.vararg = arg;
param.fp = NULL;
param.data = NULL;
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 ())
{
pagfp = popen (pager, "w");
if (pagfp)
@@ -1935,18 +1942,19 @@ run_command (struct command *cmd, struct gdbmarglist *arglist)
}
return 0;
}
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
{
char *fname;
char *p = getenv ("HOME");
if (!p)
@@ -1959,17 +1967,23 @@ source_rcfile ()
}
p = pw->pw_dir;
}
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
void
debug_printer (char const *fmt, ...)
{
@@ -1981,18 +1995,18 @@ debug_printer (char const *fmt, ...)
}
#endif
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
gdbm_debug_printer = debug_printer;
#endif
@@ -2002,30 +2016,28 @@ main (int argc, char *argv[])
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
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);
variable_set ("open", VART_STRING, "wrcreat");
variable_set ("pager", VART_STRING, getenv ("PAGER"));
input_init ();
+ lex_trace (0);
for (opt = parseopt_first (argc, argv, optab);
opt != EOF;
opt = parseopt_next ())
switch (opt)
{
case 'f':
source = optarg;
- intr = 0;
break;
case 'l':
bv = 0;
variable_set ("lock", VART_BOOL, &bv);
break;
@@ -2065,43 +2077,70 @@ main (int argc, char *argv[])
break;
case 'q':
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"),
progname);
exit (EXIT_USAGE);
}
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);
memset (&param, 0, sizeof (param));
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 ();
return res;
}
diff --git a/src/gdbmtool.h b/src/gdbmtool.h
index 8a7725c..c472276 100644
--- a/src/gdbmtool.h
+++ b/src/gdbmtool.h
@@ -20,12 +20,13 @@
#include "gdbm.h"
#include "gdbmapp.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
+#include <limits.h>
#ifndef GDBM_ARG_UNUSED
# define GDBM_ARG_UNUSED __attribute__ ((__unused__))
#endif
#ifndef GDBM_PRINTFLIKE
@@ -104,25 +105,73 @@ void lerror (struct locus *loc, const char *fmt, ...)
void terror (const char *fmt, ...)
GDBM_PRINTFLIKE (1, 2);
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);
void print_prompt_at_bol (void);
char *command_generator (const char *text, int state);
diff --git a/src/gram.y b/src/gram.y
index 57d90c5..5372795 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -68,45 +68,49 @@ input : /* empty */
;
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;
}
else
YYERROR;
}
- | error { end_def(); } '\n'
+ | error { end_def(); } eol
{
- if (interactive)
+ if (interactive ())
{
yyclearin;
yyerrok;
}
else
YYERROR;
}
;
+eol : '\n'
+ | ';'
+ ;
+
arglist : /* empty */
{
gdbmarglist_init (&$$, NULL);
}
| arg1list
;
@@ -364,6 +368,12 @@ terror (const char *fmt, ...)
int
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
--- /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
--- /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
@@ -59,70 +59,12 @@ retrieve_history (char *str)
free (out);
return 1;
}
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;
int count;
};
@@ -177,13 +119,13 @@ input_history_handler (struct handler_param *param)
}
#define HISTFILE_PREFIX "~/."
#define HISTFILE_SUFFIX "_history"
static char *
-get_history_file_name ()
+get_history_file_name (void)
{
static char *filename = NULL;
if (!filename)
{
char *hname;
@@ -219,17 +161,102 @@ void
input_init (void)
{
/* Allow conditional parsing of the ~/.inputrc file. */
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
@@ -13,23 +13,48 @@
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"
-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
input_init (void)
{
/* nothing */
diff --git a/src/lex.l b/src/lex.l
index a2a8ee8..0318031 100644
--- a/src/lex.l
+++ b/src/lex.l
@@ -16,189 +16,167 @@
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 "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 \
do \
{ \
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);
#undef YY_INPUT
#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);
void string_begin (void);
void string_add (const char *s, int l);
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