summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--NEWS7
-rw-r--r--README5
-rw-r--r--configure.ac49
-rw-r--r--doc/gdbm.texi22
-rw-r--r--doc/gdbmtool.115
-rw-r--r--src/Makefile.am12
-rw-r--r--src/gdbmtool.c102
-rw-r--r--src/gdbmtool.h31
-rw-r--r--src/gram.y7
-rw-r--r--src/input-rl.c235
-rw-r--r--src/input-std.c43
-rw-r--r--src/lex.l134
12 files changed, 578 insertions, 84 deletions
diff --git a/NEWS b/NEWS
index a564b2e..c9ab654 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,7 @@
-GNU dbm NEWS -- history of user-visible changes. 2016-07-09
+GNU dbm NEWS -- history of user-visible changes. 2016-07-12
Copyright (C) 1990-2016 Free Software Foundation, Inc.
See the end of file for copying conditions.
Please send gdbm bug reports to <bug-gdbm@gnu.org>.
Version 1.12.90 (Git)
@@ -28,12 +28,17 @@ Instead it sets gdbm_errno to GDBM_MALLOC_ERROR and returns NULL datum.
** gdbm_last_errno
** gdbm_clear_error
** gdbm_needs_recovery
+* Line editing in gdbmtool
+
+The gdbmtool utility now offers the usual line-editing facilities (if
+the package has been compiled with GNU Readline.
+
Version 1.12, 2016-05-16
* New configuration variable COMPATINCLUDEDIR
When used with --enable-libgdbm-compat, this variable points to the
diff --git a/README b/README
index 99e680a..369460a 100644
--- a/README
+++ b/README
@@ -56,12 +56,17 @@ a loader argument valid for the use with cc(1), e.g.: -lgdbm-1.8.
Build gdbmexport with the gdbm library from the specified directory.
** --gdbm183-includedir=DIR
Build gdbmexport with gdbm.h from the specified directory.
+** --without-readline
+
+Don't compile GNU Readline support. By default, configure enables
+line-editing support if GNU Readline is available on the system.
+
* Installation
make install
* Bug reporting
diff --git a/configure.ac b/configure.ac
index 15270b9..336e05c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -121,12 +121,61 @@ AC_CHECK_MEMBERS([struct stat.st_blksize])
AC_SUBST(GDBM183_LIBRARY)
AC_SUBST(GDBM183_LIBDIR)
AC_SUBST(GDBM183_INCLUDEDIR)
AM_CONDITIONAL([COMPAT_OPT], [test "$want_compat" = yes])
AM_CONDITIONAL([ENABLE_EXPORT], [test "$want_export" = yes])
+# Check for Curses libs.
+for lib in ncurses curses termcap
+do
+ AC_CHECK_LIB($lib, tputs, [CURSES_LIBS="-l$lib"; break])
+done
+
+AC_SUBST(CURSES_LIBS)
+# Readline
+AC_ARG_WITH([readline],
+ AC_HELP_STRING([--without-readline],
+ [do not use readline]),
+ [
+case "${withval}" in
+ yes) status_readline=yes ;;
+ no) status_readline=no ;;
+ *) AC_MSG_ERROR(bad value ${withval} for --without-readline) ;;
+esac],[status_readline=probe])
+
+AC_SUBST(READLINE_LIBS)
+
+if test "$status_readline" != "no"; then
+ dnl FIXME This should only link in the curses libraries if it's
+ dnl really needed!
+ saved_LIBS=$LIBS
+ LIBS="$LIBS $CURSES_LIBS"
+ AC_CHECK_LIB(readline, readline,
+ [status_readline=yes],
+ [if test "$status_readline" = "yes"; then
+ AC_MSG_ERROR(readline requested but does not seem to be installed)
+ else
+ status_readline=no
+ fi])
+ LIBS=$saved_LIBS
+
+ if test "$status_readline" = "yes"; then
+ AC_CHECK_HEADERS(readline/readline.h,
+ AC_DEFINE(WITH_READLINE,1,[Enable use of readline]))
+ READLINE_LIBS="-lreadline $CURSES_LIBS"
+ saved_LIBS=$LIBS
+ LIBS="$LIBS $READLINE_LIBS"
+ AC_CHECK_FUNCS(rl_completion_matches)
+ LIBS=$saved_LIBS
+ fi
+else
+ status_readline=no
+fi
+
+AM_CONDITIONAL([GDBM_COND_READLINE], [test "$status_readline" = "yes"])
+
# Initialize the test suite.
AC_CONFIG_TESTDIR(tests)
AC_CONFIG_FILES([tests/Makefile tests/atlocal po/Makefile.in])
AM_MISSING_PROG([AUTOM4TE], [autom4te])
AC_CONFIG_FILES([Makefile
diff --git a/doc/gdbm.texi b/doc/gdbm.texi
index 4c7c5c0..d172ad7 100644
--- a/doc/gdbm.texi
+++ b/doc/gdbm.texi
@@ -1825,12 +1825,18 @@ data? @kbd{3}
However, such prompting is possible only in interactive mode. In
non-interactive mode (e.g.@: when running a script), all arguments must
be supplied with each command, otherwise @command{gdbmtool} will report an
error and exit immediately.
+@cindex readline
+@cindex GNU Readline
+If the package is compiled with GNU Readline, the input line can be
+edited (@pxref{Command Line Editing, ,
+Command Line Editing, readline, GNU Readline Library}).
+
@menu
* variables:: shell variables.
* commands:: shell commands.
* definitions:: how to define structured data.
* startup files::
@end menu
@@ -2110,12 +2116,28 @@ Import data from a flat dump file @var{file-name}
(@pxref{Flat files}). If the word @samp{replace} is given
as an argument, any records with the same keys as the already
existing ones will replace them. The word @samp{nometa} turns off
restoring meta-information from the dump file.
@end deffn
+@deffn {command verb} history
+@deffnx {command verb} history @var{count}
+@deffnx {command verb} history @var{n} @var{count}
+Shows the command history list with line numbers. When used without
+arguments, shows entire history. When used with one argument,
+displays @var{count} last commands from the history. With two
+arguments, displays @var{count} commands starting from @var{n}th
+command. Command numbering starts with 1.
+
+This command is available only if GDBM was compiled with GNU Readline.
+The history is saved in file @file{.gdbmtool_history} in the user's
+home directory. If this file exists upon startup, it is read to
+populate the history. Thus, command history is preserved between
+@command{gdbmtool} invocations.
+@end deffn
+
@deffn {command verb} list
List the contents of the database.
@end deffn
@deffn {command verb} next [@var{key}]
Sequential access: fetch and display the next record. If the @var{key} is
diff --git a/doc/gdbmtool.1 b/doc/gdbmtool.1
index dae881c..c479822 100644
--- a/doc/gdbmtool.1
+++ b/doc/gdbmtool.1
@@ -10,13 +10,13 @@
.\" 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/>. */
-.TH GDBMTOOL 1 "May 18, 2016" "GDBM" "GDBM User Reference"
+.TH GDBMTOOL 1 "July 12, 2016" "GDBM" "GDBM User Reference"
.SH NAME
gdbmtool \- examine and modify a GDBM database
.SH SYNOPSIS
\fBgdbmtool\fR [\fB\-lmNnqrs\fR] [\fB\-b\fR \fISIZE\fR] [\fB\-c\fR \fISIZE\fR]\
[\fB\-f\fR \fIFILE\fR] [\fB\-\-block\-size\fR=\fISIZE\fR]
[\fB\-\-cache\-size\fR=\fISIZE\fR] [\fB\-\-file\fR \fIFILE\fR]\
@@ -155,13 +155,13 @@ Print the current bucket.
\fBdelete\fR \fIKEY\fR
Delete record with the given \fIKEY\fR.
.TP
.BR dir
Print hash directory.
.TP
-\fBexport\fR, \fBe\fR \fIFILE\-NAME\fR [\fBtruncate\fR] [\fBbinary\fR|\fBascii\fR]
+\fBexport\fR \fIFILE\-NAME\fR [\fBtruncate\fR] [\fBbinary\fR|\fBascii\fR]
Export the database to the flat file \fIFILE\-NAME\fR. This is equivalent to
.BR gdbm_dump (1).
This command will not overwrite an existing file, unless the
.B truncate
parameter is also given. Another optional parameter determines the type of
@@ -184,12 +184,23 @@ Print file header.
.TP
.BR help " or " ?
Print a concise command summary, showing each command letter and
verb with its parameters and a short description of what it does.
Optional arguments are enclosed in square brackets.
.TP
+.B history
+Shows the command history list with line numbers. This command is
+available only if the program was compiled with GNU Readline.
+.TP
+\fBhistory\fR \fICOUNT\fR.
+Shows \fICOUNT\fR latest commands from the command history.
+.TP
+\fBhistory\fR \fIN\fR \fICOUNT\fR.
+Shows \fICOUNT\fR commands from the command history starting with \fIN\fRth
+command.
+.TP
\fBimport\fR \fIFILE\-NAME\fR [\fBreplace\fR] [\fBnometa\fR]
Import data from a flat dump file \fIFILE\-NAME\fR.
If the
.B replace
argument is given, any records with the same keys as the already
existing ones will replace them. The
diff --git a/src/Makefile.am b/src/Makefile.am
index 6a05634..b55deb6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -76,21 +76,31 @@ libgdbmapp_a_SOURCES =\
parseopt.c\
progname.c
# Programs
bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump
-gdbmtool_LDADD = ./libgdbmapp.a ./libgdbm.la
+gdbmtool_LDADD = \
+ ./libgdbmapp.a\
+ ./libgdbm.la\
+ @READLINE_LIBS@
+
gdbmtool_SOURCES = \
datconv.c\
gram.y\
lex.l\
gdbmtool.h\
gdbmtool.c\
var.c\
util.c
+if GDBM_COND_READLINE
+ gdbmtool_SOURCES += input-rl.c
+else
+ gdbmtool_SOURCES += input-std.c
+endif
+
AM_YFLAGS = -dtv
#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 65b6740..2d57cbc 100644
--- a/src/gdbmtool.c
+++ b/src/gdbmtool.c
@@ -320,26 +320,12 @@ get_screen_lines ()
const char *lines = getenv ("LINES");
if (lines)
return strtol (lines, NULL, 10);
#endif
return -1;
}
-
-
-#define ARG_UNUSED __attribute__ ((__unused__))
-
-#define NARGS 5
-
-struct handler_param
-{
- int argc;
- struct gdbmarg **argv;
- FILE *fp;
- void *data;
-};
-
/* Open database */
void
open_handler (struct handler_param *param)
{
if (opendb (param->argv[0]->v.string) == 0)
@@ -748,13 +734,13 @@ list_handler (struct handler_param *param)
/* quit - quit the program */
void
quit_handler (struct handler_param *param ARG_UNUSED)
{
if (gdbm_file != NULL)
gdbm_close (gdbm_file);
-
+ input_done ();
exit (EXIT_OK);
}
/* export FILE [truncate] - export to a flat file format */
void
export_handler (struct handler_param *param)
@@ -888,24 +874,25 @@ struct argdef
{
char *name;
int type;
int ds;
};
+#define NARGS 5
+
struct command
{
char *name; /* Command name */
size_t len; /* Name length */
int tok;
int (*begin) (struct handler_param *param, size_t *);
void (*handler) (struct handler_param *param);
void (*end) (void *data);
struct argdef args[NARGS];
char *doc;
};
-
struct command command_tab[] = {
#define S(s) #s, sizeof (#s) - 1
{ S(count), T_CMD,
checkdb, count_handler, NULL,
{ { NULL } }, N_("count (number of entries)") },
@@ -1004,12 +991,19 @@ struct command command_tab[] = {
NULL, close_handler, NULL,
{ { NULL } }, N_("close the database") },
{ S(open), T_CMD,
NULL, open_handler, NULL,
{ { "FILE", GDBM_ARG_STRING }, { NULL } },
N_("open new database") },
+#ifdef WITH_READLINE
+ { S(history), T_CMD,
+ input_history_begin, input_history_handler, NULL,
+ { { N_("[FROM]"), GDBM_ARG_STRING },
+ { N_("[COUNT]"), GDBM_ARG_STRING },
+ { NULL } }, N_("show input history") },
+#endif
#undef S
{ 0 }
};
static int
cmdcmp (const void *a, const void *b)
@@ -1023,12 +1017,45 @@ void
sort_commands ()
{
qsort (command_tab, sizeof (command_tab) / sizeof (command_tab[0]) - 1,
sizeof (command_tab[0]), cmdcmp);
}
+/* Generator function for command completion. STATE lets us know whether
+ to start from scratch; without any state (i.e. STATE == 0), then we
+ start at the top of the list. */
+char *
+command_generator (const char *text, int state)
+{
+ const char *name;
+ static int len;
+ static struct command *cmd;
+
+ /* If this is a new word to complete, initialize now. This includes
+ saving the length of TEXT for efficiency, and initializing the index
+ variable to 0. */
+ if (!state)
+ {
+ cmd = command_tab;
+ len = strlen (text);
+ }
+
+ if (!cmd->name)
+ return NULL;
+
+ /* Return the next name which partially matches from the command list. */
+ while ((name = cmd->name))
+ {
+ cmd++;
+ if (strncmp (name, text, len) == 0)
+ return strdup (name);
+ }
+
+ /* If no names matched, then return NULL. */
+ return NULL;
+}
/* ? - help handler */
#define CMDCOLS 30
int
help_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
@@ -1171,31 +1198,61 @@ gdbmarg_kvpair (struct kvpair *kvp, struct locus *loc)
arg->loc = *loc;
arg->v.kvpair = kvp;
return arg;
}
struct slist *
-slist_new (char *s)
+slist_new_s (char *s)
{
struct slist *lp = emalloc (sizeof (*lp));
lp->next = NULL;
lp->str = s;
return lp;
}
+struct slist *
+slist_new (char const *s)
+{
+ return slist_new_s (estrdup (s));
+}
+
+struct slist *
+slist_new_l (char const *s, size_t l)
+{
+ char *copy = emalloc (l + 1);
+ memcpy (copy, s, l);
+ copy[l] = 0;
+ return slist_new_s (copy);
+}
+
void
slist_free (struct slist *lp)
{
while (lp)
{
struct slist *next = lp->next;
free (lp->str);
free (lp);
lp = next;
}
}
+
+void
+slist_insert (struct slist **where, struct slist *what)
+{
+ if (*where)
+ {
+ while (what->next)
+ what = what->next;
+ what->next = (*where)->next;
+ (*where)->next = what;
+ }
+ else
+ what->next = NULL;
+ *where = what;
+}
struct kvpair *
kvpair_string (struct locus *loc, char *val)
{
struct kvpair *p = ecalloc (1, sizeof (*p));
p->type = KV_STRING;
@@ -1213,13 +1270,12 @@ kvpair_list (struct locus *loc, struct slist *s)
if (loc)
p->loc = *loc;
p->val.l = s;
return p;
}
-
static void
kvlist_free (struct kvpair *kvp)
{
while (kvp)
{
struct kvpair *next = kvp->next;
@@ -1531,32 +1587,36 @@ int
main (int argc, char *argv[])
{
int intr;
int opt;
int bv;
int norc = 0;
+ int res;
char *source = "-";
set_progname (argv[0]);
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
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 ();
+
for (opt = parseopt_first (argc, argv, optab);
opt != EOF;
opt = parseopt_next ())
switch (opt)
{
case 'f':
@@ -1637,8 +1697,10 @@ main (int argc, char *argv[])
/* Welcome message. */
if (intr && !variable_is_true ("quiet"))
printf (_("\nWelcome to the gdbm tool. Type ? for help.\n\n"));
if (setsource (source, intr))
exit (EXIT_FATAL);
- return yyparse ();
+ res = yyparse ();
+ input_done ();
+ return res;
}
diff --git a/src/gdbmtool.h b/src/gdbmtool.h
index 84f1409..d0e9aa0 100644
--- a/src/gdbmtool.h
+++ b/src/gdbmtool.h
@@ -21,12 +21,14 @@
#include "gdbmapp.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
+#define ARG_UNUSED __attribute__ ((__unused__))
+
/* Position in input file */
struct point
{
char *file; /* file name */
unsigned line; /* line number */
unsigned col; /* column number */
@@ -91,32 +93,46 @@ typedef struct locus gdbm_yyltype_t;
void vlerror (struct locus *loc, const char *fmt, va_list ap);
void lerror (struct locus *loc, const char *fmt, ...);
void terror (const char *fmt, ...);
-void print_prompt (void);
+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);
+void input_init (void);
+void input_done (void);
+
+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);
struct slist
{
struct slist *next;
char *str;
};
-struct slist *slist_new (char *s);
+struct slist *slist_new (char const *s);
+struct slist *slist_new_s (char *s);
+struct slist *slist_new_l (char const *s, size_t l);
void slist_free (struct slist *);
+void slist_insert (struct slist **where, struct slist *what);
#define KV_STRING 0
#define KV_LIST 1
struct kvpair
{
@@ -158,12 +174,20 @@ struct gdbmarg
/* List of arguments */
struct gdbmarglist
{
struct gdbmarg *head, *tail;
};
+struct handler_param
+{
+ int argc;
+ struct gdbmarg **argv;
+ FILE *fp;
+ void *data;
+};
+
void gdbmarglist_init (struct gdbmarglist *, struct gdbmarg *);
void gdbmarglist_add (struct gdbmarglist *, struct gdbmarg *);
void gdbmarglist_free (struct gdbmarglist *lst);
struct gdbmarg *gdbmarg_string (char *, struct locus *);
struct gdbmarg *gdbmarg_datum (datum *, struct locus *);
@@ -259,6 +283,9 @@ int datum_scan (datum *dat, struct dsegm *ds, struct kvpair *kv);
void dsprint (FILE *fp, int what, struct dsegm *ds);
char *mkfilename (const char *dir, const char *file, const char *suf);
char *tildexpand (char *s);
int vgetyn (const char *prompt, va_list ap);
int getyn (const char *prompt, ...);
+
+int getnum (int *pnum, char *arg, char **endp);
+int get_screen_lines (void);
diff --git a/src/gram.y b/src/gram.y
index 9a6e3c2..2f6d7c0 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -165,19 +165,18 @@ value : string
$$ = kvpair_list (&@1, $2.head);
}
;
slist : string
{
- $$.head = $$.tail = slist_new ($1);
+ $$.head = $$.tail = slist_new_s ($1);
}
| slist ',' string
{
- struct slist *s = slist_new ($3);
- $1.tail->next = s;
- $1.tail = s;
+ struct slist *s = slist_new_s ($3);
+ slist_insert (&$1.tail, s);
$$ = $1;
}
;
string : T_IDENT
| T_WORD
diff --git a/src/input-rl.c b/src/input-rl.c
new file mode 100644
index 0000000..f435ed4
--- a/dev/null
+++ b/src/input-rl.c
@@ -0,0 +1,235 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 2016 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 <readline/readline.h>
+#include <readline/history.h>
+
+static char *pre_input_line;
+
+static int
+pre_input (void)
+{
+ if (pre_input_line)
+ {
+ rl_insert_text (pre_input_line);
+ free (pre_input_line);
+ pre_input_line = NULL;
+ rl_redisplay ();
+ }
+ return 0;
+}
+
+static int
+retrieve_history (char *str)
+{
+ char *out;
+ int rc;
+
+ rc = history_expand (str, &out);
+ switch (rc)
+ {
+ case -1:
+ yyerror (out);
+ free (out);
+ return 1;
+
+ case 0:
+ break;
+
+ case 1:
+ pre_input_line = out;
+ return 1;
+
+ case 2:
+ printf ("%s\n", out);
+ 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;
+};
+
+int
+input_history_begin (struct handler_param *param, size_t *exp_count)
+{
+ struct history_param *p;
+ int from = 0, count = history_length;
+
+ switch (param->argc)
+ {
+ case 1:
+ if (getnum (&count, param->argv[0]->v.string, NULL))
+ return 1;
+ if (count > history_length)
+ count = history_length;
+ else
+ from = history_length - count;
+ break;
+
+ case 2:
+ if (getnum (&from, param->argv[0]->v.string, NULL))
+ return 1;
+ if (from)
+ --from;
+ if (getnum (&count, param->argv[1]->v.string, NULL))
+ return 1;
+
+ if (count > history_length)
+ count = history_length;
+ }
+ p = emalloc (sizeof *p);
+ p->from = from;
+ p->count = count;
+ param->data = p;
+ if (exp_count)
+ *exp_count = count;
+ return 0;
+}
+
+void
+input_history_handler (struct handler_param *param)
+{
+ struct history_param *p = param->data;
+ int i;
+ HIST_ENTRY **hlist;
+ FILE *fp = param->fp;
+
+ hlist = history_list ();
+ for (i = 0; i < p->count; i++)
+ fprintf (fp, "%4d) %s\n", p->from + i + 1, hlist[p->from + i]->line);
+}
+
+#define HISTFILE_PREFIX "~/."
+#define HISTFILE_SUFFIX "_history"
+
+static char *
+get_history_file_name ()
+{
+ static char *filename = NULL;
+
+ if (!filename)
+ {
+ char *hname;
+
+ hname = emalloc (sizeof HISTFILE_PREFIX + strlen (rl_readline_name) +
+ sizeof HISTFILE_SUFFIX - 1);
+ strcpy (hname, HISTFILE_PREFIX);
+ strcat (hname, rl_readline_name);
+ strcat (hname, HISTFILE_SUFFIX);
+ filename = tildexpand (hname);
+ free (hname);
+ }
+ return filename;
+}
+
+static char **
+shell_completion (const char *text, int start, int end)
+{
+ char **matches;
+
+ matches = (char **) NULL;
+
+ /* If this word is at the start of the line, then it is a command
+ to complete. Otherwise it is the name of a file in the current
+ directory. */
+ if (start == 0)
+ matches = rl_completion_matches (text, command_generator);
+
+ return (matches);
+}
+
+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 ());
+}
+
+void
+input_done (void)
+{
+ if (interactive)
+ write_history (get_history_file_name ());
+}
+
diff --git a/src/input-std.c b/src/input-std.c
new file mode 100644
index 0000000..4508368
--- a/dev/null
+++ b/src/input-std.c
@@ -0,0 +1,43 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 2016 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"
+
+ssize_t
+input_read (FILE *fp, 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);
+}
+
+void
+input_init (void)
+{
+ /* nothing */
+}
+
+void
+input_done (void)
+{
+ /* nothing */
+}
+
diff --git a/src/lex.l b/src/lex.l
index 9c2c5f8..236474d 100644
--- a/src/lex.l
+++ b/src/lex.l
@@ -43,24 +43,22 @@ advance_line ()
while (0);
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
do \
{ \
- result = read_input (buf, max_size); \
+ result = input_read (yyin, buf, max_size); \
} \
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);
-static ssize_t read_input (char *buf, size_t size);
-
struct context /* Input context */
{
struct context *parent; /* Pointer to the parent context */
struct locus locus; /* Locus */
struct point point;
int interactive;
@@ -312,24 +310,22 @@ begin_def (void)
void
end_def (void)
{
BEGIN (INITIAL);
}
-static ssize_t
-read_input (char *buf, size_t size)
+void
+print_prompt_at_bol (void)
{
- if (interactive)
+ if (YY_AT_BOL ())
{
- if (YY_AT_BOL ())
- print_prompt ();
- if (fgets (buf, size, yyin) == NULL)
- return 0;
- return strlen (buf);
+ char *s = make_prompt ();
+ fputs (s, stdout);
+ fflush (stdout);
+ free (s);
}
- return fread (buf, 1, size, yyin);
}
struct strseg
{
struct strseg *next;
@@ -450,121 +446,151 @@ lerror (struct locus *loc, const char *fmt, ...)
va_start (ap, fmt);
vlerror (loc, fmt, ap);
va_end (ap);
}
-struct prompt_exp;
-
-void
-pe_file_name (struct prompt_exp *p)
+static struct slist *
+pe_file_name (void)
{
- if (file_name)
- fwrite (file_name, strlen (file_name), 1, stdout);
+ return file_name ? slist_new (file_name) : NULL;
}
-void
-pe_program_name (struct prompt_exp *p)
+static struct slist *
+pe_program_name (void)
{
- fwrite (progname, strlen (progname), 1, stdout);
+ return slist_new (progname);
}
-void
-pe_package_name (struct prompt_exp *p)
+static struct slist *
+pe_package_name (void)
{
- fwrite (PACKAGE_NAME, sizeof (PACKAGE_NAME) - 1, 1, stdout);
+ return slist_new (PACKAGE_NAME);
}
-void
-pe_program_version (struct prompt_exp *p)
+static struct slist *
+pe_program_version (void)
{
- fwrite (PACKAGE_VERSION, sizeof (PACKAGE_VERSION) - 1, 1, stdout);
+ return slist_new (PACKAGE_VERSION);
}
-void
-pe_space (struct prompt_exp *p)
+static struct slist *
+pe_space (void)
{
- fwrite (" ", 1, 1, stdout);
+ return slist_new (" ");
}
struct prompt_exp
{
int ch;
- void (*fun) (struct prompt_exp *);
- char *cache;
+ struct slist *(*fun) (void);
};
struct prompt_exp prompt_exp[] = {
{ 'f', pe_file_name },
{ 'p', pe_program_name },
{ 'P', pe_package_name },
{ 'v', pe_program_version },
{ '_', pe_space },
{ 0 }
};
-static void
-expand_char (int c)
+static int
+expand_char (int c, struct slist **tailp)
{
struct prompt_exp *p;
if (c && c != '%')
{
for (p = prompt_exp; p->ch; p++)
{
if (c == p->ch)
{
- if (p->cache)
- free (p->cache);
- p->fun (p);
- return;
+ struct slist *s = p->fun ();
+ if (s)
+ slist_insert (tailp, s);
+ return 0;
}
}
}
- putchar ('%');
- putchar (c);
+ return 1;
}
char const *
-psname ()
+psname (void)
{
if (YYSTATE == DEF || YYSTATE == MLSTR)
return "ps2";
return "ps1";
}
-void
-print_prompt ()
+char *
+make_prompt (void)
{
const char *s;
const char *prompt;
-
+ struct slist *head = NULL, *tail = NULL, *p;
+ char *ret, *end;
+ size_t len;
+
switch (variable_get (psname (), VART_STRING, (void *) &prompt))
{
case VAR_OK:
break;
case VAR_ERR_NOTSET:
- return;
+ return NULL;
default:
abort ();
}
-
- for (s = prompt; *s; s++)
+
+ for (s = prompt; *s; )
{
- if (*s == '%')
+ if (*s == '%' && s[1])
{
- if (!*++s)
+ if (s > prompt)
+ {
+ slist_insert (&tail, slist_new_l (prompt, s - prompt));
+ if (!head)
+ head = tail;
+ }
+ if (expand_char (s[1], &tail) == 0)
{
- putchar ('%');
- break;
+ if (!head)
+ head = tail;
+ prompt = s + 2;
}
- expand_char (*s);
+ else
+ prompt = s;
+ s += 2;
}
else
- putchar (*s);
+ ++s;
+ }
+
+ if (s > prompt)
+ {
+ slist_insert (&tail, slist_new_l (prompt, s - prompt));
+ if (!head)
+ head = tail;
}
- fflush (stdout);
+ len = 0;
+ for (p = head; p; p = p->next)
+ len += strlen (p->str);
+
+ ret = emalloc (len + 1);
+ end = ret;
+ for (p = head; p; p = p->next)
+ {
+ s = p->str;
+ while (*s)
+ *end++ = *s++;
+ }
+ *end = 0;
+
+ slist_free (head);
+
+ return ret;
}

Return to:

Send suggestions and report system problems to the System administrator.