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