nail.1, mk-mk.in: include the VERSION in the manual
[s-mailx.git] / tty.c
blob4429e4240aacec259136763972b3d173a7a30f0a
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ TTY interaction.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
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
13 * are met:
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
37 * SUCH DAMAGE.
39 /* The NCL version is
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.
56 #include "rcv.h"
58 #include <errno.h>
59 #include <unistd.h>
61 #ifdef HAVE_READLINE
62 # include <readline/history.h>
63 # include <readline/readline.h>
64 #elif defined HAVE_EDITLINE
65 # include <histedit.h>
66 #elif defined HAVE_LINE_EDITOR
67 # define __NCL
68 # include <sys/stat.h>
69 # include <wchar.h>
70 # include <wctype.h>
71 #endif
73 #include "extern.h"
75 /* */
76 #define _CL_HISTFILE(S) \
77 do {\
78 S = voption("NAIL_HISTFILE");\
79 if ((S) != NULL)\
80 S = fexpand(S, FEXP_LOCAL);\
81 } while (0)
83 /* */
84 #define _CL_HISTSIZE(V) \
85 do {\
86 char const *__sv = voption("NAIL_HISTSIZE");\
87 long __rv;\
88 if (__sv == NULL || *__sv == '\0' ||\
89 (__rv = strtol(__sv, NULL, 10)) == 0)\
90 (V) = HIST_SIZE;\
91 else if (__rv < 0)\
92 (V) = 0;\
93 else\
94 (V) = __rv;\
95 } while (0)
97 /* */
98 #define _CL_CHECK_ADDHIST(S,NOACT) \
99 do {\
100 switch (*(S)) {\
101 case '\0':\
102 case ' ':\
103 NOACT;\
104 /* FALLTHRU */\
105 default:\
106 break;\
108 } while (0)
110 /* fexpand() flags for expand-on-tab */
111 #define _CL_TAB_FEXP_FL (FEXP_FULL | FEXP_SILENT | FEXP_MULTIOK)
114 * Because we have multiple identical implementations, change file layout a bit
115 * and place the implementations one after the other below the other externals
118 bool_t
119 yorn(char const *msg)
121 char *cp;
123 if (! (options & OPT_INTERACTIVE))
124 return TRU1;
125 do if ((cp = readstr_input(msg, NULL)) == NULL)
126 return FAL0;
127 while (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N');
128 return (*cp == 'y' || *cp == 'Y');
131 char *
132 getuser(char const *query)
134 char *user = NULL;
136 if (query == NULL)
137 query = tr(509, "User: ");
139 if (readline_input(LNED_NONE, query, &termios_state.ts_linebuf,
140 &termios_state.ts_linesize) >= 0)
141 user = termios_state.ts_linebuf;
142 termios_state_reset();
143 return user;
146 char *
147 getpassword(char const *query) /* FIXME encaps ttystate signal safe */
149 struct termios tios;
150 char *pass = NULL;
152 if (query == NULL)
153 query = tr(510, "Password: ");
154 fputs(query, stdout);
155 fflush(stdout);
157 if (options & OPT_TTYIN) {
158 tcgetattr(0, &termios_state.ts_tios);
159 memcpy(&tios, &termios_state.ts_tios, sizeof tios);
160 termios_state.ts_needs_reset = TRU1;
161 tios.c_iflag &= ~(ISTRIP);
162 tios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
163 tcsetattr(0, TCSAFLUSH, &tios);
166 if (readline_restart(stdin, &termios_state.ts_linebuf,
167 &termios_state.ts_linesize, 0) >= 0)
168 pass = termios_state.ts_linebuf;
169 termios_state_reset();
171 if (options & OPT_TTYIN)
172 fputc('\n', stdout);
173 return pass;
176 bool_t
177 getcredentials(char **user, char **pass)
179 bool_t rv = TRU1;
180 char *u = *user, *p = *pass;
182 if (u == NULL) {
183 if ((u = getuser(NULL)) == NULL)
184 rv = FAL0;
185 else if (p == NULL)
186 u = savestr(u);
187 *user = u;
190 if (p == NULL) {
191 if ((p = getpassword(NULL)) == NULL)
192 rv = FAL0;
193 *pass = p;
195 return rv;
199 * readline(3)
202 #ifdef HAVE_READLINE
203 static sighandler_type _rl_shup;
204 static char * _rl_buf; /* pre_input() hook: initial line */
205 static int _rl_buflen; /* content, and its length */
207 static int _rl_pre_input(void);
209 static int
210 _rl_pre_input(void)
212 /* Handle leftover data from \ escaped former line */
213 rl_extend_line_buffer(_rl_buflen + 10);
214 memcpy(rl_line_buffer, _rl_buf, _rl_buflen + 1);
215 rl_point = rl_end = _rl_buflen;
216 rl_pre_input_hook = (rl_hook_func_t*)NULL;
217 rl_redisplay();
218 return 0;
221 void
222 tty_init(void)
224 long hs;
225 char *v;
227 _CL_HISTSIZE(hs);
229 rl_readline_name = UNCONST(uagent);
230 using_history();
231 stifle_history((int)hs);
232 rl_read_init_file(NULL);
234 /* Because rl_read_init_file() may have introduced yet a different
235 * history size limit, simply load and incorporate the history, leave
236 * it up to readline(3) to do the rest */
237 _CL_HISTFILE(v);
238 if (v != NULL)
239 read_history(v);
242 void
243 tty_destroy(void)
245 char *v;
247 _CL_HISTFILE(v);
248 if (v != NULL)
249 write_history(v);
252 void
253 tty_signal(int sig)
255 sigset_t nset, oset;
257 switch (sig) {
258 # ifdef SIGWINCH
259 case SIGWINCH:
260 break;
261 # endif
262 case SIGHUP:
263 /* readline(3) doesn't catch it :( */
264 rl_free_line_state();
265 rl_cleanup_after_signal();
266 safe_signal(SIGHUP, _rl_shup);
267 sigemptyset(&nset);
268 sigaddset(&nset, sig);
269 sigprocmask(SIG_UNBLOCK, &nset, &oset);
270 kill(0, sig);
271 /* XXX When we come here we'll continue editing, so reestablish
272 * XXX cannot happen */
273 sigprocmask(SIG_BLOCK, &oset, (sigset_t*)NULL);
274 _rl_shup = safe_signal(SIGHUP, &tty_signal);
275 rl_reset_after_signal();
276 break;
277 default:
278 break;
283 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
284 SMALLOC_DEBUG_ARGS)
286 int nn;
287 char *line;
289 if (n > 0) {
290 _rl_buf = *linebuf;
291 _rl_buflen = (int)n;
292 rl_pre_input_hook = &_rl_pre_input;
295 _rl_shup = safe_signal(SIGHUP, &tty_signal);
296 line = readline(prompt);
297 safe_signal(SIGHUP, _rl_shup);
299 if (line == NULL) {
300 nn = -1;
301 goto jleave;
303 n = strlen(line);
305 if (n >= *linesize) {
306 *linesize = LINESIZE + n + 1;
307 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
309 memcpy(*linebuf, line, n);
310 (free)(line);
311 (*linebuf)[n] = '\0';
312 nn = (int)n;
313 jleave:
314 return nn;
317 void
318 tty_addhist(char const *s)
320 _CL_CHECK_ADDHIST(s, goto jleave);
321 hold_all_sigs(); /* XXX too heavy */
322 add_history(s); /* XXX yet we jump away! */
323 rele_all_sigs(); /* XXX remove jumps */
324 jleave:
327 #endif /* HAVE_READLINE */
330 * BSD editline(3)
333 #ifdef HAVE_EDITLINE
334 static EditLine * _el_el; /* editline(3) handle */
335 static History * _el_hcom; /* History handle for commline */
336 static char const * _el_prompt; /* Current prompt */
338 static char const * _el_getprompt(void);
340 static char const *
341 _el_getprompt(void)
343 return _el_prompt;
346 void
347 tty_init(void)
349 HistEvent he;
350 long hs;
351 char *v;
353 _CL_HISTSIZE(hs);
355 _el_hcom = history_init();
356 history(_el_hcom, &he, H_SETSIZE, (int)hs);
357 history(_el_hcom, &he, H_SETUNIQUE, 1);
359 _el_el = el_init(uagent, stdin, stdout, stderr);
360 el_set(_el_el, EL_SIGNAL, 1);
361 el_set(_el_el, EL_TERMINAL, NULL);
362 /* Need to set HIST before EDITOR, otherwise it won't work automatic */
363 el_set(_el_el, EL_HIST, &history, _el_hcom);
364 el_set(_el_el, EL_EDITOR, "emacs");
365 el_set(_el_el, EL_PROMPT, &_el_getprompt);
366 # if 0
367 el_set(_el_el, EL_ADDFN, "tab_complete",
368 "editline(3) internal completion function", &_el_file_cpl);
369 el_set(_el_el, EL_BIND, "^I", "tab_complete", NULL);
370 # endif
371 el_set(_el_el, EL_BIND, "^R", "ed-search-prev-history", NULL);
372 el_source(_el_el, NULL); /* Source ~/.editrc */
374 /* Because el_source() may have introduced yet a different history size
375 * limit, simply load and incorporate the history, leave it up to
376 * editline(3) to do the rest */
377 _CL_HISTFILE(v);
378 if (v != NULL)
379 history(_el_hcom, &he, H_LOAD, v);
382 void
383 tty_destroy(void)
385 HistEvent he;
386 char *v;
388 el_end(_el_el);
390 _CL_HISTFILE(v);
391 if (v != NULL)
392 history(_el_hcom, &he, H_SAVE, v);
393 history_end(_el_hcom);
396 void
397 tty_signal(int sig)
399 switch (sig) {
400 # ifdef SIGWINCH
401 case SIGWINCH:
402 el_resize(_el_el);
403 break;
404 # endif
405 default:
406 break;
411 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
412 SMALLOC_DEBUG_ARGS)
414 int nn;
415 char const *line;
417 _el_prompt = prompt;
418 if (n > 0)
419 el_push(_el_el, *linebuf);
420 line = el_gets(_el_el, &nn);
422 if (line == NULL) {
423 nn = -1;
424 goto jleave;
426 assert(nn >= 0);
427 n = (size_t)nn;
428 if (n > 0 && line[n - 1] == '\n')
429 nn = (int)--n;
431 if (n >= *linesize) {
432 *linesize = LINESIZE + n + 1;
433 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
435 memcpy(*linebuf, line, n);
436 (*linebuf)[n] = '\0';
437 jleave:
438 return nn;
441 void
442 tty_addhist(char const *s)
444 /* Enlarge meaning of unique .. to something that rocks;
445 * xxx unfortunately this is expensive to do with editline(3)
446 * xxx maybe it would be better to hook the ptfs instead? */
447 HistEvent he;
448 int i;
450 _CL_CHECK_ADDHIST(s, goto jleave);
452 hold_all_sigs(); /* XXX too heavy, yet we jump away! */
453 if (history(_el_hcom, &he, H_GETUNIQUE) < 0 || he.num == 0)
454 goto jadd;
456 for (i = history(_el_hcom, &he, H_FIRST); i >= 0;
457 i = history(_el_hcom, &he, H_NEXT))
458 if (strcmp(he.str, s) == 0) {
459 history(_el_hcom, &he, H_DEL, he.num);
460 break;
462 jadd:
463 history(_el_hcom, &he, H_ENTER, s);
464 rele_all_sigs(); /* XXX remove jumps */
465 jleave:
468 #endif /* HAVE_EDITLINE */
471 * NCL: our homebrew version (inspired from NetBSD sh(1) / dash(1)s hetio.c).
473 * Only used in interactive mode, simply use STDIN_FILENO as point of interest.
474 * We do not handle character widths because the terminal must deal with that
475 * anyway on the one hand, and also wcwidth(3) doesn't support zero-width
476 * characters by definition on the other. We're addicted.
478 * To avoid memory leaks etc. with the current codebase that simply longjmp(3)s
479 * we're forced to use the very same buffer--the one that is passed through to
480 * us from the outside--to store anything we need, i.e., a `struct cell[]', and
481 * convert that on-the-fly back to the plain char* result once we're done.
482 * To simplify our live, use savestr() buffers for all other needed memory
486 * TODO NCL: don't use that stupid .sint=-1 stuff, but simply block all signals
487 * TODO NCL: during handler de-/installation handling.
490 #ifdef __NCL
491 union xsighdl {
492 sighandler_type shdl; /* Try avoid races by setting */
493 sl_it sint; /* .sint=-1 when inactive */
495 CTA(sizeof(sl_it) >= sizeof(sighandler_type));
497 struct xtios {
498 struct termios told;
499 struct termios tnew;
502 struct cell {
503 wchar_t wc;
504 ui_it count;
505 char cbuf[MB_LEN_MAX * 2]; /* .. plus reset shift sequence */
508 struct hist {
509 struct hist * older;
510 struct hist * younger;
511 size_t len;
512 char dat[VFIELD_SIZE(sizeof(size_t))];
515 struct line {
516 size_t cursor; /* Current cursor position */
517 size_t topins; /* Outermost cursor col set */
518 union {
519 char * cbuf; /* *x_buf */
520 struct cell * cells;
521 } line;
522 struct str defc; /* Current default content */
523 struct str savec; /* Saved default content */
524 struct hist * hist; /* History cursor */
525 char const * prompt;
526 char const * nd; /* Cursor right */
527 char ** x_buf; /* Caller pointers */
528 size_t * x_bufsize;
531 static union xsighdl _ncl_oint;
532 static union xsighdl _ncl_oquit;
533 static union xsighdl _ncl_oterm;
534 static union xsighdl _ncl_ohup;
535 static union xsighdl _ncl_otstp;
536 static union xsighdl _ncl_ottin;
537 static union xsighdl _ncl_ottou;
538 static struct xtios _ncl_tios;
539 static struct hist * _ncl_hist;
540 static size_t _ncl_hist_size;
541 static size_t _ncl_hist_size_max;
542 static bool_t _ncl_hist_load;
544 static void _ncl_sigs_up(void);
545 static void _ncl_sigs_down(void);
547 static void _ncl_check_grow(struct line *l, size_t no SMALLOC_DEBUG_ARGS);
548 static void _ncl_bs_eof_dvup(struct cell *cap, size_t i);
549 static ssize_t _ncl_wboundary(struct line *l, ssize_t dir);
550 static ssize_t _ncl_cell2dat(struct line *l);
551 static void _ncl_cell2save(struct line *l);
553 static void _ncl_khome(struct line *l, bool_t dobell);
554 static void _ncl_kend(struct line *l);
555 static void _ncl_kbs(struct line *l);
556 static void _ncl_kkill(struct line *l, bool_t dobell);
557 static ssize_t _ncl_keof(struct line *l);
558 static void _ncl_kleft(struct line *l);
559 static void _ncl_kright(struct line *l);
560 static void _ncl_krefresh(struct line *l);
561 static size_t _ncl_kht(struct line *l);
562 static size_t __ncl_khist_shared(struct line *l, struct hist *hp);
563 static size_t _ncl_khist(struct line *l, bool_t backwd);
564 static size_t _ncl_krhist(struct line *l);
565 static void _ncl_kbwddelw(struct line *l);
566 static void _ncl_kgow(struct line *l, ssize_t dir);
567 static void _ncl_kother(struct line *l, wchar_t wc);
568 static ssize_t _ncl_readline(char const *prompt, char **buf, size_t *bufsize,
569 size_t len SMALLOC_DEBUG_ARGS);
571 static void
572 _ncl_sigs_up(void)
574 if (_ncl_oint.sint == -1)
575 _ncl_oint.shdl = safe_signal(SIGINT, &tty_signal);
576 if (_ncl_oquit.sint == -1)
577 _ncl_oquit.shdl = safe_signal(SIGQUIT, &tty_signal);
578 if (_ncl_oterm.sint == -1)
579 _ncl_oterm.shdl = safe_signal(SIGTERM, &tty_signal);
580 if (_ncl_ohup.sint == -1)
581 _ncl_ohup.shdl = safe_signal(SIGHUP, &tty_signal);
582 if (_ncl_otstp.sint == -1)
583 _ncl_otstp.shdl = safe_signal(SIGTSTP, &tty_signal);
584 if (_ncl_ottin.sint == -1)
585 _ncl_ottin.shdl = safe_signal(SIGTTIN, &tty_signal);
586 if (_ncl_ottou.sint == -1)
587 _ncl_ottou.shdl = safe_signal(SIGTTOU, &tty_signal);
590 static void
591 _ncl_sigs_down(void)
593 /* aaah.. atomic cas would be nice (but isn't it all redundant?) */
594 sighandler_type st;
596 if (_ncl_ottou.sint != -1) {
597 st = _ncl_ottou.shdl, _ncl_ottou.sint = -1;
598 safe_signal(SIGTTOU, st);
600 if (_ncl_ottin.sint != -1) {
601 st = _ncl_ottin.shdl, _ncl_ottin.sint = -1;
602 safe_signal(SIGTTIN, st);
604 if (_ncl_otstp.sint != -1) {
605 st = _ncl_otstp.shdl, _ncl_otstp.sint = -1;
606 safe_signal(SIGTSTP, st);
608 if (_ncl_ohup.sint != -1) {
609 st = _ncl_ohup.shdl, _ncl_ohup.sint = -1;
610 safe_signal(SIGHUP, st);
612 if (_ncl_oterm.sint != -1) {
613 st = _ncl_oterm.shdl, _ncl_oterm.sint = -1;
614 safe_signal(SIGTERM, st);
616 if (_ncl_oquit.sint != -1) {
617 st = _ncl_oquit.shdl, _ncl_oquit.sint = -1;
618 safe_signal(SIGQUIT, st);
620 if (_ncl_oint.sint != -1) {
621 st = _ncl_oint.shdl, _ncl_oint.sint = -1;
622 safe_signal(SIGINT, st);
626 static void
627 _ncl_check_grow(struct line *l, size_t no SMALLOC_DEBUG_ARGS)
629 size_t i = (l->topins + no) * sizeof(struct cell) + 2 * sizeof(struct cell);
631 if (i > *l->x_bufsize) {
632 i <<= 1;
633 *l->x_bufsize = i;
634 l->line.cbuf =
635 *l->x_buf = (srealloc)(*l->x_buf, i SMALLOC_DEBUG_ARGSCALL);
639 static void
640 _ncl_bs_eof_dvup(struct cell *cap, size_t i)
642 size_t j;
644 if (i > 0)
645 memmove(cap, cap + 1, i * sizeof(*cap));
647 /* And.. the (rest of the) visual update */
648 for (j = 0; j < i; ++j)
649 fwrite(cap[j].cbuf, sizeof *cap->cbuf, cap[j].count, stdout);
650 fputs(" \b", stdout);
651 for (j = 0; j < i; ++j)
652 putchar('\b');
655 static ssize_t
656 _ncl_wboundary(struct line *l, ssize_t dir)
658 size_t c = l->cursor, t = l->topins;
659 ssize_t i;
660 struct cell *cap;
661 bool_t anynon;
663 i = -1;
664 if (dir < 0) {
665 if (c == 0)
666 goto jleave;
667 } else if (c == t)
668 goto jleave;
669 else
670 --t, --c; /* Unsigned wrapping may occur (twice), then */
672 for (i = 0, cap = l->line.cells, anynon = FAL0;;) {
673 wchar_t wc = cap[c + dir].wc;
674 if (iswblank(wc) || iswpunct(wc)) {
675 if (anynon)
676 break;
677 } else
678 anynon = TRU1;
679 ++i;
680 c += dir;
681 if (dir < 0) {
682 if (c == 0)
683 break;
684 } else if (c == t)
685 break;
687 jleave:
688 return i;
691 static ssize_t
692 _ncl_cell2dat(struct line *l)
694 size_t len = 0, i;
696 if (l->topins > 0)
697 for (i = 0; i < l->topins; ++i) {
698 struct cell *cap = l->line.cells + i;
699 memcpy(l->line.cbuf + len, cap->cbuf, cap->count);
700 len += cap->count;
702 l->line.cbuf[len] = '\0';
703 return (ssize_t)len;
706 static void
707 _ncl_cell2save(struct line *l)
709 size_t len, i;
710 struct cell *cap;
712 l->savec.s = NULL, l->savec.l = 0;
713 if (l->topins == 0)
714 goto jleave;
716 for (cap = l->line.cells, len = i = 0; i < l->topins; ++cap, ++i)
717 len += cap->count;
719 l->savec.l = len;
720 l->savec.s = salloc(len + 1);
722 for (cap = l->line.cells, len = i = 0; i < l->topins; ++cap, ++i) {
723 memcpy(l->savec.s + len, cap->cbuf, cap->count);
724 len += cap->count;
726 l->savec.s[len] = '\0';
727 jleave:
731 static void
732 _ncl_khome(struct line *l, bool_t dobell)
734 size_t c = l->cursor;
736 if (c > 0) {
737 l->cursor = 0;
738 while (c-- != 0)
739 putchar('\b');
740 } else if (dobell)
741 putchar('\a');
744 static void
745 _ncl_kend(struct line *l)
747 ssize_t i = (ssize_t)(l->topins - l->cursor);
749 if (i > 0) {
750 l->cursor = l->topins;
751 while (i-- != 0)
752 fputs(l->nd, stdout);
753 } else
754 putchar('\a');
757 static void
758 _ncl_kbs(struct line *l)
760 ssize_t c = l->cursor, t = l->topins;
762 if (c > 0) {
763 putchar('\b');
764 l->cursor = --c;
765 l->topins = --t;
766 t -= c;
767 _ncl_bs_eof_dvup(l->line.cells + c, t);
768 } else
769 putchar('\a');
772 static void
773 _ncl_kkill(struct line *l, bool_t dobell)
775 size_t j, c = l->cursor, i = (size_t)(l->topins - c);
777 if (i > 0) {
778 l->topins = c;
779 for (j = i; j != 0; --j)
780 putchar(' ');
781 for (j = i; j != 0; --j)
782 putchar('\b');
783 } else if (dobell)
784 putchar('\a');
787 static ssize_t
788 _ncl_keof(struct line *l)
790 size_t c = l->cursor, t = l->topins;
791 ssize_t i = (ssize_t)(t - c);
793 if (i > 0) {
794 l->topins = --t;
795 _ncl_bs_eof_dvup(l->line.cells + c, --i);
796 } else if (t == 0 && ! boption("ignoreeof")) {
797 fputs("^D", stdout);
798 fflush(stdout);
799 i = -1;
800 } else {
801 putchar('\a');
802 i = 0;
804 return i;
807 static void
808 _ncl_kleft(struct line *l)
810 if (l->cursor > 0) {
811 --l->cursor;
812 putchar('\b');
813 } else
814 putchar('\a');
817 static void
818 _ncl_kright(struct line *l)
820 if (l->cursor < l->topins) {
821 ++l->cursor;
822 fputs(l->nd, stdout);
823 } else
824 putchar('\a');
827 static void
828 _ncl_krefresh(struct line *l)
830 struct cell *cap;
831 size_t i;
833 putchar('\r');
834 if (*l->prompt)
835 fputs(l->prompt, stdout);
836 for (cap = l->line.cells, i = l->topins; i > 0; ++cap, --i)
837 fwrite(cap->cbuf, sizeof *cap->cbuf, cap->count, stdout);
838 for (i = l->topins - l->cursor; i > 0; --i)
839 putchar('\b');
842 static size_t
843 _ncl_kht(struct line *l)
845 struct str orig, bot, topp, sub, exp;
846 struct cell *cword, *ctop, *cx;
847 bool_t set_savec = FAL0;
848 size_t rv = 0;
850 /* We cannot expand an empty line */
851 if (l->topins == 0)
852 goto jleave;
854 /* Get plain line data; if this is the first expansion/xy, update the
855 * very original content so that ^G gets the origin back */
856 orig = l->savec;
857 _ncl_cell2save(l);
858 exp = l->savec;
859 if (orig.s != NULL)
860 l->savec = orig;
861 else
862 set_savec = TRU1;
863 orig = exp;
865 cword = l->line.cells;
866 ctop = cword + l->cursor;
868 /* topp: separate data right of cursor */
869 if ((cx = cword + l->topins) != ctop) {
870 for (rv = 0; cx > ctop; --cx)
871 rv += cx->count;
872 topp.l = rv;
873 topp.s = orig.s + orig.l - rv;
874 } else
875 topp.s = NULL, topp.l = 0;
877 /* bot, sub: we cannot expand the entire data left of cursor, but only
878 * the last "word", so separate them */
879 while (cx > cword && ! iswspace(cx[-1].wc))
880 --cx;
881 for (rv = 0; cword < cx; ++cword)
882 rv += cword->count;
883 sub =
884 bot = orig;
885 bot.l = rv;
886 sub.s += rv;
887 sub.l -= rv;
888 sub.l -= topp.l;
890 if (sub.l > 0) {
891 sub.s = savestrbuf(sub.s, sub.l);
892 /* TODO there is a TODO note upon fexpand() with multi-return;
893 * TODO if that will change, the if() below can be simplified */
894 /* Super-Heavy-Metal: block all sigs, avoid leaks on jump */
895 hold_all_sigs();
896 exp.s = fexpand(sub.s, _CL_TAB_FEXP_FL);
897 rele_all_sigs();
899 if (exp.s != NULL && (exp.l = strlen(exp.s)) > 0 &&
900 (exp.l != sub.l || strcmp(exp.s, sub.s))) {
901 /* TODO cramp expansion length to MAX_INPUT, or 255 if not defined;
902 * TODO the problem is that we loose control otherwise; in the best
903 * TODO case the user can control via ^A and ^K etc., but be safe;
904 * TODO we cannot simply adjust fexpand() because we don't know how
905 * TODO that is implemented... The real solution would be to check
906 * TODO wether we fit on a line, and start a pager if not.
907 * TODO However, that should be part of a real tab-COMPLETION, then,
908 * TODO i.e., don't EXPAND, but SHOW COMPLETIONS, page-wise if needed.
909 * TODO And: MAX_INPUT is dynamic: pathconf(2), _SC_MAX_INPUT:
910 * TODO Note how hacky this is in respect to excess of final string */
911 # ifndef MAX_INPUT
912 # define MAX_INPUT 255 /* (_POSIX_MAX_INPUT = 255 as of Issue 7 TC1) */
913 # endif
914 if (exp.l >= MAX_INPUT) {
915 char const cp[] = "[maximum line size exceeded]";
916 exp.s = UNCONST(cp);
917 exp.l = sizeof(cp) - 1;
919 orig.l = bot.l + exp.l + topp.l;
920 orig.s = salloc(orig.l + 1);
921 rv = bot.l;
922 memcpy(orig.s, bot.s, rv);
923 memcpy(orig.s + rv, exp.s, exp.l);
924 rv += exp.l;
925 memcpy(orig.s + rv, topp.s, topp.l);
926 rv += topp.l;
927 orig.s[rv] = '\0';
929 l->defc = orig;
930 _ncl_khome(l, FAL0);
931 _ncl_kkill(l, FAL0);
932 goto jleave;
936 /* If we've provided a default content, but failed to expand, there is
937 * nothing we can "revert to": drop that default again */
938 if (set_savec)
939 l->savec.s = NULL, l->savec.l = 0;
940 rv = 0;
941 jleave:
942 return rv;
945 static size_t
946 __ncl_khist_shared(struct line *l, struct hist *hp)
948 size_t rv;
950 if ((l->hist = hp) != NULL) {
951 l->defc.s = savestrbuf(hp->dat, hp->len);
952 rv =
953 l->defc.l = hp->len;
954 if (l->topins > 0) {
955 _ncl_khome(l, FAL0);
956 _ncl_kkill(l, FAL0);
958 } else {
959 putchar('\a');
960 rv = 0;
962 return rv;
965 static size_t
966 _ncl_khist(struct line *l, bool_t backwd)
968 struct hist *hp;
970 /* If we're not in history mode yet, save line content;
971 * also, disallow forward search, then, and, of course, bail unless we
972 * do have any history at all */
973 if ((hp = l->hist) == NULL) {
974 if (! backwd)
975 goto jleave;
976 if ((hp = _ncl_hist) == NULL)
977 goto jleave;
978 _ncl_cell2save(l);
979 goto jleave;
982 hp = backwd ? hp->older : hp->younger;
983 jleave:
984 return __ncl_khist_shared(l, hp);
987 static size_t
988 _ncl_krhist(struct line *l)
990 struct str orig_savec;
991 struct hist *hp = NULL;
993 /* We cannot complete an empty line */
994 if (l->topins == 0) {
995 /* XXX The upcoming hard reset would restore a set savec buffer,
996 * XXX so forcefully reset that. A cleaner solution would be to
997 * XXX reset it whenever a restore is no longer desired */
998 l->savec.s = NULL, l->savec.l = 0;
999 goto jleave;
1001 if ((hp = l->hist) == NULL) {
1002 if ((hp = _ncl_hist) == NULL)
1003 goto jleave;
1004 orig_savec.s = NULL;
1005 } else if ((hp = hp->older) == NULL)
1006 goto jleave;
1007 else
1008 orig_savec = l->savec;
1010 if (orig_savec.s == NULL)
1011 _ncl_cell2save(l);
1012 for (; hp != NULL; hp = hp->older)
1013 if (is_prefix(l->savec.s, hp->dat))
1014 break;
1015 if (orig_savec.s != NULL)
1016 l->savec = orig_savec;
1017 jleave:
1018 return __ncl_khist_shared(l, hp);
1021 static void
1022 _ncl_kbwddelw(struct line *l)
1024 ssize_t i;
1025 size_t c = l->cursor, t, j;
1026 struct cell *cap;
1028 i = _ncl_wboundary(l, -1);
1029 if (i <= 0) {
1030 if (i < 0)
1031 putchar('\a');
1032 goto jleave;
1035 c = l->cursor - i;
1036 t = l->topins;
1037 l->topins = t - i;
1038 l->cursor = c;
1039 cap = l->line.cells + c;
1041 if (t != l->cursor) {
1042 j = t - c + i;
1043 memmove(cap, cap + i, j * sizeof(*cap));
1046 for (j = i; j > 0; --j)
1047 putchar('\b');
1048 for (j = l->topins - c; j > 0; ++cap, --j)
1049 fwrite(cap[0].cbuf, sizeof *cap->cbuf, cap[0].count, stdout);
1050 for (j = i; j > 0; --j)
1051 putchar(' ');
1052 for (j = t - c; j > 0; --j)
1053 putchar('\b');
1054 jleave: ;
1057 static void
1058 _ncl_kgow(struct line *l, ssize_t dir)
1060 ssize_t i = _ncl_wboundary(l, dir);
1061 if (i <= 0) {
1062 if (i < 0)
1063 putchar('\a');
1064 goto jleave;
1067 if (dir < 0) {
1068 l->cursor -= i;
1069 while (i-- > 0)
1070 putchar('\b');
1071 } else {
1072 l->cursor += i;
1073 while (i-- > 0)
1074 fputs(l->nd, stdout);
1076 jleave:
1080 static void
1081 _ncl_kother(struct line *l, wchar_t wc)
1083 /* Append if at EOL, insert otherwise;
1084 * since we may move around character-wise, always use a fresh ps */
1085 mbstate_t ps;
1086 struct cell cell, *cap;
1087 size_t i, c;
1089 /* First init a cell and see wether we'll really handle this wc */
1090 cell.wc = wc;
1091 memset(&ps, 0, sizeof ps);
1092 i = wcrtomb(cell.cbuf, wc, &ps);
1093 if (i > MB_LEN_MAX)
1094 goto jleave;
1095 cell.count = (ui_it)i;
1096 if (enc_has_state) {
1097 i = wcrtomb(cell.cbuf + i, L'\0', &ps);
1098 if (i == 1)
1100 else if (--i < MB_LEN_MAX)
1101 cell.count += (ui_it)i;
1102 else
1103 goto jleave;
1106 /* Yes, we will! Place it in the array */
1107 c = l->cursor++;
1108 i = l->topins++ - c;
1109 cap = l->line.cells + c;
1110 if (i > 0)
1111 memmove(cap + 1, cap, i * sizeof(cell));
1112 memcpy(cap, &cell, sizeof cell);
1114 /* And update visual */
1115 c = i;
1117 fwrite(cap->cbuf, sizeof *cap->cbuf, cap->count, stdout);
1118 while ((++cap, i-- != 0));
1119 while (c-- != 0)
1120 putchar('\b');
1121 jleave: ;
1124 static ssize_t
1125 _ncl_readline(char const *prompt, char **buf, size_t *bufsize, size_t len
1126 SMALLOC_DEBUG_ARGS)
1128 /* We want to save code, yet we may have to incorporate a lines'
1129 * default content and / or default input to switch back to after some
1130 * history movement; let "len > 0" mean "have to display some data
1131 * buffer", and only otherwise read(2) it */
1132 mbstate_t ps[2];
1133 struct line l;
1134 char cbuf_base[MB_LEN_MAX * 2], *cbuf, *cbufp;
1135 wchar_t wc;
1136 ssize_t rv;
1138 memset(&l, 0, sizeof l);
1139 l.line.cbuf = *buf;
1140 if (len != 0) {
1141 l.defc.s = savestrbuf(*buf, len);
1142 l.defc.l = len;
1144 l.prompt = prompt;
1145 if ((l.nd = voption("line-editor-cursor-right")) == NULL)
1146 l.nd = "\033[C";
1147 l.x_buf = buf;
1148 l.x_bufsize = bufsize;
1150 if (*prompt) {
1151 fputs(prompt, stdout);
1152 fflush(stdout);
1154 jrestart:
1155 memset(ps, 0, sizeof ps);
1156 /* TODO: NCL: we should output the reset sequence when we jrestart:
1157 * TODO: NCL: if we are using a stateful encoding? !
1158 * TODO: NCL: in short: this is not yet well understood */
1159 for (;;) {
1160 _ncl_check_grow(&l, len SMALLOC_DEBUG_ARGSCALL);
1162 /* Normal read(2)? Else buffer-takeover: speed this one up */
1163 if (len == 0)
1164 cbufp =
1165 cbuf = cbuf_base;
1166 else {
1167 assert(l.defc.l > 0 && l.defc.s != NULL);
1168 cbufp =
1169 cbuf = l.defc.s + (l.defc.l - len);
1170 cbufp += len;
1173 /* Read in the next complete multibyte character */
1174 for (;;) {
1175 if (len == 0) {
1176 if ((rv = read(STDIN_FILENO, cbufp, 1)) < 1) {
1177 if (errno == EINTR) /* xxx #if !SA_RESTART ? */
1178 continue;
1179 goto jleave;
1181 ++cbufp;
1184 /* Ach! the ISO C multibyte handling!
1185 * Encodings with locking shift states cannot really be helped, since
1186 * it is impossible to only query the shift state, as opposed to the
1187 * entire shift state + character pair (via ISO C functions) */
1188 rv = (ssize_t)mbrtowc(&wc, cbuf, (size_t)(cbufp - cbuf), ps + 0);
1189 if (rv <= 0) {
1190 /* Any error during take-over can only result in a hard reset;
1191 * Otherwise, if it's a hard error, or if too many redundant shift
1192 * sequences overflow our buffer, also perform a hard reset */
1193 if (len != 0 || rv == -1 ||
1194 sizeof cbuf_base == (size_t)(cbufp - cbuf)) {
1195 l.savec.s = l.defc.s = NULL,
1196 l.savec.l = l.defc.l = len = 0;
1197 putchar('\a');
1198 wc = 'G';
1199 goto jreset;
1201 /* Otherwise, due to the way we deal with the buffer, we need to
1202 * restore the mbstate_t from before this conversion */
1203 ps[0] = ps[1];
1204 continue;
1207 if (len != 0 && (len -= (size_t)rv) == 0)
1208 l.defc.s = NULL, l.defc.l = 0;
1209 ps[1] = ps[0];
1210 break;
1213 /* Don't interpret control bytes during buffer take-over */
1214 if (cbuf != cbuf_base)
1215 goto jprint;
1216 switch (wc) {
1217 case 'A' ^ 0x40: /* cursor home */
1218 _ncl_khome(&l, TRU1);
1219 break;
1220 case 'B' ^ 0x40: /* ("history backward") */
1221 if ((len = _ncl_khist(&l, TRU1)) > 0)
1222 goto jrestart;
1223 wc = 'G' ^ 0x40;
1224 goto jreset;
1225 /* 'C': interrupt (CTRL-C) */
1226 case 'D' ^ 0x40: /* delete char forward if any, else EOF */
1227 if ((rv = _ncl_keof(&l)) < 0)
1228 goto jleave;
1229 break;
1230 case 'E' ^ 0x40: /* end of line */
1231 _ncl_kend(&l);
1232 break;
1233 case 'F' ^ 0x40: /* history forward */
1234 if (l.hist == NULL)
1235 goto jbell;
1236 if ((len = _ncl_khist(&l, FAL0)) > 0)
1237 goto jrestart;
1238 wc = 'G' ^ 0x40;
1239 goto jreset;
1240 /* 'G' below */
1241 case 'H' ^ 0x40: /* backspace */
1242 case '\177':
1243 _ncl_kbs(&l);
1244 break;
1245 case 'I' ^ 0x40: /* horizontal tab */
1246 if ((len = _ncl_kht(&l)) > 0)
1247 goto jrestart;
1248 goto jbell;
1249 case 'J' ^ 0x40: /* NL (\n) */
1250 goto jdone;
1251 case 'G' ^ 0x40: /* full reset */
1252 jreset:
1253 /* FALLTHRU */
1254 case 'U' ^ 0x40: /* ^U: ^A + ^K */
1255 _ncl_khome(&l, FAL0);
1256 /* FALLTHRU */
1257 case 'K' ^ 0x40: /* kill from cursor to end of line */
1258 _ncl_kkill(&l, (wc == ('K' ^ 0x40) || l.topins == 0));
1259 /* (Handle full reset?) */
1260 if (wc == ('G' ^ 0x40)) {
1261 l.hist = NULL;
1262 if ((len = l.savec.l) != 0) {
1263 l.defc = l.savec;
1264 l.savec.s = NULL, l.savec.l = 0;
1265 } else
1266 len = l.defc.l;
1268 fflush(stdout);
1269 goto jrestart;
1270 case 'L' ^ 0x40: /* repaint line */
1271 _ncl_krefresh(&l);
1272 break;
1273 /* 'M': CR (\r) */
1274 /* 'N' */
1275 case 'O' ^ 0x40: /* cursor left */
1276 _ncl_kleft(&l);
1277 break;
1278 case 'P' ^ 0x40: /* cursor right */
1279 _ncl_kright(&l);
1280 break;
1281 /* 'Q': no code */
1282 case 'R' ^ 0x40: /* reverse history search */
1283 if ((len = _ncl_krhist(&l)) > 0)
1284 goto jrestart;
1285 wc = 'G' ^ 0x40;
1286 goto jreset;
1287 /* 'S': no code */
1288 /* 'U' above */
1289 /*case 'V' ^ 0x40: TODO*/ /* forward delete "word" */
1290 case 'W' ^ 0x40: /* backward delete "word" */
1291 _ncl_kbwddelw(&l);
1292 break;
1293 case 'X' ^ 0x40: /* move cursor forward "word" */
1294 _ncl_kgow(&l, +1);
1295 break;
1296 case 'Y' ^ 0x40: /* move cursor backward "word" */
1297 _ncl_kgow(&l, -1);
1298 break;
1299 /* 'Z': suspend (CTRL-Z) */
1300 default:
1301 jprint:
1302 if (iswprint(wc)) {
1303 _ncl_kother(&l, wc);
1304 /* Don't clear the history during takeover..
1305 * ..and also avoid fflush()ing unless we've
1306 * worked the entire buffer */
1307 if (len > 0)
1308 continue;
1309 if (cbuf == cbuf_base)
1310 l.hist = NULL;
1311 } else {
1312 jbell:
1313 putchar('\a');
1315 break;
1317 fflush(stdout);
1320 /* We have a completed input line, convert the struct cell data to its
1321 * plain character equivalent */
1322 jdone:
1323 putchar('\n');
1324 fflush(stdout);
1325 len = _ncl_cell2dat(&l);
1326 rv = (ssize_t)len;
1327 jleave:
1328 return rv;
1331 void
1332 tty_init(void)
1334 long hs;
1335 char *v, *lbuf;
1336 FILE *f;
1337 size_t lsize, cnt, llen;
1339 _ncl_oint.sint = _ncl_oquit.sint = _ncl_oterm.sint =
1340 _ncl_ohup.sint = _ncl_otstp.sint = _ncl_ottin.sint =
1341 _ncl_ottou.sint = -1;
1343 tcgetattr(STDIN_FILENO, &_ncl_tios.told);
1344 memcpy(&_ncl_tios.tnew, &_ncl_tios.told, sizeof _ncl_tios.tnew);
1345 _ncl_tios.tnew.c_cc[VMIN] = 1;
1346 _ncl_tios.tnew.c_cc[VTIME] = 0;
1347 _ncl_tios.tnew.c_iflag &= ~(ISTRIP);
1348 _ncl_tios.tnew.c_lflag &= ~(ECHO | ICANON | IEXTEN);
1350 _CL_HISTSIZE(hs);
1351 _ncl_hist_size_max = hs;
1352 if (hs == 0)
1353 goto jleave;
1355 _CL_HISTFILE(v);
1356 if (v == NULL)
1357 goto jleave;
1359 hold_all_sigs(); /* XXX too heavy, yet we may jump even here!? */
1360 f = fopen(v, "r"); /* TODO HISTFILE LOAD: use linebuf pool */
1361 if (f == NULL)
1362 goto jdone;
1364 lbuf = NULL;
1365 lsize = 0;
1366 cnt = fsize(f);
1367 while (fgetline(&lbuf, &lsize, &cnt, &llen, f, FAL0) != NULL) {
1368 if (llen > 0 && lbuf[llen - 1] == '\n')
1369 lbuf[--llen] = '\0';
1370 if (llen == 0 || lbuf[0] == '#') /* xxx comments? noone! */
1371 continue;
1372 _ncl_hist_load = TRU1;
1373 tty_addhist(lbuf);
1374 _ncl_hist_load = FAL0;
1376 if (lbuf != NULL)
1377 free(lbuf);
1379 fclose(f);
1380 jdone:
1381 rele_all_sigs(); /* XXX remove jumps */
1382 jleave:
1386 void
1387 tty_destroy(void)
1389 long hs;
1390 char *v;
1391 struct hist *hp;
1392 FILE *f;
1394 _CL_HISTSIZE(hs);
1395 if (hs == 0)
1396 goto jleave;
1398 _CL_HISTFILE(v);
1399 if (v == NULL)
1400 goto jleave;
1402 if ((hp = _ncl_hist) != NULL)
1403 while (hp->older != NULL && hs-- != 0)
1404 hp = hp->older;
1406 hold_all_sigs(); /* too heavy, yet we may jump even here!? */
1407 f = fopen(v, "w"); /* TODO temporary + rename?! */
1408 if (f == NULL)
1409 goto jdone;
1410 if (fchmod(fileno(f), S_IRUSR | S_IWUSR) != 0)
1411 goto jclose;
1413 for (; hp != NULL; hp = hp->younger) {
1414 fwrite(hp->dat, sizeof *hp->dat, hp->len, f);
1415 putc('\n', f);
1417 jclose:
1418 fclose(f);
1419 jdone:
1420 rele_all_sigs(); /* XXX remove jumps */
1421 jleave:
1425 void
1426 tty_signal(int sig)
1428 sigset_t nset, oset;
1430 switch (sig) {
1431 case SIGWINCH:
1432 /* We don't deal with SIGWINCH, yet get called from main.c */
1433 break;
1434 default:
1435 tcsetattr(STDIN_FILENO, TCSANOW, &_ncl_tios.told);
1436 _ncl_sigs_down();
1437 sigemptyset(&nset);
1438 sigaddset(&nset, sig);
1439 sigprocmask(SIG_UNBLOCK, &nset, &oset);
1440 kill(0, sig);
1441 /* When we come here we'll continue editing, so reestablish */
1442 sigprocmask(SIG_BLOCK, &oset, (sigset_t*)NULL);
1443 _ncl_sigs_up();
1444 tcsetattr(STDIN_FILENO, TCSANOW, &_ncl_tios.tnew);
1445 break;
1450 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
1451 SMALLOC_DEBUG_ARGS)
1453 ssize_t nn;
1455 /* Of course we have races here, but they cannot be avoided on POSIX
1456 * (except by even *more* actions) */
1457 _ncl_sigs_up();
1458 tcsetattr(STDIN_FILENO, TCSANOW, &_ncl_tios.tnew);
1459 nn = _ncl_readline(prompt, linebuf, linesize, n SMALLOC_DEBUG_ARGSCALL);
1460 tcsetattr(STDIN_FILENO, TCSANOW, &_ncl_tios.told);
1461 _ncl_sigs_down();
1463 return (int)nn;
1466 void
1467 tty_addhist(char const *s)
1469 /* Super-Heavy-Metal: block all sigs, avoid leaks+ on jump */
1470 size_t l = strlen(s);
1471 struct hist *h, *o, *y;
1473 _CL_CHECK_ADDHIST(s, goto j_leave);
1475 /* Eliminating duplicates is expensive, but simply inacceptable so
1476 * during the load of a potentially large history file! */
1477 if (! _ncl_hist_load)
1478 for (h = _ncl_hist; h != NULL; h = h->older)
1479 if (h->len == l && strcmp(h->dat, s) == 0) {
1480 hold_all_sigs();
1481 o = h->older;
1482 y = h->younger;
1483 if (o != NULL) {
1484 if ((o->younger = y) == NULL)
1485 _ncl_hist = o;
1487 if (y != NULL)
1488 y->older = o;
1489 else
1490 _ncl_hist = o;
1491 goto jleave;
1493 hold_all_sigs();
1495 if (! _ncl_hist_load && _ncl_hist_size >= _ncl_hist_size_max) {
1496 (h = _ncl_hist->younger
1497 )->older = NULL;
1498 free(_ncl_hist);
1499 _ncl_hist = h;
1502 h = smalloc((sizeof(struct hist) - VFIELD_SIZEOF(struct hist, dat)) + l + 1);
1503 h->len = l;
1504 memcpy(h->dat, s, l + 1);
1505 jleave:
1506 if ((h->older = _ncl_hist) != NULL)
1507 _ncl_hist->younger = h;
1508 h->younger = NULL;
1509 _ncl_hist = h;
1511 rele_all_sigs();
1512 j_leave:
1515 #endif /* __NCL */
1518 * The really-nothing-at-all implementation
1521 #ifndef HAVE_LINE_EDITOR
1522 void
1523 tty_init(void)
1526 void
1527 tty_destroy(void)
1530 void
1531 tty_signal(int sig)
1533 (void)sig;
1537 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
1538 SMALLOC_DEBUG_ARGS)
1541 * TODO The nothing-at-all tty layer even forces re-entering all the
1542 * TODO original data when re-editing a field
1544 bool_t doffl = FAL0;
1546 if (*prompt != '\0') {
1547 fputs(prompt, stdout);
1548 doffl = TRU1;
1550 if (n > 0) {
1551 fprintf(stdout, tr(511, "{former content: %.*s} "), (int)n, *linebuf);
1552 n = 0;
1553 doffl = TRU1;
1555 if (doffl)
1556 fflush(stdout);
1557 return (readline_restart)(stdin, linebuf, linesize,n SMALLOC_DEBUG_ARGSCALL);
1560 void
1561 tty_addhist(char const *s)
1563 (void)s;
1565 #endif /* ! HAVE_LINE_EDITOR */
1567 /* vim:set fenc=utf-8:s-it-mode */