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 (n_lex_input(prompt
, TRU1
, &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 (n_lex_input(query
, TRU1
, &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) */
615 /* .tl_pos_buf is a hack */
617 char *tl_pos_buf
; /* mle-position colour-on, [4], reset seq. */
618 char *tl_pos
; /* Address of the [4] */
624 struct a_tty_hist
*th_older
;
625 struct a_tty_hist
*th_younger
;
626 ui32_t th_isgabby
: 1;
628 char th_dat
[VFIELD_SIZE(sizeof(ui32_t
))];
632 static struct a_tty_global a_tty
;
635 static void a_tty_term_mode(bool_t raw
);
637 /* 0-X (2), UI8_MAX == \t / TAB */
638 static ui8_t
a_tty_wcwidth(wchar_t wc
);
640 /* Memory / cell / word generics */
641 static void a_tty_check_grow(struct a_tty_line
*tlp
, ui32_t no
643 static ssize_t
a_tty_cell2dat(struct a_tty_line
*tlp
);
644 static void a_tty_cell2save(struct a_tty_line
*tlp
);
646 /* Save away data bytes of given range (max = non-inclusive) */
647 static void a_tty_yank(struct a_tty_line
*tlp
, struct a_tty_cell
*tcpmin
,
648 struct a_tty_cell
*tcpmax
);
650 /* Ask user for hexadecimal number, interpret as UTF-32 */
651 static wchar_t a_tty_vinuni(struct a_tty_line
*tlp
);
653 /* Visual screen synchronization */
654 static bool_t
a_tty_vi_refresh(struct a_tty_line
*tlp
);
656 /* Search for word boundary, starting at tl_cursor, in "dir"ection (<> 0).
657 * Return <0 when moving is impossible (backward direction but in position 0,
658 * forward direction but in outermost column), and relative distance to
659 * tl_cursor otherwise */
660 static si32_t
a_tty_wboundary(struct a_tty_line
*tlp
, si32_t dir
);
663 static void a_tty_khome(struct a_tty_line
*tlp
, bool_t dobell
);
664 static void a_tty_kend(struct a_tty_line
*tlp
);
665 static void a_tty_kbs(struct a_tty_line
*tlp
);
666 static void a_tty_kkill(struct a_tty_line
*tlp
, bool_t dobell
);
667 static si32_t
a_tty_kdel(struct a_tty_line
*tlp
);
668 static void a_tty_kleft(struct a_tty_line
*tlp
);
669 static void a_tty_kright(struct a_tty_line
*tlp
);
670 static void a_tty_kbwddelw(struct a_tty_line
*tlp
);
671 static void a_tty_kgow(struct a_tty_line
*tlp
, si32_t dir
);
672 static void a_tty_kother(struct a_tty_line
*tlp
, wchar_t wc
);
673 static ui32_t
a_tty_kht(struct a_tty_line
*tlp
);
675 static ui32_t
a_tty__khist_shared(struct a_tty_line
*tlp
,
676 struct a_tty_hist
*thp
);
677 static ui32_t
a_tty_khist(struct a_tty_line
*tlp
, bool_t backwd
);
678 static ui32_t
a_tty_krhist(struct a_tty_line
*tlp
);
682 static ssize_t
a_tty_readline(struct a_tty_line
*tlp
, size_t len
686 a_tty_term_mode(bool_t raw
){
687 struct termios
*tiosp
;
690 tiosp
= &a_tty
.tg_tios_old
;
694 /* Always requery the attributes, in case we've been moved from background
695 * to foreground or however else in between sessions */
696 /* XXX Always enforce ECHO and ICANON in the OLD attributes - do so as long
697 * XXX as we don't properly deal with TTIN and TTOU etc. */
698 tcgetattr(STDIN_FILENO
, tiosp
);
699 tiosp
->c_lflag
|= ECHO
| ICANON
;
701 memcpy(&a_tty
.tg_tios_new
, tiosp
, sizeof *tiosp
);
702 tiosp
= &a_tty
.tg_tios_new
;
703 tiosp
->c_cc
[VMIN
] = 1;
704 tiosp
->c_cc
[VTIME
] = 0;
705 tiosp
->c_iflag
&= ~(ISTRIP
);
706 tiosp
->c_lflag
&= ~(ECHO
/*| ECHOE | ECHONL */| ICANON
| IEXTEN
);
708 tcsetattr(STDIN_FILENO
, TCSADRAIN
, tiosp
);
713 a_tty_wcwidth(wchar_t wc
){
717 /* Special case the backslash at first */
724 rv
= ((i
= wcwidth(wc
)) > 0) ? (ui8_t
)i
: 0;
726 rv
= iswprint(wc
) ? 1 + (wc
>= 0x1100u
) : 0; /* TODO use S-CText */
734 a_tty_check_grow(struct a_tty_line
*tlp
, ui32_t no SMALLOC_DEBUG_ARGS
){
738 if(UNLIKELY((cmax
= tlp
->tl_count
+ no
) > tlp
->tl_count_max
)){
741 i
= cmax
* sizeof(struct a_tty_cell
) + 2 * sizeof(struct a_tty_cell
);
742 if(LIKELY(i
>= *tlp
->tl_x_bufsize
)){
743 hold_all_sigs(); /* XXX v15 drop */
746 *tlp
->tl_x_buf
= (srealloc
)(*tlp
->tl_x_buf
, i SMALLOC_DEBUG_ARGSCALL
);
747 rele_all_sigs(); /* XXX v15 drop */
749 tlp
->tl_count_max
= cmax
;
750 *tlp
->tl_x_bufsize
= i
;
756 a_tty_cell2dat(struct a_tty_line
*tlp
){
762 if(LIKELY((i
= tlp
->tl_count
) > 0)){
763 struct a_tty_cell
const *tcap
;
765 tcap
= tlp
->tl_line
.cells
;
767 memcpy(tlp
->tl_line
.cbuf
+ len
, tcap
->tc_cbuf
, tcap
->tc_count
);
768 len
+= tcap
->tc_count
;
769 }while(++tcap
, --i
> 0);
772 tlp
->tl_line
.cbuf
[len
] = '\0';
778 a_tty_cell2save(struct a_tty_line
*tlp
){
780 struct a_tty_cell
*tcap
;
783 tlp
->tl_savec
.s
= NULL
;
786 if(UNLIKELY(tlp
->tl_count
== 0))
789 for(tcap
= tlp
->tl_line
.cells
, len
= 0, i
= tlp
->tl_count
; i
> 0;
791 len
+= tcap
->tc_count
;
793 tlp
->tl_savec
.s
= salloc((tlp
->tl_savec
.l
= len
) +1);
795 for(tcap
= tlp
->tl_line
.cells
, len
= 0, i
= tlp
->tl_count
; i
> 0;
797 memcpy(tlp
->tl_savec
.s
+ len
, tcap
->tc_cbuf
, tcap
->tc_count
);
798 len
+= tcap
->tc_count
;
800 tlp
->tl_savec
.s
[len
] = '\0';
806 a_tty_yank(struct a_tty_line
*tlp
, struct a_tty_cell
*tcpmin
,
807 struct a_tty_cell
*tcpmax
){
809 struct a_tty_cell
*tcp
;
814 for(tcp
= tcpmin
; tcp
< tcpmax
; ++tcp
)
817 tlp
->tl_yankbuf
.s
= cp
= salloc((tlp
->tl_yankbuf
.l
= l
) +1);
820 for(tcp
= tcpmin
; tcp
< tcpmax
; cp
+= l
, ++tcp
)
821 memcpy(cp
, tcp
->tc_cbuf
, l
= tcp
->tc_count
);
827 a_tty_vinuni(struct a_tty_line
*tlp
){
829 union {size_t i
; long l
;} u
;
835 tlp
->tl_vi_flags
|= a_TTY_VF_MOD_DIRTY
;
836 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
) ||
837 !n_termcap_cmd(n_TERMCAP_CMD_ce
, 0, -1))
841 struct str
const *cpre
, *csuf
;
843 struct n_colour_pen
*cpen
;
845 cpen
= n_colour_pen_create(n_COLOUR_ID_MLE_PROMPT
, NULL
);
846 if((cpre
= n_colour_pen_to_str(cpen
)) != NULL
)
847 csuf
= n_colour_reset_to_str();
853 printf(_("%sPlease enter Unicode code point:%s "),
854 (cpre
!= NULL
? cpre
->s
: ""), (csuf
!= NULL
? csuf
->s
: ""));
858 buf
[sizeof(buf
) -1] = '\0';
860 if(read(STDIN_FILENO
, &buf
[u
.i
], 1) != 1){
861 if(errno
== EINTR
) /* xxx #if !SA_RESTART ? */
867 if(!hexchar(buf
[u
.i
])){
868 char const emsg
[] = "[0-9a-fA-F]";
870 LCTA(sizeof emsg
<= sizeof(buf
));
871 memcpy(buf
, emsg
, sizeof emsg
);
875 putc(buf
[u
.i
], stdout
);
877 if(++u
.i
== sizeof buf
)
882 u
.l
= strtol(buf
, &eptr
, 16);
883 if(u
.l
<= 0 || u
.l
>= 0x10FFFF/* XXX magic; CText */ || *eptr
!= '\0'){
885 n_err(_("\nInvalid input: %s\n"), buf
);
896 a_tty_vi_refresh(struct a_tty_line
*tlp
){
898 a_TRUE_RV
= a_TTY__VF_LAST
<<1, /* Return value bit */
899 a_HAVE_PROMPT
= a_TTY__VF_LAST
<<2, /* Have a prompt */
900 a_SHOW_PROMPT
= a_TTY__VF_LAST
<<3, /* Shall print the prompt */
901 a_MOVE_CURSOR
= a_TTY__VF_LAST
<<4, /* Move visual cursor for user! */
902 a_LEFT_MIN
= a_TTY__VF_LAST
<<5, /* On left boundary */
903 a_RIGHT_MAX
= a_TTY__VF_LAST
<<6,
904 a_HAVE_POSITION
= a_TTY__VF_LAST
<<7, /* Print the position indicator */
906 /* We carry some flags over invocations (not worth a specific field) */
907 a_VISIBLE_PROMPT
= a_TTY__VF_LAST
<<8, /* The prompt is on the screen */
908 a_PERSIST_MASK
= a_VISIBLE_PROMPT
,
909 a__LAST
= a_PERSIST_MASK
912 ui32_t f
, w
, phy_wid_base
, phy_wid
, phy_base
, phy_cur
, cnt
, lstcur
, cur
,
913 vi_left
, vi_right
, phy_nxtcur
;
914 struct a_tty_cell
const *tccp
, *tcp_left
, *tcp_right
, *tcxp
;
916 n_LCTA(UICMP(64, a__LAST
, <, UI32_MAX
), "Flag bits excess storage datatype");
918 f
= tlp
->tl_vi_flags
;
919 tlp
->tl_vi_flags
= (f
& ~(a_TTY_VF_REFRESH
| a_PERSIST_MASK
)) |
922 if((w
= tlp
->tl_prompt_length
) > 0)
924 f
|= a_HAVE_POSITION
;
926 /* XXX We don't have a OnTerminalResize event (see main.c) yet, so we need
927 * XXX to reevaluate our circumstances over and over again */
928 /* Don't display prompt or position indicator on very small screens */
929 if((phy_wid_base
= (ui32_t
)scrnwidth
) <= a_TTY_WIDTH_RIPOFF
)
930 f
&= ~(a_HAVE_PROMPT
| a_HAVE_POSITION
);
932 phy_wid_base
-= a_TTY_WIDTH_RIPOFF
;
934 /* Disable the prompt if the screen is too small; due to lack of some
935 * indicator simply add a second ripoff */
936 if((f
& a_HAVE_PROMPT
) && w
+ a_TTY_WIDTH_RIPOFF
>= phy_wid_base
)
940 phy_wid
= phy_wid_base
;
942 phy_cur
= tlp
->tl_phy_cursor
;
944 lstcur
= tlp
->tl_lst_cursor
;
946 /* XXX Assume dirty screen if shrunk */
947 if(cnt
< tlp
->tl_lst_count
)
948 f
|= a_TTY_VF_MOD_DIRTY
;
950 /* TODO Without HAVE_TERMCAP, it would likely be much cheaper to simply
951 * TODO always "cr + paint + ce + ch", since ce is simulated via spaces.. */
953 /* Quickshot: if the line is empty, possibly print prompt and out */
955 /* In that special case dirty anything if it seems better */
956 if((f
& a_TTY_VF_MOD_CONTENT
) || tlp
->tl_lst_count
> 0)
957 f
|= a_TTY_VF_MOD_DIRTY
;
959 if((f
& a_TTY_VF_MOD_DIRTY
) && phy_cur
!= 0){
960 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
))
965 if((f
& (a_TTY_VF_MOD_DIRTY
| a_HAVE_PROMPT
)) ==
966 (a_TTY_VF_MOD_DIRTY
| a_HAVE_PROMPT
)){
967 if(fputs(tlp
->tl_prompt
, stdout
) == EOF
)
969 phy_cur
= tlp
->tl_prompt_width
+ 1;
972 /* May need to clear former line content */
973 if((f
& a_TTY_VF_MOD_DIRTY
) &&
974 !n_termcap_cmd(n_TERMCAP_CMD_ce
, phy_cur
, -1))
977 tlp
->tl_phy_start
= tlp
->tl_line
.cells
;
981 /* Try to get an idea of the visual window */
983 /* Find the left visual boundary */
984 phy_wid
= (phy_wid
>> 1) + (phy_wid
>> 2);
985 if((cur
= tlp
->tl_cursor
) == cnt
)
988 w
= (tcp_left
= tccp
= tlp
->tl_line
.cells
+ cur
)->tc_width
;
989 if(w
== UI8_MAX
) /* TODO yet TAB == SPC */
991 while(tcp_left
> tlp
->tl_line
.cells
){
992 ui16_t cw
= tcp_left
[-1].tc_width
;
994 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
996 if(w
+ cw
>= phy_wid
)
1003 /* If the left hand side of our visual viewpoint consumes less than half
1004 * of the screen width, show the prompt */
1005 if(tcp_left
== tlp
->tl_line
.cells
)
1008 if((f
& (a_LEFT_MIN
| a_HAVE_PROMPT
)) == (a_LEFT_MIN
| a_HAVE_PROMPT
) &&
1009 w
+ tlp
->tl_prompt_width
< phy_wid
){
1010 phy_base
= tlp
->tl_prompt_width
;
1014 /* Then search for right boundary. We always leave the rightmost column
1015 * empty because some terminals [cw]ould wrap the line if we write into
1016 * that. XXX terminfo(5)/termcap(5) have the semi_auto_right_margin/sam/YE
1017 * XXX capability to indicate this, but we don't look at that */
1018 phy_wid
= phy_wid_base
- phy_base
;
1019 tcp_right
= tlp
->tl_line
.cells
+ cnt
;
1021 while(&tccp
[1] < tcp_right
){
1022 ui16_t cw
= tccp
[1].tc_width
;
1025 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
1033 vi_right
= w
- vi_left
;
1035 /* If the complete line including prompt fits on the screen, show prompt */
1036 if(--tcp_right
== tccp
){
1039 /* Since we did brute-force walk also for the left boundary we may end up
1040 * in a situation were anything effectively fits on the screen, including
1041 * the prompt that is, but were we don't recognize this since we
1042 * restricted the search to fit in some visual viewpoint. Therefore try
1043 * again to extend the left boundary to overcome that */
1044 if(!(f
& a_LEFT_MIN
)){
1045 struct a_tty_cell
const *tc1p
= tlp
->tl_line
.cells
;
1046 ui32_t vil1
= vi_left
;
1048 assert(!(f
& a_SHOW_PROMPT
));
1049 w
+= tlp
->tl_prompt_width
;
1050 for(tcxp
= tcp_left
;;){
1051 ui32_t i
= tcxp
[-1].tc_width
;
1053 if(i
== UI8_MAX
) /* TODO yet TAB == SPC */
1067 /*w -= tlp->tl_prompt_width;*/
1071 tccp
= tlp
->tl_line
.cells
+ cur
;
1073 if((f
& (a_LEFT_MIN
| a_RIGHT_MAX
| a_HAVE_PROMPT
| a_SHOW_PROMPT
)) ==
1074 (a_LEFT_MIN
| a_RIGHT_MAX
| a_HAVE_PROMPT
) &&
1075 w
+ tlp
->tl_prompt_width
<= phy_wid
){
1076 phy_wid
-= (phy_base
= tlp
->tl_prompt_width
);
1080 /* Try to avoid repainting the complete line - this is possible if the
1081 * cursor "did not leave the screen" and the prompt status hasn't changed.
1082 * I.e., after clamping virtual viewpoint, compare relation to physical */
1083 if((f
& (a_TTY_VF_MOD_SINGLE
/*FIXME*/ |
1084 a_TTY_VF_MOD_CONTENT
/* xxx */ | a_TTY_VF_MOD_DIRTY
)) ||
1085 (tcxp
= tlp
->tl_phy_start
) == NULL
||
1086 tcxp
> tccp
|| tcxp
<= tcp_right
)
1087 f
|= a_TTY_VF_MOD_DIRTY
;
1089 f
|= a_TTY_VF_MOD_DIRTY
;
1091 struct a_tty_cell
const *tcyp
;
1092 si32_t cur_displace
;
1093 ui32_t phy_lmargin
, phy_rmargin
, fx
, phy_displace
;
1095 phy_lmargin
= (fx
= phy_wid
) / 100;
1096 phy_rmargin
= fx
- (phy_lmargin
* a_TTY_SCROLL_MARGIN_RIGHT
);
1097 phy_lmargin
*= a_TTY_SCROLL_MARGIN_LEFT
;
1098 fx
= (f
& (a_SHOW_PROMPT
| a_VISIBLE_PROMPT
));
1100 if(fx
== 0 || fx
== (a_SHOW_PROMPT
| a_VISIBLE_PROMPT
)){
1106 /* We know what we have to paint, start synchronizing */
1108 assert(phy_cur
== tlp
->tl_phy_cursor
);
1109 assert(phy_wid
== phy_wid_base
- phy_base
);
1110 assert(cnt
== tlp
->tl_count
);
1112 assert(lstcur
== tlp
->tl_lst_cursor
);
1113 assert(tccp
== tlp
->tl_line
.cells
+ cur
);
1115 phy_nxtcur
= phy_base
; /* FIXME only if repaint cpl. */
1117 /* Quickshot: is it only cursor movement within the visible screen? */
1118 if((f
& a_TTY_VF_REFRESH
) == a_TTY_VF_MOD_CURSOR
){
1123 /* To be able to apply some quick jump offs, clear line if possible */
1124 if(f
& a_TTY_VF_MOD_DIRTY
){
1125 /* Force complete clearance and cursor reinitialization */
1126 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
) ||
1127 !n_termcap_cmd(n_TERMCAP_CMD_ce
, 0, -1))
1129 tlp
->tl_phy_start
= tcp_left
;
1133 if((f
& (a_TTY_VF_MOD_DIRTY
| a_SHOW_PROMPT
)) && phy_cur
!= 0){
1134 if(!n_termcap_cmdx(n_TERMCAP_CMD_cr
))
1139 if(f
& a_SHOW_PROMPT
){
1140 assert(phy_base
== tlp
->tl_prompt_width
);
1141 if(fputs(tlp
->tl_prompt
, stdout
) == EOF
)
1143 phy_cur
= phy_nxtcur
;
1144 f
|= a_VISIBLE_PROMPT
;
1146 f
&= ~a_VISIBLE_PROMPT
;
1148 /* FIXME reposition cursor for paint */
1149 for(w
= phy_nxtcur
; tcp_left
<= tcp_right
; ++tcp_left
){
1152 cw
= tcp_left
->tc_width
;
1154 if(LIKELY(!tcp_left
->tc_novis
)){
1155 if(fwrite(tcp_left
->tc_cbuf
, sizeof *tcp_left
->tc_cbuf
,
1156 tcp_left
->tc_count
, stdout
) != tcp_left
->tc_count
)
1158 }else{ /* XXX Shouldn't be here <-> CText, ui_str.c */
1159 char wbuf
[8]; /* XXX magic */
1161 if(options
& OPT_UNICODE
){
1164 wc
= (ui32_t
)tcp_left
->tc_wc
;
1165 if((wc
& ~0x1Fu
) == 0)
1171 n_utf32_to_utf8(wc
, wbuf
);
1173 wbuf
[0] = '?', wbuf
[1] = '\0';
1175 if(fputs(wbuf
, stdout
) == EOF
)
1180 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
1183 if(tcp_left
== tccp
)
1188 /* Write something position marker alike if it doesn't fit on screen */
1189 if((f
& a_HAVE_POSITION
) &&
1190 ((f
& (a_LEFT_MIN
| a_RIGHT_MAX
)) != (a_LEFT_MIN
| a_RIGHT_MAX
) ||
1191 ((f
& a_HAVE_PROMPT
) && !(f
& a_SHOW_PROMPT
)))){
1193 char *posbuf
= tlp
->tl_pos_buf
, *pos
= tlp
->tl_pos
;
1195 char posbuf
[5], *pos
= posbuf
;
1200 if(phy_cur
!= (w
= phy_wid_base
) &&
1201 !n_termcap_cmd(n_TERMCAP_CMD_ch
, phy_cur
= w
, 0))
1205 if((f
& a_LEFT_MIN
) && (!(f
& a_HAVE_PROMPT
) || (f
& a_SHOW_PROMPT
)))
1206 memcpy(pos
, "^.+", 3);
1207 else if(f
& a_RIGHT_MAX
)
1208 memcpy(pos
, ".+$", 3);
1210 /* Theoretical line length limit a_TTY_LINE_MAX, choose next power of
1211 * ten (10 ** 10) to represent 100 percent, since we don't have a macro
1212 * that generates a constant, and i don't trust the standard "u type
1213 * suffix automatically scales" calculate the large number */
1214 static char const itoa
[] = "0123456789";
1216 ui64_t
const fact100
= (ui64_t
)0x3B9ACA00u
* 10u, fact
= fact100
/ 100;
1217 ui32_t i
= (ui32_t
)(((fact100
/ cnt
) * tlp
->tl_cursor
) / fact
);
1218 n_LCTA(a_TTY_LINE_MAX
<= SI32_MAX
, "a_TTY_LINE_MAX too large");
1221 pos
[0] = ' ', pos
[1] = itoa
[i
];
1223 pos
[1] = itoa
[i
% 10], pos
[0] = itoa
[i
/ 10];
1227 if(fputs(posbuf
, stdout
) == EOF
)
1232 /* Users are used to see the cursor right of the point of interest, so we
1233 * need some further adjustments unless in special conditions. Be aware
1234 * that we may have adjusted cur at the beginning, too */
1235 if((cur
= tlp
->tl_cursor
) == 0)
1236 phy_nxtcur
= phy_base
;
1237 else if(cur
!= cnt
){
1238 ui16_t cw
= tccp
->tc_width
;
1240 if(cw
== UI8_MAX
) /* TODO yet TAB == SPC */
1246 if(((f
& a_MOVE_CURSOR
) || phy_nxtcur
!= phy_cur
) &&
1247 !n_termcap_cmd(n_TERMCAP_CMD_ch
, phy_cur
= phy_nxtcur
, 0))
1251 tlp
->tl_vi_flags
|= (f
& a_PERSIST_MASK
);
1252 tlp
->tl_lst_count
= tlp
->tl_count
;
1253 tlp
->tl_lst_cursor
= tlp
->tl_cursor
;
1254 tlp
->tl_phy_cursor
= phy_cur
;
1257 return ((f
& a_TRUE_RV
) != 0);
1264 a_tty_wboundary(struct a_tty_line
*tlp
, si32_t dir
){
1266 struct a_tty_cell
*tcap
;
1271 assert(dir
== 1 || dir
== -1);
1274 cnt
= tlp
->tl_count
;
1275 cur
= tlp
->tl_cursor
;
1280 }else if(cur
+ 1 >= cnt
)
1283 --cnt
, --cur
; /* xxx Unsigned wrapping may occur (twice), then */
1285 for(rv
= 0, tcap
= tlp
->tl_line
.cells
, anynon
= FAL0
;;){
1288 wc
= tcap
[cur
+= (ui32_t
)dir
].tc_wc
;
1289 if(iswblank(wc
) || iswpunct(wc
)){
1300 }else if(cur
+ 1 >= cnt
){
1311 a_tty_khome(struct a_tty_line
*tlp
, bool_t dobell
){
1315 if(LIKELY(tlp
->tl_cursor
> 0)){
1317 f
= a_TTY_VF_MOD_CURSOR
;
1323 tlp
->tl_vi_flags
|= f
;
1328 a_tty_kend(struct a_tty_line
*tlp
){
1332 if(LIKELY(tlp
->tl_cursor
< tlp
->tl_count
)){
1333 tlp
->tl_cursor
= tlp
->tl_count
;
1334 f
= a_TTY_VF_MOD_CURSOR
;
1338 tlp
->tl_vi_flags
|= f
;
1343 a_tty_kbs(struct a_tty_line
*tlp
){
1347 cur
= tlp
->tl_cursor
;
1348 cnt
= tlp
->tl_count
;
1350 if(LIKELY(cur
> 0)){
1351 tlp
->tl_cursor
= --cur
;
1352 tlp
->tl_count
= --cnt
;
1354 if((cnt
-= cur
) > 0){
1355 struct a_tty_cell
*tcap
;
1357 tcap
= tlp
->tl_line
.cells
+ cur
;
1358 memmove(tcap
, &tcap
[1], cnt
*= sizeof(*tcap
));
1360 f
= a_TTY_VF_MOD_CURSOR
| a_TTY_VF_MOD_CONTENT
;
1364 tlp
->tl_vi_flags
|= f
;
1369 a_tty_kkill(struct a_tty_line
*tlp
, bool_t dobell
){
1373 if(LIKELY((i
= tlp
->tl_cursor
) < tlp
->tl_count
)){
1374 struct a_tty_cell
*tcap
;
1376 tcap
= &tlp
->tl_line
.cells
[0];
1377 a_tty_yank(tlp
, &tcap
[i
], &tcap
[tlp
->tl_count
]);
1379 i
= a_TTY_VF_MOD_CONTENT
;
1385 tlp
->tl_vi_flags
|= i
;
1390 a_tty_kdel(struct a_tty_line
*tlp
){
1395 cur
= tlp
->tl_cursor
;
1396 cnt
= tlp
->tl_count
;
1397 i
= (si32_t
)(cnt
- cur
);
1400 tlp
->tl_count
= --cnt
;
1402 if(LIKELY(--i
> 0)){
1403 struct a_tty_cell
*tcap
;
1405 tcap
= &tlp
->tl_line
.cells
[cur
];
1406 memmove(tcap
, &tcap
[1], (ui32_t
)i
* sizeof(*tcap
));
1408 f
= a_TTY_VF_MOD_CONTENT
;
1409 }else if(cnt
== 0 && !ok_blook(ignoreeof
)){
1419 tlp
->tl_vi_flags
|= f
;
1425 a_tty_kleft(struct a_tty_line
*tlp
){
1429 if(LIKELY(tlp
->tl_cursor
> 0)){
1431 f
= a_TTY_VF_MOD_CURSOR
;
1435 tlp
->tl_vi_flags
|= f
;
1440 a_tty_kright(struct a_tty_line
*tlp
){
1444 if(LIKELY((i
= tlp
->tl_cursor
+ 1) <= tlp
->tl_count
)){
1446 i
= a_TTY_VF_MOD_CURSOR
;
1450 tlp
->tl_vi_flags
|= i
;
1455 a_tty_kbwddelw(struct a_tty_line
*tlp
){
1456 struct a_tty_cell
*tcap
;
1461 if(UNLIKELY((i
= a_tty_wboundary(tlp
, -1)) <= 0)){
1462 f
= (i
< 0) ? a_TTY_VF_BELL
: a_TTY_VF_NONE
;
1466 cnt
= tlp
->tl_count
- (ui32_t
)i
;
1467 cur
= tlp
->tl_cursor
- (ui32_t
)i
;
1468 tcap
= &tlp
->tl_line
.cells
[cur
];
1470 a_tty_yank(tlp
, &tcap
[0], &tcap
[i
]);
1472 if((tlp
->tl_count
= cnt
) != (tlp
->tl_cursor
= cur
)){
1474 memmove(&tcap
[0], &tcap
[i
], cnt
* sizeof(*tcap
)); /* FIXME*/
1477 f
= a_TTY_VF_MOD_CURSOR
| a_TTY_VF_MOD_CONTENT
;
1479 tlp
->tl_vi_flags
|= f
;
1484 a_tty_kgow(struct a_tty_line
*tlp
, si32_t dir
){
1489 if(UNLIKELY((i
= a_tty_wboundary(tlp
, dir
)) <= 0))
1490 f
= (i
< 0) ? a_TTY_VF_BELL
: a_TTY_VF_NONE
;
1494 tlp
->tl_cursor
+= (ui32_t
)i
;
1495 f
= a_TTY_VF_MOD_CURSOR
;
1498 tlp
->tl_vi_flags
|= f
;
1503 a_tty_kother(struct a_tty_line
*tlp
, wchar_t wc
){
1504 /* Append if at EOL, insert otherwise;
1505 * since we may move around character-wise, always use a fresh ps */
1507 struct a_tty_cell tc
, *tcap
;
1513 n_LCTA(a_TTY_LINE_MAX
<= SI32_MAX
, "a_TTY_LINE_MAX too large");
1514 if(tlp
->tl_count
+ 1 >= a_TTY_LINE_MAX
){
1515 n_err(_("Stop here, we can't extend line beyond size limit\n"));
1519 /* First init a cell and see wether we'll really handle this wc */
1520 memset(&ps
, 0, sizeof ps
);
1524 l
= wcrtomb(tc
.tc_cbuf
, tc
.tc_wc
= wc
, &ps
);
1525 if(UNLIKELY(l
> MB_LEN_MAX
)){
1527 n_err(_("wcrtomb(3) error: too many multibyte character bytes\n"));
1530 tc
.tc_count
= (ui16_t
)l
;
1532 if(UNLIKELY((options
& OPT_ENC_MBSTATE
) != 0)){
1533 l
= wcrtomb(&tc
.tc_cbuf
[l
], L
'\0', &ps
);
1535 /* Only NUL terminator */;
1536 else if(LIKELY(--l
< MB_LEN_MAX
))
1537 tc
.tc_count
+= (ui16_t
)l
;
1543 /* Yes, we will! Place it in the array */
1544 tc
.tc_novis
= (iswprint(wc
) == 0);
1545 tc
.tc_width
= a_tty_wcwidth(wc
);
1546 /* TODO if(tc.tc_novis && tc.tc_width > 0) */
1548 cur
= tlp
->tl_cursor
++;
1549 cnt
= tlp
->tl_count
++ - cur
;
1550 tcap
= &tlp
->tl_line
.cells
[cur
];
1552 memmove(&tcap
[1], tcap
, cnt
* sizeof(*tcap
));
1553 f
= a_TTY_VF_MOD_CONTENT
;
1555 f
= a_TTY_VF_MOD_SINGLE
;
1556 memcpy(tcap
, &tc
, sizeof *tcap
);
1558 f
|= a_TTY_VF_MOD_CURSOR
;
1560 tlp
->tl_vi_flags
|= f
;
1565 a_tty_kht(struct a_tty_line
*tlp
){
1566 struct str orig
, bot
, topp
, sub
, exp
;
1567 struct a_tty_cell
*cword
, *ctop
, *cx
;
1568 bool_t set_savec
= FAL0
;
1572 /* We cannot expand an empty line */
1573 if(UNLIKELY(tlp
->tl_count
== 0)){
1575 f
= a_TTY_VF_BELL
; /* xxx really bell if no expansion is possible? */
1579 /* Get plain line data; if this is the first expansion/xy, update the
1580 * very original content so that ^G gets the origin back */
1581 orig
= tlp
->tl_savec
;
1582 a_tty_cell2save(tlp
);
1583 exp
= tlp
->tl_savec
;
1585 tlp
->tl_savec
= orig
;
1590 /* Find the word to be expanded */
1592 cword
= tlp
->tl_line
.cells
;
1593 ctop
= cword
+ tlp
->tl_cursor
;
1594 cx
= cword
+ tlp
->tl_count
;
1596 /* topp: separate data right of cursor */
1598 for(rv
= 0; --cx
> ctop
; --cx
)
1601 topp
.s
= orig
.s
+ orig
.l
- rv
;
1603 topp
.s
= NULL
, topp
.l
= 0;
1605 /* bot, sub: we cannot expand the entire data left of cursor, but only
1606 * the last "word", so separate them */
1607 /* TODO Context-sensitive completion: stop for | too, try expand shell?
1608 * TODO Ditto, "cx==cword(+space)": try mail command expansion? */
1609 while(cx
> cword
&& !iswspace(cx
[-1].tc_wc
))
1611 for(rv
= 0; cword
< cx
; ++cword
)
1612 rv
+= cword
->tc_count
;
1620 /* Leave room for "implicit asterisk" expansion, as below */
1622 sub
.s
= UNCONST("*");
1625 exp
.s
= salloc(sub
.l
+ 1 +1);
1626 memcpy(exp
.s
, sub
.s
, sub
.l
);
1627 exp
.s
[sub
.l
] = '\0';
1631 /* TODO there is a TODO note upon fexpand() with multi-return;
1632 * TODO if that will change, the if() below can be simplified.
1633 * TODO Also: iff multireturn, offer a numbered list of possible
1634 * TODO expansions, 0 meaning "none" and * (?) meaning "all",
1635 * TODO go over the pager as necesary (use *crt*, generalized) */
1636 /* Super-Heavy-Metal: block all sigs, avoid leaks on jump */
1639 exp
.s
= fexpand(sub
.s
, a_TTY_TAB_FEXP_FL
);
1642 if(exp
.s
== NULL
|| (exp
.l
= strlen(exp
.s
)) == 0)
1644 /* xxx That is not really true since the limit counts characters not bytes */
1645 n_LCTA(a_TTY_LINE_MAX
<= SI32_MAX
, "a_TTY_LINE_MAX too large");
1646 if(exp
.l
+ 1 >= a_TTY_LINE_MAX
){
1647 n_err(_("Tabulator expansion would extend beyond line size limit\n"));
1651 /* If the expansion equals the original string, assume the user wants what
1652 * is usually known as tab completion, append `*' and restart */
1653 if(exp
.l
== sub
.l
&& !strcmp(exp
.s
, sub
.s
)){
1654 if(sub
.s
[sub
.l
- 1] == '*')
1656 sub
.s
[sub
.l
++] = '*';
1657 sub
.s
[sub
.l
] = '\0';
1661 orig
.l
= bot
.l
+ exp
.l
+ topp
.l
;
1662 orig
.s
= salloc(orig
.l
+ 5 +1);
1663 if((rv
= (ui32_t
)bot
.l
) > 0)
1664 memcpy(orig
.s
, bot
.s
, rv
);
1665 memcpy(orig
.s
+ rv
, exp
.s
, exp
.l
);
1668 memcpy(orig
.s
+ rv
, topp
.s
, topp
.l
);
1673 tlp
->tl_defc
= orig
;
1674 tlp
->tl_count
= tlp
->tl_cursor
= 0;
1675 f
= a_TTY_VF_MOD_DIRTY
;
1677 tlp
->tl_vi_flags
|= f
;
1682 /* If we've provided a default content, but failed to expand, there is
1683 * nothing we can "revert to": drop that default again */
1685 tlp
->tl_savec
.s
= NULL
;
1686 tlp
->tl_savec
.l
= 0;
1693 # ifdef HAVE_HISTORY
1695 a_tty__khist_shared(struct a_tty_line
*tlp
, struct a_tty_hist
*thp
){
1699 if(LIKELY((tlp
->tl_hist
= thp
) != NULL
)){
1700 tlp
->tl_defc
.s
= savestrbuf(thp
->th_dat
, thp
->th_len
);
1701 rv
= tlp
->tl_defc
.l
= thp
->th_len
;
1702 f
= (tlp
->tl_count
> 0) ? a_TTY_VF_MOD_DIRTY
: a_TTY_VF_NONE
;
1703 tlp
->tl_count
= tlp
->tl_cursor
= 0;
1709 tlp
->tl_vi_flags
|= f
;
1715 a_tty_khist(struct a_tty_line
*tlp
, bool_t backwd
){
1716 struct a_tty_hist
*thp
;
1720 /* If we're not in history mode yet, save line content;
1721 * also, disallow forward search, then, and, of course, bail unless we
1722 * do have any history at all */
1723 if((thp
= tlp
->tl_hist
) == NULL
){
1726 if((thp
= a_tty
.tg_hist
) == NULL
)
1728 a_tty_cell2save(tlp
);
1732 thp
= backwd
? thp
->th_older
: thp
->th_younger
;
1734 rv
= a_tty__khist_shared(tlp
, thp
);
1740 a_tty_krhist(struct a_tty_line
*tlp
){
1741 struct str orig_savec
;
1742 struct a_tty_hist
*thp
;
1748 /* We cannot complete an empty line */
1749 if(UNLIKELY(tlp
->tl_count
== 0)){
1750 /* XXX The upcoming hard reset would restore a set savec buffer,
1751 * XXX so forcefully reset that. A cleaner solution would be to
1752 * XXX reset it whenever a restore is no longer desired */
1753 tlp
->tl_savec
.s
= NULL
;
1754 tlp
->tl_savec
.l
= 0;
1758 if((thp
= tlp
->tl_hist
) == NULL
){
1759 if((thp
= a_tty
.tg_hist
) == NULL
)
1761 orig_savec
.s
= NULL
;
1762 orig_savec
.l
= 0; /* silence CC */
1763 }else if((thp
= thp
->th_older
) == NULL
)
1766 orig_savec
= tlp
->tl_savec
;
1768 if(orig_savec
.s
== NULL
)
1769 a_tty_cell2save(tlp
);
1771 for(; thp
!= NULL
; thp
= thp
->th_older
)
1772 if(is_prefix(tlp
->tl_savec
.s
, thp
->th_dat
))
1775 if(orig_savec
.s
!= NULL
)
1776 tlp
->tl_savec
= orig_savec
;
1778 rv
= a_tty__khist_shared(tlp
, thp
);
1782 # endif /* HAVE_HISTORY */
1785 a_tty_readline(struct a_tty_line
*tlp
, size_t len SMALLOC_DEBUG_ARGS
){
1786 /* We want to save code, yet we may have to incorporate a lines'
1787 * default content and / or default input to switch back to after some
1788 * history movement; let "len > 0" mean "have to display some data
1789 * buffer", and only otherwise read(2) it */
1791 char cbuf_base
[MB_LEN_MAX
* 2], *cbuf
, *cbufp
, cursor_maybe
, cursor_store
;
1797 memset(ps
, 0, sizeof ps
);
1798 tlp
->tl_vi_flags
|= a_TTY_VF_REFRESH
| a_TTY_VF_SYNC
;
1800 for(cursor_maybe
= cursor_store
= 0;;){
1801 /* Ensure we have valid pointers, and room for grow */
1802 a_tty_check_grow(tlp
, (len
== 0 ? 1 : (ui32_t
)len
)
1803 SMALLOC_DEBUG_ARGSCALL
);
1805 /* Handle visual state flags, except in buffer take-over mode */
1807 if(tlp
->tl_vi_flags
& a_TTY_VF_BELL
){
1808 tlp
->tl_vi_flags
|= a_TTY_VF_SYNC
;
1812 if(tlp
->tl_vi_flags
& a_TTY_VF_REFRESH
){
1813 if(!a_tty_vi_refresh(tlp
)){
1814 clearerr(stdout
); /* xxx I/O layer rewrite */
1815 n_err(_("Visual refresh failed! Is $TERM set correctly?\n"
1816 " Setting *line-editor-disable* to get us through!\n"));
1817 ok_bset(line_editor_disable
, TRU1
);
1823 if(tlp
->tl_vi_flags
& a_TTY_VF_SYNC
){
1824 tlp
->tl_vi_flags
&= ~a_TTY_VF_SYNC
;
1828 tlp
->tl_vi_flags
&= ~a_TTY_VF_ALL_MASK
;
1831 /* Ready for messing around.
1832 * Normal read(2)? Else buffer-takeover: speed this one up */
1837 assert(tlp
->tl_defc
.l
> 0 && tlp
->tl_defc
.s
!= NULL
);
1839 cbuf
= tlp
->tl_defc
.s
+ (tlp
->tl_defc
.l
- len
);
1843 /* Read in the next complete multibyte character */
1846 /* Let me at least once dream of iomon(itor), timer with one-shot,
1847 * enwrapped with key_event and key_sequence_event, all driven by
1849 if((rv
= read(STDIN_FILENO
, cbufp
, 1)) < 1){
1850 if(errno
== EINTR
) /* xxx #if !SA_RESTART ? */
1857 rv
= (ssize_t
)mbrtowc(&wc
, cbuf
, PTR2SIZE(cbufp
- cbuf
), &ps
[0]);
1859 /* Any error during take-over can only result in a hard reset;
1860 * Otherwise, if it's a hard error, or if too many redundant shift
1861 * sequences overflow our buffer, also perform a hard reset */
1862 if(len
!= 0 || rv
== -1 ||
1863 sizeof cbuf_base
== PTR2SIZE(cbufp
- cbuf
)){
1864 tlp
->tl_savec
.s
= tlp
->tl_defc
.s
= NULL
;
1865 tlp
->tl_savec
.l
= tlp
->tl_defc
.l
= len
= 0;
1866 tlp
->tl_vi_flags
|= a_TTY_VF_BELL
;
1869 /* Otherwise, due to the way we deal with the buffer, we need to
1870 * restore the mbstate_t from before this conversion */
1875 /* Buffer takeover completed? */
1876 if(len
!= 0 && (len
-= (size_t)rv
) == 0){
1877 tlp
->tl_defc
.s
= NULL
;
1884 /* Don't interpret control bytes during buffer take-over */
1885 if(cbuf
!= cbuf_base
)
1888 case 'A' ^ 0x40: /* cursor home */
1889 a_tty_khome(tlp
, TRU1
);
1891 case 'B' ^ 0x40: /* backward character */
1895 /* 'C': interrupt (CTRL-C) */
1896 case 'D' ^ 0x40: /* delete char forward if any, else EOF */
1897 if((rv
= a_tty_kdel(tlp
)) < 0)
1900 case 'E' ^ 0x40: /* end of line */
1903 case 'F' ^ 0x40: /* forward character */
1908 case 'H' ^ 0x40: /* backspace */
1912 case 'I' ^ 0x40: /* horizontal tab */
1913 if((len
= a_tty_kht(tlp
)) > 0)
1916 case 'J' ^ 0x40: /* NL (\n) */
1918 case 'G' ^ 0x40: /* full reset */
1921 case 'U' ^ 0x40: /* ^U: ^A + ^K */
1922 a_tty_khome(tlp
, FAL0
);
1924 case 'K' ^ 0x40: /* kill from cursor to end of line */
1925 a_tty_kkill(tlp
, (wc
== ('K' ^ 0x40) || tlp
->tl_count
== 0));
1926 /* (Handle full reset?) */
1927 if(wc
== ('G' ^ 0x40)) {
1928 # ifdef HAVE_HISTORY
1929 tlp
->tl_hist
= NULL
;
1931 if((len
= tlp
->tl_savec
.l
) != 0){
1932 tlp
->tl_defc
= tlp
->tl_savec
;
1933 tlp
->tl_savec
.s
= NULL
;
1934 tlp
->tl_savec
.l
= 0;
1936 len
= tlp
->tl_defc
.l
;
1939 case 'L' ^ 0x40: /* repaint line */
1940 tlp
->tl_vi_flags
|= a_TTY_VF_MOD_DIRTY
;
1943 case 'N' ^ 0x40: /* history next */
1945 # ifdef HAVE_HISTORY
1946 if(tlp
->tl_hist
== NULL
)
1948 if((len
= a_tty_khist(tlp
, FAL0
)) > 0)
1956 tlp
->tl_reenter_after_cmd
= "dp";
1958 case 'P' ^ 0x40: /* history previous */
1960 # ifdef HAVE_HISTORY
1961 if((len
= a_tty_khist(tlp
, TRU1
)) > 0)
1969 case 'R' ^ 0x40: /* reverse history search */
1970 # ifdef HAVE_HISTORY
1971 if((len
= a_tty_krhist(tlp
)) > 0)
1980 case 'V' ^ 0x40: /* insert (Unicode) character */
1981 if((wc
= a_tty_vinuni(tlp
)) > 0)
1984 case 'W' ^ 0x40: /* backward delete "word" */
1985 a_tty_kbwddelw(tlp
);
1987 case 'X' ^ 0x40: /* move cursor forward "word" */
1988 a_tty_kgow(tlp
, +1);
1990 case 'Y' ^ 0x40: /* move cursor backward "word" */
1991 a_tty_kgow(tlp
, -1);
1993 /* 'Z': suspend (CTRL-Z) */
1995 if(cursor_maybe
++ != 0)
1999 /* XXX Handle usual ^[[[ABCD1456] cursor keys: UGLY,"MAGIC",INFLEX */
2000 if(cursor_maybe
> 0){
2001 if(++cursor_maybe
== 2){
2005 }else if(cursor_maybe
== 3){
2009 case L
'A': goto j_p
;
2010 case L
'B': goto j_n
;
2011 case L
'C': goto j_f
;
2012 case L
'D': goto j_b
;
2023 cursor_store
= ((wc
== L
'1') ? '0' :
2024 (wc
== L
'4' ? '$' : (wc
== L
'5' ? '-' : '+')));
2028 a_tty_kother(tlp
, L
'[');
2031 if(wc
== L
'~') J_xterm_noapp
:{
2032 char *cp
= salloc(3);
2035 cp
[1] = cursor_store
;
2037 tlp
->tl_reenter_after_cmd
= cp
;
2039 }else if(cursor_store
== '-' && (wc
== L
'A' || wc
== L
'B')){
2040 char const cmd
[] = "dotmove";
2041 char *cp
= salloc(sizeof(cmd
) -1 + 1 +1);
2043 memcpy(cp
, cmd
, sizeof(cmd
) -1);
2044 cp
[sizeof(cmd
) -1] = (wc
!= L
'A') ? '+' : cursor_store
;
2045 cp
[sizeof(cmd
)] = '\0';
2046 tlp
->tl_reenter_after_cmd
= cp
;
2049 a_tty_kother(tlp
, L
'[');
2050 a_tty_kother(tlp
, (wchar_t)cursor_store
);
2056 a_tty_kother(tlp
, wc
);
2057 /* Don't clear the history during takeover..
2058 * ..and also avoid fflush()ing unless we've worked entire buffer */
2061 # ifdef HAVE_HISTORY
2062 if(cbuf
== cbuf_base
)
2063 tlp
->tl_hist
= NULL
;
2067 tlp
->tl_vi_flags
|= a_TTY_VF_BELL
;
2071 tlp
->tl_vi_flags
|= a_TTY_VF_SYNC
;
2074 /* We have a completed input line, convert the struct cell data to its
2075 * plain character equivalent */
2077 rv
= a_tty_cell2dat(tlp
);
2087 # ifdef HAVE_HISTORY
2092 size_t lsize
, cnt
, llen
;
2096 /* Load the history file */
2097 # ifdef HAVE_HISTORY
2099 a_tty
.tg_hist_size
= 0;
2100 a_tty
.tg_hist_size_max
= (size_t)hs
;
2108 hold_all_sigs(); /* TODO too heavy, yet we may jump even here!? */
2109 f
= fopen(v
, "r"); /* TODO HISTFILE LOAD: use linebuf pool */
2112 (void)n_file_lock(fileno(f
), FLT_READ
, 0,0, 500);
2116 cnt
= (size_t)fsize(f
);
2117 while(fgetline(&lbuf
, &lsize
, &cnt
, &llen
, f
, FAL0
) != NULL
){
2118 if(llen
> 0 && lbuf
[llen
- 1] == '\n')
2119 lbuf
[--llen
] = '\0';
2120 if(llen
== 0 || lbuf
[0] == '#') /* xxx comments? noone! */
2123 bool_t isgabby
= (lbuf
[0] == '*');
2125 n_tty_addhist(lbuf
+ isgabby
, isgabby
);
2133 rele_all_sigs(); /* XXX remove jumps */
2135 # endif /* HAVE_HISTORY */
2136 pstate
|= PS_HISTORY_LOADED
;
2141 n_tty_destroy(void){
2142 # ifdef HAVE_HISTORY
2145 struct a_tty_hist
*thp
;
2151 # ifdef HAVE_HISTORY
2160 dogabby
= ok_blook(history_gabby_persist
);
2162 if((thp
= a_tty
.tg_hist
) != NULL
)
2163 for(; thp
->th_older
!= NULL
; thp
= thp
->th_older
)
2164 if((dogabby
|| !thp
->th_isgabby
) && --hs
== 0)
2167 hold_all_sigs(); /* TODO too heavy, yet we may jump even here!? */
2168 f
= fopen(v
, "w"); /* TODO temporary + rename?! */
2171 (void)n_file_lock(fileno(f
), FLT_WRITE
, 0,0, 500);
2172 if (fchmod(fileno(f
), S_IRUSR
| S_IWUSR
) != 0)
2175 for(; thp
!= NULL
; thp
= thp
->th_younger
){
2176 if(dogabby
|| !thp
->th_isgabby
){
2179 fwrite(thp
->th_dat
, sizeof *thp
->th_dat
, thp
->th_len
, f
);
2186 rele_all_sigs(); /* XXX remove jumps */
2188 # endif /* HAVE_HISTORY */
2193 n_tty_signal(int sig
){
2194 sigset_t nset
, oset
;
2195 NYD_X
; /* Signal handler */
2199 /* We don't deal with SIGWINCH, yet get called from main.c */
2202 a_tty_term_mode(FAL0
);
2203 n_TERMCAP_SUSPEND(TRU1
);
2207 sigaddset(&nset
, sig
);
2208 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
2210 /* When we come here we'll continue editing, so reestablish */
2211 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
2214 n_TERMCAP_RESUME(TRU1
);
2215 a_tty_term_mode(TRU1
);
2221 (n_tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
2222 SMALLOC_DEBUG_ARGS
){
2223 struct a_tty_line tl
;
2227 ui32_t plen
, pwidth
;
2232 n_colour_env_create(n_COLOUR_CTX_MLE
, FAL0
);
2235 /* Classify prompt */
2239 size_t i
= strlen(prompt
);
2241 if(i
== 0 || i
>= UI32_MAX
)
2245 /* TODO *prompt* is in multibyte and not in a_tty_cell, therefore
2246 * TODO we cannot handle it in parts, it's all or nothing.
2247 * TODO Later (S-CText, SysV signals) the prompt should be some global
2248 * TODO carrier thing, fully evaluated and passed around as UI-enabled
2249 * TODO string, then we can print it character by character */
2250 if((i
= field_detect_width(prompt
, i
)) != (size_t)-1)
2253 n_err(_("Character set error in evaluation of prompt\n"));
2261 struct n_colour_pen
*ccp
;
2262 struct str
const *sp
;
2264 if(prompt
!= NULL
&&
2265 (ccp
= n_colour_pen_create(n_COLOUR_ID_MLE_PROMPT
, NULL
)) != NULL
&&
2266 (sp
= n_colour_pen_to_str(ccp
)) != NULL
){
2267 char const *ccol
= sp
->s
;
2269 if((sp
= n_colour_reset_to_str()) != NULL
){
2270 size_t l1
= strlen(ccol
), l2
= strlen(sp
->s
);
2271 ui32_t nplen
= (ui32_t
)(l1
+ plen
+ l2
);
2272 char *nprompt
= salloc(nplen
+1);
2274 memcpy(nprompt
, ccol
, l1
);
2275 memcpy(&nprompt
[l1
], prompt
, plen
);
2276 memcpy(&nprompt
[l1
+= plen
], sp
->s
, ++l2
);
2283 /* .tl_pos_buf is a hack */
2284 posbuf
= pos
= NULL
;
2285 if((ccp
= n_colour_pen_create(n_COLOUR_ID_MLE_POSITION
, NULL
)) != NULL
&&
2286 (sp
= n_colour_pen_to_str(ccp
)) != NULL
){
2287 char const *ccol
= sp
->s
;
2289 if((sp
= n_colour_reset_to_str()) != NULL
){
2290 size_t l1
= strlen(ccol
), l2
= strlen(sp
->s
);
2292 posbuf
= salloc(l1
+ 4 + l2
+1);
2293 memcpy(posbuf
, ccol
, l1
);
2295 memcpy(&pos
[4], sp
->s
, ++l2
);
2299 posbuf
= pos
= salloc(4 +1);
2303 # endif /* HAVE_COLOUR */
2306 memset(&tl
, 0, sizeof tl
);
2308 if((tl
.tl_prompt
= prompt
) != NULL
){ /* XXX not re-evaluated */
2309 tl
.tl_prompt_length
= plen
;
2310 tl
.tl_prompt_width
= pwidth
;
2313 tl
.tl_pos_buf
= posbuf
;
2317 tl
.tl_line
.cbuf
= *linebuf
;
2319 tl
.tl_defc
.s
= savestrbuf(*linebuf
, n
);
2322 tl
.tl_x_buf
= linebuf
;
2323 tl
.tl_x_bufsize
= linesize
;
2326 a_tty_term_mode(TRU1
);
2327 nn
= a_tty_readline(&tl
, n SMALLOC_DEBUG_ARGSCALL
);
2328 a_tty_term_mode(FAL0
);
2331 if(tl
.tl_reenter_after_cmd
!= NULL
){
2333 n_source_command(tl
.tl_reenter_after_cmd
);
2338 n_colour_env_gut(stdout
);
2345 n_tty_addhist(char const *s
, bool_t isgabby
){
2346 # ifdef HAVE_HISTORY
2347 /* Super-Heavy-Metal: block all sigs, avoid leaks+ on jump */
2349 struct a_tty_hist
*thp
, *othp
, *ythp
;
2355 # ifdef HAVE_HISTORY
2356 if(isgabby
&& !ok_blook(history_gabby
))
2359 if(a_tty
.tg_hist_size_max
== 0)
2361 a_TTY_CHECK_ADDHIST(s
, goto j_leave
);
2363 l
= (ui32_t
)strlen(s
);
2365 /* Eliminating duplicates is expensive, but simply inacceptable so
2366 * during the load of a potentially large history file! */
2367 if(pstate
& PS_HISTORY_LOADED
)
2368 for(thp
= a_tty
.tg_hist
; thp
!= NULL
; thp
= thp
->th_older
)
2369 if(thp
->th_len
== l
&& !strcmp(thp
->th_dat
, s
)){
2370 hold_all_sigs(); /* TODO */
2372 thp
->th_isgabby
= !!isgabby
;
2373 othp
= thp
->th_older
;
2374 ythp
= thp
->th_younger
;
2376 othp
->th_younger
= ythp
;
2378 a_tty
.tg_hist_tail
= ythp
;
2380 ythp
->th_older
= othp
;
2382 a_tty
.tg_hist
= othp
;
2387 ++a_tty
.tg_hist_size
;
2388 if((pstate
& PS_HISTORY_LOADED
) &&
2389 a_tty
.tg_hist_size
> a_tty
.tg_hist_size_max
){
2390 --a_tty
.tg_hist_size
;
2391 if((thp
= a_tty
.tg_hist_tail
) != NULL
){
2392 if((a_tty
.tg_hist_tail
= thp
->th_younger
) == NULL
)
2393 a_tty
.tg_hist
= NULL
;
2395 a_tty
.tg_hist_tail
->th_older
= NULL
;
2400 thp
= smalloc((sizeof(struct a_tty_hist
) -
2401 VFIELD_SIZEOF(struct a_tty_hist
, th_dat
)) + l
+1);
2402 thp
->th_isgabby
= !!isgabby
;
2404 memcpy(thp
->th_dat
, s
, l
+1);
2406 if((thp
->th_older
= a_tty
.tg_hist
) != NULL
)
2407 a_tty
.tg_hist
->th_younger
= thp
;
2409 a_tty
.tg_hist_tail
= thp
;
2410 thp
->th_younger
= NULL
;
2411 a_tty
.tg_hist
= thp
;
2419 # ifdef HAVE_HISTORY
2427 struct a_tty_hist
*thp
;
2429 if(a_tty
.tg_hist
== NULL
)
2432 if((fp
= Ftmp(NULL
, "hist", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
2433 n_perr(_("tmpfile"), 0);
2438 i
= a_tty
.tg_hist_size
;
2440 for(thp
= a_tty
.tg_hist
; thp
!= NULL
;
2441 --i
, b
+= thp
->th_len
, thp
= thp
->th_older
)
2443 "%c%4" PRIuZ
". %-50.50s (%4" PRIuZ
"+%2" PRIu32
" B)\n",
2444 (thp
->th_isgabby
? '*' : ' '), i
, thp
->th_dat
, b
, thp
->th_len
);
2446 page_or_print(fp
, i
);
2452 struct a_tty_hist
*thp
;
2454 while((thp
= a_tty
.tg_hist
) != NULL
){
2455 a_tty
.tg_hist
= thp
->th_older
;
2458 a_tty
.tg_hist_tail
= NULL
;
2459 a_tty
.tg_hist_size
= 0;
2464 struct a_tty_hist
*thp
;
2466 if(UICMP(z
, entry
, <=, a_tty
.tg_hist_size
)){
2467 entry
= (long)a_tty
.tg_hist_size
- entry
;
2468 for(thp
= a_tty
.tg_hist
;; thp
= thp
->th_older
)
2471 else if(entry
-- != 0)
2474 v
= temporary_arg_v_store
= thp
->th_dat
;
2482 # endif /* HAVE_HISTORY */
2483 #endif /* HAVE_MLE */
2486 * The really-nothing-at-all implementation
2488 #if !defined HAVE_READLINE && !defined HAVE_MLE
2497 n_tty_destroy(void){
2503 n_tty_signal(int sig
){
2504 NYD_X
; /* Signal handler */
2507 # ifdef HAVE_TERMCAP
2510 sigset_t nset
, oset
;
2512 n_TERMCAP_SUSPEND(TRU1
);
2516 sigaddset(&nset
, sig
);
2517 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
2519 /* When we come here we'll continue editing, so reestablish */
2520 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
2523 n_TERMCAP_RESUME(TRU1
);
2527 # endif /* HAVE_TERMCAP */
2531 (n_tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
2532 SMALLOC_DEBUG_ARGS
){
2538 fputs(prompt
, stdout
);
2541 # ifdef HAVE_TERMCAP
2544 rv
= (readline_restart
)(stdin
, linebuf
, linesize
,n SMALLOC_DEBUG_ARGSCALL
);
2545 # ifdef HAVE_TERMCAP
2553 n_tty_addhist(char const *s
, bool_t isgabby
){
2559 #endif /* nothing at all */
2561 #undef a_TTY_SIGNALS