ncurses 6.1 - patch 20191207
[ncurses.git] / ncurses / base / lib_screen.c
index 3038393c226676bb3957f43c7a72642a3794e61e..9e942eaf8494013f9c240551302c1121133cab70 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * Copyright (c) 1998 Free Software Foundation, Inc.                        *
+ * Copyright (c) 1998-2018,2019 Free Software Foundation, Inc.              *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
 /****************************************************************************
  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
+ *     and: Thomas E. Dickey                        1996 on                 *
+ *     and: Juergen Pfeifer                         2009                    *
  ****************************************************************************/
 
-
 #include <curses.priv.h>
 
-#include <sys/stat.h>
-#include <time.h>
-#include <term.h>      /* exit_ca_mode, non_rev_rmcup */
+#include <ctype.h>
+
+#ifndef CUR
+#define CUR SP_TERMTYPE
+#endif
+
+MODULE_ID("$Id: lib_screen.c,v 1.96 2019/07/20 20:23:21 tom Exp $")
+
+#define MAX_SIZE 0x3fff                /* 16k is big enough for a window or pad */
+
+#define MARKER '\\'
+#define APPEND '+'
+#define GUTTER '|'
+#define L_CURL '{'
+#define R_CURL '}'
+
+#if USE_STRING_HACKS && HAVE_SNPRINTF
+#define ARG_SLIMIT(name) size_t name,
+#else
+#define ARG_SLIMIT(name)       /* nothing */
+#endif
 
-MODULE_ID("$Id: lib_screen.c,v 1.15 1999/07/24 20:05:29 tom Exp $")
+#define CUR_SLIMIT _nc_SLIMIT(limit - (target - base))
+#define TOP_SLIMIT _nc_SLIMIT(sizeof(buffer))
 
-static time_t  dumptime;
+/*
+ * Use 0x888888 as the magic number for new-format files, since it cannot be
+ * mistaken for the _cury/_curx pair of 16-bit numbers which start the old
+ * format.  It happens to be unused in the file 5.22 database (2015/03/07).
+ */
+static const char my_magic[] =
+{'\210', '\210', '\210', '\210'};
 
-WINDOW *getwin(FILE *filep)
+#if NCURSES_EXT_PUTWIN
+typedef enum {
+    pINT                       /* int */
+    ,pSHORT                    /* short */
+    ,pBOOL                     /* bool */
+    ,pATTR                     /* attr_t */
+    ,pCHAR                     /* chtype */
+    ,pSIZE                     /* NCURSES_SIZE_T */
+#if NCURSES_WIDECHAR
+    ,pCCHAR                    /* cchar_t */
+#endif
+} PARAM_TYPE;
+
+typedef struct {
+    const char name[11];
+    attr_t attr;
+} SCR_ATTRS;
+
+typedef struct {
+    const char name[17];
+    PARAM_TYPE type;
+    size_t size;
+    size_t offset;
+} SCR_PARAMS;
+
+#define DATA(name) { { #name }, A_##name }
+static const SCR_ATTRS scr_attrs[] =
 {
-       WINDOW  tmp, *nwin;
-       int     n;
+    DATA(NORMAL),
+    DATA(STANDOUT),
+    DATA(UNDERLINE),
+    DATA(REVERSE),
+    DATA(BLINK),
+    DATA(DIM),
+    DATA(BOLD),
+    DATA(ALTCHARSET),
+    DATA(INVIS),
+    DATA(PROTECT),
+    DATA(HORIZONTAL),
+    DATA(LEFT),
+    DATA(LOW),
+    DATA(RIGHT),
+    DATA(TOP),
+    DATA(VERTICAL),
 
-       T((T_CALLED("getwin(%p)"), filep));
+#ifdef A_ITALIC
+    DATA(ITALIC),
+#endif
+};
+#undef DATA
 
-       (void) fread(&tmp, sizeof(WINDOW), 1, filep);
-       if (ferror(filep))
-               returnWin(0);
+#define sizeof2(type,name) sizeof(((type *)0)->name)
+#define DATA(name, type) { { #name }, type, sizeof2(WINDOW, name), offsetof(WINDOW, name) }
 
