1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * Copyright (c) 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
43 * Permission to use, copy, modify, and/or distribute this software for any
44 * purpose with or without fee is hereby granted, provided that the above
45 * copyright notice and this permission notice appear in all copies.
47 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
48 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
49 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
50 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
51 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
52 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
53 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
59 # include <readline/history.h>
60 # include <readline/readline.h>
61 #elif defined HAVE_EDITLINE
62 # include <histedit.h>
63 #elif defined HAVE_LINE_EDITOR
68 #define _CL_HISTFILE(S) \
70 S = voption("NAIL_HISTFILE");\
72 S = fexpand(S, FEXP_LOCAL);\
76 #define _CL_HISTSIZE(V) \
78 char const *__sv = voption("NAIL_HISTSIZE");\
80 if (__sv == NULL || *__sv == '\0' ||\
81 (__rv = strtol(__sv, NULL, 10)) == 0)\
90 #define _CL_CHECK_ADDHIST(S,NOACT) \
102 /* fexpand() flags for expand-on-tab */
103 #define _CL_TAB_FEXP_FL (FEXP_FULL | FEXP_SILENT | FEXP_MULTIOK)
106 * Because we have multiple identical implementations, change file layout a bit
107 * and place the implementations one after the other below the other externals
111 yorn(char const *msg
)
115 if (! (options
& OPT_INTERACTIVE
))
117 do if ((cp
= readstr_input(msg
, NULL
)) == NULL
)
119 while (*cp
!= 'y' && *cp
!= 'Y' && *cp
!= 'n' && *cp
!= 'N');
120 return (*cp
== 'y' || *cp
== 'Y');
124 getuser(char const *query
)
129 query
= tr(509, "User: ");
131 if (readline_input(LNED_NONE
, query
, &termios_state
.ts_linebuf
,
132 &termios_state
.ts_linesize
) >= 0)
133 user
= termios_state
.ts_linebuf
;
134 termios_state_reset();
139 getpassword(char const *query
) /* FIXME encaps ttystate signal safe */
145 query
= tr(510, "Password: ");
146 fputs(query
, stdout
);
149 if (options
& OPT_TTYIN
) {
150 tcgetattr(STDIN_FILENO
, &termios_state
.ts_tios
);
151 memcpy(&tios
, &termios_state
.ts_tios
, sizeof tios
);
152 termios_state
.ts_needs_reset
= TRU1
;
153 tios
.c_iflag
&= ~(ISTRIP
);
154 tios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
155 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tios
);
158 if (readline_restart(stdin
, &termios_state
.ts_linebuf
,
159 &termios_state
.ts_linesize
, 0) >= 0)
160 pass
= termios_state
.ts_linebuf
;
161 termios_state_reset();
163 if (options
& OPT_TTYIN
)
169 getcredentials(char **user
, char **pass
)
172 char *u
= *user
, *p
= *pass
;
175 if ((u
= getuser(NULL
)) == NULL
)
183 if ((p
= getpassword(NULL
)) == NULL
)
195 static sighandler_type _rl_shup
;
196 static char * _rl_buf
; /* pre_input() hook: initial line */
197 static int _rl_buflen
; /* content, and its length */
199 static int _rl_pre_input(void);
204 /* Handle leftover data from \ escaped former line */
205 rl_extend_line_buffer(_rl_buflen
+ 10);
206 memcpy(rl_line_buffer
, _rl_buf
, _rl_buflen
+ 1);
207 rl_point
= rl_end
= _rl_buflen
;
208 rl_pre_input_hook
= (rl_hook_func_t
*)NULL
;
221 rl_readline_name
= UNCONST(uagent
);
223 stifle_history((int)hs
);
224 rl_read_init_file(NULL
);
226 /* Because rl_read_init_file() may have introduced yet a different
227 * history size limit, simply load and incorporate the history, leave
228 * it up to readline(3) to do the rest */
255 /* readline(3) doesn't catch it :( */
256 rl_free_line_state();
257 rl_cleanup_after_signal();
258 safe_signal(SIGHUP
, _rl_shup
);
260 sigaddset(&nset
, sig
);
261 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
263 /* XXX When we come here we'll continue editing, so reestablish
264 * XXX cannot happen */
265 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
266 _rl_shup
= safe_signal(SIGHUP
, &tty_signal
);
267 rl_reset_after_signal();
275 (tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
284 rl_pre_input_hook
= &_rl_pre_input
;
287 _rl_shup
= safe_signal(SIGHUP
, &tty_signal
);
288 line
= readline(prompt
);
289 safe_signal(SIGHUP
, _rl_shup
);
297 if (n
>= *linesize
) {
298 *linesize
= LINESIZE
+ n
+ 1;
299 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
301 memcpy(*linebuf
, line
, n
);
303 (*linebuf
)[n
] = '\0';
310 tty_addhist(char const *s
)
312 _CL_CHECK_ADDHIST(s
, goto jleave
);
313 hold_all_sigs(); /* XXX too heavy */
314 add_history(s
); /* XXX yet we jump away! */
315 rele_all_sigs(); /* XXX remove jumps */
319 #endif /* HAVE_READLINE */
326 static EditLine
* _el_el
; /* editline(3) handle */
327 static History
* _el_hcom
; /* History handle for commline */
328 static char const * _el_prompt
; /* Current prompt */
330 static char const * _el_getprompt(void);
347 _el_hcom
= history_init();
348 history(_el_hcom
, &he
, H_SETSIZE
, (int)hs
);
349 history(_el_hcom
, &he
, H_SETUNIQUE
, 1);
351 _el_el
= el_init(uagent
, stdin
, stdout
, stderr
);
352 el_set(_el_el
, EL_SIGNAL
, 1);
353 el_set(_el_el
, EL_TERMINAL
, NULL
);
354 /* Need to set HIST before EDITOR, otherwise it won't work automatic */
355 el_set(_el_el
, EL_HIST
, &history
, _el_hcom
);
356 el_set(_el_el
, EL_EDITOR
, "emacs");
357 el_set(_el_el
, EL_PROMPT
, &_el_getprompt
);
359 el_set(_el_el
, EL_ADDFN
, "tab_complete",
360 "editline(3) internal completion function", &_el_file_cpl
);
361 el_set(_el_el
, EL_BIND
, "^I", "tab_complete", NULL
);
363 el_set(_el_el
, EL_BIND
, "^R", "ed-search-prev-history", NULL
);
364 el_source(_el_el
, NULL
); /* Source ~/.editrc */
366 /* Because el_source() may have introduced yet a different history size
367 * limit, simply load and incorporate the history, leave it up to
368 * editline(3) to do the rest */
371 history(_el_hcom
, &he
, H_LOAD
, v
);
384 history(_el_hcom
, &he
, H_SAVE
, v
);
385 history_end(_el_hcom
);
403 (tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
411 el_push(_el_el
, *linebuf
);
412 line
= el_gets(_el_el
, &nn
);
420 if (n
> 0 && line
[n
- 1] == '\n')
423 if (n
>= *linesize
) {
424 *linesize
= LINESIZE
+ n
+ 1;
425 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
427 memcpy(*linebuf
, line
, n
);
428 (*linebuf
)[n
] = '\0';
434 tty_addhist(char const *s
)
436 /* Enlarge meaning of unique .. to something that rocks;
437 * xxx unfortunately this is expensive to do with editline(3)
438 * xxx maybe it would be better to hook the ptfs instead? */
442 _CL_CHECK_ADDHIST(s
, goto jleave
);
444 hold_all_sigs(); /* XXX too heavy, yet we jump away! */
445 if (history(_el_hcom
, &he
, H_GETUNIQUE
) < 0 || he
.num
== 0)
448 for (i
= history(_el_hcom
, &he
, H_FIRST
); i
>= 0;
449 i
= history(_el_hcom
, &he
, H_NEXT
))
450 if (strcmp(he
.str
, s
) == 0) {
451 history(_el_hcom
, &he
, H_DEL
, he
.num
);
455 history(_el_hcom
, &he
, H_ENTER
, s
);
456 rele_all_sigs(); /* XXX remove jumps */
460 #endif /* HAVE_EDITLINE */
463 * NCL: our homebrew version (inspired from NetBSD sh(1) / dash(1)s hetio.c).
465 * Only used in interactive mode, simply use STDIN_FILENO as point of interest.
466 * We do not handle character widths because the terminal must deal with that
467 * anyway on the one hand, and also wcwidth(3) doesn't support zero-width
468 * characters by definition on the other. We're addicted.
470 * To avoid memory leaks etc. with the current codebase that simply longjmp(3)s
471 * we're forced to use the very same buffer--the one that is passed through to
472 * us from the outside--to store anything we need, i.e., a `struct cell[]', and
473 * convert that on-the-fly back to the plain char* result once we're done.
474 * To simplify our live, use savestr() buffers for all other needed memory
478 * TODO NCL: don't use that stupid .sint=-1 stuff, but simply block all signals
479 * TODO NCL: during handler de-/installation handling.
484 sighandler_type shdl
; /* Try avoid races by setting */
485 sl_it sint
; /* .sint=-1 when inactive */
487 CTA(sizeof(sl_it
) >= sizeof(sighandler_type
));
497 char cbuf
[MB_LEN_MAX
* 2]; /* .. plus reset shift sequence */
502 struct hist
* younger
;
504 char dat
[VFIELD_SIZE(sizeof(size_t))];
508 size_t cursor
; /* Current cursor position */
509 size_t topins
; /* Outermost cursor col set */
511 char * cbuf
; /* *x_buf */
514 struct str defc
; /* Current default content */
515 struct str savec
; /* Saved default content */
516 struct hist
* hist
; /* History cursor */
518 char const * nd
; /* Cursor right */
519 char ** x_buf
; /* Caller pointers */
523 static union xsighdl _ncl_oint
;
524 static union xsighdl _ncl_oquit
;
525 static union xsighdl _ncl_oterm
;
526 static union xsighdl _ncl_ohup
;
527 static union xsighdl _ncl_otstp
;
528 static union xsighdl _ncl_ottin
;
529 static union xsighdl _ncl_ottou
;
530 static struct xtios _ncl_tios
;
531 static struct hist
* _ncl_hist
;
532 static size_t _ncl_hist_size
;
533 static size_t _ncl_hist_size_max
;
534 static bool_t _ncl_hist_load
;
536 static void _ncl_sigs_up(void);
537 static void _ncl_sigs_down(void);
539 static void _ncl_term_mode(bool_t raw
);
541 static void _ncl_check_grow(struct line
*l
, size_t no SMALLOC_DEBUG_ARGS
);
542 static void _ncl_bs_eof_dvup(struct cell
*cap
, size_t i
);
543 static ssize_t
_ncl_wboundary(struct line
*l
, ssize_t dir
);
544 static ssize_t
_ncl_cell2dat(struct line
*l
);
545 static void _ncl_cell2save(struct line
*l
);
547 static void _ncl_khome(struct line
*l
, bool_t dobell
);
548 static void _ncl_kend(struct line
*l
);
549 static void _ncl_kbs(struct line
*l
);
550 static void _ncl_kkill(struct line
*l
, bool_t dobell
);
551 static ssize_t
_ncl_keof(struct line
*l
);
552 static void _ncl_kleft(struct line
*l
);
553 static void _ncl_kright(struct line
*l
);
554 static void _ncl_krefresh(struct line
*l
);
555 static size_t _ncl_kht(struct line
*l
);
556 static size_t __ncl_khist_shared(struct line
*l
, struct hist
*hp
);
557 static size_t _ncl_khist(struct line
*l
, bool_t backwd
);
558 static size_t _ncl_krhist(struct line
*l
);
559 static void _ncl_kbwddelw(struct line
*l
);
560 static void _ncl_kgow(struct line
*l
, ssize_t dir
);
561 static void _ncl_kother(struct line
*l
, wchar_t wc
);
562 static ssize_t
_ncl_readline(char const *prompt
, char **buf
, size_t *bufsize
,
563 size_t len SMALLOC_DEBUG_ARGS
);
568 if (_ncl_oint
.sint
== -1)
569 _ncl_oint
.shdl
= safe_signal(SIGINT
, &tty_signal
);
570 if (_ncl_oquit
.sint
== -1)
571 _ncl_oquit
.shdl
= safe_signal(SIGQUIT
, &tty_signal
);
572 if (_ncl_oterm
.sint
== -1)
573 _ncl_oterm
.shdl
= safe_signal(SIGTERM
, &tty_signal
);
574 if (_ncl_ohup
.sint
== -1)
575 _ncl_ohup
.shdl
= safe_signal(SIGHUP
, &tty_signal
);
576 if (_ncl_otstp
.sint
== -1)
577 _ncl_otstp
.shdl
= safe_signal(SIGTSTP
, &tty_signal
);
578 if (_ncl_ottin
.sint
== -1)
579 _ncl_ottin
.shdl
= safe_signal(SIGTTIN
, &tty_signal
);
580 if (_ncl_ottou
.sint
== -1)
581 _ncl_ottou
.shdl
= safe_signal(SIGTTOU
, &tty_signal
);
587 /* aaah.. atomic cas would be nice (but isn't it all redundant?) */
590 if (_ncl_ottou
.sint
!= -1) {
591 st
= _ncl_ottou
.shdl
, _ncl_ottou
.sint
= -1;
592 safe_signal(SIGTTOU
, st
);
594 if (_ncl_ottin
.sint
!= -1) {
595 st
= _ncl_ottin
.shdl
, _ncl_ottin
.sint
= -1;
596 safe_signal(SIGTTIN
, st
);
598 if (_ncl_otstp
.sint
!= -1) {
599 st
= _ncl_otstp
.shdl
, _ncl_otstp
.sint
= -1;
600 safe_signal(SIGTSTP
, st
);
602 if (_ncl_ohup
.sint
!= -1) {
603 st
= _ncl_ohup
.shdl
, _ncl_ohup
.sint
= -1;
604 safe_signal(SIGHUP
, st
);
606 if (_ncl_oterm
.sint
!= -1) {
607 st
= _ncl_oterm
.shdl
, _ncl_oterm
.sint
= -1;
608 safe_signal(SIGTERM
, st
);
610 if (_ncl_oquit
.sint
!= -1) {
611 st
= _ncl_oquit
.shdl
, _ncl_oquit
.sint
= -1;
612 safe_signal(SIGQUIT
, st
);
614 if (_ncl_oint
.sint
!= -1) {
615 st
= _ncl_oint
.shdl
, _ncl_oint
.sint
= -1;
616 safe_signal(SIGINT
, st
);
621 _ncl_term_mode(bool_t raw
)
623 struct termios
*tiosp
= &_ncl_tios
.told
;
628 /* Always requery the attributes, in case we've been moved from background
629 * to foreground or however else in between sessions */
630 tcgetattr(STDIN_FILENO
, tiosp
);
631 memcpy(&_ncl_tios
.tnew
, tiosp
, sizeof *tiosp
);
632 tiosp
= &_ncl_tios
.tnew
;
633 tiosp
->c_cc
[VMIN
] = 1;
634 tiosp
->c_cc
[VTIME
] = 0;
635 tiosp
->c_iflag
&= ~(ISTRIP
);
636 tiosp
->c_lflag
&= ~(ECHO
/*| ECHOE | ECHONL */| ICANON
| IEXTEN
);
638 tcsetattr(STDIN_FILENO
, TCSADRAIN
, tiosp
);
642 _ncl_check_grow(struct line
*l
, size_t no SMALLOC_DEBUG_ARGS
)
644 size_t i
= (l
->topins
+ no
) * sizeof(struct cell
) + 2 * sizeof(struct cell
);
646 if (i
> *l
->x_bufsize
) {
650 *l
->x_buf
= (srealloc_safe
)(*l
->x_buf
, i SMALLOC_DEBUG_ARGSCALL
);
655 _ncl_bs_eof_dvup(struct cell
*cap
, size_t i
)
660 memmove(cap
, cap
+ 1, i
* sizeof(*cap
));
662 /* And.. the (rest of the) visual update */
663 for (j
= 0; j
< i
; ++j
)
664 fwrite(cap
[j
].cbuf
, sizeof *cap
->cbuf
, cap
[j
].count
, stdout
);
665 fputs(" \b", stdout
);
666 for (j
= 0; j
< i
; ++j
)
671 _ncl_wboundary(struct line
*l
, ssize_t dir
)
673 size_t c
= l
->cursor
, t
= l
->topins
;
685 --t
, --c
; /* Unsigned wrapping may occur (twice), then */
687 for (i
= 0, cap
= l
->line
.cells
, anynon
= FAL0
;;) {
688 wchar_t wc
= cap
[c
+ dir
].wc
;
689 if (iswblank(wc
) || iswpunct(wc
)) {
707 _ncl_cell2dat(struct line
*l
)
712 for (i
= 0; i
< l
->topins
; ++i
) {
713 struct cell
*cap
= l
->line
.cells
+ i
;
714 memcpy(l
->line
.cbuf
+ len
, cap
->cbuf
, cap
->count
);
717 l
->line
.cbuf
[len
] = '\0';
722 _ncl_cell2save(struct line
*l
)
727 l
->savec
.s
= NULL
, l
->savec
.l
= 0;
731 for (cap
= l
->line
.cells
, len
= i
= 0; i
< l
->topins
; ++cap
, ++i
)
735 l
->savec
.s
= salloc(len
+ 1);
737 for (cap
= l
->line
.cells
, len
= i
= 0; i
< l
->topins
; ++cap
, ++i
) {
738 memcpy(l
->savec
.s
+ len
, cap
->cbuf
, cap
->count
);
741 l
->savec
.s
[len
] = '\0';
747 _ncl_khome(struct line
*l
, bool_t dobell
)
749 size_t c
= l
->cursor
;
760 _ncl_kend(struct line
*l
)
762 ssize_t i
= (ssize_t
)(l
->topins
- l
->cursor
);
765 l
->cursor
= l
->topins
;
767 fputs(l
->nd
, stdout
);
773 _ncl_kbs(struct line
*l
)
775 ssize_t c
= l
->cursor
, t
= l
->topins
;
782 _ncl_bs_eof_dvup(l
->line
.cells
+ c
, t
);
788 _ncl_kkill(struct line
*l
, bool_t dobell
)
790 size_t j
, c
= l
->cursor
, i
= (size_t)(l
->topins
- c
);
794 for (j
= i
; j
!= 0; --j
)
796 for (j
= i
; j
!= 0; --j
)
803 _ncl_keof(struct line
*l
)
805 size_t c
= l
->cursor
, t
= l
->topins
;
806 ssize_t i
= (ssize_t
)(t
- c
);
810 _ncl_bs_eof_dvup(l
->line
.cells
+ c
, --i
);
811 } else if (t
== 0 && ! boption("ignoreeof")) {
823 _ncl_kleft(struct line
*l
)
833 _ncl_kright(struct line
*l
)
835 if (l
->cursor
< l
->topins
) {
837 fputs(l
->nd
, stdout
);
843 _ncl_krefresh(struct line
*l
)
850 fputs(l
->prompt
, stdout
);
851 for (cap
= l
->line
.cells
, i
= l
->topins
; i
> 0; ++cap
, --i
)
852 fwrite(cap
->cbuf
, sizeof *cap
->cbuf
, cap
->count
, stdout
);
853 for (i
= l
->topins
- l
->cursor
; i
> 0; --i
)
858 _ncl_kht(struct line
*l
)
860 struct str orig
, bot
, topp
, sub
, exp
;
861 struct cell
*cword
, *ctop
, *cx
;
862 bool_t set_savec
= FAL0
;
865 /* We cannot expand an empty line */
869 /* Get plain line data; if this is the first expansion/xy, update the
870 * very original content so that ^G gets the origin back */
880 cword
= l
->line
.cells
;
881 ctop
= cword
+ l
->cursor
;
883 /* topp: separate data right of cursor */
884 if ((cx
= cword
+ l
->topins
) != ctop
) {
885 for (rv
= 0; cx
> ctop
; --cx
)
888 topp
.s
= orig
.s
+ orig
.l
- rv
;
890 topp
.s
= NULL
, topp
.l
= 0;
892 /* bot, sub: we cannot expand the entire data left of cursor, but only
893 * the last "word", so separate them */
894 while (cx
> cword
&& ! iswspace(cx
[-1].wc
))
896 for (rv
= 0; cword
< cx
; ++cword
)
906 sub
.s
= savestrbuf(sub
.s
, sub
.l
);
907 /* TODO there is a TODO note upon fexpand() with multi-return;
908 * TODO if that will change, the if() below can be simplified */
909 /* Super-Heavy-Metal: block all sigs, avoid leaks on jump */
911 exp
.s
= fexpand(sub
.s
, _CL_TAB_FEXP_FL
);
914 if (exp
.s
!= NULL
&& (exp
.l
= strlen(exp
.s
)) > 0 &&
915 (exp
.l
!= sub
.l
|| strcmp(exp
.s
, sub
.s
))) {
916 /* TODO cramp expansion length to MAX_INPUT, or 255 if not defined;
917 * TODO the problem is that we loose control otherwise; in the best
918 * TODO case the user can control via ^A and ^K etc., but be safe;
919 * TODO we cannot simply adjust fexpand() because we don't know how
920 * TODO that is implemented... The real solution would be to check
921 * TODO wether we fit on a line, and start a pager if not.
922 * TODO However, that should be part of a real tab-COMPLETION, then,
923 * TODO i.e., don't EXPAND, but SHOW COMPLETIONS, page-wise if needed.
924 * TODO And: MAX_INPUT is dynamic: pathconf(2), _SC_MAX_INPUT:
925 * TODO Note how hacky this is in respect to excess of final string */
927 # define MAX_INPUT 255 /* (_POSIX_MAX_INPUT = 255 as of Issue 7 TC1) */
929 if (exp
.l
>= MAX_INPUT
) {
930 char const cp
[] = "[maximum line size exceeded]";
932 exp
.l
= sizeof(cp
) - 1;
934 orig
.l
= bot
.l
+ exp
.l
+ topp
.l
;
935 orig
.s
= salloc(orig
.l
+ 1);
937 memcpy(orig
.s
, bot
.s
, rv
);
938 memcpy(orig
.s
+ rv
, exp
.s
, exp
.l
);
940 memcpy(orig
.s
+ rv
, topp
.s
, topp
.l
);
951 /* If we've provided a default content, but failed to expand, there is
952 * nothing we can "revert to": drop that default again */
954 l
->savec
.s
= NULL
, l
->savec
.l
= 0;
961 __ncl_khist_shared(struct line
*l
, struct hist
*hp
)
965 if ((l
->hist
= hp
) != NULL
) {
966 l
->defc
.s
= savestrbuf(hp
->dat
, hp
->len
);
981 _ncl_khist(struct line
*l
, bool_t backwd
)
985 /* If we're not in history mode yet, save line content;
986 * also, disallow forward search, then, and, of course, bail unless we
987 * do have any history at all */
988 if ((hp
= l
->hist
) == NULL
) {
991 if ((hp
= _ncl_hist
) == NULL
)
997 hp
= backwd
? hp
->older
: hp
->younger
;
999 return __ncl_khist_shared(l
, hp
);
1003 _ncl_krhist(struct line
*l
)
1005 struct str orig_savec
;
1006 struct hist
*hp
= NULL
;
1008 /* We cannot complete an empty line */
1009 if (l
->topins
== 0) {
1010 /* XXX The upcoming hard reset would restore a set savec buffer,
1011 * XXX so forcefully reset that. A cleaner solution would be to
1012 * XXX reset it whenever a restore is no longer desired */
1013 l
->savec
.s
= NULL
, l
->savec
.l
= 0;
1016 if ((hp
= l
->hist
) == NULL
) {
1017 if ((hp
= _ncl_hist
) == NULL
)
1019 orig_savec
.s
= NULL
;
1020 } else if ((hp
= hp
->older
) == NULL
)
1023 orig_savec
= l
->savec
;
1025 if (orig_savec
.s
== NULL
)
1027 for (; hp
!= NULL
; hp
= hp
->older
)
1028 if (is_prefix(l
->savec
.s
, hp
->dat
))
1030 if (orig_savec
.s
!= NULL
)
1031 l
->savec
= orig_savec
;
1033 return __ncl_khist_shared(l
, hp
);
1037 _ncl_kbwddelw(struct line
*l
)
1040 size_t c
= l
->cursor
, t
, j
;
1043 i
= _ncl_wboundary(l
, -1);
1054 cap
= l
->line
.cells
+ c
;
1056 if (t
!= l
->cursor
) {
1058 memmove(cap
, cap
+ i
, j
* sizeof(*cap
));
1061 for (j
= i
; j
> 0; --j
)
1063 for (j
= l
->topins
- c
; j
> 0; ++cap
, --j
)
1064 fwrite(cap
[0].cbuf
, sizeof *cap
->cbuf
, cap
[0].count
, stdout
);
1065 for (j
= i
; j
> 0; --j
)
1067 for (j
= t
- c
; j
> 0; --j
)
1073 _ncl_kgow(struct line
*l
, ssize_t dir
)
1075 ssize_t i
= _ncl_wboundary(l
, dir
);
1089 fputs(l
->nd
, stdout
);
1096 _ncl_kother(struct line
*l
, wchar_t wc
)
1098 /* Append if at EOL, insert otherwise;
1099 * since we may move around character-wise, always use a fresh ps */
1101 struct cell cell
, *cap
;
1104 /* First init a cell and see wether we'll really handle this wc */
1106 memset(&ps
, 0, sizeof ps
);
1107 i
= wcrtomb(cell
.cbuf
, wc
, &ps
);
1110 cell
.count
= (ui_it
)i
;
1111 if (enc_has_state
) {
1112 i
= wcrtomb(cell
.cbuf
+ i
, L
'\0', &ps
);
1115 else if (--i
< MB_LEN_MAX
)
1116 cell
.count
+= (ui_it
)i
;
1121 /* Yes, we will! Place it in the array */
1123 i
= l
->topins
++ - c
;
1124 cap
= l
->line
.cells
+ c
;
1126 memmove(cap
+ 1, cap
, i
* sizeof(cell
));
1127 memcpy(cap
, &cell
, sizeof cell
);
1129 /* And update visual */
1132 fwrite(cap
->cbuf
, sizeof *cap
->cbuf
, cap
->count
, stdout
);
1133 while ((++cap
, i
-- != 0));
1140 _ncl_readline(char const *prompt
, char **buf
, size_t *bufsize
, size_t len
1143 /* We want to save code, yet we may have to incorporate a lines'
1144 * default content and / or default input to switch back to after some
1145 * history movement; let "len > 0" mean "have to display some data
1146 * buffer", and only otherwise read(2) it */
1149 char cbuf_base
[MB_LEN_MAX
* 2], *cbuf
, *cbufp
;
1153 memset(&l
, 0, sizeof l
);
1156 l
.defc
.s
= savestrbuf(*buf
, len
);
1160 if ((l
.nd
= voption("line-editor-cursor-right")) == NULL
)
1163 l
.x_bufsize
= bufsize
;
1166 fputs(prompt
, stdout
);
1170 memset(ps
, 0, sizeof ps
);
1171 /* TODO: NCL: we should output the reset sequence when we jrestart:
1172 * TODO: NCL: if we are using a stateful encoding? !
1173 * TODO: NCL: in short: this is not yet well understood */
1175 _ncl_check_grow(&l
, len SMALLOC_DEBUG_ARGSCALL
);
1177 /* Normal read(2)? Else buffer-takeover: speed this one up */
1182 assert(l
.defc
.l
> 0 && l
.defc
.s
!= NULL
);
1184 cbuf
= l
.defc
.s
+ (l
.defc
.l
- len
);
1188 /* Read in the next complete multibyte character */
1191 if ((rv
= read(STDIN_FILENO
, cbufp
, 1)) < 1) {
1192 if (errno
== EINTR
) /* xxx #if !SA_RESTART ? */
1199 /* Ach! the ISO C multibyte handling!
1200 * Encodings with locking shift states cannot really be helped, since
1201 * it is impossible to only query the shift state, as opposed to the
1202 * entire shift state + character pair (via ISO C functions) */
1203 rv
= (ssize_t
)mbrtowc(&wc
, cbuf
, (size_t)(cbufp
- cbuf
), ps
+ 0);
1205 /* Any error during take-over can only result in a hard reset;
1206 * Otherwise, if it's a hard error, or if too many redundant shift
1207 * sequences overflow our buffer, also perform a hard reset */
1208 if (len
!= 0 || rv
== -1 ||
1209 sizeof cbuf_base
== (size_t)(cbufp
- cbuf
)) {
1210 l
.savec
.s
= l
.defc
.s
= NULL
,
1211 l
.savec
.l
= l
.defc
.l
= len
= 0;
1216 /* Otherwise, due to the way we deal with the buffer, we need to
1217 * restore the mbstate_t from before this conversion */
1222 if (len
!= 0 && (len
-= (size_t)rv
) == 0)
1223 l
.defc
.s
= NULL
, l
.defc
.l
= 0;
1228 /* Don't interpret control bytes during buffer take-over */
1229 if (cbuf
!= cbuf_base
)
1232 case 'A' ^ 0x40: /* cursor home */
1233 _ncl_khome(&l
, TRU1
);
1235 case 'B' ^ 0x40: /* ("history backward") */
1236 if ((len
= _ncl_khist(&l
, TRU1
)) > 0)
1240 /* 'C': interrupt (CTRL-C) */
1241 case 'D' ^ 0x40: /* delete char forward if any, else EOF */
1242 if ((rv
= _ncl_keof(&l
)) < 0)
1245 case 'E' ^ 0x40: /* end of line */
1248 case 'F' ^ 0x40: /* history forward */
1251 if ((len
= _ncl_khist(&l
, FAL0
)) > 0)
1256 case 'H' ^ 0x40: /* backspace */
1260 case 'I' ^ 0x40: /* horizontal tab */
1261 if ((len
= _ncl_kht(&l
)) > 0)
1264 case 'J' ^ 0x40: /* NL (\n) */
1266 case 'G' ^ 0x40: /* full reset */
1269 case 'U' ^ 0x40: /* ^U: ^A + ^K */
1270 _ncl_khome(&l
, FAL0
);
1272 case 'K' ^ 0x40: /* kill from cursor to end of line */
1273 _ncl_kkill(&l
, (wc
== ('K' ^ 0x40) || l
.topins
== 0));
1274 /* (Handle full reset?) */
1275 if (wc
== ('G' ^ 0x40)) {
1277 if ((len
= l
.savec
.l
) != 0) {
1279 l
.savec
.s
= NULL
, l
.savec
.l
= 0;
1285 case 'L' ^ 0x40: /* repaint line */
1290 case 'O' ^ 0x40: /* cursor left */
1293 case 'P' ^ 0x40: /* cursor right */
1297 case 'R' ^ 0x40: /* reverse history search */
1298 if ((len
= _ncl_krhist(&l
)) > 0)
1304 /*case 'V' ^ 0x40: TODO*/ /* forward delete "word" */
1305 case 'W' ^ 0x40: /* backward delete "word" */
1308 case 'X' ^ 0x40: /* move cursor forward "word" */
1311 case 'Y' ^ 0x40: /* move cursor backward "word" */
1314 /* 'Z': suspend (CTRL-Z) */
1318 _ncl_kother(&l
, wc
);
1319 /* Don't clear the history during takeover..
1320 * ..and also avoid fflush()ing unless we've
1321 * worked the entire buffer */
1324 if (cbuf
== cbuf_base
)
1335 /* We have a completed input line, convert the struct cell data to its
1336 * plain character equivalent */
1340 len
= _ncl_cell2dat(&l
);
1352 size_t lsize
, cnt
, llen
;
1354 _ncl_oint
.sint
= _ncl_oquit
.sint
= _ncl_oterm
.sint
=
1355 _ncl_ohup
.sint
= _ncl_otstp
.sint
= _ncl_ottin
.sint
=
1356 _ncl_ottou
.sint
= -1;
1359 _ncl_hist_size_max
= hs
;
1367 hold_all_sigs(); /* XXX too heavy, yet we may jump even here!? */
1368 f
= fopen(v
, "r"); /* TODO HISTFILE LOAD: use linebuf pool */
1375 while (fgetline(&lbuf
, &lsize
, &cnt
, &llen
, f
, FAL0
) != NULL
) {
1376 if (llen
> 0 && lbuf
[llen
- 1] == '\n')
1377 lbuf
[--llen
] = '\0';
1378 if (llen
== 0 || lbuf
[0] == '#') /* xxx comments? noone! */
1380 _ncl_hist_load
= TRU1
;
1382 _ncl_hist_load
= FAL0
;
1389 rele_all_sigs(); /* XXX remove jumps */
1410 if ((hp
= _ncl_hist
) != NULL
)
1411 while (hp
->older
!= NULL
&& hs
-- != 0)
1414 hold_all_sigs(); /* too heavy, yet we may jump even here!? */
1415 f
= fopen(v
, "w"); /* TODO temporary + rename?! */
1418 if (fchmod(fileno(f
), S_IRUSR
| S_IWUSR
) != 0)
1421 for (; hp
!= NULL
; hp
= hp
->younger
) {
1422 fwrite(hp
->dat
, sizeof *hp
->dat
, hp
->len
, f
);
1428 rele_all_sigs(); /* XXX remove jumps */
1436 sigset_t nset
, oset
;
1440 /* We don't deal with SIGWINCH, yet get called from main.c */
1443 _ncl_term_mode(FAL0
);
1446 sigaddset(&nset
, sig
);
1447 sigprocmask(SIG_UNBLOCK
, &nset
, &oset
);
1449 /* When we come here we'll continue editing, so reestablish */
1450 sigprocmask(SIG_BLOCK
, &oset
, (sigset_t
*)NULL
);
1452 _ncl_term_mode(TRU1
);
1458 (tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
1463 /* Of course we have races here, but they cannot be avoided on POSIX
1464 * (except by even *more* actions) */
1466 _ncl_term_mode(TRU1
);
1467 nn
= _ncl_readline(prompt
, linebuf
, linesize
, n SMALLOC_DEBUG_ARGSCALL
);
1468 _ncl_term_mode(FAL0
);
1475 tty_addhist(char const *s
)
1477 /* Super-Heavy-Metal: block all sigs, avoid leaks+ on jump */
1478 size_t l
= strlen(s
);
1479 struct hist
*h
, *o
, *y
;
1481 _CL_CHECK_ADDHIST(s
, goto j_leave
);
1483 /* Eliminating duplicates is expensive, but simply inacceptable so
1484 * during the load of a potentially large history file! */
1485 if (! _ncl_hist_load
)
1486 for (h
= _ncl_hist
; h
!= NULL
; h
= h
->older
)
1487 if (h
->len
== l
&& strcmp(h
->dat
, s
) == 0) {
1492 if ((o
->younger
= y
) == NULL
)
1503 if (! _ncl_hist_load
&& _ncl_hist_size
>= _ncl_hist_size_max
) {
1504 (h
= _ncl_hist
->younger
1510 h
= smalloc((sizeof(struct hist
) - VFIELD_SIZEOF(struct hist
, dat
)) + l
+ 1);
1512 memcpy(h
->dat
, s
, l
+ 1);
1514 if ((h
->older
= _ncl_hist
) != NULL
)
1515 _ncl_hist
->younger
= h
;
1526 * The really-nothing-at-all implementation
1529 #ifndef HAVE_LINE_EDITOR
1545 (tty_readline
)(char const *prompt
, char **linebuf
, size_t *linesize
, size_t n
1549 * TODO The nothing-at-all tty layer even forces re-entering all the
1550 * TODO original data when re-editing a field
1552 bool_t doffl
= FAL0
;
1554 if (*prompt
!= '\0') {
1555 fputs(prompt
, stdout
);
1559 fprintf(stdout
, tr(511, "{former content: %.*s} "), (int)n
, *linebuf
);
1565 return (readline_restart
)(stdin
, linebuf
, linesize
,n SMALLOC_DEBUG_ARGSCALL
);
1569 tty_addhist(char const *s
)
1573 #endif /* ! HAVE_LINE_EDITOR */
1575 /* vim:set fenc=utf-8:s-it-mode */