* Fix not allow remote execution by adding PIPE_NOSHELL to the opening of a url by
[alpine.git] / alpine / radio.c
blob436be052f0af6b23a82c73387823fb40dcbb6374
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: radio.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "radio.h"
21 #include "keymenu.h"
22 #include "busy.h"
23 #include "status.h"
24 #include "mailcmd.h"
25 #include "titlebar.h"
26 #include "roleconf.h"
27 #include "../pith/state.h"
28 #include "../pith/conf.h"
29 #include "../pith/newmail.h"
30 #include "../pith/util.h"
34 * Internal prototypes
36 int pre_screen_config_want_to(char *, int, int);
37 ESCKEY_S *construct_combined_esclist(ESCKEY_S *, ESCKEY_S *);
38 void radio_help(int, int, HelpType);
39 void draw_radio_prompt(int, unsigned, unsigned, char *);
42 #define RAD_BUT_COL 0
43 #define WANT_TO_BUF 2500
47 * want_to's array passed to radio_buttions...
49 static ESCKEY_S yorn[] = {
50 {'y', 'y', "Y", N_("Yes")},
51 {'n', 'n', "N", N_("No")},
52 {-1, 0, NULL, NULL}
55 int
56 pre_screen_config_want_to(char *question, int dflt, int on_ctrl_C)
58 int ret = 0;
59 char rep[WANT_TO_BUF], *p;
60 #ifdef _WINDOWS
61 rep[0] = '\0';
62 mswin_flush();
63 if(strlen(question) + 3 < WANT_TO_BUF){
64 snprintf(rep, sizeof(rep), "%s ?", question);
65 rep[sizeof(rep)-1] = '\0';
68 switch (mswin_yesno_utf8 (*rep ? rep : question)) {
69 default:
70 case 0: return (on_ctrl_C);
71 case 1: return ('y');
72 case 2: return ('n');
74 #endif
75 while(!ret){
76 fprintf(stdout, "%s? [%c]:", question, dflt);
77 fgets(rep, WANT_TO_BUF, stdin);
78 if((p = strpbrk(rep, "\r\n")) != NULL)
79 *p = '\0';
80 switch(*rep){
81 case 'Y':
82 case 'y':
83 ret = (int)'y';
84 break;
85 case 'N':
86 case 'n':
87 ret = (int)'n';
88 break;
89 case '\0':
90 ret = dflt;
91 break;
92 default:
93 break;
96 return ret;
100 /*----------------------------------------------------------------------
101 Ask a yes/no question in the status line
103 Args: question -- string to prompt user with
104 dflt -- The default answer to the question (should probably
105 be y or n)
106 on_ctrl_C -- Answer returned on ^C
107 help -- Two line help text
108 flags -- Flags to modify behavior
109 WT_FLUSH_IN - Discard pending input.
110 WT_SEQ_SENSITIVE - Caller is sensitive to sequence
111 number changes caused by
112 unsolicited expunges while we're
113 viewing a message.
115 Result: Messes up the status line,
116 returns y, n, dflt, on_ctrl_C, or SEQ_EXCEPTION
117 ---*/
119 want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
121 char *free_this = NULL, *free_this2 = NULL, *prompt;
122 int rv, width;
123 size_t len;
125 if(!ps_global->ttyo || ps_global->send_immediately)
126 return(pre_screen_config_want_to(question, dflt, on_ctrl_C));
127 #ifdef _WINDOWS
128 if (mswin_usedialog ()) {
129 mswin_flush ();
130 switch (mswin_yesno_utf8 (question)) {
131 default:
132 case 0: return (on_ctrl_C);
133 case 1: return ('y');
134 case 2: return ('n');
137 #endif
139 /*----
140 One problem with adding the (y/n) here is that shrinking the
141 screen while in radio_buttons() will cause it to get chopped
142 off. It would be better to truncate the question passed in
143 here and leave the full "(y/n) [x] : " on.
144 ----*/
146 len = strlen(question) + 4;
147 free_this = (char *) fs_get(len);
148 width = utf8_width(question);
150 if(width + 2 < ps_global->ttyo->screen_cols){
151 snprintf(free_this, len, "%s? ", question);
152 free_this[len-1] = '\0';
153 prompt = free_this;
155 else if(width + 1 < ps_global->ttyo->screen_cols){
156 snprintf(free_this, len, "%s?", question);
157 free_this[len-1] = '\0';
158 prompt = free_this;
160 else if(width < ps_global->ttyo->screen_cols){
161 snprintf(free_this, len, "%s", question);
162 free_this[len-1] = '\0';
163 prompt = free_this;
165 else{
166 free_this2 = (char *) fs_get(len);
167 snprintf(free_this2, len, "%s? ", question);
168 prompt = short_str(free_this2, free_this, len, ps_global->ttyo->screen_cols-1, MidDots);
171 if(on_ctrl_C == 'n') /* don't ever let cancel == 'n' */
172 on_ctrl_C = 0;
174 rv = radio_buttons(prompt,
175 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
176 yorn, dflt, on_ctrl_C, help, flags);
178 if(free_this)
179 fs_give((void **) &free_this);
181 if(free_this2)
182 fs_give((void **) &free_this2);
184 return(rv);
189 one_try_want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
191 char *q2;
192 int rv;
193 size_t l;
195 l = strlen(question) + 5;
196 q2 = fs_get((l+1) * sizeof(char));
197 strncpy(q2, question, l);
198 q2[l] = '\0';
199 (void) utf8_truncate(q2, ps_global->ttyo->screen_cols - 6);
200 strncat(q2, "? ", l+1 - strlen(q2) - 1);
201 q2[l] = '\0';
202 rv = radio_buttons(q2,
203 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
204 yorn, dflt, on_ctrl_C, help, flags | RB_ONE_TRY);
205 fs_give((void **) &q2);
207 return(rv);
211 /*----------------------------------------------------------------------
212 Prompt user for a choice among alternatives
214 Args -- utf8prompt: The prompt for the question/selection
215 line: The line to prompt on, if negative then relative to bottom
216 esc_list: ESC_KEY_S list of keys
217 dflt: The selection when the <CR> is pressed (should probably
218 be one of the chars in esc_list)
219 on_ctrl_C: The selection when ^C is pressed
220 help_text: Text to be displayed on bottom two lines
221 flags: Logically OR'd flags modifying our behavior to:
222 RB_FLUSH_IN - Discard any pending input chars.
223 RB_ONE_TRY - Only give one chance to answer. Returns
224 on_ctrl_C value if not answered acceptably
225 on first try.
226 RB_NO_NEWMAIL - Quell the usual newmail check.
227 RB_SEQ_SENSITIVE - The caller is sensitive to sequence number
228 changes so return on_ctrl_C if an
229 unsolicited expunge happens while we're
230 viewing a message.
231 RB_RET_HELP - Instead of the regular internal handling
232 way of handling help_text, this just causes
233 radio_buttons to return 3 when help is
234 asked for, so that the caller handles it
235 instead.
237 Note: If there are enough keys in the esc_list to need a second
238 screen, and there is no help, then the 13th key will be
239 put in the help position.
241 Result -- Returns the letter pressed. Will be one of the characters in the
242 esc_list argument, or dflt, or on_ctrl_C, or SEQ_EXCEPTION.
244 This will pause for any new status message to be seen and then prompt the user.
245 The prompt will be truncated to fit on the screen. Redraw and resize are
246 handled along with ^Z suspension. Typing ^G will toggle the help text on and
247 off. Character types that are not buttons will result in a beep (unless one_try
248 is set).
249 ----*/
251 radio_buttons(char *utf8prompt, int line, ESCKEY_S *esc_list, int dflt,
252 int on_ctrl_C, HelpType help_text, int flags)
254 UCS ucs;
255 register int ch, real_line;
256 char *q, *ds = NULL;
257 unsigned maxcol;
258 int max_label, i, start, fkey_table[12];
259 int km_popped = 0;
260 struct key rb_keys[12];
261 struct key_menu rb_keymenu;
262 bitmap_t bitmap;
263 struct variable *vars = ps_global->vars;
264 COLOR_PAIR *lastc = NULL, *promptc = NULL;
266 #ifdef _WINDOWS
267 int cursor_shown;
269 if (mswin_usedialog()){
270 MDlgButton button_list[25];
271 LPTSTR free_names[25];
272 LPTSTR free_labels[25];
273 int b, i, ret;
274 char **help;
276 memset(&free_names, 0, sizeof(LPTSTR) * 25);
277 memset(&free_labels, 0, sizeof(LPTSTR) * 25);
278 memset(&button_list, 0, sizeof (MDlgButton) * 25);
279 b = 0;
281 if(flags & RB_RET_HELP){
282 if(help_text != NO_HELP)
283 panic("RET_HELP and help in radio_buttons!");
285 button_list[b].ch = '?';
286 button_list[b].rval = 3;
287 button_list[b].name = TEXT("?");
288 free_labels[b] = utf8_to_lptstr(N_("Help"));
289 button_list[b].label = free_labels[b];
290 ++b;
293 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 23; ++i){
294 if(esc_list[i].ch != -2){
295 button_list[b].ch = esc_list[i].ch;
296 button_list[b].rval = esc_list[i].rval;
297 free_names[b] = utf8_to_lptstr(esc_list[i].name);
298 button_list[b].name = free_names[b];
299 free_labels[b] = utf8_to_lptstr(esc_list[i].label);
300 button_list[b].label = free_labels[b];
301 ++b;
305 button_list[b].ch = -1;
307 /* assumption here is that HelpType is char ** */
308 help = help_text;
310 ret = mswin_select(utf8prompt, button_list, dflt, on_ctrl_C, help, flags);
311 for(i = 0; i < 25; i++){
312 if(free_names[i])
313 fs_give((void **) &free_names[i]);
314 if(free_labels[i])
315 fs_give((void **) &free_labels[i]);
318 return (ret);
321 #endif /* _WINDOWS */
323 suspend_busy_cue();
324 flush_ordered_messages(); /* show user previous status msgs */
325 mark_status_dirty(); /* clear message next display call */
326 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
327 MoveCursor(real_line, RAD_BUT_COL);
328 CleartoEOLN();
330 /*---- Find widest label ----*/
331 max_label = 0;
332 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 11; i++){
333 if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
334 continue;
335 if(esc_list[i].name)
336 max_label = MAX(max_label, utf8_width(esc_list[i].name));
339 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
340 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
341 else
342 maxcol = 0;
345 * We need to be able to truncate q, so copy it in case it is
346 * a readonly string.
348 q = cpystr(utf8prompt);
350 /*---- Init structs for keymenu ----*/
351 for(i = 0; i < 12; i++)
352 memset((void *)&rb_keys[i], 0, sizeof(struct key));
354 memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
355 rb_keymenu.how_many = 1;
356 rb_keymenu.keys = rb_keys;
358 /*---- Setup key menu ----*/
359 start = 0;
360 clrbitmap(bitmap);
361 memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
362 if(flags & RB_RET_HELP && help_text != NO_HELP)
363 panic("RET_HELP and help in radio_buttons!");
365 /* if shown, always at position 0 */
366 if(help_text != NO_HELP || flags & RB_RET_HELP){
367 rb_keymenu.keys[0].name = "?";
368 rb_keymenu.keys[0].label = N_("Help");
369 setbitn(0, bitmap);
370 fkey_table[0] = ctrl('G');
371 start++;
374 if(on_ctrl_C){
375 rb_keymenu.keys[1].name = "^C";
376 rb_keymenu.keys[1].label = N_("Cancel");
377 setbitn(1, bitmap);
378 fkey_table[1] = ctrl('C');
379 start++;
382 start = start ? 2 : 0;
383 /*---- Show the usual possible keys ----*/
384 for(i=start; esc_list && esc_list[i-start].ch != -1; i++){
386 * If we have an esc_list item we'd like to put in the non-existent
387 * 13th slot, and there is no help, we put it in the help slot
388 * instead. We're hacking now...!
390 * We may also have invisible esc_list items that don't show up
391 * on the screen. We use this when we have two different keys
392 * which are synonyms, like ^P and KEY_UP. If all the slots are
393 * already full we can still fit invisible keys off the screen to
394 * the right. A key is invisible if it's label is "".
396 if(i >= 12){
397 if(esc_list[i-start].label
398 && esc_list[i-start].label[0] != '\0'){ /* visible */
399 if(i == 12){ /* special case where we put it in help slot */
400 if(help_text != NO_HELP)
401 panic("Programming botch in radio_buttons(): too many keys");
403 if(esc_list[i-start].ch != -2)
404 setbitn(0, bitmap); /* the help slot */
406 fkey_table[0] = esc_list[i-start].ch;
407 rb_keymenu.keys[0].name = esc_list[i-start].name;
408 if(esc_list[i-start].ch != -2
409 && esc_list[i-start].rval == dflt
410 && esc_list[i-start].label){
411 size_t l;
413 l = strlen(esc_list[i-start].label) + 2;
414 ds = (char *)fs_get((l+1) * sizeof(char));
415 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
416 ds[l] = '\0';
417 rb_keymenu.keys[0].label = ds;
419 else
420 rb_keymenu.keys[0].label = esc_list[i-start].label;
422 else
423 panic("Botch in radio_buttons(): too many keys");
426 else{
427 if(esc_list[i-start].ch != -2)
428 setbitn(i, bitmap);
430 fkey_table[i] = esc_list[i-start].ch;
431 rb_keymenu.keys[i].name = esc_list[i-start].name;
432 if(esc_list[i-start].ch != -2
433 && esc_list[i-start].rval == dflt
434 && esc_list[i-start].label){
435 size_t l;
437 l = strlen(esc_list[i-start].label) + 2;
438 ds = (char *)fs_get((l+1) * sizeof(char));
439 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
440 ds[l] = '\0';
441 rb_keymenu.keys[i].label = ds;
443 else
444 rb_keymenu.keys[i].label = esc_list[i-start].label;
448 for(; i < 12; i++)
449 rb_keymenu.keys[i].name = NULL;
451 ps_global->mangled_footer = 1;
453 #ifdef _WINDOWS
454 cursor_shown = mswin_showcaret(1);
455 #endif
457 if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
458 VAR_PROMPT_BACK_COLOR &&
459 pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
460 pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
461 lastc = pico_get_cur_color();
462 if(lastc){
463 promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
464 VAR_PROMPT_BACK_COLOR);
465 (void)pico_set_colorp(promptc, PSC_NONE);
468 else
469 StartInverse();
471 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
473 while(1){
474 fflush(stdout);
476 /*---- Paint the keymenu ----*/
477 if(lastc)
478 (void)pico_set_colorp(lastc, PSC_NONE);
479 else
480 EndInverse();
482 draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
483 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
484 if(promptc)
485 (void)pico_set_colorp(promptc, PSC_NONE);
486 else
487 StartInverse();
489 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
491 if(flags & RB_FLUSH_IN)
492 flush_input();
494 newcmd:
495 /* Timeout 5 min to keep imap mail stream alive */
496 ucs = read_char(600);
497 dprint((2,
498 "Want_to read: %s (0x%x)\n", pretty_command(ucs), ucs));
499 if((ucs < 0x80) && isupper((unsigned char) ucs))
500 ucs = tolower((unsigned char) ucs);
502 if(F_ON(F_USE_FK,ps_global)
503 && (((ucs < 0x80) && isalpha((unsigned char) ucs) && !strchr("YyNn",(int) ucs))
504 || ((ucs >= PF1 && ucs <= PF12)
505 && (ucs = fkey_table[ucs - PF1]) == NO_OP_COMMAND))){
507 * The funky test above does two things. It maps
508 * esc_list character commands to function keys, *and* prevents
509 * character commands from input while in function key mode.
510 * NOTE: this breaks if we ever need more than the first
511 * twelve function keys...
513 if(flags & RB_ONE_TRY){
514 ch = ucs = on_ctrl_C;
515 goto out_of_loop;
518 Writechar(BELL, 0);
519 continue;
522 switch(ucs){
524 default:
525 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
526 if(ucs == esc_list[i].ch){
527 int len, n;
529 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
530 for(n = 0, len = ps_global->ttyo->screen_cols - len;
531 esc_list[i].label && esc_list[i].label[n] && len > 0;
532 n++, len--)
533 Writechar(esc_list[i].label[n], 0);
535 ch = esc_list[i].rval;
536 goto out_of_loop;
539 if(flags & RB_ONE_TRY){
540 ch = on_ctrl_C;
541 goto out_of_loop;
544 Writechar(BELL, 0);
545 break;
547 case ctrl('M'):
548 case ctrl('J'):
549 ch = dflt;
550 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
551 if(ch == esc_list[i].rval){
552 int len, n;
554 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
555 for(n = 0, len = ps_global->ttyo->screen_cols - len;
556 esc_list[i].label && esc_list[i].label[n] && len > 0;
557 n++, len--)
558 Writechar(esc_list[i].label[n], 0);
559 break;
562 goto out_of_loop;
564 case ctrl('C'):
565 if(on_ctrl_C || (flags & RB_ONE_TRY)){
566 ch = on_ctrl_C;
567 goto out_of_loop;
570 Writechar(BELL, 0);
571 break;
574 case '?':
575 case ctrl('G'):
576 if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
577 km_popped++;
578 FOOTER_ROWS(ps_global) = 3;
579 line = -3;
580 real_line = ps_global->ttyo->screen_rows + line;
581 if(lastc)
582 (void)pico_set_colorp(lastc, PSC_NONE);
583 else
584 EndInverse();
586 clearfooter(ps_global);
587 if(promptc)
588 (void)pico_set_colorp(promptc, PSC_NONE);
589 else
590 StartInverse();
592 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
593 break;
596 if(flags & RB_RET_HELP){
597 ch = 3;
598 goto out_of_loop;
600 else if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
601 mark_keymenu_dirty();
602 if(lastc)
603 (void)pico_set_colorp(lastc, PSC_NONE);
604 else
605 EndInverse();
607 MoveCursor(real_line + 1, RAD_BUT_COL);
608 CleartoEOLN();
609 MoveCursor(real_line + 2, RAD_BUT_COL);
610 CleartoEOLN();
611 radio_help(real_line, RAD_BUT_COL, help_text);
612 sleep(5);
613 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
614 if(promptc)
615 (void)pico_set_colorp(promptc, PSC_NONE);
616 else
617 StartInverse();
619 else
620 Writechar(BELL, 0);
622 break;
625 case NO_OP_COMMAND:
626 goto newcmd; /* misunderstood escape? */
628 case NO_OP_IDLE: /* UNODIR, keep the stream alive */
629 if(flags & RB_NO_NEWMAIL)
630 goto newcmd;
632 i = new_mail(0, VeryBadTime, NM_DEFER_SORT);
633 if(sp_expunge_count(ps_global->mail_stream)
634 && flags & RB_SEQ_SENSITIVE){
635 if(on_ctrl_C)
636 ch = on_ctrl_C;
637 else
638 ch = SEQ_EXCEPTION;
640 goto out_of_loop;
643 if(i < 0)
644 break; /* no changes, get on with life */
645 /* Else fall into redraw to adjust displayed numbers and such */
648 case KEY_RESIZE:
649 case ctrl('L'):
650 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
651 if(lastc)
652 (void)pico_set_colorp(lastc, PSC_NONE);
653 else
654 EndInverse();
656 ClearScreen();
657 redraw_titlebar();
658 if(ps_global->redrawer != NULL)
659 (*ps_global->redrawer)();
660 if(FOOTER_ROWS(ps_global) == 3 || km_popped)
661 redraw_keymenu();
663 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
664 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
665 else
666 maxcol = 0;
668 if(promptc)
669 (void)pico_set_colorp(promptc, PSC_NONE);
670 else
671 StartInverse();
673 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
674 break;
676 } /* switch */
679 out_of_loop:
681 #ifdef _WINDOWS
682 if(!cursor_shown)
683 mswin_showcaret(0);
684 #endif
686 fs_give((void **) &q);
687 if(ds)
688 fs_give((void **) &ds);
690 if(lastc){
691 (void) pico_set_colorp(lastc, PSC_NONE);
692 free_color_pair(&lastc);
693 if(promptc)
694 free_color_pair(&promptc);
696 else
697 EndInverse();
699 fflush(stdout);
700 resume_busy_cue(0);
701 if(km_popped){
702 FOOTER_ROWS(ps_global) = 1;
703 clearfooter(ps_global);
704 ps_global->mangled_body = 1;
707 return(ch);
711 #define OTHER_RETURN_VAL 1300
712 #define KEYS_PER_LIST 8
715 * This should really be part of radio_buttons itself, I suppose. It was
716 * easier to do it this way. This is for when there are more than 12
717 * possible commands. We could have all the radio_buttons calls call this
718 * instead of radio_buttons, or rename this to radio_buttons.
720 * Radio_buttons is limited to 10 visible commands unless there is no Help,
721 * in which case it is 11 visible commands.
722 * Double_radio_buttons is limited to 16 visible commands because it uses
723 * slots 3 and 4 for readability and the OTHER CMD.
726 double_radio_buttons(char *prompt, int line, ESCKEY_S *esc_list, int dflt, int on_ctrl_C, HelpType help_text, int flags)
728 ESCKEY_S *list = NULL, *list1 = NULL, *list2 = NULL;
729 int i = 0, j;
730 int v = OTHER_RETURN_VAL, listnum = 0;
732 #ifdef _WINDOWS
733 if(mswin_usedialog())
734 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
735 help_text, flags));
736 #endif
738 /* check to see if it will all fit in one */
739 while(esc_list && esc_list[i].ch != -1)
740 i++;
742 i++; /* for ^C */
743 if(help_text != NO_HELP)
744 i++;
746 if(i <= 12)
747 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
748 help_text, flags));
751 * Won't fit, split it into two lists.
753 * We can fit at most 8 items in the visible list. The rest of
754 * the commands have to be invisible. Each of list1 and list2 should
755 * have no more than 8 visible (name != "" || label != "") items.
757 list1 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list1));
758 memset(list1, 0, (KEYS_PER_LIST+1) * sizeof(*list1));
759 list2 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list2));
760 memset(list2, 0, (KEYS_PER_LIST+1) * sizeof(*list2));
762 for(j=0,i=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
763 list1[j] = esc_list[i];
765 list1[j].ch = -1;
767 for(j=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
768 list2[j] = esc_list[i];
770 list2[j].ch = -1;
772 list = construct_combined_esclist(list1, list2);
774 while(v == OTHER_RETURN_VAL){
775 v = radio_buttons(prompt,line,list,dflt,on_ctrl_C,help_text,flags);
776 if(v == OTHER_RETURN_VAL){
777 fs_give((void **) &list);
778 listnum = 1 - listnum;
779 list = construct_combined_esclist(listnum ? list2 : list1,
780 listnum ? list1 : list2);
784 if(list)
785 fs_give((void **) &list);
786 if(list1)
787 fs_give((void **) &list1);
788 if(list2)
789 fs_give((void **) &list2);
791 return(v);
795 ESCKEY_S *
796 construct_combined_esclist(ESCKEY_S *list1, ESCKEY_S *list2)
798 ESCKEY_S *list;
799 int i, j=0, count;
801 count = 2; /* for blank key and for OTHER key */
802 for(i=0; list1 && list1[i].ch != -1; i++)
803 count++;
804 for(i=0; list2 && list2[i].ch != -1; i++)
805 count++;
807 list = (ESCKEY_S *) fs_get((count + 1) * sizeof(*list));
808 memset(list, 0, (count + 1) * sizeof(*list));
810 list[j].ch = -2; /* leave blank */
811 list[j].rval = 0;
812 list[j].name = NULL;
813 list[j++].label = NULL;
815 list[j].ch = 'o';
816 list[j].rval = OTHER_RETURN_VAL;
817 list[j].name = "O";
818 list[j].label = N_("OTHER CMDS");
821 * Make sure that O for OTHER CMD or the return val for OTHER CMD
822 * isn't used for something else.
824 for(i=0; list1 && list1[i].ch != -1; i++){
825 if(list1[i].rval == list[j].rval)
826 panic("1bad rval in d_r");
827 if(F_OFF(F_USE_FK,ps_global) && list1[i].ch == list[j].ch)
828 panic("1bad ch in ccl");
831 for(i=0; list2 && list2[i].ch != -1; i++){
832 if(list2[i].rval == list[j].rval)
833 panic("2bad rval in d_r");
834 if(F_OFF(F_USE_FK,ps_global) && list2[i].ch == list[j].ch)
835 panic("2bad ch in ccl");
838 j++;
840 /* the visible set */
841 for(i=0; list1 && list1[i].ch != -1; i++){
842 if(i >= KEYS_PER_LIST && list1[i].label[0] != '\0')
843 panic("too many visible keys in ccl");
845 list[j++] = list1[i];
848 /* the rest are invisible */
849 for(i=0; list2 && list2[i].ch != -1; i++){
850 list[j] = list2[i];
851 list[j].label = "";
852 list[j++].name = "";
855 list[j].ch = -1;
857 return(list);
861 /*----------------------------------------------------------------------
863 ----*/
864 void
865 radio_help(int line, int column, HelpType help)
867 char **text;
869 /* assumption here is that HelpType is char ** */
870 text = help;
871 if(text == NULL)
872 return;
874 MoveCursor(line + 1, column);
875 CleartoEOLN();
876 if(text[0])
877 PutLine0(line + 1, column, text[0]);
879 MoveCursor(line + 2, column);
880 CleartoEOLN();
881 if(text[1])
882 PutLine0(line + 2, column, text[1]);
884 fflush(stdout);
888 /*----------------------------------------------------------------------
889 Paint the screen with the radio buttons prompt
890 ----*/
891 void
892 draw_radio_prompt(int line, unsigned start_c, unsigned max_c, char *q)
894 size_t len;
895 unsigned x, width, got_width;
896 char *tmpq = NULL, *useq;
898 width = utf8_width(q);
899 if(width > max_c - start_c + 1){
900 tmpq = (char *) fs_get((len=(strlen(q)+1)) * sizeof(char));
901 (void) utf8_to_width(tmpq, q, len, max_c - start_c + 1, &got_width);
902 useq = tmpq;
903 width = got_width;
905 else
906 useq = q;
908 PutLine0(line, start_c, useq);
909 x = start_c + width;
910 MoveCursor(line, x);
911 while(x++ < ps_global->ttyo->screen_cols)
912 Writechar(' ', 0);
914 MoveCursor(line, start_c + width);
915 fflush(stdout);
916 if(tmpq)
917 fs_give((void **) &tmpq);