-       if ((nwin = newwin(tmp._maxy+1, tmp._maxx+1, 0, 0)) == 0)
-               returnWin(0);
+static const SCR_PARAMS scr_params[] =
+{
+    DATA(_cury, pSIZE),
+    DATA(_curx, pSIZE),
+    DATA(_maxy, pSIZE),
+    DATA(_maxx, pSIZE),
+    DATA(_begy, pSIZE),
+    DATA(_begx, pSIZE),
+    DATA(_flags, pSHORT),
+    DATA(_attrs, pATTR),
+    DATA(_bkgd, pCHAR),
+    DATA(_notimeout, pBOOL),
+    DATA(_clear, pBOOL),
+    DATA(_leaveok, pBOOL),
+    DATA(_scroll, pBOOL),
+    DATA(_idlok, pBOOL),
+    DATA(_idcok, pBOOL),
+    DATA(_immed, pBOOL),
+    DATA(_sync, pBOOL),
+    DATA(_use_keypad, pBOOL),
+    DATA(_delay, pINT),
+    DATA(_regtop, pSIZE),
+    DATA(_regbottom, pSIZE),
+    DATA(_pad._pad_y, pSIZE),
+    DATA(_pad._pad_x, pSIZE),
+    DATA(_pad._pad_top, pSIZE),
+    DATA(_pad._pad_left, pSIZE),
+    DATA(_pad._pad_bottom, pSIZE),
+    DATA(_pad._pad_right, pSIZE),
+    DATA(_yoffset, pSIZE),
+#if NCURSES_WIDECHAR
+    DATA(_bkgrnd, pCCHAR),
+#if NCURSES_EXT_COLORS
+    DATA(_color, pINT),
+#endif
+#endif
+};
+#undef DATA
 
