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