1
/* Input history for input fields. */
2
/* $Id: inphist.c,v 1.74 2004/01/05 11:29:40 jonas Exp $ */
13
#include "bfu/dialog.h"
14
#include "bfu/inphist.h"
16
#include "config/options.h"
17
#include "lowlevel/home.h"
18
#include "terminal/terminal.h"
19
#include "terminal/window.h"
20
#include "util/conv.h"
21
#include "util/file.h"
22
#include "util/lists.h"
23
#include "util/memory.h"
24
#include "util/secsave.h"
28
tab_compl_n(struct terminal *term, unsigned char *item, int len,
31
struct term_event ev = INIT_TERM_EVENT(EV_REDRAW, term->width, term->height, 0);
32
struct dialog_data *dlg_data = (struct dialog_data *) win->data;
33
struct widget_data *widget_data = selected_widget(dlg_data);
35
int_upper_bound(&len, widget_data->widget->datalen - 1);
36
memcpy(widget_data->cdata, item, len);
37
widget_data->cdata[len] = 0;
38
widget_data->info.field.cpos = len;
39
widget_data->info.field.vpos = 0;
40
dialog_func(win, &ev, 0);
44
tab_compl(struct terminal *term, unsigned char *item, struct window *win)
46
tab_compl_n(term, item, strlen(item), win);
49
/* Complete to last unambiguous character, and display menu for all possible
50
* further completions. */
52
do_tab_compl(struct terminal *term, struct list_head *history,
55
struct dialog_data *dlg_data = (struct dialog_data *) win->data;
56
struct widget_data *widget_data = selected_widget(dlg_data);
57
int cdata_len = strlen(widget_data->cdata);
59
struct input_history_entry *entry;
60
struct menu_item *items = new_menu(FREE_LIST | NO_INTL);
64
foreach (entry, *history) {
65
if (strncmp(widget_data->cdata, entry->data, cdata_len))
68
add_to_menu(&items, entry->data, NULL, ACT_NONE,
69
(menu_func) tab_compl, entry->data, 0);
75
tab_compl(term, items->data, win);
80
do_menu_selected(term, items, win, n - 1, 0);
84
/* Complete to the last unambiguous character. Eg., I've been to google.com,
85
* google.com/search?q=foo, and google.com/search?q=bar. This function then
86
* completes `go' to `google.com' and `google.com/' to `google.com/search?q='.
89
do_tab_compl_unambiguous(struct terminal *term, struct list_head *history,
92
struct dialog_data *dlg_data = (struct dialog_data *) win->data;
93
struct widget_data *widget_data = selected_widget(dlg_data);
94
int base_len = strlen(widget_data->cdata);
95
/* Maximum number of characters in a match. Characters after this
96
* position are varying in other matches. Zero means that no max has
98
int longest_common_match = 0;
99
unsigned char *match = NULL;
100
struct input_history_entry *entry;
102
foreach (entry, *history) {
103
unsigned char *cur = entry->data;
104
unsigned char *matchpos = match ? match : widget_data->cdata;
107
for (; *cur && *cur == *matchpos; ++cur, ++matchpos) {
110
/* XXX: I think that unifying the two cases of this
111
* test could seriously hurt readability. --pasky */
112
if (longest_common_match
113
&& cur_len >= longest_common_match)
117
if (cur_len < base_len)
120
if (!match) cur_len = strlen(entry->data);
122
/* By now, @cur_len oscillates between @base_len and
123
* @longest_common_match. */
124
if (longest_common_match
125
&& cur_len >= longest_common_match)
128
/* We found the next shortest common match. */
129
longest_common_match = cur_len;
135
tab_compl_n(term, match, longest_common_match, win);
139
/* Search for duplicate entries in history list, save first one and remove
141
static struct input_history_entry *
142
check_duplicate_entries(struct input_history *history, unsigned char *data)
144
struct input_history_entry *entry, *first_duplicate = NULL;
146
if (!history || !data || !*data) return NULL;
148
foreach (entry, history->entries) {
149
struct input_history_entry *duplicate;
151
if (strcmp(entry->data, data)) continue;
153
/* Found a duplicate -> remove it from history list */
158
del_from_list(duplicate);
160
/* Save the first duplicate entry */
161
if (!first_duplicate) {
162
first_duplicate = duplicate;
169
return first_duplicate;
172
/* Add a new entry in inputbox history list, take care of duplicate if
173
* check_duplicate and respect history size limit. */
175
add_to_input_history(struct input_history *history, unsigned char *data,
178
struct input_history_entry *entry;
181
if (!history || !data || !*data)
184
/* Strip spaces at the margins */
185
trim_chars(data, ' ', &length);
188
if (check_duplicate) {
189
entry = check_duplicate_entries(history, data);
191
add_to_list(history->entries, entry);
192
if (!history->nosave) history->dirty = 1;
197
/* Copy it all etc. */
198
/* One byte is already reserved for url in struct input_history_item. */
199
entry = mem_alloc(sizeof(struct input_history_entry) + length);
202
memcpy(entry->data, data, length + 1);
204
add_to_history_list(history, entry);
206
/* limit size of history to MAX_INPUT_HISTORY_ENTRIES
207
* removing first entries if needed */
208
for (; history->size > MAX_INPUT_HISTORY_ENTRIES; history->size--) {
209
if (list_empty(history->entries)) {
210
INTERNAL("history is empty");
215
entry = history->entries.prev;
216
del_from_list(entry);
221
/* Load history file */
223
load_input_history(struct input_history *history, unsigned char *filename)
225
unsigned char *history_file = filename;
226
unsigned char line[MAX_STR_LEN];
229
if (get_opt_int_tree(cmdline_options, "anonymous")) return 0;
231
history_file = straconcat(elinks_home, filename, NULL);
232
if (!history_file) return 0;
237
file = fopen(history_file, "r");
238
if (elinks_home) mem_free(history_file);
241
while (safe_fgets(line, MAX_STR_LEN, file)) {
243
if (*line) line[strlen(line) - 1] = 0;
244
add_to_input_history(history, line, 0);
253
/* Write history list to file. It returns a value different from 0 in case of
254
* failure, 0 on success. */
256
save_input_history(struct input_history *history, unsigned char *filename)
258
struct input_history_entry *entry;
259
struct secure_save_info *ssi;
260
unsigned char *history_file;
265
|| get_opt_int_tree(cmdline_options, "anonymous"))
268
history_file = straconcat(elinks_home, filename, NULL);
269
if (!history_file) return -1;
271
ssi = secure_open(history_file, 0177);
272
mem_free(history_file);
275
foreachback (entry, history->entries) {
276
if (i++ > MAX_INPUT_HISTORY_ENTRIES) break;
277
secure_fputs(ssi, entry->data);
278
secure_fputc(ssi, '\n');
282
if (!ssi->err) history->dirty = 0;
284
return secure_close(ssi);