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