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