NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
question.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <langinfo.h>
33#include <limits.h>
34#include <stdbool.h>
35#include <stdint.h>
36#include <string.h>
37#include "mutt/lib.h"
38#include "config/lib.h"
39#include "gui/lib.h"
40#include "color/lib.h"
41#include "key/lib.h"
42#ifdef HAVE_SYS_PARAM_H
43#include <sys/param.h> // IWYU pragma: keep
44#endif
45
62int mw_multi_choice(const char *prompt, const char *letters)
63{
64 if (!prompt || !letters)
65 return -1;
66
67 struct MuttWindow *win = msgwin_new(true);
68 if (!win)
69 return -1;
70
71 int choice = 0;
72
73 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
74 const struct AttrColor *ac_prompt = merged_color_overlay(ac_normal,
76
78 {
79 const struct AttrColor *ac_opts = merged_color_overlay(ac_prompt,
81 char *cur = NULL;
82
83 while ((cur = strchr(prompt, '(')))
84 {
85 // write the part between prompt and cur using MT_COLOR_PROMPT
86 msgwin_add_text_n(win, prompt, cur - prompt, ac_prompt);
87
88 if (mutt_isalnum(cur[1]) && (cur[2] == ')'))
89 {
90 // we have a single letter within parentheses - MT_COLOR_OPTIONS
91 msgwin_add_text_n(win, cur + 1, 1, ac_opts);
92 prompt = cur + 3;
93 }
94 else
95 {
96 // we have a parenthesis followed by something else
97 msgwin_add_text_n(win, cur, 1, ac_prompt);
98 prompt = cur + 1;
99 }
100 }
101 }
102
103 msgwin_add_text(win, prompt, ac_prompt);
104 msgwin_add_text(win, " ", ac_normal);
105
107 struct MuttWindow *old_focus = window_set_focus(win);
108 window_redraw(win);
109
110 // ---------------------------------------------------------------------------
111 // Event Loop
112 struct KeyEvent event = { 0, OP_NULL };
113 while (true)
114 {
115 event = mutt_getch(GETCH_NO_FLAGS);
116 mutt_debug(LL_DEBUG1, "mw_multi_choice: EVENT(%d,%d)\n", event.ch, event.op);
117
118 if (event.op == OP_REPAINT)
119 window_redraw(NULL);
120
121 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
122 continue;
123
124 if ((event.op == OP_ABORT) || key_is_return(event.ch))
125 {
126 choice = -1;
127 break;
128 }
129
130 char *p = (event.ch != 0) ? strchr(letters, event.ch) : NULL;
131 if (p)
132 {
133 choice = p - letters + 1;
134 break;
135 }
136
137 if ((event.ch > '0') && (event.ch <= '9'))
138 {
139 choice = event.ch - '0';
140 if (choice <= mutt_str_len(letters))
141 break;
142 }
143 }
144 // ---------------------------------------------------------------------------
145
146 win = msgcont_pop_window();
147 window_set_focus(old_focus);
148 mutt_window_free(&win);
149
150 return choice;
151}
152
176static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
177 struct ConfigDef *cdef, GetChFlags flags)
178{
179 struct MuttWindow *win = msgwin_new(true);
180 if (!win)
181 return MUTT_ABORT;
182
183 char *yes = N_("yes");
184 char *no = N_("no");
185 char *trans_yes = _(yes);
186 char *trans_no = _(no);
187
188 regex_t reyes = { 0 };
189 regex_t reno = { 0 };
190
191 bool reyes_ok = false;
192 bool reno_ok = false;
193
194#ifdef OpenBSD
195 /* OpenBSD only supports locale C and UTF-8
196 * so there is no suitable base system's locale identification
197 * Remove this code immediately if this situation changes! */
198 char rexyes[16] = "^[+1YyYy]";
199 rexyes[6] = mutt_toupper(trans_yes[0]);
200 rexyes[7] = mutt_tolower(trans_yes[0]);
201
202 char rexno[16] = "^[-0NnNn]";
203 rexno[6] = mutt_toupper(trans_no[0]);
204 rexno[7] = mutt_tolower(trans_no[0]);
205
206 if (REG_COMP(&reyes, rexyes, REG_NOSUB) == 0)
207 reyes_ok = true;
208
209 if (REG_COMP(&reno, rexno, REG_NOSUB) == 0)
210 reno_ok = true;
211
212#else
213 char *expr = NULL;
214 reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
215 (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
216 reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
217 (REG_COMP(&reno, expr, REG_NOSUB) == 0);
218#endif
219
220 if ((yes != trans_yes) && (no != trans_no) && reyes_ok && reno_ok)
221 {
222 // If all parts of the translation succeeded...
223 yes = trans_yes;
224 no = trans_no;
225 }
226 else
227 {
228 // otherwise, fallback to English
229 if (reyes_ok)
230 {
231 regfree(&reyes);
232 reyes_ok = false;
233 }
234 if (reno_ok)
235 {
236 regfree(&reno);
237 reno_ok = false;
238 }
239 }
240
241 bool show_help_prompt = cdef;
242
243 struct Buffer *text = buf_pool_get();
244 buf_printf(text, "%s ([%s]/%s%s): ", prompt, (def == MUTT_YES) ? yes : no,
245 (def == MUTT_YES) ? no : yes, show_help_prompt ? "/?" : "");
246
249 struct MuttWindow *old_focus = window_set_focus(win);
250
251 struct KeyEvent event = { 0, OP_NULL };
252 window_redraw(NULL);
253 while (true)
254 {
255 event = mutt_getch(flags);
256 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
257 {
258 window_redraw(NULL);
259 mutt_refresh();
260 continue;
261 }
262
263 if (key_is_return(event.ch))
264 break; // Do nothing, use default
265
266 if (event.op == OP_ABORT)
267 {
268 def = MUTT_ABORT;
269 break;
270 }
271
272 char answer[4] = { 0 };
273 answer[0] = event.ch;
274 if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (mutt_tolower(event.ch) == 'y'))
275 {
276 def = MUTT_YES;
277 break;
278 }
279 if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (mutt_tolower(event.ch) == 'n'))
280 {
281 def = MUTT_NO;
282 break;
283 }
284 if (show_help_prompt && (event.ch == '?'))
285 {
286 show_help_prompt = false;
288 buf_printf(text, "$%s - %s\n", cdef->name, cdef->docs);
289
290 char hyphen[128] = { 0 };
291 mutt_str_hyphenate(hyphen, sizeof(hyphen), cdef->name);
292 buf_add_printf(text, "https://neomutt.org/guide/reference#%s\n", hyphen);
293
295
296 buf_printf(text, "%s ([%s]/%s): ", prompt, (def == MUTT_YES) ? yes : no,
297 (def == MUTT_YES) ? no : yes);
299 msgwin_add_text(win, NULL, NULL);
300
301 window_redraw(NULL);
302 mutt_refresh();
303 }
304
305 mutt_beep(false);
306 }
307
308 win = msgcont_pop_window();
309 window_set_focus(old_focus);
310 mutt_window_free(&win);
311
312 if (reyes_ok)
313 regfree(&reyes);
314 if (reno_ok)
315 regfree(&reno);
316
317 buf_pool_release(&text);
318 return def;
319}
320
329enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
330{
331 return mw_yesorno(prompt, def, NULL, GETCH_NO_FLAGS);
332}
333
342enum QuadOption query_yesorno_ignore_macro(const char *prompt, enum QuadOption def)
343{
344 return mw_yesorno(prompt, def, NULL, GETCH_IGNORE_MACRO);
345}
346
357enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
358 struct ConfigSubset *sub, const char *name)
359{
360 struct HashElem *he = cs_subset_create_inheritance(sub, name);
361 if (!he)
362 return mw_yesorno(prompt, def, NULL, GETCH_NO_FLAGS);
363
364 struct HashElem *he_base = cs_get_base(he);
365 ASSERT(CONFIG_TYPE(he_base->type) == DT_BOOL);
366
367 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
368 ASSERT(value != INT_MIN);
369
370 struct ConfigDef *cdef = he_base->data;
371 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
372}
373
384enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
385{
386 struct HashElem *he = cs_subset_create_inheritance(sub, name);
387 if (!he)
388 return mw_yesorno(prompt, MUTT_NO, NULL, GETCH_NO_FLAGS);
389
390 struct HashElem *he_base = cs_get_base(he);
391 ASSERT(CONFIG_TYPE(he_base->type) == DT_QUAD);
392
393 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
394 ASSERT(value != INT_MIN);
395
396 if ((value == MUTT_YES) || (value == MUTT_NO))
397 return value;
398
399 struct ConfigDef *cdef = he_base->data;
400 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
401 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
402}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
Color and attribute parsing.
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition simple.c:116
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition simple.c:95
@ MT_COLOR_OPTIONS
Options in prompt.
Definition color.h:54
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:53
@ MT_COLOR_PROMPT
Question/user input.
Definition color.h:56
Convenience wrapper for the config headers.
struct HashElem * cs_get_base(struct HashElem *he)
Find the root Config Item.
Definition set.c:161
int mutt_toupper(int arg)
Wrapper for toupper(3)
Definition ctype.c:140
bool mutt_isalnum(int arg)
Wrapper for isalnum(3)
Definition ctype.c:40
int mutt_tolower(int arg)
Wrapper for tolower(3)
Definition ctype.c:126
void mutt_refresh(void)
Force a refresh of the screen.
Definition curs_lib.c:78
void mutt_beep(bool force)
Irritate the user.
Definition curs_lib.c:68
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition get.c:201
uint8_t GetChFlags
Flags for mutt_getch(), e.g. GETCH_NO_FLAGS.
Definition get.h:33
#define GETCH_IGNORE_MACRO
Don't use MacroEvents.
Definition get.h:35
#define GETCH_NO_FLAGS
No flags are set.
Definition get.h:34
static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def, struct ConfigDef *cdef, GetChFlags flags)
Ask the user a Yes/No question offering help -.
Definition question.c:176
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition question.c:62
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
Convenience wrapper for the gui headers.
Manage keymappings.
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition merged.c:107
void msgcont_push_window(struct MuttWindow *win)
Add a window to the Container Stack.
Definition msgcont.c:100
struct MuttWindow * msgcont_pop_window(void)
Remove the last Window from the Container Stack.
Definition msgcont.c:57
struct MuttWindow * msgwin_new(bool interactive)
Create the Message Window.
Definition msgwin.c:370
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:518
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition msgwin.c:418
void msgwin_add_text_n(struct MuttWindow *win, const char *text, int bytes, const struct AttrColor *ac_color)
Add some text to the Message Window.
Definition msgwin.c:449
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition msgwin.c:483
Convenience wrapper for the library headers.
#define N_(a)
Definition message.h:32
#define _(a)
Definition message.h:28
void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
Hyphenate a snake-case string.
Definition string.c:854
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
#define key_is_return(ch)
Definition mutt_curses.h:57
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
#define OP_TIMEOUT
1 second with no events
Definition opcodes.h:35
#define OP_REPAINT
Repaint is needed.
Definition opcodes.h:34
#define OP_ABORT
$abort_key pressed (Ctrl-G)
Definition opcodes.h:36
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
QuadOption
Possible values for a quad-option.
Definition quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
@ MUTT_ASKYES
Ask the user, defaulting to 'Yes'.
Definition quad.h:41
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
enum QuadOption query_yesorno_ignore_macro(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question ignoring the macro buffer.
Definition question.c:342
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition question.c:357
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
#define ASSERT(COND)
Definition signal2.h:59
A curses colour and its attributes.
Definition attr.h:65
String manipulation buffer.
Definition buffer.h:36
const char * name
User-visible name.
Definition set.h:66
const char * docs
One-liner description.
Definition set.h:84
A set of inherited config items.
Definition subset.h:46
The item stored in a Hash Table.
Definition hash.h:44
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition hash.h:45
void * data
User-supplied data.
Definition hash.h:47
An event such as a keypress.
Definition get.h:50
int op
Function opcode, e.g. OP_HELP.
Definition get.h:52
int ch
Raw key pressed.
Definition get.h:51
intptr_t cs_subset_he_native_get(const struct ConfigSubset *sub, struct HashElem *he, struct Buffer *err)
Natively get the value of a HashElem config item.
Definition subset.c:264
struct HashElem * cs_subset_create_inheritance(const struct ConfigSubset *sub, const char *name)
Create a Subset config item (inherited)
Definition subset.c:214
#define CONFIG_TYPE(t)
Extract the type from the flags.
Definition types.h:50
@ DT_BOOL
boolean option
Definition types.h:32
@ DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition types.h:40