1 /**************************************************************************** 2 * Copyright (c) 1998-2002,2006 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.68 2006/10/14 20:45:16 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[2]; 101 char *s = 0; 102 103 if (code == 0 || (s = strchr(dirnames, code)) == 0) 104 _nc_err_abort("Illegal terminfo subdirectory \"%c\"", code); 105 106 if (verified[s - dirnames]) 107 return; 108 109 dir[0] = code; 110 dir[1] = '\0'; 111 if (make_db_root(dir) < 0) { 112 _nc_err_abort("%s/%s: permission denied", _nc_tic_dir(0), dir); 113 } 114 115 verified[s - dirnames] = TRUE; 116 } 117 #endif /* !USE_HASHED_DB */ 118 119 static int 120 make_db_path(char *dst, const char *src, unsigned limit) 121 { 122 int rc = -1; 123 const char *top = _nc_tic_dir(0); 124 125 if (src == top || _nc_is_abs_path(src)) { 126 if (strlen(src) + 1 <= limit) { 127 (void) strcpy(dst, src); 128 rc = 0; 129 } 130 } else { 131 if (strlen(top) + strlen(src) + 2 <= limit) { 132 (void) sprintf(dst, "%s/%s", top, src); 133 rc = 0; 134 } 135 } 136 #if USE_HASHED_DB 137 if (rc == 0) { 138 if (_nc_is_dir_path(dst)) { 139 rc = -1; 140 } else { 141 unsigned have = strlen(dst); 142 if (have > 3 && strcmp(dst + have - 3, DBM_SUFFIX)) { 143 if (have + 3 <= limit) 144 strcat(dst, DBM_SUFFIX); 145 else 146 rc = -1; 147 } 148 } 149 } 150 #endif 151 return rc; 152 } 153 154 /* 155 * Make a database-root if it doesn't exist. 156 */ 157 static int 158 make_db_root(const char *path) 159 { 160 int rc; 161 char fullpath[PATH_MAX]; 162 163 if ((rc = make_db_path(fullpath, path, sizeof(fullpath))) == 0) { 164 #if USE_HASHED_DB 165 DB *capdbp; 166 167 if ((capdbp = _nc_db_open(fullpath, TRUE)) == NULL) 168 rc = -1; 169 else if (_nc_db_close(capdbp) < 0) 170 rc = -1; 171 #else 172 struct stat statbuf; 173 174 if ((rc = stat(path, &statbuf)) < 0) { 175 rc = mkdir(path, 0777); 176 } else if (_nc_access(path, R_OK | W_OK | X_OK) < 0) { 177 rc = -1; /* permission denied */ 178 } else if (!(S_ISDIR(statbuf.st_mode))) { 179 rc = -1; /* not a directory */ 180 } 181 #endif 182 } 183 return rc; 184 } 185 186 /* 187 * Set the write directory for compiled entries. 188 */ 189 NCURSES_EXPORT(void) 190 _nc_set_writedir(char *dir) 191 { 192 const char *destination; 193 char actual[PATH_MAX]; 194 195 if (dir == 0 196 && use_terminfo_vars()) 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 279 (void) strcpy(name_list, tp->term_names); 280 DEBUG(7, ("Name list = '%s'", name_list)); 281 282 first_name = name_list; 283 284 ptr = &name_list[strlen(name_list) - 1]; 285 other_names = ptr + 1; 286 287 while (ptr > name_list && *ptr != '|') 288 ptr--; 289 290 if (ptr != name_list) { 291 *ptr = '\0'; 292 293 for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++) 294 continue; 295 296 if (*ptr == '\0') 297 other_names = ptr; 298 else { 299 *ptr = '\0'; 300 other_names = ptr + 1; 301 } 302 } 303 304 DEBUG(7, ("First name = '%s'", first_name)); 305 DEBUG(7, ("Other names = '%s'", other_names)); 306 307 _nc_set_type(first_name); 308 309 #if USE_HASHED_DB 310 if (write_object(tp, buffer + 1, &offset, limit - 1) != ERR) { 311 DB *capdb = _nc_db_open(_nc_tic_dir(0), TRUE); 312 DBT key, data; 313 314 if (capdb != 0) { 315 buffer[0] = 0; 316 317 memset(&key, 0, sizeof(key)); 318 key.data = tp->term_names; 319 key.size = strlen(tp->term_names); 320 321 memset(&data, 0, sizeof(data)); 322 data.data = buffer; 323 data.size = offset + 1; 324 325 _nc_db_put(capdb, &key, &data); 326 327 buffer[0] = 2; 328 329 key.data = name_list; 330 key.size = strlen(name_list); 331 332 strcpy(buffer + 1, tp->term_names); 333 data.size = strlen(tp->term_names) + 1; 334 335 _nc_db_put(capdb, &key, &data); 336 337 while (*other_names != '\0') { 338 ptr = other_names++; 339 while (*other_names != '|' && *other_names != '\0') 340 other_names++; 341 342 if (*other_names != '\0') 343 *(other_names++) = '\0'; 344 345 key.data = ptr; 346 key.size = strlen(ptr); 347 348 _nc_db_put(capdb, &key, &data); 349 } 350 _nc_db_close(capdb); 351 } 352 } 353 #else /* !USE_HASHED_DB */ 354 if (call_count++ == 0) { 355 start_time = 0; 356 } 357 358 if (strlen(first_name) > sizeof(filename) - 3) 359 _nc_warning("terminal name too long."); 360 361 sprintf(filename, "%c/%s", first_name[0], first_name); 362 363 /* 364 * Has this primary name been written since the first call to 365 * write_entry()? If so, the newer write will step on the older, 366 * so warn the user. 367 */ 368 if (start_time > 0 && 369 stat(filename, &statbuf) >= 0 370 && statbuf.st_mtime >= start_time) { 371 _nc_warning("name multiply defined."); 372 } 373 374 check_writeable(first_name[0]); 375 write_file(filename, tp); 376 377 if (start_time == 0) { 378 if (stat(filename, &statbuf) < 0 379 || (start_time = statbuf.st_mtime) == 0) { 380 _nc_syserr_abort("error obtaining time from %s/%s", 381 _nc_tic_dir(0), filename); 382 } 383 } 384 while (*other_names != '\0') { 385 ptr = other_names++; 386 while (*other_names != '|' && *other_names != '\0') 387 other_names++; 388 389 if (*other_names != '\0') 390 *(other_names++) = '\0'; 391 392 if (strlen(ptr) > sizeof(linkname) - 3) { 393 _nc_warning("terminal alias %s too long.", ptr); 394 continue; 395 } 396 if (strchr(ptr, '/') != 0) { 397 _nc_warning("cannot link alias %s.", ptr); 398 continue; 399 } 400 401 check_writeable(ptr[0]); 402 sprintf(linkname, "%c/%s", ptr[0], ptr); 403 404 if (strcmp(filename, linkname) == 0) { 405 _nc_warning("self-synonym ignored"); 406 } else if (stat(linkname, &statbuf) >= 0 && 407 statbuf.st_mtime < start_time) { 408 _nc_warning("alias %s multiply defined.", ptr); 409 } else if (_nc_access(linkname, W_OK) == 0) 410 #if HAVE_LINK 411 { 412 int code; 413 #if USE_SYMLINKS 414 strcpy(symlinkname, "../"); 415 strncat(symlinkname, filename, sizeof(symlinkname) - 4); 416 symlinkname[sizeof(symlinkname) - 1] = '\0'; 417 #endif /* USE_SYMLINKS */ 418 #if HAVE_REMOVE 419 code = remove(linkname); 420 #else 421 code = unlink(linkname); 422 #endif 423 if (code != 0 && errno == ENOENT) 424 code = 0; 425 #if USE_SYMLINKS 426 if (symlink(symlinkname, linkname) < 0) 427 #else 428 if (link(filename, linkname) < 0) 429 #endif /* USE_SYMLINKS */ 430 { 431 /* 432 * If there wasn't anything there, and we cannot 433 * link to the target because it is the same as the 434 * target, then the source must be on a filesystem 435 * that uses caseless filenames, such as Win32, etc. 436 */ 437 if (code == 0 && errno == EEXIST) 438 _nc_warning("can't link %s to %s", filename, linkname); 439 else if (code == 0 && (errno == EPERM || errno == ENOENT)) 440 write_file(linkname, tp); 441 else { 442 #if MIXEDCASE_FILENAMES 443 _nc_syserr_abort("can't link %s to %s", filename, linkname); 444 #else 445 _nc_warning("can't link %s to %s (errno=%d)", filename, 446 linkname, errno); 447 #endif 448 } 449 } else { 450 DEBUG(1, ("Linked %s", linkname)); 451 } 452 } 453 #else /* just make copies */ 454 write_file(linkname, tp); 455 #endif /* HAVE_LINK */ 456 } 457 #endif /* USE_HASHED_DB */ 458 } 459 460 static unsigned 461 fake_write(char *dst, 462 unsigned *offset, 463 unsigned limit, 464 char *src, 465 unsigned want, 466 unsigned size) 467 { 468 int have = (limit - *offset); 469 470 want *= size; 471 if (have > 0) { 472 if ((int) want > have) 473 want = have; 474 memcpy(dst + *offset, src, want); 475 *offset += want; 476 } else { 477 want = 0; 478 } 479 return (int) (want / size); 480 } 481 482 #define Write(buf, size, count) fake_write(buffer, offset, limit, (char *) buf, count, size) 483 484 #undef LITTLE_ENDIAN /* BSD/OS defines this as a feature macro */ 485 #define HI(x) ((x) / 256) 486 #define LO(x) ((x) % 256) 487 #define LITTLE_ENDIAN(p, x) (p)[0] = LO(x), (p)[1] = HI(x) 488 489 #define WRITE_STRING(str) (Write(str, sizeof(char), strlen(str) + 1) == strlen(str) + 1) 490 491 static int 492 compute_offsets(char **Strings, unsigned strmax, short *offsets) 493 { 494 size_t nextfree = 0; 495 unsigned i; 496 497 for (i = 0; i < strmax; i++) { 498 if (Strings[i] == ABSENT_STRING) { 499 offsets[i] = -1; 500 } else if (Strings[i] == CANCELLED_STRING) { 501 offsets[i] = -2; 502 } else { 503 offsets[i] = nextfree; 504 nextfree += strlen(Strings[i]) + 1; 505 TRACE_OUT(("put Strings[%d]=%s(%d)", i, _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", 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", 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