1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Terminal capability interaction.
3 *@ For encapsulation purposes provide a basic foundation even without
4 *@ HAVE_TERMCAP, but with config.h:n_HAVE_TCAP.
5 *@ HOWTO add a new non-dynamic command or query:
6 *@ - add an entry to nail.h:enum n_termcap_{cmd,query}
7 *@ - run make-tcap-map.pl
8 *@ - update the *termcap* member documentation on changes!
9 *@ Bug: in case of clashes of two-letter names terminfo(5) wins.
11 * Copyright (c) 2016 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
13 * Permission to use, copy, modify, and/or distribute this software for any
14 * purpose with or without fee is hereby granted, provided that the above
15 * copyright notice and this permission notice appear in all copies.
17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 #define n_FILE termcap
28 #ifndef HAVE_AMALGAMATION
34 /* If available, curses.h must be included before term.h! */
36 # ifdef HAVE_TERMCAP_CURSES
43 * xxx We are not really compatible with very old and strange terminals since
44 * we don't care at all for circumstances indicated by terminal flags: if we
45 * find a capability we use it and assume it works. E.g., if "Co" indicates
46 * colours we simply use ISO 6429 also for font attributes etc. That is,
47 * we don't use the ncurses/terminfo interface with all its internal logic.
50 /* Unless HAVE_TERMINFO or HAVE_TGETENT_NULL_BUF are defined we will use this
51 * to space the buffer we pass through to tgetent(3).
52 * Since for (such) elder non-emulated terminals really weird things will
53 * happen if an entry would require more than 1024 bytes, don't really mind.
54 * Use a ui16_t for storage */
55 #define a_TERMCAP_ENTRYSIZE_MAX ((2668 + 64) & ~64) /* As of ncurses 6.0 */
57 n_CTA(a_TERMCAP_ENTRYSIZE_MAX
< UI16_MAX
,
58 "Chosen buffer size exceeds datatype capability");
60 /* For simplicity we store commands and queries in single continuous control
61 * and entry structure arrays: to index queries one has to add
62 * n__TERMCAP_CMD_MAX1 first! And don't confound with ENTRYSIZE_MAX! */
64 a_TERMCAP_ENT_MAX1
= n__TERMCAP_CMD_MAX1
+ n__TERMCAP_QUERY_MAX1
69 /* enum n_termcap_captype values stored here.
70 * Note that presence of a type in an a_termcap_ent signals initialization */
71 a_TERMCAP_F_TYPE_MASK
= (1<<4) - 1,
73 a_TERMCAP_F_QUERY
= 1<<4, /* A query rather than a command */
74 a_TERMCAP_F_DISABLED
= 1<<5, /* User explicitly disabled command/query */
75 a_TERMCAP_F_ALTERN
= 1<<6, /* Not available, but has alternative */
76 a_TERMCAP_F_NOENT
= 1<<7, /* Not available */
78 /* _cmd() argument interpretion (_T_STR) */
79 a_TERMCAP_F_ARG_IDX1
= 1<<11, /* Argument 1 used, and is an index */
80 a_TERMCAP_F_ARG_IDX2
= 1<<12,
81 a_TERMCAP_F_ARG_CNT
= 1<<13, /* .., and is a count */
83 a_TERMCAP_F__LAST
= a_TERMCAP_F_ARG_CNT
85 n_CTA((ui32_t
)n__TERMCAP_CAPTYPE_MAX1
<= (ui32_t
)a_TERMCAP_F_TYPE_MASK
,
86 "enum n_termcap_captype exceeds bit range of a_termcap_flags");
88 struct a_termcap_control
{
90 /* Offset base into a_termcap_namedat[], which stores the two-letter
91 * termcap(5) name directly followed by a NUL terminated terminfo(5) name.
92 * A termcap(5) name may consist of two NULs meaning ERR_NOENT,
93 * a terminfo(5) name may be empty for the same purpose */
96 n_CTA(a_TERMCAP_F__LAST
<= UI16_MAX
,
97 "a_termcap_flags exceed storage datatype in a_termcap_control");
101 ui16_t te_off
; /* in a_termcap_g->tg_dat / value for T_BOOL and T_NUM */
103 n_CTA(a_TERMCAP_F__LAST
<= UI16_MAX
,
104 "a_termcap_flags exceed storage datatype in a_termcap_ent");
106 /* Structure for extended queries, which don't have an entry constant in
107 * n_termcap_query (to allow free query/binding of keycodes) */
108 struct a_termcap_ext_ent
{
109 struct a_termcap_ent tee_super
;
111 struct a_termcap_ext_ent
*tee_next
;
112 /* Resolvable termcap(5)/terminfo(5) name as given by user; the actual data
113 * is stored just like for normal queries */
114 char tee_name
[n_VFIELD_SIZE(0)];
118 struct a_termcap_ext_ent
*tg_ext_ents
; /* List of extended queries */
119 struct a_termcap_ent tg_ents
[a_TERMCAP_ENT_MAX1
];
120 struct n_string tg_dat
; /* Storage for resolved caps */
121 # if !defined HAVE_TGETENT_NULL_BUF && !defined HAVE_TERMINFO
122 char tg_lib_buf
[a_TERMCAP_ENTRYSIZE_MAX
];
126 /* Include the constant make-tcap-map.pl output */
127 #include "gen-tcaps.h"
128 n_CTA(sizeof a_termcap_namedat
<= UI16_MAX
,
129 "Termcap command and query name data exceed storage datatype");
130 n_CTA(a_TERMCAP_ENT_MAX1
== n_NELEM(a_termcap_control
),
131 "Control array doesn't match command/query array to be controlled");
133 static struct a_termcap_g
*a_termcap_g
;
135 /* Query *termcap*, parse it and incorporate into a_termcap_g */
136 static void a_termcap_init_var(struct str
const *termvar
);
138 /* Expand ^CNTRL, \[Ee] and \OCT. False for parse error and empty results */
139 static bool_t
a_termcap__strexp(struct n_string
*store
, char const *ibuf
);
141 /* Initialize any _ent for which we have _F_ALTERN and which isn't yet set */
142 static void a_termcap_init_altern(void);
145 /* Setup the library we use to work with term */
146 static bool_t
a_termcap_load(char const *term
);
148 /* Query the capability tcp and fill in tep (upon success) */
149 static bool_t
a_termcap_ent_query(struct a_termcap_ent
*tep
,
150 char const *cname
, ui16_t cflags
);
151 SINLINE bool_t
a_termcap_ent_query_tcp(struct a_termcap_ent
*tep
,
152 struct a_termcap_control
const *tcp
);
154 /* Output PTF for both, termcap(5) and terminfo(5) */
155 static int a_termcap_putc(int c
);
158 /* Get n_termcap_cmd or n_termcap_query constant belonging to (nlen bytes of)
159 * name, or -1 if not found. min and max have to be used to cramp the result */
160 static si32_t
a_termcap_enum_for_name(char const *name
, size_t nlen
,
161 si32_t min
, si32_t max
);
162 #define a_termcap_cmd_for_name(NB,NL) \
163 a_termcap_enum_for_name(NB, NL, 0, n__TERMCAP_CMD_MAX1)
164 #define a_termcap_query_for_name(NB,NL) \
165 a_termcap_enum_for_name(NB, NL, n__TERMCAP_CMD_MAX1, a_TERMCAP_ENT_MAX1)
168 a_termcap_init_var(struct str
const *termvar
){
169 char *cbp_base
, *cbp
;
174 if(termvar
->l
>= UI16_MAX
){
175 n_err(_("*termcap*: length excesses internal limit, skipping\n"));
179 assert(termvar
->s
[termvar
->l
] == '\0');
181 cbp_base
= salloc(i
);
182 memcpy(cbp
= cbp_base
, termvar
->s
, i
);
184 for(; (ccp
= n_strsep(&cbp
, ',', TRU1
)) != NULL
;){
185 struct a_termcap_ent
*tep
;
190 /* Separate key/value, if any */
191 if(/* no empties ccp[0] == '\0' ||*/ ccp
[1] == '\0'){
193 n_err(_("*termcap*: invalid entry: %s\n"), ccp
);
196 for(kl
= 2, v
= &ccp
[2];; ++kl
, ++v
){
200 f
= n_TERMCAP_CAPTYPE_BOOL
;
203 f
= n_TERMCAP_CAPTYPE_NUMERIC
;
207 f
= n_TERMCAP_CAPTYPE_STRING
;
213 /* Do we know about this one? */
215 struct a_termcap_control
const *tcp
;
218 tci
= a_termcap_enum_for_name(ccp
, kl
, 0, a_TERMCAP_ENT_MAX1
);
220 /* For key binding purposes, save any given string */
221 #ifdef HAVE_KEY_BINDINGS
222 if((f
& a_TERMCAP_F_TYPE_MASK
) == n_TERMCAP_CAPTYPE_STRING
){
223 struct a_termcap_ext_ent
*teep
;
225 teep
= smalloc(n_VSTRUCT_SIZEOF(struct a_termcap_ext_ent
,
227 teep
->tee_next
= a_termcap_g
->tg_ext_ents
;
228 a_termcap_g
->tg_ext_ents
= teep
;
229 memcpy(teep
->tee_name
, ccp
, kl
);
230 teep
->tee_name
[kl
] = '\0';
232 tep
= &teep
->tee_super
;
233 tep
->te_flags
= n_TERMCAP_CAPTYPE_STRING
| a_TERMCAP_F_QUERY
;
234 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
235 if(!a_termcap__strexp(&a_termcap_g
->tg_dat
, v
))
236 tep
->te_flags
|= a_TERMCAP_F_DISABLED
;
239 #endif /* HAVE_KEY_BINDINGS */
240 if(n_poption
& n_PO_D_V
)
241 n_err(_("*termcap*: unknown capability: %s\n"), ccp
);
246 tcp
= &a_termcap_control
[i
];
247 if((tcp
->tc_flags
& a_TERMCAP_F_TYPE_MASK
) != f
){
248 n_err(_("*termcap*: entry type mismatch: %s\n"), ccp
);
251 tep
= &a_termcap_g
->tg_ents
[i
];
252 tep
->te_flags
= tcp
->tc_flags
;
253 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
256 if((f
& a_TERMCAP_F_TYPE_MASK
) == n_TERMCAP_CAPTYPE_BOOL
)
259 tep
->te_flags
|= a_TERMCAP_F_DISABLED
;
260 else if((f
& a_TERMCAP_F_TYPE_MASK
) == n_TERMCAP_CAPTYPE_NUMERIC
){
261 if((n_idec_ui16_cp(&tep
->te_off
, v
, 0, NULL
262 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
263 ) != n_IDEC_STATE_CONSUMED
)
265 }else if(!a_termcap__strexp(&a_termcap_g
->tg_dat
, v
))
266 tep
->te_flags
|= a_TERMCAP_F_DISABLED
;
267 #ifdef HAVE_KEY_BINDINGS
270 if(n_poption
& n_PO_D_VV
)
271 n_err(_("*termcap*: learned %.*s: %s\n"), (int)kl
, ccp
,
272 (tep
->te_flags
& a_TERMCAP_F_DISABLED
? "<disabled>"
273 : (f
& a_TERMCAP_F_TYPE_MASK
) == n_TERMCAP_CAPTYPE_BOOL
? "true"
276 DBG( if(n_poption
& n_PO_D_V
) n_err("*termcap* parsed: buffer used=%lu\n",
277 (ul_i
)a_termcap_g
->tg_dat
.s_len
) );
279 /* Catch some inter-dependencies the user may have triggered */
281 if(a_termcap_g
->tg_ents
[n_TERMCAP_CMD_te
].te_flags
& a_TERMCAP_F_DISABLED
)
282 a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ti
].te_flags
= a_TERMCAP_F_DISABLED
;
283 else if(a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ti
].te_flags
&
284 a_TERMCAP_F_DISABLED
)
285 a_termcap_g
->tg_ents
[n_TERMCAP_CMD_te
].te_flags
= a_TERMCAP_F_DISABLED
;
293 a_termcap__strexp(struct n_string
*store
, char const *ibuf
){ /* XXX ASCII */
301 for(oibuf
= ibuf
; (c
= *ibuf
) != '\0';){
303 if((c
= ibuf
[1]) == '\0')
315 if((c2
= ibuf
[2]) == '\0' || !octalchar(c2
) ||
316 (c3
= ibuf
[3]) == '\0' || !octalchar(c3
)){
317 n_err(_("*termcap*: invalid octal sequence: %s\n"), oibuf
);
320 c
-= '0', c2
-= '0', c3
-= '0';
323 n_err(_("*termcap*: octal number too large: %s\n"), oibuf
);
331 n_err(_("*termcap*: invalid reverse solidus \\ sequence: %s\n"),oibuf
);
334 if((c
= ibuf
[1]) == '\0'){
335 n_err(_("*termcap*: incomplete ^CNTRL sequence: %s\n"), oibuf
);
338 c
= upperconv(c
) ^ 0x40;
339 if((ui8_t
)c
> 0x1F && c
!= 0x7F){ /* ASCII C0: 0..1F, 7F */
340 n_err(_("*termcap*: invalid ^CNTRL sequence: %s\n"), oibuf
);
348 store
= n_string_push_c(store
, c
);
351 c
= (store
->s_len
!= olen
) ? '\1' : '\0';
353 n_string_push_c(store
, '\0');
357 store
= n_string_trunc(store
, olen
);
363 a_termcap_init_altern(void){
364 /* We silently ignore user _F_DISABLED requests for those entries for which
365 * we have fallback entries, and which we need to ensure proper functioning.
366 * I.e., this allows users to explicitly disable some termcap(5) capability
367 * and enforce usage of the built-in fallback */
368 /* xxx Use table-based approach for fallback strategies */
369 #define a_OK(CMD) a_OOK(&a_termcap_g->tg_ents[CMD])
371 ((TEP)->te_flags != 0 && !((TEP)->te_flags & a_TERMCAP_F_NOENT))
372 #define a_SET(TEP,CMD,ALT) \
373 (TEP)->te_flags = a_termcap_control[CMD].tc_flags |\
374 ((ALT) ? a_TERMCAP_F_ALTERN : 0)
376 struct a_termcap_ent
*tep
;
380 /* For simplicity in the rest of this file null flags of disabled commands,
381 * as we won't check and try to lazy query any command */
385 for(i
= n__TERMCAP_CMD_MAX1
;;){
388 if((tep
= &a_termcap_g
->tg_ents
[i
])->te_flags
& a_TERMCAP_F_DISABLED
)
395 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_cl
];
397 if(a_OK(n_TERMCAP_CMD_cd
) && a_OK(n_TERMCAP_CMD_ho
))
398 a_SET(tep
, n_TERMCAP_CMD_cl
, TRU1
);
403 /* ce == ch + [:SPC:] (start column specified by argument) */
404 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ce
];
406 a_SET(tep
, n_TERMCAP_CMD_ce
, TRU1
);
408 /* ch == cr[\r] + nd[:\033C:] */
409 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ch
];
411 a_SET(tep
, n_TERMCAP_CMD_ch
, TRU1
);
414 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_cr
];
416 a_SET(tep
, n_TERMCAP_CMD_cr
, FAL0
);
417 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
418 n_string_push_c(n_string_push_c(&a_termcap_g
->tg_dat
, '\r'), '\0');
422 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_le
];
424 a_SET(tep
, n_TERMCAP_CMD_le
, FAL0
);
425 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
426 n_string_push_c(n_string_push_c(&a_termcap_g
->tg_dat
, '\b'), '\0');
429 /* nd == \033[C (we may not fail, anyway, so use xterm sequence default) */
430 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_nd
];
432 a_SET(tep
, n_TERMCAP_CMD_nd
, FAL0
);
433 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
434 n_string_push_buf(&a_termcap_g
->tg_dat
, "\033[C", sizeof("\033[C"));
436 #endif /* HAVE_MLE */
445 # ifdef HAVE_TERMINFO
447 a_termcap_load(char const *term
){
452 if(!(rv
= (setupterm(term
, fileno(n_tty_fp
), &err
) == OK
)))
453 n_err(_("Unknown ${TERM}inal, using only *termcap*: %s\n"), term
);
459 a_termcap_ent_query(struct a_termcap_ent
*tep
,
460 char const *cname
, ui16_t cflags
){
463 assert(!(n_psonce
& n_PSO_TERMCAP_DISABLE
));
465 if(n_UNLIKELY(*cname
== '\0'))
467 else switch((tep
->te_flags
= cflags
) & a_TERMCAP_F_TYPE_MASK
){
468 case n_TERMCAP_CAPTYPE_BOOL
:
469 tep
->te_off
= (tigetflag(cname
) > 0);
472 case n_TERMCAP_CAPTYPE_NUMERIC
:{
473 int r
= tigetnum(cname
);
476 tep
->te_off
= (ui16_t
)n_MIN(UI16_MAX
, r
);
478 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
481 case n_TERMCAP_CAPTYPE_STRING
:{
484 cp
= tigetstr(cname
);
485 if((rv
= (cp
!= NULL
&& cp
!= (char*)-1))){
486 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
487 n_string_push_buf(&a_termcap_g
->tg_dat
, cp
, strlen(cp
) +1);
489 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
497 a_termcap_ent_query_tcp(struct a_termcap_ent
*tep
,
498 struct a_termcap_control
const *tcp
){
499 assert(!(n_psonce
& n_PSO_TERMCAP_DISABLE
));
500 return a_termcap_ent_query(tep
, &a_termcap_namedat
[tcp
->tc_off
] + 2,
504 # else /* HAVE_TERMINFO */
506 a_termcap_load(char const *term
){
510 /* ncurses may return -1 */
511 # ifndef HAVE_TGETENT_NULL_BUF
512 # define a_BUF &a_termcap_g->tg_lib_buf[0]
516 if(!(rv
= tgetent(a_BUF
, term
) > 0))
517 n_err(_("Unknown ${TERM}inal, using only *termcap*: %s\n"), term
);
524 a_termcap_ent_query(struct a_termcap_ent
*tep
,
525 char const *cname
, ui16_t cflags
){
528 assert(!(n_psonce
& n_PSO_TERMCAP_DISABLE
));
530 if(n_UNLIKELY(*cname
== '\0'))
532 else switch((tep
->te_flags
= cflags
) & a_TERMCAP_F_TYPE_MASK
){
533 case n_TERMCAP_CAPTYPE_BOOL
:
534 tep
->te_off
= (tgetflag(cname
) > 0);
537 case n_TERMCAP_CAPTYPE_NUMERIC
:{
538 int r
= tgetnum(cname
);
541 tep
->te_off
= (ui16_t
)n_MIN(UI16_MAX
, r
);
543 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
546 case n_TERMCAP_CAPTYPE_STRING
:{
547 # ifndef HAVE_TGETENT_NULL_BUF
548 char buf_base
[a_TERMCAP_ENTRYSIZE_MAX
], *buf
= &buf_base
[0];
555 if((rv
= ((cp
= tgetstr(cname
, a_BUF
)) != NULL
))){
556 tep
->te_off
= (ui16_t
)a_termcap_g
->tg_dat
.s_len
;
557 n_string_push_buf(&a_termcap_g
->tg_dat
, cp
, strlen(cp
) +1);
560 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
568 a_termcap_ent_query_tcp(struct a_termcap_ent
*tep
,
569 struct a_termcap_control
const *tcp
){
570 assert(!(n_psonce
& n_PSO_TERMCAP_DISABLE
));
571 return a_termcap_ent_query(tep
, &a_termcap_namedat
[tcp
->tc_off
],
574 # endif /* !HAVE_TERMINFO */
577 a_termcap_putc(int c
){
578 return putc(c
, n_tty_fp
);
580 #endif /* HAVE_TERMCAP */
583 a_termcap_enum_for_name(char const *name
, size_t nlen
, si32_t min
, si32_t max
){
584 struct a_termcap_control
const *tcp
;
589 /* Prefer terminfo(5) names */
596 tcp
= &a_termcap_control
[(ui32_t
)rv
];
597 cnam
= &a_termcap_namedat
[tcp
->tc_off
];
599 char const *xcp
= cnam
+ 2;
601 if(nlen
== strlen(xcp
) && !memcmp(xcp
, name
, nlen
))
604 if(nlen
== 2 && cnam
[0] == name
[0] && cnam
[1] == name
[1])
612 n_termcap_init(void){
617 assert((n_psonce
& n_PSO_INTERACTIVE
) && !(n_poption
& n_PO_QUICKRUN_MASK
));
619 a_termcap_g
= smalloc(sizeof *a_termcap_g
);
620 a_termcap_g
->tg_ext_ents
= NULL
;
621 memset(&a_termcap_g
->tg_ents
[0], 0, sizeof(a_termcap_g
->tg_ents
));
622 if((ccp
= ok_vlook(termcap
)) != NULL
)
623 termvar
.l
= strlen(termvar
.s
= n_UNCONST(ccp
));
625 /*termvar.s = NULL,*/ termvar
.l
= 0;
626 n_string_reserve(n_string_creat(&a_termcap_g
->tg_dat
),
627 ((termvar
.l
+ (256 - 64)) & ~127));
630 a_termcap_init_var(&termvar
);
632 if(ok_blook(termcap_disable
))
633 n_psonce
|= n_PSO_TERMCAP_DISABLE
;
635 else if((ccp
= ok_vlook(TERM
)) == NULL
){
636 n_err(_("Environment variable $TERM is not set, using only *termcap*\n"));
637 n_psonce
|= n_PSO_TERMCAP_DISABLE
;
638 }else if(!a_termcap_load(ccp
))
639 n_psonce
|= n_PSO_TERMCAP_DISABLE
;
641 /* Query termcap(5) for each command slot that is not yet set */
642 struct a_termcap_ent
*tep
;
645 for(i
= n__TERMCAP_CMD_MAX1
;;){
648 if((tep
= &a_termcap_g
->tg_ents
[i
])->te_flags
== 0)
649 a_termcap_ent_query_tcp(tep
, &a_termcap_control
[i
]);
654 a_termcap_init_altern();
657 if(a_termcap_g
->tg_ents
[n_TERMCAP_CMD_te
].te_flags
!= 0)
658 n_psonce
|= n_PSO_TERMCAP_CA_MODE
;
660 n_TERMCAP_RESUME(TRU1
);
665 n_termcap_destroy(void){
667 assert((n_psonce
& n_PSO_INTERACTIVE
) && !(n_poption
& n_PO_QUICKRUN_MASK
));
669 n_TERMCAP_SUSPEND(TRU1
);
673 struct a_termcap_ext_ent
*tmp
;
675 while((tmp
= a_termcap_g
->tg_ext_ents
) != NULL
){
676 a_termcap_g
->tg_ext_ents
= tmp
->tee_next
;
680 n_string_gut(&a_termcap_g
->tg_dat
);
689 n_termcap_resume(bool_t complete
){
691 if((n_psonce
& (n_PSO_INTERACTIVE
| n_PSO_TERMCAP_DISABLE
)
692 ) == n_PSO_INTERACTIVE
&& !(n_poption
& n_PO_QUICKRUN_MASK
)){
693 if(complete
&& (n_psonce
& n_PSO_TERMCAP_CA_MODE
))
694 n_termcap_cmdx(n_TERMCAP_CMD_ti
);
695 n_termcap_cmdx(n_TERMCAP_CMD_ks
);
702 n_termcap_suspend(bool_t complete
){
704 if((n_psonce
& (n_PSO_INTERACTIVE
| n_PSO_TERMCAP_DISABLE
)
705 ) == n_PSO_INTERACTIVE
&& !(n_poption
& n_PO_QUICKRUN_MASK
)){
706 n_termcap_cmdx(n_TERMCAP_CMD_ke
);
707 if(complete
&& (n_psonce
& n_PSO_TERMCAP_CA_MODE
))
708 n_termcap_cmdx(n_TERMCAP_CMD_te
);
713 #endif /* HAVE_TERMCAP */
716 n_termcap_cmd(enum n_termcap_cmd cmd
, ssize_t a1
, ssize_t a2
){
717 /* Commands are not lazy queried */
718 struct a_termcap_ent
const *tep
;
719 enum a_termcap_flags flags
;
726 if(!(n_psonce
& n_PSO_INTERACTIVE
) || (n_poption
& n_PO_QUICKRUN_MASK
))
728 assert(a_termcap_g
!= NULL
);
730 flags
= cmd
& ~n__TERMCAP_CMD_MASK
;
731 cmd
&= n__TERMCAP_CMD_MASK
;
732 tep
= a_termcap_g
->tg_ents
;
734 if((flags
& n_TERMCAP_CMD_FLAG_CA_MODE
) &&
735 !(n_psonce
& n_PSO_TERMCAP_CA_MODE
))
737 else if((tep
+= cmd
)->te_flags
== 0 || (tep
->te_flags
& a_TERMCAP_F_NOENT
))
739 else if(!(tep
->te_flags
& a_TERMCAP_F_ALTERN
)){
740 char const *cp
= a_termcap_g
->tg_dat
.s_dat
+ tep
->te_off
;
742 assert((tep
->te_flags
& a_TERMCAP_F_TYPE_MASK
) ==
743 n_TERMCAP_CAPTYPE_STRING
);
746 if(tep
->te_flags
& (a_TERMCAP_F_ARG_IDX1
| a_TERMCAP_F_ARG_IDX2
)){
747 if(n_psonce
& n_PSO_TERMCAP_DISABLE
){
748 if(n_poption
& n_PO_D_V
){
749 char const *cnam
= &a_termcap_namedat
[
750 a_termcap_control
[cmd
].tc_off
];
754 n_err(_("*termcap-disable*d (/$TERM not set/unknown): "
755 "can't perform CAP: %s\n"), cnam
);
760 /* Follow Thomas Dickey's advise on pre-va_arg prototypes, add 0s */
761 # ifdef HAVE_TERMINFO
762 if((cp
= tparm(cp
, a1
, a2
, 0,0,0,0,0,0,0)) == NULL
)
766 * The \fBtgoto\fP function swaps the order of parameters.
767 * It does this also for calls requiring only a single parameter.
768 * In that case, the first parameter is merely a placeholder. */
769 if(!(tep
->te_flags
& a_TERMCAP_F_ARG_IDX2
)){
773 if((cp
= tgoto(cp
, (int)a1
, (int)a2
)) == NULL
)
781 if(!(n_psonce
& n_PSO_TERMCAP_DISABLE
)){
782 if(tputs(cp
, 1, &a_termcap_putc
) != OK
)
786 if(fputs(cp
, n_tty_fp
) == EOF
)
788 if(!(tep
->te_flags
& a_TERMCAP_F_ARG_CNT
) || --a1
<= 0){
801 case n_TERMCAP_CMD_cl
: /* cl = ho + cd */
802 rv
= n_termcap_cmdx(n_TERMCAP_CMD_ho
);
804 rv
= n_termcap_cmdx(n_TERMCAP_CMD_cd
| flags
);
809 case n_TERMCAP_CMD_ce
: /* ce == ch + [:SPC:] */
812 if((rv
= n_termcap_cmd(n_TERMCAP_CMD_ch
, a1
, 0)) > 0){
813 for(a2
= n_scrnwidth
- a1
- 1; a2
> 0; --a2
)
814 if(putc(' ', n_tty_fp
) == EOF
){
818 if(rv
&& n_termcap_cmd(n_TERMCAP_CMD_ch
, a1
, -1) != TRU1
)
822 case n_TERMCAP_CMD_ch
: /* ch == cr + nd */
823 rv
= n_termcap_cmdx(n_TERMCAP_CMD_cr
);
824 if(rv
> 0 && a1
> 0){
825 rv
= n_termcap_cmd(n_TERMCAP_CMD_nd
, a1
, -1);
828 #endif /* HAVE_MLE */
832 if(flags
& n_TERMCAP_CMD_FLAG_FLUSH
)
844 n_termcap_query(enum n_termcap_query query
, struct n_termcap_value
*tvp
){
845 /* Queries are lazy queried upon request */
846 struct a_termcap_ent
const *tep
;
853 if(!(n_psonce
& n_PSO_INTERACTIVE
) || (n_poption
& n_PO_QUICKRUN_MASK
))
855 assert(a_termcap_g
!= NULL
);
857 /* Is it a built-in query? */
858 if(query
!= n__TERMCAP_QUERY_MAX1
){
859 tep
= &a_termcap_g
->tg_ents
[n__TERMCAP_CMD_MAX1
+ query
];
861 if(tep
->te_flags
== 0
863 && ((n_psonce
& n_PSO_TERMCAP_DISABLE
) ||
864 !a_termcap_ent_query_tcp(n_UNCONST(tep
),
865 &a_termcap_control
[n__TERMCAP_CMD_MAX1
+ query
]))
873 struct a_termcap_ext_ent
*teep
;
874 char const *ndat
= tvp
->tv_data
.tvd_string
;
876 for(teep
= a_termcap_g
->tg_ext_ents
; teep
!= NULL
; teep
= teep
->tee_next
)
877 if(!strcmp(teep
->tee_name
, ndat
)){
878 tep
= &teep
->tee_super
;
883 if(n_psonce
& n_PSO_TERMCAP_DISABLE
)
887 nlen
= strlen(ndat
) +1;
888 teep
= smalloc(n_VSTRUCT_SIZEOF(struct a_termcap_ext_ent
, tee_name
) +
890 tep
= &teep
->tee_super
;
891 teep
->tee_next
= a_termcap_g
->tg_ext_ents
;
892 a_termcap_g
->tg_ext_ents
= teep
;
893 memcpy(teep
->tee_name
, ndat
, nlen
);
895 if(!a_termcap_ent_query(n_UNCONST(tep
), ndat
,
896 n_TERMCAP_CAPTYPE_STRING
| a_TERMCAP_F_QUERY
))
902 if(tep
->te_flags
& a_TERMCAP_F_NOENT
)
905 rv
= (tep
->te_flags
& a_TERMCAP_F_ALTERN
) ? TRUM1
: TRU1
;
907 switch((tvp
->tv_captype
= tep
->te_flags
& a_TERMCAP_F_TYPE_MASK
)){
908 case n_TERMCAP_CAPTYPE_BOOL
:
909 tvp
->tv_data
.tvd_bool
= (bool_t
)tep
->te_off
;
911 case n_TERMCAP_CAPTYPE_NUMERIC
:
912 tvp
->tv_data
.tvd_numeric
= (ui32_t
)tep
->te_off
;
915 case n_TERMCAP_CAPTYPE_STRING
:
916 tvp
->tv_data
.tvd_string
= a_termcap_g
->tg_dat
.s_dat
+ tep
->te_off
;
924 #ifdef HAVE_KEY_BINDINGS
926 n_termcap_query_for_name(char const *name
, enum n_termcap_captype type
){
930 if((rv
= a_termcap_query_for_name(name
, strlen(name
))) >= 0){
931 struct a_termcap_control
const *tcp
= &a_termcap_control
[(ui32_t
)rv
];
933 if(type
!= n_TERMCAP_CAPTYPE_NONE
&&
934 (tcp
->tc_flags
& a_TERMCAP_F_TYPE_MASK
) != type
)
937 rv
-= n__TERMCAP_CMD_MAX1
;
944 n_termcap_name_of_query(enum n_termcap_query query
){
948 rv
= &a_termcap_namedat
[
949 a_termcap_control
[n__TERMCAP_CMD_MAX1
+ query
].tc_off
+ 2];
953 #endif /* HAVE_KEY_BINDINGS */
954 #endif /* n_HAVE_TCAP */