ncurses 5.9 - patch 20150321
[ncurses.git] / ncurses / base / lib_screen.c
1 /****************************************************************************
2  * Copyright (c) 1998-2011,2015 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey                        1996 on                 *
33  *     and: Juergen Pfeifer                         2009                    *
34  ****************************************************************************/
35
36 #include <curses.priv.h>
37
38 #include <ctype.h>
39
40 #ifndef CUR
41 #define CUR SP_TERMTYPE
42 #endif
43
44 MODULE_ID("$Id: lib_screen.c,v 1.64 2015/03/21 23:59:32 tom Exp $")
45
46 #define MAX_SIZE 0x3fff         /* 16k is big enough for a window or pad */
47
48 #define MARKER '\\'
49 #define GUTTER '|'
50 #define L_CURL '{'
51 #define R_CURL '}'
52
53 #define L_MARK "\\{"
54 #define R_MARK "}"
55
56 /*
57  * Use 0x8888 as the magic number for new-format files, since it cannot be
58  * mistaken for the _cury/_curx pair of 16-bit numbers which start the old
59  * format.  It happens to be unused in the file 5.22 database (2015/03/07).
60  */
61 static char my_magic[] =
62 {'\210', '\210', '\210', '\210'};
63
64 #if NCURSES_EXT_SCREEN_DUMP
65 typedef enum {
66     pINT                        /* int */
67     ,pSHORT                     /* short */
68     ,pBOOL                      /* bool */
69     ,pATTR                      /* attr_t */
70     ,pCHAR                      /* chtype */
71     ,pSIZE                      /* NCURSES_SIZE_T */
72 #if NCURSES_WIDECHAR
73     ,pCCHAR                     /* cchar_t */
74 #endif
75 } PARAM_TYPE;
76
77 typedef struct {
78     const char *name;
79     attr_t attr;
80 } SCR_ATTRS;
81
82 typedef struct {
83     const char *name;
84     PARAM_TYPE type;
85     size_t size;
86     size_t offset;
87 } SCR_PARAMS;
88
89 #define DATA(name) { #name, A_##name }
90 static SCR_ATTRS scr_attrs[] =
91 {
92     DATA(NORMAL),
93     DATA(STANDOUT),
94     DATA(UNDERLINE),
95     DATA(REVERSE),
96     DATA(BLINK),
97     DATA(DIM),
98     DATA(BOLD),
99     DATA(ALTCHARSET),
100     DATA(INVIS),
101     DATA(PROTECT),
102     DATA(HORIZONTAL),
103     DATA(LEFT),
104     DATA(LOW),
105     DATA(RIGHT),
106     DATA(TOP),
107     DATA(VERTICAL),
108
109 #ifdef A_ITALIC
110     DATA(ITALIC),
111 #endif
112 };
113 #undef DATA
114
115 #define sizeof2(type,name) sizeof(((type *)0)->name)
116 #define DATA(name, type) { #name, type, sizeof2(WINDOW, name), offsetof(WINDOW, name) }
117
118 static SCR_PARAMS scr_params[] =
119 {
120     DATA(_cury, pSIZE),
121     DATA(_curx, pSIZE),
122     DATA(_maxy, pSIZE),
123     DATA(_maxx, pSIZE),
124     DATA(_begy, pSIZE),
125     DATA(_begx, pSIZE),
126     DATA(_flags, pSHORT),
127     DATA(_attrs, pATTR),
128     DATA(_bkgd, pCHAR),
129     DATA(_notimeout, pBOOL),
130     DATA(_clear, pBOOL),
131     DATA(_leaveok, pBOOL),
132     DATA(_scroll, pBOOL),
133     DATA(_idlok, pBOOL),
134     DATA(_idcok, pBOOL),
135     DATA(_immed, pBOOL),
136     DATA(_sync, pBOOL),
137     DATA(_use_keypad, pBOOL),
138     DATA(_delay, pINT),
139     DATA(_regtop, pSIZE),
140     DATA(_regbottom, pSIZE),
141     DATA(_pad._pad_y, pSIZE),
142     DATA(_pad._pad_x, pSIZE),
143     DATA(_pad._pad_top, pSIZE),
144     DATA(_pad._pad_left, pSIZE),
145     DATA(_pad._pad_bottom, pSIZE),
146     DATA(_pad._pad_right, pSIZE),
147     DATA(_yoffset, pSIZE),
148 #if NCURSES_WIDECHAR
149     DATA(_bkgrnd, pCCHAR),
150 #if NCURSES_EXT_COLORS
151     DATA(_color, pINT),
152 #endif
153 #endif
154 };
155 #undef DATA
156
157 static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
158
159 /*
160  * Allocate and read a line of text.  Caller must free it.
161  */
162 static char *
163 read_txt(FILE *fp)
164 {
165     size_t limit = 1024;
166     size_t used = 0;
167     char *result = malloc(limit);
168     char *buffer;
169
170     if (result != 0) {
171         int ch = 0;
172
173         clearerr(fp);
174         result[used] = '\0';
175         do {
176             if (used + 2 >= limit) {
177                 limit += 1024;
178                 buffer = realloc(result, limit);
179                 if (buffer == 0) {
180                     free(result);
181                     result = 0;
182                     break;
183                 }
184             }
185             ch = fgetc(fp);
186             if (ch == EOF)
187                 break;
188             result[used++] = (char) ch;
189             result[used] = '\0';
190         } while (ch != '\n');
191
192         if (ch == '\n') {
193             result[--used] = '\0';
194             T(("READ:%s", result));
195         } else if (used == 0) {
196             free(result);
197             result = 0;
198         }
199     }
200     return result;
201 }
202
203 static char *
204 decode_attr(char *source, attr_t *target, int *color)
205 {
206     bool found = FALSE;
207
208     T(("decode_attr   '%s'", source));
209
210     while (*source) {
211         if (source[0] == MARKER && source[1] == L_CURL) {
212             source += 2;
213             found = TRUE;
214         } else if (source[0] == R_CURL) {
215             source++;
216             found = FALSE;
217         } else if (found) {
218             size_t n;
219             char *next = source;
220
221             if (source[0] == GUTTER) {
222                 ++next;
223             } else if (*next == 'C') {
224                 int value = 0;
225                 next++;
226                 while (isdigit(UChar(*next))) {
227                     value = value * 10 + (*next++ - '0');
228                 }
229                 *target &= ~A_COLOR;
230                 if (value > 256) {
231                     *target |= COLOR_PAIR(255);
232                 } else {
233                     *target |= COLOR_PAIR(value);
234                 }
235                 *color = value;
236             } else {
237                 while (isalnum(UChar(*next))) {
238                     ++next;
239                 }
240                 for (n = 0; n < SIZEOF(scr_attrs); ++n) {
241                     if ((size_t) (next - source) == strlen(scr_attrs[n].name)) {
242                         if (scr_attrs[n].attr) {
243                             *target |= scr_attrs[n].attr;
244                         } else {
245                             *target = A_NORMAL;
246                         }
247                         break;
248                     }
249                 }
250             }
251             source = next;
252         } else {
253             break;
254         }
255     }
256     return source;
257 }
258
259 static char *
260 decode_char(char *source, int *target)
261 {
262     int limit = 0;
263     int base = 16;
264     const char digits[] = "0123456789abcdef";
265
266     T(("decode_char   '%s'", source));
267     *target = ' ';
268     switch (*source) {
269     case '\\':
270         switch (*++source) {
271         case '\\':
272             *target = '\\';
273             ++source;
274             break;
275         case 's':
276             *target = ' ';
277             ++source;
278             break;
279         case '0':
280         case '1':
281         case '2':
282         case '3':
283             base = 8;
284             limit = 3;
285             break;
286         case 'u':
287             limit = 4;
288             ++source;
289             break;
290         case 'U':
291             limit = 8;
292             ++source;
293             break;
294         }
295         if (limit) {
296             *target = 0;
297             while (limit-- > 0) {
298                 char *find = strchr(digits, *source++);
299                 int ch = (find != 0) ? (int) (find - digits) : -1;
300                 *target *= base;
301                 if (ch >= 0 && ch < base) {
302                     *target += ch;
303                 }
304             }
305         }
306         break;
307     default:
308         *target = *source++;
309         break;
310     }
311     return source;
312 }
313
314 static char *
315 decode_chtype(char *source, chtype fillin, chtype *target)
316 {
317     attr_t attr = ChAttrOf(fillin);
318     int color = PAIR_NUMBER(attr);
319     int value;
320
321     T(("decode_chtype '%s'", source));
322     source = decode_attr(source, &attr, &color);
323     source = decode_char(source, &value);
324     *target = ChCharOf(value) | attr | COLOR_PAIR(color);
325     return source;
326 }
327
328 #if NCURSES_WIDECHAR
329 static char *
330 decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
331 {
332     int color;
333     attr_t attr = fillin->attr;
334     wchar_t chars[CCHARW_MAX];
335
336     T(("decode_cchar  '%s'", source));
337     *target = blank;
338 #if NCURSES_EXT_COLORS
339     color = fillin->ext_color;
340 #else
341     color = (int) PAIR_NUMBER(attr);
342 #endif
343     source = decode_attr(source, &attr, &color);
344     memset(chars, 0, sizeof(chars));
345     source = decode_char(source, &chars[0]);
346     /* FIXME - handle combining characters at this point */
347     setcchar(target, chars, attr, (short) color, NULL);
348     return source;
349 }
350 #endif
351
352 static int
353 read_win(WINDOW *win, FILE *fp)
354 {
355     int code = ERR;
356     char *txt;
357     char *name;
358     char *value;
359     size_t n;
360     int color;
361 #if NCURSES_WIDECHAR
362     NCURSES_CH_T prior;
363 #endif
364     chtype prior2;
365
366     memset(win, 0, sizeof(WINDOW));
367     for (;;) {
368         txt = read_txt(fp);
369         if (txt == 0)
370             break;
371         if (!strcmp(txt, "rows:")) {
372             free(txt);
373             code = OK;
374             break;
375         }
376         if ((value = strchr(txt, '=')) == 0) {
377             free(txt);
378             continue;
379         }
380         *value++ = '\0';
381         name = !strcmp(txt, "flag") ? value : txt;
382         for (n = 0; n < SIZEOF(scr_params); ++n) {
383             if (!strcmp(name, scr_params[n].name)) {
384                 void *data = (void *) ((char *) win + scr_params[n].offset);
385
386                 switch (scr_params[n].type) {
387                 case pATTR:
388                     (void) decode_attr(value, data, &color);
389                     break;
390                 case pBOOL:
391                     *(bool *) data = TRUE;
392                     break;
393                 case pCHAR:
394                     prior2 = ' ';
395                     decode_chtype(value, prior2, data);
396                     break;
397                 case pINT:
398                     *(int *) data = atoi(value);
399                     break;
400                 case pSHORT:
401                     *(short *) data = (short) atoi(value);
402                     break;
403                 case pSIZE:
404                     *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value);
405                     break;
406 #if NCURSES_WIDECHAR
407                 case pCCHAR:
408                     prior = blank;
409                     decode_cchar(value, &prior, data);
410                     break;
411 #endif
412                 }
413                 break;
414             }
415         }
416         free(txt);
417     }
418     return code;
419 }
420
421 static int
422 read_row(char *source, NCURSES_CH_T * prior, NCURSES_CH_T * target, int length)
423 {
424     while (*source != '\0' && length > 0) {
425 #if NCURSES_WIDECHAR
426         source = decode_cchar(source, prior, target);
427 #else
428         source = decode_chtype(source, *prior, target);
429 #endif
430         *prior = *target;
431         ++target;
432         --length;
433     }
434     while (length-- > 0) {
435         *target++ = blank;
436     }
437     /* FIXME - see what error conditions should apply if I need to return ERR */
438     return 0;
439 }
440 #endif /* NCURSES_EXT_SCREEN_DUMP */
441
442 /*
443  * Originally, getwin/putwin used fread/fwrite, because they used binary data.
444  * The new format uses printable ASCII, which does not have as predictable
445  * sizes.  Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need
446  * special handling if we want to read screen dumps from an older library.
447  */
448 static int
449 read_block(void *target, size_t length, FILE *fp)
450 {
451     int result = 0;
452     char *buffer = target;
453
454     clearerr(fp);
455     while (length-- != 0) {
456         int ch = fgetc(fp);
457         if (ch == EOF) {
458             result = -1;
459             break;
460         }
461         *buffer++ = (char) ch;
462     }
463     return result;
464 }
465
466 NCURSES_EXPORT(WINDOW *)
467 NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
468 {
469     WINDOW tmp, *nwin;
470     int n;
471     bool old_format = FALSE;
472
473     T((T_CALLED("getwin(%p)"), (void *) filep));
474
475     if (filep == 0) {
476         returnWin(0);
477     }
478
479     /*
480      * Read the first 4 bytes to determine first if this is an old-format
481      * screen-dump, or new-format.
482      */
483     if (read_block(&tmp, 4, filep) < 0) {
484         returnWin(0);
485     }
486     /*
487      * If this is a new-format file, and we do not support it, give up.
488      */
489     if (!memcmp(&tmp, my_magic, 4)) {
490 #if NCURSES_EXT_SCREEN_DUMP
491         if (read_win(&tmp, filep) < 0)
492 #endif
493             returnWin(0);
494     } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
495         returnWin(0);
496     } else {
497         old_format = TRUE;
498     }
499
500     /*
501      * Check the window-size:
502      */
503     if (tmp._maxy == 0 ||
504         tmp._maxy > MAX_SIZE ||
505         tmp._maxx == 0 ||
506         tmp._maxx > MAX_SIZE) {
507         returnWin(0);
508     }
509
510     if (tmp._flags & _ISPAD) {
511         nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
512                                         tmp._maxy + 1,
513                                         tmp._maxx + 1);
514     } else {
515         nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
516                                         tmp._maxy + 1,
517                                         tmp._maxx + 1, 0, 0);
518     }
519
520     /*
521      * We deliberately do not restore the _parx, _pary, or _parent
522      * fields, because the window hierarchy within which they
523      * made sense is probably gone.
524      */
525     if (nwin != 0) {
526         size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
527
528         nwin->_curx = tmp._curx;
529         nwin->_cury = tmp._cury;
530         nwin->_maxy = tmp._maxy;
531         nwin->_maxx = tmp._maxx;
532         nwin->_begy = tmp._begy;
533         nwin->_begx = tmp._begx;
534         nwin->_yoffset = tmp._yoffset;
535         nwin->_flags = tmp._flags & ~(_SUBWIN);
536
537         WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp);
538         nwin->_nc_bkgd = tmp._nc_bkgd;
539
540         nwin->_notimeout = tmp._notimeout;
541         nwin->_clear = tmp._clear;
542         nwin->_leaveok = tmp._leaveok;
543         nwin->_idlok = tmp._idlok;
544         nwin->_idcok = tmp._idcok;
545         nwin->_immed = tmp._immed;
546         nwin->_scroll = tmp._scroll;
547         nwin->_sync = tmp._sync;
548         nwin->_use_keypad = tmp._use_keypad;
549         nwin->_delay = tmp._delay;
550
551         nwin->_regtop = tmp._regtop;
552         nwin->_regbottom = tmp._regbottom;
553
554         if (tmp._flags & _ISPAD)
555             nwin->_pad = tmp._pad;
556
557         if (old_format) {
558             T(("reading old-format screen dump"));
559             for (n = 0; n <= nwin->_maxy; n++) {
560                 if (read_block(nwin->_line[n].text, linesize, filep) < 0) {
561                     delwin(nwin);
562                     returnWin(0);
563                 }
564             }
565         }
566 #if NCURSES_EXT_SCREEN_DUMP
567         else {
568             char *txt;
569             bool success = TRUE;
570             NCURSES_CH_T prior = blank;
571
572             T(("reading new-format screen dump"));
573             for (n = 0; n <= nwin->_maxy; n++) {
574                 long row;
575                 char *next;
576
577                 if ((txt = read_txt(filep)) == 0) {
578                     T(("...failed to read string for row %d", n + 1));
579                     success = FALSE;
580                     break;
581                 }
582                 row = strtol(txt, &next, 10);
583                 if (row != (n + 1) || *next != ':') {
584                     T(("...failed to read row-number %d", n + 1));
585                     success = FALSE;
586                     break;
587                 }
588
589                 if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx
590                              + 1) < 0) {
591                     T(("...failed to read cells for row %d", n + 1));
592                     success = FALSE;
593                     break;
594                 }
595                 free(txt);
596                 txt = 0;
597             }
598
599             if (!success) {
600                 free(txt);
601                 delwin(nwin);
602                 returnWin(0);
603             }
604         }
605 #endif
606         touchwin(nwin);
607     }
608     returnWin(nwin);
609 }
610
611 #if NCURSES_SP_FUNCS
612 NCURSES_EXPORT(WINDOW *)
613 getwin(FILE *filep)
614 {
615     return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
616 }
617 #endif
618
619 #if NCURSES_EXT_SCREEN_DUMP
620 static void
621 encode_attr(char *target, attr_t source, attr_t prior)
622 {
623     *target = '\0';
624     if (source != prior) {
625         size_t n;
626         bool first = TRUE;
627
628         strcpy(target, L_MARK);
629         target += strlen(target);
630
631         for (n = 0; n < SIZEOF(scr_attrs); ++n) {
632             if ((source & scr_attrs[n].attr) != 0 ||
633                 ((source & ALL_BUT_COLOR) == 0 &&
634                  (scr_attrs[n].attr == A_NORMAL))) {
635                 if (first) {
636                     first = FALSE;
637                 } else {
638                     *target++ = '|';
639                 }
640                 strcpy(target, scr_attrs[n].name);
641                 target += strlen(target);
642             }
643         }
644         if ((source & A_COLOR) != (prior & A_COLOR)) {
645             if (!first)
646                 *target++ = '|';
647             sprintf(target, "C%d", PAIR_NUMBER(source));
648             target += strlen(target);
649         }
650
651         strcpy(target, R_MARK);
652     }
653 }
654
655 static void
656 encode_cell(char *target, CARG_CH_T source, CARG_CH_T previous)
657 {
658 #if NCURSES_WIDECHAR
659     size_t n;
660
661     *target = '\0';
662     if (previous->attr != source->attr) {
663         encode_attr(target, source->attr, previous->attr);
664     }
665     target += strlen(target);
666 #if NCURSES_EXT_COLORS
667     if (previous->ext_color != source->ext_color) {
668         sprintf(target, "%sC%d%s", L_MARK, source->ext_color, R_MARK);
669     }
670 #endif
671     for (n = 0; n < SIZEOF(source->chars); ++n) {
672         if (source->chars[n] == 0)
673             continue;
674         if (source->chars[n] > 0xffff) {
675             sprintf(target, "\\U%08x", source->chars[n]);
676         } else if (source->chars[n] > 0xff) {
677             sprintf(target, "\\u%04x", source->chars[n]);
678         } else if (source->chars[n] < 32 || source->chars[n] >= 127) {
679             sprintf(target, "\\%03o", source->chars[n] & 0xff);
680         } else {
681             switch (source->chars[n]) {
682             case ' ':
683                 strcpy(target, "\\s");
684                 break;
685             case '\\':
686                 strcpy(target, "\\\\");
687                 break;
688             default:
689                 sprintf(target, "%c", source->chars[n]);
690                 break;
691             }
692         }
693         target += strlen(target);
694     }
695 #else
696     chtype ch = CharOfD(source);
697
698     *target = '\0';
699     if (AttrOfD(previous) != AttrOfD(source)) {
700         encode_attr(target, AttrOfD(source), AttrOfD(previous));
701     }
702     target += strlen(target);
703     if (ch < 32 || ch >= 127) {
704         sprintf(target, "\\%03o", ch);
705     } else {
706         switch (ch) {
707         case ' ':
708             strcpy(target, "\\s");
709             break;
710         case '\\':
711             strcpy(target, "\\\\");
712             break;
713         default:
714             sprintf(target, "%c", ch);
715             break;
716         }
717     }
718     target += strlen(target);
719 #endif
720 }
721 #endif
722
723 NCURSES_EXPORT(int)
724 putwin(WINDOW *win, FILE *filep)
725 {
726     int code = ERR;
727     int y;
728
729     T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
730
731 #if NCURSES_EXT_SCREEN_DUMP
732     if (win != 0) {
733         const char *version = curses_version();
734         char buffer[1024];
735         NCURSES_CH_T last_cell;
736
737         memset(&last_cell, 0, sizeof(last_cell));
738
739         clearerr(filep);
740
741         /*
742          * Our magic number is technically nonprinting, but aside from that,
743          * all of the file is printable ASCII.
744          */
745 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
746         PUTS(my_magic);
747         PUTS(version);
748         PUTS("\n");
749         for (y = 0; y < (int) SIZEOF(scr_params); ++y) {
750             const char *name = scr_params[y].name;
751             const char *data = (char *) win + scr_params[y].offset;
752             const void *dp = (const void *) data;
753
754             *buffer = '\0';
755             if (!strncmp(name, "_pad.", 5) && !(win->_flags & _ISPAD)) {
756                 continue;
757             }
758             switch (scr_params[y].type) {
759             case pATTR:
760                 encode_attr(buffer, (*(const attr_t *) dp) & ~A_CHARTEXT, A_NORMAL);
761                 break;
762             case pBOOL:
763                 if (!(*(const bool *) data)) {
764                     continue;
765                 }
766                 strcpy(buffer, name);
767                 name = "flag";
768                 break;
769             case pCHAR:
770                 encode_attr(buffer, *(const attr_t *) dp, A_NORMAL);
771                 break;
772             case pINT:
773                 if (!(*(const int *) dp))
774                     continue;
775                 sprintf(buffer, "%d", *(const int *) dp);
776                 break;
777             case pSHORT:
778                 if (!(*(const short *) dp))
779                     continue;
780                 sprintf(buffer, "%d", *(const short *) dp);
781                 break;
782             case pSIZE:
783                 if (!(*(const NCURSES_SIZE_T *) dp))
784                     continue;
785                 sprintf(buffer, "%d", *(const NCURSES_SIZE_T *) dp);
786                 break;
787 #if NCURSES_WIDECHAR
788             case pCCHAR:
789                 encode_cell(buffer, (CARG_CH_T) dp, CHREF(last_cell));
790                 break;
791 #endif
792             }
793             /*
794              * Only write non-default data.
795              */
796             if (*buffer != '\0') {
797                 if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
798                     || ferror(filep))
799                     returnCode(code);
800             }
801         }
802         /* Write row-data */
803         fprintf(filep, "rows:\n");
804         for (y = 0; y <= win->_maxy; y++) {
805             NCURSES_CH_T *data = win->_line[y].text;
806             int x;
807             if (fprintf(filep, "%d:", y + 1) <= 0
808                 || ferror(filep))
809                 returnCode(code);
810             for (x = 0; x <= win->_maxx; x++) {
811                 encode_cell(buffer, CHREF(data[x]), CHREF(last_cell));
812                 last_cell = data[x];
813                 PUTS(buffer);
814             }
815             PUTS("\n");
816         }
817     }
818 #else
819     /*
820      * This is the original putwin():
821      * A straight binary dump is simple, but its format can depend on whether
822      * ncurses is compiled with wide-character support, and also may depend
823      * on the version of ncurses, e.g., if the WINDOW structure is extended.
824      */
825     if (win != 0) {
826         size_t len = (size_t) (win->_maxx + 1);
827
828         clearerr(filep);
829         if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
830             || ferror(filep))
831               returnCode(code);
832
833         for (y = 0; y <= win->_maxy; y++) {
834             if (fwrite(win->_line[y].text,
835                        sizeof(NCURSES_CH_T), len, filep) != len
836                 || ferror(filep)) {
837                 returnCode(code);
838             }
839         }
840         code = OK;
841     }
842 #endif
843     returnCode(code);
844 }
845
846 NCURSES_EXPORT(int)
847 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
848 {
849     FILE *fp = 0;
850     int code = ERR;
851
852     T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
853
854     if (_nc_access(file, R_OK) >= 0
855         && (fp = fopen(file, "rb")) != 0) {
856         delwin(NewScreen(SP_PARM));
857         NewScreen(SP_PARM) = getwin(fp);
858 #if !USE_REENTRANT
859         newscr = NewScreen(SP_PARM);
860 #endif
861         (void) fclose(fp);
862         if (NewScreen(SP_PARM) != 0) {
863             code = OK;
864         }
865     }
866     returnCode(code);
867 }
868
869 #if NCURSES_SP_FUNCS
870 NCURSES_EXPORT(int)
871 scr_restore(const char *file)
872 {
873     return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
874 }
875 #endif
876
877 NCURSES_EXPORT(int)
878 scr_dump(const char *file)
879 {
880     int result;
881     FILE *fp = 0;
882
883     T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
884
885     if (_nc_access(file, W_OK) < 0
886         || (fp = fopen(file, "wb")) == 0) {
887         result = ERR;
888     } else {
889         (void) putwin(newscr, fp);
890         (void) fclose(fp);
891         result = OK;
892     }
893     returnCode(result);
894 }
895
896 NCURSES_EXPORT(int)
897 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
898 {
899     FILE *fp = 0;
900     int code = ERR;
901
902     T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
903
904     if (SP_PARM != 0 &&
905 #ifdef USE_TERM_DRIVER
906         InfoOf(SP_PARM).caninit
907 #else
908         !(exit_ca_mode && non_rev_rmcup)
909 #endif
910         ) {
911         if (_nc_access(file, R_OK) >= 0
912             && (fp = fopen(file, "rb")) != 0) {
913             delwin(CurScreen(SP_PARM));
914             CurScreen(SP_PARM) = getwin(fp);
915 #if !USE_REENTRANT
916             curscr = CurScreen(SP_PARM);
917 #endif
918             (void) fclose(fp);
919             if (CurScreen(SP_PARM) != 0) {
920                 code = OK;
921             }
922         }
923     }
924     returnCode(code);
925 }
926
927 #if NCURSES_SP_FUNCS
928 NCURSES_EXPORT(int)
929 scr_init(const char *file)
930 {
931     return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
932 }
933 #endif
934
935 NCURSES_EXPORT(int)
936 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
937 {
938     int code = ERR;
939
940     T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
941
942     if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
943         delwin(NewScreen(SP_PARM));
944         NewScreen(SP_PARM) = dupwin(curscr);
945 #if !USE_REENTRANT
946         newscr = NewScreen(SP_PARM);
947 #endif
948         if (NewScreen(SP_PARM) != 0) {
949             code = OK;
950         }
951     }
952     returnCode(code);
953 }
954
955 #if NCURSES_SP_FUNCS
956 NCURSES_EXPORT(int)
957 scr_set(const char *file)
958 {
959     return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
960 }
961 #endif