xref: /src/contrib/ncurses/ncurses/base/lib_screen.c (revision 68ad2b0d7af2a3571c4abac9afa712f9b09b721c)
1 /****************************************************************************
2  * Copyright 2019-2024,2025 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.113 2025/12/27 12:28:45 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 + 1 - 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', 0};
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 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 DATA(name, type) { { #name }, type, offsetof(WINDOW, name) }
123 
124 static const SCR_PARAMS scr_params[] =
125 {
126     DATA(_cury, pSIZE),
127     DATA(_curx, pSIZE),
128     DATA(_maxy, pSIZE),
129     DATA(_maxx, pSIZE),
130     DATA(_begy, pSIZE),
131     DATA(_begx, pSIZE),
132     DATA(_flags, pSHORT),
133     DATA(_attrs, pATTR),
134     DATA(_bkgd, pCHAR),
135     DATA(_notimeout, pBOOL),
136     DATA(_clear, pBOOL),
137     DATA(_leaveok, pBOOL),
138     DATA(_scroll, pBOOL),
139     DATA(_idlok, pBOOL),
140     DATA(_idcok, pBOOL),
141     DATA(_immed, pBOOL),
142     DATA(_sync, pBOOL),
143     DATA(_use_keypad, pBOOL),
144     DATA(_delay, pINT),
145     DATA(_regtop, pSIZE),
146     DATA(_regbottom, pSIZE),
147     DATA(_pad._pad_y, pSIZE),
148     DATA(_pad._pad_x, pSIZE),
149     DATA(_pad._pad_top, pSIZE),
150     DATA(_pad._pad_left, pSIZE),
151     DATA(_pad._pad_bottom, pSIZE),
152     DATA(_pad._pad_right, pSIZE),
153     DATA(_yoffset, pSIZE),
154 #if NCURSES_WIDECHAR
155     DATA(_bkgrnd, pCCHAR),
156 #if NCURSES_EXT_COLORS
157     DATA(_color, pINT),
158 #endif
159 #endif
160 };
161 #undef DATA
162 
163 static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
164 
165 /*
166  * Allocate and read a line of text.  Caller must free it.
167  */
168 static char *
read_txt(FILE * fp)169 read_txt(FILE *fp)
170 {
171     size_t limit = 1024;
172     char *result = malloc(limit);
173     char *buffer;
174 
175     if (result != NULL) {
176 	int ch = 0;
177 	size_t used = 0;
178 
179 	clearerr(fp);
180 	result[used] = '\0';
181 	do {
182 	    if (used + 2 >= limit) {
183 		limit += 1024;
184 		buffer = realloc(result, limit);
185 		if (buffer == NULL) {
186 		    free(result);
187 		    result = NULL;
188 		    break;
189 		}
190 		result = buffer;
191 	    }
192 	    ch = fgetc(fp);
193 	    if (ch == EOF)
194 		break;
195 	    result[used++] = (char) ch;
196 	    result[used] = '\0';
197 	} while (ch != '\n');
198 
199 	if (ch == '\n') {
200 	    result[--used] = '\0';
201 	    TR(TRACE_IEVENT, ("READ:%s", result));
202 	} else if (used == 0) {
203 	    free(result);
204 	    result = NULL;
205 	}
206     }
207     return result;
208 }
209 
210 static char *
decode_attr(char * source,attr_t * target,int * color)211 decode_attr(char *source, attr_t *target, int *color)
212 {
213     bool found = FALSE;
214 
215     TR(TRACE_IEVENT, ("decode_attr   '%s'", source));
216 
217     while (*source) {
218 	if (source[0] == MARKER && source[1] == L_CURL) {
219 	    source += 2;
220 	    found = TRUE;
221 	} else if (source[0] == R_CURL) {
222 	    source++;
223 	    found = FALSE;
224 	} else if (found) {
225 	    char *next = source;
226 
227 	    if (source[0] == GUTTER) {
228 		++next;
229 	    } else if (*next == 'C') {
230 		int value = 0;
231 		unsigned pair;
232 		next++;
233 		while (isdigit(UChar(*next))) {
234 		    value = value * 10 + (*next++ - '0');
235 		}
236 		*target &= ~A_COLOR;
237 		pair = (unsigned) ((value > 256)
238 				   ? COLOR_PAIR(255)
239 				   : COLOR_PAIR(value));
240 		*target |= pair;
241 		*color = value;
242 	    } else {
243 		size_t n;
244 
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 *
decode_char(char * source,int * target)268 decode_char(char *source, int *target)
269 {
270     int limit = 0;
271     int base = 16;
272     const char digits[] = "0123456789abcdef";
273 
274     TR(TRACE_IEVENT, ("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 		const char *find = strchr(digits, *source++);
309 		int ch = (find != NULL) ? (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 *
decode_chtype(char * source,chtype fillin,chtype * target)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     TR(TRACE_IEVENT, ("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 *
decode_cchar(char * source,const cchar_t * fillin,cchar_t * target)341 decode_cchar(char *source, const 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     TR(TRACE_IEVENT, ("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
read_win(WINDOW * win,FILE * fp)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 	const char *name;
387 	char *value;
388 	char *txt = read_txt(fp);
389 
390 	if (txt == NULL)
391 	    break;
392 	if (!strcmp(txt, "rows:")) {
393 	    free(txt);
394 	    code = OK;
395 	    break;
396 	}
397 	if ((value = strchr(txt, '=')) == NULL) {
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
read_row(char * source,NCURSES_CH_T * prior,NCURSES_CH_T * target,int length)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 = _nc_wacs_width(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
read_block(void * target,size_t length,FILE * fp)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 *)
getwin(NCURSES_SP_DCLx FILE * filep)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 == NULL) {
510 	returnWin(NULL);
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, (size_t) 4, filep) < 0) {
518 	returnWin(NULL);
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, (size_t) 4)) {
524 #if NCURSES_EXT_PUTWIN
525 	if (read_win(&tmp, filep) < 0)
526 #endif
527 	    returnWin(NULL);
528     } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
529 	returnWin(NULL);
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(NULL);
542     }
543 
544     if (IS_PAD(&tmp)) {
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 != NULL) {
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 (IS_PAD(&tmp))
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(NULL);
598 		}
599 	    }
600 	}
601 #if NCURSES_EXT_PUTWIN
602 	else {
603 	    char *txt = NULL;
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)) == NULL) {
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 = NULL;
632 	    }
633 
634 	    if (!success) {
635 		free(txt);
636 		delwin(nwin);
637 		returnWin(NULL);
638 	    }
639 	}
640 #endif
641 	touchwin(nwin);
642     }
643     returnWin(nwin);
644 }
645 
646 #if NCURSES_SP_FUNCS
647 NCURSES_EXPORT(WINDOW *)
getwin(FILE * filep)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
encode_attr(char * target,ARG_SLIMIT (limit)attr_t source,attr_t prior,int source_color,int prior_color)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
encode_cell(char * target,ARG_SLIMIT (limit)CARG_CH_T source,CARG_CH_T previous)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)
putwin(WINDOW * win,FILE * filep)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 != NULL) {
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.", (size_t) 5) && !IS_PAD(win)) {
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 = _nc_wacs_width(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 != NULL) {
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 /*
943  * Replace a window covering the whole screen, i.e., newscr or curscr.
944  */
945 static WINDOW *
replace_window(const WINDOW * target,FILE * source)946 replace_window(const WINDOW *target, FILE *source)
947 {
948     WINDOW *result = getwin(source);
949 #if NCURSES_EXT_FUNCS
950     if (result != NULL) {
951 	if (getmaxx(result) != getmaxx(target)
952 	    || getmaxy(result) != getmaxy(target)) {
953 	    int code = wresize(result,
954 			       1 + getmaxy(target),
955 			       1 + getmaxx(target));
956 	    if (code != OK) {
957 		delwin(result);
958 		result = NULL;
959 	    }
960 	}
961     }
962 #endif
963     return result;
964 }
965 
966 NCURSES_EXPORT(int)
scr_restore(NCURSES_SP_DCLx const char * file)967 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
968 {
969     FILE *fp = NULL;
970     int code = ERR;
971 
972     T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
973 
974     if (SP_PARM != NULL
975 	&& _nc_access(file, R_OK) >= 0
976 	&& (fp = safe_fopen(file, BIN_R)) != NULL) {
977 	WINDOW *my_newscr = replace_window(NewScreen(SP_PARM), fp);
978 	(void) fclose(fp);
979 	if (my_newscr != NULL) {
980 	    delwin(NewScreen(SP_PARM));
981 	    NewScreen(SP_PARM) = my_newscr;
982 #if !USE_REENTRANT
983 	    newscr = my_newscr;
984 #endif
985 	    code = OK;
986 	}
987     }
988     returnCode(code);
989 }
990 
991 #if NCURSES_SP_FUNCS
992 NCURSES_EXPORT(int)
scr_restore(const char * file)993 scr_restore(const char *file)
994 {
995     return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
996 }
997 #endif
998 
999 NCURSES_EXPORT(int)
scr_dump(const char * file)1000 scr_dump(const char *file)
1001 {
1002     int result;
1003     FILE *fp = NULL;
1004 
1005     T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
1006 
1007     if (_nc_access(file, W_OK) < 0
1008 	|| (fp = safe_fopen(file, BIN_W)) == NULL) {
1009 	result = ERR;
1010     } else {
1011 	(void) putwin(newscr, fp);
1012 	(void) fclose(fp);
1013 	result = OK;
1014     }
1015     returnCode(result);
1016 }
1017 
1018 NCURSES_EXPORT(int)
scr_init(NCURSES_SP_DCLx const char * file)1019 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
1020 {
1021     int code = ERR;
1022 
1023     T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1024 
1025     if (SP_PARM != NULL &&
1026 #if USE_TERM_DRIVER
1027 	InfoOf(SP_PARM).caninit
1028 #else
1029 	!(exit_ca_mode && non_rev_rmcup)
1030 #endif
1031 	) {
1032 	FILE *fp = NULL;
1033 
1034 	if (_nc_access(file, R_OK) >= 0
1035 	    && (fp = safe_fopen(file, BIN_R)) != NULL) {
1036 	    WINDOW *my_curscr = replace_window(CurScreen(SP_PARM), fp);
1037 	    (void) fclose(fp);
1038 	    if (my_curscr != NULL) {
1039 		delwin(CurScreen(SP_PARM));
1040 		CurScreen(SP_PARM) = my_curscr;
1041 #if !USE_REENTRANT
1042 		curscr = my_curscr;
1043 #endif
1044 		code = OK;
1045 	    }
1046 	}
1047     }
1048     returnCode(code);
1049 }
1050 
1051 #if NCURSES_SP_FUNCS
1052 NCURSES_EXPORT(int)
scr_init(const char * file)1053 scr_init(const char *file)
1054 {
1055     return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1056 }
1057 #endif
1058 
1059 NCURSES_EXPORT(int)
scr_set(NCURSES_SP_DCLx const char * file)1060 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
1061 {
1062     int code = ERR;
1063 
1064     T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1065 
1066     if (SP_PARM != NULL
1067 	&& NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
1068 	delwin(NewScreen(SP_PARM));
1069 	NewScreen(SP_PARM) = dupwin(curscr);
1070 #if !USE_REENTRANT
1071 	newscr = NewScreen(SP_PARM);
1072 #endif
1073 	if (NewScreen(SP_PARM) != NULL) {
1074 	    code = OK;
1075 	}
1076     }
1077     returnCode(code);
1078 }
1079 
1080 #if NCURSES_SP_FUNCS
1081 NCURSES_EXPORT(int)
scr_set(const char * file)1082 scr_set(const char *file)
1083 {
1084     return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
1085 }
1086 #endif
1087