-       /*
-        * We deliberately do not restore the _parx, _pary, or _parent
-        * fields, because the window hierarchy within which they
-        * made sense is probably gone.
-        */
-       nwin->_curx       = tmp._curx;
-       nwin->_cury       = tmp._cury;
-       nwin->_maxy       = tmp._maxy;
-       nwin->_maxx       = tmp._maxx;
-       nwin->_begy       = tmp._begy;
-       nwin->_begx       = tmp._begx;
-       nwin->_yoffset    = tmp._yoffset;
-       nwin->_flags      = tmp._flags & ~(_SUBWIN|_ISPAD);
-
-       nwin->_attrs      = tmp._attrs;
-       nwin->_bkgd       = tmp._bkgd;
-
-       nwin->_clear      = tmp._clear;
-       nwin->_scroll     = tmp._scroll;
-       nwin->_leaveok    = tmp._leaveok;
+static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
+
+/*
+ * Allocate and read a line of text.  Caller must free it.
+ */
+static char *
+read_txt(FILE *fp)
+{
+    size_t limit = 1024;
+    char *result = malloc(limit);
+    char *buffer;
+
+    if (result != 0) {
+       int ch = 0;
+       size_t used = 0;
+
+       clearerr(fp);
+       result[used] = '\0';
+       do {
+           if (used + 2 >= limit) {
+               limit += 1024;
+               buffer = realloc(result, limit);
+               if (buffer == 0) {
+                   free(result);
+                   result = 0;
+                   break;
+               }
+               result = buffer;
+           }
+           ch = fgetc(fp);
+           if (ch == EOF)
+               break;
+           result[used++] = (char) ch;
+           result[used] = '\0';
+       } while (ch != '\n');
+
+       if (ch == '\n') {
+           result[--used] = '\0';
+           T(("READ:%s", result));
+       } else if (used == 0) {
+           free(result);
+           result = 0;
+       }
+    }
+    return result;
+}
+
+static char *
+decode_attr(char *source, attr_t *target, int *color)
+{
+    bool found = FALSE;
+
+    T(("decode_attr   '%s'", source));
+
+    while (*source) {
+       if (source[0] == MARKER && source[1] == L_CURL) {
+           source += 2;
+           found = TRUE;
+       } else if (source[0] == R_CURL) {
+           source++;
+           found = FALSE;
+       } else if (found) {
+           size_t n;
+           char *next = source;
+
+           if (source[0] == GUTTER) {
+               ++next;
+           } else if (*next == 'C') {
+               int value = 0;
+               unsigned pair;
+               next++;
+               while (isdigit(UChar(*next))) {
+                   value = value * 10 + (*next++ - '0');
+               }
+               *target &= ~A_COLOR;
+               pair = (unsigned) ((value > 256)
+                                  ? COLOR_PAIR(255)
+                                  : COLOR_PAIR(value));
+               *target |= pair;
+               *color = value;
+           } else {
+               while (isalnum(UChar(*next))) {
+                   ++next;
+               }
+               for (n = 0; n < SIZEOF(scr_attrs); ++n) {
+                   if ((size_t) (next - source) == strlen(scr_attrs[n].name)) {
+                       if (scr_attrs[n].attr) {
+                           *target |= scr_attrs[n].attr;
+                       } else {
+                           *target = A_NORMAL;
+                       }
+                       break;
+                   }
+               }
+           }
+           source = next;
+       } else {
+           break;
+       }
+    }
+    return source;
+}
+
+static char *
+decode_char(char *source, int *target)
+{
+    int limit = 0;
+    int base = 16;
+    const char digits[] = "0123456789abcdef";
+
+    T(("decode_char   '%s'", source));
+    *target = ' ';
+    switch (*source) {
+    case MARKER:
+       switch (*++source) {
+       case APPEND:
+           break;
+       case MARKER:
+           *target = MARKER;
+           ++source;
+           break;
+       case 's':
+           *target = ' ';
+           ++source;
+           break;
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+           base = 8;
+           limit = 3;
+           break;
+       case 'u':
+           limit = 4;
+           ++source;
+           break;
+       case 'U':
+           limit = 8;
+           ++source;
+           break;
+       }
+       if (limit) {
+           *target = 0;
+           while (limit-- > 0) {
+               char *find = strchr(digits, *source++);
+               int ch = (find != 0) ? (int) (find - digits) : -1;
+               *target *= base;
+               if (ch >= 0 && ch < base) {
+                   *target += ch;
+               }
+           }
+       }
+       break;
+    default:
+       *target = *source++;
+       break;
+    }
+    return source;
+}
+
+static char *
+decode_chtype(char *source, chtype fillin, chtype *target)
+{
+    attr_t attr = ChAttrOf(fillin);
+    int color = PAIR_NUMBER((int) attr);
+    int value;
+
+    T(("decode_chtype '%s'", source));
+    source = decode_attr(source, &attr, &color);
+    source = decode_char(source, &value);
+    *target = (ChCharOf(value) | attr | (chtype) COLOR_PAIR(color));
+    /* FIXME - ignore combining characters */
+    return source;
+}
+
+#if NCURSES_WIDECHAR
+static char *
+decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
+{
+    int color;
+    attr_t attr = fillin->attr;
+    wchar_t chars[CCHARW_MAX];
+    int append = 0;
+    int value = 0;
+
+    T(("decode_cchar  '%s'", source));
+    *target = blank;
+#if NCURSES_EXT_COLORS
+    color = fillin->ext_color;
+#else
+    color = (int) PAIR_NUMBER(attr);
+#endif
+    source = decode_attr(source, &attr, &color);
+    memset(chars, 0, sizeof(chars));
+    source = decode_char(source, &value);
+    chars[0] = (wchar_t) value;
+    /* handle combining characters */
+    while (source[0] == MARKER && source[1] == APPEND) {
+       source += 2;
+       source = decode_char(source, &value);
+       if (++append < CCHARW_MAX) {
+           chars[append] = (wchar_t) value;
+       }
+    }
+    setcchar(target, chars, attr, (short) color, &color);
+    return source;
+}
+#endif
+
+static int
+read_win(WINDOW *win, FILE *fp)
+{
+    int code = ERR;
+    size_t n;
+    int color;
+#if NCURSES_WIDECHAR
+    NCURSES_CH_T prior;
+#endif
+    chtype prior2;
+
+    memset(win, 0, sizeof(WINDOW));
+    for (;;) {
+       char *name;
+       char *value;
+       char *txt = read_txt(fp);
+
+       if (txt == 0)
+           break;
+       if (!strcmp(txt, "rows:")) {
+           free(txt);
+           code = OK;
+           break;
+       }
+       if ((value = strchr(txt, '=')) == 0) {
+           free(txt);
+           continue;
+       }
+       *value++ = '\0';
+       name = !strcmp(txt, "flag") ? value : txt;
+       for (n = 0; n < SIZEOF(scr_params); ++n) {
+           if (!strcmp(name, scr_params[n].name)) {
+               void *data = (void *) ((char *) win + scr_params[n].offset);
+
+               switch (scr_params[n].type) {
+               case pATTR:
+                   (void) decode_attr(value, data, &color);
+                   break;
+               case pBOOL:
+                   *(bool *) data = TRUE;
+                   break;
+               case pCHAR:
+                   prior2 = ' ';
+                   decode_chtype(value, prior2, data);
+                   break;
+               case pINT:
+                   *(int *) data = atoi(value);
+                   break;
+               case pSHORT:
+                   *(short *) data = (short) atoi(value);
+                   break;
+               case pSIZE:
+                   *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value);
+                   break;
+#if NCURSES_WIDECHAR
+               case pCCHAR:
+                   prior = blank;
+                   decode_cchar(value, &prior, data);
+                   break;
+#endif
+               }
+               break;
+           }
+       }
+       free(txt);
+    }
+    return code;
+}
+
+static int
+read_row(char *source, NCURSES_CH_T * prior, NCURSES_CH_T * target, int length)
+{
+    while (*source != '\0' && length > 0) {
+#if NCURSES_WIDECHAR
+       int len;
+
+       source = decode_cchar(source, prior, target);
+       len = _nc_wacs_width(target->chars[0]);
+       if (len > 1) {
+           int n;
+
+           SetWidecExt(CHDEREF(target), 0);
+           for (n = 1; n < len; ++n) {
+               target[n] = target[0];
+               SetWidecExt(CHDEREF(target), n);
+           }
+           target += (len - 1);
+           length -= (len - 1);
+       }
+#else
+       source = decode_chtype(source, *prior, target);
+#endif
+       *prior = *target;
+       ++target;
+       --length;
+    }
+    while (length-- > 0) {
+       *target++ = blank;
+    }
+    /* FIXME - see what error conditions should apply if I need to return ERR */
+    return 0;
+}
+#endif /* NCURSES_EXT_PUTWIN */
+
+/*
+ * Originally, getwin/putwin used fread/fwrite, because they used binary data.
+ * The new format uses printable ASCII, which does not have as predictable
+ * sizes.  Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need
+ * special handling if we want to read screen dumps from an older library.
+ */
+static int
+read_block(void *target, size_t length, FILE *fp)
+{
+    int result = 0;
+    char *buffer = target;
+
+    clearerr(fp);
+    while (length-- != 0) {
+       int ch = fgetc(fp);
+       if (ch == EOF) {
+           result = -1;
+           break;
+       }
+       *buffer++ = (char) ch;
+    }
+    return result;
+}
+
+NCURSES_EXPORT(WINDOW *)
+NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
+{
+    WINDOW tmp, *nwin;
+    bool old_format = FALSE;
+
+    T((T_CALLED("getwin(%p)"), (void *) filep));
+
+    if (filep == 0) {
+       returnWin(0);
+    }
+
+    /*
+     * Read the first 4 bytes to determine first if this is an old-format
+     * screen-dump, or new-format.
+     */
+    if (read_block(&tmp, (size_t) 4, filep) < 0) {
+       returnWin(0);
+    }
+    /*
+     * If this is a new-format file, and we do not support it, give up.
+     */
+    if (!memcmp(&tmp, my_magic, (size_t) 4)) {
+#if NCURSES_EXT_PUTWIN
+       if (read_win(&tmp, filep) < 0)
+#endif
+           returnWin(0);
+    } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
+       returnWin(0);
+    } else {
+       old_format = TRUE;
+    }
+
+    /*
+     * Check the window-size:
+     */
+    if (tmp._maxy == 0 ||
+       tmp._maxy > MAX_SIZE ||
+       tmp._maxx == 0 ||
+       tmp._maxx > MAX_SIZE) {
+       returnWin(0);
+    }
+
+    if (tmp._flags & _ISPAD) {
+       nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
+                                       tmp._maxy + 1,
+                                       tmp._maxx + 1);
+    } else {
+       nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
+                                       tmp._maxy + 1,
+                                       tmp._maxx + 1, 0, 0);
+    }
+
+    /*
+     * We deliberately do not restore the _parx, _pary, or _parent
+     * fields, because the window hierarchy within which they
+     * made sense is probably gone.
+     */
+    if (nwin != 0) {
+       int n;
+       size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
+
+       nwin->_curx = tmp._curx;
+       nwin->_cury = tmp._cury;
+       nwin->_maxy = tmp._maxy;
+       nwin->_maxx = tmp._maxx;
+       nwin->_begy = tmp._begy;
+       nwin->_begx = tmp._begx;
+       nwin->_yoffset = tmp._yoffset;
+       nwin->_flags = tmp._flags & ~(_SUBWIN);
+
+       WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp);
+       nwin->_nc_bkgd = tmp._nc_bkgd;
+
+       nwin->_notimeout = tmp._notimeout;
+       nwin->_clear = tmp._clear;
+       nwin->_leaveok = tmp._leaveok;
+       nwin->_idlok = tmp._idlok;
+       nwin->_idcok = tmp._idcok;
+       nwin->_immed = tmp._immed;
+       nwin->_scroll = tmp._scroll;
+       nwin->_sync = tmp._sync;
        nwin->_use_keypad = tmp._use_keypad;
