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 nail.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 mk-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) 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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.
48 * TODO After I/O layer rewrite, "output to STDIN_FILENO".
51 /* As long as struct a_termcap_g.tg_buf is fixed size we need an indicator for
52 * spacing it, so that it is large enough to store the biggest terminal entry.
53 * Unless HAVE_TERMINFO or HAVE_TGETENT_NULL_BUF are defined we will also use
54 * this to space the buffer we pass through to tgetent(3).
55 * Since for (such) elder non-emulated terminals really weird things will
56 * happen if an entry would require more than 1024 bytes, don't really mind.
57 * Use a ui16_t for storage */
58 #define a_TERMCAP_ENTRYSIZE_MAX ((2668 + 64) & ~64) /* As of ncurses 6.0 */
60 n_CTA(a_TERMCAP_ENTRYSIZE_MAX
< UI16_MAX
,
61 "Chosen buffer size exceeds datatype capability");
63 /* For simplicity we store commands and queries in single continuous control
64 * and entry structure arrays: to index queries one has to add
65 * n__TERMCAP_CMD_MAX first! And don't confound with ENTRYSIZE_MAX! */
67 a_TERMCAP_ENT_MAX
= n__TERMCAP_CMD_MAX
+ n__TERMCAP_QUERY_MAX
72 /* enum n_termcap_captype values stored here.
73 * Note that presence of a type in an a_termcap_ent signals initialization */
74 a_TERMCAP_F_TYPE_MASK
= (1<<4) - 1,
76 a_TERMCAP_F_QUERY
= 1<<4, /* A query rather than a command */
77 a_TERMCAP_F_DISABLED
= 1<<5, /* User explicitly disabled command/query */
78 a_TERMCAP_F_ALTERN
= 1<<6, /* Not available, but has alternative */
79 a_TERMCAP_F_NOENT
= 1<<7, /* Not available */
81 /* _cmd() argument interpretion (_T_STR) */
82 a_TERMCAP_F_ARG_IDX1
= 1<<11, /* Argument 1 used, and is an index */
83 a_TERMCAP_F_ARG_IDX2
= 1<<12,
84 a_TERMCAP_F_ARG_CNT
= 1<<13, /* .., and is a count */
86 a_TERMCAP_F__LAST
= a_TERMCAP_F_ARG_CNT
88 n_CTA((ui32_t
)n__TERMCAP_CAPTYPE_MAX
<= (ui32_t
)a_TERMCAP_F_TYPE_MASK
,
89 "enum n_termcap_captype exceeds bit range of a_termcap_flags");
91 struct a_termcap_control
{
93 /* Offset base into a_termcap_namedat[], which stores the two-letter
94 * termcap(5) name directly followed by a NUL terminated terminfo(5) name.
95 * A termcap(5) name may consist of two NULs meaning ENOENT,
96 * a terminfo(5) name may be empty for the same purpose */
99 n_CTA(a_TERMCAP_F__LAST
<= UI16_MAX
,
100 "a_termcap_flags exceed storage datatype in a_termcap_control");
102 struct a_termcap_ent
{
104 ui16_t te_off
; /* in a_termcap_g->tg_buf / value for T_BOOL and T_NUM */
106 n_CTA(a_TERMCAP_F__LAST
<= UI16_MAX
,
107 "a_termcap_flags exceed storage datatype in a_termcap_ent");
109 /* Structure for extended queries, which don't have an entry constant in
110 * n_termcap_query (to allow free query/binding of keycodes) */
111 struct a_termcap_ext_ent
{
112 struct a_termcap_ent tee_super
;
114 struct a_termcap_ext_ent
*tee_next
;
115 /* Resolvable termcap(5)/terminfo(5) name as given by user; the actual data
116 * is stored just like for normal queries */
117 char tee_name
[VFIELD_SIZE(0)];
121 struct a_termcap_ext_ent
*tg_ext_ents
; /* List of extended queries */
122 struct a_termcap_ent tg_ents
[a_TERMCAP_ENT_MAX
];
123 char *tg_buf_tail
; /* Current cast-off in data storage */
124 char *tg_lib_buf
; /* Shall tgetent(3) need buffer, that it is */
125 char tg_buf
[VFIELD_SIZE(0)]; /* Storage for just about anything */
126 /* TODO replace .tg_buf with a string object and resize as necessary; then
127 * TODO add a variable member to hold .tg_lib_buf IF we really need that */
130 /* Include the constant mk-tcap-map.pl output */
132 n_CTA(sizeof a_termcap_namedat
<= UI16_MAX
,
133 "Termcap command and query name data exceed storage datatype");
134 n_CTA(a_TERMCAP_ENT_MAX
== NELEM(a_termcap_control
),
135 "Control array doesn't match command/query array to be controlled");
137 static struct a_termcap_g
*a_termcap_g
;
139 /* Query *termcap*, parse it and incorporate into a_termcap_g */
140 static void a_termcap_init_var(struct str
const *termvar
);
142 /* Initialize any _ent for which we have _F_ALTERN and which isn't yet set */
143 static void a_termcap_init_altern(void);
146 /* Setup the library we use to work with term */
147 static bool_t
a_termcap_load(char const *term
);
149 /* Query the capability tcp and fill in tep (upon success) */
150 static bool_t
a_termcap_ent_query(struct a_termcap_ent
*tep
,
151 char const *cname
, ui16_t cflags
);
152 SINLINE bool_t
a_termcap_ent_query_tcp(struct a_termcap_ent
*tep
,
153 struct a_termcap_control
const *tcp
);
155 /* Output PTF for both, termcap(5) and terminfo(5) */
156 static int a_termcap_putc(int c
);
159 /* Get n_termcap_cmd or n_termcap_query constant belonging to (nlen bytes of)
160 * name, or -1 if not found. min and max have to be used to cramp the result */
161 static si32_t
a_termcap_enum_for_name(char const *name
, size_t nlen
,
162 si32_t min
, si32_t max
);
163 #define a_termcap_cmd_for_name(NB,NL) \
164 a_termcap_enum_for_name(NB, NL, 0, n__TERMCAP_CMD_MAX)
165 #define a_termcap_query_for_name(NB,NL) \
166 a_termcap_enum_for_name(NB, NL, n__TERMCAP_CMD_MAX, a_TERMCAP_ENT_MAX)
169 a_termcap_init_var(struct str
const *termvar
){
170 char *cbp_base
, *cbp
, *ebp
;
175 if(termvar
->l
>= UI16_MAX
){
176 n_err(_("*termcap*: length excesses internal limit, skipping\n"));
180 assert(termvar
->s
[termvar
->l
] == '\0');
182 cbp_base
= ac_alloc(i
);
183 memcpy(cbp
= cbp_base
, termvar
->s
, i
);
185 ebp
= a_termcap_g
->tg_buf_tail
;
186 for(; (ccp
= n_strsep(&cbp
, ',', TRU1
)) != NULL
;){
187 struct a_termcap_ent
*tep
;
192 /* Separate key/value, if any */
193 if(/* no empties ccp[0] == '\0' ||*/ ccp
[1] == '\0'){
195 n_err(_("*termcap*: invalid entry: \"%s\"\n"), ccp
);
198 for(kl
= 2, v
= &ccp
[2];; ++kl
, ++v
){
202 f
= n_TERMCAP_CAPTYPE_BOOL
;
205 f
= n_TERMCAP_CAPTYPE_NUMERIC
;
209 f
= n_TERMCAP_CAPTYPE_STRING
;
215 /* Do we know about this one? */
217 struct a_termcap_control
const *tcp
;
218 si32_t tci
= a_termcap_enum_for_name(ccp
, kl
, 0, a_TERMCAP_ENT_MAX
);
221 if(options
& OPT_D_V
)
222 n_err(_("*termcap*: unknown capability: \"%s\"\n"), ccp
);
227 tcp
= &a_termcap_control
[i
];
228 if((tcp
->tc_flags
& a_TERMCAP_F_TYPE_MASK
) != f
){
229 n_err(_("*termcap*: entry type mismatch: \"%s\"\n"), ccp
);
232 tep
= &a_termcap_g
->tg_ents
[i
];
233 tep
->te_flags
= tcp
->tc_flags
;
234 tep
->te_off
= (ui16_t
)PTR2SIZE(ebp
- a_termcap_g
->tg_buf
);
237 if((f
& a_TERMCAP_F_TYPE_MASK
) == n_TERMCAP_CAPTYPE_BOOL
)
240 tep
->te_flags
|= a_TERMCAP_F_DISABLED
;
241 else if((f
& a_TERMCAP_F_TYPE_MASK
) == n_TERMCAP_CAPTYPE_NUMERIC
){
243 long l
= strtol(v
, &eptr
, 10);
245 if(*eptr
!= '\0' || l
< 0 || UICMP(32, l
, >=, UI16_MAX
))
247 tep
->te_off
= (ui16_t
)l
;
249 int c
= n_shell_expand_escape(&v
, FAL0
);
256 a_termcap_g
->tg_buf_tail
= ebp
;
257 DBG( if(options
& OPT_D_V
) n_err("*termcap* parsed: buffer used=%lu\n",
258 (ul_i
)PTR2SIZE(ebp
- a_termcap_g
->tg_buf
)) );
262 /* Catch some inter-dependencies the user may have triggered */
264 if(a_termcap_g
->tg_ents
[n_TERMCAP_CMD_te
].te_flags
& a_TERMCAP_F_DISABLED
)
265 a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ti
].te_flags
= a_TERMCAP_F_DISABLED
;
266 else if(a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ti
].te_flags
&
267 a_TERMCAP_F_DISABLED
)
268 a_termcap_g
->tg_ents
[n_TERMCAP_CMD_te
].te_flags
= a_TERMCAP_F_DISABLED
;
276 a_termcap_init_altern(void){
277 /* We silently ignore user _F_DISABLED requests for those entries for which
278 * we have fallback entries, and which we need to ensure proper functioning.
279 * I.e., this allows users to explicitly disable some termcap(5) capability
280 * and enforce usage of the builtin fallback */
281 /* xxx Use table-based approach for fallback strategies */
282 #define a_OK(CMD) a_OOK(&a_termcap_g->tg_ents[CMD])
283 #define a_OOK(TEP) ((TEP)->te_flags != 0)
284 #define a_SET(TEP,CMD,ALT) \
285 (TEP)->te_flags = a_termcap_control[CMD].tc_flags |\
286 ((ALT) ? a_TERMCAP_F_ALTERN : 0)
288 struct a_termcap_ent
*tep
;
292 /* For simplicity in the rest of this file null flags of disabled commands,
293 * as we won't check and try to lazy query any command */
297 for(i
= n__TERMCAP_CMD_MAX
;;){
300 if((tep
= &a_termcap_g
->tg_ents
[i
])->te_flags
& a_TERMCAP_F_DISABLED
)
307 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_cl
];
309 if(a_OK(n_TERMCAP_CMD_cd
) && a_OK(n_TERMCAP_CMD_ho
))
310 a_SET(tep
, n_TERMCAP_CMD_cl
, TRU1
);
315 /* ce == ch + [:SPC:] (start column specified by argument) */
316 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ce
];
318 a_SET(tep
, n_TERMCAP_CMD_ce
, TRU1
);
320 /* ch == cr[\r] + nd[:\033C:] */
321 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_ch
];
323 a_SET(tep
, n_TERMCAP_CMD_ch
, TRU1
);
326 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_cr
];
328 char *bp
= a_termcap_g
->tg_buf_tail
;
330 a_SET(tep
, n_TERMCAP_CMD_cr
, FAL0
);
331 tep
->te_off
= (ui16_t
)PTR2SIZE(bp
- a_termcap_g
->tg_buf
);
334 a_termcap_g
->tg_buf_tail
= (bp
+= 2);
338 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_le
];
340 char *bp
= a_termcap_g
->tg_buf_tail
;
342 a_SET(tep
, n_TERMCAP_CMD_le
, FAL0
);
343 tep
->te_off
= (ui16_t
)PTR2SIZE(bp
- a_termcap_g
->tg_buf
);
346 a_termcap_g
->tg_buf_tail
= (bp
+= 2);
349 /* nd == \033[C (we may not fail, anyway, so use xterm sequence default) */
350 tep
= &a_termcap_g
->tg_ents
[n_TERMCAP_CMD_nd
];
352 char *bp
= a_termcap_g
->tg_buf_tail
;
354 a_SET(tep
, n_TERMCAP_CMD_nd
, FAL0
);
355 tep
->te_off
= (ui16_t
)PTR2SIZE(bp
- a_termcap_g
->tg_buf
);
356 memcpy(bp
, "\033[C", sizeof("\033[C"));
357 a_termcap_g
->tg_buf_tail
= (bp
+= sizeof("\033[C"));
359 #endif /* HAVE_MLE */
368 # ifdef HAVE_TERMINFO
370 a_termcap_load(char const *term
){
375 if(!(rv
= (setupterm(term
, STDOUT_FILENO
, &err
) == OK
)))
376 n_err(_("Unknown ${TERM}inal \"%s\", using only *termcap*\n"), term
);
382 a_termcap_ent_query(struct a_termcap_ent
*tep
,
383 char const *cname
, ui16_t cflags
){
387 if(UNLIKELY(*cname
== '\0'))
389 else switch((tep
->te_flags
= cflags
) & a_TERMCAP_F_TYPE_MASK
){
390 case n_TERMCAP_CAPTYPE_BOOL
:
391 tep
->te_off
= (tigetflag(cname
) > 0);
394 case n_TERMCAP_CAPTYPE_NUMERIC
:{
395 int r
= tigetnum(cname
);
398 tep
->te_off
= (ui16_t
)MIN(UI16_MAX
, r
);
400 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
403 case n_TERMCAP_CAPTYPE_STRING
:{
406 cp
= tigetstr(cname
);
407 if((rv
= (cp
!= NULL
&& cp
!= (char*)-1))){
408 char *ebp_orig
= a_termcap_g
->tg_buf_tail
;
409 size_t l
= strlen(cp
) +1;
411 tep
->te_off
= (ui16_t
)PTR2SIZE(ebp_orig
- a_termcap_g
->tg_buf
);
412 assert((size_t)tep
->te_off
==
413 PTR2SIZE(ebp_orig
- a_termcap_g
->tg_buf
));
414 memcpy(ebp_orig
, cp
, l
);
415 a_termcap_g
->tg_buf_tail
= (ebp_orig
+= l
);
417 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
425 a_termcap_ent_query_tcp(struct a_termcap_ent
*tep
,
426 struct a_termcap_control
const *tcp
){
427 return a_termcap_ent_query(tep
, &a_termcap_namedat
[tcp
->tc_off
] + 2,
431 # else /* HAVE_TERMINFO */
433 a_termcap_load(char const *term
){
437 /* ncurses may return -1 */
438 if(!(rv
= tgetent(a_termcap_g
->tg_lib_buf
, term
) > 0))
439 n_err(_("Unknown ${TERM}inal \"%s\", using only *termcap*\n"), term
);
445 a_termcap_ent_query(struct a_termcap_ent
*tep
,
446 char const *cname
, ui16_t cflags
){
450 if(UNLIKELY(*cname
== '\0'))
452 else switch((tep
->te_flags
= cflags
) & a_TERMCAP_F_TYPE_MASK
){
453 case n_TERMCAP_CAPTYPE_BOOL
:
454 tep
->te_off
= (tgetflag(cname
) > 0);
457 case n_TERMCAP_CAPTYPE_NUMERIC
:{
458 int r
= tgetnum(cname
);
461 tep
->te_off
= (ui16_t
)MIN(UI16_MAX
, r
);
463 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
466 case n_TERMCAP_CAPTYPE_STRING
:{
467 char *ebp_orig
= a_termcap_g
->tg_buf_tail
;
469 if((rv
= (tgetstr(cname
, &a_termcap_g
->tg_buf_tail
) != NULL
))){
470 tep
->te_off
= (ui16_t
)PTR2SIZE(ebp_orig
- a_termcap_g
->tg_buf
);
471 assert((size_t)tep
->te_off
==
472 PTR2SIZE(ebp_orig
- a_termcap_g
->tg_buf
));
474 tep
->te_flags
|= a_TERMCAP_F_NOENT
;
482 a_termcap_ent_query_tcp(struct a_termcap_ent
*tep
,
483 struct a_termcap_control
const *tcp
){
484 return a_termcap_ent_query(tep
, &a_termcap_namedat
[tcp
->tc_off
],
487 # endif /* !HAVE_TERMINFO */
490 a_termcap_putc(int c
){
493 #endif /* HAVE_TERMCAP */
496 a_termcap_enum_for_name(char const *name
, size_t nlen
, si32_t min
, si32_t max
){
497 struct a_termcap_control
const *tcp
;
502 /* Prefer terminfo(5) names */
509 tcp
= &a_termcap_control
[(ui32_t
)rv
];
510 cnam
= &a_termcap_namedat
[tcp
->tc_off
];
512 char const *xcp
= cnam
+ 2;
514 if(nlen
== strlen(xcp
) && !memcmp(xcp
, name
, nlen
))
517 if(nlen
== 2 && cnam
[0] == name
[0] && cnam
[1] == name
[1])
525 n_termcap_init(void){
530 assert((options
& (OPT_INTERACTIVE
| OPT_QUICKRUN_MASK
)) == OPT_INTERACTIVE
);
532 if((ccp
= ok_vlook(termcap
)) != NULL
)
533 termvar
.l
= strlen(termvar
.s
= UNCONST(ccp
));
535 /*termvar.s = NULL,*/ termvar
.l
= 0;
538 size_t bl
= sizeof(struct a_termcap_g
) -
539 VFIELD_SIZEOF(struct a_termcap_g
, tg_buf
) +
540 termvar
.l
+ a_TERMCAP_ENTRYSIZE_MAX
541 # if !defined HAVE_TGETENT_NULL_BUF && !defined HAVE_TERMINFO
542 + a_TERMCAP_ENTRYSIZE_MAX
545 a_termcap_g
= smalloc(bl
);
547 a_termcap_g
->tg_ext_ents
= NULL
;
548 a_termcap_g
->tg_buf_tail
= a_termcap_g
->tg_buf
;
549 a_termcap_g
->tg_lib_buf
=
550 # if defined HAVE_TGETENT_NULL_BUF || defined HAVE_TERMINFO
553 &a_termcap_g
->tg_buf
[termvar
.l
+ a_TERMCAP_ENTRYSIZE_MAX
]
556 memset(&a_termcap_g
->tg_ents
[0], 0, sizeof(a_termcap_g
->tg_ents
));
559 a_termcap_init_var(&termvar
);
561 if(ok_blook(termcap_disable
))
562 pstate
|= PS_TERMCAP_DISABLE
;
564 else if((ccp
= env_vlook("TERM", FAL0
)) == NULL
){
565 n_err(_("Environment variable $TERM is not set, using only *termcap*\n"));
566 pstate
|= PS_TERMCAP_DISABLE
;
567 }else if(!a_termcap_load(ccp
))
568 pstate
|= PS_TERMCAP_DISABLE
;
570 /* Query termcap(5) for each command slot that is not yet set */
571 struct a_termcap_ent
*tep
;
574 for(i
= n__TERMCAP_CMD_MAX
;;){
577 if((tep
= &a_termcap_g
->tg_ents
[i
])->te_flags
== 0)
578 a_termcap_ent_query_tcp(tep
, &a_termcap_control
[i
]);
583 a_termcap_init_altern();
586 if(a_termcap_g
->tg_ents
[n_TERMCAP_CMD_te
].te_flags
!= 0)
587 pstate
|= PS_TERMCAP_CA_MODE
;
589 n_TERMCAP_RESUME(TRU1
);
594 n_termcap_destroy(void){
596 assert((options
& (OPT_INTERACTIVE
| OPT_QUICKRUN_MASK
)) == OPT_INTERACTIVE
);
598 n_TERMCAP_SUSPEND(TRU1
);
602 struct a_termcap_ext_ent
*tmp
;
604 while((tmp
= a_termcap_g
->tg_ext_ents
) != NULL
){
605 a_termcap_g
->tg_ext_ents
= tmp
->tee_next
;
617 n_termcap_resume(bool_t complete
){
619 if(!(pstate
& PS_TERMCAP_DISABLE
) &&
620 (options
& (OPT_INTERACTIVE
| OPT_QUICKRUN_MASK
)) == OPT_INTERACTIVE
){
621 if(complete
&& (pstate
& PS_TERMCAP_CA_MODE
))
622 n_termcap_cmdx(n_TERMCAP_CMD_ti
);
623 n_termcap_cmdx(n_TERMCAP_CMD_ks
);
630 n_termcap_suspend(bool_t complete
){
632 if(!(pstate
& PS_TERMCAP_DISABLE
) &&
633 (options
& (OPT_INTERACTIVE
| OPT_QUICKRUN_MASK
)) == OPT_INTERACTIVE
){
634 if(complete
&& (pstate
& PS_TERMCAP_CA_MODE
))
635 n_termcap_cmdx(n_TERMCAP_CMD_ke
);
636 n_termcap_cmdx(n_TERMCAP_CMD_te
);
641 #endif /* HAVE_TERMCAP */
644 n_termcap_cmd(enum n_termcap_cmd cmd
, ssize_t a1
, ssize_t a2
){
645 /* Commands are not lazy queried */
646 struct a_termcap_ent
const *tep
;
647 enum a_termcap_flags flags
;
654 if((options
& (OPT_INTERACTIVE
| OPT_QUICKRUN_MASK
)) != OPT_INTERACTIVE
)
656 assert(a_termcap_g
!= NULL
);
658 flags
= cmd
& ~n__TERMCAP_CMD_MASK
;
659 cmd
&= n__TERMCAP_CMD_MASK
;
660 tep
= a_termcap_g
->tg_ents
;
662 if((flags
& n_TERMCAP_CMD_FLAG_CA_MODE
) && !(pstate
& PS_TERMCAP_CA_MODE
))
664 else if((tep
+= cmd
)->te_flags
== 0 || (tep
->te_flags
& a_TERMCAP_F_NOENT
))
666 else if(!(tep
->te_flags
& a_TERMCAP_F_ALTERN
)){
667 char const *cp
= a_termcap_g
->tg_buf
+ tep
->te_off
;
669 assert((tep
->te_flags
& a_TERMCAP_F_TYPE_MASK
) ==
670 n_TERMCAP_CAPTYPE_STRING
);
673 if(tep
->te_flags
& (a_TERMCAP_F_ARG_IDX1
| a_TERMCAP_F_ARG_IDX2
)){
674 if(pstate
& PS_TERMCAP_DISABLE
){
675 if(options
& OPT_D_V
){
676 char const *cnam
= &a_termcap_namedat
[
677 a_termcap_control
[cmd
].tc_off
];
681 n_err(_("*termcap-disable*d (/$TERM not set/unknown): "
682 "can't perform CAP \"%s\"\n"), cnam
);
687 # ifdef HAVE_TERMINFO
688 if((cp
= tparm(cp
, a1
, a2
, 0,0,0,0,0,0,0)) == NULL
)
692 * The \fBtgoto\fP function swaps the order of parameters.
693 * It does this also for calls requiring only a single parameter.
694 * In that case, the first parameter is merely a placeholder. */
695 if(!(tep
->te_flags
& a_TERMCAP_F_ARG_IDX2
)){
699 if((cp
= tgoto(cp
, (int)a1
, (int)a2
)) == NULL
)
707 if(!(pstate
& PS_TERMCAP_DISABLE
)){
708 if(tputs(cp
, 1, &a_termcap_putc
) != OK
)
712 if(fputs(cp
, stdout
) == EOF
)
714 if(!(tep
->te_flags
& a_TERMCAP_F_ARG_CNT
) || --a1
<= 0){
727 case n_TERMCAP_CMD_cl
: /* cl = ho + cd */
728 rv
= n_termcap_cmdx(n_TERMCAP_CMD_ho
);
730 rv
= n_termcap_cmdx(n_TERMCAP_CMD_cd
| flags
);
735 case n_TERMCAP_CMD_ce
: /* ce == ch + [:SPC:] */
738 if((rv
= n_termcap_cmd(n_TERMCAP_CMD_ch
, a1
, 0)) > 0){
739 for(a2
= scrnwidth
- a1
- 1; a2
> 0; --a2
)
740 if(putchar(' ') == EOF
){
744 if(rv
&& n_termcap_cmd(n_TERMCAP_CMD_ch
, a1
, -1) != TRU1
)
748 case n_TERMCAP_CMD_ch
: /* ch == cr + nd */
749 rv
= n_termcap_cmdx(n_TERMCAP_CMD_cr
);
750 if(rv
> 0 && a1
> 0){
751 rv
= n_termcap_cmd(n_TERMCAP_CMD_nd
, a1
, -1);
754 #endif /* HAVE_MLE */
758 if(flags
& n_TERMCAP_CMD_FLAG_FLUSH
)
770 n_termcap_query(enum n_termcap_query query
, struct n_termcap_value
*tvp
){
771 /* Queries are lazy queried upon request */
772 struct a_termcap_ent
const *tep
;
779 if((options
& (OPT_INTERACTIVE
| OPT_QUICKRUN_MASK
)) != OPT_INTERACTIVE
)
781 assert(a_termcap_g
!= NULL
);
783 /* Is it a builtin query? */
784 if(query
!= n__TERMCAP_QUERY_MAX
){
785 tep
= &a_termcap_g
->tg_ents
[n__TERMCAP_CMD_MAX
+ query
];
787 if(tep
->te_flags
== 0
789 && !a_termcap_ent_query_tcp(UNCONST(tep
),
790 &a_termcap_control
[n__TERMCAP_CMD_MAX
+ query
])
799 struct a_termcap_ext_ent
*teep
;
800 char const *ndat
= tvp
->tv_data
.tvd_string
;
802 for(teep
= a_termcap_g
->tg_ext_ents
; teep
!= NULL
; teep
= teep
->tee_next
)
803 if(!strcmp(teep
->tee_name
, ndat
)){
804 tep
= &teep
->tee_super
;
808 nlen
= strlen(ndat
) +1;
809 teep
= smalloc(sizeof(*teep
) -
810 VFIELD_SIZEOF(struct a_termcap_ext_ent
, tee_name
) + nlen
);
811 tep
= &teep
->tee_super
;
812 teep
->tee_next
= a_termcap_g
->tg_ext_ents
;
813 a_termcap_g
->tg_ext_ents
= teep
;
814 memcpy(teep
->tee_name
, ndat
, nlen
);
816 if(!a_termcap_ent_query(UNCONST(tep
), ndat
,
817 n_TERMCAP_CAPTYPE_STRING
| a_TERMCAP_F_QUERY
))
823 if(tep
->te_flags
& a_TERMCAP_F_NOENT
)
826 rv
= (tep
->te_flags
& a_TERMCAP_F_ALTERN
) ? TRUM1
: TRU1
;
828 switch((tvp
->tv_captype
= tep
->te_flags
& a_TERMCAP_F_TYPE_MASK
)){
829 case n_TERMCAP_CAPTYPE_BOOL
:
830 tvp
->tv_data
.tvd_bool
= (bool_t
)tep
->te_off
;
832 case n_TERMCAP_CAPTYPE_NUMERIC
:
833 tvp
->tv_data
.tvd_numeric
= (ui32_t
)tep
->te_off
;
836 case n_TERMCAP_CAPTYPE_STRING
:
837 tvp
->tv_data
.tvd_string
= a_termcap_g
->tg_buf
+ tep
->te_off
;
844 #endif /* n_HAVE_TCAP */