1115140Strhodes/**************************************************************************** 2115140Strhodes * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 3115140Strhodes * * 4115140Strhodes * Permission is hereby granted, free of charge, to any person obtaining a * 5115140Strhodes * copy of this software and associated documentation files (the * 6115140Strhodes * "Software"), to deal in the Software without restriction, including * 7115140Strhodes * without limitation the rights to use, copy, modify, merge, publish, * 8115140Strhodes * distribute, distribute with modifications, sublicense, and/or sell * 9115140Strhodes * copies of the Software, and to permit persons to whom the Software is * 10115140Strhodes * furnished to do so, subject to the following conditions: * 11115140Strhodes * * 12115140Strhodes * The above copyright notice and this permission notice shall be included * 13115140Strhodes * in all copies or substantial portions of the Software. * 14115140Strhodes * * 15115140Strhodes * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16115140Strhodes * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17115140Strhodes * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18115140Strhodes * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19115140Strhodes * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20115140Strhodes * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21115140Strhodes * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22115140Strhodes * * 23115140Strhodes * Except as contained in this notice, the name(s) of the above copyright * 24115140Strhodes * holders shall not be used in advertising or otherwise to promote the * 25115140Strhodes * sale, use or other dealings in this Software without prior written * 26115140Strhodes * authorization. * 27115140Strhodes ****************************************************************************/ 28115140Strhodes 29115140Strhodes/**************************************************************************** 30115140Strhodes * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31115140Strhodes * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32232507Seadler * and: Thomas E. Dickey 1996-on * 33115140Strhodes ****************************************************************************/ 34115140Strhodes 35115140Strhodes/* 36115140Strhodes * read_entry.c -- Routine for reading in a compiled terminfo file 37115140Strhodes */ 38115140Strhodes 39115140Strhodes#include <curses.priv.h> 40115140Strhodes#include <hashed_db.h> 41115140Strhodes 42115140Strhodes#include <tic.h> 43115140Strhodes#include <term_entry.h> 44115140Strhodes 45115140StrhodesMODULE_ID("$Id: read_entry.c,v 1.102 2008/08/03 19:33:04 tom Exp $") 46115140Strhodes 47115140Strhodes#define TYPE_CALLOC(type,elts) typeCalloc(type, (unsigned)(elts)) 48115140Strhodes 49115140Strhodes#if USE_DATABASE 50115140Strhodesstatic void 51115140Strhodesconvert_shorts(char *buf, short *Numbers, int count) 52115140Strhodes{ 53115140Strhodes int i; 54115140Strhodes for (i = 0; i < count; i++) { 55115140Strhodes if (IS_NEG1(buf + 2 * i)) 56115140Strhodes Numbers[i] = ABSENT_NUMERIC; 57115140Strhodes else if (IS_NEG2(buf + 2 * i)) 58115140Strhodes Numbers[i] = CANCELLED_NUMERIC; 59115140Strhodes else 60115140Strhodes Numbers[i] = LOW_MSB(buf + 2 * i); 61115140Strhodes TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i])); 62115140Strhodes } 63115140Strhodes} 64115140Strhodes 65115140Strhodesstatic void 66115140Strhodesconvert_strings(char *buf, char **Strings, int count, int size, char *table) 67115140Strhodes{ 68115140Strhodes int i; 69115140Strhodes char *p; 70115140Strhodes 71115140Strhodes for (i = 0; i < count; i++) { 72115140Strhodes if (IS_NEG1(buf + 2 * i)) { 73219004Shselasky Strings[i] = ABSENT_STRING; 74115140Strhodes } else if (IS_NEG2(buf + 2 * i)) { 75115140Strhodes Strings[i] = CANCELLED_STRING; 76115140Strhodes } else if ((int) LOW_MSB(buf + 2 * i) > size) { 77115140Strhodes Strings[i] = ABSENT_STRING; 78115140Strhodes } else { 79115140Strhodes Strings[i] = (LOW_MSB(buf + 2 * i) + table); 80115140Strhodes TR(TRACE_DATABASE, ("Strings[%d] = %s", i, _nc_visbuf(Strings[i]))); 81115140Strhodes } 82219004Shselasky 83232501Seadler /* make sure all strings are NUL terminated */ 84232501Seadler if (VALID_STRING(Strings[i])) { 85232501Seadler for (p = Strings[i]; p <= table + size; p++) 86232501Seadler if (*p == '\0') 87232501Seadler break; 88219004Shselasky /* if there is no NUL, ignore the string */ 89219004Shselasky if (p > table + size) 90219004Shselasky Strings[i] = ABSENT_STRING; 91219004Shselasky } 92219004Shselasky } 93219004Shselasky} 94219004Shselasky 95219004Shselaskystatic int 96219004Shselaskyfake_read(char *src, int *offset, int limit, char *dst, unsigned want) 97219004Shselasky{ 98219004Shselasky int have = (limit - *offset); 99219004Shselasky 100219004Shselasky if (have > 0) { 101219004Shselasky if ((int) want > have) 102 want = have; 103 memcpy(dst, src + *offset, want); 104 *offset += want; 105 } else { 106 want = 0; 107 } 108 return (int) want; 109} 110 111#define Read(buf, count) fake_read(buffer, &offset, limit, buf, count) 112 113#define read_shorts(buf, count) \ 114 (Read(buf, (unsigned) (count)*2) == (int) (count)*2) 115 116#define even_boundary(value) \ 117 if ((value) % 2 != 0) Read(buf, 1) 118 119NCURSES_EXPORT(int) 120_nc_read_termtype(TERMTYPE *ptr, char *buffer, int limit) 121/* return 1 if read, 0 if not found or garbled */ 122{ 123 int offset = 0; 124 int name_size, bool_count, num_count, str_count, str_size; 125 int i; 126 char buf[MAX_ENTRY_SIZE + 1]; 127 char *string_table; 128 unsigned want, have; 129 130 TR(TRACE_DATABASE, ("READ termtype header @%d", offset)); 131 132 memset(ptr, 0, sizeof(*ptr)); 133 134 /* grab the header */ 135 if (!read_shorts(buf, 6) 136 || !IS_TIC_MAGIC(buf)) { 137 return (TGETENT_NO); 138 } 139 140 name_size = LOW_MSB(buf + 2); 141 bool_count = LOW_MSB(buf + 4); 142 num_count = LOW_MSB(buf + 6); 143 str_count = LOW_MSB(buf + 8); 144 str_size = LOW_MSB(buf + 10); 145 146 TR(TRACE_DATABASE, 147 ("TERMTYPE name_size=%d, bool=%d/%d, num=%d/%d str=%d/%d(%d)", 148 name_size, bool_count, BOOLCOUNT, num_count, NUMCOUNT, 149 str_count, STRCOUNT, str_size)); 150 if (name_size < 0 151 || bool_count < 0 152 || num_count < 0 153 || str_count < 0 154 || str_size < 0) { 155 return (TGETENT_NO); 156 } 157 158 want = str_size + name_size + 1; 159 if (str_size) { 160 /* try to allocate space for the string table */ 161 if (str_count * 2 >= (int) sizeof(buf) 162 || (string_table = typeMalloc(char, want)) == 0) { 163 return (TGETENT_NO); 164 } 165 } else { 166 str_count = 0; 167 if ((string_table = typeMalloc(char, want)) == 0) { 168 return (TGETENT_NO); 169 } 170 } 171 172 /* grab the name (a null-terminated string) */ 173 want = min(MAX_NAME_SIZE, (unsigned) name_size); 174 ptr->str_table = string_table; 175 ptr->term_names = string_table; 176 if ((have = Read(ptr->term_names, want)) != want) { 177 memset(ptr->term_names + have, 0, want - have); 178 } 179 ptr->term_names[want] = '\0'; 180 string_table += (want + 1); 181 182 if (have > MAX_NAME_SIZE) 183 offset = (have - MAX_NAME_SIZE); 184 185 /* grab the booleans */ 186 if ((ptr->Booleans = TYPE_CALLOC(NCURSES_SBOOL, 187 max(BOOLCOUNT, bool_count))) == 0 188 || Read(ptr->Booleans, (unsigned) bool_count) < bool_count) { 189 return (TGETENT_NO); 190 } 191 192 /* 193 * If booleans end on an odd byte, skip it. The machine they 194 * originally wrote terminfo on must have been a 16-bit 195 * word-oriented machine that would trap out if you tried a 196 * word access off a 2-byte boundary. 197 */ 198 even_boundary(name_size + bool_count); 199 200 /* grab the numbers */ 201 if ((ptr->Numbers = TYPE_CALLOC(short, max(NUMCOUNT, num_count))) == 0 202 || !read_shorts(buf, num_count)) { 203 return (TGETENT_NO); 204 } 205 convert_shorts(buf, ptr->Numbers, num_count); 206 207 if ((ptr->Strings = TYPE_CALLOC(char *, max(STRCOUNT, str_count))) == 0) 208 return (TGETENT_NO); 209 210 if (str_count) { 211 /* grab the string offsets */ 212 if (!read_shorts(buf, str_count)) { 213 return (TGETENT_NO); 214 } 215 /* finally, grab the string table itself */ 216 if (Read(string_table, (unsigned) str_size) != str_size) 217 return (TGETENT_NO); 218 convert_strings(buf, ptr->Strings, str_count, str_size, string_table); 219 } 220#if NCURSES_XNAMES 221 222 ptr->num_Booleans = BOOLCOUNT; 223 ptr->num_Numbers = NUMCOUNT; 224 ptr->num_Strings = STRCOUNT; 225 226 /* 227 * Read extended entries, if any, after the normal end of terminfo data. 228 */ 229 even_boundary(str_size); 230 TR(TRACE_DATABASE, ("READ extended_header @%d", offset)); 231 if (_nc_user_definable && read_shorts(buf, 5)) { 232 int ext_bool_count = LOW_MSB(buf + 0); 233 int ext_num_count = LOW_MSB(buf + 2); 234 int ext_str_count = LOW_MSB(buf + 4); 235 int ext_str_size = LOW_MSB(buf + 6); 236 int ext_str_limit = LOW_MSB(buf + 8); 237 unsigned need = (ext_bool_count + ext_num_count + ext_str_count); 238 int base = 0; 239 240 if (need >= sizeof(buf) 241 || ext_str_size >= (int) sizeof(buf) 242 || ext_str_limit >= (int) sizeof(buf) 243 || ext_bool_count < 0 244 || ext_num_count < 0 245 || ext_str_count < 0 246 || ext_str_size < 0 247 || ext_str_limit < 0) 248 return (TGETENT_NO); 249 250 ptr->num_Booleans = BOOLCOUNT + ext_bool_count; 251 ptr->num_Numbers = NUMCOUNT + ext_num_count; 252 ptr->num_Strings = STRCOUNT + ext_str_count; 253 254 ptr->Booleans = typeRealloc(NCURSES_SBOOL, ptr->num_Booleans, ptr->Booleans); 255 ptr->Numbers = typeRealloc(short, ptr->num_Numbers, ptr->Numbers); 256 ptr->Strings = typeRealloc(char *, ptr->num_Strings, ptr->Strings); 257 258 TR(TRACE_DATABASE, ("extended header is %d/%d/%d(%d:%d)", 259 ext_bool_count, ext_num_count, ext_str_count, 260 ext_str_size, ext_str_limit)); 261 262 TR(TRACE_DATABASE, ("READ %d extended-booleans @%d", 263 ext_bool_count, offset)); 264 if ((ptr->ext_Booleans = ext_bool_count) != 0) { 265 if (Read(ptr->Booleans + BOOLCOUNT, (unsigned) 266 ext_bool_count) != ext_bool_count) 267 return (TGETENT_NO); 268 } 269 even_boundary(ext_bool_count); 270 271 TR(TRACE_DATABASE, ("READ %d extended-numbers @%d", 272 ext_num_count, offset)); 273 if ((ptr->ext_Numbers = ext_num_count) != 0) { 274 if (!read_shorts(buf, ext_num_count)) 275 return (TGETENT_NO); 276 TR(TRACE_DATABASE, ("Before converting extended-numbers")); 277 convert_shorts(buf, ptr->Numbers + NUMCOUNT, ext_num_count); 278 } 279 280 TR(TRACE_DATABASE, ("READ extended-offsets @%d", offset)); 281 if ((ext_str_count || need) 282 && !read_shorts(buf, ext_str_count + need)) 283 return (TGETENT_NO); 284 285 TR(TRACE_DATABASE, ("READ %d bytes of extended-strings @%d", 286 ext_str_limit, offset)); 287 288 if (ext_str_limit) { 289 if ((ptr->ext_str_table = typeMalloc(char, ext_str_limit)) == 0) 290 return (TGETENT_NO); 291 if (Read(ptr->ext_str_table, (unsigned) ext_str_limit) != ext_str_limit) 292 return (TGETENT_NO); 293 TR(TRACE_DATABASE, ("first extended-string is %s", _nc_visbuf(ptr->ext_str_table))); 294 } 295 296 if ((ptr->ext_Strings = ext_str_count) != 0) { 297 TR(TRACE_DATABASE, 298 ("Before computing extended-string capabilities str_count=%d, ext_str_count=%d", 299 str_count, ext_str_count)); 300 convert_strings(buf, ptr->Strings + str_count, ext_str_count, 301 ext_str_limit, ptr->ext_str_table); 302 for (i = ext_str_count - 1; i >= 0; i--) { 303 TR(TRACE_DATABASE, ("MOVE from [%d:%d] %s", 304 i, i + str_count, 305 _nc_visbuf(ptr->Strings[i + str_count]))); 306 ptr->Strings[i + STRCOUNT] = ptr->Strings[i + str_count]; 307 if (VALID_STRING(ptr->Strings[i + STRCOUNT])) 308 base += (strlen(ptr->Strings[i + STRCOUNT]) + 1); 309 TR(TRACE_DATABASE, ("... to [%d] %s", 310 i + STRCOUNT, 311 _nc_visbuf(ptr->Strings[i + STRCOUNT]))); 312 } 313 } 314 315 if (need) { 316 if (ext_str_count >= (MAX_ENTRY_SIZE * 2)) 317 return (TGETENT_NO); 318 if ((ptr->ext_Names = TYPE_CALLOC(char *, need)) == 0) 319 return (TGETENT_NO); 320 TR(TRACE_DATABASE, 321 ("ext_NAMES starting @%d in extended_strings, first = %s", 322 base, _nc_visbuf(ptr->ext_str_table + base))); 323 convert_strings(buf + (2 * ext_str_count), 324 ptr->ext_Names, 325 (int) need, 326 ext_str_limit, ptr->ext_str_table + base); 327 } 328 329 T(("...done reading terminfo bool %d(%d) num %d(%d) str %d(%d)", 330 ptr->num_Booleans, ptr->ext_Booleans, 331 ptr->num_Numbers, ptr->ext_Numbers, 332 ptr->num_Strings, ptr->ext_Strings)); 333 334 TR(TRACE_DATABASE, ("extend: num_Booleans:%d", ptr->num_Booleans)); 335 } else 336#endif /* NCURSES_XNAMES */ 337 { 338 T(("...done reading terminfo bool %d num %d str %d", 339 bool_count, num_count, str_count)); 340#if NCURSES_XNAMES 341 TR(TRACE_DATABASE, ("normal: num_Booleans:%d", ptr->num_Booleans)); 342#endif 343 } 344 345 for (i = bool_count; i < BOOLCOUNT; i++) 346 ptr->Booleans[i] = FALSE; 347 for (i = num_count; i < NUMCOUNT; i++) 348 ptr->Numbers[i] = ABSENT_NUMERIC; 349 for (i = str_count; i < STRCOUNT; i++) 350 ptr->Strings[i] = ABSENT_STRING; 351 352 return (TGETENT_YES); 353} 354 355/* 356 * int 357 * _nc_read_file_entry(filename, ptr) 358 * 359 * Read the compiled terminfo entry in the given file into the 360 * structure pointed to by ptr, allocating space for the string 361 * table. 362 */ 363NCURSES_EXPORT(int) 364_nc_read_file_entry(const char *const filename, TERMTYPE *ptr) 365/* return 1 if read, 0 if not found or garbled */ 366{ 367 int code, fd = -1; 368 int limit; 369 char buffer[MAX_ENTRY_SIZE + 1]; 370 371 if (_nc_access(filename, R_OK) < 0 372 || (fd = open(filename, O_RDONLY | O_BINARY)) < 0) { 373 T(("cannot open terminfo %s (errno=%d)", filename, errno)); 374 code = TGETENT_NO; 375 } else { 376 if ((limit = read(fd, buffer, sizeof(buffer))) > 0) { 377 378 T(("read terminfo %s", filename)); 379 if ((code = _nc_read_termtype(ptr, buffer, limit)) == TGETENT_NO) { 380 _nc_free_termtype(ptr); 381 } 382 } else { 383 code = TGETENT_NO; 384 } 385 close(fd); 386 } 387 388 return (code); 389} 390 391/* 392 * Build a terminfo pathname and try to read the data. Returns TGETENT_YES on 393 * success, TGETENT_NO on failure. 394 */ 395static int 396_nc_read_tic_entry(char *filename, 397 unsigned limit, 398 const char *const path, 399 const char *name, 400 TERMTYPE *const tp) 401{ 402 int result = TGETENT_NO; 403 404 /* 405 * If we are looking in a directory, assume the entry is a file under that, 406 * according to the normal rules. 407 * 408 * FIXME - add caseless-filename fixup. 409 */ 410 if (_nc_is_dir_path(path)) { 411 unsigned need = 4 + strlen(path) + strlen(name); 412 413 if (need <= limit) { 414 (void) sprintf(filename, "%s/" LEAF_FMT "/%s", path, *name, name); 415 result = _nc_read_file_entry(filename, tp); 416 } 417 } 418#if USE_HASHED_DB 419 else { 420 static const char suffix[] = DBM_SUFFIX; 421 DB *capdbp; 422 unsigned lens = sizeof(suffix) - 1; 423 unsigned size = strlen(path); 424 unsigned need = lens + size; 425 426 if (need <= limit) { 427 if (size >= lens 428 && !strcmp(path + size - lens, suffix)) 429 (void) strcpy(filename, path); 430 else 431 (void) sprintf(filename, "%s%s", path, suffix); 432 433 /* 434 * It would be nice to optimize the dbopen/close activity, as 435 * done in the cgetent implementation for tc= clauses. However, 436 * since we support multiple database locations, we cannot do 437 * that. 438 */ 439 if ((capdbp = _nc_db_open(filename, FALSE)) != 0) { 440 DBT key, data; 441 int reccnt = 0; 442 char *save = strdup(name); 443 444 memset(&key, 0, sizeof(key)); 445 key.data = save; 446 key.size = strlen(save); 447 448 /* 449 * This lookup could return termcap data, which we do not want. 450 * We are looking for compiled (binary) terminfo data. 451 * 452 * cgetent uses a two-level lookup. On the first it uses the 453 * given name to return a record containing only the aliases 454 * for an entry. On the second (using that list of aliases as 455 * a key), it returns the content of the terminal description. 456 * We expect second lookup to return data beginning with the 457 * same set of aliases. 458 * 459 * For compiled terminfo, the list of aliases in the second 460 * case will be null-terminated. A termcap entry will not be, 461 * and will run on into the description. So we can easily 462 * distinguish between the two (source/binary) by checking the 463 * lengths. 464 */ 465 while (_nc_db_get(capdbp, &key, &data) == 0) { 466 int used = data.size - 1; 467 char *have = (char *) data.data; 468 469 if (*have++ == 0) { 470 if (data.size > key.size 471 && IS_TIC_MAGIC(have)) { 472 result = _nc_read_termtype(tp, have, used); 473 if (result == TGETENT_NO) { 474 _nc_free_termtype(tp); 475 } 476 } 477 break; 478 } 479 480 /* 481 * Just in case we have a corrupt database, do not waste 482 * time with it. 483 */ 484 if (++reccnt >= 3) 485 break; 486 487 /* 488 * Prepare for the second level. 489 */ 490 key.data = have; 491 key.size = used; 492 } 493 494 _nc_db_close(capdbp); 495 free(save); 496 } 497 } 498 } 499#endif 500 return result; 501} 502#endif /* USE_DATABASE */ 503 504/* 505 * _nc_read_entry(char *name, char *filename, TERMTYPE *tp) 506 * 507 * Find and read the compiled entry for a given terminal type, 508 * if it exists. We take pains here to make sure no combination 509 * of environment variables and terminal type name can be used to 510 * overrun the file buffer. 511 */ 512 513NCURSES_EXPORT(int) 514_nc_read_entry(const char *const name, char *const filename, TERMTYPE *const tp) 515{ 516 int code = TGETENT_NO; 517 518 if (strlen(name) == 0 519 || strcmp(name, ".") == 0 520 || strcmp(name, "..") == 0 521 || _nc_pathlast(name) != 0 522 || strchr(name, NCURSES_PATHSEP) != 0) { 523 T(("illegal or missing entry name '%s'", name)); 524 } else { 525#if USE_DATABASE 526 DBDIRS state = dbdTIC; 527 int offset = 0; 528 const char *path; 529 530 while ((path = _nc_next_db(&state, &offset)) != 0) { 531 code = _nc_read_tic_entry(filename, PATH_MAX, path, name, tp); 532 if (code == TGETENT_YES) { 533 _nc_last_db(); 534 break; 535 } 536 } 537#endif 538#if USE_TERMCAP 539 if (code != TGETENT_YES) { 540 code = _nc_read_termcap_entry(name, tp); 541 sprintf(filename, "%.*s", PATH_MAX - 1, _nc_get_source()); 542 } 543#endif 544 } 545 return code; 546} 547