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