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