Fix -u, change meaning of $USER to be EQ -u etc..
[s-mailx.git] / tty.c
blobd4be9283b132226603a10a42002946099f031073
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 "nail.h"
58 #ifdef HAVE_READLINE
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
64 # define __NCL
65 #endif
67 /* */
68 #define _CL_HISTFILE(S) \
69 do {\
70 S = voption("NAIL_HISTFILE");\
71 if ((S) != NULL)\
72 S = fexpand(S, FEXP_LOCAL);\
73 } while (0)
75 /* */
76 #define _CL_HISTSIZE(V) \
77 do {\
78 char const *__sv = voption("NAIL_HISTSIZE");\
79 long __rv;\
80 if (__sv == NULL || *__sv == '\0' ||\
81 (__rv = strtol(__sv, NULL, 10)) == 0)\
82 (V) = HIST_SIZE;\
83 else if (__rv < 0)\
84 (V) = 0;\
85 else\
86 (V) = __rv;\
87 } while (0)
89 /* */
90 #define _CL_CHECK_ADDHIST(S,NOACT) \
91 do {\
92 switch (*(S)) {\
93 case '\0':\
94 case ' ':\
95 NOACT;\
96 /* FALLTHRU */\
97 default:\
98 break;\
100 } while (0)
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
110 bool_t
111 yorn(char const *msg)
113 char *cp;
115 if (! (options & OPT_INTERACTIVE))
116 return TRU1;
117 do if ((cp = readstr_input(msg, NULL)) == NULL)
118 return FAL0;
119 while (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N');
120 return (*cp == 'y' || *cp == 'Y');
123 char *
124 getuser(char const *query)
126 char *user = NULL;
128 if (query == NULL)
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();
135 return user;
138 char *
139 getpassword(char const *query) /* FIXME encaps ttystate signal safe */
141 struct termios tios;
142 char *pass = NULL;
144 if (query == NULL)
145 query = tr(510, "Password: ");
146 fputs(query, stdout);
147 fflush(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)
164 fputc('\n', stdout);
165 return pass;
168 bool_t
169 getcredentials(char **user, char **pass)
171 bool_t rv = TRU1;
172 char *u = *user, *p = *pass;
174 if (u == NULL) {
175 if ((u = getuser(NULL)) == NULL)
176 rv = FAL0;
177 else if (p == NULL)
178 u = savestr(u);
179 *user = u;
182 if (p == NULL) {
183 if ((p = getpassword(NULL)) == NULL)
184 rv = FAL0;
185 *pass = p;
187 return rv;
191 * readline(3)
194 #ifdef HAVE_READLINE
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);
201 static int
202 _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;
209 rl_redisplay();
210 return 0;
213 void
214 tty_init(void)
216 long hs;
217 char *v;
219 _CL_HISTSIZE(hs);
221 rl_readline_name = UNCONST(uagent);
222 using_history();
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 */
229 _CL_HISTFILE(v);
230 if (v != NULL)
231 read_history(v);
234 void
235 tty_destroy(void)
237 char *v;
239 _CL_HISTFILE(v);
240 if (v != NULL)
241 write_history(v);
244 void
245 tty_signal(int sig)
247 sigset_t nset, oset;
249 switch (sig) {
250 # ifdef SIGWINCH
251 case SIGWINCH:
252 break;
253 # endif
254 case SIGHUP:
255 /* readline(3) doesn't catch it :( */
256 rl_free_line_state();
257 rl_cleanup_after_signal();
258 safe_signal(SIGHUP, _rl_shup);
259 sigemptyset(&nset);
260 sigaddset(&nset, sig);
261 sigprocmask(SIG_UNBLOCK, &nset, &oset);
262 kill(0, sig);
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();
268 break;
269 default:
270 break;
275 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
276 SMALLOC_DEBUG_ARGS)
278 int nn;
279 char *line;
281 if (n > 0) {
282 _rl_buf = *linebuf;
283 _rl_buflen = (int)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);
291 if (line == NULL) {
292 nn = -1;
293 goto jleave;
295 n = strlen(line);
297 if (n >= *linesize) {
298 *linesize = LINESIZE + n + 1;
299 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
301 memcpy(*linebuf, line, n);
302 (free)(line);
303 (*linebuf)[n] = '\0';
304 nn = (int)n;
305 jleave:
306 return nn;
309 void
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 */
316 jleave:
319 #endif /* HAVE_READLINE */
322 * BSD editline(3)
325 #ifdef HAVE_EDITLINE
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);
332 static char const *
333 _el_getprompt(void)
335 return _el_prompt;
338 void
339 tty_init(void)
341 HistEvent he;
342 long hs;
343 char *v;
345 _CL_HISTSIZE(hs);
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);
358 # if 0
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);
362 # endif
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 */
369 _CL_HISTFILE(v);
370 if (v != NULL)
371 history(_el_hcom, &he, H_LOAD, v);
374 void
375 tty_destroy(void)
377 HistEvent he;
378 char *v;
380 el_end(_el_el);
382 _CL_HISTFILE(v);
383 if (v != NULL)
384 history(_el_hcom, &he, H_SAVE, v);
385 history_end(_el_hcom);
388 void
389 tty_signal(int sig)
391 switch (sig) {
392 # ifdef SIGWINCH
393 case SIGWINCH:
394 el_resize(_el_el);
395 break;
396 # endif
397 default:
398 break;
403 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
404 SMALLOC_DEBUG_ARGS)
406 int nn;
407 char const *line;
409 _el_prompt = prompt;
410 if (n > 0)
411 el_push(_el_el, *linebuf);
412 line = el_gets(_el_el, &nn);
414 if (line == NULL) {
415 nn = -1;
416 goto jleave;
418 assert(nn >= 0);
419 n = (size_t)nn;
420 if (n > 0 && line[n - 1] == '\n')
421 nn = (int)--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';
429 jleave:
430 return nn;
433 void
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? */
439 HistEvent he;
440 int i;
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)
446 goto jadd;
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);
452 break;
454 jadd:
455 history(_el_hcom, &he, H_ENTER, s);
456 rele_all_sigs(); /* XXX remove jumps */
457 jleave:
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.
482 #ifdef __NCL
483 union xsighdl {
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));
489 struct xtios {
490 struct termios told;
491 struct termios tnew;
494 struct cell {
495 wchar_t wc;
496 ui_it count;
497 char cbuf[MB_LEN_MAX * 2]; /* .. plus reset shift sequence */
500 struct hist {
501 struct hist * older;
502 struct hist * younger;
503 size_t len;
504 char dat[VFIELD_SIZE(sizeof(size_t))];
507 struct line {
508 size_t cursor; /* Current cursor position */
509 size_t topins; /* Outermost cursor col set */
510 union {
511 char * cbuf; /* *x_buf */
512 struct cell * cells;
513 } line;
514 struct str defc; /* Current default content */
515 struct str savec; /* Saved default content */
516 struct hist * hist; /* History cursor */
517 char const * prompt;
518 char const * nd; /* Cursor right */
519 char ** x_buf; /* Caller pointers */
520 size_t * x_bufsize;
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);
565 static void
566 _ncl_sigs_up(void)
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);
584 static void
585 _ncl_sigs_down(void)
587 /* aaah.. atomic cas would be nice (but isn't it all redundant?) */
588 sighandler_type st;
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);
620 static void
621 _ncl_term_mode(bool_t raw)
623 struct termios *tiosp = &_ncl_tios.told;
625 if (!raw)
626 goto jleave;
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);
637 jleave:
638 tcsetattr(STDIN_FILENO, TCSADRAIN, tiosp);
641 static void
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) {
647 i <<= 1;
648 *l->x_bufsize = i;
649 l->line.cbuf =
650 *l->x_buf = (srealloc_safe)(*l->x_buf, i SMALLOC_DEBUG_ARGSCALL);
654 static void
655 _ncl_bs_eof_dvup(struct cell *cap, size_t i)
657 size_t j;
659 if (i > 0)
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)
667 putchar('\b');
670 static ssize_t
671 _ncl_wboundary(struct line *l, ssize_t dir)
673 size_t c = l->cursor, t = l->topins;
674 ssize_t i;
675 struct cell *cap;
676 bool_t anynon;
678 i = -1;
679 if (dir < 0) {
680 if (c == 0)
681 goto jleave;
682 } else if (c == t)
683 goto jleave;
684 else
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)) {
690 if (anynon)
691 break;
692 } else
693 anynon = TRU1;
694 ++i;
695 c += dir;
696 if (dir < 0) {
697 if (c == 0)
698 break;
699 } else if (c == t)
700 break;
702 jleave:
703 return i;
706 static ssize_t
707 _ncl_cell2dat(struct line *l)
709 size_t len = 0, i;
711 if (l->topins > 0)
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);
715 len += cap->count;
717 l->line.cbuf[len] = '\0';
718 return (ssize_t)len;
721 static void
722 _ncl_cell2save(struct line *l)
724 size_t len, i;
725 struct cell *cap;
727 l->savec.s = NULL, l->savec.l = 0;
728 if (l->topins == 0)
729 goto jleave;
731 for (cap = l->line.cells, len = i = 0; i < l->topins; ++cap, ++i)
732 len += cap->count;
734 l->savec.l = len;
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);
739 len += cap->count;
741 l->savec.s[len] = '\0';
742 jleave:
746 static void
747 _ncl_khome(struct line *l, bool_t dobell)
749 size_t c = l->cursor;
751 if (c > 0) {
752 l->cursor = 0;
753 while (c-- != 0)
754 putchar('\b');
755 } else if (dobell)
756 putchar('\a');
759 static void
760 _ncl_kend(struct line *l)
762 ssize_t i = (ssize_t)(l->topins - l->cursor);
764 if (i > 0) {
765 l->cursor = l->topins;
766 while (i-- != 0)
767 fputs(l->nd, stdout);
768 } else
769 putchar('\a');
772 static void
773 _ncl_kbs(struct line *l)
775 ssize_t c = l->cursor, t = l->topins;
777 if (c > 0) {
778 putchar('\b');
779 l->cursor = --c;
780 l->topins = --t;
781 t -= c;
782 _ncl_bs_eof_dvup(l->line.cells + c, t);
783 } else
784 putchar('\a');
787 static void
788 _ncl_kkill(struct line *l, bool_t dobell)
790 size_t j, c = l->cursor, i = (size_t)(l->topins - c);
792 if (i > 0) {
793 l->topins = c;
794 for (j = i; j != 0; --j)
795 putchar(' ');
796 for (j = i; j != 0; --j)
797 putchar('\b');
798 } else if (dobell)
799 putchar('\a');
802 static ssize_t
803 _ncl_keof(struct line *l)
805 size_t c = l->cursor, t = l->topins;
806 ssize_t i = (ssize_t)(t - c);
808 if (i > 0) {
809 l->topins = --t;
810 _ncl_bs_eof_dvup(l->line.cells + c, --i);
811 } else if (t == 0 && ! boption("ignoreeof")) {
812 fputs("^D", stdout);
813 fflush(stdout);
814 i = -1;
815 } else {
816 putchar('\a');
817 i = 0;
819 return i;
822 static void
823 _ncl_kleft(struct line *l)
825 if (l->cursor > 0) {
826 --l->cursor;
827 putchar('\b');
828 } else
829 putchar('\a');
832 static void
833 _ncl_kright(struct line *l)
835 if (l->cursor < l->topins) {
836 ++l->cursor;
837 fputs(l->nd, stdout);
838 } else
839 putchar('\a');
842 static void
843 _ncl_krefresh(struct line *l)
845 struct cell *cap;
846 size_t i;
848 putchar('\r');
849 if (*l->prompt)
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)
854 putchar('\b');
857 static size_t
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;
863 size_t rv = 0;
865 /* We cannot expand an empty line */
866 if (l->topins == 0)
867 goto jleave;
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 */
871 orig = l->savec;
872 _ncl_cell2save(l);
873 exp = l->savec;
874 if (orig.s != NULL)
875 l->savec = orig;
876 else
877 set_savec = TRU1;
878 orig = exp;
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)
886 rv += cx->count;
887 topp.l = rv;
888 topp.s = orig.s + orig.l - rv;
889 } else
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))
895 --cx;
896 for (rv = 0; cword < cx; ++cword)
897 rv += cword->count;
898 sub =
899 bot = orig;
900 bot.l = rv;
901 sub.s += rv;
902 sub.l -= rv;
903 sub.l -= topp.l;
905 if (sub.l > 0) {
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 */
910 hold_all_sigs();
911 exp.s = fexpand(sub.s, _CL_TAB_FEXP_FL);
912 rele_all_sigs();
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 */
926 # ifndef MAX_INPUT
927 # define MAX_INPUT 255 /* (_POSIX_MAX_INPUT = 255 as of Issue 7 TC1) */
928 # endif
929 if (exp.l >= MAX_INPUT) {
930 char const cp[] = "[maximum line size exceeded]";
931 exp.s = UNCONST(cp);
932 exp.l = sizeof(cp) - 1;
934 orig.l = bot.l + exp.l + topp.l;
935 orig.s = salloc(orig.l + 1);
936 rv = bot.l;
937 memcpy(orig.s, bot.s, rv);
938 memcpy(orig.s + rv, exp.s, exp.l);
939 rv += exp.l;
940 memcpy(orig.s + rv, topp.s, topp.l);
941 rv += topp.l;
942 orig.s[rv] = '\0';
944 l->defc = orig;
945 _ncl_khome(l, FAL0);
946 _ncl_kkill(l, FAL0);
947 goto jleave;
951 /* If we've provided a default content, but failed to expand, there is
952 * nothing we can "revert to": drop that default again */
953 if (set_savec)
954 l->savec.s = NULL, l->savec.l = 0;
955 rv = 0;
956 jleave:
957 return rv;
960 static size_t
961 __ncl_khist_shared(struct line *l, struct hist *hp)
963 size_t rv;
965 if ((l->hist = hp) != NULL) {
966 l->defc.s = savestrbuf(hp->dat, hp->len);
967 rv =
968 l->defc.l = hp->len;
969 if (l->topins > 0) {
970 _ncl_khome(l, FAL0);
971 _ncl_kkill(l, FAL0);
973 } else {
974 putchar('\a');
975 rv = 0;
977 return rv;
980 static size_t
981 _ncl_khist(struct line *l, bool_t backwd)
983 struct hist *hp;
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) {
989 if (! backwd)
990 goto jleave;
991 if ((hp = _ncl_hist) == NULL)
992 goto jleave;
993 _ncl_cell2save(l);
994 goto jleave;
997 hp = backwd ? hp->older : hp->younger;
998 jleave:
999 return __ncl_khist_shared(l, hp);
1002 static size_t
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;
1014 goto jleave;
1016 if ((hp = l->hist) == NULL) {
1017 if ((hp = _ncl_hist) == NULL)
1018 goto jleave;
1019 orig_savec.s = NULL;
1020 } else if ((hp = hp->older) == NULL)
1021 goto jleave;
1022 else
1023 orig_savec = l->savec;
1025 if (orig_savec.s == NULL)
1026 _ncl_cell2save(l);
1027 for (; hp != NULL; hp = hp->older)
1028 if (is_prefix(l->savec.s, hp->dat))
1029 break;
1030 if (orig_savec.s != NULL)
1031 l->savec = orig_savec;
1032 jleave:
1033 return __ncl_khist_shared(l, hp);
1036 static void
1037 _ncl_kbwddelw(struct line *l)
1039 ssize_t i;
1040 size_t c = l->cursor, t, j;
1041 struct cell *cap;
1043 i = _ncl_wboundary(l, -1);
1044 if (i <= 0) {
1045 if (i < 0)
1046 putchar('\a');
1047 goto jleave;
1050 c = l->cursor - i;
1051 t = l->topins;
1052 l->topins = t - i;
1053 l->cursor = c;
1054 cap = l->line.cells + c;
1056 if (t != l->cursor) {
1057 j = t - c + i;
1058 memmove(cap, cap + i, j * sizeof(*cap));
1061 for (j = i; j > 0; --j)
1062 putchar('\b');
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)
1066 putchar(' ');
1067 for (j = t - c; j > 0; --j)
1068 putchar('\b');
1069 jleave: ;
1072 static void
1073 _ncl_kgow(struct line *l, ssize_t dir)
1075 ssize_t i = _ncl_wboundary(l, dir);
1076 if (i <= 0) {
1077 if (i < 0)
1078 putchar('\a');
1079 goto jleave;
1082 if (dir < 0) {
1083 l->cursor -= i;
1084 while (i-- > 0)
1085 putchar('\b');
1086 } else {
1087 l->cursor += i;
1088 while (i-- > 0)
1089 fputs(l->nd, stdout);
1091 jleave:
1095 static void
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 */
1100 mbstate_t ps;
1101 struct cell cell, *cap;
1102 size_t i, c;
1104 /* First init a cell and see wether we'll really handle this wc */
1105 cell.wc = wc;
1106 memset(&ps, 0, sizeof ps);
1107 i = wcrtomb(cell.cbuf, wc, &ps);
1108 if (i > MB_LEN_MAX)
1109 goto jleave;
1110 cell.count = (ui_it)i;
1111 if (enc_has_state) {
1112 i = wcrtomb(cell.cbuf + i, L'\0', &ps);
1113 if (i == 1)
1115 else if (--i < MB_LEN_MAX)
1116 cell.count += (ui_it)i;
1117 else
1118 goto jleave;
1121 /* Yes, we will! Place it in the array */
1122 c = l->cursor++;
1123 i = l->topins++ - c;
1124 cap = l->line.cells + c;
1125 if (i > 0)
1126 memmove(cap + 1, cap, i * sizeof(cell));
1127 memcpy(cap, &cell, sizeof cell);
1129 /* And update visual */
1130 c = i;
1132 fwrite(cap->cbuf, sizeof *cap->cbuf, cap->count, stdout);
1133 while ((++cap, i-- != 0));
1134 while (c-- != 0)
1135 putchar('\b');
1136 jleave: ;
1139 static ssize_t
1140 _ncl_readline(char const *prompt, char **buf, size_t *bufsize, size_t len
1141 SMALLOC_DEBUG_ARGS)
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 */
1147 mbstate_t ps[2];
1148 struct line l;
1149 char cbuf_base[MB_LEN_MAX * 2], *cbuf, *cbufp;
1150 wchar_t wc;
1151 ssize_t rv;
1153 memset(&l, 0, sizeof l);
1154 l.line.cbuf = *buf;
1155 if (len != 0) {
1156 l.defc.s = savestrbuf(*buf, len);
1157 l.defc.l = len;
1159 l.prompt = prompt;
1160 if ((l.nd = voption("line-editor-cursor-right")) == NULL)
1161 l.nd = "\033[C";
1162 l.x_buf = buf;
1163 l.x_bufsize = bufsize;
1165 if (*prompt) {
1166 fputs(prompt, stdout);
1167 fflush(stdout);
1169 jrestart:
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 */
1174 for (;;) {
1175 _ncl_check_grow(&l, len SMALLOC_DEBUG_ARGSCALL);
1177 /* Normal read(2)? Else buffer-takeover: speed this one up */
1178 if (len == 0)
1179 cbufp =
1180 cbuf = cbuf_base;
1181 else {
1182 assert(l.defc.l > 0 && l.defc.s != NULL);
1183 cbufp =
1184 cbuf = l.defc.s + (l.defc.l - len);
1185 cbufp += len;
1188 /* Read in the next complete multibyte character */
1189 for (;;) {
1190 if (len == 0) {
1191 if ((rv = read(STDIN_FILENO, cbufp, 1)) < 1) {
1192 if (errno == EINTR) /* xxx #if !SA_RESTART ? */
1193 continue;
1194 goto jleave;
1196 ++cbufp;
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);
1204 if (rv <= 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;
1212 putchar('\a');
1213 wc = 'G';
1214 goto jreset;
1216 /* Otherwise, due to the way we deal with the buffer, we need to
1217 * restore the mbstate_t from before this conversion */
1218 ps[0] = ps[1];
1219 continue;
1222 if (len != 0 && (len -= (size_t)rv) == 0)
1223 l.defc.s = NULL, l.defc.l = 0;
1224 ps[1] = ps[0];
1225 break;
1228 /* Don't interpret control bytes during buffer take-over */
1229 if (cbuf != cbuf_base)
1230 goto jprint;
1231 switch (wc) {
1232 case 'A' ^ 0x40: /* cursor home */
1233 _ncl_khome(&l, TRU1);
1234 break;
1235 case 'B' ^ 0x40: /* ("history backward") */
1236 if ((len = _ncl_khist(&l, TRU1)) > 0)
1237 goto jrestart;
1238 wc = 'G' ^ 0x40;
1239 goto jreset;
1240 /* 'C': interrupt (CTRL-C) */
1241 case 'D' ^ 0x40: /* delete char forward if any, else EOF */
1242 if ((rv = _ncl_keof(&l)) < 0)
1243 goto jleave;
1244 break;
1245 case 'E' ^ 0x40: /* end of line */
1246 _ncl_kend(&l);
1247 break;
1248 case 'F' ^ 0x40: /* history forward */
1249 if (l.hist == NULL)
1250 goto jbell;
1251 if ((len = _ncl_khist(&l, FAL0)) > 0)
1252 goto jrestart;
1253 wc = 'G' ^ 0x40;
1254 goto jreset;
1255 /* 'G' below */
1256 case 'H' ^ 0x40: /* backspace */
1257 case '\177':
1258 _ncl_kbs(&l);
1259 break;
1260 case 'I' ^ 0x40: /* horizontal tab */
1261 if ((len = _ncl_kht(&l)) > 0)
1262 goto jrestart;
1263 goto jbell;
1264 case 'J' ^ 0x40: /* NL (\n) */
1265 goto jdone;
1266 case 'G' ^ 0x40: /* full reset */
1267 jreset:
1268 /* FALLTHRU */
1269 case 'U' ^ 0x40: /* ^U: ^A + ^K */
1270 _ncl_khome(&l, FAL0);
1271 /* FALLTHRU */
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)) {
1276 l.hist = NULL;
1277 if ((len = l.savec.l) != 0) {
1278 l.defc = l.savec;
1279 l.savec.s = NULL, l.savec.l = 0;
1280 } else
1281 len = l.defc.l;
1283 fflush(stdout);
1284 goto jrestart;
1285 case 'L' ^ 0x40: /* repaint line */
1286 _ncl_krefresh(&l);
1287 break;
1288 /* 'M': CR (\r) */
1289 /* 'N' */
1290 case 'O' ^ 0x40: /* cursor left */
1291 _ncl_kleft(&l);
1292 break;
1293 case 'P' ^ 0x40: /* cursor right */
1294 _ncl_kright(&l);
1295 break;
1296 /* 'Q': no code */
1297 case 'R' ^ 0x40: /* reverse history search */
1298 if ((len = _ncl_krhist(&l)) > 0)
1299 goto jrestart;
1300 wc = 'G' ^ 0x40;
1301 goto jreset;
1302 /* 'S': no code */
1303 /* 'U' above */
1304 /*case 'V' ^ 0x40: TODO*/ /* forward delete "word" */
1305 case 'W' ^ 0x40: /* backward delete "word" */
1306 _ncl_kbwddelw(&l);
1307 break;
1308 case 'X' ^ 0x40: /* move cursor forward "word" */
1309 _ncl_kgow(&l, +1);
1310 break;
1311 case 'Y' ^ 0x40: /* move cursor backward "word" */
1312 _ncl_kgow(&l, -1);
1313 break;
1314 /* 'Z': suspend (CTRL-Z) */
1315 default:
1316 jprint:
1317 if (iswprint(wc)) {
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 */
1322 if (len > 0)
1323 continue;
1324 if (cbuf == cbuf_base)
1325 l.hist = NULL;
1326 } else {
1327 jbell:
1328 putchar('\a');
1330 break;
1332 fflush(stdout);
1335 /* We have a completed input line, convert the struct cell data to its
1336 * plain character equivalent */
1337 jdone:
1338 putchar('\n');
1339 fflush(stdout);
1340 len = _ncl_cell2dat(&l);
1341 rv = (ssize_t)len;
1342 jleave:
1343 return rv;
1346 void
1347 tty_init(void)
1349 long hs;
1350 char *v, *lbuf;
1351 FILE *f;
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;
1358 _CL_HISTSIZE(hs);
1359 _ncl_hist_size_max = hs;
1360 if (hs == 0)
1361 goto jleave;
1363 _CL_HISTFILE(v);
1364 if (v == NULL)
1365 goto jleave;
1367 hold_all_sigs(); /* XXX too heavy, yet we may jump even here!? */
1368 f = fopen(v, "r"); /* TODO HISTFILE LOAD: use linebuf pool */
1369 if (f == NULL)
1370 goto jdone;
1372 lbuf = NULL;
1373 lsize = 0;
1374 cnt = fsize(f);
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! */
1379 continue;
1380 _ncl_hist_load = TRU1;
1381 tty_addhist(lbuf);
1382 _ncl_hist_load = FAL0;
1384 if (lbuf != NULL)
1385 free(lbuf);
1387 fclose(f);
1388 jdone:
1389 rele_all_sigs(); /* XXX remove jumps */
1390 jleave:
1394 void
1395 tty_destroy(void)
1397 long hs;
1398 char *v;
1399 struct hist *hp;
1400 FILE *f;
1402 _CL_HISTSIZE(hs);
1403 if (hs == 0)
1404 goto jleave;
1406 _CL_HISTFILE(v);
1407 if (v == NULL)
1408 goto jleave;
1410 if ((hp = _ncl_hist) != NULL)
1411 while (hp->older != NULL && hs-- != 0)
1412 hp = hp->older;
1414 hold_all_sigs(); /* too heavy, yet we may jump even here!? */
1415 f = fopen(v, "w"); /* TODO temporary + rename?! */
1416 if (f == NULL)
1417 goto jdone;
1418 if (fchmod(fileno(f), S_IRUSR | S_IWUSR) != 0)
1419 goto jclose;
1421 for (; hp != NULL; hp = hp->younger) {
1422 fwrite(hp->dat, sizeof *hp->dat, hp->len, f);
1423 putc('\n', f);
1425 jclose:
1426 fclose(f);
1427 jdone:
1428 rele_all_sigs(); /* XXX remove jumps */
1429 jleave:
1433 void
1434 tty_signal(int sig)
1436 sigset_t nset, oset;
1438 switch (sig) {
1439 case SIGWINCH:
1440 /* We don't deal with SIGWINCH, yet get called from main.c */
1441 break;
1442 default:
1443 _ncl_term_mode(FAL0);
1444 _ncl_sigs_down();
1445 sigemptyset(&nset);
1446 sigaddset(&nset, sig);
1447 sigprocmask(SIG_UNBLOCK, &nset, &oset);
1448 kill(0, sig);
1449 /* When we come here we'll continue editing, so reestablish */
1450 sigprocmask(SIG_BLOCK, &oset, (sigset_t*)NULL);
1451 _ncl_sigs_up();
1452 _ncl_term_mode(TRU1);
1453 break;
1458 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
1459 SMALLOC_DEBUG_ARGS)
1461 ssize_t nn;
1463 /* Of course we have races here, but they cannot be avoided on POSIX
1464 * (except by even *more* actions) */
1465 _ncl_sigs_up();
1466 _ncl_term_mode(TRU1);
1467 nn = _ncl_readline(prompt, linebuf, linesize, n SMALLOC_DEBUG_ARGSCALL);
1468 _ncl_term_mode(FAL0);
1469 _ncl_sigs_down();
1471 return (int)nn;
1474 void
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) {
1488 hold_all_sigs();
1489 o = h->older;
1490 y = h->younger;
1491 if (o != NULL) {
1492 if ((o->younger = y) == NULL)
1493 _ncl_hist = o;
1495 if (y != NULL)
1496 y->older = o;
1497 else
1498 _ncl_hist = o;
1499 goto jleave;
1501 hold_all_sigs();
1503 if (! _ncl_hist_load && _ncl_hist_size >= _ncl_hist_size_max) {
1504 (h = _ncl_hist->younger
1505 )->older = NULL;
1506 free(_ncl_hist);
1507 _ncl_hist = h;
1510 h = smalloc((sizeof(struct hist) - VFIELD_SIZEOF(struct hist, dat)) + l + 1);
1511 h->len = l;
1512 memcpy(h->dat, s, l + 1);
1513 jleave:
1514 if ((h->older = _ncl_hist) != NULL)
1515 _ncl_hist->younger = h;
1516 h->younger = NULL;
1517 _ncl_hist = h;
1519 rele_all_sigs();
1520 j_leave:
1523 #endif /* __NCL */
1526 * The really-nothing-at-all implementation
1529 #ifndef HAVE_LINE_EDITOR
1530 void
1531 tty_init(void)
1534 void
1535 tty_destroy(void)
1538 void
1539 tty_signal(int sig)
1541 (void)sig;
1545 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
1546 SMALLOC_DEBUG_ARGS)
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);
1556 doffl = TRU1;
1558 if (n > 0) {
1559 fprintf(stdout, tr(511, "{former content: %.*s} "), (int)n, *linebuf);
1560 n = 0;
1561 doffl = TRU1;
1563 if (doffl)
1564 fflush(stdout);
1565 return (readline_restart)(stdin, linebuf, linesize,n SMALLOC_DEBUG_ARGSCALL);
1568 void
1569 tty_addhist(char const *s)
1571 (void)s;
1573 #endif /* ! HAVE_LINE_EDITOR */
1575 /* vim:set fenc=utf-8:s-it-mode */