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