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