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