mk-conf.sh:compiler_flags(): better handling of optimized flags
[s-mailx.git] / tty.c
blob2996b8ea378c260a58bd5db50b4fe0fbd5719109
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 - 2014 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 - 2014 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 #ifndef HAVE_AMALGAMATION
57 # include "nail.h"
58 #endif
60 #ifdef HAVE_READLINE
61 # include <readline/readline.h>
62 # ifdef HAVE_HISTORY
63 # include <readline/history.h>
64 # endif
65 #elif defined HAVE_EDITLINE
66 # include <histedit.h>
67 #endif
69 /* Shared history support macros */
70 #ifdef HAVE_HISTORY
71 # define _CL_HISTFILE(S) \
72 do {\
73 S = ok_vlook(NAIL_HISTFILE);\
74 if ((S) != NULL)\
75 S = fexpand(S, FEXP_LOCAL);\
76 } while (0)
77 # define _CL_HISTSIZE(V) \
78 do {\
79 char const *__sv = ok_vlook(NAIL_HISTSIZE);\
80 long __rv;\
81 if (__sv == NULL || *__sv == '\0' ||\
82 (__rv = strtol(__sv, NULL, 10)) == 0)\
83 (V) = HIST_SIZE;\
84 else if (__rv < 0)\
85 (V) = 0;\
86 else\
87 (V) = __rv;\
88 } while (0)
89 # define _CL_CHECK_ADDHIST(S,NOACT) \
90 do {\
91 switch (*(S)) {\
92 case '\0':\
93 case ' ':\
94 NOACT;\
95 /* FALLTHRU */\
96 default:\
97 break;\
99 } while (0)
100 # define C_HISTORY_SHARED \
101 char **argv = v;\
103 if (*argv == NULL)\
104 goto jlist;\
105 if (argv[1] != NULL)\
106 goto jerr;\
107 if (asccasecmp(*argv, "show") == 0)\
108 goto jlist;\
109 if (asccasecmp(*argv, "clear") == 0)\
110 goto jclear;\
111 jerr:\
112 fprintf(stderr, "Synopsis: history: %s\n", tr(431,\
113 "Either <show> (default) or <clear> the line editor history"));\
114 v = NULL;\
115 jleave:\
116 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
117 #endif /* HAVE_HISTORY */
119 /* fexpand() flags for expand-on-tab */
120 #define _CL_TAB_FEXP_FL (FEXP_FULL | FEXP_SILENT | FEXP_MULTIOK)
123 * Because we have multiple identical implementations, change file layout a bit
124 * and place the implementations one after the other below the other externals
127 FL bool_t
128 yorn(char const *msg)
130 char *cp;
132 if (!(options & OPT_INTERACTIVE))
133 return TRU1;
134 do if ((cp = readstr_input(msg, NULL)) == NULL)
135 return FAL0;
136 while (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N');
137 return (*cp == 'y' || *cp == 'Y');
140 FL char *
141 getuser(char const *query)
143 char *user = NULL;
145 if (query == NULL)
146 query = tr(509, "User: ");
148 if (readline_input(LNED_NONE, query, &termios_state.ts_linebuf,
149 &termios_state.ts_linesize) >= 0)
150 user = termios_state.ts_linebuf;
151 termios_state_reset();
152 return user;
155 FL char *
156 getpassword(char const *query) /* FIXME encaps ttystate signal safe */
158 struct termios tios;
159 char *pass = NULL;
161 if (query == NULL)
162 query = tr(510, "Password: ");
163 fputs(query, stdout);
164 fflush(stdout);
167 * FIXME everywhere: tcsetattr() generates SIGTTOU when we're not in
168 * foreground pgrp, and can fail with EINTR!!
170 if (options & OPT_TTYIN) {
171 tcgetattr(STDIN_FILENO, &termios_state.ts_tios);
172 memcpy(&tios, &termios_state.ts_tios, sizeof tios);
173 termios_state.ts_needs_reset = TRU1;
174 tios.c_iflag &= ~(ISTRIP);
175 tios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
176 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios);
179 if (readline_restart(stdin, &termios_state.ts_linebuf,
180 &termios_state.ts_linesize, 0) >= 0)
181 pass = termios_state.ts_linebuf;
182 termios_state_reset();
184 if (options & OPT_TTYIN)
185 fputc('\n', stdout);
186 return pass;
189 FL bool_t
190 getcredentials(char **user, char **pass)
192 bool_t rv = TRU1;
193 char *u = *user, *p = *pass;
195 if (u == NULL) {
196 if ((u = getuser(NULL)) == NULL)
197 rv = FAL0;
198 else if (p == NULL)
199 u = savestr(u);
200 *user = u;
203 if (p == NULL) {
204 if ((p = getpassword(NULL)) == NULL)
205 rv = FAL0;
206 *pass = p;
208 return rv;
212 * readline(3)
215 #ifdef HAVE_READLINE
216 static sighandler_type _rl_shup;
217 static char * _rl_buf; /* pre_input() hook: initial line */
218 static int _rl_buflen; /* content, and its length */
220 static int _rl_pre_input(void);
222 static int
223 _rl_pre_input(void)
225 /* Handle leftover data from \ escaped former line */
226 rl_extend_line_buffer(_rl_buflen + 10);
227 memcpy(rl_line_buffer, _rl_buf, _rl_buflen + 1);
228 rl_point = rl_end = _rl_buflen;
229 rl_pre_input_hook = (rl_hook_func_t*)NULL;
230 rl_redisplay();
231 return 0;
234 FL void
235 tty_init(void)
237 # ifdef HAVE_HISTORY
238 long hs;
239 char *v;
240 # endif
242 rl_readline_name = UNCONST(uagent);
243 # ifdef HAVE_HISTORY
244 _CL_HISTSIZE(hs);
245 using_history();
246 stifle_history((int)hs);
247 # endif
248 rl_read_init_file(NULL);
250 /* Because rl_read_init_file() may have introduced yet a different
251 * history size limit, simply load and incorporate the history, leave
252 * it up to readline(3) to do the rest */
253 # ifdef HAVE_HISTORY
254 _CL_HISTFILE(v);
255 if (v != NULL)
256 read_history(v);
257 # endif
260 FL void
261 tty_destroy(void)
263 # ifdef HAVE_HISTORY
264 char *v;
266 _CL_HISTFILE(v);
267 if (v != NULL)
268 write_history(v);
269 # endif
273 FL void
274 tty_signal(int sig)
276 sigset_t nset, oset;
278 switch (sig) {
279 # ifdef SIGWINCH
280 case SIGWINCH:
281 break;
282 # endif
283 case SIGHUP:
284 /* readline(3) doesn't catch it :( */
285 rl_free_line_state();
286 rl_cleanup_after_signal();
287 safe_signal(SIGHUP, _rl_shup);
288 sigemptyset(&nset);
289 sigaddset(&nset, sig);
290 sigprocmask(SIG_UNBLOCK, &nset, &oset);
291 kill(0, sig);
292 /* XXX When we come here we'll continue editing, so reestablish
293 * XXX cannot happen */
294 sigprocmask(SIG_BLOCK, &oset, (sigset_t*)NULL);
295 _rl_shup = safe_signal(SIGHUP, &tty_signal);
296 rl_reset_after_signal();
297 break;
298 default:
299 break;
303 FL int
304 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
305 SMALLOC_DEBUG_ARGS)
307 int nn;
308 char *line;
310 if (n > 0) {
311 _rl_buf = *linebuf;
312 _rl_buflen = (int)n;
313 rl_pre_input_hook = &_rl_pre_input;
316 _rl_shup = safe_signal(SIGHUP, &tty_signal);
317 line = readline(prompt != NULL ? prompt : "");
318 safe_signal(SIGHUP, _rl_shup);
320 if (line == NULL) {
321 nn = -1;
322 goto jleave;
324 n = strlen(line);
326 if (n >= *linesize) {
327 *linesize = LINESIZE + n + 1;
328 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
330 memcpy(*linebuf, line, n);
331 (free)(line);
332 (*linebuf)[n] = '\0';
333 nn = (int)n;
334 jleave:
335 return nn;
338 FL void
339 tty_addhist(char const *s)
341 # ifdef HAVE_HISTORY
342 _CL_CHECK_ADDHIST(s, goto jleave);
343 hold_all_sigs(); /* XXX too heavy */
344 add_history(s); /* XXX yet we jump away! */
345 rele_all_sigs(); /* XXX remove jumps */
346 jleave:
347 # endif
348 UNUSED(s);
351 # ifdef HAVE_HISTORY
352 FL int
353 c_history(void *v)
355 C_HISTORY_SHARED;
357 jlist: {
358 FILE *fp;
359 char *cp;
360 HIST_ENTRY **hl;
361 size_t i, b;
363 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
364 perror("tmpfile");
365 v = NULL;
366 goto jleave;
368 rm(cp);
369 Ftfree(&cp);
371 hl = history_list();
372 i = 0;
373 if (hl != NULL)
374 while (hl[i] != NULL)
375 ++i;
377 b = 0;
378 while (i-- > 0) {
379 size_t sl = strlen(hl[i]->line);
380 fprintf(fp, "%4lu. %-50.50s (%4lu+%lu bytes)\n",
381 (ul_it)i, hl[i]->line, (ul_it)b, (ul_it)sl);
382 b += sl;
385 page_or_print(fp, i);
386 Fclose(fp);
388 goto jleave;
390 jclear:
391 clear_history();
392 goto jleave;
394 # endif /* HAVE_HISTORY */
395 #endif /* HAVE_READLINE */
398 * BSD editline(3)
401 #ifdef HAVE_EDITLINE
402 static EditLine * _el_el; /* editline(3) handle */
403 static char const * _el_prompt; /* Current prompt */
404 # ifdef HAVE_HISTORY
405 static History * _el_hcom; /* History handle for commline */
406 # endif
408 static char const * _el_getprompt(void);
410 static char const *
411 _el_getprompt(void)
413 return _el_prompt;
416 FL void
417 tty_init(void)
419 # ifdef HAVE_HISTORY
420 HistEvent he;
421 long hs;
422 char *v;
423 # endif
425 # ifdef HAVE_HISTORY
426 _CL_HISTSIZE(hs);
427 _el_hcom = history_init();
428 history(_el_hcom, &he, H_SETSIZE, (int)hs);
429 history(_el_hcom, &he, H_SETUNIQUE, 1);
430 # endif
432 _el_el = el_init(uagent, stdin, stdout, stderr);
433 el_set(_el_el, EL_SIGNAL, 1);
434 el_set(_el_el, EL_TERMINAL, NULL);
435 /* Need to set HIST before EDITOR, otherwise it won't work automatic */
436 # ifdef HAVE_HISTORY
437 el_set(_el_el, EL_HIST, &history, _el_hcom);
438 # endif
439 el_set(_el_el, EL_EDITOR, "emacs");
440 # ifdef EL_PROMPT_ESC
441 el_set(_el_el, EL_PROMPT_ESC, &_el_getprompt, '\1');
442 # else
443 el_set(_el_el, EL_PROMPT, &_el_getprompt);
444 # endif
445 # if 0
446 el_set(_el_el, EL_ADDFN, "tab_complete",
447 "editline(3) internal completion function", &_el_file_cpl);
448 el_set(_el_el, EL_BIND, "^I", "tab_complete", NULL);
449 # endif
450 # ifdef HAVE_HISTORY
451 el_set(_el_el, EL_BIND, "^R", "ed-search-prev-history", NULL);
452 # endif
453 el_source(_el_el, NULL); /* Source ~/.editrc */
455 /* Because el_source() may have introduced yet a different history size
456 * limit, simply load and incorporate the history, leave it up to
457 * editline(3) to do the rest */
458 # ifdef HAVE_HISTORY
459 _CL_HISTFILE(v);
460 if (v != NULL)
461 history(_el_hcom, &he, H_LOAD, v);
462 # endif
465 FL void
466 tty_destroy(void)
468 # ifdef HAVE_HISTORY
469 HistEvent he;
470 char *v;
471 # endif
473 el_end(_el_el);
475 # ifdef HAVE_HISTORY
476 _CL_HISTFILE(v);
477 if (v != NULL)
478 history(_el_hcom, &he, H_SAVE, v);
479 history_end(_el_hcom);
480 # endif
483 FL void
484 tty_signal(int sig)
486 switch (sig) {
487 # ifdef SIGWINCH
488 case SIGWINCH:
489 el_resize(_el_el);
490 break;
491 # endif
492 default:
493 break;
497 FL int
498 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
499 SMALLOC_DEBUG_ARGS)
501 int nn;
502 char const *line;
504 _el_prompt = (prompt != NULL) ? prompt : "";
505 if (n > 0)
506 el_push(_el_el, *linebuf);
507 line = el_gets(_el_el, &nn);
509 if (line == NULL) {
510 nn = -1;
511 goto jleave;
513 assert(nn >= 0);
514 n = (size_t)nn;
515 if (n > 0 && line[n - 1] == '\n')
516 nn = (int)--n;
518 if (n >= *linesize) {
519 *linesize = LINESIZE + n + 1;
520 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
522 memcpy(*linebuf, line, n);
523 (*linebuf)[n] = '\0';
524 jleave:
525 return nn;
528 FL void
529 tty_addhist(char const *s)
531 # ifdef HAVE_HISTORY
532 /* Enlarge meaning of unique .. to something that rocks;
533 * xxx unfortunately this is expensive to do with editline(3)
534 * xxx maybe it would be better to hook the ptfs instead? */
535 HistEvent he;
536 int i;
538 _CL_CHECK_ADDHIST(s, goto jleave);
540 hold_all_sigs(); /* XXX too heavy, yet we jump away! */
541 if (history(_el_hcom, &he, H_GETUNIQUE) < 0 || he.num == 0)
542 goto jadd;
544 for (i = history(_el_hcom, &he, H_FIRST); i >= 0;
545 i = history(_el_hcom, &he, H_NEXT))
546 if (strcmp(he.str, s) == 0) {
547 history(_el_hcom, &he, H_DEL, he.num);
548 break;
550 jadd:
551 history(_el_hcom, &he, H_ENTER, s);
552 rele_all_sigs(); /* XXX remove jumps */
553 jleave:
554 # endif
555 UNUSED(s);
558 # ifdef HAVE_HISTORY
559 FL int
560 c_history(void *v)
562 C_HISTORY_SHARED;
564 jlist: {
565 HistEvent he;
566 FILE *fp;
567 char *cp;
568 size_t i, b;
569 int x;
571 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
572 perror("tmpfile");
573 v = NULL;
574 goto jleave;
576 rm(cp);
577 Ftfree(&cp);
579 i = (size_t)((history(_el_hcom, &he, H_GETSIZE) >= 0) ? he.num : 0);
580 b = 0;
581 for (x = history(_el_hcom, &he, H_FIRST); x >= 0;
582 x = history(_el_hcom, &he, H_NEXT)) {
583 size_t sl = strlen(he.str);
584 fprintf(fp, "%4lu. %-50.50s (%4lu+%lu bytes)\n",
585 (ul_it)i, he.str, (ul_it)b, (ul_it)sl);
586 --i;
587 b += sl;
590 page_or_print(fp, i);
591 Fclose(fp);
593 goto jleave;
595 jclear: {
596 HistEvent he;
597 history(_el_hcom, &he, H_CLEAR);
599 goto jleave;
601 # endif /* HAVE_HISTORY */
602 #endif /* HAVE_EDITLINE */
605 * NCL: our homebrew version (inspired from NetBSD sh(1) / dash(1)s hetio.c).
607 * Only used in interactive mode, simply use STDIN_FILENO as point of interest.
608 * We do not handle character widths because the terminal must deal with that
609 * anyway on the one hand, and also wcwidth(3) doesn't support zero-width
610 * characters by definition on the other. We're addicted.
612 * To avoid memory leaks etc. with the current codebase that simply longjmp(3)s
613 * we're forced to use the very same buffer--the one that is passed through to
614 * us from the outside--to store anything we need, i.e., a `struct cell[]', and
615 * convert that on-the-fly back to the plain char* result once we're done.
616 * To simplify our live, use savestr() buffers for all other needed memory
620 * TODO NCL: don't use that stupid .sint=-1 stuff, but simply block all signals
621 * TODO NCL: during handler de-/installation handling.
624 #ifdef HAVE_NCL
625 # ifndef MAX_INPUT
626 # define MAX_INPUT 255 /* (_POSIX_MAX_INPUT = 255 as of Issue 7 TC1) */
627 # endif
629 /* Since we simply fputs(3) the prompt, assume each character requires two
630 * visual cells -- and we need to restrict the maximum prompt size because
631 * of MAX_INPUT and our desire to have room for some error message left */
632 # define _PROMPT_VLEN(P) (strlen(P) * 2)
633 # define _PROMPT_MAX ((MAX_INPUT / 2) + (MAX_INPUT / 4))
635 union xsighdl {
636 sighandler_type shdl; /* Try avoid races by setting */
637 sl_it sint; /* .sint=-1 when inactive */
639 CTA(sizeof(sl_it) >= sizeof(sighandler_type));
641 struct xtios {
642 struct termios told;
643 struct termios tnew;
646 struct cell {
647 wchar_t wc;
648 ui_it count;
649 char cbuf[MB_LEN_MAX * 2]; /* .. plus reset shift sequence */
652 struct line {
653 size_t cursor; /* Current cursor position */
654 size_t topins; /* Outermost cursor col set */
655 union {
656 char * cbuf; /* *x_buf */
657 struct cell * cells;
658 } line;
659 struct str defc; /* Current default content */
660 struct str savec; /* Saved default content */
661 # ifdef HAVE_HISTORY
662 struct hist * hist; /* History cursor */
663 # endif
664 char const * prompt;
665 char const * nd; /* Cursor right */
666 char ** x_buf; /* Caller pointers */
667 size_t * x_bufsize;
670 # ifdef HAVE_HISTORY
671 struct hist {
672 struct hist * older;
673 struct hist * younger;
674 size_t len;
675 char dat[VFIELD_SIZE(sizeof(size_t))];
677 # endif
679 static union xsighdl _ncl_oint;
680 static union xsighdl _ncl_oquit;
681 static union xsighdl _ncl_oterm;
682 static union xsighdl _ncl_ohup;
683 static union xsighdl _ncl_otstp;
684 static union xsighdl _ncl_ottin;
685 static union xsighdl _ncl_ottou;
686 static struct xtios _ncl_tios;
687 # ifdef HAVE_HISTORY
688 static struct hist * _ncl_hist;
689 static size_t _ncl_hist_size;
690 static size_t _ncl_hist_size_max;
691 static bool_t _ncl_hist_load;
692 # endif
694 static void _ncl_sigs_up(void);
695 static void _ncl_sigs_down(void);
697 static void _ncl_term_mode(bool_t raw);
699 static void _ncl_check_grow(struct line *l, size_t no SMALLOC_DEBUG_ARGS);
700 static void _ncl_bs_eof_dvup(struct cell *cap, size_t i);
701 static ssize_t _ncl_wboundary(struct line *l, ssize_t dir);
702 static ssize_t _ncl_cell2dat(struct line *l);
703 # if defined HAVE_HISTORY || defined HAVE_TABEXPAND
704 static void _ncl_cell2save(struct line *l);
705 # endif
707 static void _ncl_khome(struct line *l, bool_t dobell);
708 static void _ncl_kend(struct line *l);
709 static void _ncl_kbs(struct line *l);
710 static void _ncl_kkill(struct line *l, bool_t dobell);
711 static ssize_t _ncl_keof(struct line *l);
712 static void _ncl_kleft(struct line *l);
713 static void _ncl_kright(struct line *l);
714 static void _ncl_krefresh(struct line *l);
715 static void _ncl_kbwddelw(struct line *l);
716 static void _ncl_kgow(struct line *l, ssize_t dir);
717 static void _ncl_kother(struct line *l, wchar_t wc);
718 # ifdef HAVE_HISTORY
719 static size_t __ncl_khist_shared(struct line *l, struct hist *hp);
720 static size_t _ncl_khist(struct line *l, bool_t backwd);
721 static size_t _ncl_krhist(struct line *l);
722 # endif
723 # ifdef HAVE_TABEXPAND
724 static size_t _ncl_kht(struct line *l);
725 # endif
726 static ssize_t _ncl_readline(char const *prompt, char **buf, size_t *bufsize,
727 size_t len SMALLOC_DEBUG_ARGS);
729 static void
730 _ncl_sigs_up(void)
732 if (_ncl_oint.sint == -1)
733 _ncl_oint.shdl = safe_signal(SIGINT, &tty_signal);
734 if (_ncl_oquit.sint == -1)
735 _ncl_oquit.shdl = safe_signal(SIGQUIT, &tty_signal);
736 if (_ncl_oterm.sint == -1)
737 _ncl_oterm.shdl = safe_signal(SIGTERM, &tty_signal);
738 if (_ncl_ohup.sint == -1)
739 _ncl_ohup.shdl = safe_signal(SIGHUP, &tty_signal);
740 if (_ncl_otstp.sint == -1)
741 _ncl_otstp.shdl = safe_signal(SIGTSTP, &tty_signal);
742 if (_ncl_ottin.sint == -1)
743 _ncl_ottin.shdl = safe_signal(SIGTTIN, &tty_signal);
744 if (_ncl_ottou.sint == -1)
745 _ncl_ottou.shdl = safe_signal(SIGTTOU, &tty_signal);
748 static void
749 _ncl_sigs_down(void)
751 /* aaah.. atomic cas would be nice (but isn't it all redundant?) */
752 sighandler_type st;
754 if (_ncl_ottou.sint != -1) {
755 st = _ncl_ottou.shdl, _ncl_ottou.sint = -1;
756 safe_signal(SIGTTOU, st);
758 if (_ncl_ottin.sint != -1) {
759 st = _ncl_ottin.shdl, _ncl_ottin.sint = -1;
760 safe_signal(SIGTTIN, st);
762 if (_ncl_otstp.sint != -1) {
763 st = _ncl_otstp.shdl, _ncl_otstp.sint = -1;
764 safe_signal(SIGTSTP, st);
766 if (_ncl_ohup.sint != -1) {
767 st = _ncl_ohup.shdl, _ncl_ohup.sint = -1;
768 safe_signal(SIGHUP, st);
770 if (_ncl_oterm.sint != -1) {
771 st = _ncl_oterm.shdl, _ncl_oterm.sint = -1;
772 safe_signal(SIGTERM, st);
774 if (_ncl_oquit.sint != -1) {
775 st = _ncl_oquit.shdl, _ncl_oquit.sint = -1;
776 safe_signal(SIGQUIT, st);
778 if (_ncl_oint.sint != -1) {
779 st = _ncl_oint.shdl, _ncl_oint.sint = -1;
780 safe_signal(SIGINT, st);
784 static void
785 _ncl_term_mode(bool_t raw)
787 struct termios *tiosp = &_ncl_tios.told;
789 if (!raw)
790 goto jleave;
792 /* Always requery the attributes, in case we've been moved from background
793 * to foreground or however else in between sessions */
794 tcgetattr(STDIN_FILENO, tiosp);
795 memcpy(&_ncl_tios.tnew, tiosp, sizeof *tiosp);
796 tiosp = &_ncl_tios.tnew;
797 tiosp->c_cc[VMIN] = 1;
798 tiosp->c_cc[VTIME] = 0;
799 tiosp->c_iflag &= ~(ISTRIP);
800 tiosp->c_lflag &= ~(ECHO /*| ECHOE | ECHONL */| ICANON | IEXTEN);
801 jleave:
802 tcsetattr(STDIN_FILENO, TCSADRAIN, tiosp);
805 static void
806 _ncl_check_grow(struct line *l, size_t no SMALLOC_DEBUG_ARGS)
808 size_t i = (l->topins + no) * sizeof(struct cell) + 2 * sizeof(struct cell);
810 if (i > *l->x_bufsize) {
811 i <<= 1;
812 *l->x_bufsize = i;
813 l->line.cbuf =
814 *l->x_buf = (srealloc)(*l->x_buf, i SMALLOC_DEBUG_ARGSCALL);
818 static void
819 _ncl_bs_eof_dvup(struct cell *cap, size_t i)
821 size_t j;
823 if (i > 0)
824 memmove(cap, cap + 1, i * sizeof(*cap));
826 /* And.. the (rest of the) visual update */
827 for (j = 0; j < i; ++j)
828 fwrite(cap[j].cbuf, sizeof *cap->cbuf, cap[j].count, stdout);
829 fputs(" \b", stdout);
830 for (j = 0; j < i; ++j)
831 putchar('\b');
834 static ssize_t
835 _ncl_wboundary(struct line *l, ssize_t dir)
837 size_t c = l->cursor, t = l->topins, i;
838 struct cell *cap;
839 bool_t anynon;
841 i = (size_t)-1;
842 if (dir < 0) {
843 if (c == 0)
844 goto jleave;
845 } else if (c == t)
846 goto jleave;
847 else
848 --t, --c; /* Unsigned wrapping may occur (twice), then */
850 for (i = 0, cap = l->line.cells, anynon = FAL0;;) {
851 wchar_t wc = cap[c + dir].wc;
852 if (iswblank(wc) || iswpunct(wc)) {
853 if (anynon)
854 break;
855 } else
856 anynon = TRU1;
857 ++i;
858 c += dir;
859 if (dir < 0) {
860 if (c == 0)
861 break;
862 } else if (c == t)
863 break;
865 jleave:
866 return (ssize_t)i;
869 static ssize_t
870 _ncl_cell2dat(struct line *l)
872 size_t len = 0, i;
874 if (l->topins > 0)
875 for (i = 0; i < l->topins; ++i) {
876 struct cell *cap = l->line.cells + i;
877 memcpy(l->line.cbuf + len, cap->cbuf, cap->count);
878 len += cap->count;
880 l->line.cbuf[len] = '\0';
881 return (ssize_t)len;
884 # if defined HAVE_HISTORY || defined HAVE_TABEXPAND
885 static void
886 _ncl_cell2save(struct line *l)
888 size_t len, i;
889 struct cell *cap;
891 l->savec.s = NULL, l->savec.l = 0;
892 if (l->topins == 0)
893 goto jleave;
895 for (cap = l->line.cells, len = i = 0; i < l->topins; ++cap, ++i)
896 len += cap->count;
898 l->savec.l = len;
899 l->savec.s = salloc(len + 1);
901 for (cap = l->line.cells, len = i = 0; i < l->topins; ++cap, ++i) {
902 memcpy(l->savec.s + len, cap->cbuf, cap->count);
903 len += cap->count;
905 l->savec.s[len] = '\0';
906 jleave:
909 # endif
911 static void
912 _ncl_khome(struct line *l, bool_t dobell)
914 size_t c = l->cursor;
916 if (c > 0) {
917 l->cursor = 0;
918 while (c-- != 0)
919 putchar('\b');
920 } else if (dobell)
921 putchar('\a');
924 static void
925 _ncl_kend(struct line *l)
927 ssize_t i = (ssize_t)(l->topins - l->cursor);
929 if (i > 0) {
930 l->cursor = l->topins;
931 while (i-- != 0)
932 fputs(l->nd, stdout);
933 } else
934 putchar('\a');
937 static void
938 _ncl_kbs(struct line *l)
940 ssize_t c = l->cursor, t = l->topins;
942 if (c > 0) {
943 putchar('\b');
944 l->cursor = --c;
945 l->topins = --t;
946 t -= c;
947 _ncl_bs_eof_dvup(l->line.cells + c, t);
948 } else
949 putchar('\a');
952 static void
953 _ncl_kkill(struct line *l, bool_t dobell)
955 size_t j, c = l->cursor, i = (size_t)(l->topins - c);
957 if (i > 0) {
958 l->topins = c;
959 for (j = i; j != 0; --j)
960 putchar(' ');
961 for (j = i; j != 0; --j)
962 putchar('\b');
963 } else if (dobell)
964 putchar('\a');
967 static ssize_t
968 _ncl_keof(struct line *l)
970 size_t c = l->cursor, t = l->topins;
971 ssize_t i = (ssize_t)(t - c);
973 if (i > 0) {
974 l->topins = --t;
975 _ncl_bs_eof_dvup(l->line.cells + c, --i);
976 } else if (t == 0 && !ok_blook(ignoreeof)) {
977 fputs("^D", stdout);
978 fflush(stdout);
979 i = -1;
980 } else {
981 putchar('\a');
982 i = 0;
984 return i;
987 static void
988 _ncl_kleft(struct line *l)
990 if (l->cursor > 0) {
991 --l->cursor;
992 putchar('\b');
993 } else
994 putchar('\a');
997 static void
998 _ncl_kright(struct line *l)
1000 if (l->cursor < l->topins) {
1001 ++l->cursor;
1002 fputs(l->nd, stdout);
1003 } else
1004 putchar('\a');
1007 static void
1008 _ncl_krefresh(struct line *l)
1010 struct cell *cap;
1011 size_t i;
1013 putchar('\r');
1014 if (l->prompt != NULL && *l->prompt != '\0')
1015 fputs(l->prompt, stdout);
1016 for (cap = l->line.cells, i = l->topins; i > 0; ++cap, --i)
1017 fwrite(cap->cbuf, sizeof *cap->cbuf, cap->count, stdout);
1018 for (i = l->topins - l->cursor; i > 0; --i)
1019 putchar('\b');
1022 static void
1023 _ncl_kbwddelw(struct line *l)
1025 ssize_t i;
1026 size_t c = l->cursor, t, j;
1027 struct cell *cap;
1029 i = _ncl_wboundary(l, -1);
1030 if (i <= 0) {
1031 if (i < 0)
1032 putchar('\a');
1033 goto jleave;
1036 c = l->cursor - i;
1037 t = l->topins;
1038 l->topins = t - i;
1039 l->cursor = c;
1040 cap = l->line.cells + c;
1042 if (t != l->cursor) {
1043 j = t - c + i;
1044 memmove(cap, cap + i, j * sizeof(*cap));
1047 for (j = i; j > 0; --j)
1048 putchar('\b');
1049 for (j = l->topins - c; j > 0; ++cap, --j)
1050 fwrite(cap[0].cbuf, sizeof *cap->cbuf, cap[0].count, stdout);
1051 for (j = i; j > 0; --j)
1052 putchar(' ');
1053 for (j = t - c; j > 0; --j)
1054 putchar('\b');
1055 jleave:
1059 static void
1060 _ncl_kgow(struct line *l, ssize_t dir)
1062 ssize_t i = _ncl_wboundary(l, dir);
1063 if (i <= 0) {
1064 if (i < 0)
1065 putchar('\a');
1066 goto jleave;
1069 if (dir < 0) {
1070 l->cursor -= i;
1071 while (i-- > 0)
1072 putchar('\b');
1073 } else {
1074 l->cursor += i;
1075 while (i-- > 0)
1076 fputs(l->nd, stdout);
1078 jleave:
1082 static void
1083 _ncl_kother(struct line *l, wchar_t wc)
1085 /* Append if at EOL, insert otherwise;
1086 * since we may move around character-wise, always use a fresh ps */
1087 mbstate_t ps;
1088 struct cell cell, *cap;
1089 size_t i, c;
1091 /* First init a cell and see wether we'll really handle this wc */
1092 cell.wc = wc;
1093 memset(&ps, 0, sizeof ps);
1094 i = wcrtomb(cell.cbuf, wc, &ps);
1095 if (i > MB_LEN_MAX)
1096 goto jleave;
1097 cell.count = (ui_it)i;
1098 if (enc_has_state) {
1099 i = wcrtomb(cell.cbuf + i, L'\0', &ps);
1100 if (i == 1)
1102 else if (--i < MB_LEN_MAX)
1103 cell.count += (ui_it)i;
1104 else
1105 goto jleave;
1108 /* Yes, we will! Place it in the array */
1109 c = l->cursor++;
1110 i = l->topins++ - c;
1111 cap = l->line.cells + c;
1112 if (i > 0)
1113 memmove(cap + 1, cap, i * sizeof(cell));
1114 memcpy(cap, &cell, sizeof cell);
1116 /* And update visual */
1117 c = i;
1119 fwrite(cap->cbuf, sizeof *cap->cbuf, cap->count, stdout);
1120 while ((++cap, i-- != 0));
1121 while (c-- != 0)
1122 putchar('\b');
1123 jleave:
1127 # ifdef HAVE_HISTORY
1128 static size_t
1129 __ncl_khist_shared(struct line *l, struct hist *hp)
1131 size_t rv;
1133 if ((l->hist = hp) != NULL) {
1134 l->defc.s = savestrbuf(hp->dat, hp->len);
1135 rv =
1136 l->defc.l = hp->len;
1137 if (l->topins > 0) {
1138 _ncl_khome(l, FAL0);
1139 _ncl_kkill(l, FAL0);
1141 } else {
1142 putchar('\a');
1143 rv = 0;
1145 return rv;
1148 static size_t
1149 _ncl_khist(struct line *l, bool_t backwd)
1151 struct hist *hp;
1153 /* If we're not in history mode yet, save line content;
1154 * also, disallow forward search, then, and, of course, bail unless we
1155 * do have any history at all */
1156 if ((hp = l->hist) == NULL) {
1157 if (!backwd)
1158 goto jleave;
1159 if ((hp = _ncl_hist) == NULL)
1160 goto jleave;
1161 _ncl_cell2save(l);
1162 goto jleave;
1165 hp = backwd ? hp->older : hp->younger;
1166 jleave:
1167 return __ncl_khist_shared(l, hp);
1170 static size_t
1171 _ncl_krhist(struct line *l)
1173 struct str orig_savec;
1174 struct hist *hp = NULL;
1176 /* We cannot complete an empty line */
1177 if (l->topins == 0) {
1178 /* XXX The upcoming hard reset would restore a set savec buffer,
1179 * XXX so forcefully reset that. A cleaner solution would be to
1180 * XXX reset it whenever a restore is no longer desired */
1181 l->savec.s = NULL, l->savec.l = 0;
1182 goto jleave;
1184 if ((hp = l->hist) == NULL) {
1185 if ((hp = _ncl_hist) == NULL)
1186 goto jleave;
1187 orig_savec.s = NULL;
1188 orig_savec.l = 0; /* silence CC */
1189 } else if ((hp = hp->older) == NULL)
1190 goto jleave;
1191 else
1192 orig_savec = l->savec;
1194 if (orig_savec.s == NULL)
1195 _ncl_cell2save(l);
1196 for (; hp != NULL; hp = hp->older)
1197 if (is_prefix(l->savec.s, hp->dat))
1198 break;
1199 if (orig_savec.s != NULL)
1200 l->savec = orig_savec;
1201 jleave:
1202 return __ncl_khist_shared(l, hp);
1204 # endif
1206 # ifdef HAVE_TABEXPAND
1207 static size_t
1208 _ncl_kht(struct line *l)
1210 struct str orig, bot, topp, sub, exp;
1211 struct cell *cword, *ctop, *cx;
1212 bool_t set_savec = FAL0;
1213 size_t rv = 0;
1215 /* We cannot expand an empty line */
1216 if (l->topins == 0)
1217 goto jleave;
1219 /* Get plain line data; if this is the first expansion/xy, update the
1220 * very original content so that ^G gets the origin back */
1221 orig = l->savec;
1222 _ncl_cell2save(l);
1223 exp = l->savec;
1224 if (orig.s != NULL)
1225 l->savec = orig;
1226 else
1227 set_savec = TRU1;
1228 orig = exp;
1230 cword = l->line.cells;
1231 ctop = cword + l->cursor;
1233 /* topp: separate data right of cursor */
1234 if ((cx = cword + l->topins) != ctop) {
1235 for (rv = 0; cx > ctop; --cx)
1236 rv += cx->count;
1237 topp.l = rv;
1238 topp.s = orig.s + orig.l - rv;
1239 } else
1240 topp.s = NULL, topp.l = 0;
1242 /* bot, sub: we cannot expand the entire data left of cursor, but only
1243 * the last "word", so separate them */
1244 while (cx > cword && !iswspace(cx[-1].wc))
1245 --cx;
1246 for (rv = 0; cword < cx; ++cword)
1247 rv += cword->count;
1248 sub =
1249 bot = orig;
1250 bot.l = rv;
1251 sub.s += rv;
1252 sub.l -= rv;
1253 sub.l -= topp.l;
1255 if (sub.l > 0) {
1256 sub.s = savestrbuf(sub.s, sub.l);
1257 /* TODO there is a TODO note upon fexpand() with multi-return;
1258 * TODO if that will change, the if() below can be simplified */
1259 /* Super-Heavy-Metal: block all sigs, avoid leaks on jump */
1260 hold_all_sigs();
1261 exp.s = fexpand(sub.s, _CL_TAB_FEXP_FL);
1262 rele_all_sigs();
1264 if (exp.s != NULL && (exp.l = strlen(exp.s)) > 0 &&
1265 (exp.l != sub.l || strcmp(exp.s, sub.s))) {
1266 /* Cramp expansion length to MAX_INPUT, or 255 if not defined.
1267 * Take care to take *prompt* into account, since we don't know
1268 * anything about it's visual length (fputs(3) is used), simply
1269 * assume each character requires two columns */
1270 /* TODO the problem is that we loose control otherwise; in the best
1271 * TODO case the user can control via ^A and ^K etc., but be safe;
1272 * TODO we cannot simply adjust fexpand() because we don't know how
1273 * TODO that is implemented... The real solution would be to check
1274 * TODO wether we fit on a line, and start a pager if not.
1275 * TODO However, that should be part of a real tab-COMPLETION, then,
1276 * TODO i.e., don't EXPAND, but SHOW COMPLETIONS, page-wise if needed.
1277 * TODO And: MAX_INPUT is dynamic: pathconf(2), _SC_MAX_INPUT */
1278 rv = (l->prompt != NULL) ? _PROMPT_VLEN(l->prompt) : 0;
1279 if (rv + bot.l + exp.l + topp.l >= MAX_INPUT) {
1280 char const e1[] = "[maximum line size exceeded]";
1281 exp.s = UNCONST(e1);
1282 exp.l = sizeof(e1) - 1;
1283 topp.l = 0;
1284 if (rv + bot.l + exp.l >= MAX_INPUT)
1285 bot.l = 0;
1286 if (rv + exp.l >= MAX_INPUT) {
1287 char const e2[] = "[ERR]";
1288 exp.s = UNCONST(e2);
1289 exp.l = sizeof(e2) - 1;
1292 orig.l = bot.l + exp.l + topp.l;
1293 orig.s = salloc(orig.l + 1 + 5);
1294 if ((rv = bot.l) > 0)
1295 memcpy(orig.s, bot.s, rv);
1296 memcpy(orig.s + rv, exp.s, exp.l);
1297 rv += exp.l;
1298 if (topp.l > 0) {
1299 memcpy(orig.s + rv, topp.s, topp.l);
1300 rv += topp.l;
1302 orig.s[rv] = '\0';
1304 l->defc = orig;
1305 _ncl_khome(l, FAL0);
1306 _ncl_kkill(l, FAL0);
1307 goto jleave;
1311 /* If we've provided a default content, but failed to expand, there is
1312 * nothing we can "revert to": drop that default again */
1313 if (set_savec)
1314 l->savec.s = NULL, l->savec.l = 0;
1315 rv = 0;
1316 jleave:
1317 return rv;
1319 # endif /* HAVE_TABEXPAND */
1321 static ssize_t
1322 _ncl_readline(char const *prompt, char **buf, size_t *bufsize, size_t len
1323 SMALLOC_DEBUG_ARGS)
1325 /* We want to save code, yet we may have to incorporate a lines'
1326 * default content and / or default input to switch back to after some
1327 * history movement; let "len > 0" mean "have to display some data
1328 * buffer", and only otherwise read(2) it */
1329 mbstate_t ps[2];
1330 struct line l;
1331 char cbuf_base[MB_LEN_MAX * 2], *cbuf, *cbufp;
1332 wchar_t wc;
1333 ssize_t rv;
1334 ui32_t maybe_cursor;
1336 memset(&l, 0, sizeof l);
1337 l.line.cbuf = *buf;
1338 if (len != 0) {
1339 l.defc.s = savestrbuf(*buf, len);
1340 l.defc.l = len;
1342 if ((l.prompt = prompt) != NULL && _PROMPT_VLEN(prompt) > _PROMPT_MAX)
1343 l.prompt = prompt = "?ERR?";
1344 /* TODO *l.nd=='\0' only because we have no value-cache -> see acmava.c */
1345 if ((l.nd = ok_vlook(line_editor_cursor_right)) == NULL || *l.nd == '\0')
1346 l.nd = "\033[C"; /* XXX no "magic" constant */
1347 l.x_buf = buf;
1348 l.x_bufsize = bufsize;
1350 if (prompt != NULL && *prompt != '\0') {
1351 fputs(prompt, stdout);
1352 fflush(stdout);
1354 jrestart:
1355 memset(ps, 0, sizeof ps);
1356 maybe_cursor = 0;
1357 /* TODO: NCL: we should output the reset sequence when we jrestart:
1358 * TODO: NCL: if we are using a stateful encoding? !
1359 * TODO: NCL: in short: this is not yet well understood */
1360 for (;;) {
1361 _ncl_check_grow(&l, len SMALLOC_DEBUG_ARGSCALL);
1363 /* Normal read(2)? Else buffer-takeover: speed this one up */
1364 if (len == 0)
1365 cbufp =
1366 cbuf = cbuf_base;
1367 else {
1368 assert(l.defc.l > 0 && l.defc.s != NULL);
1369 cbufp =
1370 cbuf = l.defc.s + (l.defc.l - len);
1371 cbufp += len;
1374 /* Read in the next complete multibyte character */
1375 for (;;) {
1376 if (len == 0) {
1377 if ((rv = read(STDIN_FILENO, cbufp, 1)) < 1) {
1378 if (errno == EINTR) /* xxx #if !SA_RESTART ? */
1379 continue;
1380 goto jleave;
1382 ++cbufp;
1385 /* Ach! the ISO C multibyte handling!
1386 * Encodings with locking shift states cannot really be helped, since
1387 * it is impossible to only query the shift state, as opposed to the
1388 * entire shift state + character pair (via ISO C functions) */
1389 rv = (ssize_t)mbrtowc(&wc, cbuf, (size_t)(cbufp - cbuf), ps + 0);
1390 if (rv <= 0) {
1391 /* Any error during take-over can only result in a hard reset;
1392 * Otherwise, if it's a hard error, or if too many redundant shift
1393 * sequences overflow our buffer, also perform a hard reset */
1394 if (len != 0 || rv == -1 ||
1395 sizeof cbuf_base == (size_t)(cbufp - cbuf)) {
1396 l.savec.s = l.defc.s = NULL,
1397 l.savec.l = l.defc.l = len = 0;
1398 putchar('\a');
1399 wc = 'G';
1400 goto jreset;
1402 /* Otherwise, due to the way we deal with the buffer, we need to
1403 * restore the mbstate_t from before this conversion */
1404 ps[0] = ps[1];
1405 continue;
1408 if (len != 0 && (len -= (size_t)rv) == 0)
1409 l.defc.s = NULL, l.defc.l = 0;
1410 ps[1] = ps[0];
1411 break;
1414 /* Don't interpret control bytes during buffer take-over */
1415 if (cbuf != cbuf_base)
1416 goto jprint;
1417 switch (wc) {
1418 case 'A' ^ 0x40: /* cursor home */
1419 _ncl_khome(&l, TRU1);
1420 break;
1421 j_b:
1422 case 'B' ^ 0x40: /* backward character */
1423 _ncl_kleft(&l);
1424 break;
1425 /* 'C': interrupt (CTRL-C) */
1426 case 'D' ^ 0x40: /* delete char forward if any, else EOF */
1427 if ((rv = _ncl_keof(&l)) < 0)
1428 goto jleave;
1429 break;
1430 case 'E' ^ 0x40: /* end of line */
1431 _ncl_kend(&l);
1432 break;
1433 j_f:
1434 case 'F' ^ 0x40: /* forward character */
1435 _ncl_kright(&l);
1436 break;
1437 /* 'G' below */
1438 case 'H' ^ 0x40: /* backspace */
1439 case '\177':
1440 _ncl_kbs(&l);
1441 break;
1442 case 'I' ^ 0x40: /* horizontal tab */
1443 # ifdef HAVE_TABEXPAND
1444 if ((len = _ncl_kht(&l)) > 0)
1445 goto jrestart;
1446 # endif
1447 goto jbell;
1448 case 'J' ^ 0x40: /* NL (\n) */
1449 goto jdone;
1450 case 'G' ^ 0x40: /* full reset */
1451 jreset:
1452 /* FALLTHRU */
1453 case 'U' ^ 0x40: /* ^U: ^A + ^K */
1454 _ncl_khome(&l, FAL0);
1455 /* FALLTHRU */
1456 case 'K' ^ 0x40: /* kill from cursor to end of line */
1457 _ncl_kkill(&l, (wc == ('K' ^ 0x40) || l.topins == 0));
1458 /* (Handle full reset?) */
1459 if (wc == ('G' ^ 0x40)) {
1460 # ifdef HAVE_HISTORY
1461 l.hist = NULL;
1462 # endif
1463 if ((len = l.savec.l) != 0) {
1464 l.defc = l.savec;
1465 l.savec.s = NULL, l.savec.l = 0;
1466 } else
1467 len = l.defc.l;
1469 fflush(stdout);
1470 goto jrestart;
1471 case 'L' ^ 0x40: /* repaint line */
1472 _ncl_krefresh(&l);
1473 break;
1474 /* 'M': CR (\r) */
1475 j_n:
1476 case 'N' ^ 0x40: /* history next */
1477 # ifdef HAVE_HISTORY
1478 if (l.hist == NULL)
1479 goto jbell;
1480 if ((len = _ncl_khist(&l, FAL0)) > 0)
1481 goto jrestart;
1482 wc = 'G' ^ 0x40;
1483 goto jreset;
1484 # else
1485 goto jbell;
1486 # endif
1487 /* 'O' */
1488 j_p:
1489 case 'P' ^ 0x40: /* history previous */
1490 # ifdef HAVE_HISTORY
1491 if ((len = _ncl_khist(&l, TRU1)) > 0)
1492 goto jrestart;
1493 wc = 'G' ^ 0x40;
1494 goto jreset;
1495 # else
1496 goto jbell;
1497 # endif
1498 /* 'Q': no code */
1499 case 'R' ^ 0x40: /* reverse history search */
1500 # ifdef HAVE_HISTORY
1501 if ((len = _ncl_krhist(&l)) > 0)
1502 goto jrestart;
1503 wc = 'G' ^ 0x40;
1504 goto jreset;
1505 # else
1506 goto jbell;
1507 # endif
1508 /* 'S': no code */
1509 /* 'U' above */
1510 /*case 'V' ^ 0x40: TODO*/ /* forward delete "word" */
1511 case 'W' ^ 0x40: /* backward delete "word" */
1512 _ncl_kbwddelw(&l);
1513 break;
1514 case 'X' ^ 0x40: /* move cursor forward "word" */
1515 _ncl_kgow(&l, +1);
1516 break;
1517 case 'Y' ^ 0x40: /* move cursor backward "word" */
1518 _ncl_kgow(&l, -1);
1519 break;
1520 /* 'Z': suspend (CTRL-Z) */
1521 case 0x1B:
1522 if (maybe_cursor++ != 0)
1523 goto jreset;
1524 continue;
1525 default:
1526 /* XXX Handle usual ^[[[ABCD] cursor keys -- UGLY, "MAGIC", INFLEX */
1527 if (maybe_cursor > 0) {
1528 if (++maybe_cursor == 2) {
1529 if (wc == L'[')
1530 continue;
1531 maybe_cursor = 0;
1532 } else {
1533 maybe_cursor = 0;
1534 switch (wc) {
1535 case L'A': goto j_p;
1536 case L'B': goto j_n;
1537 case L'C': goto j_f;
1538 case L'D': goto j_b;
1540 _ncl_kother(&l, L'[');
1543 jprint:
1544 if (iswprint(wc)) {
1545 _ncl_kother(&l, wc);
1546 /* Don't clear the history during takeover..
1547 * ..and also avoid fflush()ing unless we've
1548 * worked the entire buffer */
1549 if (len > 0)
1550 continue;
1551 # ifdef HAVE_HISTORY
1552 if (cbuf == cbuf_base)
1553 l.hist = NULL;
1554 # endif
1555 } else {
1556 jbell:
1557 putchar('\a');
1559 break;
1561 fflush(stdout);
1564 /* We have a completed input line, convert the struct cell data to its
1565 * plain character equivalent */
1566 jdone:
1567 putchar('\n');
1568 fflush(stdout);
1569 len = _ncl_cell2dat(&l);
1570 rv = (ssize_t)len;
1571 jleave:
1572 return rv;
1575 FL void
1576 tty_init(void)
1578 # ifdef HAVE_HISTORY
1579 long hs;
1580 char *v, *lbuf;
1581 FILE *f;
1582 size_t lsize, cnt, llen;
1583 # endif
1585 _ncl_oint.sint = _ncl_oquit.sint = _ncl_oterm.sint =
1586 _ncl_ohup.sint = _ncl_otstp.sint = _ncl_ottin.sint =
1587 _ncl_ottou.sint = -1;
1589 # ifdef HAVE_HISTORY
1590 _CL_HISTSIZE(hs);
1591 _ncl_hist_size = 0;
1592 _ncl_hist_size_max = hs;
1593 if (hs == 0)
1594 goto jleave;
1596 _CL_HISTFILE(v);
1597 if (v == NULL)
1598 goto jleave;
1600 hold_all_sigs(); /* XXX too heavy, yet we may jump even here!? */
1601 f = fopen(v, "r"); /* TODO HISTFILE LOAD: use linebuf pool */
1602 if (f == NULL)
1603 goto jdone;
1605 lbuf = NULL;
1606 lsize = 0;
1607 cnt = fsize(f);
1608 while (fgetline(&lbuf, &lsize, &cnt, &llen, f, FAL0) != NULL) {
1609 if (llen > 0 && lbuf[llen - 1] == '\n')
1610 lbuf[--llen] = '\0';
1611 if (llen == 0 || lbuf[0] == '#') /* xxx comments? noone! */
1612 continue;
1613 _ncl_hist_load = TRU1;
1614 tty_addhist(lbuf);
1615 _ncl_hist_load = FAL0;
1617 if (lbuf != NULL)
1618 free(lbuf);
1620 fclose(f);
1621 jdone:
1622 rele_all_sigs(); /* XXX remove jumps */
1623 jleave:
1624 # endif /* HAVE_HISTORY */
1628 FL void
1629 tty_destroy(void)
1631 # ifdef HAVE_HISTORY
1632 long hs;
1633 char *v;
1634 struct hist *hp;
1635 FILE *f;
1637 _CL_HISTSIZE(hs);
1638 if (hs == 0)
1639 goto jleave;
1641 _CL_HISTFILE(v);
1642 if (v == NULL)
1643 goto jleave;
1645 if ((hp = _ncl_hist) != NULL)
1646 while (hp->older != NULL && hs-- != 0)
1647 hp = hp->older;
1649 hold_all_sigs(); /* too heavy, yet we may jump even here!? */
1650 f = fopen(v, "w"); /* TODO temporary + rename?! */
1651 if (f == NULL)
1652 goto jdone;
1653 if (fchmod(fileno(f), S_IRUSR | S_IWUSR) != 0)
1654 goto jclose;
1656 for (; hp != NULL; hp = hp->younger) {
1657 fwrite(hp->dat, sizeof *hp->dat, hp->len, f);
1658 putc('\n', f);
1660 jclose:
1661 fclose(f);
1662 jdone:
1663 rele_all_sigs(); /* XXX remove jumps */
1664 jleave:
1665 # endif /* HAVE_HISTORY */
1669 FL void
1670 tty_signal(int sig)
1672 sigset_t nset, oset;
1674 switch (sig) {
1675 case SIGWINCH:
1676 /* We don't deal with SIGWINCH, yet get called from main.c */
1677 break;
1678 default:
1679 _ncl_term_mode(FAL0);
1680 _ncl_sigs_down();
1681 sigemptyset(&nset);
1682 sigaddset(&nset, sig);
1683 sigprocmask(SIG_UNBLOCK, &nset, &oset);
1684 kill(0, sig);
1685 /* When we come here we'll continue editing, so reestablish */
1686 sigprocmask(SIG_BLOCK, &oset, (sigset_t*)NULL);
1687 _ncl_sigs_up();
1688 _ncl_term_mode(TRU1);
1689 break;
1693 FL int
1694 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
1695 SMALLOC_DEBUG_ARGS)
1697 ssize_t nn;
1699 /* Of course we have races here, but they cannot be avoided on POSIX
1700 * (except by even *more* actions) */
1701 _ncl_sigs_up();
1702 _ncl_term_mode(TRU1);
1703 nn = _ncl_readline(prompt, linebuf, linesize, n SMALLOC_DEBUG_ARGSCALL);
1704 _ncl_term_mode(FAL0);
1705 _ncl_sigs_down();
1707 return (int)nn;
1710 FL void
1711 tty_addhist(char const *s)
1713 # ifdef HAVE_HISTORY
1714 /* Super-Heavy-Metal: block all sigs, avoid leaks+ on jump */
1715 size_t l = strlen(s);
1716 struct hist *h, *o, *y;
1718 _CL_CHECK_ADDHIST(s, goto j_leave);
1720 /* Eliminating duplicates is expensive, but simply inacceptable so
1721 * during the load of a potentially large history file! */
1722 if (!_ncl_hist_load)
1723 for (h = _ncl_hist; h != NULL; h = h->older)
1724 if (h->len == l && strcmp(h->dat, s) == 0) {
1725 hold_all_sigs();
1726 o = h->older;
1727 y = h->younger;
1728 if (o != NULL) {
1729 if ((o->younger = y) == NULL)
1730 _ncl_hist = o;
1732 if (y != NULL)
1733 y->older = o;
1734 else
1735 _ncl_hist = o;
1736 goto jleave;
1738 hold_all_sigs();
1740 ++_ncl_hist_size;
1741 if (!_ncl_hist_load && _ncl_hist_size >= _ncl_hist_size_max) {
1742 --_ncl_hist_size;
1743 (h = _ncl_hist->younger
1744 )->older = NULL;
1745 free(_ncl_hist);
1746 _ncl_hist = h;
1749 h = smalloc((sizeof(struct hist) - VFIELD_SIZEOF(struct hist, dat)) + l + 1);
1750 h->len = l;
1751 memcpy(h->dat, s, l + 1);
1752 jleave:
1753 if ((h->older = _ncl_hist) != NULL)
1754 _ncl_hist->younger = h;
1755 h->younger = NULL;
1756 _ncl_hist = h;
1758 rele_all_sigs();
1759 j_leave:
1760 # endif
1761 UNUSED(s);
1764 # ifdef HAVE_HISTORY
1765 FL int
1766 c_history(void *v)
1768 C_HISTORY_SHARED;
1770 jlist: {
1771 FILE *fp;
1772 char *cp;
1773 size_t i, b;
1774 struct hist *h;
1776 if (_ncl_hist == NULL)
1777 goto jleave;
1779 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1780 perror("tmpfile");
1781 v = NULL;
1782 goto jleave;
1784 rm(cp);
1785 Ftfree(&cp);
1787 i = _ncl_hist_size;
1788 b = 0;
1789 for (h = _ncl_hist; h != NULL; --i, b += h->len, h = h->older)
1790 fprintf(fp, "%4lu. %-50.50s (%4lu+%lu bytes)\n",
1791 (ul_it)i, h->dat, (ul_it)b, (ul_it)h->len);
1793 page_or_print(fp, i);
1794 Fclose(fp);
1796 goto jleave;
1798 jclear: {
1799 struct hist *h;
1800 while ((h = _ncl_hist) != NULL) {
1801 _ncl_hist = h->older;
1802 free(h);
1804 _ncl_hist_size = 0;
1806 goto jleave;
1808 # endif /* HAVE_HISTORY */
1809 #endif /* HAVE_NCL */
1812 * The really-nothing-at-all implementation
1815 #if !defined HAVE_READLINE && !defined HAVE_EDITLINE && !defined HAVE_NCL
1816 FL void
1817 tty_init(void)
1820 FL void
1821 tty_destroy(void)
1824 FL void
1825 tty_signal(int sig)
1827 UNUSED(sig);
1830 FL int
1831 (tty_readline)(char const *prompt, char **linebuf, size_t *linesize, size_t n
1832 SMALLOC_DEBUG_ARGS)
1835 * TODO The nothing-at-all tty layer even forces re-entering all the
1836 * TODO original data when re-editing a field
1838 bool_t doffl = FAL0;
1840 if (prompt != NULL && *prompt != '\0') {
1841 fputs(prompt, stdout);
1842 doffl = TRU1;
1844 if (n > 0) {
1845 fprintf(stdout, tr(511, "{former content: %.*s} "), (int)n, *linebuf);
1846 n = 0;
1847 doffl = TRU1;
1849 if (doffl)
1850 fflush(stdout);
1851 return (readline_restart)(stdin, linebuf, linesize,n SMALLOC_DEBUG_ARGSCALL);
1854 FL void
1855 tty_addhist(char const *s)
1857 UNUSED(s);
1859 #endif /* nothing at all */
1861 /* vim:set fenc=utf-8:s-it-mode */