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