* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / alpine / osdep / termin.gen.c
blob0179e524f31c7f3f04a85d4a7790aa7027c9367d
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 #include <system.h>
15 #include <general.h>
17 #include "../../c-client/mail.h" /* for MAILSTREAM and friends */
18 #include "../../c-client/osdep.h"
19 #include "../../c-client/rfc822.h" /* for soutr_t and such */
20 #include "../../c-client/misc.h" /* for cpystr proto */
21 #include "../../c-client/utf8.h" /* for CHARSET and such*/
22 #include "../../c-client/imap4r1.h"
24 #include "../../pith/osdep/color.h"
26 #include "../../pith/charconv/utf8.h"
28 #include "../../pith/debug.h"
29 #include "../../pith/newmail.h"
30 #include "../../pith/conf.h"
31 #include "../../pith/busy.h"
33 #include "../../pico/estruct.h"
34 #include "../../pico/pico.h"
35 #include "../../pico/keydefs.h"
37 #include "../../pico/osdep/color.h"
39 #include "../status.h"
40 #include "../folder.h"
41 #include "../keymenu.h"
42 #include "../send.h"
43 #include "../radio.h"
44 #include "../busy.h"
46 #ifdef _WINDOWS
47 #include "../../pico/osdep/mswin.h"
48 #include "termin.wnt.h"
49 #include "termout.wnt.h"
50 #else
51 #include "termout.unx.h"
52 #endif
54 #include "termin.gen.h"
55 #include "termout.gen.h"
57 #include "../mailcmd.h"
60 #ifdef _WINDOWS
61 static int g_mc_row, g_mc_col;
63 int pcpine_oe_cursor(int, long);
64 #endif
68 * Generic tty input routines
72 /*----------------------------------------------------------------------
73 Read a character from keyboard with timeout
74 Input: none
76 Result: Returns command read via read_char
77 Times out and returns a null command every so often
79 Calculates the timeout for the read, and does a few other house keeping
80 things. The duration of the timeout is set in pine.c.
81 ----------------------------------------------------------------------*/
82 UCS
83 read_command(char **utf8str)
85 int tm = 0, more_freq_timeo;
86 UCS ucs;
87 long dtime;
88 static unsigned char utf8buf[7];
89 unsigned char *newdestp;
92 * timeo is the mail-check-interval. What we want to do (ignoring the
93 * messages_queued part) is timeout more often than timeo but only
94 * check for new mail every timeo or so seconds. The reason we want to
95 * timeout more often is so that we will have a chance to catch the user
96 * in an idle period where we can do a check_point(). Otherwise, with
97 * a default mail-check-interval, we are almost always calling newmail
98 * right after the user presses a key, making it the worst possible
99 * time to do a checkpoint.
102 more_freq_timeo = MIN(get_input_timeout(), IDLE_TIMEOUT);
103 if(more_freq_timeo == 0)
104 more_freq_timeo = IDLE_TIMEOUT;
106 cancel_busy_cue(-1);
107 tm = (messages_queued(&dtime) > 1) ? (int)dtime : more_freq_timeo;
109 if(utf8str)
110 *utf8str = NULL;
112 ucs = read_char(tm);
113 if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE)
114 zero_new_mail_count();
116 #ifdef BACKGROUND_POST
118 * Any expired children to report on?
120 if(ps_global->post && ps_global->post->pid == 0){
121 int winner = 0;
123 if(ps_global->post->status < 0){
124 q_status_message(SM_ORDER | SM_DING, 3, 3, "Abysmal failure!");
126 else{
127 (void) pine_send_status(ps_global->post->status,
128 ps_global->post->fcc, tmp_20k_buf, SIZEOF_20KBUF,
129 &winner);
130 q_status_message(SM_ORDER | (winner ? 0 : SM_DING), 3, 3,
131 tmp_20k_buf);
135 if(!winner)
136 q_status_message(SM_ORDER, 0, 3,
137 "Re-send via \"Compose\" then \"Yes\" to \"Continue INTERRUPTED?\"");
139 if(ps_global->post->fcc)
140 fs_give((void **) &ps_global->post->fcc);
142 fs_give((void **) &ps_global->post);
144 #endif
147 * The character we get from read_char() is a UCS-4 char. Or it could be a special
148 * value like KEY_UP or NO_OP_IDLE or something similar. From here on out
149 * we're going to operate with UTF-8 internally. This is the point where we
150 * convert the UCS-4 input (actually whatever sort of input the user is typing
151 * was converted to UCS-4 first) to UTF-8. It's easy in this read_command
152 * case because if user types a non-ascii character as a command it's going to be
153 * an error. All commands are ascii. In order to present a reasonable error
154 * message we pass back the UTF-8 string to the caller.
156 if(ucs >= 0x80 && ucs < KEY_BASE){
158 * User typed a character that is non-ascii. Convert it to
159 * UTF-8.
161 memset(utf8buf, 0, sizeof(utf8buf));
162 newdestp = utf8_put(utf8buf, (unsigned long) ucs);
163 if(newdestp - utf8buf > 1){ /* this should happen */
164 if(utf8str)
165 *utf8str = (char *) utf8buf;
167 dprint((9, "Read command: looks like user typed non-ascii command 0x%x %s: returning KEY_UTF8\n", ucs, pretty_command(ucs)));
168 ucs = KEY_UTF8;
170 else{
171 dprint((9, "Read command: looks like user typed unknown, non-ascii command 0x%x %s: returning KEY_UNKNOWN\n", ucs, pretty_command(ucs)));
172 ucs = KEY_UNKNOWN; /* best we can do, shouldn't happen */
175 else{
176 dprint((9, "Read command returning: 0x%x %s\n", ucs, pretty_command(ucs)));
179 return(ucs);
184 read_command_prep()
186 int i;
187 char *fname;
188 MAILSTREAM *m;
191 * Before we sniff at the input queue, make sure no external event's
192 * changed our picture of the message sequence mapping. If so,
193 * recalculate the dang thing and run thru whatever processing loop
194 * we're in again...
196 for(i = 0; i < ps_global->s_pool.nstream; i++){
197 m = ps_global->s_pool.streams[i];
198 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
199 && sp_expunge_count(m)){
200 fname = STREAMNAME(m);
201 q_status_message3(SM_ORDER, 3, 3,
202 "%s message%s expunged from folder \"%s\"",
203 long2string(sp_expunge_count(m)),
204 plural(sp_expunge_count(m)),
205 pretty_fn(fname));
206 sp_set_expunge_count(m, 0L);
207 display_message('x');
211 if(sp_mail_box_changed(ps_global->mail_stream)
212 && sp_new_mail_count(ps_global->mail_stream)){
213 dprint((2, "Noticed %ld new msgs! \n",
214 sp_new_mail_count(ps_global->mail_stream)));
215 return(FALSE); /* cycle thru so caller can update */
218 return(TRUE);
222 /*----------------------------------------------------------------------
223 Prompt user for a string in status line with various options
225 Args: utf8string -- the buffer result is returned in, and original string (if
226 any) is passed in.
227 y_base -- y position on screen to start on. 0,0 is upper left
228 negative numbers start from bottom
229 x_base -- column position on screen to start on. 0,0 is upper left
230 utf8string_size -- Length of utf8string buffer
231 utf8prompt -- The string to prompt with
232 escape_list -- pointer to array of ESCKEY_S's. input chars matching
233 those in list return value from list.
234 help -- Array of strings for help text in bottom screen lines
235 flags -- pointer (because some are return values) to flags
236 OE_USER_MODIFIED - Set on return if user modified buffer
237 OE_DISALLOW_CANCEL - No cancel in menu.
238 OE_DISALLOW_HELP - No help in menu.
239 OE_KEEP_TRAILING_SPACE - Allow trailing space.
240 OE_SEQ_SENSITIVE - Caller is sensitive to sequence
241 number changes.
242 OE_APPEND_CURRENT - String should not be truncated
243 before accepting user input.
244 OE_PASSWD - Don't echo on screen.
246 Result: editing input string
247 returns -1 unexpected errors
248 returns 0 normal entry typed (editing and return or PF2)
249 returns 1 typed ^C or PF2 (cancel)
250 returns 3 typed ^G or PF1 (help)
251 returns 4 typed ^L for a screen redraw
253 WARNING: Care is required with regard to the escape_list processing.
254 The passed array is terminated with an entry that has ch = -1.
255 Function key labels and key strokes need to be setup externally!
256 Traditionally, a return value of 2 is used for ^T escapes.
258 Unless in escape_list, tabs are trapped by isprint().
259 This allows near full weemacs style editing in the line
260 ^A beginning of line
261 ^E End of line
262 ^R Redraw line
263 ^G Help
264 ^F forward
265 ^B backward
266 ^D delete
267 ----------------------------------------------------------------------*/
270 optionally_enter(char *utf8string, int y_base, int x_base, int utf8string_size,
271 char *utf8prompt, ESCKEY_S *escape_list, HelpType help, int *flags)
273 UCS *string = NULL, ucs;
274 size_t string_size;
275 UCS *s2;
276 UCS *saved_original = NULL;
277 char *candidate;
278 UCS *kill_buffer = NULL;
279 UCS *k, *kb;
280 int field_pos; /* offset into array dline.vl */
281 int i, j, return_v, cols, prompt_width, too_thin,
282 real_y_base, km_popped, passwd;
283 char **help_text;
284 long fkey_table[12];
285 struct key_menu *km;
286 bitmap_t bitmap;
287 COLOR_PAIR *lastc = NULL, *promptc = NULL;
288 struct variable *vars = ps_global->vars;
289 struct display_line dline;
290 #ifdef _WINDOWS
291 int cursor_shown;
292 #endif
294 dprint((5, "=== optionally_enter called ===\n"));
295 dprint((9, "utf8string:\"%s\" y:%d x:%d length: %d append: %d\n",
296 utf8string ? utf8string : "",
297 x_base, y_base, utf8string_size,
298 (flags && *flags & OE_APPEND_CURRENT)));
299 dprint((9, "passwd:%d utf8prompt:\"%s\" label:\"%s\"\n",
300 (flags && *flags & OE_PASSWD_NOAST) ? 10 :
301 (flags && *flags & OE_PASSWD) ? 1 : 0,
302 utf8prompt ? utf8prompt : "",
303 (escape_list && escape_list[0].ch != -1 && escape_list[0].label)
304 ? escape_list[0].label: ""));
306 if(!ps_global->ttyo)
307 return(pre_screen_config_opt_enter(utf8string, utf8string_size, utf8prompt,
308 escape_list, help, flags));
310 #ifdef _WINDOWS
311 if (mswin_usedialog ())
312 return(win_dialog_opt_enter(utf8string, utf8string_size, utf8prompt,
313 escape_list, help, flags));
314 #endif
318 * Utf8string comes in as UTF-8. We'll convert it to a UCS-4 array and operate on
319 * that array, then convert it back before returning. Utf8string_size is the size
320 * of the utf8string array but that doesn't help us much for the array we need to
321 * operate on here. We'll just allocate a big array and then cut it off when
322 * sending it back.
324 * This should come before the specialized calls above but those aren't
325 * converted to use UCS-4 yet.
327 string = utf8_to_ucs4_cpystr(utf8string);
328 dline.vused = ucs4_strlen(string);
330 string_size = (2 * MAX(utf8string_size,dline.vused) + 100);
331 fs_resize((void **) &string, string_size * sizeof(UCS));
333 suspend_busy_cue();
334 cols = ps_global->ttyo->screen_cols;
335 prompt_width = utf8_width(utf8prompt);
336 too_thin = 0;
337 km_popped = 0;
338 if(y_base > 0)
339 real_y_base = y_base;
340 else{
341 real_y_base = y_base + ps_global->ttyo->screen_rows;
342 real_y_base = MAX(real_y_base, 0);
345 flush_ordered_messages();
346 mark_status_dirty();
348 if(flags && *flags & OE_APPEND_CURRENT) /* save a copy in case of cancel */
349 saved_original = ucs4_cpystr(string);
352 * build the function key mapping table, skipping predefined keys...
354 memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(long));
355 for(i = 0, j = 0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
356 if(i+j == OE_HELP_KEY)
357 j++;
359 if(i+j == OE_CANCEL_KEY)
360 j++;
362 if(i+j == OE_ENTER_KEY)
363 j++;
365 fkey_table[i+j] = escape_list[i].ch;
368 /* assumption that HelpType is char ** */
369 help_text = help;
370 if(help_text){ /*---- Show help text -----*/
371 int width = ps_global->ttyo->screen_cols - x_base;
373 if(FOOTER_ROWS(ps_global) == 1){
374 km_popped++;
375 FOOTER_ROWS(ps_global) = 3;
376 clearfooter(ps_global);
378 y_base = -3;
379 real_y_base = y_base + ps_global->ttyo->screen_rows;
382 for(j = 0; j < 2 && help_text[j]; j++){
383 MoveCursor(real_y_base + 1 + j, x_base);
384 CleartoEOLN();
386 if(width < utf8_width(help_text[j])){
387 char *tmp = cpystr(help_text[j]);
388 (void) utf8_truncate(tmp, width);
389 PutLine0(real_y_base + 1 + j, x_base, tmp);
390 fs_give((void **) &tmp);
392 else
393 PutLine0(real_y_base + 1 + j, x_base, help_text[j]);
396 else{
397 clrbitmap(bitmap);
398 clrbitmap((km = &oe_keymenu)->bitmap); /* force formatting */
399 if(!(flags && (*flags) & OE_DISALLOW_HELP))
400 setbitn(OE_HELP_KEY, bitmap);
402 setbitn(OE_ENTER_KEY, bitmap);
403 if(!(flags && (*flags) & OE_DISALLOW_CANCEL))
404 setbitn(OE_CANCEL_KEY, bitmap);
406 setbitn(OE_CTRL_T_KEY, bitmap);
408 /*---- Show the usual possible keys ----*/
409 for(i=0,j=0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
410 if(i+j == OE_HELP_KEY)
411 j++;
413 if(i+j == OE_CANCEL_KEY)
414 j++;
416 if(i+j == OE_ENTER_KEY)
417 j++;
419 oe_keymenu.keys[i+j].label = escape_list[i].label;
420 oe_keymenu.keys[i+j].name = escape_list[i].name;
421 setbitn(i+j, bitmap);
424 for(i = i+j; i < 12; i++)
425 if(!(i == OE_HELP_KEY || i == OE_ENTER_KEY || i == OE_CANCEL_KEY))
426 oe_keymenu.keys[i].name = NULL;
428 draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global), 0, FirstMenu);
431 if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global))
432 ClearLine(real_y_base);
433 if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
434 VAR_PROMPT_BACK_COLOR &&
435 pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
436 pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
437 lastc = pico_get_cur_color();
438 if(lastc){
439 promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
440 VAR_PROMPT_BACK_COLOR);
441 (void)pico_set_colorp(promptc, PSC_NONE);
444 else
445 StartInverse();
448 * if display length isn't wide enough to support input,
449 * shorten up the prompt...
451 if((dline.dwid = cols - (x_base + prompt_width)) < MIN_OPT_ENT_WIDTH){
452 char *p;
453 unsigned got_width;
456 * Scoot prompt pointer forward at least (MIN_OPT_ENT_WIDTH - dline.dwid) screencells.
458 p = utf8_count_forw_width(utf8prompt, MIN_OPT_ENT_WIDTH-dline.dwid, &got_width);
459 if(got_width < MIN_OPT_ENT_WIDTH-dline.dwid)
460 p = utf8_count_forw_width(utf8prompt, MIN_OPT_ENT_WIDTH+1-dline.dwid, &got_width);
462 if(p){
463 prompt_width = utf8_width(p);
464 dline.dwid = cols - (x_base + prompt_width);
465 utf8prompt = p;
470 * How many UCS-4 characters will we need to make up the width dwid? It could be
471 * unlimited because of zero-width characters, I suppose, but realistically it
472 * isn't going to be much more than dwid.
474 dline.dlen = 2 * dline.dwid + 100;
476 dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
477 dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
478 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
479 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
481 dline.movecursor = MoveCursor;
482 dline.writechar = Writewchar;
484 dline.row = real_y_base;
485 dline.col = x_base + prompt_width;
487 dline.vl = string;
488 dline.vlen = --string_size; /* -1 for terminating zero */
489 dline.vbase = field_pos = 0;
491 #ifdef _WINDOWS
492 cursor_shown = mswin_showcaret(1);
493 #endif
495 PutLine0(real_y_base, x_base, utf8prompt);
498 * If appending, position field_pos at end of input.
500 if(flags && *flags & OE_APPEND_CURRENT)
501 while(string[field_pos])
502 field_pos++;
504 passwd = (flags && *flags & OE_PASSWD_NOAST) ? 10 :
505 (flags && *flags & OE_PASSWD) ? 1 : 0;
506 line_paint(field_pos, &dline, &passwd);
508 /*----------------------------------------------------------------------
509 The main loop
510 loops until someone sets the return_v.
511 ----------------------------------------------------------------------*/
512 return_v = -10;
514 while(return_v == -10) {
516 #ifdef MOUSE
517 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
518 register_mfunc(mouse_in_content,
519 real_y_base, x_base + prompt_width,
520 real_y_base, ps_global->ttyo->screen_cols);
521 #endif
522 #ifdef _WINDOWS
523 mswin_allowpaste(MSWIN_PASTE_LINE);
524 g_mc_row = real_y_base;
525 g_mc_col = x_base + prompt_width;
526 mswin_mousetrackcallback(pcpine_oe_cursor);
527 #endif
529 /* Timeout 10 min to keep imap mail stream alive */
530 ps_global->conceal_sensitive_debugging = passwd ? 1 : 0;
531 ucs = read_char(600);
532 ps_global->conceal_sensitive_debugging = 0;
534 #ifdef MOUSE
535 clear_mfunc(mouse_in_content);
536 #endif
537 #ifdef _WINDOWS
538 mswin_allowpaste(MSWIN_PASTE_DISABLE);
539 mswin_mousetrackcallback(NULL);
540 #endif
543 * Don't want to intercept all characters if typing in passwd.
544 * We select an ad hoc set that we will catch and let the rest
545 * through. We would have caught the set below in the big switch
546 * but we skip the switch instead. Still catch things like ^K,
547 * DELETE, ^C, RETURN.
549 if(passwd)
550 switch(ucs){
551 case ctrl('F'):
552 case KEY_RIGHT:
553 case ctrl('B'):
554 case KEY_LEFT:
555 case ctrl('U'):
556 case ctrl('A'):
557 case KEY_HOME:
558 case ctrl('E'):
559 case KEY_END:
560 case TAB:
561 goto ok_for_passwd;
564 if(too_thin && ucs != KEY_RESIZE && ucs != ctrl('Z') && ucs != ctrl('C'))
565 goto bleep;
567 switch(ucs){
569 /*--------------- KEY RIGHT ---------------*/
570 case ctrl('F'):
571 case KEY_RIGHT:
572 if(field_pos >= string_size || string[field_pos] == '\0')
573 goto bleep;
575 line_paint(++field_pos, &dline, &passwd);
576 break;
578 /*--------------- KEY LEFT ---------------*/
579 case ctrl('B'):
580 case KEY_LEFT:
581 if(field_pos <= 0)
582 goto bleep;
584 line_paint(--field_pos, &dline, &passwd);
585 break;
587 /*-------------------- WORD SKIP --------------------*/
588 case ctrl('@'):
590 * Note: read_char *can* return NO_OP_COMMAND which is
591 * the def'd with the same value as ^@ (NULL), BUT since
592 * read_char has a big timeout (>25 secs) it won't.
595 /* skip thru current word */
596 while(string[field_pos]
597 && isalnum((unsigned char) string[field_pos]))
598 field_pos++;
600 /* skip thru current white space to next word */
601 while(string[field_pos]
602 && !isalnum((unsigned char) string[field_pos]))
603 field_pos++;
605 line_paint(field_pos, &dline, &passwd);
606 break;
608 /*-------------------- RETURN --------------------*/
609 case PF4:
610 if(F_OFF(F_USE_FK,ps_global)) goto bleep;
611 case ctrl('J'):
612 case ctrl('M'):
613 return_v = 0;
614 break;
616 /*-------------------- Destructive backspace --------------------*/
617 case '\177': /* DEL */
618 case ctrl('H'):
619 /* Try and do this with by telling the terminal to delete a
620 a character. If that fails, then repaint the rest of the
621 line, achieving the same much less efficiently
623 if(field_pos <= 0)
624 goto bleep;
626 field_pos--;
627 /* drop thru to pull line back ... */
629 /*-------------------- Delete char --------------------*/
630 case ctrl('D'):
631 case KEY_DEL:
632 if(field_pos >= string_size || !string[field_pos])
633 goto bleep;
635 dline.vused--;
636 for(s2 = &string[field_pos]; *s2 != 0; s2++)
637 *s2 = s2[1];
639 *s2 = 0; /* Copy last NULL */
640 line_paint(field_pos, &dline, &passwd);
641 if(flags) /* record change if requested */
642 *flags |= OE_USER_MODIFIED;
644 break;
646 /*--------------- Kill line -----------------*/
647 case ctrl('K'):
648 if(kill_buffer != NULL)
649 fs_give((void **) &kill_buffer);
651 if(field_pos != 0 || string[0]){
652 if(!passwd && F_ON(F_DEL_FROM_DOT, ps_global))
653 dline.vused -= ucs4_strlen(&string[i = field_pos]);
654 else
655 dline.vused = i = 0;
657 kill_buffer = ucs4_cpystr(&string[field_pos = i]);
658 string[field_pos] = '\0';
659 line_paint(field_pos, &dline, &passwd);
660 if(flags) /* record change if requested */
661 *flags |= OE_USER_MODIFIED;
664 break;
666 /*------------------- Undelete line --------------------*/
667 case ctrl('U'):
668 if(kill_buffer == NULL)
669 goto bleep;
671 /* Make string so it will fit */
672 kb = ucs4_cpystr(kill_buffer);
673 if(ucs4_strlen(kb) + ucs4_strlen(string) > string_size)
674 kb[string_size - ucs4_strlen(string)] = '\0';
676 if(string[field_pos] == '\0') {
677 /*--- adding to the end of the string ----*/
678 for(k = kb; *k; k++)
679 string[field_pos++] = *k;
681 string[field_pos] = '\0';
683 else{
684 int shift;
686 shift = ucs4_strlen(kb);
688 /* shift field_pos ... end to right */
689 for(k = &string[field_pos] + ucs4_strlen(&string[field_pos]);
690 k >= &string[field_pos]; k--)
691 *(k+shift) = *k;
693 for(k = kb; *k; k++)
694 string[field_pos++] = *k;
697 if(*kb && flags) /* record change if requested */
698 *flags |= OE_USER_MODIFIED;
700 dline.vused = ucs4_strlen(string);
701 fs_give((void **) &kb);
702 line_paint(field_pos, &dline, &passwd);
703 break;
705 /*-------------------- Interrupt --------------------*/
706 case ctrl('C'): /* ^C */
707 if(F_ON(F_USE_FK,ps_global) || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
708 goto bleep;
710 goto cancel;
712 case PF2:
713 if(F_OFF(F_USE_FK,ps_global) || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
714 goto bleep;
716 cancel:
717 return_v = 1;
718 if(saved_original){
719 for(i = 0; saved_original[i]; i++)
720 string[i] = saved_original[i];
722 string[i] = 0;
725 break;
727 case ctrl('A'):
728 case KEY_HOME:
729 /*-------------------- Start of line -------------*/
730 line_paint(field_pos = 0, &dline, &passwd);
731 break;
733 case ctrl('E'):
734 case KEY_END:
735 /*-------------------- End of line ---------------*/
736 line_paint(field_pos = dline.vused, &dline, &passwd);
737 break;
739 /*-------------------- Help --------------------*/
740 case ctrl('G') :
741 case PF1:
742 if(flags && ((*flags) & OE_DISALLOW_HELP))
743 goto bleep;
744 else if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
745 km_popped++;
746 FOOTER_ROWS(ps_global) = 3;
747 clearfooter(ps_global);
748 if(lastc)
749 (void)pico_set_colorp(lastc, PSC_NONE);
750 else
751 EndInverse();
753 draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global),
754 0, FirstMenu);
756 if(promptc)
757 (void)pico_set_colorp(promptc, PSC_NONE);
758 else
759 StartInverse();
761 mark_keymenu_dirty();
762 y_base = -3;
763 dline.row = real_y_base = y_base + ps_global->ttyo->screen_rows;
764 PutLine0(real_y_base, x_base, utf8prompt);
765 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
766 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
767 line_paint(field_pos, &dline, &passwd);
768 break;
771 if(FOOTER_ROWS(ps_global) > 1){
772 mark_keymenu_dirty();
773 return_v = 3;
775 else
776 goto bleep;
778 break;
781 #ifdef MOUSE
782 /* Mouse support untested in pine 5.00 */
783 case KEY_MOUSE :
785 MOUSEPRESS mp;
786 int w;
788 mouse_get_last (NULL, &mp);
790 switch(mp.button){
791 case M_BUTTON_LEFT : /* position cursor */
792 mp.col -= dline.col;
795 * We have to figure out which character is under the cursor.
796 * This is complicated by the fact that characters may
797 * be other than one cell wide.
800 /* the -1 is for the '<' when text is offscreen left */
801 w = (dline.vbase > 0) ? mp.col-1 : mp.col;
803 if(mp.col <= 0)
804 field_pos = dline.vbase - 1;
805 else{
806 if(dline.vused <= dline.vbase
807 || ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vused-1) <= w)
808 field_pos = dline.vused;
809 else{
811 * Find index of 1st character that causes the
812 * width to be > w.
814 for(i = 0;
815 ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vbase+i) <= w;
816 i++)
819 field_pos = dline.vbase + i;
823 field_pos = MIN(MAX(field_pos, 0), dline.vused);
825 /* just allow line_paint to choose vbase */
826 line_paint(field_pos, &dline, &passwd);
827 break;
829 case M_BUTTON_RIGHT :
830 #ifdef _WINDOWS
833 * Same as M_BUTTON_LEFT except we paste in text after
834 * moving the cursor.
837 mp.col -= dline.col;
839 /* the -1 is for the '<' when text is offscreen left */
840 w = (dline.vbase > 0) ? mp.col-1 : mp.col;
842 if(mp.col <= 0)
843 field_pos = dline.vbase - 1;
844 else{
845 if(dline.vused <= dline.vbase
846 || ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vused-1) <= w)
847 field_pos = dline.vused;
848 else{
850 * Find index of 1st character that causes the
851 * width to be > w.
853 for(i = 0;
854 ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vbase+i) <= w;
855 i++)
858 field_pos = dline.vbase + i;
862 field_pos = MIN(MAX(field_pos, 0), dline.vused);
864 line_paint(field_pos, &dline, &passwd);
866 mswin_allowpaste(MSWIN_PASTE_LINE);
867 mswin_paste_popup();
868 mswin_allowpaste(MSWIN_PASTE_DISABLE);
869 break;
870 #endif
872 case M_BUTTON_MIDDLE : /* NO-OP for now */
873 default: /* just ignore */
874 break;
878 break;
879 #endif
882 case NO_OP_IDLE:
884 * Keep mail stream alive by checking for new mail.
885 * If we're asking for a password in a login prompt
886 * we don't want to check for new_mail because the
887 * new mail check might be what got us here in the first
888 * place (because of a filter trying to save a message).
889 * If we need to wait for the user to come back then
890 * the caller will just have to deal with the failure
891 * to login.
893 i = -1;
894 if(!ps_global->no_newmail_check_from_optionally_enter)
895 i = new_mail(0, 2, NM_DEFER_SORT);
897 if(sp_expunge_count(ps_global->mail_stream) &&
898 flags && ((*flags) & OE_SEQ_SENSITIVE))
899 goto cancel;
901 if(i < 0){
902 line_paint(field_pos, &dline, &passwd);
903 break; /* no changes, get on with life */
905 /* Else fall into redraw */
907 /*-------------------- Redraw --------------------*/
908 case ctrl('L'):
909 /*---------------- re size ----------------*/
910 case KEY_RESIZE:
912 dline.row = real_y_base = y_base > 0 ? y_base :
913 y_base + ps_global->ttyo->screen_rows;
914 if(lastc)
915 (void)pico_set_colorp(lastc, PSC_NONE);
916 else
917 EndInverse();
919 ClearScreen();
920 redraw_titlebar();
921 if(ps_global->redrawer != (void (*)(void))NULL)
922 (*ps_global->redrawer)();
924 redraw_keymenu();
925 if(promptc)
926 (void)pico_set_colorp(promptc, PSC_NONE);
927 else
928 StartInverse();
930 PutLine0(real_y_base, x_base, utf8prompt);
931 cols = ps_global->ttyo->screen_cols;
932 too_thin = 0;
933 if(cols < x_base + prompt_width + 4){
934 Writechar(BELL, 0);
935 PutLine0(real_y_base, 0, "Screen's too thin. Ouch!");
936 too_thin = 1;
938 else{
939 dline.col = x_base + prompt_width;
940 dline.dwid = cols - (x_base + prompt_width);
941 dline.dlen = 2 * dline.dwid + 100;
942 fs_resize((void **) &dline.dl, (size_t) dline.dlen * sizeof(UCS));
943 fs_resize((void **) &dline.olddl, (size_t) dline.dlen * sizeof(UCS));
944 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
945 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
946 line_paint(field_pos, &dline, &passwd);
949 fflush(stdout);
951 dprint((9,
952 "optionally_enter RESIZE new_cols:%d too_thin: %d\n",
953 cols, too_thin));
954 break;
956 case PF3 : /* input to potentially remap */
957 case PF5 :
958 case PF6 :
959 case PF7 :
960 case PF8 :
961 case PF9 :
962 case PF10 :
963 case PF11 :
964 case PF12 :
965 if(F_ON(F_USE_FK,ps_global)
966 && fkey_table[ucs - PF1] != NO_OP_COMMAND)
967 ucs = fkey_table[ucs - PF1]; /* remap function key input */
969 default:
970 if(escape_list){ /* in the escape key list? */
971 for(j=0; escape_list[j].ch != -1; j++){
972 if(escape_list[j].ch == ucs){
973 return_v = escape_list[j].rval;
974 break;
978 if(return_v != -10)
979 break;
982 if(ucs < 0x80 && FILTER_THIS((unsigned char) ucs)){
983 bleep:
984 putc(BELL, stdout);
985 continue;
988 ok_for_passwd:
989 /*--- Insert a character -----*/
990 if(dline.vused >= string_size)
991 goto bleep;
993 /*---- extending the length of the string ---*/
994 for(s2 = &string[++dline.vused]; s2 - string > field_pos; s2--)
995 *s2 = *(s2-1);
997 string[field_pos++] = ucs;
998 line_paint(field_pos, &dline, &passwd);
999 if(flags) /* record change if requested */
1000 *flags |= OE_USER_MODIFIED;
1002 } /*---- End of switch on char ----*/
1005 #ifdef _WINDOWS
1006 if(!cursor_shown)
1007 mswin_showcaret(0);
1008 #endif
1010 if(dline.dl)
1011 fs_give((void **) &dline.dl);
1013 if(dline.olddl)
1014 fs_give((void **) &dline.olddl);
1016 if(saved_original)
1017 fs_give((void **) &saved_original);
1019 if(kill_buffer)
1020 fs_give((void **) &kill_buffer);
1023 * Change string back into UTF-8.
1025 candidate = ucs4_to_utf8_n_cpystr(string, utf8string_size);
1027 if(string)
1028 fs_give((void **) &string);
1030 if(candidate){
1031 strcpy(utf8string, candidate);
1032 fs_give((void **) &candidate);
1035 if (!(flags && (*flags) & OE_KEEP_TRAILING_SPACE))
1036 removing_trailing_white_space(utf8string);
1038 if(lastc){
1039 (void)pico_set_colorp(lastc, PSC_NONE);
1040 free_color_pair(&lastc);
1041 if(promptc)
1042 free_color_pair(&promptc);
1044 else
1045 EndInverse();
1047 MoveCursor(real_y_base, x_base); /* Move the cursor to show we're done */
1048 fflush(stdout);
1049 resume_busy_cue(0);
1050 if(km_popped){
1051 FOOTER_ROWS(ps_global) = 1;
1052 clearfooter(ps_global);
1053 ps_global->mangled_body = 1;
1056 return(return_v);
1060 /*----------------------------------------------------------------------
1061 Check to see if the given command is reasonably valid
1063 Args: ch -- the character to check
1065 Result: A valid command is returned, or a well know bad command is returned.
1067 ---*/
1069 validatekeys(UCS ch)
1071 if(F_ON(F_USE_FK,ps_global)){
1072 if(ch >= 'a' && ch <= 'z')
1073 return(KEY_JUNK);
1075 else{
1076 if(ch >= PF1 && ch <= PF12)
1077 return(KEY_JUNK);
1080 return(ch);
1085 /*----------------------------------------------------------------------
1086 Prepend config'd commands to keyboard input
1088 Args: ch -- pointer to storage for returned command
1090 Returns: TRUE if we're passing back a useful command, FALSE otherwise
1092 ---*/
1094 process_config_input(UCS *ch)
1096 static char firsttime = (char) 1;
1097 int c;
1098 unsigned long octets_so_far, remaining_octets, ret = 0;
1099 unsigned char *inputp;
1100 UCS ucs;
1101 unsigned char inputbuf[20];
1103 /* commands in config file */
1104 if(ps_global->initial_cmds && *ps_global->initial_cmds) {
1106 * There are a few commands that may require keyboard input before
1107 * we enter the main command loop. That input should be interactive,
1108 * not from our list of initial keystrokes.
1110 if(ps_global->dont_use_init_cmds)
1111 return(ret);
1113 c = *ps_global->initial_cmds++;
1116 * Use enough bytes to make up a character and convert it to UCS-4.
1118 if(c < 0x80 || c > KEY_BASE){
1119 *ch = (UCS) c;
1120 ret = 1;
1122 else{
1123 memset(inputbuf, 0, sizeof(inputbuf));
1124 inputbuf[0] = (0xff & c);
1125 octets_so_far = 1;
1127 while(!ret){
1128 remaining_octets = octets_so_far;
1129 inputp = inputbuf;
1130 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
1131 switch(ucs){
1132 case U8G_ENDSTRG: /* incomplete character, wait */
1133 case U8G_ENDSTRI: /* incomplete character, wait */
1134 if(!*ps_global->initial_cmds || octets_so_far >= sizeof(inputbuf)){
1135 *ch = BADESC;
1136 ret = 1;
1138 else
1139 inputbuf[octets_so_far++] = (0xff & *ps_global->initial_cmds++);
1141 break;
1143 default:
1144 if(ucs & U8G_ERROR || ucs == UBOGON)
1145 *ch = BADESC;
1146 else
1147 *ch = ucs;
1149 ret = 1;
1150 break;
1155 if(ps_global->initial_cmds && !*ps_global->initial_cmds && ps_global->free_initial_cmds){
1156 fs_give((void **) &ps_global->free_initial_cmds);
1157 ps_global->initial_cmds = NULL;
1160 return(ret);
1163 if(firsttime) {
1164 firsttime = 0;
1165 if(ps_global->in_init_seq) {
1166 ps_global->in_init_seq = 0;
1167 ps_global->save_in_init_seq = 0;
1168 clear_cursor_pos();
1169 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1170 /* draw screen */
1171 *ch = (UCS) ctrl('L');
1172 return(1);
1176 return(0);
1180 #define TAPELEN 256
1181 static int tape[TAPELEN];
1182 static long recorded = 0L;
1183 static short length = 0;
1187 * record user keystrokes
1189 * Args: ch -- the character to record
1191 * Returns: character recorded
1194 key_recorder(int ch)
1196 tape[recorded++ % TAPELEN] = ch;
1197 if(length < TAPELEN)
1198 length++;
1200 return(ch);
1205 * playback user keystrokes
1207 * Args: ch -- ignored
1209 * Returns: character played back or -1 to indicate end of tape
1212 key_playback(int ch)
1214 ch = length ? tape[(recorded + TAPELEN - length--) % TAPELEN] : -1;
1215 return(ch);
1220 * recent_keystroke - verbose version of key_playback
1223 recent_keystroke(int *cv, char *cs, size_t cslen)
1225 int c;
1227 if((c = key_playback(0)) != -1){
1228 *cv = c;
1229 snprintf(cs, cslen, "%.32s", pretty_command(c));
1230 return(0);
1233 return(-1);
1237 #ifdef _WINDOWS
1239 pcpine_oe_cursor(col, row)
1240 int col;
1241 long row;
1243 return((row == g_mc_row
1244 && col >= g_mc_col
1245 && col < ps_global->ttyo->screen_cols)
1246 ? MSWIN_CURSOR_IBEAM
1247 : MSWIN_CURSOR_ARROW);
1249 #endif