1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ TTY (command line) editing interaction.
3 *@ Because we have multiple line-editor implementations, including our own
4 *@ M(ailx) L(ine) E(ditor), change the file layout a bit and place those
5 *@ one after the other below the other externals.
7 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #ifndef HAVE_AMALGAMATION
29 # include <readline/readline.h>
31 # include <readline/history.h>
35 #if defined HAVE_READLINE || defined HAVE_MLE || defined HAVE_TERMCAP
36 # define a_TTY_SIGNALS
39 /* Shared history support macros */
41 # define a_TTY_HISTFILE(S) \
43 char const *__hist_obsolete = ok_vlook(NAIL_HISTFILE);\
44 if(__hist_obsolete != NULL)\
45 OBSOLETE(_("please use *history-file* instead of *NAIL_HISTFILE*"));\
46 S = ok_vlook(history_file);\
48 (S) = __hist_obsolete;\
50 S = fexpand(S, FEXP_LOCAL | FEXP_NSHELL);\
53 # define a_TTY_HISTSIZE(V) \
55 char const *__hist_obsolete = ok_vlook(NAIL_HISTSIZE);\
56 char const *__sv = ok_vlook(history_size);\
58 if(__hist_obsolete != NULL)\
59 OBSOLETE(_("please use *history-size* instead of *NAIL_HISTSIZE*"));\
61 __sv = __hist_obsolete;\
62 if(__sv == NULL || *__sv == '\0' || (__rv = strtol(__sv, NULL, 10)) == 0)\
70 # define a_TTY_CHECK_ADDHIST(S,NOACT) \
82 # define C_HISTORY_SHARED \
91 if(!asccasecmp(*argv, "show"))\
93 if(!asccasecmp(*argv, "clear"))\
95 if((entry = strtol(*argv, argv, 10)) > 0 && **argv == '\0')\
98 n_err(_("Synopsis: history: %s\n" \
99 "<show> (default), <clear> or select <NO> from editor history"));\
103 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
104 #endif /* HAVE_HISTORY */
106 /* fexpand() flags for expand-on-tab */
107 #define a_TTY_TAB_FEXP_FL (FEXP_FULL | FEXP_SILENT | FEXP_MULTIOK)
110 static sighandler_type a_tty_oint
, a_tty_oquit
, a_tty_oterm
,
112 a_tty_otstp
, a_tty_ottin
, a_tty_ottou
;
116 static void a_tty_sigs_up(void), a_tty_sigs_down(void);
127 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
128 a_tty_oint
= safe_signal(SIGINT
, &n_tty_signal
);
129 a_tty_oquit
= safe_signal(SIGQUIT
, &n_tty_signal
);
130 a_tty_oterm
= safe_signal(SIGTERM
, &n_tty_signal
);
131 a_tty_ohup
= safe_signal(SIGHUP
, &n_tty_signal
);
132 a_tty_otstp
= safe_signal(SIGTSTP
, &n_tty_signal
);
133 a_tty_ottin
= safe_signal(SIGTTIN
, &n_tty_signal
);
134 a_tty_ottou
= safe_signal(SIGTTOU
, &n_tty_signal
);
135 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
140 a_tty_sigs_down(void){
146 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
147 safe_signal(SIGINT
, a_tty_oint
);
148 safe_signal(SIGQUIT
, a_tty_oquit
);
149 safe_signal(SIGTERM
, a_tty_oterm
);
150 safe_signal(SIGHUP
, a_tty_ohup
);
151 safe_signal(SIGTSTP
, a_tty_otstp
);
152 safe_signal(SIGTTIN
, a_tty_ottin
);
153 safe_signal(SIGTTOU
, a_tty_ottou
);
154 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
157 #endif /* a_TTY_SIGNALS */
159 static sigjmp_buf a_tty__actjmp
; /* TODO someday, we won't need it no more */
161 a_tty__acthdl(int s
) /* TODO someday, we won't need it no more */
163 NYD_X
; /* Signal handler */
164 termios_state_reset();
165 siglongjmp(a_tty__actjmp
, s
);
169 getapproval(char const * volatile prompt
, bool_t noninteract_default
)
171 sighandler_type
volatile oint
, ohup
;
176 if (!(options
& OPT_INTERACTIVE
)) {
178 rv
= noninteract_default
;
184 char const *quest
= noninteract_default
185 ? _("[yes]/no? ") : _("[no]/yes? ");
188 prompt
= _("Continue");
189 prompt
= savecatsep(prompt
, ' ', quest
);
192 oint
= safe_signal(SIGINT
, SIG_IGN
);
193 ohup
= safe_signal(SIGHUP
, SIG_IGN
);
194 if ((sig
= sigsetjmp(a_tty__actjmp
, 1)) != 0)
196 safe_signal(SIGINT
, &a_tty__acthdl
);
197 safe_signal(SIGHUP
, &a_tty__acthdl
);
199 if (readline_input(prompt
, FAL0
, &termios_state
.ts_linebuf
,
200 &termios_state
.ts_linesize
, NULL
) >= 0)
201 rv
= (boolify(termios_state
.ts_linebuf
, UIZ_MAX
,
202 noninteract_default
) > 0);
204 termios_state_reset();
206 safe_signal(SIGHUP
, ohup
);
207 safe_signal(SIGINT
, oint
);
217 getuser(char const * volatile query
) /* TODO v15-compat obsolete */
219 sighandler_type
volatile oint
, ohup
;
220 char * volatile user
= NULL
;
227 oint
= safe_signal(SIGINT
, SIG_IGN
);
228 ohup
= safe_signal(SIGHUP
, SIG_IGN
);
229 if ((sig
= sigsetjmp(a_tty__actjmp
, 1)) != 0)
231 safe_signal(SIGINT
, &a_tty__acthdl
);
232 safe_signal(SIGHUP
, &a_tty__acthdl
);
234 if (readline_input(query
, FAL0
, &termios_state
.ts_linebuf
,
235 &termios_state
.ts_linesize
, NULL
) >= 0)
236 user
= termios_state
.ts_linebuf
;
238 termios_state_reset();
240 safe_signal(SIGHUP
, ohup
);
241 safe_signal(SIGINT
, oint
);
249 getpassword(char const *query
)
251 sighandler_type
volatile oint
, ohup
;
253 char * volatile pass
= NULL
;
258 query
= _("Password: ");
259 fputs(query
, stdout
);
262 /* FIXME everywhere: tcsetattr() generates SIGTTOU when we're not in
263 * FIXME foreground pgrp, and can fail with EINTR!! also affects
264 * FIXME termios_state_reset() */
265 if (options
& OPT_TTYIN
) {
266 tcgetattr(STDIN_FILENO
, &termios_state
.ts_tios
);
267 memcpy(&tios
, &termios_state
.ts_tios
, sizeof tios
);
268 termios_state
.ts_needs_reset
= TRU1
;
269 tios
.c_iflag
&= ~(ISTRIP
);
270 tios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
273 oint
= safe_signal(SIGINT
, SIG_IGN
);
274 ohup
= safe_signal(SIGHUP
, SIG_IGN
);
275 if ((sig
= sigsetjmp(a_tty__actjmp
, 1)) != 0)
277 safe_signal(SIGINT
, &a_tty__acthdl
);
278 safe_signal(SIGHUP
, &a_tty__acthdl
);
280 if (options
& OPT_TTYIN
)
281 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tios
);
283 if (readline_restart(stdin
, &termios_state
.ts_linebuf
,
284 &termios_state
.ts_linesize
, 0) >= 0)
285 pass
= termios_state
.ts_linebuf
;
287 termios_state_reset();
288 if (options
& OPT_TTYIN
)
291 safe_signal(SIGHUP
, ohup
);
292 safe_signal(SIGINT
, oint
);
298 #endif /* HAVE_SOCKETS */
305 static char *a_tty_rl_buf
; /* pre_input() hook: initial line */
306 static int a_tty_rl_buflen
; /* content, and its length */
308 /* Our rl_pre_input_hook */
309 static int a_tty_rl_pre_input(void);
312 a_tty_rl_pre_input(void){
314 /* Handle leftover data from \ escaped former line */
315 rl_extend_line_buffer(a_tty_rl_buflen
+ 10);
316 memcpy(rl_line_buffer
, a_tty_rl_buf
, a_tty_rl_buflen
+1);
317 rl_point
= rl_end
= a_tty_rl_buflen
;
318 rl_pre_input_hook
= (rl_hook_func_t
*)NULL
;
332 rl_readline_name
= UNCONST(uagent
);
336 stifle_history((int)hs
);
338 rl_read_init_file(NULL
);
340 /* Because rl_read_init_file() may have introduced yet a different
341 * history size limit, simply load and incorporate the history, leave
342 * it up to readline(3) to do the rest */
367 n_tty_signal(int sig
){
368 NYD_X
; /* Signal handler */
370 /* WINCH comes from main.c */
379 /* readline(3) doesn't catch SIGHUP :( */
381 rl_free_line_state();
382 rl_cleanup_after_signal();
384 n_TERMCAP_SUSPEND(TRU1
);
388 sigaddset(&nset
, sig
);
389 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
391 /* When we come here we'll continue editing, so reestablish */
392 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
395 n_TERMCAP_RESUME(TRU1
);
397 rl_reset_after_signal();
404 (n_tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
411 a_tty_rl_buf
= *linebuf
;
412 a_tty_rl_buflen
= (int)n
;
413 rl_pre_input_hook
= &a_tty_rl_pre_input
;
417 n_TERMCAP_SUSPEND(FAL0
);
418 line
= readline(prompt
!= NULL
? prompt
: "");
419 n_TERMCAP_RESUME(FAL0
);
429 *linesize
= LINESIZE
+ n
+1;
430 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
432 memcpy(*linebuf
, line
, n
);
434 (*linebuf
)[n
] = '\0';
442 n_tty_addhist(char const *s
, bool_t isgabby
){
448 if(isgabby
&& !ok_blook(history_gabby
))
450 a_TTY_CHECK_ADDHIST(s
, goto jleave
);
451 hold_all_sigs(); /* XXX too heavy */
452 add_history(s
); /* XXX yet we jump away! */
453 rele_all_sigs(); /* XXX remove jumps */
470 if((fp
= Ftmp(NULL
, "hist", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
471 n_perr(_("tmpfile"), 0);
476 hs
= history_get_history_state();
478 for(i
= (ul_i
)hs
->length
, hl
= hs
->entries
+ i
, b
= 0; i
> 0; --i
){
479 char *cp
= (*--hl
)->line
;
480 size_t sl
= strlen(cp
);
482 fprintf(fp
, "%4lu. %-50.50s (%4lu+%2lu B)\n", i
, cp
, b
, sl
);
486 page_or_print(fp
, (size_t)hs
->length
);
496 HISTORY_STATE
*hs
= history_get_history_state();
498 if(UICMP(z
, entry
, <=, hs
->length
))
499 v
= temporary_arg_v_store
= hs
->entries
[entry
- 1]->line
;
505 # endif /* HAVE_HISTORY */
506 #endif /* HAVE_READLINE */
509 * MLE: the Mailx-Line-Editor, our homebrew editor
510 * (inspired from NetBSD sh(1) / dash(1)s hetio.c).
512 * Only used in interactive mode, simply use STDIN_FILENO as point of interest.
513 * TODO . After I/O layer rewrite, also "output to STDIN_FILENO".
514 * TODO . We work with wide characters, but not for buffer takeovers and
515 * TODO cell2save()ings. This should be changed. For the former the buffer
516 * TODO thus needs to be converted to wide first, and then simply be fed in.
517 * TODO . No BIDI support.
518 * TODO . We repaint too much. To overcome this use the same approach that my
519 * TODO terminal library uses, add a true "virtual screen line" that stores
520 * TODO the actually visible content, keep a notion of "first modified slot"
521 * TODO and "last modified slot" (including "unknown" and "any" specials),
522 * TODO update that virtual instead, then synchronize what has truly changed.
523 * TODO I.e., add an indirection layer.
526 /* To avoid memory leaks etc. with the current codebase that simply longjmp(3)s
527 * we're forced to use the very same buffer--the one that is passed through to
528 * us from the outside--to store anything we need, i.e., a "struct cell[]", and
529 * convert that on-the-fly back to the plain char* result once we're done.
530 * To simplify our live, use savestr() buffers for all other needed memory */
532 /* Columns to ripoff: outermost may not be touched, plus position indicator.
533 * Must thus be at least 1, but should be >= 1+4 to dig the position indicator
534 * that we place (if there is sufficient space) */
535 # define a_TTY_WIDTH_RIPOFF 5
537 /* When shall the visual screen be scrolled, in % of usable screen width */
538 # define a_TTY_SCROLL_MARGIN_LEFT 15
539 # define a_TTY_SCROLL_MARGIN_RIGHT 10
541 /* The maximum size (of a_tty_cell's) in a line */
542 # define a_TTY_LINE_MAX SI32_MAX
544 /* (Some more CTAs around) */
545 n_CTA(a_TTY_LINE_MAX
<= SI32_MAX
,
546 "a_TTY_LINE_MAX larger than SI32_MAX, but the MLE uses 32-bit arithmetic");
548 enum a_tty_visual_flags
{
550 a_TTY_VF_MOD_CURSOR
= 1<<0, /* Cursor moved */
551 a_TTY_VF_MOD_CONTENT
= 1<<1, /* Content modified */
552 a_TTY_VF_MOD_DIRTY
= 1<<2, /* Needs complete repaint */
553 a_TTY_VF_MOD_SINGLE
= 1<<3, /* TODO Drop when indirection as above comes */
554 a_TTY_VF_REFRESH
= a_TTY_VF_MOD_DIRTY
| a_TTY_VF_MOD_CURSOR
|
555 a_TTY_VF_MOD_CONTENT
| a_TTY_VF_MOD_SINGLE
,
556 a_TTY_VF_BELL
= 1<<8, /* Ring the bell */
557 a_TTY_VF_SYNC
= 1<<9, /* Flush/Sync I/O channel */
559 a_TTY_VF_ALL_MASK
= a_TTY_VF_REFRESH
| a_TTY_VF_BELL
| a_TTY_VF_SYNC
,
560 a_TTY__VF_LAST
= a_TTY_VF_SYNC
565 struct a_tty_hist
*tg_hist
;
566 struct a_tty_hist
*tg_hist_tail
;
568 size_t tg_hist_size_max
;
570 struct termios tg_tios_old
;
571 struct termios tg_tios_new
;
576 ui16_t tc_count
; /* ..of bytes */
577 ui8_t tc_width
; /* Visual width; TAB==UI8_MAX! */
578 bool_t tc_novis
; /* Don't display visually as such (control character) */
579 char tc_cbuf
[MB_LEN_MAX
* 2]; /* .. plus reset shift sequence */
583 /* Caller pointers */
585 size_t *tl_x_bufsize
;
586 /* Input processing */
587 char const *tl_reenter_after_cmd
; /* `bind' cmd to exec, then re-readline */
588 /* Line data / content handling */
589 ui32_t tl_count
; /* ..of a_tty_cell's (<= a_TTY_LINE_MAX) */
590 ui32_t tl_cursor
; /* Current a_tty_cell insertion point */
592 char *cbuf
; /* *.tl_x_buf */
593 struct a_tty_cell
*cells
;
595 struct str tl_defc
; /* Current default content */
596 struct str tl_savec
; /* Saved default content */
597 struct str tl_yankbuf
; /* Last yanked data */
599 struct a_tty_hist
*tl_hist
; /* History cursor */
601 ui32_t tl_count_max
; /* ..before buffer needs to grow */
602 /* Visual data representation handling */
603 ui32_t tl_vi_flags
; /* enum a_tty_visual_flags */
604 ui32_t tl_lst_count
; /* .tl_count after last sync */
605 ui32_t tl_lst_cursor
; /* .tl_cursor after last sync */
606 /* TODO Add another indirection layer by adding a tl_phy_line of
607 * TODO a_tty_cell objects, incorporate changes in visual layer,
608 * TODO then check what _really_ has changed, sync those changes only */
609 struct a_tty_cell
const *tl_phy_start
; /* First visible cell, left border */
610 ui32_t tl_phy_cursor
; /* Physical cursor position */
611 ui32_t tl_prompt_length
; /* Preclassified (TODO needed as a_tty_cell) */
612 ui32_t tl_prompt_width
;
614 char const *tl_prompt
; /* Preformatted prompt (including colours) */
619 struct a_tty_hist
*th_older
;
620 struct a_tty_hist
*th_younger
;
621 ui32_t th_isgabby
: 1;
623 char th_dat
[VFIELD_SIZE(sizeof(ui32_t
))];
627 static struct a_tty_global a_tty
;
630 static void a_tty_term_mode(bool_t raw
);
632 /* 0-X (2), UI8_MAX == \t / TAB */
633 static ui8_t
a_tty_wcwidth(wchar_t wc
);
635 /* Memory / cell / word generics */
636 static void a_tty_check_grow(struct a_tty_line
*tlp
, ui32_t no
638 static ssize_t
a_tty_cell2dat(struct a_tty_line
*tlp
);
639 static void a_tty_cell2save(struct a_tty_line
*tlp
);
641 /* Save away data bytes of given range (max = non-inclusive) */
642 static void a_tty_yank(struct a_tty_line
*tlp
, struct a_tty_cell
*tcpmin
,
643 struct a_tty_cell
*tcpmax
);
645 /* Ask user for hexadecimal number, interpret as UTF-32 */
646 static wchar_t a_tty_vinuni(struct a_tty_line
*tlp
);
648 /* Visual screen synchronization */
649 static bool_t
a_tty_vi_refresh(struct a_tty_line
*tlp
);
651 /* Search for word boundary, starting at tl_cursor, in "dir"ection (<> 0).
652 * Return <0 when moving is impossible (backward direction but in position 0,
653 * forward direction but in outermost column), and relative distance to
654 * tl_cursor otherwise */
655 static si32_t
a_tty_wboundary(struct a_tty_line
*tlp
, si32_t dir
);
658 static void a_tty_khome(struct a_tty_line
*tlp
, bool_t dobell
);
659 static void a_tty_kend(struct a_tty_line
*tlp
);
660 static void a_tty_kbs(struct a_tty_line
*tlp
);
661 static void a_tty_kkill(struct a_tty_line
*tlp
, bool_t dobell
);
662 static si32_t
a_tty_kdel(struct a_tty_line
*tlp
);
663 static void a_tty_kleft(struct a_tty_line
*tlp
);
664 static void a_tty_kright(struct a_tty_line
*tlp
);
665 static void a_tty_kbwddelw(struct a_tty_line
*tlp
);
666 static void a_tty_kgow(struct a_tty_line
*tlp
, si32_t dir
);
667 static void a_tty_kother(struct a_tty_line
*tlp
, wchar_t wc
);
668 static ui32_t
a_tty_kht(struct a_tty_line
*tlp
);
670 static ui32_t
a_tty__khist_shared(struct a_tty_line
*tlp
,
671 struct a_tty_hist
*thp
);
672 static ui32_t
a_tty_khist(struct a_tty_line
*tlp
, bool_t backwd
);
673 static ui32_t
a_tty_krhist(struct a_tty_line
*tlp
);
677 static ssize_t
a_tty_readline(struct a_tty_line
*tlp
, size_t len
681 a_tty_term_mode(bool_t raw
){
682 struct termios
*tiosp
;
685 tiosp
= &a_tty
.tg_tios_old
;
689 /* Always requery the attributes, in case we've been moved from background
690 * to foreground or however else in between sessions */
691 /* XXX Always enforce ECHO and ICANON in the OLD attributes - do so as long
692 * XXX as we don't properly deal with TTIN and TTOU etc. */
693 tcgetattr(STDIN_FILENO
, tiosp
);
694 tiosp
->c_lflag
|= ECHO
| ICANON
;
696 memcpy(&a_tty
.tg_tios_new
, tiosp
, sizeof *tiosp
);
697 tiosp
= &a_tty
.tg_tios_new
;
698 tiosp
->c_cc
[VMIN
] = 1;
699 tiosp
->c_cc
[VTIME
] = 0;
700 tiosp
->c_iflag
&= ~(ISTRIP
);
701 tiosp
->c_lflag
&= ~(ECHO
/*| ECHOE | ECHONL */| ICANON
| IEXTEN
);
703 tcsetattr(STDIN_FILENO
, TCSADRAIN
, tiosp
);
708 a_tty_wcwidth(wchar_t wc
){
712 /* Special case the backslash at first */
719 rv
= ((i
= wcwidth(wc
)) > 0) ? (ui8_t
)i
: 0;
721 rv
= iswprint(wc
) ? 1 + (wc
>= 0x1100u
) : 0; /* TODO use S-CText */
729 a_tty_check_grow(struct a_tty_line
*tlp
, ui32_t no SMALLOC_DEBUG_ARGS
){
733 if(UNLIKELY((cmax
= tlp
->tl_count
+ no
) > tlp
->tl_count_max
)){
736 i
= cmax
* sizeof(struct a_tty_cell
) + 2 * sizeof(struct a_tty_cell
);
737 if(LIKELY(i
>= *tlp
->tl_x_bufsize
)){
738 hold_all_sigs(); /* XXX v15 drop */
741 *tlp
->tl_x_buf
= (srealloc
)(*tlp
->tl_x_buf
, i SMALLOC_DEBUG_ARGSCALL
);
742 rele_all_sigs(); /* XXX v15 drop */
744 tlp
->tl_count_max
= cmax
;
745 *tlp
->tl_x_bufsize
= i
;
751 a_tty_cell2dat(struct a_tty_line
*tlp
){
757 if(LIKELY((i
= tlp
->tl_count
) > 0)){
758 struct a_tty_cell
const *tcap
;
760 tcap
= tlp
->tl_line
.cells
;
762 memcpy(tlp
->tl_line
.cbuf
+ len
, tcap
->tc_cbuf
, tcap
->tc_count
);
763 len
+= tcap
->tc_count
;
764 }while(++tcap
, --i
> 0);
767 tlp
->tl_line
.cbuf
[len
] = '\0';
773 a_tty_cell2save(struct a_tty_line
*tlp
){
775 struct a_tty_cell
*tcap
;
778 tlp
->tl_savec
.s
= NULL
;
781 if(UNLIKELY(tlp
->tl_count
== 0))
784 for(tcap
= tlp
->tl_line
.cells
, len
= 0, i
= tlp
->tl_count
; i
> 0;
786 len
+= tcap
->tc_count
;
788 tlp
->tl_savec
.s
= salloc((tlp
->tl_savec
.l
= len
) +1);
790 for(tcap
= tlp
->tl_line
.cells
, len
= 0, i
= tlp
->tl_count
; i
> 0;
792 memcpy(tlp
->tl_savec
.s
+ len
, tcap
->tc_cbuf
, tcap
->tc_count
);
793 len
+= tcap
->tc_count
;
795 tlp
->tl_savec
.s
[len
] = '\0';
801 a_tty_yank(struct a_tty_line
*tlp
, struct a_tty_cell
*tcpmin
,
802 struct a_tty_cell
*tcpmax
){
804 struct a_tty_cell
*tcp
;
809 for(tcp
= tcpmin
; tcp
< tcpmax
; ++tcp
)
812 tlp
->tl_yankbuf
.s
= cp
= salloc((tlp
->tl_yankbuf
.l
= l
) +1);
815 for(tcp
= tcpmin
; tcp
< tcpmax
; cp
+= l
, ++tcp
)
816 memcpy(cp
, tcp
->tc_cbuf
, l
= tcp
->tc_count
);
822 a_tty_vinuni(struct a_tty_line
*tlp
){
824 union {size_t i
; long l
;} u
;
830 tlp
->tl_vi_flags
|= a_TTY_VF_MOD_DIRTY
;
831 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
) ||
832 !n_termcap_cmd(n_TERMCAP_CMD_ce
, 0, -1))
835 fputs(_("Please enter Unicode code point: "), stdout
);
838 buf
[sizeof(buf
) -1] = '\0';
840 if(read(STDIN_FILENO
, &buf
[u
.i
], 1) != 1){
841 if(errno
== EINTR
) /* xxx #if !SA_RESTART ? */
847 if(!hexchar(buf
[u
.i
])){
848 char const emsg
[] = "[0-9a-fA-F]";
850 LCTA(sizeof emsg
<= sizeof(buf
));
851 memcpy(buf
, emsg
, sizeof emsg
);
855 putc(buf
[u
.i
], stdout
);
857 if(++u
.i
== sizeof buf
)
862 u
.l
= strtol(buf
, &eptr
, 16);
863 if(u
.l
<= 0 || u
.l
>= 0x10FFFF/* XXX magic; CText */ || *eptr
!= '\0'){
865 n_err(_("\nInvalid input: %s\n"), buf
);
876 a_tty_vi_refresh(struct a_tty_line
*tlp
){
878 a_TRUE_RV
= a_TTY__VF_LAST
<<1, /* Return value bit */
879 a_HAVE_PROMPT
= a_TTY__VF_LAST
<<2, /* Have a prompt */
880 a_SHOW_PROMPT
= a_TTY__VF_LAST
<<3, /* Shall print the prompt */
881 a_MOVE_CURSOR
= a_TTY__VF_LAST
<<4, /* Move visual cursor for user! */
882 a_LEFT_MIN
= a_TTY__VF_LAST
<<5, /* On left boundary */
883 a_RIGHT_MAX
= a_TTY__VF_LAST
<<6,
884 a_HAVE_POSITION
= a_TTY__VF_LAST
<<7, /* Print the position indicator */
886 /* We carry some flags over invocations (not worth a specific field) */
887 a_VISIBLE_PROMPT
= a_TTY__VF_LAST
<<8, /* The prompt is on the screen */
888 a_PERSIST_MASK
= a_VISIBLE_PROMPT
,
889 a__LAST
= a_PERSIST_MASK
892 ui32_t f
, w
, phy_wid_base
, phy_wid
, phy_base
, phy_cur
, cnt
, lstcur
, cur
,
893 vi_left
, vi_right
, phy_nxtcur
;
894 struct a_tty_cell
const *tccp
, *tcp_left
, *tcp_right
, *tcxp
;
896 n_LCTA(UICMP(64, a__LAST
, <, UI32_MAX
), "Flag bits excess storage datatype");
898 f
= tlp
->tl_vi_flags
;
899 tlp
->tl_vi_flags
= (f
& ~(a_TTY_VF_REFRESH
| a_PERSIST_MASK
)) |
902 if((w
= tlp
->tl_prompt_length
) > 0)
904 f
|= a_HAVE_POSITION
;
906 /* XXX We don't have a OnTerminalResize event (see main.c) yet, so we need
907 * XXX to reevaluate our circumstances over and over again */
908 /* Don't display prompt or position indicator on very small screens */
909 if((phy_wid_base
= (ui32_t
)scrnwidth
) <= a_TTY_WIDTH_RIPOFF
)
910 f
&= ~(a_HAVE_PROMPT
| a_HAVE_POSITION
);
912 phy_wid_base
-= a_TTY_WIDTH_RIPOFF
;
914 /* Disable the prompt if the screen is too small; due to lack of some
915 * indicator simply add a second ripoff */
916 if((f
& a_HAVE_PROMPT
) && w
+ a_TTY_WIDTH_RIPOFF
>= phy_wid_base
)
920 phy_wid
= phy_wid_base
;
922 phy_cur
= tlp
->tl_phy_cursor
;
924 lstcur
= tlp
->tl_lst_cursor
;
926 /* XXX Assume dirty screen if shrunk */
927 if(cnt
< tlp
->tl_lst_count
)
928 f
|= a_TTY_VF_MOD_DIRTY
;
930 /* TODO Without HAVE_TERMCAP, it would likely be much cheaper to simply
931 * TODO always "cr + paint + ce + ch", since ce is simulated via spaces.. */
933 /* Quickshot: if the line is empty, possibly print prompt and out */
935 /* In that special case dirty anything if it seems better */
936 if((f
& a_TTY_VF_MOD_CONTENT
) || tlp
->tl_lst_count
> 0)
937 f
|= a_TTY_VF_MOD_DIRTY
;
939 if((f
& a_TTY_VF_MOD_DIRTY
) && phy_cur
!= 0){
940 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
))
945 if((f
& (a_TTY_VF_MOD_DIRTY
| a_HAVE_PROMPT
)) ==
946 (a_TTY_VF_MOD_DIRTY
| a_HAVE_PROMPT
)){
947 if(fputs(tlp
->tl_prompt
, stdout
) == EOF
)
949 phy_cur
= tlp
->tl_prompt_width
+ 1;
952 /* May need to clear former line content */
953 if((f
& a_TTY_VF_MOD_DIRTY
) &&
954 !n_termcap_cmd(n_TERMCAP_CMD_ce
, phy_cur
, -1))
957 tlp
->tl_phy_start
= tlp
->tl_line
.cells
;
961 /* Try to get an idea of the visual window */
963 /* Find the left visual boundary */
964 phy_wid
= (phy_wid
>> 1) + (phy_wid
>> 2);
965 if((cur
= tlp
->tl_cursor
) == cnt
)
968 w
= (tcp_left
= tccp
= tlp
->tl_line
.cells
+ cur
)->tc_width
;
969 if(w
== UI8_MAX
) /* TODO yet TAB == SPC */
971 while(tcp_left
> tlp
->tl_line
.cells
){
972 ui16_t cw
= tcp_left
[-1].tc_width
;
974 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
976 if(w
+ cw
>= phy_wid
)
983 /* If the left hand side of our visual viewpoint consumes less than half
984 * of the screen width, show the prompt */
985 if(tcp_left
== tlp
->tl_line
.cells
)
988 if((f
& (a_LEFT_MIN
| a_HAVE_PROMPT
)) == (a_LEFT_MIN
| a_HAVE_PROMPT
) &&
989 w
+ tlp
->tl_prompt_width
< phy_wid
){
990 phy_base
= tlp
->tl_prompt_width
;
994 /* Then search for right boundary. We always leave the rightmost column
995 * empty because some terminals [cw]ould wrap the line if we write into
996 * that. XXX terminfo(5)/termcap(5) have the semi_auto_right_margin/sam/YE
997 * XXX capability to indicate this, but we don't look at that */
998 phy_wid
= phy_wid_base
- phy_base
;
999 tcp_right
= tlp
->tl_line
.cells
+ cnt
;
1001 while(&tccp
[1] < tcp_right
){
1002 ui16_t cw
= tccp
[1].tc_width
;
1005 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
1013 vi_right
= w
- vi_left
;
1015 /* If the complete line including prompt fits on the screen, show prompt */
1016 if(--tcp_right
== tccp
){
1019 /* Since we did brute-force walk also for the left boundary we may end up
1020 * in a situation were anything effectively fits on the screen, including
1021 * the prompt that is, but were we don't recognize this since we
1022 * restricted the search to fit in some visual viewpoint. Therefore try
1023 * again to extend the left boundary to overcome that */
1024 if(!(f
& a_LEFT_MIN
)){
1025 struct a_tty_cell
const *tc1p
= tlp
->tl_line
.cells
;
1026 ui32_t vil1
= vi_left
;
1028 assert(!(f
& a_SHOW_PROMPT
));
1029 w
+= tlp
->tl_prompt_width
;
1030 for(tcxp
= tcp_left
;;){
1031 ui32_t i
= tcxp
[-1].tc_width
;
1033 if(i
== UI8_MAX
) /* TODO yet TAB == SPC */
1047 /*w -= tlp->tl_prompt_width;*/
1051 tccp
= tlp
->tl_line
.cells
+ cur
;
1053 if((f
& (a_LEFT_MIN
| a_RIGHT_MAX
| a_HAVE_PROMPT
| a_SHOW_PROMPT
)) ==
1054 (a_LEFT_MIN
| a_RIGHT_MAX
| a_HAVE_PROMPT
) &&
1055 w
+ tlp
->tl_prompt_width
<= phy_wid
){
1056 phy_wid
-= (phy_base
= tlp
->tl_prompt_width
);
1060 /* Try to avoid repainting the complete line - this is possible if the
1061 * cursor "did not leave the screen" and the prompt status hasn't changed.
1062 * I.e., after clamping virtual viewpoint, compare relation to physical */
1063 if((f
& (a_TTY_VF_MOD_SINGLE
/*FIXME*/ |
1064 a_TTY_VF_MOD_CONTENT
/* xxx */ | a_TTY_VF_MOD_DIRTY
)) ||
1065 (tcxp
= tlp
->tl_phy_start
) == NULL
||
1066 tcxp
> tccp
|| tcxp
<= tcp_right
)
1067 f
|= a_TTY_VF_MOD_DIRTY
;
1069 f
|= a_TTY_VF_MOD_DIRTY
;
1071 struct a_tty_cell
const *tcyp
;
1072 si32_t cur_displace
;
1073 ui32_t phy_lmargin
, phy_rmargin
, fx
, phy_displace
;
1075 phy_lmargin
= (fx
= phy_wid
) / 100;
1076 phy_rmargin
= fx
- (phy_lmargin
* a_TTY_SCROLL_MARGIN_RIGHT
);
1077 phy_lmargin
*= a_TTY_SCROLL_MARGIN_LEFT
;
1078 fx
= (f
& (a_SHOW_PROMPT
| a_VISIBLE_PROMPT
));
1080 if(fx
== 0 || fx
== (a_SHOW_PROMPT
| a_VISIBLE_PROMPT
)){
1086 /* We know what we have to paint, start synchronizing */
1088 assert(phy_cur
== tlp
->tl_phy_cursor
);
1089 assert(phy_wid
== phy_wid_base
- phy_base
);
1090 assert(cnt
== tlp
->tl_count
);
1092 assert(lstcur
== tlp
->tl_lst_cursor
);
1093 assert(tccp
== tlp
->tl_line
.cells
+ cur
);
1095 phy_nxtcur
= phy_base
; /* FIXME only if repaint cpl. */
1097 /* Quickshot: is it only cursor movement within the visible screen? */
1098 if((f
& a_TTY_VF_REFRESH
) == a_TTY_VF_MOD_CURSOR
){
1103 /* To be able to apply some quick jump offs, clear line if possible */
1104 if(f
& a_TTY_VF_MOD_DIRTY
){
1105 /* Force complete clearance and cursor reinitialization */
1106 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
) ||
1107 !n_termcap_cmd(n_TERMCAP_CMD_ce
, 0, -1))
1109 tlp
->tl_phy_start
= tcp_left
;
1113 if((f
& (a_TTY_VF_MOD_DIRTY
| a_SHOW_PROMPT
)) && phy_cur
!= 0){
1114 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
))
1119 if(f
& a_SHOW_PROMPT
){
1120 assert(phy_base
== tlp
->tl_prompt_width
);
1121 if(fputs(tlp
->tl_prompt
, stdout
) == EOF
)
1123 phy_cur
= phy_nxtcur
;
1124 f
|= a_VISIBLE_PROMPT
;
1126 f
&= ~a_VISIBLE_PROMPT
;
1128 /* FIXME reposition cursor for paint */
1129 for(w
= phy_nxtcur
; tcp_left
<= tcp_right
; ++tcp_left
){
1132 cw
= tcp_left
->tc_width
;
1134 if(LIKELY(!tcp_left
->tc_novis
)){
1135 if(fwrite(tcp_left
->tc_cbuf
, sizeof *tcp_left
->tc_cbuf
,
1136 tcp_left
->tc_count
, stdout
) != tcp_left
->tc_count
)
1138 }else{ /* XXX Shouldn't be here <-> CText, ui_str.c */
1139 char wbuf
[8]; /* XXX magic */
1141 if(options
& OPT_UNICODE
){
1144 wc
= (ui32_t
)tcp_left
->tc_wc
;
1145 if((wc
& ~0x1Fu
) == 0)
1151 n_utf32_to_utf8(wc
, wbuf
);
1153 wbuf
[0] = '?', wbuf
[1] = '\0';
1155 if(fputs(wbuf
, stdout
) == EOF
)
1160 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
1163 if(tcp_left
== tccp
)
1168 /* Write something position marker alike if it doesn't fit on screen */
1169 if((f
& a_HAVE_POSITION
) &&
1170 ((f
& (a_LEFT_MIN
| a_RIGHT_MAX
)) != (a_LEFT_MIN
| a_RIGHT_MAX
) ||
1171 ((f
& a_HAVE_PROMPT
) && !(f
& a_SHOW_PROMPT
)))){
1172 char posbuf
[5], *pos
= posbuf
;
1176 if(phy_cur
!= (w
= phy_wid_base
) &&
1177 !n_termcap_cmd(n_TERMCAP_CMD_ch
, phy_cur
= w
, 0))
1181 if((f
& a_LEFT_MIN
) && (!(f
& a_HAVE_PROMPT
) || (f
& a_SHOW_PROMPT
)))
1182 memcpy(pos
, "^.+", 3);
1183 else if(f
& a_RIGHT_MAX
)
1184 memcpy(pos
, ".+$", 3);
1186 /* Theoretical line length limit a_TTY_LINE_MAX, choose next power of
1187 * ten (10 ** 10) to represent 100 percent, since we don't have a macro
1188 * that generates a constant, and i don't trust the standard "u type
1189 * suffix automatically scales" calculate the large number */
1190 static char const itoa
[] = "0123456789";
1192 ui64_t
const fact100
= (ui64_t
)0x3B9ACA00u
* 10u, fact
= fact100
/ 100;
1193 ui32_t i
= (ui32_t
)(((fact100
/ cnt
) * tlp
->tl_cursor
) / fact
);
1194 n_LCTA(a_TTY_LINE_MAX
<= SI32_MAX
, "a_TTY_LINE_MAX too large");
1197 pos
[0] = ' ', pos
[1] = itoa
[i
];
1199 pos
[1] = itoa
[i
% 10], pos
[0] = itoa
[i
/ 10];
1203 if(fputs(posbuf
, stdout
) == EOF
)
1208 /* Users are used to see the cursor right of the point of interest, so we
1209 * need some further adjustments unless in special conditions. Be aware
1210 * that we may have adjusted cur at the beginning, too */
1211 if((cur
= tlp
->tl_cursor
) == 0)
1212 phy_nxtcur
= phy_base
;
1213 else if(cur
!= cnt
){
1214 ui16_t cw
= tccp
->tc_width
;
1216 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
1222 if(((f
& a_MOVE_CURSOR
) || phy_nxtcur
!= phy_cur
) &&
1223 !n_termcap_cmd(n_TERMCAP_CMD_ch
, phy_cur
= phy_nxtcur
, 0))
1227 tlp
->tl_vi_flags
|= (f
& a_PERSIST_MASK
);
1228 tlp
->tl_lst_count
= tlp
->tl_count
;
1229 tlp
->tl_lst_cursor
= tlp
->tl_cursor
;
1230 tlp
->tl_phy_cursor
= phy_cur
;
1233 return ((f
& a_TRUE_RV
) != 0);
1240 a_tty_wboundary(struct a_tty_line
*tlp
, si32_t dir
){
1242 struct a_tty_cell
*tcap
;
1247 assert(dir
== 1 || dir
== -1);
1250 cnt
= tlp
->tl_count
;
1251 cur
= tlp
->tl_cursor
;
1256 }else if(cur
+ 1 >= cnt
)
1259 --cnt
, --cur
; /* xxx Unsigned wrapping may occur (twice), then */
1261 for(rv
= 0, tcap
= tlp
->tl_line
.cells
, anynon
= FAL0
;;){
1264 wc
= tcap
[cur
+= (ui32_t
)dir
].tc_wc
;
1265 if(iswblank(wc
) || iswpunct(wc
)){
1276 }else if(cur
+ 1 >= cnt
){
1287 a_tty_khome(struct a_tty_line
*tlp
, bool_t dobell
){
1291 if(LIKELY(tlp
->tl_cursor
> 0)){
1293 f
= a_TTY_VF_MOD_CURSOR
;
1299 tlp
->tl_vi_flags
|= f
;
1304 a_tty_kend(struct a_tty_line
*tlp
){
1308 if(LIKELY(tlp
->tl_cursor
< tlp
->tl_count
)){
1309 tlp
->tl_cursor
= tlp
->tl_count
;
1310 f
= a_TTY_VF_MOD_CURSOR
;
1314 tlp
->tl_vi_flags
|= f
;
1319 a_tty_kbs(struct a_tty_line
*tlp
){
1323 cur
= tlp
->tl_cursor
;
1324 cnt
= tlp
->tl_count
;
1326 if(LIKELY(cur
> 0)){
1327 tlp
->tl_cursor
= --cur
;
1328 tlp
->tl_count
= --cnt
;
1330 if((cnt
-= cur
) > 0){
1331 struct a_tty_cell
*tcap
;
1333 tcap
= tlp
->tl_line
.cells
+ cur
;
1334 memmove(tcap
, &tcap
[1], cnt
*= sizeof(*tcap
));
1336 f
= a_TTY_VF_MOD_CURSOR
| a_TTY_VF_MOD_CONTENT
;
1340 tlp
->tl_vi_flags
|= f
;
1345 a_tty_kkill(struct a_tty_line
*tlp
, bool_t dobell
){
1349 if(LIKELY((i
= tlp
->tl_cursor
) < tlp
->tl_count
)){
1350 struct a_tty_cell
*tcap
;
1352 tcap
= &tlp
->tl_line
.cells
[0];
1353 a_tty_yank(tlp
, &tcap
[i
], &tcap
[tlp
->tl_count
]);
1355 i
= a_TTY_VF_MOD_CONTENT
;
1361 tlp
->tl_vi_flags
|= i
;
1366 a_tty_kdel(struct a_tty_line
*tlp
){
1371 cur
= tlp
->tl_cursor
;
1372 cnt
= tlp
->tl_count
;
1373 i
= (si32_t
)(cnt
- cur
);
1376 tlp
->tl_count
= --cnt
;
1378 if(LIKELY(--i
> 0)){
1379 struct a_tty_cell
*tcap
;
1381 tcap
= &tlp
->tl_line
.cells
[cur
];
1382 memmove(tcap
, &tcap
[1], (ui32_t
)i
* sizeof(*tcap
));
1384 f
= a_TTY_VF_MOD_CONTENT
;
1385 }else if(cnt
== 0 && !ok_blook(ignoreeof
)){
1395 tlp
->tl_vi_flags
|= f
;
1401 a_tty_kleft(struct a_tty_line
*tlp
){
1405 if(LIKELY(tlp
->tl_cursor
> 0)){
1407 f
= a_TTY_VF_MOD_CURSOR
;
1411 tlp
->tl_vi_flags
|= f
;
1416 a_tty_kright(struct a_tty_line
*tlp
){
1420 if(LIKELY((i
= tlp
->tl_cursor
+ 1) <= tlp
->tl_count
)){
1422 i
= a_TTY_VF_MOD_CURSOR
;
1426 tlp
->tl_vi_flags
|= i
;
1431 a_tty_kbwddelw(struct a_tty_line
*tlp
){
1432 struct a_tty_cell
*tcap
;
1437 if(UNLIKELY((i
= a_tty_wboundary(tlp
, -1)) <= 0)){
1438 f
= (i
< 0) ? a_TTY_VF_BELL
: a_TTY_VF_NONE
;
1442 cnt
= tlp
->tl_count
- (ui32_t
)i
;
1443 cur
= tlp
->tl_cursor
- (ui32_t
)i
;
1444 tcap
= &tlp
->tl_line
.cells
[cur
];
1446 a_tty_yank(tlp
, &tcap
[0], &tcap
[i
]);
1448 if((tlp
->tl_count
= cnt
) != (tlp
->tl_cursor
= cur
)){
1450 memmove(&tcap
[0], &tcap
[i
], cnt
* sizeof(*tcap
)); /* FIXME*/
1453 f
= a_TTY_VF_MOD_CURSOR
| a_TTY_VF_MOD_CONTENT
;
1455 tlp
->tl_vi_flags
|= f
;
1460 a_tty_kgow(struct a_tty_line
*tlp
, si32_t dir
){
1465 if(UNLIKELY((i
= a_tty_wboundary(tlp
, dir
)) <= 0))
1466 f
= (i
< 0) ? a_TTY_VF_BELL
: a_TTY_VF_NONE
;
1470 tlp
->tl_cursor
+= (ui32_t
)i
;
1471 f
= a_TTY_VF_MOD_CURSOR
;
1474 tlp
->tl_vi_flags
|= f
;
1479 a_tty_kother(struct a_tty_line
*tlp
, wchar_t wc
){
1480 /* Append if at EOL, insert otherwise;
1481 * since we may move around character-wise, always use a fresh ps */
1483 struct a_tty_cell tc
, *tcap
;
1489 n_LCTA(a_TTY_LINE_MAX
<= SI32_MAX
, "a_TTY_LINE_MAX too large");
1490 if(tlp
->tl_count
+ 1 >= a_TTY_LINE_MAX
){
1491 n_err(_("Stop here, we can't extend line beyond size limit\n"));
1495 /* First init a cell and see wether we'll really handle this wc */
1496 memset(&ps
, 0, sizeof ps
);
1500 l
= wcrtomb(tc
.tc_cbuf
, tc
.tc_wc
= wc
, &ps
);
1501 if(UNLIKELY(l
> MB_LEN_MAX
)){
1503 n_err(_("wcrtomb(3) error: too many multibyte character bytes\n"));
1506 tc
.tc_count
= (ui16_t
)l
;
1508 if(UNLIKELY((options
& OPT_ENC_MBSTATE
) != 0)){
1509 l
= wcrtomb(&tc
.tc_cbuf
[l
], L
'\0', &ps
);
1511 /* Only NUL terminator */;
1512 else if(LIKELY(--l
< MB_LEN_MAX
))
1513 tc
.tc_count
+= (ui16_t
)l
;
1519 /* Yes, we will! Place it in the array */
1520 tc
.tc_novis
= (iswprint(wc
) == 0);
1521 tc
.tc_width
= a_tty_wcwidth(wc
);
1522 /* TODO if(tc.tc_novis && tc.tc_width > 0) */
1524 cur
= tlp
->tl_cursor
++;
1525 cnt
= tlp
->tl_count
++ - cur
;
1526 tcap
= &tlp
->tl_line
.cells
[cur
];
1528 memmove(&tcap
[1], tcap
, cnt
* sizeof(*tcap
));
1529 f
= a_TTY_VF_MOD_CONTENT
;
1531 f
= a_TTY_VF_MOD_SINGLE
;
1532 memcpy(tcap
, &tc
, sizeof *tcap
);
1534 f
|= a_TTY_VF_MOD_CURSOR
;
1536 tlp
->tl_vi_flags
|= f
;
1541 a_tty_kht(struct a_tty_line
*tlp
){
1542 struct str orig
, bot
, topp
, sub
, exp
;
1543 struct a_tty_cell
*cword
, *ctop
, *cx
;
1544 bool_t set_savec
= FAL0
;
1548 /* We cannot expand an empty line */
1549 if(UNLIKELY(tlp
->tl_count
== 0)){
1551 f
= a_TTY_VF_BELL
; /* xxx really bell if no expansion is possible? */
1555 /* Get plain line data; if this is the first expansion/xy, update the
1556 * very original content so that ^G gets the origin back */
1557 orig
= tlp
->tl_savec
;
1558 a_tty_cell2save(tlp
);
1559 exp
= tlp
->tl_savec
;
1561 tlp
->tl_savec
= orig
;
1566 /* Find the word to be expanded */
1568 cword
= tlp
->tl_line
.cells
;
1569 ctop
= cword
+ tlp
->tl_cursor
;
1570 cx
= cword
+ tlp
->tl_count
;
1572 /* topp: separate data right of cursor */
1574 for(rv
= 0; --cx
> ctop
; --cx
)
1577 topp
.s
= orig
.s
+ orig
.l
- rv
;
1579 topp
.s
= NULL
, topp
.l
= 0;
1581 /* bot, sub: we cannot expand the entire data left of cursor, but only
1582 * the last "word", so separate them */
1583 /* TODO Context-sensitive completion: stop for | too, try expand shell?
1584 * TODO Ditto, "cx==cword(+space)": try mail command expansion? */
1585 while(cx
> cword
&& !iswspace(cx
[-1].tc_wc
))
1587 for(rv
= 0; cword
< cx
; ++cword
)
1588 rv
+= cword
->tc_count
;
1596 /* Leave room for "implicit asterisk" expansion, as below */
1598 sub
.s
= UNCONST("*");
1601 exp
.s
= salloc(sub
.l
+ 1 +1);
1602 memcpy(exp
.s
, sub
.s
, sub
.l
);
1603 exp
.s
[sub
.l
] = '\0';
1607 /* TODO there is a TODO note upon fexpand() with multi-return;
1608 * TODO if that will change, the if() below can be simplified.
1609 * TODO Also: iff multireturn, offer a numbered list of possible
1610 * TODO expansions, 0 meaning "none" and * (?) meaning "all",
1611 * TODO go over the pager as necesary (use *crt*, generalized) */
1612 /* Super-Heavy-Metal: block all sigs, avoid leaks on jump */
1615 exp
.s
= fexpand(sub
.s
, a_TTY_TAB_FEXP_FL
);
1618 if(exp
.s
== NULL
|| (exp
.l
= strlen(exp
.s
)) == 0)
1620 /* xxx That is not really true since the limit counts characters not bytes */
1621 n_LCTA(a_TTY_LINE_MAX
<= SI32_MAX
, "a_TTY_LINE_MAX too large");
1622 if(exp
.l
+ 1 >= a_TTY_LINE_MAX
){
1623 n_err(_("Tabulator expansion would extend beyond line size limit\n"));
1627 /* If the expansion equals the original string, assume the user wants what
1628 * is usually known as tab completion, append `*' and restart */
1629 if(exp
.l
== sub
.l
&& !strcmp(exp
.s
, sub
.s
)){
1630 if(sub
.s
[sub
.l
- 1] == '*')
1632 sub
.s
[sub
.l
++] = '*';
1633 sub
.s
[sub
.l
] = '\0';
1637 orig
.l
= bot
.l
+ exp
.l
+ topp
.l
;
1638 orig
.s
= salloc(orig
.l
+ 5 +1);
1639 if((rv
= (ui32_t
)bot
.l
) > 0)
1640 memcpy(orig
.s
, bot
.s
, rv
);
1641 memcpy(orig
.s
+ rv
, exp
.s
, exp
.l
);
1644 memcpy(orig
.s
+ rv
, topp
.s
, topp
.l
);
1649 tlp
->tl_defc
= orig
;
1650 tlp
->tl_count
= tlp
->tl_cursor
= 0;
1651 f
= a_TTY_VF_MOD_DIRTY
;
1653 tlp
->tl_vi_flags
|= f
;
1658 /* If we've provided a default content, but failed to expand, there is
1659 * nothing we can "revert to": drop that default again */
1661 tlp
->tl_savec
.s
= NULL
;
1662 tlp
->tl_savec
.l
= 0;
1669 # ifdef HAVE_HISTORY
1671 a_tty__khist_shared(struct a_tty_line
*tlp
, struct a_tty_hist
*thp
){
1675 if(LIKELY((tlp
->tl_hist
= thp
) != NULL
)){
1676 tlp
->tl_defc
.s
= savestrbuf(thp
->th_dat
, thp
->th_len
);
1677 rv
= tlp
->tl_defc
.l
= thp
->th_len
;
1678 f
= (tlp
->tl_count
> 0) ? a_TTY_VF_MOD_DIRTY
: a_TTY_VF_NONE
;
1679 tlp
->tl_count
= tlp
->tl_cursor
= 0;
1685 tlp
->tl_vi_flags
|= f
;
1691 a_tty_khist(struct a_tty_line
*tlp
, bool_t backwd
){
1692 struct a_tty_hist
*thp
;
1696 /* If we're not in history mode yet, save line content;
1697 * also, disallow forward search, then, and, of course, bail unless we
1698 * do have any history at all */
1699 if((thp
= tlp
->tl_hist
) == NULL
){
1702 if((thp
= a_tty
.tg_hist
) == NULL
)
1704 a_tty_cell2save(tlp
);
1708 thp
= backwd
? thp
->th_older
: thp
->th_younger
;
1710 rv
= a_tty__khist_shared(tlp
, thp
);
1716 a_tty_krhist(struct a_tty_line
*tlp
){
1717 struct str orig_savec
;
1718 struct a_tty_hist
*thp
;
1724 /* We cannot complete an empty line */
1725 if(UNLIKELY(tlp
->tl_count
== 0)){
1726 /* XXX The upcoming hard reset would restore a set savec buffer,
1727 * XXX so forcefully reset that. A cleaner solution would be to
1728 * XXX reset it whenever a restore is no longer desired */
1729 tlp
->tl_savec
.s
= NULL
;
1730 tlp
->tl_savec
.l
= 0;
1734 if((thp
= tlp
->tl_hist
) == NULL
){
1735 if((thp
= a_tty
.tg_hist
) == NULL
)
1737 orig_savec
.s
= NULL
;
1738 orig_savec
.l
= 0; /* silence CC */
1739 }else if((thp
= thp
->th_older
) == NULL
)
1742 orig_savec
= tlp
->tl_savec
;
1744 if(orig_savec
.s
== NULL
)
1745 a_tty_cell2save(tlp
);
1747 for(; thp
!= NULL
; thp
= thp
->th_older
)
1748 if(is_prefix(tlp
->tl_savec
.s
, thp
->th_dat
))
1751 if(orig_savec
.s
!= NULL
)
1752 tlp
->tl_savec
= orig_savec
;
1754 rv
= a_tty__khist_shared(tlp
, thp
);
1758 # endif /* HAVE_HISTORY */
1761 a_tty_readline(struct a_tty_line
*tlp
, size_t len SMALLOC_DEBUG_ARGS
){
1762 /* We want to save code, yet we may have to incorporate a lines'
1763 * default content and / or default input to switch back to after some
1764 * history movement; let "len > 0" mean "have to display some data
1765 * buffer", and only otherwise read(2) it */
1767 char cbuf_base
[MB_LEN_MAX
* 2], *cbuf
, *cbufp
, cursor_maybe
, cursor_store
;
1773 memset(ps
, 0, sizeof ps
);
1774 tlp
->tl_vi_flags
|= a_TTY_VF_REFRESH
| a_TTY_VF_SYNC
;
1776 for(cursor_maybe
= cursor_store
= 0;;){
1777 /* Ensure we have valid pointers, and room for grow */
1778 a_tty_check_grow(tlp
, (len
== 0 ? 1 : (ui32_t
)len
)
1779 SMALLOC_DEBUG_ARGSCALL
);
1781 /* Handle visual state flags, except in buffer take-over mode */
1783 if(tlp
->tl_vi_flags
& a_TTY_VF_BELL
){
1784 tlp
->tl_vi_flags
|= a_TTY_VF_SYNC
;
1788 if(tlp
->tl_vi_flags
& a_TTY_VF_REFRESH
){
1789 if(!a_tty_vi_refresh(tlp
)){
1790 clearerr(stdout
); /* xxx I/O layer rewrite */
1791 n_err(_("Visual refresh failed! Is $TERM set correctly?\n"
1792 " Setting *line-editor-disable* to get us through!\n"));
1793 ok_bset(line_editor_disable
, TRU1
);
1799 if(tlp
->tl_vi_flags
& a_TTY_VF_SYNC
){
1800 tlp
->tl_vi_flags
&= ~a_TTY_VF_SYNC
;
1804 tlp
->tl_vi_flags
&= ~a_TTY_VF_ALL_MASK
;
1807 /* Ready for messing around.
1808 * Normal read(2)? Else buffer-takeover: speed this one up */
1813 assert(tlp
->tl_defc
.l
> 0 && tlp
->tl_defc
.s
!= NULL
);
1815 cbuf
= tlp
->tl_defc
.s
+ (tlp
->tl_defc
.l
- len
);
1819 /* Read in the next complete multibyte character */
1822 /* Let me at least once dream of iomon(itor), timer with one-shot,
1823 * enwrapped with key_event and key_sequence_event, all driven by
1825 if((rv
= read(STDIN_FILENO
, cbufp
, 1)) < 1){
1826 if(errno
== EINTR
) /* xxx #if !SA_RESTART ? */
1833 rv
= (ssize_t
)mbrtowc(&wc
, cbuf
, PTR2SIZE(cbufp
- cbuf
), &ps
[0]);
1835 /* Any error during take-over can only result in a hard reset;
1836 * Otherwise, if it's a hard error, or if too many redundant shift
1837 * sequences overflow our buffer, also perform a hard reset */
1838 if(len
!= 0 || rv
== -1 ||
1839 sizeof cbuf_base
== PTR2SIZE(cbufp
- cbuf
)){
1840 tlp
->tl_savec
.s
= tlp
->tl_defc
.s
= NULL
;
1841 tlp
->tl_savec
.l
= tlp
->tl_defc
.l
= len
= 0;
1842 tlp
->tl_vi_flags
|= a_TTY_VF_BELL
;
1845 /* Otherwise, due to the way we deal with the buffer, we need to
1846 * restore the mbstate_t from before this conversion */
1851 /* Buffer takeover completed? */
1852 if(len
!= 0 && (len
-= (size_t)rv
) == 0){
1853 tlp
->tl_defc
.s
= NULL
;
1860 /* Don't interpret control bytes during buffer take-over */
1861 if(cbuf
!= cbuf_base
)
1864 case 'A' ^ 0x40: /* cursor home */
1865 a_tty_khome(tlp
, TRU1
);
1867 case 'B' ^ 0x40: /* backward character */
1871 /* 'C': interrupt (CTRL-C) */
1872 case 'D' ^ 0x40: /* delete char forward if any, else EOF */
1873 if((rv
= a_tty_kdel(tlp
)) < 0)
1876 case 'E' ^ 0x40: /* end of line */
1879 case 'F' ^ 0x40: /* forward character */
1884 case 'H' ^ 0x40: /* backspace */
1888 case 'I' ^ 0x40: /* horizontal tab */
1889 if((len
= a_tty_kht(tlp
)) > 0)
1892 case 'J' ^ 0x40: /* NL (\n) */
1894 case 'G' ^ 0x40: /* full reset */
1897 case 'U' ^ 0x40: /* ^U: ^A + ^K */
1898 a_tty_khome(tlp
, FAL0
);
1900 case 'K' ^ 0x40: /* kill from cursor to end of line */
1901 a_tty_kkill(tlp
, (wc
== ('K' ^ 0x40) || tlp
->tl_count
== 0));
1902 /* (Handle full reset?) */
1903 if(wc
== ('G' ^ 0x40)) {
1904 # ifdef HAVE_HISTORY
1905 tlp
->tl_hist
= NULL
;
1907 if((len
= tlp
->tl_savec
.l
) != 0){
1908 tlp
->tl_defc
= tlp
->tl_savec
;
1909 tlp
->tl_savec
.s
= NULL
;
1910 tlp
->tl_savec
.l
= 0;
1912 len
= tlp
->tl_defc
.l
;
1915 case 'L' ^ 0x40: /* repaint line */
1916 tlp
->tl_vi_flags
|= a_TTY_VF_MOD_DIRTY
;
1919 case 'N' ^ 0x40: /* history next */
1921 # ifdef HAVE_HISTORY
1922 if(tlp
->tl_hist
== NULL
)
1924 if((len
= a_tty_khist(tlp
, FAL0
)) > 0)
1932 tlp
->tl_reenter_after_cmd
= "dp";
1934 case 'P' ^ 0x40: /* history previous */
1936 # ifdef HAVE_HISTORY
1937 if((len
= a_tty_khist(tlp
, TRU1
)) > 0)
1945 case 'R' ^ 0x40: /* reverse history search */
1946 # ifdef HAVE_HISTORY
1947 if((len
= a_tty_krhist(tlp
)) > 0)
1956 case 'V' ^ 0x40: /* insert (Unicode) character */
1957 if((wc
= a_tty_vinuni(tlp
)) > 0)
1960 case 'W' ^ 0x40: /* backward delete "word" */
1961 a_tty_kbwddelw(tlp
);
1963 case 'X' ^ 0x40: /* move cursor forward "word" */
1964 a_tty_kgow(tlp
, +1);
1966 case 'Y' ^ 0x40: /* move cursor backward "word" */
1967 a_tty_kgow(tlp
, -1);
1969 /* 'Z': suspend (CTRL-Z) */
1971 if(cursor_maybe
++ != 0)
1975 /* XXX Handle usual ^[[[ABCD1456] cursor keys: UGLY,"MAGIC",INFLEX */
1976 if(cursor_maybe
> 0){
1977 if(++cursor_maybe
== 2){
1981 }else if(cursor_maybe
== 3){
1985 case L
'A': goto j_p
;
1986 case L
'B': goto j_n
;
1987 case L
'C': goto j_f
;
1988 case L
'D': goto j_b
;
1999 cursor_store
= ((wc
== L
'1') ? '0' :
2000 (wc
== L
'4' ? '$' : (wc
== L
'5' ? '-' : '+')));
2004 a_tty_kother(tlp
, L
'[');
2007 if(wc
== L
'~') J_xterm_noapp
:{
2008 char *cp
= salloc(3);
2011 cp
[1] = cursor_store
;
2013 tlp
->tl_reenter_after_cmd
= cp
;
2015 }else if(cursor_store
== '-' && (wc
== L
'A' || wc
== L
'B')){
2016 char const cmd
[] = "dotmove";
2017 char *cp
= salloc(sizeof(cmd
) -1 + 1 +1);
2019 memcpy(cp
, cmd
, sizeof(cmd
) -1);
2020 cp
[sizeof(cmd
) -1] = (wc
!= L
'A') ? '+' : cursor_store
;
2021 cp
[sizeof(cmd
)] = '\0';
2022 tlp
->tl_reenter_after_cmd
= cp
;
2025 a_tty_kother(tlp
, L
'[');
2026 a_tty_kother(tlp
, (wchar_t)cursor_store
);
2032 a_tty_kother(tlp
, wc
);
2033 /* Don't clear the history during takeover..
2034 * ..and also avoid fflush()ing unless we've worked entire buffer */
2037 # ifdef HAVE_HISTORY
2038 if(cbuf
== cbuf_base
)
2039 tlp
->tl_hist
= NULL
;
2043 tlp
->tl_vi_flags
|= a_TTY_VF_BELL
;
2047 tlp
->tl_vi_flags
|= a_TTY_VF_SYNC
;
2050 /* We have a completed input line, convert the struct cell data to its
2051 * plain character equivalent */
2053 rv
= a_tty_cell2dat(tlp
);
2063 # ifdef HAVE_HISTORY
2068 size_t lsize
, cnt
, llen
;
2072 /* Load the history file */
2073 # ifdef HAVE_HISTORY
2075 a_tty
.tg_hist_size
= 0;
2076 a_tty
.tg_hist_size_max
= (size_t)hs
;
2084 hold_all_sigs(); /* TODO too heavy, yet we may jump even here!? */
2085 f
= fopen(v
, "r"); /* TODO HISTFILE LOAD: use linebuf pool */
2088 (void)file_lock(fileno(f
), FLT_READ
, 0,0, 500);
2092 cnt
= (size_t)fsize(f
);
2093 while(fgetline(&lbuf
, &lsize
, &cnt
, &llen
, f
, FAL0
) != NULL
){
2094 if(llen
> 0 && lbuf
[llen
- 1] == '\n')
2095 lbuf
[--llen
] = '\0';
2096 if(llen
== 0 || lbuf
[0] == '#') /* xxx comments? noone! */
2099 bool_t isgabby
= (lbuf
[0] == '*');
2101 n_tty_addhist(lbuf
+ isgabby
, isgabby
);
2109 rele_all_sigs(); /* XXX remove jumps */
2111 # endif /* HAVE_HISTORY */
2112 pstate
|= PS_HISTORY_LOADED
;
2117 n_tty_destroy(void){
2118 # ifdef HAVE_HISTORY
2121 struct a_tty_hist
*thp
;
2127 # ifdef HAVE_HISTORY
2136 dogabby
= ok_blook(history_gabby_persist
);
2138 if((thp
= a_tty
.tg_hist
) != NULL
)
2139 for(; thp
->th_older
!= NULL
; thp
= thp
->th_older
)
2140 if((dogabby
|| !thp
->th_isgabby
) && --hs
== 0)
2143 hold_all_sigs(); /* TODO too heavy, yet we may jump even here!? */
2144 f
= fopen(v
, "w"); /* TODO temporary + rename?! */
2147 (void)file_lock(fileno(f
), FLT_WRITE
, 0,0, 500);
2148 if (fchmod(fileno(f
), S_IRUSR
| S_IWUSR
) != 0)
2151 for(; thp
!= NULL
; thp
= thp
->th_younger
){
2152 if(dogabby
|| !thp
->th_isgabby
){
2155 fwrite(thp
->th_dat
, sizeof *thp
->th_dat
, thp
->th_len
, f
);
2162 rele_all_sigs(); /* XXX remove jumps */
2164 # endif /* HAVE_HISTORY */
2169 n_tty_signal(int sig
){
2170 sigset_t nset
, oset
;
2171 NYD_X
; /* Signal handler */
2175 /* We don't deal with SIGWINCH, yet get called from main.c */
2178 a_tty_term_mode(FAL0
);
2179 n_TERMCAP_SUSPEND(TRU1
);
2183 sigaddset(&nset
, sig
);
2184 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
2186 /* When we come here we'll continue editing, so reestablish */
2187 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
2190 n_TERMCAP_RESUME(TRU1
);
2191 a_tty_term_mode(TRU1
);
2197 (n_tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
2198 SMALLOC_DEBUG_ARGS
){
2199 struct a_tty_line tl
;
2200 ui32_t plen
, pwidth
;
2204 /* Classify prompt */
2208 size_t i
= strlen(prompt
);
2210 if(i
== 0 || i
>= UI32_MAX
)
2214 /* TODO *prompt* is in multibyte and not in a_tty_cell, therefore
2215 * TODO we cannot handle it in parts, it's all or nothing.
2216 * TODO Later (S-CText, SysV signals) the prompt should be some global
2217 * TODO carrier thing, fully evaluated and passed around as UI-enabled
2218 * TODO string, then we can print it character by character */
2219 if((i
= field_detect_width(prompt
, i
)) != (size_t)-1)
2222 n_err(_("Character set error in evaluation of prompt\n"));
2229 memset(&tl
, 0, sizeof tl
);
2231 if((tl
.tl_prompt
= prompt
) != NULL
){ /* XXX not re-evaluated */
2232 tl
.tl_prompt_length
= plen
;
2233 tl
.tl_prompt_width
= pwidth
;
2236 tl
.tl_line
.cbuf
= *linebuf
;
2238 tl
.tl_defc
.s
= savestrbuf(*linebuf
, n
);
2241 tl
.tl_x_buf
= linebuf
;
2242 tl
.tl_x_bufsize
= linesize
;
2245 a_tty_term_mode(TRU1
);
2246 nn
= a_tty_readline(&tl
, n SMALLOC_DEBUG_ARGSCALL
);
2247 a_tty_term_mode(FAL0
);
2250 if(tl
.tl_reenter_after_cmd
!= NULL
){
2253 n_source_command(tl
.tl_reenter_after_cmd
);
2262 n_tty_addhist(char const *s
, bool_t isgabby
){
2263 # ifdef HAVE_HISTORY
2264 /* Super-Heavy-Metal: block all sigs, avoid leaks+ on jump */
2266 struct a_tty_hist
*thp
, *othp
, *ythp
;
2272 # ifdef HAVE_HISTORY
2273 if(isgabby
&& !ok_blook(history_gabby
))
2276 if(a_tty
.tg_hist_size_max
== 0)
2278 a_TTY_CHECK_ADDHIST(s
, goto j_leave
);
2280 l
= (ui32_t
)strlen(s
);
2282 /* Eliminating duplicates is expensive, but simply inacceptable so
2283 * during the load of a potentially large history file! */
2284 if(pstate
& PS_HISTORY_LOADED
)
2285 for(thp
= a_tty
.tg_hist
; thp
!= NULL
; thp
= thp
->th_older
)
2286 if(thp
->th_len
== l
&& !strcmp(thp
->th_dat
, s
)){
2287 hold_all_sigs(); /* TODO */
2289 thp
->th_isgabby
= !!isgabby
;
2290 othp
= thp
->th_older
;
2291 ythp
= thp
->th_younger
;
2293 othp
->th_younger
= ythp
;
2295 a_tty
.tg_hist_tail
= ythp
;
2297 ythp
->th_older
= othp
;
2299 a_tty
.tg_hist
= othp
;
2304 ++a_tty
.tg_hist_size
;
2305 if((pstate
& PS_HISTORY_LOADED
) &&
2306 a_tty
.tg_hist_size
> a_tty
.tg_hist_size_max
){
2307 --a_tty
.tg_hist_size
;
2308 if((thp
= a_tty
.tg_hist_tail
) != NULL
){
2309 if((a_tty
.tg_hist_tail
= thp
->th_younger
) == NULL
)
2310 a_tty
.tg_hist
= NULL
;
2312 a_tty
.tg_hist_tail
->th_older
= NULL
;
2317 thp
= smalloc((sizeof(struct a_tty_hist
) -
2318 VFIELD_SIZEOF(struct a_tty_hist
, th_dat
)) + l
+1);
2319 thp
->th_isgabby
= !!isgabby
;
2321 memcpy(thp
->th_dat
, s
, l
+1);
2323 if((thp
->th_older
= a_tty
.tg_hist
) != NULL
)
2324 a_tty
.tg_hist
->th_younger
= thp
;
2326 a_tty
.tg_hist_tail
= thp
;
2327 thp
->th_younger
= NULL
;
2328 a_tty
.tg_hist
= thp
;
2336 # ifdef HAVE_HISTORY
2344 struct a_tty_hist
*thp
;
2346 if(a_tty
.tg_hist
== NULL
)
2349 if((fp
= Ftmp(NULL
, "hist", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
2350 n_perr(_("tmpfile"), 0);
2355 i
= a_tty
.tg_hist_size
;
2357 for(thp
= a_tty
.tg_hist
; thp
!= NULL
;
2358 --i
, b
+= thp
->th_len
, thp
= thp
->th_older
)
2360 "%c%4" PRIuZ
". %-50.50s (%4" PRIuZ
"+%2" PRIu32
" B)\n",
2361 (thp
->th_isgabby
? '*' : ' '), i
, thp
->th_dat
, b
, thp
->th_len
);
2363 page_or_print(fp
, i
);
2369 struct a_tty_hist
*thp
;
2371 while((thp
= a_tty
.tg_hist
) != NULL
){
2372 a_tty
.tg_hist
= thp
->th_older
;
2375 a_tty
.tg_hist_tail
= NULL
;
2376 a_tty
.tg_hist_size
= 0;
2381 struct a_tty_hist
*thp
;
2383 if(UICMP(z
, entry
, <=, a_tty
.tg_hist_size
)){
2384 entry
= (long)a_tty
.tg_hist_size
- entry
;
2385 for(thp
= a_tty
.tg_hist
;; thp
= thp
->th_older
)
2388 else if(entry
-- != 0)
2391 v
= temporary_arg_v_store
= thp
->th_dat
;
2399 # endif /* HAVE_HISTORY */
2400 #endif /* HAVE_MLE */
2403 * The really-nothing-at-all implementation
2405 #if !defined HAVE_READLINE && !defined HAVE_MLE
2414 n_tty_destroy(void){
2420 n_tty_signal(int sig
){
2421 NYD_X
; /* Signal handler */
2424 # ifdef HAVE_TERMCAP
2427 sigset_t nset
, oset
;
2429 n_TERMCAP_SUSPEND(TRU1
);
2433 sigaddset(&nset
, sig
);
2434 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
2436 /* When we come here we'll continue editing, so reestablish */
2437 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
2440 n_TERMCAP_RESUME(TRU1
);
2444 # endif /* HAVE_TERMCAP */
2448 (n_tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
2449 SMALLOC_DEBUG_ARGS
){
2455 fputs(prompt
, stdout
);
2458 # ifdef HAVE_TERMCAP
2461 rv
= (readline_restart
)(stdin
, linebuf
, linesize
,n SMALLOC_DEBUG_ARGSCALL
);
2462 # ifdef HAVE_TERMCAP
2470 n_tty_addhist(char const *s
, bool_t isgabby
){
2476 #endif /* nothing at all */
2478 #undef a_TTY_SIGNALS