1 /**
2 * @file
3 * GUI ask the user to enter a string
4 *
5 * @authors
6 * Copyright (C) 1996-2000,2007,2011,2013 Michael R. Elkins <me@mutt.org>
7 * Copyright (C) 2000-2001 Edmund Grimley Evans <edmundo@rano.org>
8 *
9 * @copyright
10 * This program is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 2 of the License, or (at your option) any later
13 * version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /**
25 * @page neo_enter GUI ask the user to enter a string
26 *
27 * GUI ask the user to enter a string
28 */
29
30 #include "config.h"
31 #include <stddef.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <wchar.h>
35 #include <wctype.h>
36 #include "mutt/lib.h"
37 #include "config/lib.h"
38 #include "core/lib.h"
39 #include "alias/lib.h"
40 #include "gui/lib.h"
41 #include "mutt.h"
42 #include "history/lib.h"
43 #include "menu/lib.h"
44 #include "pattern/lib.h"
45 #include "browser.h"
46 #include "enter_state.h"
47 #include "init.h"
48 #include "keymap.h"
49 #include "mutt_globals.h"
50 #include "mutt_history.h"
51 #include "mutt_mailbox.h"
52 #include "muttlib.h"
53 #include "opcodes.h"
54 #include "protos.h"
55
56 /**
57 * enum EnterRedrawFlags - Redraw flags for mutt_enter_string_full()
58 */
59 enum EnterRedrawFlags
60 {
61 ENTER_REDRAW_NONE = 0, ///< Nothing to redraw
62 ENTER_REDRAW_INIT, ///< Go to end of line and redraw
63 ENTER_REDRAW_LINE, ///< Redraw entire line
64 };
65
66 /* combining mark / non-spacing character */
67 #define COMB_CHAR(wc) (IsWPrint(wc) && !wcwidth(wc))
68
69 /**
70 * my_addwch - Display one wide character on screen
71 * @param win Window
72 * @param wc Character to display
73 * @retval OK Success
74 * @retval ERR Failure
75 */
my_addwch(struct MuttWindow * win,wchar_t wc)76 static int my_addwch(struct MuttWindow *win, wchar_t wc)
77 {
78 int n = wcwidth(wc);
79 if (IsWPrint(wc) && (n > 0))
80 return mutt_addwch(win, wc);
81 if (!(wc & ~0x7f))
82 return mutt_window_printf(win, "^%c", ((int) wc + 0x40) & 0x7f);
83 if (!(wc & ~0xffff))
84 return mutt_window_printf(win, "\\u%04x", (int) wc);
85 return mutt_window_printf(win, "\\u%08x", (int) wc);
86 }
87
88 /**
89 * replace_part - Search and replace on a buffer
90 * @param state Current state of the input buffer
91 * @param from Starting point for the replacement
92 * @param buf Replacement string
93 */
replace_part(struct EnterState * state,size_t from,char * buf)94 static void replace_part(struct EnterState *state, size_t from, char *buf)
95 {
96 /* Save the suffix */
97 size_t savelen = state->lastchar - state->curpos;
98 wchar_t *savebuf = NULL;
99
100 if (savelen)
101 {
102 savebuf = mutt_mem_calloc(savelen, sizeof(wchar_t));
103 memcpy(savebuf, state->wbuf + state->curpos, savelen * sizeof(wchar_t));
104 }
105
106 /* Convert to wide characters */
107 state->curpos = mutt_mb_mbstowcs(&state->wbuf, &state->wbuflen, from, buf);
108
109 if (savelen)
110 {
111 /* Make space for suffix */
112 if (state->curpos + savelen > state->wbuflen)
113 {
114 state->wbuflen = state->curpos + savelen;
115 mutt_mem_realloc(&state->wbuf, state->wbuflen * sizeof(wchar_t));
116 }
117
118 /* Restore suffix */
119 memcpy(state->wbuf + state->curpos, savebuf, savelen * sizeof(wchar_t));
120 FREE(&savebuf);
121 }
122
123 state->lastchar = state->curpos + savelen;
124 }
125
126 /**
127 * mutt_enter_state_new - Create a new EnterState
128 * @retval ptr New EnterState
129 */
mutt_enter_state_new(void)130 struct EnterState *mutt_enter_state_new(void)
131 {
132 return mutt_mem_calloc(1, sizeof(struct EnterState));
133 }
134
135 /**
136 * mutt_enter_string_full - Ask the user for a string
137 * @param[in] buf Buffer to store the string
138 * @param[in] buflen Buffer length
139 * @param[in] col Initial cursor position
140 * @param[in] flags Flags, see #CompletionFlags
141 * @param[in] multiple Allow multiple matches
142 * @param[in] m Mailbox
143 * @param[out] files List of files selected
144 * @param[out] numfiles Number of files selected
145 * @param[out] state Current state (if function is called repeatedly)
146 * @retval 1 Redraw the screen and call the function again
147 * @retval 0 Selection made
148 * @retval -1 Aborted
149 */
mutt_enter_string_full(char * buf,size_t buflen,int col,CompletionFlags flags,bool multiple,struct Mailbox * m,char *** files,int * numfiles,struct EnterState * state)150 int mutt_enter_string_full(char *buf, size_t buflen, int col, CompletionFlags flags,
151 bool multiple, struct Mailbox *m, char ***files,
152 int *numfiles, struct EnterState *state)
153 {
154 struct MuttWindow *win = msgwin_get_window();
155 if (!win)
156 return -1;
157
158 int width = win->state.cols - col - 1;
159 enum EnterRedrawFlags redraw = ENTER_REDRAW_NONE;
160 bool pass = (flags & MUTT_PASS);
161 bool first = true;
162 int ch;
163 wchar_t *tempbuf = NULL;
164 size_t templen = 0;
165 enum HistoryClass hclass;
166 int rc = 0;
167 mbstate_t mbstate;
168 memset(&mbstate, 0, sizeof(mbstate));
169
170 if (state->wbuf)
171 {
172 /* Coming back after return 1 */
173 redraw = ENTER_REDRAW_LINE;
174 first = false;
175 }
176 else
177 {
178 /* Initialise wbuf from buf */
179 state->wbuflen = 0;
180 state->lastchar = mutt_mb_mbstowcs(&state->wbuf, &state->wbuflen, 0, buf);
181 redraw = ENTER_REDRAW_INIT;
182 }
183
184 if (flags & MUTT_FILE)
185 hclass = HC_FILE;
186 else if (flags & MUTT_EFILE)
187 hclass = HC_MBOX;
188 else if (flags & MUTT_CMD)
189 hclass = HC_CMD;
190 else if (flags & MUTT_ALIAS)
191 hclass = HC_ALIAS;
192 else if (flags & MUTT_COMMAND)
193 hclass = HC_COMMAND;
194 else if (flags & MUTT_PATTERN)
195 hclass = HC_PATTERN;
196 else
197 hclass = HC_OTHER;
198
199 while (true)
200 {
201 if (!pass)
202 {
203 if (redraw == ENTER_REDRAW_INIT)
204 {
205 /* Go to end of line */
206 state->curpos = state->lastchar;
207 state->begin = mutt_mb_width_ceiling(
208 state->wbuf, state->lastchar,
209 mutt_mb_wcswidth(state->wbuf, state->lastchar) - width + 1);
210 }
211 if ((state->curpos < state->begin) ||
212 (mutt_mb_wcswidth(state->wbuf + state->begin, state->curpos - state->begin) >= width))
213 {
214 state->begin = mutt_mb_width_ceiling(
215 state->wbuf, state->lastchar,
216 mutt_mb_wcswidth(state->wbuf, state->curpos) - (width / 2));
217 }
218 mutt_window_move(win, col, 0);
219 int w = 0;
220 for (size_t i = state->begin; i < state->lastchar; i++)
221 {
222 w += mutt_mb_wcwidth(state->wbuf[i]);
223 if (w > width)
224 break;
225 my_addwch(win, state->wbuf[i]);
226 }
227 mutt_window_clrtoeol(win);
228 mutt_window_move(win,
229 col + mutt_mb_wcswidth(state->wbuf + state->begin,
230 state->curpos - state->begin),
231 0);
232 }
233 mutt_refresh();
234
235 ch = km_dokey(MENU_EDITOR);
236 if (ch < 0)
237 {
238 rc = (SigWinch && (ch == -2)) ? 1 : -1;
239 goto bye;
240 }
241
242 if (ch != OP_NULL)
243 {
244 first = false;
245 if ((ch != OP_EDITOR_COMPLETE) && (ch != OP_EDITOR_COMPLETE_QUERY))
246 state->tabs = 0;
247 redraw = ENTER_REDRAW_LINE;
248 switch (ch)
249 {
250 case OP_EDITOR_HISTORY_UP:
251 state->curpos = state->lastchar;
252 if (mutt_hist_at_scratch(hclass))
253 {
254 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
255 mutt_hist_save_scratch(hclass, buf);
256 }
257 replace_part(state, 0, mutt_hist_prev(hclass));
258 redraw = ENTER_REDRAW_INIT;
259 break;
260
261 case OP_EDITOR_HISTORY_DOWN:
262 state->curpos = state->lastchar;
263 if (mutt_hist_at_scratch(hclass))
264 {
265 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
266 mutt_hist_save_scratch(hclass, buf);
267 }
268 replace_part(state, 0, mutt_hist_next(hclass));
269 redraw = ENTER_REDRAW_INIT;
270 break;
271
272 case OP_EDITOR_HISTORY_SEARCH:
273 state->curpos = state->lastchar;
274 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
275 mutt_hist_complete(buf, buflen, hclass);
276 replace_part(state, 0, buf);
277 rc = 1;
278 goto bye;
279 break;
280
281 case OP_EDITOR_BACKSPACE:
282 if (state->curpos == 0)
283 {
284 // Pressing backspace when no text is in the command prompt should exit the prompt
285 const bool c_abort_backspace =
286 cs_subset_bool(NeoMutt->sub, "abort_backspace");
287 if (c_abort_backspace && (state->lastchar == 0))
288 goto bye;
289 // Pressing backspace with text in the command prompt should just beep
290 mutt_beep(false);
291 }
292 else
293 {
294 size_t i = state->curpos;
295 while ((i > 0) && COMB_CHAR(state->wbuf[i - 1]))
296 i--;
297 if (i > 0)
298 i--;
299 memmove(state->wbuf + i, state->wbuf + state->curpos,
300 (state->lastchar - state->curpos) * sizeof(wchar_t));
301 state->lastchar -= state->curpos - i;
302 state->curpos = i;
303 }
304 break;
305
306 case OP_EDITOR_BOL:
307 state->curpos = 0;
308 break;
309
310 case OP_EDITOR_EOL:
311 redraw = ENTER_REDRAW_INIT;
312 break;
313
314 case OP_EDITOR_KILL_LINE:
315 state->curpos = 0;
316 state->lastchar = 0;
317 break;
318
319 case OP_EDITOR_KILL_EOL:
320 state->lastchar = state->curpos;
321 break;
322
323 case OP_EDITOR_BACKWARD_CHAR:
324 if (state->curpos == 0)
325 mutt_beep(false);
326 else
327 {
328 while (state->curpos && COMB_CHAR(state->wbuf[state->curpos - 1]))
329 state->curpos--;
330 if (state->curpos)
331 state->curpos--;
332 }
333 break;
334
335 case OP_EDITOR_FORWARD_CHAR:
336 if (state->curpos == state->lastchar)
337 mutt_beep(false);
338 else
339 {
340 state->curpos++;
341 while ((state->curpos < state->lastchar) &&
342 COMB_CHAR(state->wbuf[state->curpos]))
343 {
344 state->curpos++;
345 }
346 }
347 break;
348
349 case OP_EDITOR_BACKWARD_WORD:
350 if (state->curpos == 0)
351 mutt_beep(false);
352 else
353 {
354 while (state->curpos && iswspace(state->wbuf[state->curpos - 1]))
355 state->curpos--;
356 while (state->curpos && !iswspace(state->wbuf[state->curpos - 1]))
357 state->curpos--;
358 }
359 break;
360
361 case OP_EDITOR_FORWARD_WORD:
362 if (state->curpos == state->lastchar)
363 mutt_beep(false);
364 else
365 {
366 while ((state->curpos < state->lastchar) &&
367 iswspace(state->wbuf[state->curpos]))
368 {
369 state->curpos++;
370 }
371 while ((state->curpos < state->lastchar) &&
372 !iswspace(state->wbuf[state->curpos]))
373 {
374 state->curpos++;
375 }
376 }
377 break;
378
379 case OP_EDITOR_CAPITALIZE_WORD:
380 case OP_EDITOR_UPCASE_WORD:
381 case OP_EDITOR_DOWNCASE_WORD:
382 if (state->curpos == state->lastchar)
383 {
384 mutt_beep(false);
385 break;
386 }
387 while (state->curpos && !iswspace(state->wbuf[state->curpos]))
388 state->curpos--;
389 while ((state->curpos < state->lastchar) && iswspace(state->wbuf[state->curpos]))
390 state->curpos++;
391 while ((state->curpos < state->lastchar) &&
392 !iswspace(state->wbuf[state->curpos]))
393 {
394 if (ch == OP_EDITOR_DOWNCASE_WORD)
395 state->wbuf[state->curpos] = towlower(state->wbuf[state->curpos]);
396 else
397 {
398 state->wbuf[state->curpos] = towupper(state->wbuf[state->curpos]);
399 if (ch == OP_EDITOR_CAPITALIZE_WORD)
400 ch = OP_EDITOR_DOWNCASE_WORD;
401 }
402 state->curpos++;
403 }
404 break;
405
406 case OP_EDITOR_DELETE_CHAR:
407 if (state->curpos == state->lastchar)
408 mutt_beep(false);
409 else
410 {
411 size_t i = state->curpos;
412 while ((i < state->lastchar) && COMB_CHAR(state->wbuf[i]))
413 i++;
414 if (i < state->lastchar)
415 i++;
416 while ((i < state->lastchar) && COMB_CHAR(state->wbuf[i]))
417 i++;
418 memmove(state->wbuf + state->curpos, state->wbuf + i,
419 (state->lastchar - i) * sizeof(wchar_t));
420 state->lastchar -= i - state->curpos;
421 }
422 break;
423
424 case OP_EDITOR_KILL_WORD:
425 /* delete to beginning of word */
426 if (state->curpos != 0)
427 {
428 size_t i = state->curpos;
429 while (i && iswspace(state->wbuf[i - 1]))
430 i--;
431 if (i > 0)
432 {
433 if (iswalnum(state->wbuf[i - 1]))
434 {
435 for (--i; (i > 0) && iswalnum(state->wbuf[i - 1]); i--)
436 ; // do nothing
437 }
438 else
439 i--;
440 }
441 memmove(state->wbuf + i, state->wbuf + state->curpos,
442 (state->lastchar - state->curpos) * sizeof(wchar_t));
443 state->lastchar += i - state->curpos;
444 state->curpos = i;
445 }
446 break;
447
448 case OP_EDITOR_KILL_EOW:
449 {
450 /* delete to end of word */
451
452 /* first skip over whitespace */
453 size_t i;
454 for (i = state->curpos; (i < state->lastchar) && iswspace(state->wbuf[i]); i++)
455 ; // do nothing
456
457 /* if there are any characters left.. */
458 if (i < state->lastchar)
459 {
460 /* if the current character is alphanumeric.. */
461 if (iswalnum(state->wbuf[i]))
462 {
463 /* skip over the rest of the word consistent of only alphanumerics */
464 for (; (i < state->lastchar) && iswalnum(state->wbuf[i]); i++)
465 ; // do nothing
466 }
467 else
468 {
469 /* skip over one non-alphanumeric character */
470 i++;
471 }
472 }
473
474 memmove(state->wbuf + state->curpos, state->wbuf + i,
475 (state->lastchar - i) * sizeof(wchar_t));
476 state->lastchar += state->curpos - i;
477 break;
478 }
479
480 case OP_EDITOR_MAILBOX_CYCLE:
481 if (flags & MUTT_EFILE)
482 {
483 first = true; /* clear input if user types a real key later */
484 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
485
486 struct Buffer *pool = mutt_buffer_pool_get();
487 mutt_buffer_addstr(pool, buf);
488 mutt_mailbox_next(m, pool);
489 mutt_str_copy(buf, mutt_buffer_string(pool), buflen);
490 mutt_buffer_pool_release(&pool);
491
492 state->curpos = state->lastchar =
493 mutt_mb_mbstowcs(&state->wbuf, &state->wbuflen, 0, buf);
494 break;
495 }
496 else if (!(flags & MUTT_FILE))
497 {
498 goto self_insert;
499 }
500 /* fallthrough */
501
502 case OP_EDITOR_COMPLETE:
503 case OP_EDITOR_COMPLETE_QUERY:
504 state->tabs++;
505 if (flags & MUTT_CMD)
506 {
507 size_t i;
508 for (i = state->curpos;
509 (i > 0) && !mutt_mb_is_shell_char(state->wbuf[i - 1]); i--)
510 {
511 }
512 mutt_mb_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
513 if (tempbuf && (templen == (state->lastchar - i)) &&
514 (memcmp(tempbuf, state->wbuf + i, (state->lastchar - i) * sizeof(wchar_t)) == 0))
515 {
516 mutt_select_file(buf, buflen, (flags & MUTT_EFILE) ? MUTT_SEL_FOLDER : MUTT_SEL_NO_FLAGS,
517 m, NULL, NULL);
518 if (buf[0] != '\0')
519 replace_part(state, i, buf);
520 rc = 1;
521 goto bye;
522 }
523 if (mutt_complete(buf, buflen) == 0)
524 {
525 templen = state->lastchar - i;
526 mutt_mem_realloc(&tempbuf, templen * sizeof(wchar_t));
527 }
528 else
529 mutt_beep(false);
530
531 replace_part(state, i, buf);
532 }
533 else if ((flags & MUTT_ALIAS) && (ch == OP_EDITOR_COMPLETE))
534 {
535 /* invoke the alias-menu to get more addresses */
536 size_t i;
537 for (i = state->curpos;
538 (i > 0) && (state->wbuf[i - 1] != ',') && (state->wbuf[i - 1] != ':'); i--)
539 {
540 }
541 for (; (i < state->lastchar) && (state->wbuf[i] == ' '); i++)
542 ; // do nothing
543
544 mutt_mb_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
545 int rc2 = alias_complete(buf, buflen, NeoMutt->sub);
546 replace_part(state, i, buf);
547 if (rc2 != 1)
548 {
549 rc = 1;
550 goto bye;
551 }
552 break;
553 }
554 else if ((flags & MUTT_LABEL) && (ch == OP_EDITOR_COMPLETE))
555 {
556 size_t i;
557 for (i = state->curpos;
558 (i > 0) && (state->wbuf[i - 1] != ',') && (state->wbuf[i - 1] != ':'); i--)
559 {
560 }
561 for (; (i < state->lastchar) && (state->wbuf[i] == ' '); i++)
562 ; // do nothing
563
564 mutt_mb_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
565 int rc2 = mutt_label_complete(buf, buflen, state->tabs);
566 replace_part(state, i, buf);
567 if (rc2 != 1)
568 {
569 rc = 1;
570 goto bye;
571 }
572 break;
573 }
574 else if ((flags & MUTT_PATTERN) && (ch == OP_EDITOR_COMPLETE))
575 {
576 size_t i = state->curpos;
577 if (i && (state->wbuf[i - 1] == '~'))
578 {
579 if (dlg_select_pattern(buf, buflen))
580 replace_part(state, i - 1, buf);
581 rc = 1;
582 goto bye;
583 }
584 for (; (i > 0) && (state->wbuf[i - 1] != '~'); i--)
585 ; // do nothing
586
587 if ((i > 0) && (i < state->curpos) && (state->wbuf[i - 1] == '~') &&
588 (state->wbuf[i] == 'y'))
589 {
590 i++;
591 mutt_mb_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
592 int rc2 = mutt_label_complete(buf, buflen, state->tabs);
593 replace_part(state, i, buf);
594 if (rc2 != 1)
595 {
596 rc = 1;
597 goto bye;
598 }
599 }
600 else
601 goto self_insert;
602 break;
603 }
604 else if ((flags & MUTT_ALIAS) && (ch == OP_EDITOR_COMPLETE_QUERY))
605 {
606 size_t i = state->curpos;
607 if (i != 0)
608 {
609 for (; (i > 0) && (state->wbuf[i - 1] != ','); i--)
610 ; // do nothing
611
612 for (; (i < state->curpos) && (state->wbuf[i] == ' '); i++)
613 ; // do nothing
614 }
615
616 mutt_mb_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
617 query_complete(buf, buflen, NeoMutt->sub);
618 replace_part(state, i, buf);
619
620 rc = 1;
621 goto bye;
622 }
623 else if (flags & MUTT_COMMAND)
624 {
625 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
626 size_t i = strlen(buf);
627 if ((i != 0) && (buf[i - 1] == '=') &&
628 (mutt_var_value_complete(buf, buflen, i) != 0))
629 {
630 state->tabs = 0;
631 }
632 else if (mutt_command_complete(buf, buflen, i, state->tabs) == 0)
633 mutt_beep(false);
634 replace_part(state, 0, buf);
635 }
636 else if (flags & (MUTT_FILE | MUTT_EFILE))
637 {
638 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
639
640 /* see if the path has changed from the last time */
641 if ((!tempbuf && !state->lastchar) ||
642 (tempbuf && (templen == state->lastchar) &&
643 (memcmp(tempbuf, state->wbuf, state->lastchar * sizeof(wchar_t)) == 0)))
644 {
645 mutt_select_file(buf, buflen,
646 ((flags & MUTT_EFILE) ? MUTT_SEL_FOLDER : MUTT_SEL_NO_FLAGS) |
647 (multiple ? MUTT_SEL_MULTI : MUTT_SEL_NO_FLAGS),
648 m, files, numfiles);
649 if (buf[0] != '\0')
650 {
651 mutt_pretty_mailbox(buf, buflen);
652 if (!pass)
653 mutt_hist_add(hclass, buf, true);
654 rc = 0;
655 goto bye;
656 }
657
658 /* file selection cancelled */
659 rc = 1;
660 goto bye;
661 }
662
663 if (mutt_complete(buf, buflen) == 0)
664 {
665 templen = state->lastchar;
666 mutt_mem_realloc(&tempbuf, templen * sizeof(wchar_t));
667 memcpy(tempbuf, state->wbuf, templen * sizeof(wchar_t));
668 }
669 else
670 mutt_beep(false); /* let the user know that nothing matched */
671 replace_part(state, 0, buf);
672 }
673 #ifdef USE_NOTMUCH
674 else if (flags & MUTT_NM_QUERY)
675 {
676 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
677 size_t len = strlen(buf);
678 if (!mutt_nm_query_complete(buf, buflen, len, state->tabs))
679 mutt_beep(false);
680
681 replace_part(state, 0, buf);
682 }
683 else if (flags & MUTT_NM_TAG)
684 {
685 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos);
686 if (!mutt_nm_tag_complete(buf, buflen, state->tabs))
687 mutt_beep(false);
688
689 replace_part(state, 0, buf);
690 }
691 #endif
692 else
693 goto self_insert;
694 break;
695
696 case OP_EDITOR_QUOTE_CHAR:
697 {
698 struct KeyEvent event;
699 do
700 {
701 event = mutt_getch();
702 } while (event.ch == -2); // Timeout
703 if (event.ch >= 0)
704 {
705 LastKey = event.ch;
706 goto self_insert;
707 }
708 break;
709 }
710
711 case OP_EDITOR_TRANSPOSE_CHARS:
712 if (state->lastchar < 2)
713 mutt_beep(false);
714 else
715 {
716 wchar_t t;
717
718 if (state->curpos == 0)
719 state->curpos = 2;
720 else if (state->curpos < state->lastchar)
721 state->curpos++;
722
723 t = state->wbuf[state->curpos - 2];
724 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
725 state->wbuf[state->curpos - 1] = t;
726 }
727 break;
728
729 default:
730 mutt_beep(false);
731 }
732 }
733 else
734 {
735 self_insert:
736 state->tabs = 0;
737 wchar_t wc;
738 /* use the raw keypress */
739 ch = LastKey;
740
741 /* quietly ignore all other function keys */
742 if (ch & ~0xff)
743 continue;
744
745 /* gather the octets into a wide character */
746 {
747 char c = ch;
748 size_t k = mbrtowc(&wc, &c, 1, &mbstate);
749 if (k == (size_t) (-2))
750 continue;
751 else if ((k != 0) && (k != 1))
752 {
753 memset(&mbstate, 0, sizeof(mbstate));
754 continue;
755 }
756 }
757
758 if (first && (flags & MUTT_CLEAR))
759 {
760 first = false;
761 if (IsWPrint(wc)) /* why? */
762 {
763 state->curpos = 0;
764 state->lastchar = 0;
765 }
766 }
767
768 if ((wc == '\r') || (wc == '\n'))
769 {
770 /* Convert from wide characters */
771 mutt_mb_wcstombs(buf, buflen, state->wbuf, state->lastchar);
772 if (!pass)
773 mutt_hist_add(hclass, buf, true);
774
775 if (multiple)
776 {
777 char **tfiles = NULL;
778 *numfiles = 1;
779 tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
780 mutt_expand_path(buf, buflen);
781 tfiles[0] = mutt_str_dup(buf);
782 *files = tfiles;
783 }
784 rc = 0;
785 goto bye;
786 }
787 else if (wc && ((wc < ' ') || IsWPrint(wc))) /* why? */
788 {
789 if (state->lastchar >= state->wbuflen)
790 {
791 state->wbuflen = state->lastchar + 20;
792 mutt_mem_realloc(&state->wbuf, state->wbuflen * sizeof(wchar_t));
793 }
794 memmove(state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
795 (state->lastchar - state->curpos) * sizeof(wchar_t));
796 state->wbuf[state->curpos++] = wc;
797 state->lastchar++;
798 }
799 else
800 {
801 mutt_flushinp();
802 mutt_beep(false);
803 }
804 }
805 }
806
807 bye:
808
809 mutt_hist_reset_state(hclass);
810 FREE(&tempbuf);
811 return rc;
812 }
813
814 /**
815 * mutt_enter_state_free - Free an EnterState
816 * @param[out] ptr EnterState to free
817 */
mutt_enter_state_free(struct EnterState ** ptr)818 void mutt_enter_state_free(struct EnterState **ptr)
819 {
820 if (!ptr || !*ptr)
821 return;
822
823 struct EnterState *es = *ptr;
824
825 FREE(&es->wbuf);
826 FREE(ptr);
827 }
828