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