diff options
Diffstat (limited to 'src/input-rl.c')
-rw-r--r-- | src/input-rl.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/input-rl.c b/src/input-rl.c new file mode 100644 index 0000000..f435ed4 --- /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 | |||