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
45MODULE_ID("$Id: lib_screen.c,v 1.100 2020/05/25 22:48:41 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 - (size_t) (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 */
69static const char my_magic[] =
70{'\210', '\210', '\210', '\210', 0};
71
72#if NCURSES_EXT_PUTWIN
73typedef 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
85typedef struct {
86    const char name[11];
87    attr_t attr;
88} SCR_ATTRS;
89
90typedef 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 }
98static 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
126static 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
165static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
166
167/*
168 * Allocate and read a line of text.  Caller must free it.
169 */
170static char *
171read_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
212static char *
213decode_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
268static char *
269decode_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
325static char *
326decode_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
341static char *
342decode_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
374static int
375read_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
443static int
444read_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 */
484static int
485read_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
502NCURSES_EXPORT(WINDOW *)
503NCURSES_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
648NCURSES_EXPORT(WINDOW *)
649getwin(FILE *filep)
650{
651    return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
652}
653#endif
654
655#if NCURSES_EXT_PUTWIN
656static void
657encode_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
702static void
703encode_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
794NCURSES_EXPORT(int)
795putwin(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
943NCURSES_EXPORT(int)
944NCURSES_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
967NCURSES_EXPORT(int)
968scr_restore(const char *file)
969{
970    return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
971}
972#endif
973
974NCURSES_EXPORT(int)
975scr_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
993NCURSES_EXPORT(int)
994NCURSES_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
1026NCURSES_EXPORT(int)
1027scr_init(const char *file)
1028{
1029    return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1030}
1031#endif
1032
1033NCURSES_EXPORT(int)
1034NCURSES_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
1054NCURSES_EXPORT(int)
1055scr_set(const char *file)
1056{
1057    return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
1058}
1059#endif
1060