"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.1/src/prompt.c" (22 Dec 2021, 19790 Bytes) of package /linux/misc/tin-2.6.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : prompt.c
    4  *  Author    : I. Lea
    5  *  Created   : 1991-04-01
    6  *  Updated   : 2021-10-29
    7  *  Notes     :
    8  *
    9  * Copyright (c) 1991-2022 Iain Lea <iain@bricbrac.de>
   10  * All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  *
   16  * 1. Redistributions of source code must retain the above copyright notice,
   17  *    this list of conditions and the following disclaimer.
   18  *
   19  * 2. Redistributions in binary form must reproduce the above copyright
   20  *    notice, this list of conditions and the following disclaimer in the
   21  *    documentation and/or other materials provided with the distribution.
   22  *
   23  * 3. Neither the name of the copyright holder nor the names of its
   24  *    contributors may be used to endorse or promote products derived from
   25  *    this software without specific prior written permission.
   26  *
   27  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 
   41 #ifndef TIN_H
   42 #   include "tin.h"
   43 #endif /* !TIN_H */
   44 #ifndef TCURSES_H
   45 #   include "tcurses.h"
   46 #endif /* !TCURSES_H */
   47 
   48 
   49 static char *prompt_slk_message;    /* prompt message for prompt_slk_redraw */
   50 static char *prompt_yn_message;
   51 static char *prompt_yn_choice;
   52 
   53 /*
   54  * Local prototypes
   55  */
   56 static int prompt_list(int row, int col, int var, constext *help_text, constext *prompt_text, constext *list[], int size);
   57 
   58 
   59 /*
   60  *  prompt_num
   61  *  get a number from the user
   62  *  Return -1 if missing or bad number typed
   63  */
   64 int
   65 prompt_num(
   66     int ch,
   67     const char *prompt)
   68 {
   69     char *p;
   70     char tmp[LEN];
   71     int num;
   72 
   73     clear_message();
   74     snprintf(tmp, sizeof(tmp), "%c", ch);
   75     if ((p = tin_getline(prompt, 1, tmp, 0, FALSE, HIST_OTHER)) != NULL) {
   76         STRCPY(tmp, p);
   77         num = atoi(tmp);
   78     } else
   79         num = -1;
   80 
   81     clear_message();
   82     return num;
   83 }
   84 
   85 
   86 /*
   87  *  prompt_string
   88  *  get a string from the user
   89  *  Return TRUE if a valid string was typed, FALSE otherwise
   90  *  TODO: no bounds checking on buf size, tin_getline() defaults to 1024
   91  */
   92 t_bool
   93 prompt_string(
   94     const char *prompt,
   95     char *buf,
   96     int which_hist)
   97 {
   98     return prompt_default_string(prompt, buf, 0, (char *) NULL, which_hist);
   99 }
  100 
  101 
  102 /*
  103  * prompt_default_string
  104  * get a string from the user, display default value
  105  * Return TRUE if a valid string was typed, FALSE otherwise
  106  */
  107 t_bool
  108 prompt_default_string(
  109     const char *prompt,
  110     char *buf,
  111     int buf_len,
  112     char *default_prompt,
  113     int which_hist)
  114 {
  115     char *p;
  116 
  117     clear_message();
  118     if ((p = tin_getline(prompt, 0, default_prompt, buf_len, FALSE, which_hist)) == NULL) {
  119         buf[0] = '\0';
  120         clear_message();
  121         return FALSE;
  122     }
  123     strcpy(buf, p);
  124     clear_message();
  125     return TRUE;
  126 }
  127 
  128 
  129 /*
  130  *  prompt_menu_string
  131  *  get a string from the user
  132  *  Return TRUE if a valid string was typed, FALSE otherwise
  133  */
  134 t_bool
  135 prompt_menu_string(
  136     int line,
  137     const char *prompt,
  138     char *var)
  139 {
  140     char *p;
  141 
  142     /*
  143      * clear buffer - this is needed, otherwise a lost
  144      * connection right before a resync_active() call
  145      * would lead to a 'n' answer to the reconnect prompt
  146      */
  147     /* fflush(stdin); */
  148     MoveCursor(line, 0);
  149     if ((p = tin_getline(prompt, 0, var, 0, FALSE, HIST_OTHER)) == NULL)
  150         return FALSE;
  151 
  152     strcpy(var, p);
  153     return TRUE;
  154 }
  155 
  156 
  157 /*
  158  * prompt_yn
  159  * prompt user for 'y'es or 'n'o decision. "prompt" will be displayed in the
  160  * last line giving the default answer "default_answer".
  161  * The function returns 1 if the user decided "yes", -1 if the user wanted
  162  * to escape, or 0 for any other decision.
  163  */
  164 int
  165 prompt_yn(
  166     const char *prompt,
  167     t_bool default_answer)
  168 {
  169     char *keyprompt;
  170     char keyno[MAXKEYLEN], keyyes[MAXKEYLEN];
  171     int keyyes_len, keyno_len, maxlen, prompt_len;
  172     t_function func;
  173 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  174     wint_t yes, no, prompt_ch, ch;
  175 #else
  176     char yes, no, prompt_ch;
  177     int ch;
  178 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  179 
  180 /*  fflush(stdin); */       /* Prevent finger trouble from making important decisions */
  181 
  182 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  183     yes = (wint_t) func_to_key(PROMPT_YES, prompt_keys);
  184     no = (wint_t) func_to_key(PROMPT_NO, prompt_keys);
  185 
  186     printascii(keyyes, (default_answer ? towupper(yes) : yes));
  187     printascii(keyno, (!default_answer ? towupper(no) : no));
  188 #else
  189     yes = func_to_key(PROMPT_YES, prompt_keys);
  190     no = func_to_key(PROMPT_NO, prompt_keys);
  191 
  192     printascii(keyyes, (default_answer ? my_toupper(yes) : yes));
  193     printascii(keyno, (!default_answer ? my_toupper(no) : no));
  194 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  195 
  196     keyyes_len = strwidth(keyyes);
  197     keyno_len = strwidth(keyno);
  198     maxlen = MAX(keyyes_len, keyno_len);
  199     prompt_len = keyyes_len + keyno_len + maxlen + 6;
  200     prompt_yn_message = my_strdup(prompt);
  201     prompt_yn_choice = my_malloc(prompt_len + 1);
  202 
  203     input_context = cPromptYN;
  204 
  205     do {
  206         prompt_ch = (default_answer ? yes : no);
  207         keyprompt = (default_answer ? keyyes : keyno);
  208 
  209         snprintf(prompt_yn_choice, (size_t) prompt_len, " (%s/%s) %-*s", keyyes, keyno, maxlen, keyprompt);
  210         prompt_yn_redraw();
  211 
  212 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  213         if (((ch = ReadWch()) == '\n') || (ch == '\r'))
  214 #else
  215         if (((ch = (char) ReadCh()) == '\n') || (ch == '\r'))
  216 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  217             ch = prompt_ch;
  218 
  219         switch (ch) {
  220             case ESC:   /* (ESC) common arrow keys */
  221 #ifdef HAVE_KEY_PREFIX
  222             case KEY_PREFIX:
  223 #endif /* HAVE_KEY_PREFIX */
  224                 switch (get_arrow_key((int) ch)) {
  225                     case KEYMAP_UP:
  226                     case KEYMAP_DOWN:
  227                         default_answer = bool_not(default_answer);
  228                         ch = '\0';  /* set to a not bindable key to not leave the loop yet */
  229                         break;
  230 
  231                     case KEYMAP_LEFT:
  232                         ch = ESC;
  233                         break;
  234 
  235                     case KEYMAP_RIGHT:
  236                         ch = prompt_ch;
  237                         break;
  238 
  239                     default:
  240                         break;
  241                 }
  242                 break;
  243 
  244             default:
  245                 break;
  246         }
  247         func = key_to_func((wchar_t) ch, prompt_keys);
  248     } while (func == NOT_ASSIGNED);
  249 
  250     input_context = cNone;
  251     FreeAndNull(prompt_yn_message);
  252     FreeAndNull(prompt_yn_choice);
  253 
  254     if (!cmd_line) {
  255         clear_message();
  256         my_flush();
  257     }
  258     return (func == PROMPT_YES) ? 1 : (func == GLOBAL_ABORT) ? -1 : 0;
  259 }
  260 
  261 
  262 /*
  263  * (Re)draws and resize the prompt message for prompt_yn()
  264  */
  265 void
  266 prompt_yn_redraw(
  267     void)
  268 {
  269     char *buf;
  270     int choice_len = strwidth(prompt_yn_choice);
  271     int message_len = strwidth(prompt_yn_message);
  272 
  273     if (!cmd_line) {
  274         MoveCursor(cLINES, 0);
  275         CleartoEOLN();
  276     }
  277     if (message_len + choice_len > cCOLS - 1) {
  278         buf = strunc(prompt_yn_message, cCOLS - choice_len - 1);
  279         message_len = strwidth(buf);
  280         my_printf("%s%s", buf, prompt_yn_choice);
  281         free(buf);
  282     } else
  283         my_printf("%s%s", prompt_yn_message, prompt_yn_choice);
  284 
  285     if (!cmd_line)
  286         cursoron();
  287     my_flush();
  288     if (!cmd_line)
  289         MoveCursor(cLINES, (message_len + choice_len) - 1);
  290 }
  291 
  292 
  293 /*
  294  * help_text is displayed near the bottom of the screen.
  295  * var is an index into a list containing size elements.
  296  * The text from list is shown at row, col + len(prompt_text)
  297  * Choice is incremented using the space bar, wrapping to 0
  298  * ESC is used to abort any changes, RET saves changes.
  299  * The new value is returned.
  300  */
  301 static int
  302 prompt_list(
  303     int row,
  304     int col,
  305     int var,
  306     constext *help_text,
  307     constext *prompt_text,
  308     constext *list[],
  309     int size)
  310 {
  311     int ch, var_orig;
  312     int i, offset, width = 0;
  313     int change;
  314     int adjust = (strcasecmp(_(list[0]), _(txt_default)) == 0);
  315 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  316     char *buf;
  317 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  318 
  319     var += adjust;
  320     var_orig = var;
  321 
  322     /*
  323      * Find the length of longest printable text
  324      */
  325     for (i = 0; i < size; i++)
  326         width = MAX(width, strwidth(_(list[i])));
  327 
  328     show_menu_help(help_text);
  329     cursoron();
  330 
  331     offset = strwidth(_(prompt_text));
  332 
  333     /*
  334      * Make sure to not exceed cCOLS
  335      */
  336     if (offset + width >= cCOLS)
  337         width = cCOLS - offset - 1;
  338 
  339     do {
  340         MoveCursor(row, col + offset);
  341         ch = (char) ReadCh();
  342 
  343         /*
  344          * change:
  345          *   1 = move to the next list element
  346          *   0 = do nothing
  347          *  -1 = move to the previous list element
  348          *
  349          *  if an arrow key was pressed change ch to another value
  350          *  otherwise we will exit the while loop
  351          */
  352         switch (ch) {
  353             case ' ':
  354                 change = 1;
  355                 break;
  356 
  357             case ESC:   /* (ESC) common arrow keys */
  358 #ifdef HAVE_KEY_PREFIX
  359             case KEY_PREFIX:
  360 #endif /* HAVE_KEY_PREFIX */
  361                 switch (get_arrow_key(ch)) {
  362                     case KEYMAP_UP:
  363                         change = -1;
  364                         ch = ' ';
  365                         break;
  366 
  367                     case KEYMAP_DOWN:
  368                         change = 1;
  369                         ch = ' ';
  370                         break;
  371 
  372                     default:
  373                         change = 0;
  374                         break;
  375                 }
  376                 break;
  377 
  378             default:
  379                 change = 0;
  380                 break;
  381         }
  382 
  383         if (change) {
  384             /*
  385              * increment or decrement list, loop around at the limits
  386              */
  387             var += change;
  388             if (var < 0)
  389                 var = size - 1;
  390             else
  391                 var %= (size ? size : 1);
  392 
  393 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  394             if ((buf = spart(_(list[var]), width, TRUE)) != NULL) {
  395                 my_printf("%s", buf);
  396                 free(buf);
  397             } else
  398 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  399                 my_printf("%-*s", width, _(list[var]));
  400             my_flush();
  401         }
  402     } while (ch != '\r' && ch != '\n' && ch != ESC);
  403 
  404     if (ch == ESC) {
  405         var = var_orig;
  406 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  407         if ((buf = spart(_(list[var]), width, TRUE)) != NULL) {
  408             my_printf("%s", buf);
  409             free(buf);
  410         } else
  411 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  412             my_printf("%-*s", width, _(list[var]));
  413         my_flush();
  414     }
  415 
  416     cursoroff();
  417     return (var - adjust);
  418 }
  419 
  420 
  421 /*
  422  * Special case of prompt_option_list() Toggle between ON and OFF
  423  * The function returns TRUE, if the value was changed, FALSE otherwise.
  424  */
  425 t_bool
  426 prompt_option_on_off(
  427     enum option_enum option)
  428 {
  429     char prompt[LEN];
  430     t_bool *variable = OPT_ON_OFF_list[option_table[option].var_index];
  431     t_bool old_value = *variable;
  432 
  433     fmt_option_prompt(prompt, sizeof(prompt), TRUE, option);
  434     *variable = prompt_list(option_row(option), 0, (int) *variable, option_table[option].txt->help, prompt, txt_onoff, 2) ? TRUE : FALSE;
  435     return bool_not(bool_equal(*variable, old_value));
  436 }
  437 
  438 
  439 /*
  440  * The function returns TRUE, if the value was changed, FALSE otherwise.
  441  */
  442 t_bool
  443 prompt_option_list(
  444     enum option_enum option)
  445 {
  446     char prompt[LEN];
  447     int *variable = option_table[option].variable;
  448     int old_value = *variable;
  449     int opt_count = 0;
  450 
  451     while (option_table[option].opt_list[opt_count] != NULL)
  452         ++opt_count;
  453     fmt_option_prompt(prompt, sizeof(prompt), TRUE, option);
  454     *variable = prompt_list(option_row(option), 0, *variable, option_table[option].txt->help, prompt, option_table[option].opt_list, opt_count);
  455     return *variable != old_value;
  456 }
  457 
  458 
  459 /*
  460  * Displays option text and actual option value for string based options in
  461  * one line, help text for that option near the bottom of the screen. Allows
  462  * change of the old value by normal editing; history function of tin_getline()
  463  * will be used properly so that editing won't leave the actual line.
  464  *
  465  * The function returns TRUE, if the value was changed, FALSE otherwise.
  466  */
  467 t_bool
  468 prompt_option_string(
  469     enum option_enum option) /* return value is always ignored */
  470 {
  471     char *variable = OPT_STRING_list[option_table[option].var_index];
  472     char prompt[LEN];
  473     char old_value[LEN];
  474 
  475     STRCPY(old_value, variable);
  476     show_menu_help(option_table[option].txt->help);
  477     fmt_option_prompt(prompt, sizeof(prompt) - 1, TRUE, option);
  478     if (prompt_menu_string(option_row(option), prompt, variable))
  479         return strcmp(old_value, variable) ? TRUE : FALSE;
  480     else
  481         return FALSE;
  482 }
  483 
  484 
  485 /*
  486  * Displays option text and current option value for number based options in
  487  * one line, help text for that option near the bottom of the screen. Allows
  488  * change of the old value by normal editing; history function of tin_getline()
  489  * will be used properly so that editing won't leave the current line.
  490  *
  491  * The function returns TRUE if the value was changed, FALSE otherwise.
  492  */
  493 t_bool
  494 prompt_option_num(
  495     enum option_enum option) /* return value is always ignored */
  496 {
  497     char prompt[LEN];
  498     char number[LEN];
  499     char *p;
  500     int num;
  501 
  502     show_menu_help(option_table[option].txt->help);
  503     MoveCursor(option_row(option), 0);
  504     fmt_option_prompt(prompt, sizeof(prompt) - 1, TRUE, option);
  505     snprintf(&number[0], sizeof(number), "%d", *(option_table[option].variable));
  506 
  507     if ((p = tin_getline(prompt, 2, number, 0, FALSE, HIST_OTHER)) == NULL)
  508         return FALSE;
  509 
  510     STRCPY(number, p);
  511     num = atoi(number);
  512     *(option_table[option].variable) = num;
  513     clear_message();
  514     return TRUE;
  515 }
  516 
  517 
  518 /*
  519  * Displays option text and actual option value for character based options
  520  * in one line, help text for that option near the bottom of the screen.
  521  * Allows change of the old value by normal editing.
  522  *
  523  * The function returns TRUE if the value was changed, FALSE otherwise.
  524  */
  525 t_bool
  526 prompt_option_char(
  527     enum option_enum option) /* return value is always ignored */
  528 {
  529     char prompt[LEN];
  530 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  531     wchar_t input[2];
  532     wchar_t *variable = OPT_CHAR_list[option_table[option].var_index];
  533     int max_chars = (int) sizeof(wchar_t) + 1;
  534     wchar_t *wp;
  535     char *p;
  536     char *curr_val;
  537 #else
  538     char input[2];
  539     char *variable = OPT_CHAR_list[option_table[option].var_index];
  540     char *p;
  541     char *curr_val = &input[0];
  542     int max_chars = 1;
  543 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  544 
  545     input[0] = *variable;
  546     input[1] = '\0';
  547 
  548 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  549     if ((curr_val = wchar_t2char(input))) {
  550 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  551 
  552     do {
  553         show_menu_help(option_table[option].txt->help);
  554         MoveCursor(option_row(option), 0);
  555         fmt_option_prompt(prompt, sizeof(prompt) - 1, TRUE, option);
  556 
  557         if ((p = tin_getline(prompt, 0, curr_val, max_chars, FALSE, HIST_OTHER)) == NULL) {
  558             clear_message();
  559             return FALSE;
  560         }
  561         if (!*p)
  562             info_message(_(txt_info_enter_valid_character));
  563     } while (!*p);
  564 
  565 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  566         if ((wp = char2wchar_t(p))) {
  567             *variable = wp[0];
  568             free(wp);
  569         }
  570         free(curr_val);
  571     }
  572 #else
  573     *variable = p[0];
  574 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  575 
  576     clear_message();
  577     return TRUE;
  578 }
  579 
  580 
  581 /*
  582  * Get a string. Make it the new default.
  583  * If none given, use the default.
  584  * Return the string or NULL if we can't get anything useful
  585  */
  586 char *
  587 prompt_string_default(
  588     const char *prompt,
  589     char *def,
  590     const char *failtext,
  591     int history)
  592 {
  593     char pattern[LEN];
  594 
  595     clear_message();
  596 
  597     if (!prompt_string(prompt, pattern, history)) {
  598         clear_message();
  599         return NULL;
  600     }
  601 
  602     if (pattern[0] != '\0')         /* got a string - make it the default */
  603         my_strncpy(def, pattern, LEN);
  604     else {
  605         if (def[0] == '\0') {       /* no default - give up */
  606             error_message(2, "%s", failtext);
  607             return NULL;
  608         }
  609     }
  610 
  611     return def;                 /* use the default */
  612 }
  613 
  614 
  615 /*
  616  * Get a message ID for the 'L' command. Add <> if needed
  617  * If the msgid exists and is reachable, return its index
  618  * in arts[], else ART_UNAVAILABLE
  619  */
  620 int
  621 prompt_msgid(
  622     void)
  623 {
  624     char buf[LEN];
  625 
  626     if (prompt_string(_(txt_enter_message_id), buf + 1, HIST_MESSAGE_ID) && buf[1]) {
  627         char *ptr = str_trim(buf + 1);
  628         struct t_msgid *msgid;
  629 
  630         /*
  631          * If the user failed to supply Message-ID in <>, add them
  632          */
  633         if (buf[1] != '<') {
  634             buf[0] = '<';
  635             strcat(buf, ">");
  636             ptr = buf;
  637         }
  638 
  639         if ((msgid = find_msgid(ptr)) == NULL) {
  640             info_message(_(txt_art_unavailable));
  641             return ART_UNAVAILABLE;
  642         }
  643 
  644         /*
  645          * Is it expired or otherwise not on the spool ?
  646          */
  647         if (msgid->article == ART_UNAVAILABLE) {
  648             info_message(_(txt_art_unavailable));
  649             return ART_UNAVAILABLE;
  650         }
  651 
  652         /*
  653          * If the article is no longer part of a thread, then there is
  654          * no way to display it
  655          */
  656         if (which_thread(msgid->article) == -1) {
  657             info_message(_(txt_no_last_message));
  658             return ART_UNAVAILABLE;
  659         }
  660 
  661         return msgid->article;
  662     }
  663 
  664     return ART_UNAVAILABLE;
  665 }
  666 
  667 
  668 /*
  669  * Format a message such that it'll fit within the screen width
  670  * Useful for fitting long Subjects and newsgroup names into prompts
  671  * result will contain a pointer to the malloced memory containing the
  672  * sized message
  673  */
  674 char *
  675 sized_message(
  676     char **result,
  677     const char *format,
  678     const char *subject)
  679 {
  680     char *buf;
  681     int max_len;
  682 
  683     max_len = cCOLS - strwidth(format) + 2 - 1; /* The formatting info (%s) wastes 2 chars, but our prompt needs 1 char */
  684 
  685     buf = strunc(subject, max_len);
  686 
  687     *result = fmt_string(format, buf);
  688     free(buf);
  689 
  690     return *result;
  691 }
  692 
  693 
  694 /*
  695  * Implement the Single-Letter-Key mini menus at the bottom of the screen
  696  * eg, Press a)ppend, o)verwrite, q)uit :
  697  */
  698 t_function
  699 prompt_slk_response(
  700     t_function default_func,
  701     const struct keylist keys,
  702     const char *fmt,
  703     ...)
  704 {
  705     va_list ap;
  706     char buf[LEN];
  707     t_function func;
  708 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  709     wchar_t ch;
  710 #else
  711     char ch;
  712 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  713 
  714     va_start(ap, fmt);
  715     vsnprintf(buf, sizeof(buf), fmt, ap);
  716     va_end(ap);
  717 
  718     prompt_slk_message = my_malloc(strlen(buf) + 2);
  719 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  720     {
  721         char *tmp;
  722         wchar_t wtmp[2] = { '\0', '\0' };
  723 
  724         wtmp[0] = func_to_key(default_func, keys);
  725         tmp = wchar_t2char(wtmp);
  726         snprintf(prompt_slk_message, strlen(buf) + 2, "%s%s", buf, tmp);
  727         FreeIfNeeded(tmp);
  728     }
  729 #else
  730     snprintf(prompt_slk_message, strlen(buf) + 2, "%s%c", buf, func_to_key(default_func, keys));
  731 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  732 
  733     input_context = cPromptSLK;
  734 
  735     do {
  736         prompt_slk_redraw();        /* draw the prompt */
  737 
  738 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  739         if ((ch = (wchar_t) ReadWch()) == '\r' || ch == '\n')
  740 #else
  741         if ((ch = ReadCh()) == '\r' || ch == '\n')
  742 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  743             func = default_func;
  744         else
  745             func = key_to_func(ch, keys);
  746 
  747 #if 1
  748         /*
  749          * ignore special-keys which are represented as a multibyte ESC-seq
  750          * to avoid interpreting them as 'ESC' only
  751          */
  752         if (ch == ESC) {
  753             switch (get_arrow_key(ch)) {
  754                 case KEYMAP_UP:
  755                 case KEYMAP_DOWN:
  756                 case KEYMAP_LEFT:
  757                 case KEYMAP_RIGHT:
  758                 case KEYMAP_PAGE_DOWN:
  759                 case KEYMAP_PAGE_UP:
  760                 case KEYMAP_HOME:
  761                 case KEYMAP_END:
  762                     ch = '\0';
  763                     func = NOT_ASSIGNED;
  764                     break;
  765 
  766                 default:
  767                     break;
  768             }
  769         }
  770 #endif /* 1 */
  771     } while (func == NOT_ASSIGNED);
  772 
  773     input_context = cNone;
  774     FreeAndNull(prompt_slk_message);
  775 
  776     clear_message();
  777     return func;
  778 }
  779 
  780 
  781 /* (Re)draws the prompt message for prompt_slk_response() */
  782 void
  783 prompt_slk_redraw(
  784     void)
  785 {
  786     int column;
  787 
  788     wait_message(0, "%s", prompt_slk_message);
  789 
  790     /* get the cursor _just_ right */
  791     column = strwidth(prompt_slk_message) - 1;
  792     MoveCursor(cLINES, column);
  793 }
  794 
  795 
  796 /*
  797  * Wait until a key is pressed. We specify the <RETURN> key otherwise
  798  * pedants will point out that:
  799  * i)  There is no 'any' key on a keyboard
  800  * ii) CTRL, SHIFT etc don't work
  801  */
  802 void
  803 prompt_continue(
  804     void)
  805 {
  806     int ch;
  807     int save_signal_context = signal_context;
  808 
  809     cmd_line = TRUE;
  810     info_message(_(txt_return_key));
  811     signal_context = cMain;
  812     input_context = cPromptCONT;
  813 
  814     switch ((ch = ReadCh())) {
  815         case ESC:
  816 #ifdef HAVE_KEY_PREFIX
  817         case KEY_PREFIX:
  818 #endif /* HAVE_KEY_PREFIX */
  819             (void) get_arrow_key(ch);
  820             /* FALLTHROUGH */
  821 
  822         default:
  823             break;
  824     }
  825 
  826     input_context = cNone;
  827     signal_context = save_signal_context;
  828 
  829 #ifdef USE_CURSES
  830     my_fputc('\n', stdout);
  831 #endif /* USE_CURSES */
  832     cmd_line = FALSE;
  833 #ifdef USE_CURSES
  834     my_retouch();
  835 #endif /* USE_CURSES */
  836 }