1 /**************************************************************************** 2 * Copyright 2018,2020 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 #define __INTERNAL_CAPS_VISIBLE 42 #include <curses.priv.h> 43 44 #include <termcap.h> 45 #include <tic.h> 46 #include <ctype.h> 47 48 #ifndef CUR 49 #define CUR SP_TERMTYPE 50 #endif 51 52 MODULE_ID("$Id: lib_termcap.c,v 1.88 2020/02/02 23:34:34 tom Exp $") 53 54 NCURSES_EXPORT_VAR(char *) UP = 0; 55 NCURSES_EXPORT_VAR(char *) BC = 0; 56 57 #define MyCache _nc_globals.tgetent_cache 58 #define CacheInx _nc_globals.tgetent_index 59 #define CacheSeq _nc_globals.tgetent_sequence 60 61 #define FIX_SGR0 MyCache[CacheInx].fix_sgr0 62 #define LAST_TRM MyCache[CacheInx].last_term 63 #define LAST_BUF MyCache[CacheInx].last_bufp 64 #define LAST_USE MyCache[CacheInx].last_used 65 #define LAST_SEQ MyCache[CacheInx].sequence 66 67 /* 68 * Termcap names are matched only using the first two bytes. 69 * Ignore any extended names longer than two bytes, to avoid problems 70 * with legacy code which passes in parameters whose use is long forgotten. 71 */ 72 #define ValidCap(cap) (((cap)[0] != '\0') && ((cap)[1] != '\0')) 73 #define SameCap(a,b) (((a)[0] == (b)[0]) && ((a)[1] == (b)[1])) 74 #define ValidExt(ext) (ValidCap(ext) && (ext)[2] == '\0') 75 76 /*************************************************************************** 77 * 78 * tgetent(bufp, term) 79 * 80 * In termcap, this function reads in the entry for terminal `term' into the 81 * buffer pointed to by bufp. It must be called before any of the functions 82 * below are called. 83 * In this terminfo emulation, tgetent() simply calls setupterm() (which 84 * does a bit more than tgetent() in termcap does), and returns its return 85 * value (1 if successful, 0 if no terminal with the given name could be 86 * found, or -1 if no terminal descriptions have been installed on the 87 * system). The bufp argument is ignored. 88 * 89 ***************************************************************************/ 90 91 NCURSES_EXPORT(int) 92 NCURSES_SP_NAME(tgetent) (NCURSES_SP_DCLx char *bufp, const char *name) 93 { 94 int rc = ERR; 95 int n; 96 bool found_cache = FALSE; 97 #ifdef USE_TERM_DRIVER 98 TERMINAL *termp = 0; 99 #endif 100 101 START_TRACE(); 102 T((T_CALLED("tgetent()"))); 103 104 TINFO_SETUP_TERM(&termp, name, STDOUT_FILENO, &rc, TRUE); 105 106 #ifdef USE_TERM_DRIVER 107 if (termp == 0 || 108 !((TERMINAL_CONTROL_BLOCK *) termp)->drv->isTerminfo) 109 returnCode(rc); 110 #endif 111 112 /* 113 * In general we cannot tell if the fixed sgr0 is still used by the 114 * caller, but if tgetent() is called with the same buffer, that is 115 * good enough, since the previous data would be invalidated by the 116 * current call. 117 * 118 * bufp may be a null pointer, e.g., GNU termcap. That allocates data, 119 * which is good until the next tgetent() call. The conventional termcap 120 * is inconvenient because of the fixed buffer size, but because it uses 121 * caller-supplied buffers, can have multiple terminal descriptions in 122 * use at a given time. 123 */ 124 for (n = 0; n < TGETENT_MAX; ++n) { 125 bool same_result = (MyCache[n].last_used && MyCache[n].last_bufp == bufp); 126 if (same_result) { 127 CacheInx = n; 128 if (FIX_SGR0 != 0) { 129 FreeAndNull(FIX_SGR0); 130 } 131 /* 132 * Also free the terminfo data that we loaded (much bigger leak). 133 */ 134 if (LAST_TRM != 0 && LAST_TRM != TerminalOf(SP_PARM)) { 135 TERMINAL *trm = LAST_TRM; 136 NCURSES_SP_NAME(del_curterm) (NCURSES_SP_ARGx LAST_TRM); 137 for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) 138 if (LAST_TRM == trm) 139 LAST_TRM = 0; 140 CacheInx = n; 141 } 142 found_cache = TRUE; 143 break; 144 } 145 } 146 if (!found_cache) { 147 int best = 0; 148 149 for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) { 150 if (LAST_SEQ < MyCache[best].sequence) { 151 best = CacheInx; 152 } 153 } 154 CacheInx = best; 155 } 156 if (rc == 1) { 157 LAST_TRM = TerminalOf(SP_PARM); 158 LAST_SEQ = ++CacheSeq; 159 } else { 160 LAST_TRM = 0; 161 } 162 163 PC = 0; 164 UP = 0; 165 BC = 0; 166 FIX_SGR0 = 0; /* don't free it - application may still use */ 167 168 if (rc == 1) { 169 170 if (cursor_left) 171 if ((backspaces_with_bs = (char) !strcmp(cursor_left, "\b")) == 0) 172 backspace_if_not_bs = cursor_left; 173 174 /* we're required to export these */ 175 if (pad_char != NULL) 176 PC = pad_char[0]; 177 if (cursor_up != NULL) 178 UP = cursor_up; 179 if (backspace_if_not_bs != NULL) 180 BC = backspace_if_not_bs; 181 182 if ((FIX_SGR0 = _nc_trim_sgr0(&TerminalType(TerminalOf(SP_PARM)))) 183 != 0) { 184 if (!strcmp(FIX_SGR0, exit_attribute_mode)) { 185 if (FIX_SGR0 != exit_attribute_mode) { 186 free(FIX_SGR0); 187 } 188 FIX_SGR0 = 0; 189 } 190 } 191 LAST_BUF = bufp; 192 LAST_USE = TRUE; 193 194 SetNoPadding(SP_PARM); 195 (void) NCURSES_SP_NAME(baudrate) (NCURSES_SP_ARG); /* sets ospeed as a side-effect */ 196 197 /* LINT_PREPRO 198 #if 0*/ 199 #include <capdefaults.c> 200 /* LINT_PREPRO 201 #endif*/ 202 203 } 204 returnCode(rc); 205 } 206 207 #if NCURSES_SP_FUNCS 208 NCURSES_EXPORT(int) 209 tgetent(char *bufp, const char *name) 210 { 211 return NCURSES_SP_NAME(tgetent) (CURRENT_SCREEN, bufp, name); 212 } 213 #endif 214 215 #if 0 216 static bool 217 same_tcname(const char *a, const char *b) 218 { 219 bool code = SameCap(a, b); 220 fprintf(stderr, "compare(%s,%s) %s\n", a, b, code ? "same" : "diff"); 221 return code; 222 } 223 224 #else 225 #define same_tcname(a,b) SameCap(a,b) 226 #endif 227 228 /*************************************************************************** 229 * 230 * tgetflag(str) 231 * 232 * Look up boolean termcap capability str and return its value (TRUE=1 if 233 * present, FALSE=0 if not). 234 * 235 ***************************************************************************/ 236 237 NCURSES_EXPORT(int) 238 NCURSES_SP_NAME(tgetflag) (NCURSES_SP_DCLx const char *id) 239 { 240 int result = 0; /* Solaris returns zero for missing flag */ 241 242 T((T_CALLED("tgetflag(%p, %s)"), (void *) SP_PARM, id)); 243 if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) { 244 TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM)); 245 struct name_table_entry const *entry_ptr; 246 int j = -1; 247 248 entry_ptr = _nc_find_type_entry(id, BOOLEAN, TRUE); 249 if (entry_ptr != 0) { 250 j = entry_ptr->nte_index; 251 } 252 #if NCURSES_XNAMES 253 else { 254 int i; 255 for_each_ext_boolean(i, tp) { 256 const char *capname = ExtBoolname(tp, i, boolcodes); 257 if (same_tcname(id, capname) && ValidExt(capname)) { 258 j = i; 259 break; 260 } 261 } 262 } 263 #endif 264 if (j >= 0) { 265 /* note: setupterm forces invalid booleans to false */ 266 result = tp->Booleans[j]; 267 } 268 } 269 returnCode(result); 270 } 271 272 #if NCURSES_SP_FUNCS 273 NCURSES_EXPORT(int) 274 tgetflag(const char *id) 275 { 276 return NCURSES_SP_NAME(tgetflag) (CURRENT_SCREEN, id); 277 } 278 #endif 279 280 /*************************************************************************** 281 * 282 * tgetnum(str) 283 * 284 * Look up numeric termcap capability str and return its value, or -1 if 285 * not given. 286 * 287 ***************************************************************************/ 288 289 NCURSES_EXPORT(int) 290 NCURSES_SP_NAME(tgetnum) (NCURSES_SP_DCLx const char *id) 291 { 292 int result = ABSENT_NUMERIC; 293 294 T((T_CALLED("tgetnum(%p, %s)"), (void *) SP_PARM, id)); 295 if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) { 296 TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM)); 297 struct name_table_entry const *entry_ptr; 298 int j = -1; 299 300 entry_ptr = _nc_find_type_entry(id, NUMBER, TRUE); 301 if (entry_ptr != 0) { 302 j = entry_ptr->nte_index; 303 } 304 #if NCURSES_XNAMES 305 else { 306 int i; 307 for_each_ext_number(i, tp) { 308 const char *capname = ExtNumname(tp, i, numcodes); 309 if (same_tcname(id, capname) && ValidExt(capname)) { 310 j = i; 311 break; 312 } 313 } 314 } 315 #endif 316 if (j >= 0) { 317 if (VALID_NUMERIC(tp->Numbers[j])) 318 result = tp->Numbers[j]; 319 } 320 } 321 returnCode(result); 322 } 323 324 #if NCURSES_SP_FUNCS 325 NCURSES_EXPORT(int) 326 tgetnum(const char *id) 327 { 328 return NCURSES_SP_NAME(tgetnum) (CURRENT_SCREEN, id); 329 } 330 #endif 331 332 /*************************************************************************** 333 * 334 * tgetstr(str, area) 335 * 336 * Look up string termcap capability str and return a pointer to its value, 337 * or NULL if not given. 338 * 339 ***************************************************************************/ 340 341 NCURSES_EXPORT(char *) 342 NCURSES_SP_NAME(tgetstr) (NCURSES_SP_DCLx const char *id, char **area) 343 { 344 char *result = NULL; 345 346 T((T_CALLED("tgetstr(%s,%p)"), id, (void *) area)); 347 if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) { 348 TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM)); 349 struct name_table_entry const *entry_ptr; 350 int j = -1; 351 352 entry_ptr = _nc_find_type_entry(id, STRING, TRUE); 353 if (entry_ptr != 0) { 354 j = entry_ptr->nte_index; 355 } 356 #if NCURSES_XNAMES 357 else { 358 int i; 359 for_each_ext_string(i, tp) { 360 const char *capname = ExtStrname(tp, i, strcodes); 361 if (same_tcname(id, capname) && ValidExt(capname)) { 362 j = i; 363 break; 364 } 365 } 366 } 367 #endif 368 if (j >= 0) { 369 result = tp->Strings[j]; 370 TR(TRACE_DATABASE, ("found match %d: %s", j, _nc_visbuf(result))); 371 /* setupterm forces canceled strings to null */ 372 if (VALID_STRING(result)) { 373 if (result == exit_attribute_mode 374 && FIX_SGR0 != 0) { 375 result = FIX_SGR0; 376 TR(TRACE_DATABASE, ("altered to : %s", _nc_visbuf(result))); 377 } 378 if (area != 0 379 && *area != 0) { 380 _nc_STRCPY(*area, result, 1024); 381 result = *area; 382 *area += strlen(*area) + 1; 383 } 384 } 385 } 386 } 387 returnPtr(result); 388 } 389 390 #if NCURSES_SP_FUNCS 391 NCURSES_EXPORT(char *) 392 tgetstr(const char *id, char **area) 393 { 394 return NCURSES_SP_NAME(tgetstr) (CURRENT_SCREEN, id, area); 395 } 396 #endif 397 398 #if NO_LEAKS 399 #undef CacheInx 400 #define CacheInx num 401 NCURSES_EXPORT(void) 402 _nc_tgetent_leak(TERMINAL *termp) 403 { 404 if (termp != 0) { 405 int num; 406 for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) { 407 if (LAST_TRM == termp) { 408 FreeAndNull(FIX_SGR0); 409 if (LAST_TRM != 0) { 410 LAST_TRM = 0; 411 } 412 break; 413 } 414 } 415 } 416 } 417 418 NCURSES_EXPORT(void) 419 _nc_tgetent_leaks(void) 420 { 421 int num; 422 for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) { 423 if (LAST_TRM != 0) { 424 del_curterm(LAST_TRM); 425 _nc_tgetent_leak(LAST_TRM); 426 } 427 } 428 } 429 #endif 430