-       nwin->_delay      = tmp._delay;
-       nwin->_immed      = tmp._immed;
-       nwin->_sync       = tmp._sync;
-
-       nwin->_regtop     = tmp._regtop;
-       nwin->_regbottom  = tmp._regbottom;
-
-       for (n = 0; n < nwin->_maxy + 1; n++)
-       {
-               (void) fread(nwin->_line[n].text,
-                             sizeof(chtype), (size_t)(nwin->_maxx + 1), filep);
-               if (ferror(filep))
-               {
-                       delwin(nwin);
-                       returnWin(0);
+       nwin->_delay = tmp._delay;
+
+       nwin->_regtop = tmp._regtop;
+       nwin->_regbottom = tmp._regbottom;
+
+       if (tmp._flags & _ISPAD)
+           nwin->_pad = tmp._pad;
+
+       if (old_format) {
+           T(("reading old-format screen dump"));
+           for (n = 0; n <= nwin->_maxy; n++) {
+               if (read_block(nwin->_line[n].text, linesize, filep) < 0) {
+                   delwin(nwin);
+                   returnWin(0);
                }
+           }
        }
+#if NCURSES_EXT_PUTWIN
+       else {
+           char *txt = 0;
+           bool success = TRUE;
+           NCURSES_CH_T prior = blank;
+
+           T(("reading new-format screen dump"));
+           for (n = 0; n <= nwin->_maxy; n++) {
+               long row;
+               char *next;
+
+               if ((txt = read_txt(filep)) == 0) {
+                   T(("...failed to read string for row %d", n + 1));
+                   success = FALSE;
+                   break;
+               }
+               row = strtol(txt, &next, 10);
+               if (row != (n + 1) || *next != ':') {
+                   T(("...failed to read row-number %d", n + 1));
+                   success = FALSE;
+                   break;
+               }
+
+               if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx
+                            + 1) < 0) {
+                   T(("...failed to read cells for row %d", n + 1));
+                   success = FALSE;
+                   break;
+               }
+               free(txt);
+               txt = 0;
+           }
+
+           if (!success) {
+               free(txt);
+               delwin(nwin);
+               returnWin(0);
+           }
+       }
+#endif
        touchwin(nwin);
