167468Snon/**************************************************************************** 267468Snon * Copyright 2019,2020 Thomas E. Dickey * 367468Snon * Copyright 1998-2017,2018 Free Software Foundation, Inc. * 467468Snon * * 567468Snon * Permission is hereby granted, free of charge, to any person obtaining a * 667468Snon * copy of this software and associated documentation files (the * 767468Snon * "Software"), to deal in the Software without restriction, including * 867468Snon * without limitation the rights to use, copy, modify, merge, publish, * 967468Snon * distribute, distribute with modifications, sublicense, and/or sell * 1067468Snon * copies of the Software, and to permit persons to whom the Software is * 1167468Snon * furnished to do so, subject to the following conditions: * 1267468Snon * * 1367468Snon * The above copyright notice and this permission notice shall be included * 1467468Snon * in all copies or substantial portions of the Software. * 1567468Snon * * 1667468Snon * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1767468Snon * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1867468Snon * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1967468Snon * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 2067468Snon * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2167468Snon * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2267468Snon * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2367468Snon * * 2467468Snon * Except as contained in this notice, the name(s) of the above copyright * 2567468Snon * holders shall not be used in advertising or otherwise to promote the * 2667468Snon * sale, use or other dealings in this Software without prior written * 2767468Snon * authorization. * 2867468Snon ****************************************************************************/ 2967468Snon 3067468Snon/**************************************************************************** 3167468Snon * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3267468Snon * and: Eric S. Raymond <esr@snark.thyrsus.com> * 3367468Snon * and: Thomas E. Dickey 1996 on * 3467468Snon * and: Juergen Pfeifer 2009 * 3567468Snon ****************************************************************************/ 3667468Snon 3767468Snon#include <curses.priv.h> 3867468Snon 3967468Snon#include <ctype.h> 4067468Snon 4167468Snon#ifndef CUR 4267468Snon#define CUR SP_TERMTYPE 4367468Snon#endif 4467468Snon 4567468SnonMODULE_ID("$Id: lib_screen.c,v 1.100 2020/05/25 22:48:41 tom Exp $") 4667468Snon 4767468Snon#define MAX_SIZE 0x3fff /* 16k is big enough for a window or pad */ 4867468Snon 4967468Snon#define MARKER '\\' 5067468Snon#define APPEND '+' 5167468Snon#define GUTTER '|' 5267468Snon#define L_CURL '{' 5367468Snon#define R_CURL '}' 5467468Snon 5567468Snon#if USE_STRING_HACKS && HAVE_SNPRINTF 5667468Snon#define ARG_SLIMIT(name) size_t name, 5767468Snon#else 5867468Snon#define ARG_SLIMIT(name) /* nothing */ 5967468Snon#endif 6067468Snon 6167468Snon#define CUR_SLIMIT _nc_SLIMIT(limit - (size_t) (target - base)) 6267468Snon#define TOP_SLIMIT _nc_SLIMIT(sizeof(buffer)) 6367468Snon 6467468Snon/* 6567468Snon * Use 0x888888 as the magic number for new-format files, since it cannot be 6667848Snon * mistaken for the _cury/_curx pair of 16-bit numbers which start the old 6767468Snon * format. It happens to be unused in the file 5.22 database (2015/03/07). 6867468Snon */ 6967468Snonstatic const char my_magic[] = 7067468Snon{'\210', '\210', '\210', '\210', 0}; 7170834Swollman 7267468Snon#if NCURSES_EXT_PUTWIN 7370834Swollmantypedef enum { 7467468Snon pINT /* int */ 7567468Snon ,pSHORT /* short */ 7667468Snon ,pBOOL /* bool */ 7767468Snon ,pATTR /* attr_t */ 7867468Snon ,pCHAR /* chtype */ 7967468Snon ,pSIZE /* NCURSES_SIZE_T */ 8092739Salfred#if NCURSES_WIDECHAR 8167468Snon ,pCCHAR /* cchar_t */ 8267468Snon#endif 8367468Snon} PARAM_TYPE; 8467468Snon 8567468Snontypedef struct { 8667468Snon const char name[11]; 8767468Snon attr_t attr; 8867468Snon} SCR_ATTRS; 8967468Snon 9067468Snontypedef struct { 9167468Snon const char name[17]; 9267468Snon PARAM_TYPE type; 9367468Snon size_t size; 9467468Snon size_t offset; 9567468Snon} SCR_PARAMS; 9667468Snon 9767468Snon#define DATA(name) { { #name }, A_##name } 9867468Snonstatic const SCR_ATTRS scr_attrs[] = 9967468Snon{ 10067468Snon DATA(NORMAL), 10167468Snon DATA(STANDOUT), 10267468Snon DATA(UNDERLINE), 10367468Snon DATA(REVERSE), 10467468Snon DATA(BLINK), 10567468Snon DATA(DIM), 10673087Snon DATA(BOLD), 10773087Snon DATA(ALTCHARSET), 10873087Snon DATA(INVIS), 10973087Snon DATA(PROTECT), 11073087Snon DATA(HORIZONTAL), 11167468Snon DATA(LEFT), 11267468Snon DATA(LOW), 11367468Snon DATA(RIGHT), 11467468Snon DATA(TOP), 11567468Snon DATA(VERTICAL), 11667468Snon 11767468Snon#ifdef A_ITALIC 11867468Snon DATA(ITALIC), 11967468Snon#endif 12067468Snon}; 12167468Snon#undef DATA 12267468Snon 12367468Snon#define sizeof2(type,name) sizeof(((type *)0)->name) 12467468Snon#define DATA(name, type) { { #name }, type, sizeof2(WINDOW, name), offsetof(WINDOW, name) } 12567468Snon 12667468Snonstatic const SCR_PARAMS scr_params[] = 12773087Snon{ 12867468Snon DATA(_cury, pSIZE), 12967468Snon DATA(_curx, pSIZE), 13067468Snon DATA(_maxy, pSIZE), 13167468Snon DATA(_maxx, pSIZE), 13267468Snon DATA(_begy, pSIZE), 13367468Snon DATA(_begx, pSIZE), 13473087Snon DATA(_flags, pSHORT), 13573087Snon DATA(_attrs, pATTR), 13673087Snon DATA(_bkgd, pCHAR), 13773087Snon DATA(_notimeout, pBOOL), 13873087Snon DATA(_clear, pBOOL), 13967468Snon DATA(_leaveok, pBOOL), 14067468Snon DATA(_scroll, pBOOL), 14173087Snon DATA(_idlok, pBOOL), 14273087Snon DATA(_idcok, pBOOL), 14367468Snon DATA(_immed, pBOOL), 14467468Snon DATA(_sync, pBOOL), 14567468Snon DATA(_use_keypad, pBOOL), 14667468Snon DATA(_delay, pINT), 14767468Snon DATA(_regtop, pSIZE), 14873087Snon DATA(_regbottom, pSIZE), 14973087Snon DATA(_pad._pad_y, pSIZE), 15073087Snon DATA(_pad._pad_x, pSIZE), 15173087Snon DATA(_pad._pad_top, pSIZE), 15273087Snon DATA(_pad._pad_left, pSIZE), 15373087Snon DATA(_pad._pad_bottom, pSIZE), 15473087Snon DATA(_pad._pad_right, pSIZE), 15573087Snon DATA(_yoffset, pSIZE), 15673087Snon#if NCURSES_WIDECHAR 15773087Snon DATA(_bkgrnd, pCCHAR), 15873087Snon#if NCURSES_EXT_COLORS 15973087Snon DATA(_color, pINT), 16073087Snon#endif 16173087Snon#endif 16267468Snon}; 16367468Snon#undef DATA 16467468Snon 16567468Snonstatic const NCURSES_CH_T blank = NewChar(BLANK_TEXT); 16667468Snon 16767468Snon/* 16867468Snon * Allocate and read a line of text. Caller must free it. 16967468Snon */ 17067468Snonstatic char * 17167468Snonread_txt(FILE *fp) 17267468Snon{ 17367468Snon size_t limit = 1024; 17467468Snon char *result = malloc(limit); 17567468Snon char *buffer; 17667468Snon 17767468Snon if (result != 0) { 17867468Snon int ch = 0; 17967468Snon size_t used = 0; 18067468Snon 18167468Snon clearerr(fp); 18282922Snon result[used] = '\0'; 18367468Snon do { 18467468Snon if (used + 2 >= limit) { 18567468Snon limit += 1024; 18667468Snon buffer = realloc(result, limit); 18767468Snon if (buffer == 0) { 18867468Snon free(result); 18967468Snon result = 0; 19067468Snon break; 19167468Snon } 19267468Snon result = buffer; 19367468Snon } 19467468Snon ch = fgetc(fp); 19567468Snon if (ch == EOF) 19667468Snon break; 19767468Snon result[used++] = (char) ch; 19867468Snon result[used] = '\0'; 19967468Snon } while (ch != '\n'); 20067468Snon 20167468Snon if (ch == '\n') { 20267468Snon result[--used] = '\0'; 20367468Snon T(("READ:%s", result)); 20467468Snon } else if (used == 0) { 20567468Snon free(result); 20667468Snon result = 0; 20767468Snon } 20867468Snon } 20967468Snon return result; 21067468Snon} 21167468Snon 21267468Snonstatic char * 21367468Snondecode_attr(char *source, attr_t *target, int *color) 21467468Snon{ 21567468Snon bool found = FALSE; 21667468Snon 21767468Snon T(("decode_attr '%s'", source)); 21867468Snon 21967468Snon while (*source) { 22067468Snon if (source[0] == MARKER && source[1] == L_CURL) { 22167468Snon source += 2; 22267468Snon found = TRUE; 22367468Snon } else if (source[0] == R_CURL) { 22467468Snon source++; 22573280Smarkm found = FALSE; 22667468Snon } else if (found) { 22767468Snon size_t n; 22867468Snon char *next = source; 22967468Snon 23067468Snon if (source[0] == GUTTER) { 23167468Snon ++next; 23267468Snon } else if (*next == 'C') { 23367468Snon int value = 0; 23467468Snon unsigned pair; 23567468Snon next++; 23667468Snon while (isdigit(UChar(*next))) { 23767468Snon value = value * 10 + (*next++ - '0'); 23867468Snon } 23967468Snon *target &= ~A_COLOR; 24067468Snon pair = (unsigned) ((value > 256) 24167468Snon ? COLOR_PAIR(255) 24267468Snon : COLOR_PAIR(value)); 24367468Snon *target |= pair; 24467468Snon *color = value; 24567468Snon } else { 24667468Snon while (isalnum(UChar(*next))) { 24767468Snon ++next; 24867468Snon } 24967468Snon for (n = 0; n < SIZEOF(scr_attrs); ++n) { 25067468Snon if ((size_t) (next - source) == strlen(scr_attrs[n].name)) { 25167468Snon if (scr_attrs[n].attr) { 25267468Snon *target |= scr_attrs[n].attr; 25367468Snon } else { 25467468Snon *target = A_NORMAL; 25567468Snon } 25667468Snon break; 25767468Snon } 25867468Snon } 25967468Snon } 26067468Snon source = next; 26167468Snon } else { 26267468Snon break; 26367468Snon } 26487983Snon } 26567468Snon return source; 26667468Snon} 26767468Snon 26867468Snonstatic char * 26967468Snondecode_char(char *source, int *target) 27067468Snon{ 27179697Snon int limit = 0; 27267468Snon int base = 16; 27367468Snon const char digits[] = "0123456789abcdef"; 27479697Snon 27567468Snon T(("decode_char '%s'", source)); 27667468Snon *target = ' '; 27779697Snon switch (*source) { 27867468Snon case MARKER: 27967468Snon switch (*++source) { 28067468Snon case APPEND: 28167468Snon break; 28267468Snon case MARKER: 28367468Snon *target = MARKER; 28467468Snon ++source; 28567468Snon break; 28667468Snon case 's': 28767468Snon *target = ' '; 28867468Snon ++source; 28967468Snon break; 29067468Snon case '0': 29167468Snon case '1': 29267468Snon case '2': 29367468Snon case '3': 29467468Snon base = 8; 29567468Snon limit = 3; 29667468Snon break; 29767468Snon case 'u': 29867468Snon limit = 4; 29967468Snon ++source; 30079697Snon break; 30167468Snon case 'U': 30267468Snon limit = 8; 30367468Snon ++source; 30467468Snon break; 30567468Snon } 30667468Snon if (limit) { 30767468Snon *target = 0; 30867468Snon while (limit-- > 0) { 30967468Snon char *find = strchr(digits, *source++); 31067468Snon int ch = (find != 0) ? (int) (find - digits) : -1; 31167468Snon *target *= base; 31267468Snon if (ch >= 0 && ch < base) { 31367468Snon *target += ch; 31467468Snon } 31567468Snon } 31667468Snon } 31767468Snon break; 31879697Snon default: 31967468Snon *target = *source++; 32079697Snon break; 32167468Snon } 32267468Snon return source; 32367468Snon} 324 325static char * 326decode_chtype(char *source, chtype fillin, chtype *target) 327{ 328 attr_t attr = ChAttrOf(fillin); 329 int color = PAIR_NUMBER((int) attr); 330 int value; 331 332 T(("decode_chtype '%s'", source)); 333 source = decode_attr(source, &attr, &color); 334 source = decode_char(source, &value); 335 *target = (ChCharOf(value) | attr | (chtype) COLOR_PAIR(color)); 336 /* FIXME - ignore combining characters */ 337 return source; 338} 339 340#if NCURSES_WIDECHAR 341static char * 342decode_cchar(char *source, cchar_t *fillin, cchar_t *target) 343{ 344 int color; 345 attr_t attr = fillin->attr; 346 wchar_t chars[CCHARW_MAX]; 347 int append = 0; 348 int value = 0; 349 350 T(("decode_cchar '%s'", source)); 351 *target = blank; 352#if NCURSES_EXT_COLORS 353 color = fillin->ext_color; 354#else 355 color = (int) PAIR_NUMBER(attr); 356#endif 357 source = decode_attr(source, &attr, &color); 358 memset(chars, 0, sizeof(chars)); 359 source = decode_char(source, &value); 360 chars[0] = (wchar_t) value; 361 /* handle combining characters */ 362 while (source[0] == MARKER && source[1] == APPEND) { 363 source += 2; 364 source = decode_char(source, &value); 365 if (++append < CCHARW_MAX) { 366 chars[append] = (wchar_t) value; 367 } 368 } 369 setcchar(target, chars, attr, (short) color, &color); 370 return source; 371} 372#endif 373 374static int 375read_win(WINDOW *win, FILE *fp) 376{ 377 int code = ERR; 378 size_t n; 379 int color; 380#if NCURSES_WIDECHAR 381 NCURSES_CH_T prior; 382#endif 383 chtype prior2; 384 385 memset(win, 0, sizeof(WINDOW)); 386 for (;;) { 387 char *name; 388 char *value; 389 char *txt = read_txt(fp); 390 391 if (txt == 0) 392 break; 393 if (!strcmp(txt, "rows:")) { 394 free(txt); 395 code = OK; 396 break; 397 } 398 if ((value = strchr(txt, '=')) == 0) { 399 free(txt); 400 continue; 401 } 402 *value++ = '\0'; 403 name = !strcmp(txt, "flag") ? value : txt; 404 for (n = 0; n < SIZEOF(scr_params); ++n) { 405 if (!strcmp(name, scr_params[n].name)) { 406 void *data = (void *) ((char *) win + scr_params[n].offset); 407 408 switch (scr_params[n].type) { 409 case pATTR: 410 (void) decode_attr(value, data, &color); 411 break; 412 case pBOOL: 413 *(bool *) data = TRUE; 414 break; 415 case pCHAR: 416 prior2 = ' '; 417 decode_chtype(value, prior2, data); 418 break; 419 case pINT: 420 *(int *) data = atoi(value); 421 break; 422 case pSHORT: 423 *(short *) data = (short) atoi(value); 424 break; 425 case pSIZE: 426 *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value); 427 break; 428#if NCURSES_WIDECHAR 429 case pCCHAR: 430 prior = blank; 431 decode_cchar(value, &prior, data); 432 break; 433#endif 434 } 435 break; 436 } 437 } 438 free(txt); 439 } 440 return code; 441} 442 443static int 444read_row(char *source, NCURSES_CH_T *prior, NCURSES_CH_T *target, int length) 445{ 446 while (*source != '\0' && length > 0) { 447#if NCURSES_WIDECHAR 448 int len; 449 450 source = decode_cchar(source, prior, target); 451 len = _nc_wacs_width(target->chars[0]); 452 if (len > 1) { 453 int n; 454 455 SetWidecExt(CHDEREF(target), 0); 456 for (n = 1; n < len; ++n) { 457 target[n] = target[0]; 458 SetWidecExt(CHDEREF(target), n); 459 } 460 target += (len - 1); 461 length -= (len - 1); 462 } 463#else 464 source = decode_chtype(source, *prior, target); 465#endif 466 *prior = *target; 467 ++target; 468 --length; 469 } 470 while (length-- > 0) { 471 *target++ = blank; 472 } 473 /* FIXME - see what error conditions should apply if I need to return ERR */ 474 return 0; 475} 476#endif /* NCURSES_EXT_PUTWIN */ 477 478/* 479 * Originally, getwin/putwin used fread/fwrite, because they used binary data. 480 * The new format uses printable ASCII, which does not have as predictable 481 * sizes. Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need 482 * special handling if we want to read screen dumps from an older library. 483 */ 484static int 485read_block(void *target, size_t length, FILE *fp) 486{ 487 int result = 0; 488 char *buffer = target; 489 490 clearerr(fp); 491 while (length-- != 0) { 492 int ch = fgetc(fp); 493 if (ch == EOF) { 494 result = -1; 495 break; 496 } 497 *buffer++ = (char) ch; 498 } 499 return result; 500} 501 502NCURSES_EXPORT(WINDOW *) 503NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep) 504{ 505 WINDOW tmp, *nwin; 506 bool old_format = FALSE; 507 508 T((T_CALLED("getwin(%p)"), (void *) filep)); 509 510 if (filep == 0) { 511 returnWin(0); 512 } 513 514 /* 515 * Read the first 4 bytes to determine first if this is an old-format 516 * screen-dump, or new-format. 517 */ 518 if (read_block(&tmp, (size_t) 4, filep) < 0) { 519 returnWin(0); 520 } 521 /* 522 * If this is a new-format file, and we do not support it, give up. 523 */ 524 if (!memcmp(&tmp, my_magic, (size_t) 4)) { 525#if NCURSES_EXT_PUTWIN 526 if (read_win(&tmp, filep) < 0) 527#endif 528 returnWin(0); 529 } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) { 530 returnWin(0); 531 } else { 532 old_format = TRUE; 533 } 534 535 /* 536 * Check the window-size: 537 */ 538 if (tmp._maxy == 0 || 539 tmp._maxy > MAX_SIZE || 540 tmp._maxx == 0 || 541 tmp._maxx > MAX_SIZE) { 542 returnWin(0); 543 } 544 545 if (tmp._flags & _ISPAD) { 546 nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx 547 tmp._maxy + 1, 548 tmp._maxx + 1); 549 } else { 550 nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx 551 tmp._maxy + 1, 552 tmp._maxx + 1, 0, 0); 553 } 554 555 /* 556 * We deliberately do not restore the _parx, _pary, or _parent 557 * fields, because the window hierarchy within which they 558 * made sense is probably gone. 559 */ 560 if (nwin != 0) { 561 int n; 562 size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1); 563 564 nwin->_curx = tmp._curx; 565 nwin->_cury = tmp._cury; 566 nwin->_maxy = tmp._maxy; 567 nwin->_maxx = tmp._maxx; 568 nwin->_begy = tmp._begy; 569 nwin->_begx = tmp._begx; 570 nwin->_yoffset = tmp._yoffset; 571 nwin->_flags = tmp._flags & ~(_SUBWIN); 572 573 WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp); 574 nwin->_nc_bkgd = tmp._nc_bkgd; 575 576 nwin->_notimeout = tmp._notimeout; 577 nwin->_clear = tmp._clear; 578 nwin->_leaveok = tmp._leaveok; 579 nwin->_idlok = tmp._idlok; 580 nwin->_idcok = tmp._idcok; 581 nwin->_immed = tmp._immed; 582 nwin->_scroll = tmp._scroll; 583 nwin->_sync = tmp._sync; 584 nwin->_use_keypad = tmp._use_keypad; 585 nwin->_delay = tmp._delay; 586 587 nwin->_regtop = tmp._regtop; 588 nwin->_regbottom = tmp._regbottom; 589 590 if (tmp._flags & _ISPAD) 591 nwin->_pad = tmp._pad; 592 593 if (old_format) { 594 T(("reading old-format screen dump")); 595 for (n = 0; n <= nwin->_maxy; n++) { 596 if (read_block(nwin->_line[n].text, linesize, filep) < 0) { 597 delwin(nwin); 598 returnWin(0); 599 } 600 } 601 } 602#if NCURSES_EXT_PUTWIN 603 else { 604 char *txt = 0; 605 bool success = TRUE; 606 NCURSES_CH_T prior = blank; 607 608 T(("reading new-format screen dump")); 609 for (n = 0; n <= nwin->_maxy; n++) { 610 long row; 611 char *next; 612 613 if ((txt = read_txt(filep)) == 0) { 614 T(("...failed to read string for row %d", n + 1)); 615 success = FALSE; 616 break; 617 } 618 row = strtol(txt, &next, 10); 619 if (row != (n + 1) || *next != ':') { 620 T(("...failed to read row-number %d", n + 1)); 621 success = FALSE; 622 break; 623 } 624 625 if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx 626 + 1) < 0) { 627 T(("...failed to read cells for row %d", n + 1)); 628 success = FALSE; 629 break; 630 } 631 free(txt); 632 txt = 0; 633 } 634 635 if (!success) { 636 free(txt); 637 delwin(nwin); 638 returnWin(0); 639 } 640 } 641#endif 642 touchwin(nwin); 643 } 644 returnWin(nwin); 645} 646 647#if NCURSES_SP_FUNCS 648NCURSES_EXPORT(WINDOW *) 649getwin(FILE *filep) 650{ 651 return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep); 652} 653#endif 654 655#if NCURSES_EXT_PUTWIN 656static void 657encode_attr(char *target, ARG_SLIMIT(limit) 658 attr_t source, 659 attr_t prior, 660 int source_color, 661 int prior_color) 662{ 663#if USE_STRING_HACKS && HAVE_SNPRINTF 664 char *base = target; 665#endif 666 source &= ~A_CHARTEXT; 667 prior &= ~A_CHARTEXT; 668 669 *target = '\0'; 670 if ((source != prior) || (source_color != prior_color)) { 671 size_t n; 672 bool first = TRUE; 673 674 *target++ = MARKER; 675 *target++ = L_CURL; 676 677 for (n = 0; n < SIZEOF(scr_attrs); ++n) { 678 if ((source & scr_attrs[n].attr) != 0 || 679 ((source & ALL_BUT_COLOR) == 0 && 680 (scr_attrs[n].attr == A_NORMAL))) { 681 if (first) { 682 first = FALSE; 683 } else { 684 *target++ = '|'; 685 } 686 _nc_STRCPY(target, scr_attrs[n].name, limit); 687 target += strlen(target); 688 } 689 } 690 if (source_color != prior_color) { 691 if (!first) 692 *target++ = '|'; 693 _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color); 694 target += strlen(target); 695 } 696 697 *target++ = R_CURL; 698 *target = '\0'; 699 } 700} 701 702static void 703encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous) 704{ 705#if USE_STRING_HACKS && HAVE_SNPRINTF 706 char *base = target; 707#endif 708#if NCURSES_WIDECHAR 709 size_t n; 710 int source_pair = GetPair(*source); 711 int previous_pair = GetPair(*previous); 712 713 *target = '\0'; 714 if ((previous->attr != source->attr) || (previous_pair != source_pair)) { 715 encode_attr(target, CUR_SLIMIT 716 source->attr, 717 previous->attr, 718 source_pair, 719 previous_pair); 720 } 721 target += strlen(target); 722#if NCURSES_EXT_COLORS 723 if (previous->ext_color != source->ext_color) { 724 _nc_SPRINTF(target, CUR_SLIMIT 725 "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL); 726 } 727#endif 728 for (n = 0; n < SIZEOF(source->chars); ++n) { 729 unsigned uch = (unsigned) source->chars[n]; 730 if (uch == 0) 731 continue; 732 if (n) { 733 *target++ = MARKER; 734 *target++ = APPEND; 735 } 736 *target++ = MARKER; 737 if (uch > 0xffff) { 738 _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch); 739 } else if (uch > 0xff) { 740 _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch); 741 } else if (uch < 32 || uch >= 127) { 742 _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff); 743 } else { 744 switch (uch) { 745 case ' ': 746 _nc_STRCPY(target, "s", limit); 747 break; 748 case MARKER: 749 *target++ = MARKER; 750 *target = '\0'; 751 break; 752 default: 753 --target; 754 _nc_SPRINTF(target, CUR_SLIMIT "%c", uch); 755 break; 756 } 757 } 758 target += strlen(target); 759 } 760#else 761 chtype ch = CharOfD(source); 762 763 *target = '\0'; 764 if (AttrOfD(previous) != AttrOfD(source)) { 765 encode_attr(target, CUR_SLIMIT 766 AttrOfD(source), 767 AttrOfD(previous), 768 GetPair(source), 769 GetPair(previous)); 770 } 771 target += strlen(target); 772 *target++ = MARKER; 773 if (ch < 32 || ch >= 127) { 774 _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch)); 775 } else { 776 switch (ch) { 777 case ' ': 778 _nc_STRCPY(target, "s", limit); 779 break; 780 case MARKER: 781 *target++ = MARKER; 782 *target = '\0'; 783 break; 784 default: 785 --target; 786 _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch)); 787 break; 788 } 789 } 790#endif 791} 792#endif 793 794NCURSES_EXPORT(int) 795putwin(WINDOW *win, FILE *filep) 796{ 797 int code = ERR; 798 799 T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep)); 800 801#if NCURSES_EXT_PUTWIN 802 if (win != 0) { 803 const char *version = curses_version(); 804 char buffer[1024]; 805 NCURSES_CH_T last_cell; 806 int y; 807 808 memset(&last_cell, 0, sizeof(last_cell)); 809 810 clearerr(filep); 811 812 /* 813 * Our magic number is technically nonprinting, but aside from that, 814 * all of the file is printable ASCII. 815 */ 816#define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code) 817 PUTS(my_magic); 818 PUTS(version); 819 PUTS("\n"); 820 for (y = 0; y < (int) SIZEOF(scr_params); ++y) { 821 const char *name = scr_params[y].name; 822 const char *data = (char *) win + scr_params[y].offset; 823 const void *dp = (const void *) data; 824 attr_t attr; 825 826 *buffer = '\0'; 827 if (!strncmp(name, "_pad.", (size_t) 5) && !(win->_flags & _ISPAD)) { 828 continue; 829 } 830 switch (scr_params[y].type) { 831 case pATTR: 832 attr = (*(const attr_t *) dp) & ~A_CHARTEXT; 833 encode_attr(buffer, TOP_SLIMIT 834 (*(const attr_t *) dp) & ~A_CHARTEXT, 835 A_NORMAL, 836 COLOR_PAIR((int) attr), 837 0); 838 break; 839 case pBOOL: 840 if (!(*(const bool *) data)) { 841 continue; 842 } 843 _nc_STRCPY(buffer, name, sizeof(buffer)); 844 name = "flag"; 845 break; 846 case pCHAR: 847 attr = (*(const attr_t *) dp); 848 encode_attr(buffer, TOP_SLIMIT 849 * (const attr_t *) dp, 850 A_NORMAL, 851 COLOR_PAIR((int) attr), 852 0); 853 break; 854 case pINT: 855 if (!(*(const int *) dp)) 856 continue; 857 _nc_SPRINTF(buffer, TOP_SLIMIT 858 "%d", *(const int *) dp); 859 break; 860 case pSHORT: 861 if (!(*(const short *) dp)) 862 continue; 863 _nc_SPRINTF(buffer, TOP_SLIMIT 864 "%d", *(const short *) dp); 865 break; 866 case pSIZE: 867 if (!(*(const NCURSES_SIZE_T *) dp)) 868 continue; 869 _nc_SPRINTF(buffer, TOP_SLIMIT 870 "%d", *(const NCURSES_SIZE_T *) dp); 871 break; 872#if NCURSES_WIDECHAR 873 case pCCHAR: 874 encode_cell(buffer, TOP_SLIMIT 875 (CARG_CH_T) dp, CHREF(last_cell)); 876 break; 877#endif 878 } 879 /* 880 * Only write non-default data. 881 */ 882 if (*buffer != '\0') { 883 if (fprintf(filep, "%s=%s\n", name, buffer) <= 0 884 || ferror(filep)) 885 returnCode(code); 886 } 887 } 888 /* Write row-data */ 889 fprintf(filep, "rows:\n"); 890 for (y = 0; y <= win->_maxy; y++) { 891 NCURSES_CH_T *data = win->_line[y].text; 892 int x; 893 if (fprintf(filep, "%d:", y + 1) <= 0 894 || ferror(filep)) 895 returnCode(code); 896 for (x = 0; x <= win->_maxx; x++) { 897#if NCURSES_WIDECHAR 898 int len = _nc_wacs_width(data[x].chars[0]); 899 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell)); 900 last_cell = data[x]; 901 PUTS(buffer); 902 if (len > 1) 903 x += (len - 1); 904#else 905 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell)); 906 last_cell = data[x]; 907 PUTS(buffer); 908#endif 909 } 910 PUTS("\n"); 911 } 912 code = OK; 913 } 914#else 915 /* 916 * This is the original putwin(): 917 * A straight binary dump is simple, but its format can depend on whether 918 * ncurses is compiled with wide-character support, and also may depend 919 * on the version of ncurses, e.g., if the WINDOW structure is extended. 920 */ 921 if (win != 0) { 922 size_t len = (size_t) (win->_maxx + 1); 923 int y; 924 925 clearerr(filep); 926 if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1 927 || ferror(filep)) 928 returnCode(code); 929 930 for (y = 0; y <= win->_maxy; y++) { 931 if (fwrite(win->_line[y].text, 932 sizeof(NCURSES_CH_T), len, filep) != len 933 || ferror(filep)) { 934 returnCode(code); 935 } 936 } 937 code = OK; 938 } 939#endif 940 returnCode(code); 941} 942 943NCURSES_EXPORT(int) 944NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file) 945{ 946 FILE *fp = 0; 947 int code = ERR; 948 949 T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file))); 950 951 if (_nc_access(file, R_OK) >= 0 952 && (fp = fopen(file, BIN_R)) != 0) { 953 delwin(NewScreen(SP_PARM)); 954 NewScreen(SP_PARM) = getwin(fp); 955#if !USE_REENTRANT 956 newscr = NewScreen(SP_PARM); 957#endif 958 (void) fclose(fp); 959 if (NewScreen(SP_PARM) != 0) { 960 code = OK; 961 } 962 } 963 returnCode(code); 964} 965 966#if NCURSES_SP_FUNCS 967NCURSES_EXPORT(int) 968scr_restore(const char *file) 969{ 970 return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file); 971} 972#endif 973 974NCURSES_EXPORT(int) 975scr_dump(const char *file) 976{ 977 int result; 978 FILE *fp = 0; 979 980 T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file))); 981 982 if (_nc_access(file, W_OK) < 0 983 || (fp = fopen(file, BIN_W)) == 0) { 984 result = ERR; 985 } else { 986 (void) putwin(newscr, fp); 987 (void) fclose(fp); 988 result = OK; 989 } 990 returnCode(result); 991} 992 993NCURSES_EXPORT(int) 994NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file) 995{ 996 int code = ERR; 997 998 T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file))); 999 1000 if (SP_PARM != 0 && 1001#ifdef USE_TERM_DRIVER 1002 InfoOf(SP_PARM).caninit 1003#else 1004 !(exit_ca_mode && non_rev_rmcup) 1005#endif 1006 ) { 1007 FILE *fp = 0; 1008 1009 if (_nc_access(file, R_OK) >= 0 1010 && (fp = fopen(file, BIN_R)) != 0) { 1011 delwin(CurScreen(SP_PARM)); 1012 CurScreen(SP_PARM) = getwin(fp); 1013#if !USE_REENTRANT 1014 curscr = CurScreen(SP_PARM); 1015#endif 1016 (void) fclose(fp); 1017 if (CurScreen(SP_PARM) != 0) { 1018 code = OK; 1019 } 1020 } 1021 } 1022 returnCode(code); 1023} 1024 1025#if NCURSES_SP_FUNCS 1026NCURSES_EXPORT(int) 1027scr_init(const char *file) 1028{ 1029 return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file); 1030} 1031#endif 1032 1033NCURSES_EXPORT(int) 1034NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file) 1035{ 1036 int code = ERR; 1037 1038 T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file))); 1039 1040 if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) { 1041 delwin(NewScreen(SP_PARM)); 1042 NewScreen(SP_PARM) = dupwin(curscr); 1043#if !USE_REENTRANT 1044 newscr = NewScreen(SP_PARM); 1045#endif 1046 if (NewScreen(SP_PARM) != 0) { 1047 code = OK; 1048 } 1049 } 1050 returnCode(code); 1051} 1052 1053#if NCURSES_SP_FUNCS 1054NCURSES_EXPORT(int) 1055scr_set(const char *file) 1056{ 1057 return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file); 1058} 1059#endif 1060