1 /**************************************************************************** 2 * Copyright (c) 1998-2008,2010 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 * toe.c --- table of entries report generator 37 */ 38 39 #include <progs.priv.h> 40 41 #include <sys/stat.h> 42 43 #if USE_HASHED_DB 44 #include <hashed_db.h> 45 #endif 46 47 MODULE_ID("$Id: toe.c,v 1.52 2010/05/01 22:04:08 tom Exp $") 48 49 #define isDotname(name) (!strcmp(name, ".") || !strcmp(name, "..")) 50 51 const char *_nc_progname; 52 53 #if NO_LEAKS 54 #undef ExitProgram 55 static void ExitProgram(int code) GCC_NORETURN; 56 static void 57 ExitProgram(int code) 58 { 59 _nc_free_entries(_nc_head); 60 _nc_free_tic(code); 61 } 62 #endif 63 64 static void 65 failed(const char *msg) 66 { 67 perror(msg); 68 ExitProgram(EXIT_FAILURE); 69 } 70 71 #if USE_HASHED_DB 72 static bool 73 make_db_name(char *dst, const char *src, unsigned limit) 74 { 75 static const char suffix[] = DBM_SUFFIX; 76 77 bool result = FALSE; 78 unsigned lens = sizeof(suffix) - 1; 79 unsigned size = strlen(src); 80 unsigned need = lens + size; 81 82 if (need <= limit) { 83 if (size >= lens 84 && !strcmp(src + size - lens, suffix)) 85 (void) strcpy(dst, src); 86 else 87 (void) sprintf(dst, "%s%s", src, suffix); 88 result = TRUE; 89 } 90 return result; 91 } 92 #endif 93 94 static bool 95 is_database(const char *path) 96 { 97 bool result = FALSE; 98 #if USE_DATABASE 99 if (_nc_is_dir_path(path) && access(path, R_OK | X_OK) == 0) { 100 result = TRUE; 101 } 102 #endif 103 #if USE_TERMCAP 104 if (_nc_is_file_path(path) && access(path, R_OK) == 0) { 105 result = TRUE; 106 } 107 #endif 108 #if USE_HASHED_DB 109 if (!result) { 110 char filename[PATH_MAX]; 111 if (_nc_is_file_path(path) && access(path, R_OK) == 0) { 112 result = TRUE; 113 } else if (make_db_name(filename, path, sizeof(filename))) { 114 if (_nc_is_file_path(filename) && access(filename, R_OK) == 0) { 115 result = TRUE; 116 } 117 } 118 } 119 #endif 120 return result; 121 } 122 123 static void 124 deschook(const char *cn, TERMTYPE *tp) 125 /* display a description for the type */ 126 { 127 const char *desc; 128 129 if ((desc = strrchr(tp->term_names, '|')) == 0 || *++desc == '\0') 130 desc = "(No description)"; 131 132 (void) printf("%-10s\t%s\n", cn, desc); 133 } 134 135 #if USE_TERMCAP 136 static void 137 show_termcap(char *buffer, 138 void (*hook) (const char *, TERMTYPE *tp)) 139 { 140 TERMTYPE data; 141 char *next = strchr(buffer, ':'); 142 char *last; 143 char *list = buffer; 144 145 if (next) 146 *next = '\0'; 147 148 last = strrchr(buffer, '|'); 149 if (last) 150 ++last; 151 152 data.term_names = strdup(buffer); 153 while ((next = strtok(list, "|")) != 0) { 154 if (next != last) 155 hook(next, &data); 156 list = 0; 157 } 158 free(data.term_names); 159 } 160 #endif 161 162 static int 163 typelist(int eargc, char *eargv[], 164 bool verbosity, 165 void (*hook) (const char *, TERMTYPE *tp)) 166 /* apply a function to each entry in given terminfo directories */ 167 { 168 int i; 169 170 for (i = 0; i < eargc; i++) { 171 #if USE_DATABASE 172 if (_nc_is_dir_path(eargv[i])) { 173 char *cwd_buf = 0; 174 DIR *termdir; 175 DIRENT *subdir; 176 177 if ((termdir = opendir(eargv[i])) == 0) { 178 (void) fflush(stdout); 179 (void) fprintf(stderr, 180 "%s: can't open terminfo directory %s\n", 181 _nc_progname, eargv[i]); 182 return (EXIT_FAILURE); 183 } else if (verbosity) 184 (void) printf("#\n#%s:\n#\n", eargv[i]); 185 186 while ((subdir = readdir(termdir)) != 0) { 187 size_t len = NAMLEN(subdir); 188 size_t cwd_len = len + strlen(eargv[i]) + 3; 189 char name_1[PATH_MAX]; 190 DIR *entrydir; 191 DIRENT *entry; 192 193 cwd_buf = typeRealloc(char, cwd_len, cwd_buf); 194 if (cwd_buf == 0) 195 failed("realloc cwd_buf"); 196 197 assert(cwd_buf != 0); 198 199 strncpy(name_1, subdir->d_name, len)[len] = '\0'; 200 if (isDotname(name_1)) 201 continue; 202 203 (void) sprintf(cwd_buf, "%s/%.*s/", eargv[i], (int) len, name_1); 204 if (chdir(cwd_buf) != 0) 205 continue; 206 207 entrydir = opendir("."); 208 if (entrydir == 0) { 209 perror(cwd_buf); 210 continue; 211 } 212 while ((entry = readdir(entrydir)) != 0) { 213 char name_2[PATH_MAX]; 214 TERMTYPE lterm; 215 char *cn; 216 int status; 217 218 len = NAMLEN(entry); 219 strncpy(name_2, entry->d_name, len)[len] = '\0'; 220 if (isDotname(name_2) || !_nc_is_file_path(name_2)) 221 continue; 222 223 status = _nc_read_file_entry(name_2, <erm); 224 if (status <= 0) { 225 (void) fflush(stdout); 226 (void) fprintf(stderr, 227 "%s: couldn't open terminfo file %s.\n", 228 _nc_progname, name_2); 229 return (EXIT_FAILURE); 230 } 231 232 /* only visit things once, by primary name */ 233 cn = _nc_first_name(lterm.term_names); 234 if (!strcmp(cn, name_2)) { 235 /* apply the selected hook function */ 236 (*hook) (cn, <erm); 237 } 238 _nc_free_termtype(<erm); 239 } 240 closedir(entrydir); 241 } 242 closedir(termdir); 243 if (cwd_buf != 0) 244 free(cwd_buf); 245 } 246 #if USE_HASHED_DB 247 else { 248 DB *capdbp; 249 char filename[PATH_MAX]; 250 251 if (make_db_name(filename, eargv[i], sizeof(filename))) { 252 if ((capdbp = _nc_db_open(filename, FALSE)) != 0) { 253 DBT key, data; 254 int code; 255 256 code = _nc_db_first(capdbp, &key, &data); 257 while (code == 0) { 258 TERMTYPE lterm; 259 int used; 260 char *have; 261 char *cn; 262 263 if (_nc_db_have_data(&key, &data, &have, &used)) { 264 if (_nc_read_termtype(<erm, have, used) > 0) { 265 /* only visit things once, by primary name */ 266 cn = _nc_first_name(lterm.term_names); 267 /* apply the selected hook function */ 268 (*hook) (cn, <erm); 269 _nc_free_termtype(<erm); 270 } 271 } 272 code = _nc_db_next(capdbp, &key, &data); 273 } 274 275 _nc_db_close(capdbp); 276 } 277 } 278 } 279 #endif 280 #endif 281 #if USE_TERMCAP 282 #if HAVE_BSD_CGETENT 283 char *db_array[2]; 284 char *buffer = 0; 285 286 if (verbosity) 287 (void) printf("#\n#%s:\n#\n", eargv[i]); 288 289 db_array[0] = eargv[i]; 290 db_array[1] = 0; 291 292 if (cgetfirst(&buffer, db_array)) { 293 show_termcap(buffer, hook); 294 free(buffer); 295 while (cgetnext(&buffer, db_array)) { 296 show_termcap(buffer, hook); 297 free(buffer); 298 } 299 } 300 cgetclose(); 301 #else 302 /* scan termcap text-file only */ 303 if (_nc_is_file_path(eargv[i])) { 304 char buffer[2048]; 305 FILE *fp; 306 307 if ((fp = fopen(eargv[i], "r")) != 0) { 308 while (fgets(buffer, sizeof(buffer), fp) != 0) { 309 if (*buffer == '#') 310 continue; 311 if (isspace(*buffer)) 312 continue; 313 show_termcap(buffer, hook); 314 } 315 fclose(fp); 316 } 317 } 318 #endif 319 #endif 320 } 321 322 return (EXIT_SUCCESS); 323 } 324 325 static void 326 usage(void) 327 { 328 (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname); 329 ExitProgram(EXIT_FAILURE); 330 } 331 332 int 333 main(int argc, char *argv[]) 334 { 335 bool all_dirs = FALSE; 336 bool direct_dependencies = FALSE; 337 bool invert_dependencies = FALSE; 338 bool header = FALSE; 339 char *report_file = 0; 340 unsigned i; 341 int code; 342 int this_opt, last_opt = '?'; 343 int v_opt = 0; 344 345 _nc_progname = _nc_rootname(argv[0]); 346 347 while ((this_opt = getopt(argc, argv, "0123456789ahu:vU:V")) != -1) { 348 /* handle optional parameter */ 349 if (isdigit(this_opt)) { 350 switch (last_opt) { 351 case 'v': 352 v_opt = (this_opt - '0'); 353 break; 354 default: 355 if (isdigit(last_opt)) 356 v_opt *= 10; 357 else 358 v_opt = 0; 359 v_opt += (this_opt - '0'); 360 last_opt = this_opt; 361 } 362 continue; 363 } 364 switch (this_opt) { 365 case 'a': 366 all_dirs = TRUE; 367 break; 368 case 'h': 369 header = TRUE; 370 break; 371 case 'u': 372 direct_dependencies = TRUE; 373 report_file = optarg; 374 break; 375 case 'v': 376 v_opt = 1; 377 break; 378 case 'U': 379 invert_dependencies = TRUE; 380 report_file = optarg; 381 break; 382 case 'V': 383 puts(curses_version()); 384 ExitProgram(EXIT_SUCCESS); 385 default: 386 usage(); 387 } 388 } 389 set_trace_level(v_opt); 390 391 if (report_file != 0) { 392 if (freopen(report_file, "r", stdin) == 0) { 393 (void) fflush(stdout); 394 fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file); 395 ExitProgram(EXIT_FAILURE); 396 } 397 398 /* parse entries out of the source file */ 399 _nc_set_source(report_file); 400 _nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK); 401 } 402 403 /* maybe we want a direct-dependency listing? */ 404 if (direct_dependencies) { 405 ENTRY *qp; 406 407 for_entry_list(qp) { 408 if (qp->nuses) { 409 unsigned j; 410 411 (void) printf("%s:", _nc_first_name(qp->tterm.term_names)); 412 for (j = 0; j < qp->nuses; j++) 413 (void) printf(" %s", qp->uses[j].name); 414 putchar('\n'); 415 } 416 } 417 418 ExitProgram(EXIT_SUCCESS); 419 } 420 421 /* maybe we want a reverse-dependency listing? */ 422 if (invert_dependencies) { 423 ENTRY *qp, *rp; 424 int matchcount; 425 426 for_entry_list(qp) { 427 matchcount = 0; 428 for_entry_list(rp) { 429 if (rp->nuses == 0) 430 continue; 431 432 for (i = 0; i < rp->nuses; i++) 433 if (_nc_name_match(qp->tterm.term_names, 434 rp->uses[i].name, "|")) { 435 if (matchcount++ == 0) 436 (void) printf("%s:", 437 _nc_first_name(qp->tterm.term_names)); 438 (void) printf(" %s", 439 _nc_first_name(rp->tterm.term_names)); 440 } 441 } 442 if (matchcount) 443 putchar('\n'); 444 } 445 446 ExitProgram(EXIT_SUCCESS); 447 } 448 449 /* 450 * If we get this far, user wants a simple terminal type listing. 451 */ 452 if (optind < argc) { 453 code = typelist(argc - optind, argv + optind, header, deschook); 454 } else if (all_dirs) { 455 DBDIRS state; 456 int offset; 457 int pass; 458 const char *path; 459 char **eargv = 0; 460 461 code = EXIT_FAILURE; 462 for (pass = 0; pass < 2; ++pass) { 463 unsigned count = 0; 464 465 _nc_first_db(&state, &offset); 466 while ((path = _nc_next_db(&state, &offset)) != 0) { 467 if (!is_database(path)) { 468 ; 469 } else if (eargv != 0) { 470 unsigned n; 471 int found = FALSE; 472 473 /* eliminate duplicates */ 474 for (n = 0; n < count; ++n) { 475 if (!strcmp(path, eargv[n])) { 476 found = TRUE; 477 break; 478 } 479 } 480 if (!found) { 481 eargv[count] = strdup(path); 482 ++count; 483 } 484 } else { 485 ++count; 486 } 487 } 488 if (!pass) { 489 eargv = typeCalloc(char *, count + 1); 490 if (eargv == 0) 491 failed("realloc eargv"); 492 493 assert(eargv != 0); 494 } else { 495 code = typelist((int) count, eargv, header, deschook); 496 while (count-- > 0) 497 free(eargv[count]); 498 free(eargv); 499 } 500 } 501 } else { 502 DBDIRS state; 503 int offset; 504 const char *path; 505 char *eargv[3]; 506 int count = 0; 507 508 _nc_first_db(&state, &offset); 509 while ((path = _nc_next_db(&state, &offset)) != 0) { 510 if (is_database(path)) { 511 eargv[count++] = strdup(path); 512 break; 513 } 514 } 515 eargv[count] = 0; 516 517 code = typelist(count, eargv, header, deschook); 518 519 while (count-- > 0) 520 free(eargv[count]); 521 } 522 _nc_last_db(); 523 524 ExitProgram(code); 525 } 526