xref: /freebsd/contrib/ncurses/ncurses/base/lib_screen.c (revision 21817992b3314c908ab50f0bb88d2ee750b9c4ac)
1 /****************************************************************************
2  * Copyright 2019-2021,2023 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.105 2023/04/28 20:58:54 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  */
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 != 0) {
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 == 0) {
186 		    free(result);
187 		    result = 0;
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 = 0;
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 	    size_t n;
226 	    char *next = source;
227 
228 	    if (source[0] == GUTTER) {
229 		++next;
230 	    } else if (*next == 'C') {
231 		int value = 0;
232 		unsigned pair;
233 		next++;
234 		while (isdigit(UChar(*next))) {
235 		    value = value * 10 + (*next++ - '0');
236 		}
237 		*target &= ~A_COLOR;
238 		pair = (unsigned) ((value > 256)
239 				   ? COLOR_PAIR(255)
240 				   : COLOR_PAIR(value));
241 		*target |= pair;
242 		*color = value;
243 	    } else {
244 		while (isalnum(UChar(*next))) {
245 		    ++next;
246 		}
247 		for (n = 0; n < SIZEOF(scr_attrs); ++n) {
248 		    if ((size_t) (next - source) == strlen(scr_attrs[n].name)) {
249 			if (scr_attrs[n].attr) {
250 			    *target |= scr_attrs[n].attr;
251 			} else {
252 			    *target = A_NORMAL;
253 			}
254 			break;
255 		    }
256 		}
257 	    }
258 	    source = next;
259 	} else {
260 	    break;
261 	}
262     }
263     return source;
264 }
265 
266 static char *
decode_char(char * source,int * target)267 decode_char(char *source, int *target)
268 {
269     int limit = 0;
270     int base = 16;
271     const char digits[] = "0123456789abcdef";
272 
273     TR(TRACE_IEVENT, ("decode_char   '%s'", source));
274     *target = ' ';
275     switch (*source) {
276     case MARKER:
277 	switch (*++source) {
278 	case APPEND:
279 	    break;
280 	case MARKER:
281 	    *target = MARKER;
282 	    ++source;
283 	    break;
284 	case 's':
285 	    *target = ' ';
286 	    ++source;
287 	    break;
288 	case '0':
289 	case '1':
290 	case '2':
291 	case '3':
292 	    base = 8;
293 	    limit = 3;
294 	    break;
295 	case 'u':
296 	    limit = 4;
297 	    ++source;
298 	    break;
299 	case 'U':
300 	    limit = 8;
301 	    ++source;
302 	    break;
303 	}
304 	if (limit) {
305 	    *target = 0;
306 	    while (limit-- > 0) {
307 		char *find = strchr(digits, *source++);
308 		int ch = (find != 0) ? (int) (find - digits) : -1;
309 		*target *= base;
310 		if (ch >= 0 && ch < base) {
311 		    *target += ch;
312 		}
313 	    }
314 	}
315 	break;
316     default:
317 	*target = *source++;
318 	break;
319     }
320     return source;
321 }
322 
323 static char *
decode_chtype(char * source,chtype fillin,chtype * target)324 decode_chtype(char *source, chtype fillin, chtype *target)
325 {
326     attr_t attr = ChAttrOf(fillin);
327     int color = PAIR_NUMBER((int) attr);
328     int value;
329 
330     TR(TRACE_IEVENT, ("decode_chtype '%s'", source));
331     source = decode_attr(source, &attr, &color);
332     source = decode_char(source, &value);
333     *target = (ChCharOf(value) | attr | (chtype) COLOR_PAIR(color));
334     /* FIXME - ignore combining characters */
335     return source;
336 }
337 
338 #if NCURSES_WIDECHAR
339 static char *
decode_cchar(char * source,cchar_t * fillin,cchar_t * target)340 decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
341 {
342     int color;
343     attr_t attr = fillin->attr;
344     wchar_t chars[CCHARW_MAX];
345     int append = 0;
346     int value = 0;
347 
348     TR(TRACE_IEVENT, ("decode_cchar  '%s'", source));
349     *target = blank;
350 #if NCURSES_EXT_COLORS
351     color = fillin->ext_color;
352 #else
353     color = (int) PAIR_NUMBER(attr);
354 #endif
355     source = decode_attr(source, &attr, &color);
356     memset(chars, 0, sizeof(chars));
357     source = decode_char(source, &value);
358     chars[0] = (wchar_t) value;
359     /* handle combining characters */
360     while (source[0] == MARKER && source[1] == APPEND) {
361 	source += 2;
362 	source = decode_char(source, &value);
363 	if (++append < CCHARW_MAX) {
364 	    chars[append] = (wchar_t) value;
365 	}
366     }
367     setcchar(target, chars, attr, (short) color, &color);
368     return source;
369 }
370 #endif
371 
372 static int
read_win(WINDOW * win,FILE * fp)373 read_win(WINDOW *win, FILE *fp)
374 {
375     int code = ERR;
376     size_t n;
377     int color;
378 #if NCURSES_WIDECHAR
379     NCURSES_CH_T prior;
380 #endif
381     chtype prior2;
382 
383     memset(win, 0, sizeof(WINDOW));
384     for (;;) {
385 	char *name;
386 	char *value;
387 	char *txt = read_txt(fp);
388 
389 	if (txt == 0)
390 	    break;
391 	if (!strcmp(txt, "rows:")) {
392 	    free(txt);
393 	    code = OK;
394 	    break;
395 	}
396 	if ((value = strchr(txt, '=')) == 0) {
397 	    free(txt);
398 	    continue;
399 	}
400 	*value++ = '\0';
401 	name = !strcmp(txt, "flag") ? value : txt;
402 	for (n = 0; n < SIZEOF(scr_params); ++n) {
403 	    if (!strcmp(name, scr_params[n].name)) {
404 		void *data = (void *) ((char *) win + scr_params[n].offset);
405 
406 		switch (scr_params[n].type) {
407 		case pATTR:
408 		    (void) decode_attr(value, data, &color);
409 		    break;
410 		case pBOOL:
411 		    *(bool *) data = TRUE;
412 		    break;
413 		case pCHAR:
414 		    prior2 = ' ';
415 		    decode_chtype(value, prior2, data);
416 		    break;
417 		case pINT:
418 		    *(int *) data = atoi(value);
419 		    break;
420 		case pSHORT:
421 		    *(short *) data = (short) atoi(value);
422 		    break;
423 		case pSIZE:
424 		    *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value);
425 		    break;
426 #if NCURSES_WIDECHAR
427 		case pCCHAR:
428 		    prior = blank;
429 		    decode_cchar(value, &prior, data);
430 		    break;
431 #endif
432 		}
433 		break;
434 	    }
435 	}
436 	free(txt);
437     }
438     return code;
439 }
440 
441 static int
read_row(char * source,NCURSES_CH_T * prior,NCURSES_CH_T * target,int length)442 read_row(char *source, NCURSES_CH_T *prior, NCURSES_CH_T *target, int length)
443 {
444     while (*source != '\0' && length > 0) {
445 #if NCURSES_WIDECHAR
446 	int len;
447 
448 	source = decode_cchar(source, prior, target);
449 	len = _nc_wacs_width(target->chars[0]);
450 	if (len > 1) {
451 	    int n;
452 
453 	    SetWidecExt(CHDEREF(target), 0);
454 	    for (n = 1; n < len; ++n) {
455 		target[n] = target[0];
456 		SetWidecExt(CHDEREF(target), n);
457 	    }
458 	    target += (len - 1);
459 	    length -= (len - 1);
460 	}
461 #else
462 	source = decode_chtype(source, *prior, target);
463 #endif
464 	*prior = *target;
465 	++target;
466 	--length;
467     }
468     while (length-- > 0) {
469 	*target++ = blank;
470     }
471     /* FIXME - see what error conditions should apply if I need to return ERR */
472     return 0;
473 }
474 #endif /* NCURSES_EXT_PUTWIN */
475 
476 /*
477  * Originally, getwin/putwin used fread/fwrite, because they used binary data.
478  * The new format uses printable ASCII, which does not have as predictable
479  * sizes.  Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need
480  * special handling if we want to read screen dumps from an older library.
481  */
482 static int
read_block(void * target,size_t length,FILE * fp)483 read_block(void *target, size_t length, FILE *fp)
484 {
485     int result = 0;
486     char *buffer = target;
487 
488     clearerr(fp);
489     while (length-- != 0) {
490 	int ch = fgetc(fp);
491 	if (ch == EOF) {
492 	    result = -1;
493 	    break;
494 	}
495 	*buffer++ = (char) ch;
496     }
497     return result;
498 }
499 
500 NCURSES_EXPORT(WINDOW *)
NCURSES_SP_NAME(getwin)501 NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
502 {
503     WINDOW tmp, *nwin;
504     bool old_format = FALSE;
505 
506     T((T_CALLED("getwin(%p)"), (void *) filep));
507 
508     if (filep == 0) {
509 	returnWin(0);
510     }
511 
512     /*
513      * Read the first 4 bytes to determine first if this is an old-format
514      * screen-dump, or new-format.
515      */
516     if (read_block(&tmp, (size_t) 4, filep) < 0) {
517 	returnWin(0);
518     }
519     /*
520      * If this is a new-format file, and we do not support it, give up.
521      */
522     if (!memcmp(&tmp, my_magic, (size_t) 4)) {
523 #if NCURSES_EXT_PUTWIN
524 	if (read_win(&tmp, filep) < 0)
525 #endif
526 	    returnWin(0);
527     } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
528 	returnWin(0);
529     } else {
530 	old_format = TRUE;
531     }
532 
533     /*
534      * Check the window-size:
535      */
536     if (tmp._maxy == 0 ||
537 	tmp._maxy > MAX_SIZE ||
538 	tmp._maxx == 0 ||
539 	tmp._maxx > MAX_SIZE) {
540 	returnWin(0);
541     }
542 
543     if (IS_PAD(&tmp)) {
544 	nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
545 					tmp._maxy + 1,
546 					tmp._maxx + 1);
547     } else {
548 	nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
549 					tmp._maxy + 1,
550 					tmp._maxx + 1, 0, 0);
551     }
552 
553     /*
554      * We deliberately do not restore the _parx, _pary, or _parent
555      * fields, because the window hierarchy within which they
556      * made sense is probably gone.
557      */
558     if (nwin != 0) {
559 	int n;
560 	size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
561 
562 	nwin->_curx = tmp._curx;
563 	nwin->_cury = tmp._cury;
564 	nwin->_maxy = tmp._maxy;
565 	nwin->_maxx = tmp._maxx;
566 	nwin->_begy = tmp._begy;
567 	nwin->_begx = tmp._begx;
568 	nwin->_yoffset = tmp._yoffset;
569 	nwin->_flags = tmp._flags & ~(_SUBWIN);
570 
571 	WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp);
572 	nwin->_nc_bkgd = tmp._nc_bkgd;
573 
574 	nwin->_notimeout = tmp._notimeout;
575 	nwin->_clear = tmp._clear;
576 	nwin->_leaveok = tmp._leaveok;
577 	nwin->_idlok = tmp._idlok;
578 	nwin->_idcok = tmp._idcok;
579 	nwin->_immed = tmp._immed;
580 	nwin->_scroll = tmp._scroll;
581 	nwin->_sync = tmp._sync;
582 	nwin->_use_keypad = tmp._use_keypad;
583 	nwin->_delay = tmp._delay;
584 
585 	nwin->_regtop = tmp._regtop;
586 	nwin->_regbottom = tmp._regbottom;
587 
588 	if (IS_PAD(&tmp))
589 	    nwin->_pad = tmp._pad;
590 
591 	if (old_format) {
592 	    T(("reading old-format screen dump"));
593 	    for (n = 0; n <= nwin->_maxy; n++) {
594 		if (read_block(nwin->_line[n].text, linesize, filep) < 0) {
595 		    delwin(nwin);
596 		    returnWin(0);
597 		}
598 	    }
599 	}
600 #if NCURSES_EXT_PUTWIN
601 	else {
602 	    char *txt = 0;
603 	    bool success = TRUE;
604 	    NCURSES_CH_T prior = blank;
605 
606 	    T(("reading new-format screen dump"));
607 	    for (n = 0; n <= nwin->_maxy; n++) {
608 		long row;
609 		char *next;
610 
611 		if ((txt = read_txt(filep)) == 0) {
612 		    T(("...failed to read string for row %d", n + 1));
613 		    success = FALSE;
614 		    break;
615 		}
616 		row = strtol(txt, &next, 10);
617 		if (row != (n + 1) || *next != ':') {
618 		    T(("...failed to read row-number %d", n + 1));
619 		    success = FALSE;
620 		    break;
621 		}
622 
623 		if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx
624 			     + 1) < 0) {
625 		    T(("...failed to read cells for row %d", n + 1));
626 		    success = FALSE;
627 		    break;
628 		}
629 		free(txt);
630 		txt = 0;
631 	    }
632 
633 	    if (!success) {
634 		free(txt);
635 		delwin(nwin);
636 		returnWin(0);
637 	    }
638 	}
639 #endif
640 	touchwin(nwin);
641     }
642     returnWin(nwin);
643 }
644 
645 #if NCURSES_SP_FUNCS
646 NCURSES_EXPORT(WINDOW *)
getwin(FILE * filep)647 getwin(FILE *filep)
648 {
649     return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
650 }
651 #endif
652 
653 #if NCURSES_EXT_PUTWIN
654 static void
encode_attr(char * target,ARG_SLIMIT (limit)attr_t source,attr_t prior,int source_color,int prior_color)655 encode_attr(char *target, ARG_SLIMIT(limit)
656 	    attr_t source,
657 	    attr_t prior,
658 	    int source_color,
659 	    int prior_color)
660 {
661 #if USE_STRING_HACKS && HAVE_SNPRINTF
662     char *base = target;
663 #endif
664     source &= ~A_CHARTEXT;
665     prior &= ~A_CHARTEXT;
666 
667     *target = '\0';
668     if ((source != prior) || (source_color != prior_color)) {
669 	size_t n;
670 	bool first = TRUE;
671 
672 	*target++ = MARKER;
673 	*target++ = L_CURL;
674 
675 	for (n = 0; n < SIZEOF(scr_attrs); ++n) {
676 	    if ((source & scr_attrs[n].attr) != 0 ||
677 		((source & ALL_BUT_COLOR) == 0 &&
678 		 (scr_attrs[n].attr == A_NORMAL))) {
679 		if (first) {
680 		    first = FALSE;
681 		} else {
682 		    *target++ = '|';
683 		}
684 		_nc_STRCPY(target, scr_attrs[n].name, limit);
685 		target += strlen(target);
686 	    }
687 	}
688 	if (source_color != prior_color) {
689 	    if (!first)
690 		*target++ = '|';
691 	    _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
692 	    target += strlen(target);
693 	}
694 
695 	*target++ = R_CURL;
696 	*target = '\0';
697     }
698 }
699 
700 static void
encode_cell(char * target,ARG_SLIMIT (limit)CARG_CH_T source,CARG_CH_T previous)701 encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
702 {
703 #if USE_STRING_HACKS && HAVE_SNPRINTF
704     char *base = target;
705 #endif
706 #if NCURSES_WIDECHAR
707     size_t n;
708     int source_pair = GetPair(*source);
709     int previous_pair = GetPair(*previous);
710 
711     *target = '\0';
712     if ((previous->attr != source->attr) || (previous_pair != source_pair)) {
713 	encode_attr(target, CUR_SLIMIT
714 		    source->attr,
715 		    previous->attr,
716 		    source_pair,
717 		    previous_pair);
718     }
719     target += strlen(target);
720 #if NCURSES_EXT_COLORS
721     if (previous->ext_color != source->ext_color) {
722 	_nc_SPRINTF(target, CUR_SLIMIT
723 		    "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
724     }
725 #endif
726     for (n = 0; n < SIZEOF(source->chars); ++n) {
727 	unsigned uch = (unsigned) source->chars[n];
728 	if (uch == 0)
729 	    continue;
730 	if (n) {
731 	    *target++ = MARKER;
732 	    *target++ = APPEND;
733 	}
734 	*target++ = MARKER;
735 	if (uch > 0xffff) {
736 	    _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch);
737 	} else if (uch > 0xff) {
738 	    _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch);
739 	} else if (uch < 32 || uch >= 127) {
740 	    _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff);
741 	} else {
742 	    switch (uch) {
743 	    case ' ':
744 		_nc_STRCPY(target, "s", limit);
745 		break;
746 	    case MARKER:
747 		*target++ = MARKER;
748 		*target = '\0';
749 		break;
750 	    default:
751 		--target;
752 		_nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
753 		break;
754 	    }
755 	}
756 	target += strlen(target);
757     }
758 #else
759     chtype ch = CharOfD(source);
760 
761     *target = '\0';
762     if (AttrOfD(previous) != AttrOfD(source)) {
763 	encode_attr(target, CUR_SLIMIT
764 		    AttrOfD(source),
765 		    AttrOfD(previous),
766 		    GetPair(source),
767 		    GetPair(previous));
768     }
769     target += strlen(target);
770     *target++ = MARKER;
771     if (ch < 32 || ch >= 127) {
772 	_nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
773     } else {
774 	switch (ch) {
775 	case ' ':
776 	    _nc_STRCPY(target, "s", limit);
777 	    break;
778 	case MARKER:
779 	    *target++ = MARKER;
780 	    *target = '\0';
781 	    break;
782 	default:
783 	    --target;
784 	    _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
785 	    break;
786 	}
787     }
788 #endif
789 }
790 #endif
791 
792 NCURSES_EXPORT(int)
putwin(WINDOW * win,FILE * filep)793 putwin(WINDOW *win, FILE *filep)
794 {
795     int code = ERR;
796 
797     T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
798 
799 #if NCURSES_EXT_PUTWIN
800     if (win != 0) {
801 	const char *version = curses_version();
802 	char buffer[1024];
803 	NCURSES_CH_T last_cell;
804 	int y;
805 
806 	memset(&last_cell, 0, sizeof(last_cell));
807 
808 	clearerr(filep);
809 
810 	/*
811 	 * Our magic number is technically nonprinting, but aside from that,
812 	 * all of the file is printable ASCII.
813 	 */
814 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
815 	PUTS(my_magic);
816 	PUTS(version);
817 	PUTS("\n");
818 	for (y = 0; y < (int) SIZEOF(scr_params); ++y) {
819 	    const char *name = scr_params[y].name;
820 	    const char *data = (char *) win + scr_params[y].offset;
821 	    const void *dp = (const void *) data;
822 	    attr_t attr;
823 
824 	    *buffer = '\0';
825 	    if (!strncmp(name, "_pad.", (size_t) 5) && !IS_PAD(win)) {
826 		continue;
827 	    }
828 	    switch (scr_params[y].type) {
829 	    case pATTR:
830 		attr = (*(const attr_t *) dp) & ~A_CHARTEXT;
831 		encode_attr(buffer, TOP_SLIMIT
832 			    (*(const attr_t *) dp) & ~A_CHARTEXT,
833 			    A_NORMAL,
834 			    COLOR_PAIR((int) attr),
835 			    0);
836 		break;
837 	    case pBOOL:
838 		if (!(*(const bool *) data)) {
839 		    continue;
840 		}
841 		_nc_STRCPY(buffer, name, sizeof(buffer));
842 		name = "flag";
843 		break;
844 	    case pCHAR:
845 		attr = (*(const attr_t *) dp);
846 		encode_attr(buffer, TOP_SLIMIT
847 			    * (const attr_t *) dp,
848 			    A_NORMAL,
849 			    COLOR_PAIR((int) attr),
850 			    0);
851 		break;
852 	    case pINT:
853 		if (!(*(const int *) dp))
854 		    continue;
855 		_nc_SPRINTF(buffer, TOP_SLIMIT
856 			    "%d", *(const int *) dp);
857 		break;
858 	    case pSHORT:
859 		if (!(*(const short *) dp))
860 		    continue;
861 		_nc_SPRINTF(buffer, TOP_SLIMIT
862 			    "%d", *(const short *) dp);
863 		break;
864 	    case pSIZE:
865 		if (!(*(const NCURSES_SIZE_T *) dp))
866 		    continue;
867 		_nc_SPRINTF(buffer, TOP_SLIMIT
868 			    "%d", *(const NCURSES_SIZE_T *) dp);
869 		break;
870 #if NCURSES_WIDECHAR
871 	    case pCCHAR:
872 		encode_cell(buffer, TOP_SLIMIT
873 			    (CARG_CH_T) dp, CHREF(last_cell));
874 		break;
875 #endif
876 	    }
877 	    /*
878 	     * Only write non-default data.
879 	     */
880 	    if (*buffer != '\0') {
881 		if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
882 		    || ferror(filep))
883 		    returnCode(code);
884 	    }
885 	}
886 	/* Write row-data */
887 	fprintf(filep, "rows:\n");
888 	for (y = 0; y <= win->_maxy; y++) {
889 	    NCURSES_CH_T *data = win->_line[y].text;
890 	    int x;
891 	    if (fprintf(filep, "%d:", y + 1) <= 0
892 		|| ferror(filep))
893 		returnCode(code);
894 	    for (x = 0; x <= win->_maxx; x++) {
895 #if NCURSES_WIDECHAR
896 		int len = _nc_wacs_width(data[x].chars[0]);
897 		encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
898 		last_cell = data[x];
899 		PUTS(buffer);
900 		if (len > 1)
901 		    x += (len - 1);
902 #else
903 		encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
904 		last_cell = data[x];
905 		PUTS(buffer);
906 #endif
907 	    }
908 	    PUTS("\n");
909 	}
910 	code = OK;
911     }
912 #else
913     /*
914      * This is the original putwin():
915      * A straight binary dump is simple, but its format can depend on whether
916      * ncurses is compiled with wide-character support, and also may depend
917      * on the version of ncurses, e.g., if the WINDOW structure is extended.
918      */
919     if (win != 0) {
920 	size_t len = (size_t) (win->_maxx + 1);
921 	int y;
922 
923 	clearerr(filep);
924 	if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
925 	    || ferror(filep))
926 	      returnCode(code);
927 
928 	for (y = 0; y <= win->_maxy; y++) {
929 	    if (fwrite(win->_line[y].text,
930 		       sizeof(NCURSES_CH_T), len, filep) != len
931 		|| ferror(filep)) {
932 		returnCode(code);
933 	    }
934 	}
935 	code = OK;
936     }
937 #endif
938     returnCode(code);
939 }
940 
941 /*
942  * Replace a window covering the whole screen, i.e., newscr or curscr.
943  */
944 static WINDOW *
replace_window(WINDOW * target,FILE * source)945 replace_window(WINDOW *target, FILE *source)
946 {
947     WINDOW *result = getwin(source);
948 #if NCURSES_EXT_FUNCS
949     if (result != NULL) {
950 	if (getmaxx(result) != getmaxx(target)
951 	    || getmaxy(result) != getmaxy(target)) {
952 	    int code = wresize(result,
953 			       1 + getmaxy(target),
954 			       1 + getmaxx(target));
955 	    if (code != OK) {
956 		delwin(result);
957 		result = NULL;
958 	    }
959 	}
960     }
961 #endif
962     delwin(target);
963     return result;
964 }
965 
966 NCURSES_EXPORT(int)
NCURSES_SP_NAME(scr_restore)967 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
968 {
969     FILE *fp = 0;
970     int code = ERR;
971 
972     T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
973 
974     if (_nc_access(file, R_OK) >= 0
975 	&& (fp = safe_fopen(file, BIN_R)) != 0) {
976 	NewScreen(SP_PARM) = replace_window(NewScreen(SP_PARM), fp);
977 #if !USE_REENTRANT
978 	newscr = NewScreen(SP_PARM);
979 #endif
980 	(void) fclose(fp);
981 	if (NewScreen(SP_PARM) != 0) {
982 	    code = OK;
983 	}
984     }
985     returnCode(code);
986 }
987 
988 #if NCURSES_SP_FUNCS
989 NCURSES_EXPORT(int)
scr_restore(const char * file)990 scr_restore(const char *file)
991 {
992     return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
993 }
994 #endif
995 
996 NCURSES_EXPORT(int)
scr_dump(const char * file)997 scr_dump(const char *file)
998 {
999     int result;
1000     FILE *fp = 0;
1001 
1002     T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
1003 
1004     if (_nc_access(file, W_OK) < 0
1005 	|| (fp = safe_fopen(file, BIN_W)) == 0) {
1006 	result = ERR;
1007     } else {
1008 	(void) putwin(newscr, fp);
1009 	(void) fclose(fp);
1010 	result = OK;
1011     }
1012     returnCode(result);
1013 }
1014 
1015 NCURSES_EXPORT(int)
NCURSES_SP_NAME(scr_init)1016 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
1017 {
1018     int code = ERR;
1019 
1020     T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1021 
1022     if (SP_PARM != 0 &&
1023 #ifdef USE_TERM_DRIVER
1024 	InfoOf(SP_PARM).caninit
1025 #else
1026 	!(exit_ca_mode && non_rev_rmcup)
1027 #endif
1028 	) {
1029 	FILE *fp = 0;
1030 
1031 	if (_nc_access(file, R_OK) >= 0
1032 	    && (fp = safe_fopen(file, BIN_R)) != 0) {
1033 	    CurScreen(SP_PARM) = replace_window(CurScreen(SP_PARM), fp);
1034 #if !USE_REENTRANT
1035 	    curscr = CurScreen(SP_PARM);
1036 #endif
1037 	    (void) fclose(fp);
1038 	    if (CurScreen(SP_PARM) != 0) {
1039 		code = OK;
1040 	    }
1041 	}
1042     }
1043     returnCode(code);
1044 }
1045 
1046 #if NCURSES_SP_FUNCS
1047 NCURSES_EXPORT(int)
scr_init(const char * file)1048 scr_init(const char *file)
1049 {
1050     return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1051 }
1052 #endif
1053 
1054 NCURSES_EXPORT(int)
NCURSES_SP_NAME(scr_set)1055 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
1056 {
1057     int code = ERR;
1058 
1059     T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1060 
1061     if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
1062 	delwin(NewScreen(SP_PARM));
1063 	NewScreen(SP_PARM) = dupwin(curscr);
1064 #if !USE_REENTRANT
1065 	newscr = NewScreen(SP_PARM);
1066 #endif
1067 	if (NewScreen(SP_PARM) != 0) {
1068 	    code = OK;
1069 	}
1070     }
1071     returnCode(code);
1072 }
1073 
1074 #if NCURSES_SP_FUNCS
1075 NCURSES_EXPORT(int)
scr_set(const char * file)1076 scr_set(const char *file)
1077 {
1078     return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
1079 }
1080 #endif
1081