1/**************************************************************************** 2 * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29/**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 * and: Thomas E. Dickey 1996-on * 33 ****************************************************************************/ 34 35/* 36 * read_entry.c -- Routine for reading in a compiled terminfo file 37 */ 38 39#include <curses.priv.h> 40#include <hashed_db.h> 41 42#include <tic.h> 43 44MODULE_ID("$Id: read_entry.c,v 1.126 2013/12/15 00:35:36 tom Exp $") 45 46#define TYPE_CALLOC(type,elts) typeCalloc(type, (unsigned)(elts)) 47 48#if NCURSES_USE_DATABASE 49static void 50convert_shorts(char *buf, short *Numbers, int count) 51{ 52 int i; 53 for (i = 0; i < count; i++) { 54 if (IS_NEG1(buf + 2 * i)) 55 Numbers[i] = ABSENT_NUMERIC; 56 else if (IS_NEG2(buf + 2 * i)) 57 Numbers[i] = CANCELLED_NUMERIC; 58 else 59 Numbers[i] = (short) LOW_MSB(buf + 2 * i); 60 TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i])); 61 } 62} 63 64static void 65convert_strings(char *buf, char **Strings, int count, int size, char *table) 66{ 67 int i; 68 char *p; 69 70 for (i = 0; i < count; i++) { 71 if (IS_NEG1(buf + 2 * i)) { 72 Strings[i] = ABSENT_STRING; 73 } else if (IS_NEG2(buf + 2 * i)) { 74 Strings[i] = CANCELLED_STRING; 75 } else if ((int) LOW_MSB(buf + 2 * i) > size) { 76 Strings[i] = ABSENT_STRING; 77 } else { 78 Strings[i] = (LOW_MSB(buf + 2 * i) + table); 79 TR(TRACE_DATABASE, ("Strings[%d] = %s", i, _nc_visbuf(Strings[i]))); 80 } 81 82 /* make sure all strings are NUL terminated */ 83 if (VALID_STRING(Strings[i])) { 84 for (p = Strings[i]; p <= table + size; p++) 85 if (*p == '\0') 86 break; 87 /* if there is no NUL, ignore the string */ 88 if (p > table + size) 89 Strings[i] = ABSENT_STRING; 90 } 91 } 92} 93 94static int 95fake_read(char *src, int *offset, int limit, char *dst, unsigned want) 96{ 97 int have = (limit - *offset); 98 99 if (have > 0) { 100 if ((int) want > have) 101 want = (unsigned) have; 102 memcpy(dst, src + *offset, (size_t) want); 103 *offset += (int) want; 104 } else { 105 want = 0; 106 } 107 return (int) want; 108} 109 110#define Read(buf, count) fake_read(buffer, &offset, limit, (char *) buf, (unsigned) count) 111 112#define read_shorts(buf, count) \ 113 (Read(buf, (count)*2) == (int) (count)*2) 114 115#define even_boundary(value) \ 116 if ((value) % 2 != 0) Read(buf, 1) 117#endif 118 119NCURSES_EXPORT(void) 120_nc_init_termtype(TERMTYPE *const tp) 121{ 122 unsigned i; 123 124#if NCURSES_XNAMES 125 tp->num_Booleans = BOOLCOUNT; 126 tp->num_Numbers = NUMCOUNT; 127 tp->num_Strings = STRCOUNT; 128 tp->ext_Booleans = 0; 129 tp->ext_Numbers = 0; 130 tp->ext_Strings = 0; 131#endif 132 if (tp->Booleans == 0) 133 TYPE_MALLOC(NCURSES_SBOOL, BOOLCOUNT, tp->Booleans); 134 if (tp->Numbers == 0) 135 TYPE_MALLOC(short, NUMCOUNT, tp->Numbers); 136 if (tp->Strings == 0) 137 TYPE_MALLOC(char *, STRCOUNT, tp->Strings); 138 139 for_each_boolean(i, tp) 140 tp->Booleans[i] = FALSE; 141 142 for_each_number(i, tp) 143 tp->Numbers[i] = ABSENT_NUMERIC; 144 145 for_each_string(i, tp) 146 tp->Strings[i] = ABSENT_STRING; 147} 148 149#if NCURSES_USE_DATABASE 150/* 151 * Return TGETENT_YES if read, TGETENT_NO if not found or garbled. 152 */ 153NCURSES_EXPORT(int) 154_nc_read_termtype(TERMTYPE *ptr, char *buffer, int limit) 155{ 156 int offset = 0; 157 int name_size, bool_count, num_count, str_count, str_size; 158 int i; 159 char buf[MAX_ENTRY_SIZE + 2]; 160 char *string_table; 161 unsigned want, have; 162 163 TR(TRACE_DATABASE, ("READ termtype header @%d", offset)); 164 165 memset(ptr, 0, sizeof(*ptr)); 166 167 /* grab the header */ 168 if (!read_shorts(buf, 6) 169 || !IS_TIC_MAGIC(buf)) { 170 return (TGETENT_NO); 171 } 172 173 name_size = LOW_MSB(buf + 2); 174 bool_count = LOW_MSB(buf + 4); 175 num_count = LOW_MSB(buf + 6); 176 str_count = LOW_MSB(buf + 8); 177 str_size = LOW_MSB(buf + 10); 178 179 TR(TRACE_DATABASE, 180 ("TERMTYPE name_size=%d, bool=%d/%d, num=%d/%d str=%d/%d(%d)", 181 name_size, bool_count, BOOLCOUNT, num_count, NUMCOUNT, 182 str_count, STRCOUNT, str_size)); 183 if (name_size < 0 184 || bool_count < 0 185 || num_count < 0 186 || str_count < 0 187 || str_size < 0) { 188 return (TGETENT_NO); 189 } 190 191 want = (unsigned) (str_size + name_size + 1); 192 if (str_size) { 193 /* try to allocate space for the string table */ 194 if (str_count * 2 >= MAX_ENTRY_SIZE 195 || (string_table = typeMalloc(char, want)) == 0) { 196 return (TGETENT_NO); 197 } 198 } else { 199 str_count = 0; 200 if ((string_table = typeMalloc(char, want)) == 0) { 201 return (TGETENT_NO); 202 } 203 } 204 205 /* grab the name (a null-terminated string) */ 206 want = min(MAX_NAME_SIZE, (unsigned) name_size); 207 ptr->str_table = string_table; 208 ptr->term_names = string_table; 209 if ((have = (unsigned) Read(ptr->term_names, want)) != want) { 210 memset(ptr->term_names + have, 0, (size_t) (want - have)); 211 } 212 ptr->term_names[want] = '\0'; 213 string_table += (want + 1); 214 215 if (have > MAX_NAME_SIZE) 216 offset = (int) (have - MAX_NAME_SIZE); 217 218 /* grab the booleans */ 219 if ((ptr->Booleans = TYPE_CALLOC(NCURSES_SBOOL, 220 max(BOOLCOUNT, bool_count))) == 0 221 || Read(ptr->Booleans, (unsigned) bool_count) < bool_count) { 222 return (TGETENT_NO); 223 } 224 225 /* 226 * If booleans end on an odd byte, skip it. The machine they 227 * originally wrote terminfo on must have been a 16-bit 228 * word-oriented machine that would trap out if you tried a 229 * word access off a 2-byte boundary. 230 */ 231 even_boundary(name_size + bool_count); 232 233 /* grab the numbers */ 234 if ((ptr->Numbers = TYPE_CALLOC(short, max(NUMCOUNT, num_count))) == 0 235 || !read_shorts(buf, num_count)) { 236 return (TGETENT_NO); 237 } 238 convert_shorts(buf, ptr->Numbers, num_count); 239 240 if ((ptr->Strings = TYPE_CALLOC(char *, max(STRCOUNT, str_count))) == 0) 241 return (TGETENT_NO); 242 243 if (str_count) { 244 /* grab the string offsets */ 245 if (!read_shorts(buf, str_count)) { 246 return (TGETENT_NO); 247 } 248 /* finally, grab the string table itself */ 249 if (Read(string_table, (unsigned) str_size) != str_size) 250 return (TGETENT_NO); 251 convert_strings(buf, ptr->Strings, str_count, str_size, string_table); 252 } 253#if NCURSES_XNAMES 254 255 ptr->num_Booleans = BOOLCOUNT; 256 ptr->num_Numbers = NUMCOUNT; 257 ptr->num_Strings = STRCOUNT; 258 259 /* 260 * Read extended entries, if any, after the normal end of terminfo data. 261 */ 262 even_boundary(str_size); 263 TR(TRACE_DATABASE, ("READ extended_header @%d", offset)); 264 if (_nc_user_definable && read_shorts(buf, 5)) { 265 int ext_bool_count = LOW_MSB(buf + 0); 266 int ext_num_count = LOW_MSB(buf + 2); 267 int ext_str_count = LOW_MSB(buf + 4); 268 int ext_str_size = LOW_MSB(buf + 6); 269 int ext_str_limit = LOW_MSB(buf + 8); 270 unsigned need = (unsigned) (ext_bool_count + ext_num_count + ext_str_count); 271 int base = 0; 272 273 if (need >= (MAX_ENTRY_SIZE / 2) 274 || ext_str_size >= MAX_ENTRY_SIZE 275 || ext_str_limit >= MAX_ENTRY_SIZE 276 || ext_bool_count < 0 277 || ext_num_count < 0 278 || ext_str_count < 0 279 || ext_str_size < 0 280 || ext_str_limit < 0) 281 return (TGETENT_NO); 282 283 ptr->num_Booleans = UShort(BOOLCOUNT + ext_bool_count); 284 ptr->num_Numbers = UShort(NUMCOUNT + ext_num_count); 285 ptr->num_Strings = UShort(STRCOUNT + ext_str_count); 286 287 TYPE_REALLOC(NCURSES_SBOOL, ptr->num_Booleans, ptr->Booleans); 288 TYPE_REALLOC(short, ptr->num_Numbers, ptr->Numbers); 289 TYPE_REALLOC(char *, ptr->num_Strings, ptr->Strings); 290 291 TR(TRACE_DATABASE, ("extended header is %d/%d/%d(%d:%d)", 292 ext_bool_count, ext_num_count, ext_str_count, 293 ext_str_size, ext_str_limit)); 294 295 TR(TRACE_DATABASE, ("READ %d extended-booleans @%d", 296 ext_bool_count, offset)); 297 if ((ptr->ext_Booleans = UShort(ext_bool_count)) != 0) { 298 if (Read(ptr->Booleans + BOOLCOUNT, (unsigned) 299 ext_bool_count) != ext_bool_count) 300 return (TGETENT_NO); 301 } 302 even_boundary(ext_bool_count); 303 304 TR(TRACE_DATABASE, ("READ %d extended-numbers @%d", 305 ext_num_count, offset)); 306 if ((ptr->ext_Numbers = UShort(ext_num_count)) != 0) { 307 if (!read_shorts(buf, ext_num_count)) 308 return (TGETENT_NO); 309 TR(TRACE_DATABASE, ("Before converting extended-numbers")); 310 convert_shorts(buf, ptr->Numbers + NUMCOUNT, ext_num_count); 311 } 312 313 TR(TRACE_DATABASE, ("READ extended-offsets @%d", offset)); 314 if ((unsigned) (ext_str_count + (int) need) >= (MAX_ENTRY_SIZE / 2)) 315 return (TGETENT_NO); 316 if ((ext_str_count || need) 317 && !read_shorts(buf, ext_str_count + (int) need)) 318 return (TGETENT_NO); 319 320 TR(TRACE_DATABASE, ("READ %d bytes of extended-strings @%d", 321 ext_str_limit, offset)); 322 323 if (ext_str_limit) { 324 ptr->ext_str_table = typeMalloc(char, (size_t) ext_str_limit); 325 if (ptr->ext_str_table == 0) 326 return (TGETENT_NO); 327 if (Read(ptr->ext_str_table, (unsigned) ext_str_limit) != ext_str_limit) 328 return (TGETENT_NO); 329 TR(TRACE_DATABASE, ("first extended-string is %s", _nc_visbuf(ptr->ext_str_table))); 330 } 331 332 if ((ptr->ext_Strings = UShort(ext_str_count)) != 0) { 333 TR(TRACE_DATABASE, 334 ("Before computing extended-string capabilities str_count=%d, ext_str_count=%d", 335 str_count, ext_str_count)); 336 convert_strings(buf, ptr->Strings + str_count, ext_str_count, 337 ext_str_limit, ptr->ext_str_table); 338 for (i = ext_str_count - 1; i >= 0; i--) { 339 TR(TRACE_DATABASE, ("MOVE from [%d:%d] %s", 340 i, i + str_count, 341 _nc_visbuf(ptr->Strings[i + str_count]))); 342 ptr->Strings[i + STRCOUNT] = ptr->Strings[i + str_count]; 343 if (VALID_STRING(ptr->Strings[i + STRCOUNT])) 344 base += (int) (strlen(ptr->Strings[i + STRCOUNT]) + 1); 345 TR(TRACE_DATABASE, ("... to [%d] %s", 346 i + STRCOUNT, 347 _nc_visbuf(ptr->Strings[i + STRCOUNT]))); 348 } 349 } 350 351 if (need) { 352 if (ext_str_count >= (MAX_ENTRY_SIZE / 2)) 353 return (TGETENT_NO); 354 if ((ptr->ext_Names = TYPE_CALLOC(char *, need)) == 0) 355 return (TGETENT_NO); 356 TR(TRACE_DATABASE, 357 ("ext_NAMES starting @%d in extended_strings, first = %s", 358 base, _nc_visbuf(ptr->ext_str_table + base))); 359 convert_strings(buf + (2 * ext_str_count), 360 ptr->ext_Names, 361 (int) need, 362 ext_str_limit, ptr->ext_str_table + base); 363 } 364 365 TR(TRACE_DATABASE, 366 ("...done reading terminfo bool %d(%d) num %d(%d) str %d(%d)", 367 ptr->num_Booleans, ptr->ext_Booleans, 368 ptr->num_Numbers, ptr->ext_Numbers, 369 ptr->num_Strings, ptr->ext_Strings)); 370 371 TR(TRACE_DATABASE, ("extend: num_Booleans:%d", ptr->num_Booleans)); 372 } else 373#endif /* NCURSES_XNAMES */ 374 { 375 TR(TRACE_DATABASE, ("...done reading terminfo bool %d num %d str %d", 376 bool_count, num_count, str_count)); 377#if NCURSES_XNAMES 378 TR(TRACE_DATABASE, ("normal: num_Booleans:%d", ptr->num_Booleans)); 379#endif 380 } 381 382 for (i = bool_count; i < BOOLCOUNT; i++) 383 ptr->Booleans[i] = FALSE; 384 for (i = num_count; i < NUMCOUNT; i++) 385 ptr->Numbers[i] = ABSENT_NUMERIC; 386 for (i = str_count; i < STRCOUNT; i++) 387 ptr->Strings[i] = ABSENT_STRING; 388 389 return (TGETENT_YES); 390} 391 392/* 393 * int 394 * _nc_read_file_entry(filename, ptr) 395 * 396 * Read the compiled terminfo entry in the given file into the 397 * structure pointed to by ptr, allocating space for the string 398 * table. 399 */ 400NCURSES_EXPORT(int) 401_nc_read_file_entry(const char *const filename, TERMTYPE *ptr) 402/* return 1 if read, 0 if not found or garbled */ 403{ 404 FILE *fp = 0; 405 int code; 406 int limit; 407 char buffer[MAX_ENTRY_SIZE + 1]; 408 409 if (_nc_access(filename, R_OK) < 0 410 || (fp = fopen(filename, "rb")) == 0) { 411 TR(TRACE_DATABASE, ("cannot open terminfo %s (errno=%d)", filename, errno)); 412 code = TGETENT_NO; 413 } else { 414 if ((limit = (int) fread(buffer, sizeof(char), sizeof(buffer), fp)) 415 > 0) { 416 417 TR(TRACE_DATABASE, ("read terminfo %s", filename)); 418 if ((code = _nc_read_termtype(ptr, buffer, limit)) == TGETENT_NO) { 419 _nc_free_termtype(ptr); 420 } 421 } else { 422 code = TGETENT_NO; 423 } 424 fclose(fp); 425 } 426 427 return (code); 428} 429 430#if USE_HASHED_DB 431/* 432 * Return if if we can build the filename of a ".db" file. 433 */ 434static bool 435make_db_filename(char *filename, unsigned limit, const char *const path) 436{ 437 static const char suffix[] = DBM_SUFFIX; 438 439 size_t lens = sizeof(suffix) - 1; 440 size_t size = strlen(path); 441 size_t test = lens + size; 442 bool result = FALSE; 443 444 if (test < limit) { 445 if (size >= lens 446 && !strcmp(path + size - lens, suffix)) 447 _nc_STRCPY(filename, path, limit); 448 else 449 _nc_SPRINTF(filename, _nc_SLIMIT(limit) "%s%s", path, suffix); 450 result = TRUE; 451 } 452 return result; 453} 454#endif 455 456/* 457 * Return true if we can build the name of a filesystem entry. 458 */ 459static bool 460make_dir_filename(char *filename, 461 unsigned limit, 462 const char *const path, 463 const char *name) 464{ 465 bool result = FALSE; 466 467#if NCURSES_USE_TERMCAP 468 if (_nc_is_dir_path(path)) 469#endif 470 { 471 unsigned need = (unsigned) (LEAF_LEN + 3 + strlen(path) + strlen(name)); 472 473 if (need <= limit) { 474 _nc_SPRINTF(filename, _nc_SLIMIT(limit) 475 "%s/" LEAF_FMT "/%s", path, *name, name); 476 result = TRUE; 477 } 478 } 479 return result; 480} 481 482/* 483 * Build a terminfo pathname and try to read the data. Returns TGETENT_YES on 484 * success, TGETENT_NO on failure. 485 */ 486static int 487_nc_read_tic_entry(char *filename, 488 unsigned limit, 489 const char *const path, 490 const char *name, 491 TERMTYPE *const tp) 492{ 493 int code = TGETENT_NO; 494 495#if USE_HASHED_DB 496 DB *capdbp; 497 498 if (make_db_filename(filename, limit, path) 499 && (capdbp = _nc_db_open(filename, FALSE)) != 0) { 500 501 DBT key, data; 502 int reccnt = 0; 503 char *save = strdup(name); 504 505 memset(&key, 0, sizeof(key)); 506 key.data = save; 507 key.size = strlen(save); 508 509 /* 510 * This lookup could return termcap data, which we do not want. We are 511 * looking for compiled (binary) terminfo data. 512 * 513 * cgetent uses a two-level lookup. On the first it uses the given 514 * name to return a record containing only the aliases for an entry. 515 * On the second (using that list of aliases as a key), it returns the 516 * content of the terminal description. We expect second lookup to 517 * return data beginning with the same set of aliases. 518 * 519 * For compiled terminfo, the list of aliases in the second case will 520 * be null-terminated. A termcap entry will not be, and will run on 521 * into the description. So we can easily distinguish between the two 522 * (source/binary) by checking the lengths. 523 */ 524 while (_nc_db_get(capdbp, &key, &data) == 0) { 525 int used = (int) data.size - 1; 526 char *have = (char *) data.data; 527 528 if (*have++ == 0) { 529 if (data.size > key.size 530 && IS_TIC_MAGIC(have)) { 531 code = _nc_read_termtype(tp, have, used); 532 if (code == TGETENT_NO) { 533 _nc_free_termtype(tp); 534 } 535 } 536 break; 537 } 538 539 /* 540 * Just in case we have a corrupt database, do not waste time with 541 * it. 542 */ 543 if (++reccnt >= 3) 544 break; 545 546 /* 547 * Prepare for the second level. 548 */ 549 key.data = have; 550 key.size = used; 551 } 552 553 free(save); 554 } else /* may be either filesystem or flat file */ 555#endif 556 if (make_dir_filename(filename, limit, path, name)) { 557 code = _nc_read_file_entry(filename, tp); 558 } 559#if NCURSES_USE_TERMCAP 560 else if (code != TGETENT_YES) { 561 code = _nc_read_termcap_entry(name, tp); 562 _nc_SPRINTF(filename, _nc_SLIMIT(PATH_MAX) 563 "%.*s", PATH_MAX - 1, _nc_get_source()); 564 } 565#endif 566 return code; 567} 568#endif /* NCURSES_USE_DATABASE */ 569 570/* 571 * _nc_read_entry(char *name, char *filename, TERMTYPE *tp) 572 * 573 * Find and read the compiled entry for a given terminal type, 574 * if it exists. We take pains here to make sure no combination 575 * of environment variables and terminal type name can be used to 576 * overrun the file buffer. 577 */ 578 579NCURSES_EXPORT(int) 580_nc_read_entry(const char *const name, char *const filename, TERMTYPE *const tp) 581{ 582 int code = TGETENT_NO; 583 584 _nc_SPRINTF(filename, _nc_SLIMIT(PATH_MAX) 585 "%.*s", PATH_MAX - 1, name); 586 587 if (strlen(name) == 0 588 || strcmp(name, ".") == 0 589 || strcmp(name, "..") == 0 590 || _nc_pathlast(name) != 0 591 || strchr(name, NCURSES_PATHSEP) != 0) { 592 TR(TRACE_DATABASE, ("illegal or missing entry name '%s'", name)); 593 } else { 594#if NCURSES_USE_DATABASE 595 DBDIRS state; 596 int offset; 597 const char *path; 598 599 _nc_first_db(&state, &offset); 600 while ((path = _nc_next_db(&state, &offset)) != 0) { 601 TR(TRACE_DATABASE, ("_nc_read_tic_entry path=%s, name=%s", path, name)); 602 code = _nc_read_tic_entry(filename, PATH_MAX, path, name, tp); 603 if (code == TGETENT_YES) { 604 _nc_last_db(); 605 break; 606 } 607 } 608#elif NCURSES_USE_TERMCAP 609 if (code != TGETENT_YES) { 610 code = _nc_read_termcap_entry(name, tp); 611 _nc_SPRINTF(filename, _nc_SLIMIT(PATH_MAX) 612 "%.*s", PATH_MAX - 1, _nc_get_source()); 613 } 614#endif 615 } 616 return code; 617} 618