1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
/**************************************************************************
* prompt.c -- This file is part of GNU nano. *
* *
* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
* 2008, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc. *
* Copyright (C) 2016 Benno Schulenberg *
* *
* GNU nano is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published *
* by the Free Software Foundation, either version 3 of the License, *
* or (at your option) any later version. *
* *
* GNU nano is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty *
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see http://www.gnu.org/licenses/. *
* *
**************************************************************************/
#include "proto.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
static char *prompt = NULL;
/* The prompt string used for statusbar questions. */
static size_t statusbar_x = HIGHEST_POSITIVE;
/* The cursor position in answer. */
/* Read in a keystroke, interpret it if it is a shortcut or toggle, and
* return it. Set ran_func to TRUE if we ran a function associated with
* a shortcut key, and set finished to TRUE if we're done after running
* or trying to run a function associated with a shortcut key. */
int do_statusbar_input(bool *ran_func, bool *finished)
{
int input;
/* The character we read in. */
static int *kbinput = NULL;
/* The input buffer. */
static size_t kbinput_len = 0;
/* The length of the input buffer. */
const sc *s;
bool have_shortcut = FALSE;
const subnfunc *f;
*ran_func = FALSE;
*finished = FALSE;
/* Read in a character. */
input = get_kbinput(bottomwin);
#ifndef NANO_TINY
if (input == KEY_WINCH)
return KEY_WINCH;
#endif
#ifndef DISABLE_MOUSE
/* If we got a mouse click and it was on a shortcut, read in the
* shortcut character. */
if (input == KEY_MOUSE) {
if (do_statusbar_mouse() == 1)
input = get_kbinput(bottomwin);
else
return ERR;
}
#endif
/* Check for a shortcut in the current list. */
s = get_shortcut(&input);
/* If we got a shortcut from the current list, or a "universal"
* statusbar prompt shortcut, set have_shortcut to TRUE. */
have_shortcut = (s != NULL);
/* If we got a non-high-bit control key, a meta key sequence, or a
* function key, and it's not a shortcut or toggle, throw it out. */
if (!have_shortcut) {
if (is_ascii_cntrl_char(input) || meta_key || !is_byte(input)) {
beep();
input = ERR;
}
}
/* If the keystroke isn't a shortcut nor a toggle, it's a normal text
* character: add the it to the input buffer, when allowed. */
if (input != ERR && !have_shortcut) {
/* Only accept input when not in restricted mode, or when not at
* the "Write File" prompt, or when there is no filename yet. */
if (!ISSET(RESTRICTED) || currmenu != MWRITEFILE ||
openfile->filename[0] == '\0') {
kbinput_len++;
kbinput = (int *)nrealloc(kbinput, kbinput_len * sizeof(int));
kbinput[kbinput_len - 1] = input;
}
}
/* If we got a shortcut, or if there aren't any other keystrokes waiting
* after the one we read in, we need to insert all the characters in the
* input buffer (if not empty) into the answer. */
if ((have_shortcut || get_key_buffer_len() == 0) && kbinput != NULL) {
/* Inject all characters in the input buffer at once, filtering out
* control characters. */
do_statusbar_output(kbinput, kbinput_len, TRUE, NULL);
/* Empty the input buffer. */
kbinput_len = 0;
free(kbinput);
kbinput = NULL;
}
if (have_shortcut) {
if (s->scfunc == do_tab || s->scfunc == do_enter)
;
else if (s->scfunc == do_left)
do_statusbar_left();
else if (s->scfunc == do_right)
do_statusbar_right();
#ifndef NANO_TINY
else if (s->scfunc == do_prev_word_void)
do_statusbar_prev_word();
else if (s->scfunc == do_next_word_void)
do_statusbar_next_word();
#endif
else if (s->scfunc == do_home)
do_statusbar_home();
else if (s->scfunc == do_end)
do_statusbar_end();
/* When in restricted mode at the "Write File" prompt and the
* filename isn't blank, disallow any input and deletion. */
else if (ISSET(RESTRICTED) && currmenu == MWRITEFILE &&
openfile->filename[0] != '\0' &&
(s->scfunc == do_verbatim_input ||
s->scfunc == do_cut_text_void ||
s->scfunc == do_delete ||
s->scfunc == do_backspace))
;
else if (s->scfunc == do_verbatim_input) {
bool got_newline = FALSE;
/* Whether we got a verbatim ^J. */
do_statusbar_verbatim_input(&got_newline);
/* If we got a verbatim ^J, remove it from the input buffer,
* fake a press of Enter, and indicate that we're done. */
if (got_newline) {
get_input(NULL, 1);
input = sc_seq_or(do_enter, 0);
*finished = TRUE;
}
} else if (s->scfunc == do_cut_text_void)
do_statusbar_cut_text();
else if (s->scfunc == do_delete)
do_statusbar_delete();
else if (s->scfunc == do_backspace)
do_statusbar_backspace();
else {
/* Handle any other shortcut in the current menu, setting
* ran_func to TRUE if we try to run their associated functions,
* and setting finished to TRUE to indicatethat we're done after
* running or trying to run their associated functions. */
f = sctofunc(s);
if (s->scfunc != NULL) {
*ran_func = TRUE;
if (f && (!ISSET(VIEW_MODE) || f->viewok) &&
f->scfunc != do_gotolinecolumn_void)
f->scfunc();
}
*finished = TRUE;
}
}
return input;
}
#ifndef DISABLE_MOUSE
/* Handle a mouse click on the statusbar prompt or the shortcut list. */
int do_statusbar_mouse(void)
{
int mouse_x, mouse_y;
int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
/* We can click on the statusbar window text to move the cursor. */
if (retval == 0 && wmouse_trafo(bottomwin, &mouse_y, &mouse_x, FALSE)) {
size_t start_col;
start_col = strlenpt(prompt) + 2;
/* Move to where the click occurred. */
if (mouse_x >= start_col && mouse_y == 0) {
statusbar_x = actual_x(answer,
get_statusbar_page_start(start_col, start_col +
statusbar_xplustabs()) + mouse_x - start_col);
update_the_statusbar();
}
}
return retval;
}
#endif
/* The user typed input_len multibyte characters. Add them to the
* statusbar prompt, setting got_newline to TRUE if we got a verbatim ^J,
* and filtering out ASCII control characters if filtering is TRUE. */
void do_statusbar_output(int *the_input, size_t input_len,
bool filtering, bool *got_newline)
{
char *output = charalloc(input_len + 1);
char *char_buf = charalloc(mb_cur_max());
int i, char_len;
/* Copy the typed stuff so it can be treated. */
for (i = 0; i < input_len; i++)
output[i] = (char)the_input[i];
output[i] = '\0';
i = 0;
while (i < input_len) {
/* When not filtering, convert nulls and stop at a newline. */
if (!filtering) {
if (output[i] == '\0')
output[i] = '\n';
else if (output[i] == '\n') {
/* Put back the rest of the characters for reparsing,
* indicate that we got a ^J and get out. */
unparse_kbinput(output + i, input_len - i);
*got_newline = TRUE;
return;
}
}
/* Interpret the next multibyte character. */
char_len = parse_mbchar(output + i, char_buf, NULL);
i += char_len;
/* When filtering, skip any ASCII control character. */
if (filtering && is_ascii_cntrl_char(*(output + i - char_len)))
continue;
/* Insert the typed character into the existing answer string. */
answer = charealloc(answer, strlen(answer) + char_len + 1);
charmove(answer + statusbar_x + char_len, answer + statusbar_x,
strlen(answer) - statusbar_x + 1);
strncpy(answer + statusbar_x, char_buf, char_len);
statusbar_x += char_len;
}
free(char_buf);
free(output);
update_the_statusbar();
}
/* Move to the beginning of the answer. */
void do_statusbar_home(void)
{
statusbar_x = 0;
update_the_statusbar();
}
/* Move to the end of the answer. */
void do_statusbar_end(void)
{
statusbar_x = strlen(answer);
update_the_statusbar();
}
/* Move left one character. */
void do_statusbar_left(void)
{
if (statusbar_x > 0) {
statusbar_x = move_mbleft(answer, statusbar_x);
update_the_statusbar();
}
}
/* Move right one character. */
void do_statusbar_right(void)
{
if (answer[statusbar_x] != '\0') {
statusbar_x = move_mbright(answer, statusbar_x);
update_the_statusbar();
}
}
/* Backspace over one character. */
void do_statusbar_backspace(void)
{
if (statusbar_x > 0) {
statusbar_x = move_mbleft(answer, statusbar_x);
do_statusbar_delete();
}
}
/* Delete one character. */
void do_statusbar_delete(void)
{
if (answer[statusbar_x] != '\0') {
int char_len = parse_mbchar(answer + statusbar_x, NULL, NULL);
charmove(answer + statusbar_x, answer + statusbar_x + char_len,
strlen(answer) - statusbar_x - char_len + 1);
update_the_statusbar();
}
}
/* Zap some or all text from the answer. */
void do_statusbar_cut_text(void)
{
if (!ISSET(CUT_TO_END))
statusbar_x = 0;
null_at(&answer, statusbar_x);
update_the_statusbar();
}
#ifndef NANO_TINY
/* Move to the next word in the answer. */
void do_statusbar_next_word(void)
{
bool seen_space = !is_word_mbchar(answer + statusbar_x, FALSE);
/* Move forward until we reach the start of a word. */
while (answer[statusbar_x] != '\0') {
statusbar_x = move_mbright(answer, statusbar_x);
/* If this is not a word character, then it's a separator; else
* if we've already seen a separator, then it's a word start. */
if (!is_word_mbchar(answer + statusbar_x, FALSE))
seen_space = TRUE;
else if (seen_space)
break;
}
update_the_statusbar();
}
/* Move to the previous word in the answer. */
void do_statusbar_prev_word(void)
{
bool seen_a_word = FALSE, step_forward = FALSE;
/* Move backward until we pass over the start of a word. */
while (statusbar_x != 0) {
statusbar_x = move_mbleft(answer, statusbar_x);
if (is_word_mbchar(answer + statusbar_x, FALSE))
seen_a_word = TRUE;
else if (seen_a_word) {
/* This is space now: we've overshot the start of the word. */
step_forward = TRUE;
break;
}
}
if (step_forward)
/* Move one character forward again to sit on the start of the word. */
statusbar_x = move_mbright(answer, statusbar_x);
update_the_statusbar();
}
#endif /* !NANO_TINY */
/* Get verbatim input, setting got_newline to TRUE if we get a ^J as
* part of the verbatim input. */
void do_statusbar_verbatim_input(bool *got_newline)
{
int *kbinput;
size_t kbinput_len;
/* Read in all the verbatim characters. */
kbinput = get_verbatim_kbinput(bottomwin, &kbinput_len);
/* Display all the verbatim characters at once, not filtering out
* control characters. */
do_statusbar_output(kbinput, kbinput_len, FALSE, got_newline);
}
/* Return the zero-based column position of the cursor in the answer. */
size_t statusbar_xplustabs(void)
{
return strnlenpt(answer, statusbar_x);
}
/* Return the column number of the first character of the answer that is
* displayed in the statusbar when the cursor is at the given column,
* with the available room for the answer starting at base. Note that
* (0 <= column - get_statusbar_page_start(column) < COLS). */
size_t get_statusbar_page_start(size_t base, size_t column)
{
if (column == base || column < COLS - 1)
return 0;
else if (COLS > base + 2)
return column - base - 1 - (column - base - 1) % (COLS - base - 2);
else
return column - 2;
}
/* Reinitialize the cursor position in the answer. */
void reinit_statusbar_x(void)
{
statusbar_x = HIGHEST_POSITIVE;
}
/* Put the cursor in the answer at statusbar_x. */
void reset_statusbar_cursor(void)
{
size_t start_col = strlenpt(prompt) + 2;
size_t xpt = statusbar_xplustabs();
/* Work around a cursor-misplacement bug in VTEs. */
wmove(bottomwin, 0, 0);
wnoutrefresh(bottomwin);
doupdate();
wmove(bottomwin, 0, start_col + xpt -
get_statusbar_page_start(start_col, start_col + xpt));
wnoutrefresh(bottomwin);
}
/* Repaint the statusbar. */
void update_the_statusbar(void)
{
size_t base, the_page, end_page;
char *expanded;
base = strlenpt(prompt) + 2;
the_page = get_statusbar_page_start(base, base + strnlenpt(answer, statusbar_x));
end_page = get_statusbar_page_start(base, base + strlenpt(answer) - 1);
wattron(bottomwin, interface_color_pair[TITLE_BAR]);
blank_statusbar();
mvwaddstr(bottomwin, 0, 0, prompt);
waddch(bottomwin, ':');
waddch(bottomwin, (the_page == 0) ? ' ' : '<');
expanded = display_string(answer, the_page, COLS - base - 1, FALSE);
waddstr(bottomwin, expanded);
free(expanded);
waddch(bottomwin, (the_page >= end_page) ? ' ' : '>');
wattroff(bottomwin, interface_color_pair[TITLE_BAR]);
reset_statusbar_cursor();
}
/* Get a string of input at the statusbar prompt. */
functionptrtype acquire_an_answer(int *actual, bool allow_tabs,
#ifndef DISABLE_TABCOMP
bool allow_files, bool *listed,
#endif
#ifndef DISABLE_HISTORIES
filestruct **history_list,
#endif
void (*refresh_func)(void))
{
int kbinput = ERR;
bool ran_func, finished;
functionptrtype func;
#ifndef DISABLE_TABCOMP
bool tabbed = FALSE;
/* Whether we've pressed Tab. */
#endif
#ifndef DISABLE_HISTORIES
char *history = NULL;
/* The current history string. */
char *magichistory = NULL;
/* The temporary string typed at the bottom of the history, if
* any. */
#ifndef DISABLE_TABCOMP
int last_kbinput = ERR;
/* The key we pressed before the current key. */
size_t complete_len = 0;
/* The length of the original string that we're trying to
* tab complete, if any. */
#endif
#endif /* !DISABLE_HISTORIES */
if (statusbar_x > strlen(answer))
statusbar_x = strlen(answer);
#ifdef DEBUG
fprintf(stderr, "acquiring: answer = \"%s\", statusbar_x = %lu\n", answer, (unsigned long) statusbar_x);
#endif
update_the_statusbar();
/* Refresh edit window and statusbar before getting input. */
wnoutrefresh(edit);
wnoutrefresh(bottomwin);
while (TRUE) {
/* Ensure the cursor is shown when waiting for input. */
curs_set(1);
kbinput = do_statusbar_input(&ran_func, &finished);
#ifndef NANO_TINY
/* If the window size changed, go reformat the prompt string. */
if (kbinput == KEY_WINCH) {
refresh_func();
*actual = KEY_WINCH;
#ifndef DISABLE_HISTORIES
free(magichistory);
#endif
return NULL;
}
#endif /* !NANO_TINY */
func = func_from_key(&kbinput);
if (func == do_cancel || func == do_enter)
break;
#ifndef DISABLE_TABCOMP
if (func != do_tab)
tabbed = FALSE;
if (func == do_tab) {
#ifndef DISABLE_HISTORIES
if (history_list != NULL) {
if (last_kbinput != sc_seq_or(do_tab, TAB_CODE))
complete_len = strlen(answer);
if (complete_len > 0) {
answer = get_history_completion(history_list,
answer, complete_len);
statusbar_x = strlen(answer);
}
} else
#endif
if (allow_tabs)
answer = input_tab(answer, allow_files, &statusbar_x,
&tabbed, refresh_func, listed);
} else
#endif /* !DISABLE_TABCOMP */
#ifndef DISABLE_HISTORIES
if (func == get_history_older_void) {
if (history_list != NULL) {
/* If we're scrolling up at the bottom of the history list
* and answer isn't blank, save answer in magichistory. */
if ((*history_list)->next == NULL && *answer != '\0')
magichistory = mallocstrcpy(magichistory, answer);
/* Get the older search from the history list and save it in
* answer. If there is no older search, don't do anything. */
if ((history = get_history_older(history_list)) != NULL) {
answer = mallocstrcpy(answer, history);
statusbar_x = strlen(answer);
}
/* This key has a shortcut-list entry when it's used to
* move to an older search, which means that finished has
* been set to TRUE. Set it back to FALSE here, so that
* we aren't kicked out of the statusbar prompt. */
finished = FALSE;
}
} else if (func == get_history_newer_void) {
if (history_list != NULL) {
/* Get the newer search from the history list and save it in
* answer. If there is no newer search, don't do anything. */
if ((history = get_history_newer(history_list)) != NULL) {
answer = mallocstrcpy(answer, history);
statusbar_x = strlen(answer);
}
/* If, after scrolling down, we're at the bottom of the
* history list, answer is blank, and magichistory is set,
* save magichistory in answer. */
if ((*history_list)->next == NULL &&
*answer == '\0' && magichistory != NULL) {
answer = mallocstrcpy(answer, magichistory);
statusbar_x = strlen(answer);
}
/* This key has a shortcut-list entry when it's used to
* move to a newer search, which means that finished has
* been set to TRUE. Set it back to FALSE here, so that
* we aren't kicked out of the statusbar prompt. */
finished = FALSE;
}
} else
#endif /* !DISABLE_HISTORIES */
if (func == do_help_void) {
/* This key has a shortcut-list entry when it's used to go to
* the help browser or display a message indicating that help
* is disabled, which means that finished has been set to TRUE.
* Set it back to FALSE here, so that we aren't kicked out of
* the statusbar prompt. */
finished = FALSE;
}
/* If we have a shortcut with an associated function, break out if
* we're finished after running or trying to run the function. */
if (finished)
break;
update_the_statusbar();
#if !defined(DISABLE_HISTORIES) && !defined(DISABLE_TABCOMP)
last_kbinput = kbinput;
#endif
}
#ifndef DISABLE_HISTORIES
/* Set the current position in the history list to the bottom. */
if (history_list != NULL) {
history_reset(*history_list);
free(magichistory);
}
#endif
*actual = kbinput;
return func;
}
/* Ask a question on the statusbar. The prompt will be stored in the
* static prompt, which should be NULL initially, and the answer will be
* stored in the answer global. Returns -1 on aborted enter, -2 on a
* blank string, and 0 otherwise, the valid shortcut key caught.
* curranswer is any editable text that we want to put up by default,
* and refresh_func is the function we want to call to refresh the edit
* window.
*
* The allow_tabs parameter indicates whether we should allow tabs to be
* interpreted. The allow_files parameter indicates whether we should
* allow all files (as opposed to just directories) to be tab completed. */
int do_prompt(bool allow_tabs,
#ifndef DISABLE_TABCOMP
bool allow_files,
#endif
int menu, const char *curranswer,
#ifndef DISABLE_HISTORIES
filestruct **history_list,
#endif
void (*refresh_func)(void), const char *msg, ...)
{
va_list ap;
int retval;
functionptrtype func = NULL;
#ifndef DISABLE_TABCOMP
bool listed = FALSE;
#endif
/* Save a possible current statusbar x position. */
size_t was_statusbar_x = statusbar_x;
bottombars(menu);
answer = mallocstrcpy(answer, curranswer);
#ifndef NANO_TINY
redo_theprompt:
#endif
prompt = charalloc((COLS * mb_cur_max()) + 1);
va_start(ap, msg);
vsnprintf(prompt, COLS * mb_cur_max(), msg, ap);
va_end(ap);
/* Reserve five columns for colon plus angles plus answer, ":<aa>". */
null_at(&prompt, actual_x(prompt, (COLS < 5) ? 0 : COLS - 5));
func = acquire_an_answer(&retval, allow_tabs,
#ifndef DISABLE_TABCOMP
allow_files, &listed,
#endif
#ifndef DISABLE_HISTORIES
history_list,
#endif
refresh_func);
free(prompt);
prompt = NULL;
#ifndef NANO_TINY
if (retval == KEY_WINCH)
goto redo_theprompt;
#endif
/* If we're done with this prompt, restore the x position to what
* it was at a possible previous prompt. */
if (func == do_cancel || func == do_enter)
statusbar_x = was_statusbar_x;
/* If we left the prompt via Cancel or Enter, set the return value
* properly. */
if (func == do_cancel)
retval = -1;
else if (func == do_enter)
retval = (*answer == '\0') ? -2 : 0;
blank_statusbar();
wnoutrefresh(bottomwin);
#ifdef DEBUG
fprintf(stderr, "answer = \"%s\"\n", answer);
#endif
#ifndef DISABLE_TABCOMP
/* If we've done tab completion, there might still be a list of
* filename matches on the edit window. Clear them off. */
if (listed)
refresh_func();
#endif
return retval;
}
/* Ask a simple Yes/No (and optionally All) question, specified in msg,
* on the statusbar. Return 1 for Yes, 0 for No, 2 for All (if all is
* TRUE when passed in), and -1 for Cancel. */
int do_yesno_prompt(bool all, const char *msg)
{
int response = -2, width = 16;
/* TRANSLATORS: For the next three strings, if possible, specify
* the single-byte shortcuts for both your language and English.
* For example, in French: "OoYy", for both "Oui" and "Yes". */
const char *yesstr = _("Yy");
const char *nostr = _("Nn");
const char *allstr = _("Aa");
/* The above three variables consist of all the single-byte characters
* that are accepted for the corresponding answer. Of each variable,
* the first character is displayed in the help lines. */
do {
int kbinput;
functionptrtype func;
#ifndef DISABLE_MOUSE
int mouse_x, mouse_y;
#endif
if (!ISSET(NO_HELP)) {
char shortstr[3];
/* Temporary string for (translated) " Y", " N" and " A". */
if (COLS < 32)
width = COLS / 2;
/* Clear the shortcut list from the bottom of the screen. */
blank_bottombars();
/* Now show the ones for "Yes", "No", "Cancel" and maybe "All". */
sprintf(shortstr, " %c", yesstr[0]);
wmove(bottomwin, 1, 0);
onekey(shortstr, _("Yes"), width);
if (all) {
shortstr[1] = allstr[0];
wmove(bottomwin, 1, width);
onekey(shortstr, _("All"), width);
}
shortstr[1] = nostr[0];
wmove(bottomwin, 2, 0);
onekey(shortstr, _("No"), width);
wmove(bottomwin, 2, width);
onekey("^C", _("Cancel"), width);
}
/* Color the statusbar over its full width and display the question. */
wattron(bottomwin, interface_color_pair[TITLE_BAR]);
blank_statusbar();
mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1));
wattroff(bottomwin, interface_color_pair[TITLE_BAR]);
wnoutrefresh(bottomwin);
/* When not replacing, show the cursor. */
if (!all)
curs_set(1);
currmenu = MYESNO;
kbinput = get_kbinput(bottomwin);
#ifndef NANO_TINY
if (kbinput == KEY_WINCH)
continue;
#endif
func = func_from_key(&kbinput);
if (func == do_cancel)
response = -1;
#ifndef DISABLE_MOUSE
else if (kbinput == KEY_MOUSE) {
/* We can click on the Yes/No/All shortcuts to select an answer. */
if (get_mouseinput(&mouse_x, &mouse_y, FALSE) == 0 &&
wmouse_trafo(bottomwin, &mouse_y, &mouse_x, FALSE) &&
mouse_x < (width * 2) && mouse_y > 0) {
int x = mouse_x / width;
/* The x-coordinate among the Yes/No/All shortcuts. */
int y = mouse_y - 1;
/* The y-coordinate among the Yes/No/All shortcuts. */
assert(0 <= x && x <= 1 && 0 <= y && y <= 1);
/* x == 0 means they clicked Yes or No.
* y == 0 means Yes or All. */
response = -2 * x * y + x - y + 1;
if (response == 2 && !all)
response = -2;
}
}
#endif /* !DISABLE_MOUSE */
else {
/* Look for the kbinput in the Yes, No (and All) strings. */
if (strchr(yesstr, kbinput) != NULL)
response = 1;
else if (strchr(nostr, kbinput) != NULL)
response = 0;
else if (all && strchr(allstr, kbinput) != NULL)
response = 2;
}
} while (response == -2);
return response;
}