1 /**************************************************************************** 2 * Copyright (c) 1998-2014,2015 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 * write_entry.c -- write a terminfo structure onto the file system 37 */ 38 39 #include <curses.priv.h> 40 #include <hashed_db.h> 41 42 #include <tic.h> 43 44 #if 1 45 #define TRACE_OUT(p) DEBUG(2, p) 46 #else 47 #define TRACE_OUT(p) /*nothing */ 48 #endif 49 50 MODULE_ID("$Id: write_entry.c,v 1.93 2015/09/05 21:24:29 tom Exp $") 51 52 static int total_written; 53 54 static int make_db_root(const char *); 55 56 #if !USE_HASHED_DB 57 static void 58 write_file(char *filename, TERMTYPE *tp) 59 { 60 char buffer[MAX_ENTRY_SIZE]; 61 unsigned limit = sizeof(buffer); 62 unsigned offset = 0; 63 64 FILE *fp = (_nc_access(filename, W_OK) == 0) ? fopen(filename, "wb") : 0; 65 if (fp == 0) { 66 perror(filename); 67 _nc_syserr_abort("can't open %s/%s", _nc_tic_dir(0), filename); 68 } 69 DEBUG(1, ("Created %s", filename)); 70 71 if (_nc_write_object(tp, buffer, &offset, limit) == ERR 72 || fwrite(buffer, sizeof(char), (size_t) offset, fp) != offset) { 73 _nc_syserr_abort("error writing %s/%s", _nc_tic_dir(0), filename); 74 } 75 76 fclose(fp); 77 } 78 79 /* 80 * Check for access rights to destination directories 81 * Create any directories which don't exist. 82 * 83 * Note: there's no reason to return the result of make_db_root(), since 84 * this function is called only in instances where that has to succeed. 85 */ 86 static void 87 check_writeable(int code) 88 { 89 static const char dirnames[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 90 static bool verified[sizeof(dirnames)]; 91 92 char dir[sizeof(LEAF_FMT)]; 93 char *s = 0; 94 95 if (code == 0 || (s = (strchr) (dirnames, code)) == 0) 96 _nc_err_abort("Illegal terminfo subdirectory \"" LEAF_FMT "\"", code); 97 98 if (verified[s - dirnames]) 99 return; 100 101 _nc_SPRINTF(dir, _nc_SLIMIT(sizeof(dir)) LEAF_FMT, code); 102 if (make_db_root(dir) < 0) { 103 _nc_err_abort("%s/%s: permission denied", _nc_tic_dir(0), dir); 104 } 105 106 verified[s - dirnames] = TRUE; 107 } 108 #endif /* !USE_HASHED_DB */ 109 110 static int 111 make_db_path(char *dst, const char *src, size_t limit) 112 { 113 int rc = -1; 114 const char *top = _nc_tic_dir(0); 115 116 if (src == top || _nc_is_abs_path(src)) { 117 if (strlen(src) + 1 <= limit) { 118 _nc_STRCPY(dst, src, limit); 119 rc = 0; 120 } 121 } else { 122 if (strlen(top) + strlen(src) + 2 <= limit) { 123 _nc_SPRINTF(dst, _nc_SLIMIT(limit) "%s/%s", top, src); 124 rc = 0; 125 } 126 } 127 #if USE_HASHED_DB 128 if (rc == 0) { 129 static const char suffix[] = DBM_SUFFIX; 130 size_t have = strlen(dst); 131 size_t need = strlen(suffix); 132 if (have > need && strcmp(dst + (int) (have - need), suffix)) { 133 if (have + need <= limit) { 134 _nc_STRCAT(dst, suffix, limit); 135 } else { 136 rc = -1; 137 } 138 } else if (_nc_is_dir_path(dst)) { 139 rc = -1; 140 } 141 } 142 #endif 143 return rc; 144 } 145 146 /* 147 * Make a database-root if it doesn't exist. 148 */ 149 static int 150 make_db_root(const char *path) 151 { 152 int rc; 153 char fullpath[PATH_MAX]; 154 155 if ((rc = make_db_path(fullpath, path, sizeof(fullpath))) == 0) { 156 #if USE_HASHED_DB 157 DB *capdbp; 158 159 if ((capdbp = _nc_db_open(fullpath, TRUE)) == NULL) { 160 rc = -1; 161 } else if (_nc_db_close(capdbp) < 0) { 162 rc = -1; 163 } 164 #else 165 struct stat statbuf; 166 167 if ((rc = stat(path, &statbuf)) < 0) { 168 rc = mkdir(path 169 #if !defined(__MINGW32__) 170 ,0777 171 #endif 172 ); 173 } else if (_nc_access(path, R_OK | W_OK | X_OK) < 0) { 174 rc = -1; /* permission denied */ 175 } else if (!(S_ISDIR(statbuf.st_mode))) { 176 rc = -1; /* not a directory */ 177 } 178 #endif 179 } 180 return rc; 181 } 182 183 /* 184 * Set the write directory for compiled entries. 185 */ 186 NCURSES_EXPORT(void) 187 _nc_set_writedir(const char *dir) 188 { 189 const char *destination; 190 char actual[PATH_MAX]; 191 192 if (dir == 0 193 #ifndef USE_ROOT_ENVIRON 194 && use_terminfo_vars() 195 #endif 196 ) 197 dir = getenv("TERMINFO"); 198 199 if (dir != 0) 200 (void) _nc_tic_dir(dir); 201 202 destination = _nc_tic_dir(0); 203 if (make_db_root(destination) < 0) { 204 char *home = _nc_home_terminfo(); 205 206 if (home != 0) { 207 destination = home; 208 if (make_db_root(destination) < 0) 209 _nc_err_abort("%s: permission denied (errno %d)", 210 destination, errno); 211 } 212 } 213 214 /* 215 * Note: because of this code, this logic should be exercised 216 * *once only* per run. 217 */ 218 #if USE_HASHED_DB 219 make_db_path(actual, destination, sizeof(actual)); 220 #else 221 if (chdir(_nc_tic_dir(destination)) < 0 222 || getcwd(actual, sizeof(actual)) == 0) 223 _nc_err_abort("%s: not a directory", destination); 224 #endif 225 _nc_keep_tic_dir(strdup(actual)); 226 } 227 228 /* 229 * Save the compiled version of a description in the filesystem. 230 * 231 * make a copy of the name-list 232 * break it up into first-name and all-but-last-name 233 * creat(first-name) 234 * write object information to first-name 235 * close(first-name) 236 * for each name in all-but-last-name 237 * link to first-name 238 * 239 * Using 'time()' to obtain a reference for file timestamps is unreliable, 240 * e.g., with NFS, because the filesystem may have a different time 241 * reference. We check for pre-existence of links by latching the first 242 * timestamp from a file that we create. 243 * 244 * The _nc_warning() calls will report a correct line number only if 245 * _nc_curr_line is properly set before the write_entry() call. 246 */ 247 248 NCURSES_EXPORT(void) 249 _nc_write_entry(TERMTYPE *const tp) 250 { 251 #if USE_HASHED_DB 252 253 char buffer[MAX_ENTRY_SIZE + 1]; 254 unsigned limit = sizeof(buffer); 255 unsigned offset = 0; 256 257 #else /* !USE_HASHED_DB */ 258 259 struct stat statbuf; 260 char filename[PATH_MAX]; 261 char linkname[PATH_MAX]; 262 #if USE_SYMLINKS 263 char symlinkname[PATH_MAX]; 264 #if !HAVE_LINK 265 #undef HAVE_LINK 266 #define HAVE_LINK 1 267 #endif 268 #endif /* USE_SYMLINKS */ 269 270 static int call_count; 271 static time_t start_time; /* time at start of writes */ 272 273 #endif /* USE_HASHED_DB */ 274 275 char name_list[MAX_TERMINFO_LENGTH]; 276 char *first_name, *other_names; 277 char *ptr; 278 char *term_names = tp->term_names; 279 size_t name_size = strlen(term_names); 280 281 if (name_size == 0) { 282 _nc_syserr_abort("no terminal name found."); 283 } else if (name_size >= sizeof(name_list) - 1) { 284 _nc_syserr_abort("terminal name too long: %s", term_names); 285 } 286 287 _nc_STRCPY(name_list, term_names, sizeof(name_list)); 288 DEBUG(7, ("Name list = '%s'", name_list)); 289 290 first_name = name_list; 291 292 ptr = &name_list[name_size - 1]; 293 other_names = ptr + 1; 294 295 while (ptr > name_list && *ptr != '|') 296 ptr--; 297 298 if (ptr != name_list) { 299 *ptr = '\0'; 300 301 for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++) 302 continue; 303 304 if (*ptr == '\0') 305 other_names = ptr; 306 else { 307 *ptr = '\0'; 308 other_names = ptr + 1; 309 } 310 } 311 312 DEBUG(7, ("First name = '%s'", first_name)); 313 DEBUG(7, ("Other names = '%s'", other_names)); 314 315 _nc_set_type(first_name); 316 317 #if USE_HASHED_DB 318 if (_nc_write_object(tp, buffer + 1, &offset, limit - 1) != ERR) { 319 DB *capdb = _nc_db_open(_nc_tic_dir(0), TRUE); 320 DBT key, data; 321 322 if (capdb != 0) { 323 buffer[0] = 0; 324 325 memset(&key, 0, sizeof(key)); 326 key.data = term_names; 327 key.size = name_size; 328 329 memset(&data, 0, sizeof(data)); 330 data.data = buffer; 331 data.size = offset + 1; 332 333 _nc_db_put(capdb, &key, &data); 334 335 buffer[0] = 2; 336 337 key.data = name_list; 338 key.size = strlen(name_list); 339 340 _nc_STRCPY(buffer + 1, 341 term_names, 342 sizeof(buffer) - 1); 343 data.size = name_size + 1; 344 345 _nc_db_put(capdb, &key, &data); 346 347 while (*other_names != '\0') { 348 ptr = other_names++; 349 assert(ptr < buffer + sizeof(buffer) - 1); 350 while (*other_names != '|' && *other_names != '\0') 351 other_names++; 352 353 if (*other_names != '\0') 354 *(other_names++) = '\0'; 355 356 key.data = ptr; 357 key.size = strlen(ptr); 358 359 _nc_db_put(capdb, &key, &data); 360 } 361 } 362 } 363 #else /* !USE_HASHED_DB */ 364 if (call_count++ == 0) { 365 start_time = 0; 366 } 367 368 if (strlen(first_name) >= sizeof(filename) - (2 + LEAF_LEN)) 369 _nc_warning("terminal name too long."); 370 371 _nc_SPRINTF(filename, _nc_SLIMIT(sizeof(filename)) 372 LEAF_FMT "/%s", first_name[0], first_name); 373 374 /* 375 * Has this primary name been written since the first call to 376 * write_entry()? If so, the newer write will step on the older, 377 * so warn the user. 378 */ 379 if (start_time > 0 && 380 stat(filename, &statbuf) >= 0 381 && statbuf.st_mtime >= start_time) { 382 #if HAVE_LINK && !USE_SYMLINKS 383 /* 384 * If the file has more than one link, the reason for the previous 385 * write could be that the current primary name used to be an alias for 386 * the previous entry. In that case, unlink the file so that we will 387 * not modify the previous entry as we write this one. 388 */ 389 if (statbuf.st_nlink > 1) { 390 _nc_warning("name redefined."); 391 unlink(filename); 392 } else { 393 _nc_warning("name multiply defined."); 394 } 395 #else 396 _nc_warning("name multiply defined."); 397 #endif 398 } 399 400 check_writeable(first_name[0]); 401 write_file(filename, tp); 402 403 if (start_time == 0) { 404 if (stat(filename, &statbuf) < 0 405 || (start_time = statbuf.st_mtime) == 0) { 406 _nc_syserr_abort("error obtaining time from %s/%s", 407 _nc_tic_dir(0), filename); 408 } 409 } 410 while (*other_names != '\0') { 411 ptr = other_names++; 412 while (*other_names != '|' && *other_names != '\0') 413 other_names++; 414 415 if (*other_names != '\0') 416 *(other_names++) = '\0'; 417 418 if (strlen(ptr) > sizeof(linkname) - (2 + LEAF_LEN)) { 419 _nc_warning("terminal alias %s too long.", ptr); 420 continue; 421 } 422 if (strchr(ptr, '/') != 0) { 423 _nc_warning("cannot link alias %s.", ptr); 424 continue; 425 } 426 427 check_writeable(ptr[0]); 428 _nc_SPRINTF(linkname, _nc_SLIMIT(sizeof(linkname)) 429 LEAF_FMT "/%s", ptr[0], ptr); 430 431 if (strcmp(filename, linkname) == 0) { 432 _nc_warning("self-synonym ignored"); 433 } else if (stat(linkname, &statbuf) >= 0 && 434 statbuf.st_mtime < start_time) { 435 _nc_warning("alias %s multiply defined.", ptr); 436 } else if (_nc_access(linkname, W_OK) == 0) 437 #if HAVE_LINK 438 { 439 int code; 440 #if USE_SYMLINKS 441 if (first_name[0] == linkname[0]) 442 strncpy(symlinkname, first_name, sizeof(symlinkname) - 1); 443 else { 444 _nc_STRCPY(symlinkname, "../", sizeof(suymlinkname)); 445 strncat(symlinkname, filename, sizeof(symlinkname) - 4); 446 } 447 symlinkname[sizeof(symlinkname) - 1] = '\0'; 448 #endif /* USE_SYMLINKS */ 449 #if HAVE_REMOVE 450 code = remove(linkname); 451 #else 452 code = unlink(linkname); 453 #endif 454 if (code != 0 && errno == ENOENT) 455 code = 0; 456 #if USE_SYMLINKS 457 if (symlink(symlinkname, linkname) < 0) 458 #else 459 if (link(filename, linkname) < 0) 460 #endif /* USE_SYMLINKS */ 461 { 462 /* 463 * If there wasn't anything there, and we cannot 464 * link to the target because it is the same as the 465 * target, then the source must be on a filesystem 466 * that uses caseless filenames, such as Win32, etc. 467 */ 468 if (code == 0 && errno == EEXIST) 469 _nc_warning("can't link %s to %s", filename, linkname); 470 else if (code == 0 && (errno == EPERM || errno == ENOENT)) 471 write_file(linkname, tp); 472 else { 473 #if MIXEDCASE_FILENAMES 474 _nc_syserr_abort("can't link %s to %s", filename, linkname); 475 #else 476 _nc_warning("can't link %s to %s (errno=%d)", filename, 477 linkname, errno); 478 #endif 479 } 480 } else { 481 DEBUG(1, ("Linked %s", linkname)); 482 } 483 } 484 #else /* just make copies */ 485 write_file(linkname, tp); 486 #endif /* HAVE_LINK */ 487 } 488 #endif /* USE_HASHED_DB */ 489 } 490 491 static size_t 492 fake_write(char *dst, 493 unsigned *offset, 494 size_t limit, 495 char *src, 496 size_t want, 497 size_t size) 498 { 499 size_t have = (limit - *offset); 500 501 want *= size; 502 if (have > 0) { 503 if (want > have) 504 want = have; 505 memcpy(dst + *offset, src, want); 506 *offset += (unsigned) want; 507 } else { 508 want = 0; 509 } 510 return (want / size); 511 } 512 513 #define Write(buf, size, count) fake_write(buffer, offset, (size_t) limit, (char *) buf, (size_t) count, (size_t) size) 514 515 #undef LITTLE_ENDIAN /* BSD/OS defines this as a feature macro */ 516 #define HI(x) ((x) / 256) 517 #define LO(x) ((x) % 256) 518 #define LITTLE_ENDIAN(p, x) (p)[0] = (unsigned char)LO(x), \ 519 (p)[1] = (unsigned char)HI(x) 520 521 #define WRITE_STRING(str) (Write(str, sizeof(char), strlen(str) + 1) == strlen(str) + 1) 522 523 static int 524 compute_offsets(char **Strings, size_t strmax, short *offsets) 525 { 526 int nextfree = 0; 527 size_t i; 528 529 for (i = 0; i < strmax; i++) { 530 if (Strings[i] == ABSENT_STRING) { 531 offsets[i] = -1; 532 } else if (Strings[i] == CANCELLED_STRING) { 533 offsets[i] = -2; 534 } else { 535 offsets[i] = (short) nextfree; 536 nextfree += (int) strlen(Strings[i]) + 1; 537 TRACE_OUT(("put Strings[%d]=%s(%d)", (int) i, 538 _nc_visbuf(Strings[i]), (int) nextfree)); 539 } 540 } 541 return nextfree; 542 } 543 544 static void 545 convert_shorts(unsigned char *buf, short *Numbers, size_t count) 546 { 547 size_t i; 548 for (i = 0; i < count; i++) { 549 if (Numbers[i] == ABSENT_NUMERIC) { /* HI/LO won't work */ 550 buf[2 * i] = buf[2 * i + 1] = 0377; 551 } else if (Numbers[i] == CANCELLED_NUMERIC) { /* HI/LO won't work */ 552 buf[2 * i] = 0376; 553 buf[2 * i + 1] = 0377; 554 } else { 555 LITTLE_ENDIAN(buf + 2 * i, Numbers[i]); 556 TRACE_OUT(("put Numbers[%u]=%d", (unsigned) i, Numbers[i])); 557 } 558 } 559 } 560 561 #define even_boundary(value) \ 562 ((value) % 2 != 0 && Write(&zero, sizeof(char), 1) != 1) 563 564 #if NCURSES_XNAMES 565 static unsigned 566 extended_Booleans(TERMTYPE *tp) 567 { 568 unsigned result = 0; 569 unsigned i; 570 571 for (i = 0; i < tp->ext_Booleans; ++i) { 572 if (tp->Booleans[BOOLCOUNT + i] == TRUE) 573 result = (i + 1); 574 } 575 return result; 576 } 577 578 static unsigned 579 extended_Numbers(TERMTYPE *tp) 580 { 581 unsigned result = 0; 582 unsigned i; 583 584 for (i = 0; i < tp->ext_Numbers; ++i) { 585 if (tp->Numbers[NUMCOUNT + i] != ABSENT_NUMERIC) 586 result = (i + 1); 587 } 588 return result; 589 } 590 591 static unsigned 592 extended_Strings(TERMTYPE *tp) 593 { 594 unsigned short result = 0; 595 unsigned short i; 596 597 for (i = 0; i < tp->ext_Strings; ++i) { 598 if (tp->Strings[STRCOUNT + i] != ABSENT_STRING) 599 result = (unsigned short) (i + 1); 600 } 601 return result; 602 } 603 604 /* 605 * _nc_align_termtype() will extend entries that are referenced in a use= 606 * clause - discard the unneeded data. 607 */ 608 static bool 609 extended_object(TERMTYPE *tp) 610 { 611 bool result = FALSE; 612 613 if (_nc_user_definable) { 614 result = ((extended_Booleans(tp) 615 + extended_Numbers(tp) 616 + extended_Strings(tp)) != 0); 617 } 618 return result; 619 } 620 #endif 621 622 NCURSES_EXPORT(int) 623 _nc_write_object(TERMTYPE *tp, char *buffer, unsigned *offset, unsigned limit) 624 { 625 char *namelist; 626 size_t namelen, boolmax, nummax, strmax; 627 char zero = '\0'; 628 size_t i; 629 int nextfree; 630 short offsets[MAX_ENTRY_SIZE / 2]; 631 unsigned char buf[MAX_ENTRY_SIZE]; 632 unsigned last_bool = BOOLWRITE; 633 unsigned last_num = NUMWRITE; 634 unsigned last_str = STRWRITE; 635 636 #if NCURSES_XNAMES 637 /* 638 * Normally we limit the list of values to exclude the "obsolete" 639 * capabilities. However, if we are accepting extended names, add 640 * these as well, since they are used for supporting translation 641 * to/from termcap. 642 */ 643 if (_nc_user_definable) { 644 last_bool = BOOLCOUNT; 645 last_num = NUMCOUNT; 646 last_str = STRCOUNT; 647 } 648 #endif 649 650 namelist = tp->term_names; 651 namelen = strlen(namelist) + 1; 652 653 boolmax = 0; 654 for (i = 0; i < last_bool; i++) { 655 if (tp->Booleans[i] == TRUE) 656 boolmax = i + 1; 657 } 658 659 nummax = 0; 660 for (i = 0; i < last_num; i++) { 661 if (tp->Numbers[i] != ABSENT_NUMERIC) 662 nummax = i + 1; 663 } 664 665 strmax = 0; 666 for (i = 0; i < last_str; i++) { 667 if (tp->Strings[i] != ABSENT_STRING) 668 strmax = i + 1; 669 } 670 671 nextfree = compute_offsets(tp->Strings, strmax, offsets); 672 673 /* fill in the header */ 674 LITTLE_ENDIAN(buf, MAGIC); 675 LITTLE_ENDIAN(buf + 2, min(namelen, MAX_NAME_SIZE + 1)); 676 LITTLE_ENDIAN(buf + 4, boolmax); 677 LITTLE_ENDIAN(buf + 6, nummax); 678 LITTLE_ENDIAN(buf + 8, strmax); 679 LITTLE_ENDIAN(buf + 10, nextfree); 680 681 /* write out the header */ 682 TRACE_OUT(("Header of %s @%d", namelist, *offset)); 683 if (Write(buf, 12, 1) != 1 684 || Write(namelist, sizeof(char), namelen) != namelen) 685 return (ERR); 686 687 for (i = 0; i < boolmax; i++) 688 if (tp->Booleans[i] == TRUE) 689 buf[i] = TRUE; 690 else 691 buf[i] = FALSE; 692 if (Write(buf, sizeof(char), boolmax) != boolmax) 693 return (ERR); 694 695 if (even_boundary(namelen + boolmax)) 696 return (ERR); 697 698 TRACE_OUT(("Numerics begin at %04x", *offset)); 699 700 /* the numerics */ 701 convert_shorts(buf, tp->Numbers, nummax); 702 if (Write(buf, 2, nummax) != nummax) 703 return (ERR); 704 705 TRACE_OUT(("String offsets begin at %04x", *offset)); 706 707 /* the string offsets */ 708 convert_shorts(buf, offsets, strmax); 709 if (Write(buf, 2, strmax) != strmax) 710 return (ERR); 711 712 TRACE_OUT(("String table begins at %04x", *offset)); 713 714 /* the strings */ 715 for (i = 0; i < strmax; i++) 716 if (VALID_STRING(tp->Strings[i])) 717 if (!WRITE_STRING(tp->Strings[i])) 718 return (ERR); 719 720 #if NCURSES_XNAMES 721 if (extended_object(tp)) { 722 unsigned extcnt = (unsigned) NUM_EXT_NAMES(tp); 723 724 if (even_boundary(nextfree)) 725 return (ERR); 726 727 nextfree = compute_offsets(tp->Strings + STRCOUNT, 728 (size_t) tp->ext_Strings, 729 offsets); 730 TRACE_OUT(("after extended string capabilities, nextfree=%d", nextfree)); 731 732 if (tp->ext_Strings >= SIZEOF(offsets)) 733 return (ERR); 734 735 nextfree += compute_offsets(tp->ext_Names, 736 (size_t) extcnt, 737 offsets + tp->ext_Strings); 738 TRACE_OUT(("after extended capnames, nextfree=%d", nextfree)); 739 strmax = tp->ext_Strings + extcnt; 740 741 /* 742 * Write the extended header 743 */ 744 LITTLE_ENDIAN(buf + 0, tp->ext_Booleans); 745 LITTLE_ENDIAN(buf + 2, tp->ext_Numbers); 746 LITTLE_ENDIAN(buf + 4, tp->ext_Strings); 747 LITTLE_ENDIAN(buf + 6, strmax); 748 LITTLE_ENDIAN(buf + 8, nextfree); 749 TRACE_OUT(("WRITE extended-header @%d", *offset)); 750 if (Write(buf, 10, 1) != 1) 751 return (ERR); 752 753 TRACE_OUT(("WRITE %d booleans @%d", tp->ext_Booleans, *offset)); 754 if (tp->ext_Booleans 755 && Write(tp->Booleans + BOOLCOUNT, sizeof(char), 756 tp->ext_Booleans) != tp->ext_Booleans) 757 return (ERR); 758 759 if (even_boundary(tp->ext_Booleans)) 760 return (ERR); 761 762 TRACE_OUT(("WRITE %d numbers @%d", tp->ext_Numbers, *offset)); 763 if (tp->ext_Numbers) { 764 convert_shorts(buf, tp->Numbers + NUMCOUNT, (size_t) tp->ext_Numbers); 765 if (Write(buf, 2, tp->ext_Numbers) != tp->ext_Numbers) 766 return (ERR); 767 } 768 769 /* 770 * Convert the offsets for the ext_Strings and ext_Names tables, 771 * in that order. 772 */ 773 convert_shorts(buf, offsets, strmax); 774 TRACE_OUT(("WRITE offsets @%d", *offset)); 775 if (Write(buf, 2, strmax) != strmax) 776 return (ERR); 777 778 /* 779 * Write the string table after the offset tables so we do not 780 * have to do anything about alignment. 781 */ 782 for (i = 0; i < tp->ext_Strings; i++) { 783 if (VALID_STRING(tp->Strings[i + STRCOUNT])) { 784 TRACE_OUT(("WRITE ext_Strings[%d]=%s", (int) i, 785 _nc_visbuf(tp->Strings[i + STRCOUNT]))); 786 if (!WRITE_STRING(tp->Strings[i + STRCOUNT])) 787 return (ERR); 788 } 789 } 790 791 /* 792 * Write the extended names 793 */ 794 for (i = 0; i < extcnt; i++) { 795 TRACE_OUT(("WRITE ext_Names[%d]=%s", (int) i, tp->ext_Names[i])); 796 if (!WRITE_STRING(tp->ext_Names[i])) 797 return (ERR); 798 } 799 800 } 801 #endif /* NCURSES_XNAMES */ 802 803 total_written++; 804 return (OK); 805 } 806 807 /* 808 * Returns the total number of entries written by this process 809 */ 810 NCURSES_EXPORT(int) 811 _nc_tic_written(void) 812 { 813 return total_written; 814 } 815