xref: /openbsd/lib/libcurses/tinfo/lib_termcap.c (revision c7ef0cfc)
1 /* $OpenBSD: lib_termcap.c,v 1.11 2023/10/17 09:52:09 nicm Exp $ */
2 
3 /****************************************************************************
4  * Copyright 2018-2020,2023 Thomas E. Dickey                                *
5  * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
6  *                                                                          *
7  * Permission is hereby granted, free of charge, to any person obtaining a  *
8  * copy of this software and associated documentation files (the            *
9  * "Software"), to deal in the Software without restriction, including      *
10  * without limitation the rights to use, copy, modify, merge, publish,      *
11  * distribute, distribute with modifications, sublicense, and/or sell       *
12  * copies of the Software, and to permit persons to whom the Software is    *
13  * furnished to do so, subject to the following conditions:                 *
14  *                                                                          *
15  * The above copyright notice and this permission notice shall be included  *
16  * in all copies or substantial portions of the Software.                   *
17  *                                                                          *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
21  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
24  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
25  *                                                                          *
26  * Except as contained in this notice, the name(s) of the above copyright   *
27  * holders shall not be used in advertising or otherwise to promote the     *
28  * sale, use or other dealings in this Software without prior written       *
29  * authorization.                                                           *
30  ****************************************************************************/
31 
32 /****************************************************************************
33  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
34  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
35  *     and: Thomas E. Dickey                        1996-on                 *
36  *     and: Juergen Pfeifer                                                 *
37  *                                                                          *
38  * some of the code in here was contributed by:                             *
39  * Magnus Bengtsson, d6mbeng@dtek.chalmers.se (Nov'93)                      *
40  * (but it has changed a lot)                                               *
41  ****************************************************************************/
42 
43 #define __INTERNAL_CAPS_VISIBLE
44 #include <curses.priv.h>
45 
46 #include <termcap.h>
47 #include <tic.h>
48 #include <ctype.h>
49 
50 #ifndef CUR
51 #define CUR SP_TERMTYPE
52 #endif
53 
54 MODULE_ID("$Id: lib_termcap.c,v 1.11 2023/10/17 09:52:09 nicm Exp $")
55 
56 NCURSES_EXPORT_VAR(char *) UP = 0;
57 NCURSES_EXPORT_VAR(char *) BC = 0;
58 
59 #define MyCache  _nc_globals.tgetent_cache
60 #define CacheInx _nc_globals.tgetent_index
61 #define CacheSeq _nc_globals.tgetent_sequence
62 
63 #define FIX_SGR0 MyCache[CacheInx].fix_sgr0
64 #define LAST_TRM MyCache[CacheInx].last_term
65 #define LAST_BUF MyCache[CacheInx].last_bufp
66 #define LAST_USE MyCache[CacheInx].last_used
67 #define LAST_SEQ MyCache[CacheInx].sequence
68 
69 /*
70  * Termcap names are matched only using the first two bytes.
71  * Ignore any extended names longer than two bytes, to avoid problems
72  * with legacy code which passes in parameters whose use is long forgotten.
73  */
74 #define ValidCap(cap) (((cap)[0] != '\0') && ((cap)[1] != '\0'))
75 #define SameCap(a,b)  (((a)[0] == (b)[0]) && ((a)[1] == (b)[1]))
76 #define ValidExt(ext) (ValidCap(ext) && (ext)[2] == '\0')
77 
78 /***************************************************************************
79  *
80  * tgetent(bufp, term)
81  *
82  * In termcap, this function reads in the entry for terminal `term' into the
83  * buffer pointed to by bufp. It must be called before any of the functions
84  * below are called.
85  * In this terminfo emulation, tgetent() simply calls setupterm() (which
86  * does a bit more than tgetent() in termcap does), and returns its return
87  * value (1 if successful, 0 if no terminal with the given name could be
88  * found, or -1 if no terminal descriptions have been installed on the
89  * system).  The bufp argument is ignored.
90  *
91  ***************************************************************************/
92 
93 NCURSES_EXPORT(int)
NCURSES_SP_NAME(tgetent)94 NCURSES_SP_NAME(tgetent) (NCURSES_SP_DCLx char *bufp, const char *name)
95 {
96     int rc = ERR;
97     int n;
98     bool found_cache = FALSE;
99 #ifdef USE_TERM_DRIVER
100     TERMINAL *termp = 0;
101 #endif
102 
103     START_TRACE();
104     T((T_CALLED("tgetent()")));
105 
106     TINFO_SETUP_TERM(&termp, name, STDOUT_FILENO, &rc, TRUE);
107 
108 #ifdef USE_TERM_DRIVER
109     if (termp == 0 ||
110 	!((TERMINAL_CONTROL_BLOCK *) termp)->drv->isTerminfo)
111 	returnCode(rc);
112 #endif
113 
114     /*
115      * In general we cannot tell if the fixed sgr0 is still used by the
116      * caller, but if tgetent() is called with the same buffer, that is
117      * good enough, since the previous data would be invalidated by the
118      * current call.
119      *
120      * bufp may be a null pointer, e.g., GNU termcap.  That allocates data,
121      * which is good until the next tgetent() call.  The conventional termcap
122      * is inconvenient because of the fixed buffer size, but because it uses
123      * caller-supplied buffers, can have multiple terminal descriptions in
124      * use at a given time.
125      */
126     for (n = 0; n < TGETENT_MAX; ++n) {
127 	bool same_result = (MyCache[n].last_used && MyCache[n].last_bufp == bufp);
128 	if (same_result) {
129 	    CacheInx = n;
130 	    if (FIX_SGR0 != 0) {
131 		FreeAndNull(FIX_SGR0);
132 	    }
133 	    /*
134 	     * Also free the terminfo data that we loaded (much bigger leak).
135 	     */
136 	    if (LAST_TRM != 0 && LAST_TRM != TerminalOf(SP_PARM)) {
137 		TERMINAL *trm = LAST_TRM;
138 		NCURSES_SP_NAME(del_curterm) (NCURSES_SP_ARGx LAST_TRM);
139 		for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx)
140 		    if (LAST_TRM == trm)
141 			LAST_TRM = 0;
142 		CacheInx = n;
143 	    }
144 	    found_cache = TRUE;
145 	    break;
146 	}
147     }
148     if (!found_cache) {
149 	int best = 0;
150 
151 	for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) {
152 	    if (LAST_SEQ < MyCache[best].sequence) {
153 		best = CacheInx;
154 	    }
155 	}
156 	CacheInx = best;
157     }
158     if (rc == 1) {
159 	LAST_TRM = TerminalOf(SP_PARM);
160 	LAST_SEQ = ++CacheSeq;
161     } else {
162 	LAST_TRM = 0;
163     }
164 
165     PC = 0;
166     UP = 0;
167     BC = 0;
168     FIX_SGR0 = 0;		/* don't free it - application may still use */
169 
170     if (rc == 1) {
171 
172 	if (cursor_left)
173 	    if ((backspaces_with_bs = (char) !strcmp(cursor_left, "\b")) == 0)
174 		backspace_if_not_bs = cursor_left;
175 
176 	/* we're required to export these */
177 	if (pad_char != NULL)
178 	    PC = pad_char[0];
179 	if (cursor_up != NULL)
180 	    UP = cursor_up;
181 	if (backspace_if_not_bs != NULL)
182 	    BC = backspace_if_not_bs;
183 
184 	if ((FIX_SGR0 = _nc_trim_sgr0(&TerminalType(TerminalOf(SP_PARM))))
185 	    != 0) {
186 	    if (!strcmp(FIX_SGR0, exit_attribute_mode)) {
187 		if (FIX_SGR0 != exit_attribute_mode) {
188 		    free(FIX_SGR0);
189 		}
190 		FIX_SGR0 = 0;
191 	    }
192 	}
193 	LAST_BUF = bufp;
194 	LAST_USE = TRUE;
195 
196 	SetNoPadding(SP_PARM);
197 	(void) NCURSES_SP_NAME(baudrate) (NCURSES_SP_ARG);	/* sets ospeed as a side-effect */
198 
199 /* LINT_PREPRO
200 #if 0*/
201 #include <capdefaults.c>
202 /* LINT_PREPRO
203 #endif*/
204 
205     }
206     returnCode(rc);
207 }
208 
209 #if NCURSES_SP_FUNCS
210 NCURSES_EXPORT(int)
tgetent(char * bufp,const char * name)211 tgetent(char *bufp, const char *name)
212 {
213     return NCURSES_SP_NAME(tgetent) (CURRENT_SCREEN, bufp, name);
214 }
215 #endif
216 
217 #if 0
218 static bool
219 same_tcname(const char *a, const char *b)
220 {
221     bool code = SameCap(a, b);
222     fprintf(stderr, "compare(%s,%s) %s\n", a, b, code ? "same" : "diff");
223     return code;
224 }
225 
226 #else
227 #define same_tcname(a,b) SameCap(a,b)
228 #endif
229 
230 /***************************************************************************
231  *
232  * tgetflag(str)
233  *
234  * Look up boolean termcap capability str and return its value (TRUE=1 if
235  * present, FALSE=0 if not).
236  *
237  ***************************************************************************/
238 
239 NCURSES_EXPORT(int)
NCURSES_SP_NAME(tgetflag)240 NCURSES_SP_NAME(tgetflag) (NCURSES_SP_DCLx const char *id)
241 {
242     int result = 0;		/* Solaris returns zero for missing flag */
243 
244     T((T_CALLED("tgetflag(%p, %s)"), (void *) SP_PARM, id));
245     if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) {
246 	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
247 	struct name_table_entry const *entry_ptr;
248 	int j = -1;
249 
250 	entry_ptr = _nc_find_type_entry(id, BOOLEAN, TRUE);
251 	if (entry_ptr != 0) {
252 	    j = entry_ptr->nte_index;
253 	}
254 #if NCURSES_XNAMES
255 	else {
256 	    int i;
257 	    for_each_ext_boolean(i, tp) {
258 		const char *capname = ExtBoolname(tp, i, boolcodes);
259 		if (same_tcname(id, capname) && ValidExt(capname)) {
260 		    j = i;
261 		    break;
262 		}
263 	    }
264 	}
265 #endif
266 	if (j >= 0) {
267 	    /* note: setupterm forces invalid booleans to false */
268 	    result = tp->Booleans[j];
269 	}
270     }
271     returnCode(result);
272 }
273 
274 #if NCURSES_SP_FUNCS
275 NCURSES_EXPORT(int)
tgetflag(const char * id)276 tgetflag(const char *id)
277 {
278     return NCURSES_SP_NAME(tgetflag) (CURRENT_SCREEN, id);
279 }
280 #endif
281 
282 /***************************************************************************
283  *
284  * tgetnum(str)
285  *
286  * Look up numeric termcap capability str and return its value, or -1 if
287  * not given.
288  *
289  ***************************************************************************/
290 
291 NCURSES_EXPORT(int)
NCURSES_SP_NAME(tgetnum)292 NCURSES_SP_NAME(tgetnum) (NCURSES_SP_DCLx const char *id)
293 {
294     int result = ABSENT_NUMERIC;
295 
296     T((T_CALLED("tgetnum(%p, %s)"), (void *) SP_PARM, id));
297     if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) {
298 	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
299 	struct name_table_entry const *entry_ptr;
300 	int j = -1;
301 
302 	entry_ptr = _nc_find_type_entry(id, NUMBER, TRUE);
303 	if (entry_ptr != 0) {
304 	    j = entry_ptr->nte_index;
305 	}
306 #if NCURSES_XNAMES
307 	else {
308 	    int i;
309 	    for_each_ext_number(i, tp) {
310 		const char *capname = ExtNumname(tp, i, numcodes);
311 		if (same_tcname(id, capname) && ValidExt(capname)) {
312 		    j = i;
313 		    break;
314 		}
315 	    }
316 	}
317 #endif
318 	if (j >= 0) {
319 	    if (VALID_NUMERIC(tp->Numbers[j]))
320 		result = tp->Numbers[j];
321 	}
322     }
323     returnCode(result);
324 }
325 
326 #if NCURSES_SP_FUNCS
327 NCURSES_EXPORT(int)
tgetnum(const char * id)328 tgetnum(const char *id)
329 {
330     return NCURSES_SP_NAME(tgetnum) (CURRENT_SCREEN, id);
331 }
332 #endif
333 
334 /***************************************************************************
335  *
336  * tgetstr(str, area)
337  *
338  * Look up string termcap capability str and return a pointer to its value,
339  * or NULL if not given.
340  *
341  ***************************************************************************/
342 
343 NCURSES_EXPORT(char *)
NCURSES_SP_NAME(tgetstr)344 NCURSES_SP_NAME(tgetstr) (NCURSES_SP_DCLx const char *id, char **area)
345 {
346     char *result = NULL;
347 
348     T((T_CALLED("tgetstr(%s,%p)"), id, (void *) area));
349     if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) {
350 	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
351 	struct name_table_entry const *entry_ptr;
352 	int j = -1;
353 
354 	entry_ptr = _nc_find_type_entry(id, STRING, TRUE);
355 	if (entry_ptr != 0) {
356 	    j = entry_ptr->nte_index;
357 	}
358 #if NCURSES_XNAMES
359 	else {
360 	    int i;
361 	    for_each_ext_string(i, tp) {
362 		const char *capname = ExtStrname(tp, i, strcodes);
363 		if (same_tcname(id, capname) && ValidExt(capname)) {
364 		    j = i;
365 		    break;
366 		}
367 	    }
368 	}
369 #endif
370 	if (j >= 0) {
371 	    result = tp->Strings[j];
372 	    TR(TRACE_DATABASE, ("found match %d: %s", j, _nc_visbuf(result)));
373 	    /* setupterm forces canceled strings to null */
374 	    if (VALID_STRING(result)) {
375 		if (result == exit_attribute_mode
376 		    && FIX_SGR0 != 0) {
377 		    result = FIX_SGR0;
378 		    TR(TRACE_DATABASE, ("altered to : %s", _nc_visbuf(result)));
379 		}
380 		if (area != 0
381 		    && *area != 0) {
382 		    _nc_STRCPY(*area, result, 1024);
383 		    result = *area;
384 		    *area += strlen(*area) + 1;
385 		}
386 	    }
387 	}
388     }
389     returnPtr(result);
390 }
391 
392 #if NCURSES_SP_FUNCS
393 NCURSES_EXPORT(char *)
tgetstr(const char * id,char ** area)394 tgetstr(const char *id, char **area)
395 {
396     return NCURSES_SP_NAME(tgetstr) (CURRENT_SCREEN, id, area);
397 }
398 #endif
399 
400 #if NO_LEAKS
401 #undef CacheInx
402 #define CacheInx num
403 NCURSES_EXPORT(void)
_nc_tgetent_leak(const TERMINAL * const termp)404 _nc_tgetent_leak(const TERMINAL *const termp)
405 {
406     if (termp != 0) {
407 	int num;
408 	for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) {
409 	    if (LAST_TRM == termp) {
410 		FreeAndNull(FIX_SGR0);
411 		if (LAST_TRM != 0) {
412 		    LAST_TRM = 0;
413 		}
414 		break;
415 	    }
416 	}
417     }
418 }
419 
420 NCURSES_EXPORT(void)
_nc_tgetent_leaks(void)421 _nc_tgetent_leaks(void)
422 {
423     int num;
424     for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) {
425 	if (LAST_TRM != 0) {
426 	    del_curterm(LAST_TRM);
427 	    _nc_tgetent_leak(LAST_TRM);
428 	}
429     }
430 }
431 #endif
432