ncurses 6.0 - patch 20180120
[ncurses.git] / ncurses / base / lib_screen.c
1 /****************************************************************************
2  * Copyright (c) 1998-2017,2018 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.93 2018/01/14 17:39:47 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 0x888888 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, &color);
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             int source_color,
660             int prior_color)
661 {
662 #if USE_STRING_HACKS && HAVE_SNPRINTF
663     char *base = target;
664 #endif
665     source &= ~A_CHARTEXT;
666     prior &= ~A_CHARTEXT;
667
668     *target = '\0';
669     if ((source != prior) || (source_color != prior_color)) {
670         size_t n;
671         bool first = TRUE;
672
673         *target++ = MARKER;
674         *target++ = L_CURL;
675
676         for (n = 0; n < SIZEOF(scr_attrs); ++n) {
677             if ((source & scr_attrs[n].attr) != 0 ||
678                 ((source & ALL_BUT_COLOR) == 0 &&
679                  (scr_attrs[n].attr == A_NORMAL))) {
680                 if (first) {
681                     first = FALSE;
682                 } else {
683                     *target++ = '|';
684                 }
685                 _nc_STRCPY(target, scr_attrs[n].name, limit);
686                 target += strlen(target);
687             }
688         }
689         if (source_color != prior_color) {
690             if (!first)
691                 *target++ = '|';
692             _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
693             target += strlen(target);
694         }
695
696         *target++ = R_CURL;
697         *target = '\0';
698     }
699 }
700
701 static void
702 encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
703 {
704 #if USE_STRING_HACKS && HAVE_SNPRINTF
705     char *base = target;
706 #endif
707 #if NCURSES_WIDECHAR
708     size_t n;
709     int source_pair = GetPair(*source);
710     int previous_pair = GetPair(*previous);
711
712     *target = '\0';
713     if ((previous->attr != source->attr) || (previous_pair != source_pair)) {
714         encode_attr(target, CUR_SLIMIT
715                     source->attr,
716                     previous->attr,
717                     source_pair,
718                     previous_pair);
719     }
720     target += strlen(target);
721 #if NCURSES_EXT_COLORS
722     if (previous->ext_color != source->ext_color) {
723         _nc_SPRINTF(target, CUR_SLIMIT
724                     "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
725     }
726 #endif
727     for (n = 0; n < SIZEOF(source->chars); ++n) {
728         unsigned uch = (unsigned) source->chars[n];
729         if (uch == 0)
730             continue;
731         if (n) {
732             *target++ = MARKER;
733             *target++ = APPEND;
734         }
735         *target++ = MARKER;
736         if (uch > 0xffff) {
737             _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch);
738         } else if (uch > 0xff) {
739             _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch);
740         } else if (uch < 32 || uch >= 127) {
741             _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff);
742         } else {
743             switch (uch) {
744             case ' ':
745                 _nc_STRCPY(target, "s", limit);
746                 break;
747             case MARKER:
748                 *target++ = MARKER;
749                 *target = '\0';
750                 break;
751             default:
752                 --target;
753                 _nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
754                 break;
755             }
756         }
757         target += strlen(target);
758     }
759 #else
760     chtype ch = CharOfD(source);
761
762     *target = '\0';
763     if (AttrOfD(previous) != AttrOfD(source)) {
764         encode_attr(target, CUR_SLIMIT
765                     AttrOfD(source),
766                     AttrOfD(previous),
767                     GetPair(source),
768                     GetPair(previous));
769     }
770     target += strlen(target);
771     *target++ = MARKER;
772     if (ch < 32 || ch >= 127) {
773         _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
774     } else {
775         switch (ch) {
776         case ' ':
777             _nc_STRCPY(target, "s", limit);
778             break;
779         case MARKER:
780             *target++ = MARKER;
781             *target = '\0';
782             break;
783         default:
784             --target;
785             _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
786             break;
787         }
788     }
789 #endif
790 }
791 #endif
792
793 NCURSES_EXPORT(int)
794 putwin(WINDOW *win, FILE *filep)
795 {
796     int code = ERR;
797
798     T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
799
800 #if NCURSES_EXT_PUTWIN
801     if (win != 0) {
802         const char *version = curses_version();
803         char buffer[1024];
804         NCURSES_CH_T last_cell;
805         int y;
806
807         memset(&last_cell, 0, sizeof(last_cell));
808
809         clearerr(filep);
810
811         /*
812          * Our magic number is technically nonprinting, but aside from that,
813          * all of the file is printable ASCII.
814          */
815 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
816         PUTS(my_magic);
817         PUTS(version);
818         PUTS("\n");
819         for (y = 0; y < (int) SIZEOF(scr_params); ++y) {
820             const char *name = scr_params[y].name;
821             const char *data = (char *) win + scr_params[y].offset;
822             const void *dp = (const void *) data;
823             attr_t attr;
824
825             *buffer = '\0';
826             if (!strncmp(name, "_pad.", 5) && !(win->_flags & _ISPAD)) {
827                 continue;
828             }
829             switch (scr_params[y].type) {
830             case pATTR:
831                 attr = (*(const attr_t *) dp) & ~A_CHARTEXT;
832                 encode_attr(buffer, TOP_SLIMIT
833                             (*(const attr_t *) dp) & ~A_CHARTEXT,
834                             A_NORMAL,
835                             COLOR_PAIR((int) attr),
836                             0);
837                 break;
838             case pBOOL:
839                 if (!(*(const bool *) data)) {
840                     continue;
841                 }
842                 _nc_STRCPY(buffer, name, sizeof(buffer));
843                 name = "flag";
844                 break;
845             case pCHAR:
846                 attr = (*(const attr_t *) dp);
847                 encode_attr(buffer, TOP_SLIMIT
848                             * (const attr_t *) dp,
849                             A_NORMAL,
850                             COLOR_PAIR((int) attr),
851                             0);
852                 break;
853             case pINT:
854                 if (!(*(const int *) dp))
855                     continue;
856                 _nc_SPRINTF(buffer, TOP_SLIMIT
857                             "%d", *(const int *) dp);
858                 break;
859             case pSHORT:
860                 if (!(*(const short *) dp))
861                     continue;
862                 _nc_SPRINTF(buffer, TOP_SLIMIT
863                             "%d", *(const short *) dp);
864                 break;
865             case pSIZE:
866                 if (!(*(const NCURSES_SIZE_T *) dp))
867                     continue;
868                 _nc_SPRINTF(buffer, TOP_SLIMIT
869                             "%d", *(const NCURSES_SIZE_T *) dp);
870                 break;
871 #if NCURSES_WIDECHAR
872             case pCCHAR:
873                 encode_cell(buffer, TOP_SLIMIT
874                             (CARG_CH_T) dp, CHREF(last_cell));
875                 break;
876 #endif
877             }
878             /*
879              * Only write non-default data.
880              */
881             if (*buffer != '\0') {
882                 if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
883                     || ferror(filep))
884                     returnCode(code);
885             }
886         }
887         /* Write row-data */
888         fprintf(filep, "rows:\n");
889         for (y = 0; y <= win->_maxy; y++) {
890             NCURSES_CH_T *data = win->_line[y].text;
891             int x;
892             if (fprintf(filep, "%d:", y + 1) <= 0
893                 || ferror(filep))
894                 returnCode(code);
895             for (x = 0; x <= win->_maxx; x++) {
896 #if NCURSES_WIDECHAR
897                 int len = wcwidth(data[x].chars[0]);
898                 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
899                 last_cell = data[x];
900                 PUTS(buffer);
901                 if (len > 1)
902                     x += (len - 1);
903 #else
904                 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
905                 last_cell = data[x];
906                 PUTS(buffer);
907 #endif
908             }
909             PUTS("\n");
910         }
911         code = OK;
912     }
913 #else
914     /*
915      * This is the original putwin():
916      * A straight binary dump is simple, but its format can depend on whether
917      * ncurses is compiled with wide-character support, and also may depend
918      * on the version of ncurses, e.g., if the WINDOW structure is extended.
919      */
920     if (win != 0) {
921         size_t len = (size_t) (win->_maxx + 1);
922         int y;
923
924         clearerr(filep);
925         if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
926             || ferror(filep))
927               returnCode(code);
928
929         for (y = 0; y <= win->_maxy; y++) {
930             if (fwrite(win->_line[y].text,
931                        sizeof(NCURSES_CH_T), len, filep) != len
932                 || ferror(filep)) {
933                 returnCode(code);
934             }
935         }
936         code = OK;
937     }
938 #endif
939     returnCode(code);
940 }
941
942 NCURSES_EXPORT(int)
943 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
944 {
945     FILE *fp = 0;
946     int code = ERR;
947
948     T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
949
950     if (_nc_access(file, R_OK) >= 0
951         && (fp = fopen(file, "rb")) != 0) {
952         delwin(NewScreen(SP_PARM));
953         NewScreen(SP_PARM) = getwin(fp);
954 #if !USE_REENTRANT
955         newscr = NewScreen(SP_PARM);
956 #endif
957         (void) fclose(fp);
958         if (NewScreen(SP_PARM) != 0) {
959             code = OK;
960         }
961     }
962     returnCode(code);
963 }
964
965 #if NCURSES_SP_FUNCS
966 NCURSES_EXPORT(int)
967 scr_restore(const char *file)
968 {
969     return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
970 }
971 #endif
972
973 NCURSES_EXPORT(int)
974 scr_dump(const char *file)
975 {
976     int result;
977     FILE *fp = 0;
978
979     T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
980
981     if (_nc_access(file, W_OK) < 0
982         || (fp = fopen(file, "wb")) == 0) {
983         result = ERR;
984     } else {
985         (void) putwin(newscr, fp);
986         (void) fclose(fp);
987         result = OK;
988     }
989     returnCode(result);
990 }
991
992 NCURSES_EXPORT(int)
993 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
994 {
995     int code = ERR;
996
997     T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
998
999     if (SP_PARM != 0 &&
1000 #ifdef USE_TERM_DRIVER
1001         InfoOf(SP_PARM).caninit
1002 #else
1003         !(exit_ca_mode && non_rev_rmcup)
1004 #endif
1005         ) {
1006         FILE *fp = 0;
1007
1008         if (_nc_access(file, R_OK) >= 0
1009             && (fp = fopen(file, "rb")) != 0) {
1010             delwin(CurScreen(SP_PARM));
1011             CurScreen(SP_PARM) = getwin(fp);
1012 #if !USE_REENTRANT
1013             curscr = CurScreen(SP_PARM);
1014 #endif
1015             (void) fclose(fp);
1016             if (CurScreen(SP_PARM) != 0) {
1017                 code = OK;
1018             }
1019         }
1020     }
1021     returnCode(code);
1022 }
1023
1024 #if NCURSES_SP_FUNCS
1025 NCURSES_EXPORT(int)
1026 scr_init(const char *file)
1027 {
1028     return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1029 }
1030 #endif
1031
1032 NCURSES_EXPORT(int)
1033 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
1034 {
1035     int code = ERR;
1036
1037     T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1038
1039     if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
1040         delwin(NewScreen(SP_PARM));
1041         NewScreen(SP_PARM) = dupwin(curscr);
1042 #if !USE_REENTRANT
1043         newscr = NewScreen(SP_PARM);
1044 #endif
1045         if (NewScreen(SP_PARM) != 0) {
1046             code = OK;
1047         }
1048     }
1049     returnCode(code);
1050 }
1051
1052 #if NCURSES_SP_FUNCS
1053 NCURSES_EXPORT(int)
1054 scr_set(const char *file)
1055 {
1056     return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
1057 }
1058 #endif