1 /* Input history for input fields. */
12 #include "bfu/dialog.h"
13 #include "bfu/inphist.h"
15 #include "config/home.h"
16 #include "config/options.h"
17 #include "dialogs/menu.h"
18 #include "terminal/terminal.h"
19 #include "util/conv.h"
20 #include "util/file.h"
21 #include "util/lists.h"
22 #include "util/memory.h"
23 #include "util/secsave.h"
27 tab_compl_n(struct dialog_data
*dlg_data
, unsigned char *item
, int len
)
29 struct widget_data
*widget_data
= selected_widget(dlg_data
);
31 assert(widget_is_textfield(widget_data
));
33 int_upper_bound(&len
, widget_data
->widget
->datalen
- 1);
34 memcpy(widget_data
->cdata
, item
, len
);
35 widget_data
->cdata
[len
] = 0;
36 widget_data
->info
.field
.cpos
= len
;
37 widget_data
->info
.field
.vpos
= 0;
39 redraw_dialog(dlg_data
, 1);
43 tab_compl(struct dialog_data
*dlg_data
, unsigned char *item
)
45 tab_compl_n(dlg_data
, item
, strlen(item
));
50 menu_tab_compl(struct terminal
*term
, void *item_
, void *dlg_data_
)
52 unsigned char *item
= item_
;
53 struct dialog_data
*dlg_data
= dlg_data_
;
55 tab_compl_n(dlg_data
, item
, strlen(item
));
58 /* Complete to last unambiguous character, and display menu for all possible
59 * further completions. */
61 do_tab_compl(struct dialog_data
*dlg_data
,
62 LIST_OF(struct input_history_entry
) *history
)
64 struct terminal
*term
= dlg_data
->win
->term
;
65 struct widget_data
*widget_data
= selected_widget(dlg_data
);
66 int cpos
= widget_data
->info
.field
.cpos
;
68 struct input_history_entry
*entry
;
69 struct menu_item
*items
= new_menu(FREE_LIST
| NO_INTL
);
73 foreach (entry
, *history
) {
74 if (strncmp(widget_data
->cdata
, entry
->data
, cpos
))
77 add_to_menu(&items
, entry
->data
, NULL
, ACT_MAIN_NONE
,
78 menu_tab_compl
, entry
->data
, 0);
83 do_menu_selected(term
, items
, dlg_data
, n
- 1, 0);
85 if (n
== 1) tab_compl(dlg_data
, items
->data
);
90 /* Return the length of the common substring from the starts
91 * of the two strings a and b. */
93 strcommonlen(unsigned char *a
, unsigned char *b
)
95 unsigned char *start
= a
;
97 while (*a
&& *a
== *b
)
103 /* Complete to the last unambiguous character. Eg., I've been to google.com,
104 * google.com/search?q=foo, and google.com/search?q=bar. This function then
105 * completes `go' to `google.com' and `google.com/' to `google.com/search?q='.
108 do_tab_compl_unambiguous(struct dialog_data
*dlg_data
,
109 LIST_OF(struct input_history_entry
) *history
)
111 struct string completion
;
112 struct widget_data
*widget_data
= selected_widget(dlg_data
);
113 int base_len
= widget_data
->info
.field
.cpos
;
114 /* Maximum number of characters in a match. Characters after this
115 * position are varying in other matches. */
116 int longest_common_match
= 0;
117 unsigned char *match
= NULL
;
118 struct input_history_entry
*entry
;
120 foreach (entry
, *history
) {
121 unsigned char *cur
= entry
->data
;
122 int cur_len
= strcommonlen(cur
, match
? match
123 : widget_data
->cdata
);
125 /* Throw away it away if it isn't even as long as what the user
127 if (cur_len
< base_len
)
131 /* This is the first match, so its length is the maximum
132 * for any future matches. */
133 longest_common_match
= strlen(entry
->data
);
135 } else if (cur_len
< longest_common_match
) {
136 /* The current match has a shorter substring in common
137 * with the previous candidates, so the common substring
139 longest_common_match
= cur_len
;
145 if (!init_string(&completion
)) return;
147 add_bytes_to_string(&completion
, match
, longest_common_match
);
148 add_to_string(&completion
, widget_data
->cdata
149 + widget_data
->info
.field
.cpos
);
151 tab_compl_n(dlg_data
, completion
.source
, completion
.length
);
153 done_string(&completion
);
159 set_complete_file_menu(struct terminal
*term
, void *filename_
, void *dlg_data_
)
161 struct dialog_data
*dlg_data
= dlg_data_
;
162 struct widget_data
*widget_data
= selected_widget(dlg_data
);
163 unsigned char *filename
= filename_
;
166 assert(widget_is_textfield(widget_data
));
168 filenamelen
= int_min(widget_data
->widget
->datalen
- 1, strlen(filename
));
169 memcpy(widget_data
->cdata
, filename
, filenamelen
);
171 widget_data
->cdata
[filenamelen
] = 0;
172 widget_data
->info
.field
.cpos
= filenamelen
;
173 widget_data
->info
.field
.vpos
= 0;
177 redraw_dialog(dlg_data
, 1);
182 tab_complete_file_menu(struct terminal
*term
, void *path_
, void *dlg_data_
)
184 struct dialog_data
*dlg_data
= dlg_data_
;
185 unsigned char *path
= path_
;
187 auto_complete_file(term
, 0 /* no_elevator */, path
,
188 set_complete_file_menu
, tab_complete_file_menu
,
193 do_tab_compl_file(struct dialog_data
*dlg_data
,
194 LIST_OF(struct input_history_entry
) *history
)
196 struct widget_data
*widget_data
= selected_widget(dlg_data
);
198 if (get_cmd_opt_bool("anonymous"))
201 tab_complete_file_menu(dlg_data
->win
->term
, widget_data
->cdata
, dlg_data
);
205 /* Search for duplicate entries in history list, save first one and remove
207 static struct input_history_entry
*
208 check_duplicate_entries(struct input_history
*history
, unsigned char *data
)
210 struct input_history_entry
*entry
, *first_duplicate
= NULL
;
212 if (!history
|| !data
|| !*data
) return NULL
;
214 foreach (entry
, history
->entries
) {
215 struct input_history_entry
*duplicate
;
217 if (strcmp(entry
->data
, data
)) continue;
219 /* Found a duplicate -> remove it from history list */
224 del_from_history_list(history
, duplicate
);
226 /* Save the first duplicate entry */
227 if (!first_duplicate
) {
228 first_duplicate
= duplicate
;
234 return first_duplicate
;
237 /* Add a new entry in inputbox history list, take care of duplicate if
238 * check_duplicate and respect history size limit. */
240 add_to_input_history(struct input_history
*history
, unsigned char *data
,
243 struct input_history_entry
*entry
;
246 if (!history
|| !data
|| !*data
)
249 /* Strip spaces at the margins */
250 trim_chars(data
, ' ', &length
);
253 if (check_duplicate
) {
254 entry
= check_duplicate_entries(history
, data
);
256 add_to_history_list(history
, entry
);
261 /* Copy it all etc. */
262 /* One byte is already reserved for url in struct input_history_item. */
263 entry
= mem_alloc(sizeof(*entry
) + length
);
266 memcpy(entry
->data
, data
, length
+ 1);
268 add_to_history_list(history
, entry
);
270 /* Limit size of history to MAX_INPUT_HISTORY_ENTRIES, removing first
271 * entries if needed. */
272 while (history
->size
> MAX_INPUT_HISTORY_ENTRIES
) {
273 if (list_empty(history
->entries
)) {
274 INTERNAL("history is empty");
279 entry
= history
->entries
.prev
;
280 del_from_history_list(history
, entry
);
285 /* Load history file */
287 load_input_history(struct input_history
*history
, unsigned char *filename
)
289 unsigned char *history_file
= filename
;
290 unsigned char line
[MAX_STR_LEN
];
293 if (get_cmd_opt_bool("anonymous")) return 0;
295 history_file
= straconcat(elinks_home
, filename
,
296 (unsigned char *) NULL
);
297 if (!history_file
) return 0;
300 file
= fopen(history_file
, "rb");
301 if (elinks_home
) mem_free(history_file
);
306 while (fgets(line
, MAX_STR_LEN
, file
)) {
308 if (*line
) line
[strlen(line
) - 1] = 0;
309 add_to_input_history(history
, line
, 0);
319 /* Write history list to file. It returns a value different from 0 in case of
320 * failure, 0 on success. */
322 save_input_history(struct input_history
*history
, unsigned char *filename
)
324 struct input_history_entry
*entry
;
325 struct secure_save_info
*ssi
;
326 unsigned char *history_file
;
331 || get_cmd_opt_bool("anonymous"))
334 history_file
= straconcat(elinks_home
, filename
,
335 (unsigned char *) NULL
);
336 if (!history_file
) return -1;
338 ssi
= secure_open(history_file
);
339 mem_free(history_file
);
342 foreachback (entry
, history
->entries
) {
343 if (i
++ > MAX_INPUT_HISTORY_ENTRIES
) break;
344 secure_fputs(ssi
, entry
->data
);
345 secure_fputc(ssi
, '\n');
349 if (!ssi
->err
) history
->dirty
= 0;
351 return secure_close(ssi
);
355 dlg_set_history(struct widget_data
*widget_data
)
357 assert(widget_has_history(widget_data
));
358 assert(widget_data
->widget
->datalen
> 0);
360 if ((void *) widget_data
->info
.field
.cur_hist
!= &widget_data
->info
.field
.history
) {
361 unsigned char *s
= widget_data
->info
.field
.cur_hist
->data
;
363 widget_data
->info
.field
.cpos
= int_min(strlen(s
), widget_data
->widget
->datalen
- 1);
364 if (widget_data
->info
.field
.cpos
)
365 memcpy(widget_data
->cdata
, s
, widget_data
->info
.field
.cpos
);
367 widget_data
->info
.field
.cpos
= 0;
370 widget_data
->cdata
[widget_data
->info
.field
.cpos
] = 0;
371 widget_data
->info
.field
.vpos
= int_max(0, widget_data
->info
.field
.cpos
- widget_data
->box
.width
);