read_entry.c revision 1.2
1/* $OpenBSD: read_entry.c,v 1.2 1999/01/22 04:50:43 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 if (_nc_access(filename, R_OK) < 0 118 || (fd = open(filename, O_RDONLY|O_BINARY)) < 0) { 119 T(("cannot open terminfo %s (errno=%d)", filename, errno)); 120 return(0); 121 } 122 123 T(("read terminfo %s", filename)); 124 125 /* grab the header */ 126 (void) read(fd, buf, 12); 127 if (LOW_MSB(buf) != MAGIC) 128 { 129 close(fd); 130 return(0); 131 } 132 name_size = LOW_MSB(buf + 2); 133 bool_count = LOW_MSB(buf + 4); 134 num_count = LOW_MSB(buf + 6); 135 str_count = LOW_MSB(buf + 8); 136 str_size = LOW_MSB(buf + 10); 137 138 if (str_size) 139 { 140 /* try to allocate space for the string table */ 141 ptr->str_table = malloc((unsigned)str_size); 142 if (ptr->str_table == 0) 143 { 144 close(fd); 145 return(0); 146 } 147 } 148 else 149 str_count = 0; 150 151 /* grab the name */ 152 read(fd, buf, min(MAX_NAME_SIZE, (unsigned)name_size)); 153 buf[MAX_NAME_SIZE] = '\0'; 154 ptr->term_names = calloc(strlen(buf) + 1, sizeof(char)); 155 if (ptr->term_names == NULL) { 156 if (str_size) 157 free(ptr->str_table); 158 close(fd); 159 return(0); 160 } 161 (void) strcpy(ptr->term_names, buf); 162 if (name_size > MAX_NAME_SIZE) 163 lseek(fd, (off_t) (name_size - MAX_NAME_SIZE), 1); 164 165 /* grab the booleans */ 166 read(fd, ptr->Booleans, min(BOOLCOUNT, (unsigned)bool_count)); 167 if (bool_count > BOOLCOUNT) 168 lseek(fd, (off_t) (bool_count - BOOLCOUNT), 1); 169 else 170 for (i=bool_count; i < BOOLCOUNT; i++) 171 ptr->Booleans[i] = 0; 172 173 /* 174 * If booleans end on an odd byte, skip it. The machine they 175 * originally wrote terminfo on must have been a 16-bit 176 * word-oriented machine that would trap out if you tried a 177 * word access off a 2-byte boundary. 178 */ 179 if ((name_size + bool_count) % 2 != 0) 180 read(fd, buf, 1); 181 182 /* grab the numbers */ 183 (void) read(fd, buf, min(NUMCOUNT*2, (unsigned)num_count*2)); 184 for (i = 0; i < min(num_count, NUMCOUNT); i++) 185 { 186 if (IS_NEG1(buf + 2*i)) 187 ptr->Numbers[i] = ABSENT_NUMERIC; 188 else if (IS_NEG2(buf + 2*i)) 189 ptr->Numbers[i] = CANCELLED_NUMERIC; 190 else 191 ptr->Numbers[i] = LOW_MSB(buf + 2*i); 192 } 193 if (num_count > NUMCOUNT) 194 lseek(fd, (off_t) (2 * (num_count - NUMCOUNT)), 1); 195 else 196 for (i=num_count; i < NUMCOUNT; i++) 197 ptr->Numbers[i] = ABSENT_NUMERIC; 198 199 if (str_count) 200 { 201 if (str_count*2 >= MAX_ENTRY_SIZE) 202 { 203 close(fd); 204 return(0); 205 } 206 /* grab the string offsets */ 207 numread = read(fd, buf, (unsigned)(str_count*2)); 208 if (numread < str_count*2) 209 { 210 close(fd); 211 return(0); 212 } 213 for (i = 0; i < numread/2; i++) 214 { 215 if (i >= STRCOUNT) 216 break; 217 if (IS_NEG1(buf + 2*i)) 218 ptr->Strings[i] = ABSENT_STRING; 219 else if (IS_NEG2(buf + 2*i)) 220 ptr->Strings[i] = CANCELLED_STRING; 221 else if (LOW_MSB(buf + 2*i) > str_size) 222 ptr->Strings[i] = ABSENT_STRING; 223 else 224 ptr->Strings[i] = (LOW_MSB(buf+2*i) + ptr->str_table); 225 } 226 } 227 228 if (str_count > STRCOUNT) 229 lseek(fd, (off_t) (2 * (str_count - STRCOUNT)), 1); 230 else 231 for (i = str_count; i < STRCOUNT; i++) 232 ptr->Strings[i] = ABSENT_STRING; 233 234 if (str_size) 235 { 236 /* finally, grab the string table itself */ 237 numread = read(fd, ptr->str_table, (unsigned)str_size); 238 if (numread != str_size) 239 { 240 close(fd); 241 return(0); 242 } 243 } 244 245 /* make sure all strings are NUL terminated */ 246 for (i = str_count; i < STRCOUNT; i++) { 247 char *p; 248 249 if (VALID_STRING(ptr->Strings[i])) { 250 for (p = ptr->Strings[i]; p <= ptr->str_table + str_size; p++) 251 if (*p == '\0') 252 break; 253 /* if there is no NUL, ignore the string */ 254 if (p > ptr->str_table + str_size) 255 ptr->Strings[i] = ABSENT_STRING; 256 } 257 } 258 259 close(fd); 260 return(1); 261} 262 263/* 264 * Build a terminfo pathname and try to read the data. Returns 1 on success, 265 * 0 on failure. 266 */ 267static int _nc_read_tic_entry(char *const filename, 268 const char *const dir, const char *ttn, TERMTYPE *const tp) 269{ 270/* maximum safe length of terminfo root directory name */ 271#define MAX_TPATH (PATH_MAX - MAX_ALIAS - 6) 272 273 if (strlen(dir) > MAX_TPATH) 274 return 0; 275 (void) sprintf(filename, "%s/%s", dir, ttn); 276 return _nc_read_file_entry(filename, tp); 277} 278 279/* 280 * Process the list of :-separated directories, looking for the terminal type. 281 * We don't use strtok because it does not show us empty tokens. 282 */ 283static int _nc_read_terminfo_dirs(const char *dirs, char *const filename, const char *const ttn, TERMTYPE *const tp) 284{ 285 char *list, *a; 286 const char *b; 287 int code = 0; 288 289 /* we'll modify the argument, so we must copy */ 290 if ((b = a = list = malloc(strlen(dirs) + 1)) == NULL) 291 return(0); 292 (void) strcpy(list, dirs); 293 294 for (;;) { 295 int c = *a; 296 if (c == 0 || c == ':') { 297 *a = 0; 298 if ((b + 1) >= a) 299 b = TERMINFO; 300 if (_nc_read_tic_entry(filename, b, ttn, tp) == 1) { 301 code = 1; 302 break; 303 } 304 b = a + 1; 305 if (c == 0) 306 break; 307 } 308 a++; 309 } 310 311 free(list); 312 return(code); 313} 314 315/* 316 * _nc_read_entry(char *tn, char *filename, TERMTYPE *tp) 317 * 318 * Find and read the compiled entry for a given terminal type, 319 * if it exists. We take pains here to make sure no combination 320 * of environment variables and terminal type name can be used to 321 * overrun the file buffer. 322 */ 323 324int _nc_read_entry(const char *const tn, char *const filename, TERMTYPE *const tp) 325{ 326char *envp; 327char ttn[MAX_ALIAS + 3]; 328 329#ifdef __OpenBSD__ 330 /* First check the BSD terminfo.db file */ 331 if (_nc_read_bsd_terminfo_entry(tn, filename, tp) == 1) 332 return 1; 333#endif /* __OpenBSD__ */ 334 335 /* truncate the terminal name to prevent dangerous buffer airline */ 336 (void) sprintf(ttn, "%c/%.*s", *tn, MAX_ALIAS, tn); 337 338 /* This is System V behavior, in conjunction with our requirements for 339 * writing terminfo entries. 340 */ 341 if (have_tic_directory 342 && _nc_read_tic_entry(filename, _nc_tic_dir(0), ttn, tp) == 1) 343 return 1; 344 345 if (!issetugid() && (envp = getenv("TERMINFO")) != 0 346 && _nc_read_tic_entry(filename, _nc_tic_dir(envp), ttn, tp) == 1) 347 return 1; 348 349 if (!issetugid() && (envp = _nc_home_terminfo()) != 0) { 350 if (_nc_read_tic_entry(filename, envp, ttn, tp) == 1) { 351 return(1); 352 } 353 } 354 355 /* this is an ncurses extension */ 356 if (!issetugid() && (envp = getenv("TERMINFO_DIRS")) != 0) 357 return _nc_read_terminfo_dirs(envp, filename, ttn, tp); 358 359 /* Try the system directory. Note that the TERMINFO_DIRS value, if 360 * defined by the configure script, begins with a ":", which will be 361 * interpreted as TERMINFO. 362 */ 363#ifdef TERMINFO_DIRS 364 return _nc_read_terminfo_dirs(TERMINFO_DIRS, filename, ttn, tp); 365#else 366 return _nc_read_tic_entry(filename, TERMINFO, ttn, tp); 367#endif 368} 369 370