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