read_entry.c revision 1.3
1/* $OpenBSD: read_entry.c,v 1.3 1999/01/23 18:31:02 millert Exp $ */ 2 3/**************************************************************************** 4 * Copyright (c) 1998 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31/**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 ****************************************************************************/ 35 36 37 38/* 39 * read_entry.c -- Routine for reading in a compiled terminfo file 40 * 41 */ 42 43#include <curses.priv.h> 44 45#if HAVE_FCNTL_H 46#include <fcntl.h> 47#endif 48 49#include <term.h> 50#include <tic.h> 51#include <term_entry.h> 52 53MODULE_ID("$From: read_entry.c,v 1.47 1998/12/20 02:51:50 tom Exp $") 54 55#ifndef O_BINARY 56#define O_BINARY 0 57#endif 58 59/* 60 * int 61 * _nc_read_file_entry(filename, ptr) 62 * 63 * Read the compiled terminfo entry in the given file into the 64 * structure pointed to by ptr, allocating space for the string 65 * table. 66 */ 67 68#undef BYTE 69#define BYTE(p,n) (unsigned char)((p)[n]) 70 71#define IS_NEG1(p) ((BYTE(p,0) == 0377) && (BYTE(p,1) == 0377)) 72#define IS_NEG2(p) ((BYTE(p,0) == 0376) && (BYTE(p,1) == 0377)) 73#define LOW_MSB(p) (BYTE(p,0) + 256*BYTE(p,1)) 74 75static bool have_tic_directory = FALSE; 76static bool keep_tic_directory = FALSE; 77 78/* 79 * Record the "official" location of the terminfo directory, according to 80 * the place where we're writing to, or the normal default, if not. 81 */ 82const char *_nc_tic_dir(const char *path) 83{ 84 static const char *result = TERMINFO; 85 86 if (!keep_tic_directory) { 87 if (path != 0) { 88 result = path; 89 have_tic_directory = TRUE; 90 } else if (!have_tic_directory) { 91 char *envp; 92 if (!issetugid() && (envp = getenv("TERMINFO")) != 0) 93 return _nc_tic_dir(envp); 94 } 95 } 96 return result; 97} 98 99/* 100 * Special fix to prevent the terminfo directory from being moved after tic 101 * has chdir'd to it. If we let it be changed, then if $TERMINFO has a 102 * relative path, we'll lose track of the actual directory. 103 */ 104void _nc_keep_tic_dir(const char *path) 105{ 106 _nc_tic_dir(path); 107 keep_tic_directory = TRUE; 108} 109 110int _nc_read_file_entry(const char *const filename, TERMTYPE *ptr) 111/* return 1 if read, 0 if not found or garbled */ 112{ 113 int name_size, bool_count, num_count, str_count, str_size; 114 int i, fd, numread; 115 char buf[MAX_ENTRY_SIZE]; 116 117#ifdef __OpenBSD__ 118 if (_nc_read_bsd_terminfo_file(filename, ptr) == 1) 119 return(1); 120#endif /* __OpenBSD__ */ 121 122 if (_nc_access(filename, R_OK) < 0 123 || (fd = open(filename, O_RDONLY|O_BINARY)) < 0) { 124 T(("cannot open terminfo %s (errno=%d)", filename, errno)); 125 return(0); 126 } 127 128 T(("read terminfo %s", filename)); 129 130 /* grab the header */ 131 (void) read(fd, buf, 12); 132 if (LOW_MSB(buf) != MAGIC) 133 { 134 close(fd); 135 return(0); 136 } 137 name_size = LOW_MSB(buf + 2); 138 bool_count = LOW_MSB(buf + 4); 139 num_count = LOW_MSB(buf + 6); 140 str_count = LOW_MSB(buf + 8); 141 str_size = LOW_MSB(buf + 10); 142 143 if (str_size) 144 { 145 /* try to allocate space for the string table */ 146 ptr->str_table = malloc((unsigned)str_size); 147 if (ptr->str_table == 0) 148 { 149 close(fd); 150 return(0); 151 } 152 } 153 else 154 str_count = 0; 155 156 /* grab the name */ 157 read(fd, buf, min(MAX_NAME_SIZE, (unsigned)name_size)); 158 buf[MAX_NAME_SIZE] = '\0'; 159 ptr->term_names = calloc(strlen(buf) + 1, sizeof(char)); 160 if (ptr->term_names == NULL) { 161 if (str_size) 162 free(ptr->str_table); 163 close(fd); 164 return(0); 165 } 166 (void) strcpy(ptr->term_names, buf); 167 if (name_size > MAX_NAME_SIZE) 168 lseek(fd, (off_t) (name_size - MAX_NAME_SIZE), 1); 169 170 /* grab the booleans */ 171 read(fd, ptr->Booleans, min(BOOLCOUNT, (unsigned)bool_count)); 172 if (bool_count > BOOLCOUNT) 173 lseek(fd, (off_t) (bool_count - BOOLCOUNT), 1); 174 else 175 for (i=bool_count; i < BOOLCOUNT; i++) 176 ptr->Booleans[i] = 0; 177 178 /* 179 * If booleans end on an odd byte, skip it. The machine they 180 * originally wrote terminfo on must have been a 16-bit 181 * word-oriented machine that would trap out if you tried a 182 * word access off a 2-byte boundary. 183 */ 184 if ((name_size + bool_count) % 2 != 0) 185 read(fd, buf, 1); 186 187 /* grab the numbers */ 188 (void) read(fd, buf, min(NUMCOUNT*2, (unsigned)num_count*2)); 189 for (i = 0; i < min(num_count, NUMCOUNT); i++) 190 { 191 if (IS_NEG1(buf + 2*i)) 192 ptr->Numbers[i] = ABSENT_NUMERIC; 193 else if (IS_NEG2(buf + 2*i)) 194 ptr->Numbers[i] = CANCELLED_NUMERIC; 195 else 196 ptr->Numbers[i] = LOW_MSB(buf + 2*i); 197 } 198 if (num_count > NUMCOUNT) 199 lseek(fd, (off_t) (2 * (num_count - NUMCOUNT)), 1); 200 else 201 for (i=num_count; i < NUMCOUNT; i++) 202 ptr->Numbers[i] = ABSENT_NUMERIC; 203 204 if (str_count) 205 { 206 if (str_count*2 >= MAX_ENTRY_SIZE) 207 { 208 close(fd); 209 return(0); 210 } 211 /* grab the string offsets */ 212 numread = read(fd, buf, (unsigned)(str_count*2)); 213 if (numread < str_count*2) 214 { 215 close(fd); 216 return(0); 217 } 218 for (i = 0; i < numread/2; i++) 219 { 220 if (i >= STRCOUNT) 221 break; 222 if (IS_NEG1(buf + 2*i)) 223 ptr->Strings[i] = ABSENT_STRING; 224 else if (IS_NEG2(buf + 2*i)) 225 ptr->Strings[i] = CANCELLED_STRING; 226 else if (LOW_MSB(buf + 2*i) > str_size) 227 ptr->Strings[i] = ABSENT_STRING; 228 else 229 ptr->Strings[i] = (LOW_MSB(buf+2*i) + ptr->str_table); 230 } 231 } 232 233 if (str_count > STRCOUNT) 234 lseek(fd, (off_t) (2 * (str_count - STRCOUNT)), 1); 235 else 236 for (i = str_count; i < STRCOUNT; i++) 237 ptr->Strings[i] = ABSENT_STRING; 238 239 if (str_size) 240 { 241 /* finally, grab the string table itself */ 242 numread = read(fd, ptr->str_table, (unsigned)str_size); 243 if (numread != str_size) 244 { 245 close(fd); 246 return(0); 247 } 248 } 249 250 /* make sure all strings are NUL terminated */ 251 for (i = str_count; i < STRCOUNT; i++) { 252 char *p; 253 254 if (VALID_STRING(ptr->Strings[i])) { 255 for (p = ptr->Strings[i]; p <= ptr->str_table + str_size; p++) 256 if (*p == '\0') 257 break; 258 /* if there is no NUL, ignore the string */ 259 if (p > ptr->str_table + str_size) 260 ptr->Strings[i] = ABSENT_STRING; 261 } 262 } 263 264 close(fd); 265 return(1); 266} 267 268/* 269 * Build a terminfo pathname and try to read the data. Returns 1 on success, 270 * 0 on failure. 271 */ 272static int _nc_read_tic_entry(char *const filename, 273 const char *const dir, const char *ttn, TERMTYPE *const tp) 274{ 275/* maximum safe length of terminfo root directory name */ 276#define MAX_TPATH (PATH_MAX - MAX_ALIAS - 6) 277 278 if (strlen(dir) > MAX_TPATH) 279 return 0; 280 (void) sprintf(filename, "%s/%s", dir, ttn); 281 return _nc_read_file_entry(filename, tp); 282} 283 284/* 285 * Process the list of :-separated directories, looking for the terminal type. 286 * We don't use strtok because it does not show us empty tokens. 287 */ 288static int _nc_read_terminfo_dirs(const char *dirs, char *const filename, const char *const ttn, TERMTYPE *const tp) 289{ 290 char *list, *a; 291 const char *b; 292 int code = 0; 293 294 /* we'll modify the argument, so we must copy */ 295 if ((b = a = list = malloc(strlen(dirs) + 1)) == NULL) 296 return(0); 297 (void) strcpy(list, dirs); 298 299 for (;;) { 300 int c = *a; 301 if (c == 0 || c == ':') { 302 *a = 0; 303 if ((b + 1) >= a) 304 b = TERMINFO; 305 if (_nc_read_tic_entry(filename, b, ttn, tp) == 1) { 306 code = 1; 307 break; 308 } 309 b = a + 1; 310 if (c == 0) 311 break; 312 } 313 a++; 314 } 315 316 free(list); 317 return(code); 318} 319 320/* 321 * _nc_read_entry(char *tn, char *filename, TERMTYPE *tp) 322 * 323 * Find and read the compiled entry for a given terminal type, 324 * if it exists. We take pains here to make sure no combination 325 * of environment variables and terminal type name can be used to 326 * overrun the file buffer. 327 */ 328 329int _nc_read_entry(const char *const tn, char *const filename, TERMTYPE *const tp) 330{ 331char *envp; 332char ttn[MAX_ALIAS + 3]; 333 334#ifdef __OpenBSD__ 335 /* First check the BSD terminfo.db file */ 336 if (_nc_read_bsd_terminfo_entry(tn, filename, tp) == 1) 337 return 1; 338#endif /* __OpenBSD__ */ 339 340 /* truncate the terminal name to prevent dangerous buffer airline */ 341 (void) sprintf(ttn, "%c/%.*s", *tn, MAX_ALIAS, tn); 342 343 /* This is System V behavior, in conjunction with our requirements for 344 * writing terminfo entries. 345 */ 346 if (have_tic_directory 347 && _nc_read_tic_entry(filename, _nc_tic_dir(0), ttn, tp) == 1) 348 return 1; 349 350 if (!issetugid() && (envp = getenv("TERMINFO")) != 0 351 && _nc_read_tic_entry(filename, _nc_tic_dir(envp), ttn, tp) == 1) 352 return 1; 353 354 if (!issetugid() && (envp = _nc_home_terminfo()) != 0) { 355 if (_nc_read_tic_entry(filename, envp, ttn, tp) == 1) { 356 return(1); 357 } 358 } 359 360 /* this is an ncurses extension */ 361 if (!issetugid() && (envp = getenv("TERMINFO_DIRS")) != 0) 362 return _nc_read_terminfo_dirs(envp, filename, ttn, tp); 363 364 /* Try the system directory. Note that the TERMINFO_DIRS value, if 365 * defined by the configure script, begins with a ":", which will be 366 * interpreted as TERMINFO. 367 */ 368#ifdef TERMINFO_DIRS 369 return _nc_read_terminfo_dirs(TERMINFO_DIRS, filename, ttn, tp); 370#else 371 return _nc_read_tic_entry(filename, TERMINFO, ttn, tp); 372#endif 373} 374 375