-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | configure.ac | 49 | ||||
-rw-r--r-- | doc/gdbm.texi | 22 | ||||
-rw-r--r-- | doc/gdbmtool.1 | 15 | ||||
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/gdbmtool.c | 102 | ||||
-rw-r--r-- | src/gdbmtool.h | 31 | ||||
-rw-r--r-- | src/gram.y | 7 | ||||
-rw-r--r-- | src/input-rl.c | 235 | ||||
-rw-r--r-- | src/input-std.c | 43 | ||||
-rw-r--r-- | src/lex.l | 134 |
12 files changed, 578 insertions, 84 deletions
@@ -1,7 +1,7 @@ | |||
1 | GNU dbm NEWS -- history of user-visible changes. 2016-07-09 | 1 | GNU dbm NEWS -- history of user-visible changes. 2016-07-12 |
2 | Copyright (C) 1990-2016 Free Software Foundation, Inc. | 2 | Copyright (C) 1990-2016 Free Software Foundation, Inc. |
3 | See the end of file for copying conditions. | 3 | See the end of file for copying conditions. |
4 | 4 | ||
5 | Please send gdbm bug reports to <bug-gdbm@gnu.org>. | 5 | Please send gdbm bug reports to <bug-gdbm@gnu.org>. |
6 | 6 | ||
7 | Version 1.12.90 (Git) | 7 | Version 1.12.90 (Git) |
@@ -28,12 +28,17 @@ Instead it sets gdbm_errno to GDBM_MALLOC_ERROR and returns NULL datum. | |||
28 | ** gdbm_last_errno | 28 | ** gdbm_last_errno |
29 | 29 | ||
30 | ** gdbm_clear_error | 30 | ** gdbm_clear_error |
31 | 31 | ||
32 | ** gdbm_needs_recovery | 32 | ** gdbm_needs_recovery |
33 | 33 | ||
34 | * Line editing in gdbmtool | ||
35 | |||
36 | The gdbmtool utility now offers the usual line-editing facilities (if | ||
37 | the package has been compiled with GNU Readline. | ||
38 | |||
34 | 39 | ||
35 | Version 1.12, 2016-05-16 | 40 | Version 1.12, 2016-05-16 |
36 | 41 | ||
37 | * New configuration variable COMPATINCLUDEDIR | 42 | * New configuration variable COMPATINCLUDEDIR |
38 | 43 | ||
39 | When used with --enable-libgdbm-compat, this variable points to the | 44 | When used with --enable-libgdbm-compat, this variable points to the |
@@ -56,12 +56,17 @@ a loader argument valid for the use with cc(1), e.g.: -lgdbm-1.8. | |||
56 | Build gdbmexport with the gdbm library from the specified directory. | 56 | Build gdbmexport with the gdbm library from the specified directory. |
57 | 57 | ||
58 | ** --gdbm183-includedir=DIR | 58 | ** --gdbm183-includedir=DIR |
59 | 59 | ||
60 | Build gdbmexport with gdbm.h from the specified directory. | 60 | Build gdbmexport with gdbm.h from the specified directory. |
61 | 61 | ||
62 | ** --without-readline | ||
63 | |||
64 | Don't compile GNU Readline support. By default, configure enables | ||
65 | line-editing support if GNU Readline is available on the system. | ||
66 | |||
62 | * Installation | 67 | * Installation |
63 | 68 | ||
64 | make install | 69 | make install |
65 | 70 | ||
66 | * Bug reporting | 71 | * Bug reporting |
67 | 72 | ||
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]) | |||
121 | AC_SUBST(GDBM183_LIBRARY) | 121 | AC_SUBST(GDBM183_LIBRARY) |
122 | AC_SUBST(GDBM183_LIBDIR) | 122 | AC_SUBST(GDBM183_LIBDIR) |
123 | AC_SUBST(GDBM183_INCLUDEDIR) | 123 | AC_SUBST(GDBM183_INCLUDEDIR) |
124 | AM_CONDITIONAL([COMPAT_OPT], [test "$want_compat" = yes]) | 124 | AM_CONDITIONAL([COMPAT_OPT], [test "$want_compat" = yes]) |
125 | AM_CONDITIONAL([ENABLE_EXPORT], [test "$want_export" = yes]) | 125 | AM_CONDITIONAL([ENABLE_EXPORT], [test "$want_export" = yes]) |
126 | 126 | ||
127 | # Check for Curses libs. | ||
128 | for lib in ncurses curses termcap | ||
129 | do | ||
130 | AC_CHECK_LIB($lib, tputs, [CURSES_LIBS="-l$lib"; break]) | ||
131 | done | ||
132 | |||
133 | AC_SUBST(CURSES_LIBS) | ||
134 | # Readline | ||
135 | AC_ARG_WITH([readline], | ||
136 | AC_HELP_STRING([--without-readline], | ||
137 | [do not use readline]), | ||
138 | [ | ||
139 | case "${withval}" in | ||
140 | yes) status_readline=yes ;; | ||
141 | no) status_readline=no ;; | ||
142 | *) AC_MSG_ERROR(bad value ${withval} for --without-readline) ;; | ||
143 | esac],[status_readline=probe]) | ||
144 | |||
145 | AC_SUBST(READLINE_LIBS) | ||
146 | |||
147 | if test "$status_readline" != "no"; then | ||
148 | dnl FIXME This should only link in the curses libraries if it's | ||
149 | dnl really needed! | ||
150 | saved_LIBS=$LIBS | ||
151 | LIBS="$LIBS $CURSES_LIBS" | ||
152 | AC_CHECK_LIB(readline, readline, | ||
153 | [status_readline=yes], | ||
154 | [if test "$status_readline" = "yes"; then | ||
155 | AC_MSG_ERROR(readline requested but does not seem to be installed) | ||
156 | else | ||
157 | status_readline=no | ||
158 | fi]) | ||
159 | LIBS=$saved_LIBS | ||
160 | |||
161 | if test "$status_readline" = "yes"; then | ||
162 | AC_CHECK_HEADERS(readline/readline.h, | ||
163 | AC_DEFINE(WITH_READLINE,1,[Enable use of readline])) | ||
164 | READLINE_LIBS="-lreadline $CURSES_LIBS" | ||
165 | saved_LIBS=$LIBS | ||
166 | LIBS="$LIBS $READLINE_LIBS" | ||
167 | AC_CHECK_FUNCS(rl_completion_matches) | ||
168 | LIBS=$saved_LIBS | ||
169 | fi | ||
170 | else | ||
171 | status_readline=no | ||
172 | fi | ||
173 | |||
174 | AM_CONDITIONAL([GDBM_COND_READLINE], [test "$status_readline" = "yes"]) | ||
175 | |||
127 | # Initialize the test suite. | 176 | # Initialize the test suite. |
128 | AC_CONFIG_TESTDIR(tests) | 177 | AC_CONFIG_TESTDIR(tests) |
129 | AC_CONFIG_FILES([tests/Makefile tests/atlocal po/Makefile.in]) | 178 | AC_CONFIG_FILES([tests/Makefile tests/atlocal po/Makefile.in]) |
130 | AM_MISSING_PROG([AUTOM4TE], [autom4te]) | 179 | AM_MISSING_PROG([AUTOM4TE], [autom4te]) |
131 | 180 | ||
132 | AC_CONFIG_FILES([Makefile | 181 | 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} | |||
1825 | 1825 | ||
1826 | However, such prompting is possible only in interactive mode. In | 1826 | However, such prompting is possible only in interactive mode. In |
1827 | non-interactive mode (e.g.@: when running a script), all arguments must | 1827 | non-interactive mode (e.g.@: when running a script), all arguments must |
1828 | be supplied with each command, otherwise @command{gdbmtool} will report an | 1828 | be supplied with each command, otherwise @command{gdbmtool} will report an |
1829 | error and exit immediately. | 1829 | error and exit immediately. |
1830 | 1830 | ||
1831 | @cindex readline | ||
1832 | @cindex GNU Readline | ||
1833 | If the package is compiled with GNU Readline, the input line can be | ||
1834 | edited (@pxref{Command Line Editing, , | ||
1835 | Command Line Editing, readline, GNU Readline Library}). | ||
1836 | |||
1831 | @menu | 1837 | @menu |
1832 | * variables:: shell variables. | 1838 | * variables:: shell variables. |
1833 | * commands:: shell commands. | 1839 | * commands:: shell commands. |
1834 | * definitions:: how to define structured data. | 1840 | * definitions:: how to define structured data. |
1835 | * startup files:: | 1841 | * startup files:: |
1836 | @end menu | 1842 | @end menu |
@@ -2110,12 +2116,28 @@ Import data from a flat dump file @var{file-name} | |||
2110 | (@pxref{Flat files}). If the word @samp{replace} is given | 2116 | (@pxref{Flat files}). If the word @samp{replace} is given |
2111 | as an argument, any records with the same keys as the already | 2117 | as an argument, any records with the same keys as the already |
2112 | existing ones will replace them. The word @samp{nometa} turns off | 2118 | existing ones will replace them. The word @samp{nometa} turns off |
2113 | restoring meta-information from the dump file. | 2119 | restoring meta-information from the dump file. |
2114 | @end deffn | 2120 | @end deffn |
2115 | 2121 | ||
2122 | @deffn {command verb} history | ||
2123 | @deffnx {command verb} history @var{count} | ||
2124 | @deffnx {command verb} history @var{n} @var{count} | ||
2125 | Shows the command history list with line numbers. When used without | ||
2126 | arguments, shows entire history. When used with one argument, | ||
2127 | displays @var{count} last commands from the history. With two | ||
2128 | arguments, displays @var{count} commands starting from @var{n}th | ||
2129 | command. Command numbering starts with 1. | ||
2130 | |||
2131 | This command is available only if GDBM was compiled with GNU Readline. | ||
2132 | The history is saved in file @file{.gdbmtool_history} in the user's | ||
2133 | home directory. If this file exists upon startup, it is read to | ||
2134 | populate the history. Thus, command history is preserved between | ||
2135 | @command{gdbmtool} invocations. | ||
2136 | @end deffn | ||
2137 | |||
2116 | @deffn {command verb} list | 2138 | @deffn {command verb} list |
2117 | List the contents of the database. | 2139 | List the contents of the database. |
2118 | @end deffn | 2140 | @end deffn |
2119 | 2141 | ||
2120 | @deffn {command verb} next [@var{key}] | 2142 | @deffn {command verb} next [@var{key}] |
2121 | Sequential access: fetch and display the next record. If the @var{key} is | 2143 | 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 @@ | |||
10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | .\" GNU General Public License for more details. | 12 | .\" GNU General Public License for more details. |
13 | .\" | 13 | .\" |
14 | .\" You should have received a copy of the GNU General Public License | 14 | .\" You should have received a copy of the GNU General Public License |
15 | .\" along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ | 15 | .\" along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ |
16 | .TH GDBMTOOL 1 "May 18, 2016" "GDBM" "GDBM User Reference" | 16 | .TH GDBMTOOL 1 "July 12, 2016" "GDBM" "GDBM User Reference" |
17 | .SH NAME | 17 | .SH NAME |
18 | gdbmtool \- examine and modify a GDBM database | 18 | gdbmtool \- examine and modify a GDBM database |
19 | .SH SYNOPSIS | 19 | .SH SYNOPSIS |
20 | \fBgdbmtool\fR [\fB\-lmNnqrs\fR] [\fB\-b\fR \fISIZE\fR] [\fB\-c\fR \fISIZE\fR]\ | 20 | \fBgdbmtool\fR [\fB\-lmNnqrs\fR] [\fB\-b\fR \fISIZE\fR] [\fB\-c\fR \fISIZE\fR]\ |
21 | [\fB\-f\fR \fIFILE\fR] [\fB\-\-block\-size\fR=\fISIZE\fR] | 21 | [\fB\-f\fR \fIFILE\fR] [\fB\-\-block\-size\fR=\fISIZE\fR] |
22 | [\fB\-\-cache\-size\fR=\fISIZE\fR] [\fB\-\-file\fR \fIFILE\fR]\ | 22 | [\fB\-\-cache\-size\fR=\fISIZE\fR] [\fB\-\-file\fR \fIFILE\fR]\ |
@@ -155,13 +155,13 @@ Print the current bucket. | |||
155 | \fBdelete\fR \fIKEY\fR | 155 | \fBdelete\fR \fIKEY\fR |
156 | Delete record with the given \fIKEY\fR. | 156 | Delete record with the given \fIKEY\fR. |
157 | .TP | 157 | .TP |
158 | .BR dir | 158 | .BR dir |
159 | Print hash directory. | 159 | Print hash directory. |
160 | .TP | 160 | .TP |
161 | \fBexport\fR, \fBe\fR \fIFILE\-NAME\fR [\fBtruncate\fR] [\fBbinary\fR|\fBascii\fR] | 161 | \fBexport\fR \fIFILE\-NAME\fR [\fBtruncate\fR] [\fBbinary\fR|\fBascii\fR] |
162 | Export the database to the flat file \fIFILE\-NAME\fR. This is equivalent to | 162 | Export the database to the flat file \fIFILE\-NAME\fR. This is equivalent to |
163 | .BR gdbm_dump (1). | 163 | .BR gdbm_dump (1). |
164 | 164 | ||
165 | This command will not overwrite an existing file, unless the | 165 | This command will not overwrite an existing file, unless the |
166 | .B truncate | 166 | .B truncate |
167 | parameter is also given. Another optional parameter determines the type of | 167 | parameter is also given. Another optional parameter determines the type of |
@@ -184,12 +184,23 @@ Print file header. | |||
184 | .TP | 184 | .TP |
185 | .BR help " or " ? | 185 | .BR help " or " ? |
186 | Print a concise command summary, showing each command letter and | 186 | Print a concise command summary, showing each command letter and |
187 | verb with its parameters and a short description of what it does. | 187 | verb with its parameters and a short description of what it does. |
188 | Optional arguments are enclosed in square brackets. | 188 | Optional arguments are enclosed in square brackets. |
189 | .TP | 189 | .TP |
190 | .B history | ||
191 | Shows the command history list with line numbers. This command is | ||
192 | available only if the program was compiled with GNU Readline. | ||
193 | .TP | ||
194 | \fBhistory\fR \fICOUNT\fR. | ||
195 | Shows \fICOUNT\fR latest commands from the command history. | ||
196 | .TP | ||
197 | \fBhistory\fR \fIN\fR \fICOUNT\fR. | ||
198 | Shows \fICOUNT\fR commands from the command history starting with \fIN\fRth | ||
199 | command. | ||
200 | .TP | ||
190 | \fBimport\fR \fIFILE\-NAME\fR [\fBreplace\fR] [\fBnometa\fR] | 201 | \fBimport\fR \fIFILE\-NAME\fR [\fBreplace\fR] [\fBnometa\fR] |
191 | Import data from a flat dump file \fIFILE\-NAME\fR. | 202 | Import data from a flat dump file \fIFILE\-NAME\fR. |
192 | If the | 203 | If the |
193 | .B replace | 204 | .B replace |
194 | argument is given, any records with the same keys as the already | 205 | argument is given, any records with the same keys as the already |
195 | existing ones will replace them. The | 206 | 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 =\ | |||
76 | parseopt.c\ | 76 | parseopt.c\ |
77 | progname.c | 77 | progname.c |
78 | 78 | ||
79 | # Programs | 79 | # Programs |
80 | bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump | 80 | bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump |
81 | 81 | ||
82 | gdbmtool_LDADD = ./libgdbmapp.a ./libgdbm.la | 82 | gdbmtool_LDADD = \ |
83 | ./libgdbmapp.a\ | ||
84 | ./libgdbm.la\ | ||
85 | @READLINE_LIBS@ | ||
86 | |||
83 | gdbmtool_SOURCES = \ | 87 | gdbmtool_SOURCES = \ |
84 | datconv.c\ | 88 | datconv.c\ |
85 | gram.y\ | 89 | gram.y\ |
86 | lex.l\ | 90 | lex.l\ |
87 | gdbmtool.h\ | 91 | gdbmtool.h\ |
88 | gdbmtool.c\ | 92 | gdbmtool.c\ |
89 | var.c\ | 93 | var.c\ |
90 | util.c | 94 | util.c |
91 | 95 | ||
96 | if GDBM_COND_READLINE | ||
97 | gdbmtool_SOURCES += input-rl.c | ||
98 | else | ||
99 | gdbmtool_SOURCES += input-std.c | ||
100 | endif | ||
101 | |||
92 | AM_YFLAGS = -dtv | 102 | AM_YFLAGS = -dtv |
93 | #AM_LFLAGS = -d | 103 | #AM_LFLAGS = -d |
94 | 104 | ||
95 | gdbm_load_LDADD = ./libgdbmapp.a ./libgdbm.la | 105 | gdbm_load_LDADD = ./libgdbmapp.a ./libgdbm.la |
96 | gdbm_dump_LDADD = ./libgdbmapp.a ./libgdbm.la | 106 | 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 () | |||
320 | const char *lines = getenv ("LINES"); | 320 | const char *lines = getenv ("LINES"); |
321 | if (lines) | 321 | if (lines) |
322 | return strtol (lines, NULL, 10); | 322 | return strtol (lines, NULL, 10); |
323 | #endif | 323 | #endif |
324 | return -1; | 324 | return -1; |
325 | } | 325 | } |
326 | |||
327 | |||
328 | #define ARG_UNUSED __attribute__ ((__unused__)) | ||
329 | |||
330 | #define NARGS 5 | ||
331 | |||
332 | struct handler_param | ||
333 | { | ||
334 | int argc; | ||
335 | struct gdbmarg **argv; | ||
336 | FILE *fp; | ||
337 | void *data; | ||
338 | }; | ||
339 | |||
340 | 326 | ||
341 | /* Open database */ | 327 | /* Open database */ |
342 | void | 328 | void |
343 | open_handler (struct handler_param *param) | 329 | open_handler (struct handler_param *param) |
344 | { | 330 | { |
345 | if (opendb (param->argv[0]->v.string) == 0) | 331 | if (opendb (param->argv[0]->v.string) == 0) |
@@ -748,13 +734,13 @@ list_handler (struct handler_param *param) | |||
748 | /* quit - quit the program */ | 734 | /* quit - quit the program */ |
749 | void | 735 | void |
750 | quit_handler (struct handler_param *param ARG_UNUSED) | 736 | quit_handler (struct handler_param *param ARG_UNUSED) |
751 | { | 737 | { |
752 | if (gdbm_file != NULL) | 738 | if (gdbm_file != NULL) |
753 | gdbm_close (gdbm_file); | 739 | gdbm_close (gdbm_file); |
754 | 740 | input_done (); | |
755 | exit (EXIT_OK); | 741 | exit (EXIT_OK); |
756 | } | 742 | } |
757 | 743 | ||
758 | /* export FILE [truncate] - export to a flat file format */ | 744 | /* export FILE [truncate] - export to a flat file format */ |
759 | void | 745 | void |
760 | export_handler (struct handler_param *param) | 746 | export_handler (struct handler_param *param) |
@@ -888,24 +874,25 @@ struct argdef | |||
888 | { | 874 | { |
889 | char *name; | 875 | char *name; |
890 | int type; | 876 | int type; |
891 | int ds; | 877 | int ds; |
892 | }; | 878 | }; |
893 | 879 | ||
880 | #define NARGS 5 | ||
881 | |||
894 | struct command | 882 | struct command |
895 | { | 883 | { |
896 | char *name; /* Command name */ | 884 | char *name; /* Command name */ |
897 | size_t len; /* Name length */ | 885 | size_t len; /* Name length */ |
898 | int tok; | 886 | int tok; |
899 | int (*begin) (struct handler_param *param, size_t *); | 887 | int (*begin) (struct handler_param *param, size_t *); |
900 | void (*handler) (struct handler_param *param); | 888 | void (*handler) (struct handler_param *param); |
901 | void (*end) (void *data); | 889 | void (*end) (void *data); |
902 | struct argdef args[NARGS]; | 890 | struct argdef args[NARGS]; |
903 | char *doc; | 891 | char *doc; |
904 | }; | 892 | }; |
905 | |||
906 | 893 | ||
907 | struct command command_tab[] = { | 894 | struct command command_tab[] = { |
908 | #define S(s) #s, sizeof (#s) - 1 | 895 | #define S(s) #s, sizeof (#s) - 1 |
909 | { S(count), T_CMD, | 896 | { S(count), T_CMD, |
910 | checkdb, count_handler, NULL, | 897 | checkdb, count_handler, NULL, |
911 | { { NULL } }, N_("count (number of entries)") }, | 898 | { { NULL } }, N_("count (number of entries)") }, |
@@ -1004,12 +991,19 @@ struct command command_tab[] = { | |||
1004 | NULL, close_handler, NULL, | 991 | NULL, close_handler, NULL, |
1005 | { { NULL } }, N_("close the database") }, | 992 | { { NULL } }, N_("close the database") }, |
1006 | { S(open), T_CMD, | 993 | { S(open), T_CMD, |
1007 | NULL, open_handler, NULL, | 994 | NULL, open_handler, NULL, |
1008 | { { "FILE", GDBM_ARG_STRING }, { NULL } }, | 995 | { { "FILE", GDBM_ARG_STRING }, { NULL } }, |
1009 | N_("open new database") }, | 996 | N_("open new database") }, |
997 | #ifdef WITH_READLINE | ||
998 | { S(history), T_CMD, | ||
999 | input_history_begin, input_history_handler, NULL, | ||
1000 | { { N_("[FROM]"), GDBM_ARG_STRING }, | ||
1001 | { N_("[COUNT]"), GDBM_ARG_STRING }, | ||
1002 | { NULL } }, N_("show input history") }, | ||
1003 | #endif | ||
1010 | #undef S | 1004 | #undef S |
1011 | { 0 } | 1005 | { 0 } |
1012 | }; | 1006 | }; |
1013 | 1007 | ||
1014 | static int | 1008 | static int |
1015 | cmdcmp (const void *a, const void *b) | 1009 | cmdcmp (const void *a, const void *b) |
@@ -1023,12 +1017,45 @@ void | |||
1023 | sort_commands () | 1017 | sort_commands () |
1024 | { | 1018 | { |
1025 | qsort (command_tab, sizeof (command_tab) / sizeof (command_tab[0]) - 1, | 1019 | qsort (command_tab, sizeof (command_tab) / sizeof (command_tab[0]) - 1, |
1026 | sizeof (command_tab[0]), cmdcmp); | 1020 | sizeof (command_tab[0]), cmdcmp); |
1027 | } | 1021 | } |
1028 | 1022 | ||
1023 | /* Generator function for command completion. STATE lets us know whether | ||
1024 | to start from scratch; without any state (i.e. STATE == 0), then we | ||
1025 | start at the top of the list. */ | ||
1026 | char * | ||
1027 | command_generator (const char *text, int state) | ||
1028 | { | ||
1029 | const char *name; | ||
1030 | static int len; | ||
1031 | static struct command *cmd; | ||
1032 | |||
1033 | /* If this is a new word to complete, initialize now. This includes | ||
1034 | saving the length of TEXT for efficiency, and initializing the index | ||
1035 | variable to 0. */ | ||
1036 | if (!state) | ||
1037 | { | ||
1038 | cmd = command_tab; | ||
1039 | len = strlen (text); | ||
1040 | } | ||
1041 | |||
1042 | if (!cmd->name) | ||
1043 | return NULL; | ||
1044 | |||
1045 | /* Return the next name which partially matches from the command list. */ | ||
1046 | while ((name = cmd->name)) | ||
1047 | { | ||
1048 | cmd++; | ||
1049 | if (strncmp (name, text, len) == 0) | ||
1050 | return strdup (name); | ||
1051 | } | ||
1052 | |||
1053 | /* If no names matched, then return NULL. */ | ||
1054 | return NULL; | ||
1055 | } | ||
1029 | 1056 | ||
1030 | /* ? - help handler */ | 1057 | /* ? - help handler */ |
1031 | #define CMDCOLS 30 | 1058 | #define CMDCOLS 30 |
1032 | 1059 | ||
1033 | int | 1060 | int |
1034 | help_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) | 1061 | help_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) |
@@ -1171,31 +1198,61 @@ gdbmarg_kvpair (struct kvpair *kvp, struct locus *loc) | |||
1171 | arg->loc = *loc; | 1198 | arg->loc = *loc; |
1172 | arg->v.kvpair = kvp; | 1199 | arg->v.kvpair = kvp; |
1173 | return arg; | 1200 | return arg; |
1174 | } | 1201 | } |
1175 | 1202 | ||
1176 | struct slist * | 1203 | struct slist * |
1177 | slist_new (char *s) | 1204 | slist_new_s (char *s) |
1178 | { | 1205 | { |
1179 | struct slist *lp = emalloc (sizeof (*lp)); | 1206 | struct slist *lp = emalloc (sizeof (*lp)); |
1180 | lp->next = NULL; | 1207 | lp->next = NULL; |
1181 | lp->str = s; | 1208 | lp->str = s; |
1182 | return lp; | 1209 | return lp; |
1183 | } | 1210 | } |
1184 | 1211 | ||
1212 | struct slist * | ||
1213 | slist_new (char const *s) | ||
1214 | { | ||
1215 | return slist_new_s (estrdup (s)); | ||
1216 | } | ||
1217 | |||
1218 | struct slist * | ||
1219 | slist_new_l (char const *s, size_t l) | ||
1220 | { | ||
1221 | char *copy = emalloc (l + 1); | ||
1222 | memcpy (copy, s, l); | ||
1223 | copy[l] = 0; | ||
1224 | return slist_new_s (copy); | ||
1225 | } | ||
1226 | |||
1185 | void | 1227 | void |
1186 | slist_free (struct slist *lp) | 1228 | slist_free (struct slist *lp) |
1187 | { | 1229 | { |
1188 | while (lp) | 1230 | while (lp) |
1189 | { | 1231 | { |
1190 | struct slist *next = lp->next; | 1232 | struct slist *next = lp->next; |
1191 | free (lp->str); | 1233 | free (lp->str); |
1192 | free (lp); | 1234 | free (lp); |
1193 | lp = next; | 1235 | lp = next; |
1194 | } | 1236 | } |
1195 | } | 1237 | } |
1238 | |||
1239 | void | ||
1240 | slist_insert (struct slist **where, struct slist *what) | ||
1241 | { | ||
1242 | if (*where) | ||
1243 | { | ||
1244 | while (what->next) | ||
1245 | what = what->next; | ||
1246 | what->next = (*where)->next; | ||
1247 | (*where)->next = what; | ||
1248 | } | ||
1249 | else | ||
1250 | what->next = NULL; | ||
1251 | *where = what; | ||
1252 | } | ||
1196 | 1253 | ||
1197 | struct kvpair * | 1254 | struct kvpair * |
1198 | kvpair_string (struct locus *loc, char *val) | 1255 | kvpair_string (struct locus *loc, char *val) |
1199 | { | 1256 | { |
1200 | struct kvpair *p = ecalloc (1, sizeof (*p)); | 1257 | struct kvpair *p = ecalloc (1, sizeof (*p)); |
1201 | p->type = KV_STRING; | 1258 | p->type = KV_STRING; |
@@ -1213,13 +1270,12 @@ kvpair_list (struct locus *loc, struct slist *s) | |||
1213 | if (loc) | 1270 | if (loc) |
1214 | p->loc = *loc; | 1271 | p->loc = *loc; |
1215 | p->val.l = s; | 1272 | p->val.l = s; |
1216 | return p; | 1273 | return p; |
1217 | } | 1274 | } |
1218 | 1275 | ||
1219 | |||
1220 | static void | 1276 | static void |
1221 | kvlist_free (struct kvpair *kvp) | 1277 | kvlist_free (struct kvpair *kvp) |
1222 | { | 1278 | { |
1223 | while (kvp) | 1279 | while (kvp) |
1224 | { | 1280 | { |
1225 | struct kvpair *next = kvp->next; | 1281 | struct kvpair *next = kvp->next; |
@@ -1531,32 +1587,36 @@ int | |||
1531 | main (int argc, char *argv[]) | 1587 | main (int argc, char *argv[]) |
1532 | { | 1588 | { |
1533 | int intr; | 1589 | int intr; |
1534 | int opt; | 1590 | int opt; |
1535 | int bv; | 1591 | int bv; |
1536 | int norc = 0; | 1592 | int norc = 0; |
1593 | int res; | ||
1537 | char *source = "-"; | 1594 | char *source = "-"; |
1538 | 1595 | ||
1539 | set_progname (argv[0]); | 1596 | set_progname (argv[0]); |
1540 | 1597 | ||
1541 | #ifdef HAVE_SETLOCALE | 1598 | #ifdef HAVE_SETLOCALE |
1542 | setlocale (LC_ALL, ""); | 1599 | setlocale (LC_ALL, ""); |
1543 | #endif | 1600 | #endif |
1544 | bindtextdomain (PACKAGE, LOCALEDIR); | 1601 | bindtextdomain (PACKAGE, LOCALEDIR); |
1545 | textdomain (PACKAGE); | 1602 | textdomain (PACKAGE); |
1546 | 1603 | ||
1547 | sort_commands (); | 1604 | sort_commands (); |
1548 | 1605 | ||
1549 | /* Initialize variables. */ | 1606 | /* Initialize variables. */ |
1550 | intr = isatty (0); | 1607 | intr = isatty (0); |
1608 | interactive = intr; /* Used early by input_init */ | ||
1551 | dsdef[DS_KEY] = dsegm_new_field (datadef_lookup ("string"), NULL, 1); | 1609 | dsdef[DS_KEY] = dsegm_new_field (datadef_lookup ("string"), NULL, 1); |
1552 | dsdef[DS_CONTENT] = dsegm_new_field (datadef_lookup ("string"), NULL, 1); | 1610 | dsdef[DS_CONTENT] = dsegm_new_field (datadef_lookup ("string"), NULL, 1); |
1553 | 1611 | ||
1554 | variable_set ("open", VART_STRING, "wrcreat"); | 1612 | variable_set ("open", VART_STRING, "wrcreat"); |
1555 | variable_set ("pager", VART_STRING, getenv ("PAGER")); | 1613 | variable_set ("pager", VART_STRING, getenv ("PAGER")); |
1556 | 1614 | ||
1615 | input_init (); | ||
1616 | |||
1557 | for (opt = parseopt_first (argc, argv, optab); | 1617 | for (opt = parseopt_first (argc, argv, optab); |
1558 | opt != EOF; | 1618 | opt != EOF; |
1559 | opt = parseopt_next ()) | 1619 | opt = parseopt_next ()) |
1560 | switch (opt) | 1620 | switch (opt) |
1561 | { | 1621 | { |
1562 | case 'f': | 1622 | case 'f': |
@@ -1637,8 +1697,10 @@ main (int argc, char *argv[]) | |||
1637 | /* Welcome message. */ | 1697 | /* Welcome message. */ |
1638 | if (intr && !variable_is_true ("quiet")) | 1698 | if (intr && !variable_is_true ("quiet")) |
1639 | printf (_("\nWelcome to the gdbm tool. Type ? for help.\n\n")); | 1699 | printf (_("\nWelcome to the gdbm tool. Type ? for help.\n\n")); |
1640 | 1700 | ||
1641 | if (setsource (source, intr)) | 1701 | if (setsource (source, intr)) |
1642 | exit (EXIT_FATAL); | 1702 | exit (EXIT_FATAL); |
1643 | return yyparse (); | 1703 | res = yyparse (); |
1704 | input_done (); | ||
1705 | return res; | ||
1644 | } | 1706 | } |
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 @@ | |||
21 | #include "gdbmapp.h" | 21 | #include "gdbmapp.h" |
22 | #include <stdlib.h> | 22 | #include <stdlib.h> |
23 | #include <stdio.h> | 23 | #include <stdio.h> |
24 | #include <stdarg.h> | 24 | #include <stdarg.h> |
25 | #include <ctype.h> | 25 | #include <ctype.h> |
26 | 26 | ||
27 | #define ARG_UNUSED __attribute__ ((__unused__)) | ||
28 | |||
27 | /* Position in input file */ | 29 | /* Position in input file */ |
28 | struct point | 30 | struct point |
29 | { | 31 | { |
30 | char *file; /* file name */ | 32 | char *file; /* file name */ |
31 | unsigned line; /* line number */ | 33 | unsigned line; /* line number */ |
32 | unsigned col; /* column number */ | 34 | unsigned col; /* column number */ |
@@ -91,32 +93,46 @@ typedef struct locus gdbm_yyltype_t; | |||
91 | 93 | ||
92 | void vlerror (struct locus *loc, const char *fmt, va_list ap); | 94 | void vlerror (struct locus *loc, const char *fmt, va_list ap); |
93 | void lerror (struct locus *loc, const char *fmt, ...); | 95 | void lerror (struct locus *loc, const char *fmt, ...); |
94 | 96 | ||
95 | void terror (const char *fmt, ...); | 97 | void terror (const char *fmt, ...); |
96 | 98 | ||
97 | void print_prompt (void); | 99 | char *make_prompt (void); |
98 | 100 | ||
99 | int setsource (const char *filename, int intr); | 101 | int setsource (const char *filename, int intr); |
100 | 102 | ||
101 | extern char *file_name; | 103 | extern char *file_name; |
102 | extern int interactive; | 104 | extern int interactive; |
103 | extern int open_mode; | 105 | extern int open_mode; |
104 | 106 | ||
105 | #define GDBMTOOLRC ".gdbmtoolrc" | 107 | #define GDBMTOOLRC ".gdbmtoolrc" |
106 | #define GDBMTOOL_DEFFILE "junk.gdbm" | 108 | #define GDBMTOOL_DEFFILE "junk.gdbm" |
109 | |||
110 | ssize_t input_read (FILE *fp, char *buf, size_t size); | ||
111 | void input_init (void); | ||
112 | void input_done (void); | ||
113 | |||
114 | struct handler_param; | ||
115 | void input_history_handler (struct handler_param *param); | ||
116 | int input_history_begin (struct handler_param *param, size_t *exp_count); | ||
117 | |||
118 | void print_prompt_at_bol (void); | ||
119 | char *command_generator (const char *text, int state); | ||
107 | 120 | ||
108 | 121 | ||
109 | struct slist | 122 | struct slist |
110 | { | 123 | { |
111 | struct slist *next; | 124 | struct slist *next; |
112 | char *str; | 125 | char *str; |
113 | }; | 126 | }; |
114 | 127 | ||
115 | struct slist *slist_new (char *s); | 128 | struct slist *slist_new (char const *s); |
129 | struct slist *slist_new_s (char *s); | ||
130 | struct slist *slist_new_l (char const *s, size_t l); | ||
116 | void slist_free (struct slist *); | 131 | void slist_free (struct slist *); |
132 | void slist_insert (struct slist **where, struct slist *what); | ||
117 | 133 | ||
118 | #define KV_STRING 0 | 134 | #define KV_STRING 0 |
119 | #define KV_LIST 1 | 135 | #define KV_LIST 1 |
120 | 136 | ||
121 | struct kvpair | 137 | struct kvpair |
122 | { | 138 | { |
@@ -158,12 +174,20 @@ struct gdbmarg | |||
158 | /* List of arguments */ | 174 | /* List of arguments */ |
159 | struct gdbmarglist | 175 | struct gdbmarglist |
160 | { | 176 | { |
161 | struct gdbmarg *head, *tail; | 177 | struct gdbmarg *head, *tail; |
162 | }; | 178 | }; |
163 | 179 | ||
180 | struct handler_param | ||
181 | { | ||
182 | int argc; | ||
183 | struct gdbmarg **argv; | ||
184 | FILE *fp; | ||
185 | void *data; | ||
186 | }; | ||
187 | |||
164 | void gdbmarglist_init (struct gdbmarglist *, struct gdbmarg *); | 188 | void gdbmarglist_init (struct gdbmarglist *, struct gdbmarg *); |
165 | void gdbmarglist_add (struct gdbmarglist *, struct gdbmarg *); | 189 | void gdbmarglist_add (struct gdbmarglist *, struct gdbmarg *); |
166 | void gdbmarglist_free (struct gdbmarglist *lst); | 190 | void gdbmarglist_free (struct gdbmarglist *lst); |
167 | 191 | ||
168 | struct gdbmarg *gdbmarg_string (char *, struct locus *); | 192 | struct gdbmarg *gdbmarg_string (char *, struct locus *); |
169 | struct gdbmarg *gdbmarg_datum (datum *, struct locus *); | 193 | struct gdbmarg *gdbmarg_datum (datum *, struct locus *); |
@@ -259,6 +283,9 @@ int datum_scan (datum *dat, struct dsegm *ds, struct kvpair *kv); | |||
259 | void dsprint (FILE *fp, int what, struct dsegm *ds); | 283 | void dsprint (FILE *fp, int what, struct dsegm *ds); |
260 | 284 | ||
261 | char *mkfilename (const char *dir, const char *file, const char *suf); | 285 | char *mkfilename (const char *dir, const char *file, const char *suf); |
262 | char *tildexpand (char *s); | 286 | char *tildexpand (char *s); |
263 | int vgetyn (const char *prompt, va_list ap); | 287 | int vgetyn (const char *prompt, va_list ap); |
264 | int getyn (const char *prompt, ...); | 288 | int getyn (const char *prompt, ...); |
289 | |||
290 | int getnum (int *pnum, char *arg, char **endp); | ||
291 | int get_screen_lines (void); | ||
@@ -165,19 +165,18 @@ value : string | |||
165 | $$ = kvpair_list (&@1, $2.head); | 165 | $$ = kvpair_list (&@1, $2.head); |
166 | } | 166 | } |
167 | ; | 167 | ; |
168 | 168 | ||
169 | slist : string | 169 | slist : string |
170 | { | 170 | { |
171 | $$.head = $$.tail = slist_new ($1); | 171 | $$.head = $$.tail = slist_new_s ($1); |
172 | } | 172 | } |
173 | | slist ',' string | 173 | | slist ',' string |
174 | { | 174 | { |
175 | struct slist *s = slist_new ($3); | 175 | struct slist *s = slist_new_s ($3); |
176 | $1.tail->next = s; | 176 | slist_insert (&$1.tail, s); |
177 | $1.tail = s; | ||
178 | $$ = $1; | 177 | $$ = $1; |
179 | } | 178 | } |
180 | ; | 179 | ; |
181 | 180 | ||
182 | string : T_IDENT | 181 | string : T_IDENT |
183 | | T_WORD | 182 | | 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 @@ | |||
1 | /* This file is part of GDBM, the GNU data base manager. | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GDBM is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 3, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | GDBM is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | #include "gdbmtool.h" | ||
18 | #include <readline/readline.h> | ||
19 | #include <readline/history.h> | ||
20 | |||
21 | static char *pre_input_line; | ||
22 | |||
23 | static int | ||
24 | pre_input (void) | ||
25 | { | ||
26 | if (pre_input_line) | ||
27 | { | ||
28 | rl_insert_text (pre_input_line); | ||
29 | free (pre_input_line); | ||
30 | pre_input_line = NULL; | ||
31 | rl_redisplay (); | ||
32 | } | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | static int | ||
37 | retrieve_history (char *str) | ||
38 | { | ||
39 | char *out; | ||
40 | int rc; | ||
41 | |||
42 | rc = history_expand (str, &out); | ||
43 | switch (rc) | ||
44 | { | ||
45 | case -1: | ||
46 | yyerror (out); | ||
47 | free (out); | ||
48 | return 1; | ||
49 | |||
50 | case 0: | ||
51 | break; | ||
52 | |||
53 | case 1: | ||
54 | pre_input_line = out; | ||
55 | return 1; | ||
56 | |||
57 | case 2: | ||
58 | printf ("%s\n", out); | ||
59 | free (out); | ||
60 | return 1; | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | ssize_t | ||
66 | input_read (FILE *fp, char *buf, size_t size) | ||
67 | { | ||
68 | static char *input_line; | ||
69 | static size_t input_length; | ||
70 | static size_t input_off; | ||
71 | #define input_ptr() (input_line + input_off) | ||
72 | #define input_size() (input_length - input_off) | ||
73 | |||
74 | if (interactive) | ||
75 | { | ||
76 | size_t len = input_size (); | ||
77 | if (!len) | ||
78 | { | ||
79 | if (input_line) | ||
80 | { | ||
81 | newline: | ||
82 | free (input_line); | ||
83 | input_line = NULL; | ||
84 | buf[0] = '\n'; | ||
85 | return 1; | ||
86 | } | ||
87 | else | ||
88 | { | ||
89 | char *prompt; | ||
90 | again: | ||
91 | prompt = make_prompt (); | ||
92 | input_line = readline (prompt); | ||
93 | free (prompt); | ||
94 | if (!input_line) | ||
95 | return 0; | ||
96 | input_length = strlen (input_line); | ||
97 | input_off = 0; | ||
98 | if (input_length) | ||
99 | { | ||
100 | if (retrieve_history (input_line)) | ||
101 | { | ||
102 | free (input_line); | ||
103 | goto again; | ||
104 | } | ||
105 | } | ||
106 | else | ||
107 | goto newline; | ||
108 | len = input_size (); | ||
109 | add_history (input_line); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if (len > size) | ||
114 | len = size; | ||
115 | memcpy (buf, input_ptr (), len); | ||
116 | input_off += len; | ||
117 | |||
118 | return len; | ||
119 | } | ||
120 | return fread (buf, 1, size, fp); | ||
121 | } | ||
122 | |||
123 | struct history_param | ||
124 | { | ||
125 | int from; | ||
126 | int count; | ||
127 | }; | ||
128 | |||
129 | int | ||
130 | input_history_begin (struct handler_param *param, size_t *exp_count) | ||
131 | { | ||
132 | struct history_param *p; | ||
133 | int from = 0, count = history_length; | ||
134 | |||
135 | switch (param->argc) | ||
136 | { | ||
137 | case 1: | ||
138 | if (getnum (&count, param->argv[0]->v.string, NULL)) | ||
139 | return 1; | ||
140 | if (count > history_length) | ||
141 | count = history_length; | ||
142 | else | ||
143 | from = history_length - count; | ||
144 | break; | ||
145 | |||
146 | case 2: | ||
147 | if (getnum (&from, param->argv[0]->v.string, NULL)) | ||
148 | return 1; | ||
149 | if (from) | ||
150 | --from; | ||
151 | if (getnum (&count, param->argv[1]->v.string, NULL)) | ||
152 | return 1; | ||
153 | |||
154 | if (count > history_length) | ||
155 | count = history_length; | ||
156 | } | ||
157 | p = emalloc (sizeof *p); | ||
158 | p->from = from; | ||
159 | p->count = count; | ||
160 | param->data = p; | ||
161 | if (exp_count) | ||
162 | *exp_count = count; | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | void | ||
167 | input_history_handler (struct handler_param *param) | ||
168 | { | ||
169 | struct history_param *p = param->data; | ||
170 | int i; | ||
171 | HIST_ENTRY **hlist; | ||
172 | FILE *fp = param->fp; | ||
173 | |||
174 | hlist = history_list (); | ||
175 | for (i = 0; i < p->count; i++) | ||
176 | fprintf (fp, "%4d) %s\n", p->from + i + 1, hlist[p->from + i]->line); | ||
177 | } | ||
178 | |||
179 | #define HISTFILE_PREFIX "~/." | ||
180 | #define HISTFILE_SUFFIX "_history" | ||
181 | |||
182 | static char * | ||
183 | get_history_file_name () | ||
184 | { | ||
185 | static char *filename = NULL; | ||
186 | |||
187 | if (!filename) | ||
188 | { | ||
189 | char *hname; | ||
190 | |||
191 | hname = emalloc (sizeof HISTFILE_PREFIX + strlen (rl_readline_name) + | ||
192 | sizeof HISTFILE_SUFFIX - 1); | ||
193 | strcpy (hname, HISTFILE_PREFIX); | ||
194 | strcat (hname, rl_readline_name); | ||
195 | strcat (hname, HISTFILE_SUFFIX); | ||
196 | filename = tildexpand (hname); | ||
197 | free (hname); | ||
198 | } | ||
199 | return filename; | ||
200 | } | ||
201 | |||
202 | static char ** | ||
203 | shell_completion (const char *text, int start, int end) | ||
204 | { | ||
205 | char **matches; | ||
206 | |||
207 | matches = (char **) NULL; | ||
208 | |||
209 | /* If this word is at the start of the line, then it is a command | ||
210 | to complete. Otherwise it is the name of a file in the current | ||
211 | directory. */ | ||
212 | if (start == 0) | ||
213 | matches = rl_completion_matches (text, command_generator); | ||
214 | |||
215 | return (matches); | ||
216 | } | ||
217 | |||
218 | void | ||
219 | input_init (void) | ||
220 | { | ||
221 | /* Allow conditional parsing of the ~/.inputrc file. */ | ||
222 | rl_readline_name = (char *) progname; | ||
223 | rl_attempted_completion_function = shell_completion; | ||
224 | rl_pre_input_hook = pre_input; | ||
225 | if (interactive) | ||
226 | read_history (get_history_file_name ()); | ||
227 | } | ||
228 | |||
229 | void | ||
230 | input_done (void) | ||
231 | { | ||
232 | if (interactive) | ||
233 | write_history (get_history_file_name ()); | ||
234 | } | ||
235 | |||
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 @@ | |||
1 | /* This file is part of GDBM, the GNU data base manager. | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GDBM is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 3, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | GDBM is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | #include "gdbmtool.h" | ||
18 | |||
19 | ssize_t | ||
20 | input_read (FILE *fp, char *buf, size_t size) | ||
21 | { | ||
22 | if (interactive) | ||
23 | { | ||
24 | print_prompt_at_bol (); | ||
25 | if (fgets (buf, size, fp) == NULL) | ||
26 | return 0; | ||
27 | return strlen (buf); | ||
28 | } | ||
29 | return fread (buf, 1, size, fp); | ||
30 | } | ||
31 | |||
32 | void | ||
33 | input_init (void) | ||
34 | { | ||
35 | /* nothing */ | ||
36 | } | ||
37 | |||
38 | void | ||
39 | input_done (void) | ||
40 | { | ||
41 | /* nothing */ | ||
42 | } | ||
43 | |||
@@ -43,24 +43,22 @@ advance_line () | |||
43 | while (0); | 43 | while (0); |
44 | 44 | ||
45 | #undef YY_INPUT | 45 | #undef YY_INPUT |
46 | #define YY_INPUT(buf,result,max_size) \ | 46 | #define YY_INPUT(buf,result,max_size) \ |
47 | do \ | 47 | do \ |
48 | { \ | 48 | { \ |
49 | result = read_input (buf, max_size); \ | 49 | result = input_read (yyin, buf, max_size); \ |
50 | } \ | 50 | } \ |
51 | while (0); | 51 | while (0); |
52 | 52 | ||
53 | void string_begin (void); | 53 | void string_begin (void); |
54 | void string_add (const char *s, int l); | 54 | void string_add (const char *s, int l); |
55 | void string_addc (int c); | 55 | void string_addc (int c); |
56 | char *string_end (void); | 56 | char *string_end (void); |
57 | int unescape (int c); | 57 | int unescape (int c); |
58 | 58 | ||
59 | static ssize_t read_input (char *buf, size_t size); | ||
60 | |||
61 | struct context /* Input context */ | 59 | struct context /* Input context */ |
62 | { | 60 | { |
63 | struct context *parent; /* Pointer to the parent context */ | 61 | struct context *parent; /* Pointer to the parent context */ |
64 | struct locus locus; /* Locus */ | 62 | struct locus locus; /* Locus */ |
65 | struct point point; | 63 | struct point point; |
66 | int interactive; | 64 | int interactive; |
@@ -312,24 +310,22 @@ begin_def (void) | |||
312 | void | 310 | void |
313 | end_def (void) | 311 | end_def (void) |
314 | { | 312 | { |
315 | BEGIN (INITIAL); | 313 | BEGIN (INITIAL); |
316 | } | 314 | } |
317 | 315 | ||
318 | static ssize_t | 316 | void |
319 | read_input (char *buf, size_t size) | 317 | print_prompt_at_bol (void) |
320 | { | 318 | { |
321 | if (interactive) | 319 | if (YY_AT_BOL ()) |
322 | { | 320 | { |
323 | if (YY_AT_BOL ()) | 321 | char *s = make_prompt (); |
324 | print_prompt (); | 322 | fputs (s, stdout); |
325 | if (fgets (buf, size, yyin) == NULL) | 323 | fflush (stdout); |
326 | return 0; | 324 | free (s); |
327 | return strlen (buf); | ||
328 | } | 325 | } |
329 | return fread (buf, 1, size, yyin); | ||
330 | } | 326 | } |
331 | 327 | ||
332 | 328 | ||
333 | struct strseg | 329 | struct strseg |
334 | { | 330 | { |
335 | struct strseg *next; | 331 | struct strseg *next; |
@@ -450,121 +446,151 @@ lerror (struct locus *loc, const char *fmt, ...) | |||
450 | va_start (ap, fmt); | 446 | va_start (ap, fmt); |
451 | vlerror (loc, fmt, ap); | 447 | vlerror (loc, fmt, ap); |
452 | va_end (ap); | 448 | va_end (ap); |
453 | } | 449 | } |
454 | 450 | ||
455 | 451 | ||
456 | struct prompt_exp; | 452 | static struct slist * |
457 | 453 | pe_file_name (void) | |
458 | void | ||
459 | pe_file_name (struct prompt_exp *p) | ||
460 | { | 454 | { |
461 | if (file_name) | 455 | return file_name ? slist_new (file_name) : NULL; |
462 | fwrite (file_name, strlen (file_name), 1, stdout); | ||
463 | } | 456 | } |
464 | 457 | ||
465 | void | 458 | static struct slist * |
466 | pe_program_name (struct prompt_exp *p) | 459 | pe_program_name (void) |
467 | { | 460 | { |
468 | fwrite (progname, strlen (progname), 1, stdout); | 461 | return slist_new (progname); |
469 | } | 462 | } |
470 | 463 | ||
471 | void | 464 | static struct slist * |
472 | pe_package_name (struct prompt_exp *p) | 465 | pe_package_name (void) |
473 | { | 466 | { |
474 | fwrite (PACKAGE_NAME, sizeof (PACKAGE_NAME) - 1, 1, stdout); | 467 | return slist_new (PACKAGE_NAME); |
475 | } | 468 | } |
476 | 469 | ||
477 | void | 470 | static struct slist * |
478 | pe_program_version (struct prompt_exp *p) | 471 | pe_program_version (void) |
479 | { | 472 | { |
480 | fwrite (PACKAGE_VERSION, sizeof (PACKAGE_VERSION) - 1, 1, stdout); | 473 | return slist_new (PACKAGE_VERSION); |
481 | } | 474 | } |
482 | 475 | ||
483 | void | 476 | static struct slist * |
484 | pe_space (struct prompt_exp *p) | 477 | pe_space (void) |
485 | { | 478 | { |
486 | fwrite (" ", 1, 1, stdout); | 479 | return slist_new (" "); |
487 | } | 480 | } |
488 | 481 | ||
489 | struct prompt_exp | 482 | struct prompt_exp |
490 | { | 483 | { |
491 | int ch; | 484 | int ch; |
492 | void (*fun) (struct prompt_exp *); | 485 | struct slist *(*fun) (void); |
493 | char *cache; | ||
494 | }; | 486 | }; |
495 | 487 | ||
496 | struct prompt_exp prompt_exp[] = { | 488 | struct prompt_exp prompt_exp[] = { |
497 | { 'f', pe_file_name }, | 489 | { 'f', pe_file_name }, |
498 | { 'p', pe_program_name }, | 490 | { 'p', pe_program_name }, |
499 | { 'P', pe_package_name }, | 491 | { 'P', pe_package_name }, |
500 | { 'v', pe_program_version }, | 492 | { 'v', pe_program_version }, |
501 | { '_', pe_space }, | 493 | { '_', pe_space }, |
502 | { 0 } | 494 | { 0 } |
503 | }; | 495 | }; |
504 | 496 | ||
505 | static void | 497 | static int |
506 | expand_char (int c) | 498 | expand_char (int c, struct slist **tailp) |
507 | { | 499 | { |
508 | struct prompt_exp *p; | 500 | struct prompt_exp *p; |
509 | 501 | ||
510 | if (c && c != '%') | 502 | if (c && c != '%') |
511 | { | 503 | { |
512 | for (p = prompt_exp; p->ch; p++) | 504 | for (p = prompt_exp; p->ch; p++) |
513 | { | 505 | { |
514 | if (c == p->ch) | 506 | if (c == p->ch) |
515 | { | 507 | { |
516 | if (p->cache) | 508 | struct slist *s = p->fun (); |
517 | free (p->cache); | 509 | if (s) |
518 | p->fun (p); | 510 | slist_insert (tailp, s); |
519 | return; | 511 | return 0; |
520 | } | 512 | } |
521 | } | 513 | } |
522 | } | 514 | } |
523 | putchar ('%'); | 515 | return 1; |
524 | putchar (c); | ||
525 | } | 516 | } |
526 | 517 | ||
527 | char const * | 518 | char const * |
528 | psname () | 519 | psname (void) |
529 | { | 520 | { |
530 | if (YYSTATE == DEF || YYSTATE == MLSTR) | 521 | if (YYSTATE == DEF || YYSTATE == MLSTR) |
531 | return "ps2"; | 522 | return "ps2"; |
532 | return "ps1"; | 523 | return "ps1"; |
533 | } | 524 | } |
534 | 525 | ||
535 | void | 526 | char * |
536 | print_prompt () | 527 | make_prompt (void) |
537 | { | 528 | { |
538 | const char *s; | 529 | const char *s; |
539 | const char *prompt; | 530 | const char *prompt; |
540 | 531 | struct slist *head = NULL, *tail = NULL, *p; | |
532 | char *ret, *end; | ||
533 | size_t len; | ||
534 | |||
541 | switch (variable_get (psname (), VART_STRING, (void *) &prompt)) | 535 | switch (variable_get (psname (), VART_STRING, (void *) &prompt)) |
542 | { | 536 | { |
543 | case VAR_OK: | 537 | case VAR_OK: |
544 | break; | 538 | break; |
545 | 539 | ||
546 | case VAR_ERR_NOTSET: | 540 | case VAR_ERR_NOTSET: |
547 | return; | 541 | return NULL; |
548 | 542 | ||
549 | default: | 543 | default: |
550 | abort (); | 544 | abort (); |
551 | } | 545 | } |
552 | 546 | ||
553 | for (s = prompt; *s; s++) | 547 | for (s = prompt; *s; ) |
554 | { | 548 | { |
555 | if (*s == '%') | 549 | if (*s == '%' && s[1]) |
556 | { | 550 | { |
557 | if (!*++s) | 551 | if (s > prompt) |
552 | { | ||
553 | slist_insert (&tail, slist_new_l (prompt, s - prompt)); | ||
554 | if (!head) | ||
555 | head = tail; | ||
556 | } | ||
557 | if (expand_char (s[1], &tail) == 0) | ||
558 | { | 558 | { |
559 | putchar ('%'); | 559 | if (!head) |
560 | break; | 560 | head = tail; |
561 | prompt = s + 2; | ||
561 | } | 562 | } |
562 | expand_char (*s); | 563 | else |
564 | prompt = s; | ||
565 | s += 2; | ||
563 | } | 566 | } |
564 | else | 567 | else |
565 | putchar (*s); | 568 | ++s; |
569 | } | ||
570 | |||
571 | if (s > prompt) | ||
572 | { | ||
573 | slist_insert (&tail, slist_new_l (prompt, s - prompt)); | ||
574 | if (!head) | ||
575 | head = tail; | ||
566 | } | 576 | } |
567 | 577 | ||
568 | fflush (stdout); | 578 | len = 0; |
579 | for (p = head; p; p = p->next) | ||
580 | len += strlen (p->str); | ||
581 | |||
582 | ret = emalloc (len + 1); | ||
583 | end = ret; | ||
584 | for (p = head; p; p = p->next) | ||
585 | { | ||
586 | s = p->str; | ||
587 | while (*s) | ||
588 | *end++ = *s++; | ||
589 | } | ||
590 | *end = 0; | ||
591 | |||
592 | slist_free (head); | ||
593 | |||
594 | return ret; | ||
569 | } | 595 | } |
570 | 596 | ||