+    }
+    returnWin(nwin);
+}
 
-       returnWin(nwin);
+#if NCURSES_SP_FUNCS
+NCURSES_EXPORT(WINDOW *)
+getwin(FILE *filep)
+{
+    return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
 }
+#endif
 
-int putwin(WINDOW *win, FILE *filep)
+#if NCURSES_EXT_PUTWIN
+static void
+encode_attr(char *target, ARG_SLIMIT(limit)
+           attr_t source,
+           attr_t prior,
+           int source_color,
+           int prior_color)
 {
-        int code = ERR; 
-       int n;
+#if USE_STRING_HACKS && HAVE_SNPRINTF
+    char *base = target;
+#endif
+    source &= ~A_CHARTEXT;
+    prior &= ~A_CHARTEXT;
 
-       T((T_CALLED("putwin(%p,%p)"), win, filep));
+    *target = '\0';
+    if ((source != prior) || (source_color != prior_color)) {
+       size_t n;
+       bool first = TRUE;
 
-       if (win) {
-         (void) fwrite(win, sizeof(WINDOW), 1, filep);
-         if (ferror(filep))
-           returnCode(code);
+       *target++ = MARKER;
+       *target++ = L_CURL;
 
-         for (n = 0; n < win->_maxy + 1; n++)
-           {
-             (void) fwrite(win->_line[n].text,
-                           sizeof(chtype), (size_t)(win->_maxx + 1), filep);
-             if (ferror(filep))
-               returnCode(code);
+       for (n = 0; n < SIZEOF(scr_attrs); ++n) {
+           if ((source & scr_attrs[n].attr) != 0 ||
+               ((source & ALL_BUT_COLOR) == 0 &&
+                (scr_attrs[n].attr == A_NORMAL))) {
+               if (first) {
+                   first = FALSE;
+               } else {
+                   *target++ = '|';
+               }
+               _nc_STRCPY(target, scr_attrs[n].name, limit);
+               target += strlen(target);
            }
-         code = OK;
        }
-       returnCode(code);
+       if (source_color != prior_color) {
+           if (!first)
+               *target++ = '|';
+           _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
+           target += strlen(target);
+       }
+
+       *target++ = R_CURL;
+       *target = '\0';
+    }
 }
 
-int scr_restore(const char *file)
+static void
+encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
 {
-       FILE    *fp = 0;
+#if USE_STRING_HACKS && HAVE_SNPRINTF
+    char *base = target;
+#endif
+#if NCURSES_WIDECHAR
+    size_t n;
+    int source_pair = GetPair(*source);
+    int previous_pair = GetPair(*previous);
 
-       T((T_CALLED("scr_restore(%s)"), _nc_visbuf(file)));
+    *target = '\0';
+    if ((previous->attr != source->attr) || (previous_pair != source_pair)) {
+       encode_attr(target, CUR_SLIMIT
+                   source->attr,
+                   previous->attr,
+                   source_pair,
+                   previous_pair);
+    }
+    target += strlen(target);
+#if NCURSES_EXT_COLORS
+    if (previous->ext_color != source->ext_color) {
+       _nc_SPRINTF(target, CUR_SLIMIT
+                   "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
+    }
+#endif
+    for (n = 0; n < SIZEOF(source->chars); ++n) {
+       unsigned uch = (unsigned) source->chars[n];
+       if (uch == 0)
+           continue;
+       if (n) {
+           *target++ = MARKER;
+           *target++ = APPEND;
+       }
+       *target++ = MARKER;
+       if (uch > 0xffff) {
+           _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch);
+       } else if (uch > 0xff) {
+           _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch);
+       } else if (uch < 32 || uch >= 127) {
+           _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff);
+       } else {
+           switch (uch) {
+           case ' ':
+               _nc_STRCPY(target, "s", limit);
+               break;
+           case MARKER:
+               *target++ = MARKER;
+               *target = '\0';
+               break;
+           default:
+               --target;
+               _nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
+               break;
+           }
+       }
+       target += strlen(target);
+    }
+#else
+    chtype ch = CharOfD(source);
 
-       if (_nc_access(file, R_OK) < 0
-        || (fp = fopen(file, "rb")) == 0)
-           returnCode(ERR);
-       else
-       {
-           delwin(newscr);
-           newscr = getwin(fp);
-           (void) fclose(fp);
-           returnCode(OK);
+    *target = '\0';
+    if (AttrOfD(previous) != AttrOfD(source)) {
+       encode_attr(target, CUR_SLIMIT
+                   AttrOfD(source),
+                   AttrOfD(previous),
+                   GetPair(source),
+                   GetPair(previous));
+    }
+    target += strlen(target);
+    *target++ = MARKER;
+    if (ch < 32 || ch >= 127) {
+       _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
+    } else {
+       switch (ch) {
+       case ' ':
+           _nc_STRCPY(target, "s", limit);
+           break;
+       case MARKER:
+           *target++ = MARKER;
+           *target = '\0';
+           break;
+       default:
+           --target;
+           _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
+           break;
        }
+    }
+#endif
 }
+#endif
 
-int scr_dump(const char *file)
+NCURSES_EXPORT(int)
+putwin(WINDOW *win, FILE *filep)
 {
-       FILE    *fp = 0;
+    int code = ERR;
 
-       T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
+    T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
 
-       if (_nc_access(file, W_OK) < 0
-        || (fp = fopen(file, "wb")) == 0)
-           returnCode(ERR);
-       else
-       {
-           (void) putwin(newscr, fp);
-           (void) fclose(fp);
-           dumptime = time((time_t *)0);
-           returnCode(OK);
+#if NCURSES_EXT_PUTWIN
+    if (win != 0) {
+       const char *version = curses_version();
+       char buffer[1024];
+       NCURSES_CH_T last_cell;
+       int y;
+
+       memset(&last_cell, 0, sizeof(last_cell));
+
+       clearerr(filep);
+
+       /*
+        * Our magic number is technically nonprinting, but aside from that,
+        * all of the file is printable ASCII.
+        */
+#define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
+       PUTS(my_magic);
+       PUTS(version);
+       PUTS("\n");
+       for (y = 0; y < (int) SIZEOF(scr_params); ++y) {
+           const char *name = scr_params[y].name;
+           const char *data = (char *) win + scr_params[y].offset;
+           const void *dp = (const void *) data;
+           attr_t attr;
+
+           *buffer = '\0';
+           if (!strncmp(name, "_pad.", (size_t) 5) && !(win->_flags & _ISPAD)) {
+               continue;
+           }
+           switch (scr_params[y].type) {
+           case pATTR:
+               attr = (*(const attr_t *) dp) & ~A_CHARTEXT;
+               encode_attr(buffer, TOP_SLIMIT
+                           (*(const attr_t *) dp) & ~A_CHARTEXT,
+                           A_NORMAL,
+                           COLOR_PAIR((int) attr),
+                           0);
+               break;
+           case pBOOL:
+               if (!(*(const bool *) data)) {
+                   continue;
+               }
+               _nc_STRCPY(buffer, name, sizeof(buffer));
+               name = "flag";
+               break;
+           case pCHAR:
+               attr = (*(const attr_t *) dp);
+               encode_attr(buffer, TOP_SLIMIT
+                           * (const attr_t *) dp,
+                           A_NORMAL,
+                           COLOR_PAIR((int) attr),
+                           0);
+               break;
+           case pINT:
+               if (!(*(const int *) dp))
+                   continue;
+               _nc_SPRINTF(buffer, TOP_SLIMIT
+                           "%d", *(const int *) dp);
+               break;
+           case pSHORT:
+               if (!(*(const short *) dp))
+                   continue;
+               _nc_SPRINTF(buffer, TOP_SLIMIT
+                           "%d", *(const short *) dp);
+               break;
+           case pSIZE:
+               if (!(*(const NCURSES_SIZE_T *) dp))
+                   continue;
+               _nc_SPRINTF(buffer, TOP_SLIMIT
+                           "%d", *(const NCURSES_SIZE_T *) dp);
+               break;
+#if NCURSES_WIDECHAR
+           case pCCHAR:
+               encode_cell(buffer, TOP_SLIMIT
+                           (CARG_CH_T) dp, CHREF(last_cell));
+               break;
+#endif
+           }
+           /*
+            * Only write non-default data.
+            */
+           if (*buffer != '\0') {
+               if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
+                   || ferror(filep))
+                   returnCode(code);
+           }
+       }
+       /* Write row-data */
+       fprintf(filep, "rows:\n");
+       for (y = 0; y <= win->_maxy; y++) {
+           NCURSES_CH_T *data = win->_line[y].text;
+           int x;
+           if (fprintf(filep, "%d:", y + 1) <= 0
+               || ferror(filep))
+               returnCode(code);
+           for (x = 0; x <= win->_maxx; x++) {
+#if NCURSES_WIDECHAR
+               int len = _nc_wacs_width(data[x].chars[0]);
+               encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
+               last_cell = data[x];
+               PUTS(buffer);
+               if (len > 1)
+                   x += (len - 1);
+#else
+               encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
+               last_cell = data[x];
+               PUTS(buffer);
+#endif
+           }
+           PUTS("\n");
+       }
+       code = OK;
+    }
+#else
+    /*
+     * This is the original putwin():
+     * A straight binary dump is simple, but its format can depend on whether
+     * ncurses is compiled with wide-character support, and also may depend
+     * on the version of ncurses, e.g., if the WINDOW structure is extended.
+     */
+    if (win != 0) {
+       size_t len = (size_t) (win->_maxx + 1);
+       int y;
+
+       clearerr(filep);
+       if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
+           || ferror(filep))
+             returnCode(code);
+
+       for (y = 0; y <= win->_maxy; y++) {
+           if (fwrite(win->_line[y].text,
+                      sizeof(NCURSES_CH_T), len, filep) != len
+               || ferror(filep)) {
+               returnCode(code);
+           }
+       }
+       code = OK;
+    }
+#endif
+    returnCode(code);
+}
+
+NCURSES_EXPORT(int)
+NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
+{
+    FILE *fp = 0;
+    int code = ERR;
+
+    T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
+
+    if (_nc_access(file, R_OK) >= 0
+       && (fp = fopen(file, BIN_R)) != 0) {
+       delwin(NewScreen(SP_PARM));
+       NewScreen(SP_PARM) = getwin(fp);
+#if !USE_REENTRANT
+       newscr = NewScreen(SP_PARM);
+#endif
+       (void) fclose(fp);
+       if (NewScreen(SP_PARM) != 0) {
+           code = OK;
        }
+    }
+    returnCode(code);
+}
+
+#if NCURSES_SP_FUNCS
+NCURSES_EXPORT(int)
+scr_restore(const char *file)
+{
+    return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
+}
+#endif
+
+NCURSES_EXPORT(int)
+scr_dump(const char *file)
+{
+    int result;
+    FILE *fp = 0;
+
+    T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
+
+    if (_nc_access(file, W_OK) < 0
+       || (fp = fopen(file, BIN_W)) == 0) {
+       result = ERR;
+    } else {
+       (void) putwin(newscr, fp);
+       (void) fclose(fp);
+       result = OK;
+    }
+    returnCode(result);
 }
 
-int scr_init(const char *file)
+NCURSES_EXPORT(int)
+NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
 {
-       FILE    *fp = 0;
-       struct stat     stb;
+    int code = ERR;
 
-       T((T_CALLED("scr_init(%s)"), _nc_visbuf(file)));
+    T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
 
-       if (exit_ca_mode && non_rev_rmcup)
-           returnCode(ERR);
+    if (SP_PARM != 0 &&
+#ifdef USE_TERM_DRIVER
+       InfoOf(SP_PARM).caninit
+#else
+       !(exit_ca_mode && non_rev_rmcup)
+#endif
+       ) {
+       FILE *fp = 0;
 
-       if (_nc_access(file, R_OK) < 0
-        || (fp = fopen(file, "rb")) == 0)
-           returnCode(ERR);
-       else if (fstat(STDOUT_FILENO, &stb) || stb.st_mtime > dumptime)
-           returnCode(ERR);
-       else
-       {
-           delwin(curscr);
-           curscr = getwin(fp);
+       if (_nc_access(file, R_OK) >= 0
+           && (fp = fopen(file, BIN_R)) != 0) {
+           delwin(CurScreen(SP_PARM));
+           CurScreen(SP_PARM) = getwin(fp);
+#if !USE_REENTRANT
+           curscr = CurScreen(SP_PARM);
+#endif
            (void) fclose(fp);
-           returnCode(OK);
+           if (CurScreen(SP_PARM) != 0) {
+               code = OK;
+           }
        }
+    }
+    returnCode(code);
+}
+
+#if NCURSES_SP_FUNCS
+NCURSES_EXPORT(int)
+scr_init(const char *file)
+{
+    return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
 }
+#endif
 
-int scr_set(const char *file)
+NCURSES_EXPORT(int)
+NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
 {
-    T((T_CALLED("scr_set(%s)"), _nc_visbuf(file)));
+    int code = ERR;
 
-    if (scr_init(file) == ERR)
-       returnCode(ERR);
-    else
-    {
-       delwin(newscr);
-       newscr = dupwin(curscr);
-       returnCode(OK);
+    T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
+
+    if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
+       delwin(NewScreen(SP_PARM));
+       NewScreen(SP_PARM) = dupwin(curscr);
+#if !USE_REENTRANT
+       newscr = NewScreen(SP_PARM);
+#endif
+       if (NewScreen(SP_PARM) != 0) {
+           code = OK;
+       }
     }
+    returnCode(code);
+}
+
+#if NCURSES_SP_FUNCS
+NCURSES_EXPORT(int)
+scr_set(const char *file)
+{
+    return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
 }
+#endif