blob: e2647b54e2ebad81063bcf5b3f7d2e9f145dbbad [file] [log] [blame]
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04002 * Copyright 2019-2021,2023 Thomas E. Dickey *
3 * Copyright 1998-2017,2018 Free Software Foundation, Inc. *
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304 * *
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 *
Steve Kondikae271bc2015-11-15 02:50:53 +010034 * and: Juergen Pfeifer 2009 *
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +053035 ****************************************************************************/
36
37#include <curses.priv.h>
38
Steve Kondikae271bc2015-11-15 02:50:53 +010039#include <ctype.h>
40
41#ifndef CUR
42#define CUR SP_TERMTYPE
43#endif
44
micky3879b9f5e72025-07-08 18:04:53 -040045MODULE_ID("$Id: lib_screen.c,v 1.105 2023/04/28 20:58:54 tom Exp $")
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +053046
47#define MAX_SIZE 0x3fff /* 16k is big enough for a window or pad */
48
Steve Kondikae271bc2015-11-15 02:50:53 +010049#define MARKER '\\'
50#define APPEND '+'
51#define GUTTER '|'
52#define L_CURL '{'
53#define R_CURL '}'
54
micky3879b9f5e72025-07-08 18:04:53 -040055#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
Steve Kondikae271bc2015-11-15 02:50:53 +010064/*
micky3879b9f5e72025-07-08 18:04:53 -040065 * Use 0x888888 as the magic number for new-format files, since it cannot be
Steve Kondikae271bc2015-11-15 02:50:53 +010066 * 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 */
69static const char my_magic[] =
micky3879b9f5e72025-07-08 18:04:53 -040070{'\210', '\210', '\210', '\210', 0};
Steve Kondikae271bc2015-11-15 02:50:53 +010071
72#if NCURSES_EXT_PUTWIN
73typedef 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
85typedef struct {
86 const char name[11];
87 attr_t attr;
88} SCR_ATTRS;
89
90typedef struct {
91 const char name[17];
92 PARAM_TYPE type;
Steve Kondikae271bc2015-11-15 02:50:53 +010093 size_t offset;
94} SCR_PARAMS;
95
96#define DATA(name) { { #name }, A_##name }
97static 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
micky3879b9f5e72025-07-08 18:04:53 -0400122#define DATA(name, type) { { #name }, type, offsetof(WINDOW, name) }
Steve Kondikae271bc2015-11-15 02:50:53 +0100123
124static 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
163static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
164
165/*
166 * Allocate and read a line of text. Caller must free it.
167 */
168static char *
169read_txt(FILE *fp)
170{
171 size_t limit = 1024;
Steve Kondikae271bc2015-11-15 02:50:53 +0100172 char *result = malloc(limit);
173 char *buffer;
174
175 if (result != 0) {
176 int ch = 0;
micky3879b9f5e72025-07-08 18:04:53 -0400177 size_t used = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +0100178
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';
micky3879b9f5e72025-07-08 18:04:53 -0400201 TR(TRACE_IEVENT, ("READ:%s", result));
Steve Kondikae271bc2015-11-15 02:50:53 +0100202 } else if (used == 0) {
203 free(result);
204 result = 0;
205 }
206 }
207 return result;
208}
209
210static char *
211decode_attr(char *source, attr_t *target, int *color)
212{
213 bool found = FALSE;
214
micky3879b9f5e72025-07-08 18:04:53 -0400215 TR(TRACE_IEVENT, ("decode_attr '%s'", source));
Steve Kondikae271bc2015-11-15 02:50:53 +0100216
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
266static char *
267decode_char(char *source, int *target)
268{
269 int limit = 0;
270 int base = 16;
271 const char digits[] = "0123456789abcdef";
272
micky3879b9f5e72025-07-08 18:04:53 -0400273 TR(TRACE_IEVENT, ("decode_char '%s'", source));
Steve Kondikae271bc2015-11-15 02:50:53 +0100274 *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
323static char *
324decode_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
micky3879b9f5e72025-07-08 18:04:53 -0400330 TR(TRACE_IEVENT, ("decode_chtype '%s'", source));
Steve Kondikae271bc2015-11-15 02:50:53 +0100331 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
339static char *
340decode_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
micky3879b9f5e72025-07-08 18:04:53 -0400348 TR(TRACE_IEVENT, ("decode_cchar '%s'", source));
Steve Kondikae271bc2015-11-15 02:50:53 +0100349 *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 }
micky3879b9f5e72025-07-08 18:04:53 -0400367 setcchar(target, chars, attr, (short) color, &color);
Steve Kondikae271bc2015-11-15 02:50:53 +0100368 return source;
369}
370#endif
371
372static int
373read_win(WINDOW *win, FILE *fp)
374{
375 int code = ERR;
Steve Kondikae271bc2015-11-15 02:50:53 +0100376 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 (;;) {
micky3879b9f5e72025-07-08 18:04:53 -0400385 char *name;
386 char *value;
387 char *txt = read_txt(fp);
388
Steve Kondikae271bc2015-11-15 02:50:53 +0100389 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
441static int
micky3879b9f5e72025-07-08 18:04:53 -0400442read_row(char *source, NCURSES_CH_T *prior, NCURSES_CH_T *target, int length)
Steve Kondikae271bc2015-11-15 02:50:53 +0100443{
444 while (*source != '\0' && length > 0) {
445#if NCURSES_WIDECHAR
micky3879b9f5e72025-07-08 18:04:53 -0400446 int len;
447
Steve Kondikae271bc2015-11-15 02:50:53 +0100448 source = decode_cchar(source, prior, target);
micky3879b9f5e72025-07-08 18:04:53 -0400449 len = _nc_wacs_width(target->chars[0]);
Steve Kondikae271bc2015-11-15 02:50:53 +0100450 if (len > 1) {
micky3879b9f5e72025-07-08 18:04:53 -0400451 int n;
452
Steve Kondikae271bc2015-11-15 02:50:53 +0100453 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 */
482static int
483read_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
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530500NCURSES_EXPORT(WINDOW *)
Steve Kondikae271bc2015-11-15 02:50:53 +0100501NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530502{
503 WINDOW tmp, *nwin;
Steve Kondikae271bc2015-11-15 02:50:53 +0100504 bool old_format = FALSE;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530505
Steve Kondikae271bc2015-11-15 02:50:53 +0100506 T((T_CALLED("getwin(%p)"), (void *) filep));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530507
Steve Kondikae271bc2015-11-15 02:50:53 +0100508 if (filep == 0) {
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530509 returnWin(0);
Steve Kondikae271bc2015-11-15 02:50:53 +0100510 }
511
512 /*
513 * Read the first 4 bytes to determine first if this is an old-format
514 * screen-dump, or new-format.
515 */
micky3879b9f5e72025-07-08 18:04:53 -0400516 if (read_block(&tmp, (size_t) 4, filep) < 0) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100517 returnWin(0);
518 }
519 /*
520 * If this is a new-format file, and we do not support it, give up.
521 */
micky3879b9f5e72025-07-08 18:04:53 -0400522 if (!memcmp(&tmp, my_magic, (size_t) 4)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100523#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 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530542
micky3879b9f5e72025-07-08 18:04:53 -0400543 if (IS_PAD(&tmp)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100544 nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
545 tmp._maxy + 1,
546 tmp._maxx + 1);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530547 } else {
Steve Kondikae271bc2015-11-15 02:50:53 +0100548 nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
549 tmp._maxy + 1,
550 tmp._maxx + 1, 0, 0);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530551 }
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) {
micky3879b9f5e72025-07-08 18:04:53 -0400559 int n;
Steve Kondikae271bc2015-11-15 02:50:53 +0100560 size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
561
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530562 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
micky3879b9f5e72025-07-08 18:04:53 -0400588 if (IS_PAD(&tmp))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530589 nwin->_pad = tmp._pad;
590
Steve Kondikae271bc2015-11-15 02:50:53 +0100591 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 {
micky3879b9f5e72025-07-08 18:04:53 -0400602 char *txt = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +0100603 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);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530635 delwin(nwin);
636 returnWin(0);
637 }
638 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100639#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530640 touchwin(nwin);
641 }
642 returnWin(nwin);
643}
644
Steve Kondikae271bc2015-11-15 02:50:53 +0100645#if NCURSES_SP_FUNCS
646NCURSES_EXPORT(WINDOW *)
647getwin(FILE *filep)
648{
649 return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
650}
651#endif
652
653#if NCURSES_EXT_PUTWIN
654static void
micky3879b9f5e72025-07-08 18:04:53 -0400655encode_attr(char *target, ARG_SLIMIT(limit)
656 attr_t source,
657 attr_t prior,
658 int source_color,
659 int prior_color)
Steve Kondikae271bc2015-11-15 02:50:53 +0100660{
micky3879b9f5e72025-07-08 18:04:53 -0400661#if USE_STRING_HACKS && HAVE_SNPRINTF
662 char *base = target;
663#endif
Steve Kondikae271bc2015-11-15 02:50:53 +0100664 source &= ~A_CHARTEXT;
665 prior &= ~A_CHARTEXT;
666
667 *target = '\0';
micky3879b9f5e72025-07-08 18:04:53 -0400668 if ((source != prior) || (source_color != prior_color)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100669 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 }
micky3879b9f5e72025-07-08 18:04:53 -0400684 _nc_STRCPY(target, scr_attrs[n].name, limit);
Steve Kondikae271bc2015-11-15 02:50:53 +0100685 target += strlen(target);
686 }
687 }
micky3879b9f5e72025-07-08 18:04:53 -0400688 if (source_color != prior_color) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100689 if (!first)
690 *target++ = '|';
micky3879b9f5e72025-07-08 18:04:53 -0400691 _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
Steve Kondikae271bc2015-11-15 02:50:53 +0100692 target += strlen(target);
693 }
694
695 *target++ = R_CURL;
696 *target = '\0';
697 }
698}
699
700static void
micky3879b9f5e72025-07-08 18:04:53 -0400701encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
Steve Kondikae271bc2015-11-15 02:50:53 +0100702{
micky3879b9f5e72025-07-08 18:04:53 -0400703#if USE_STRING_HACKS && HAVE_SNPRINTF
704 char *base = target;
705#endif
Steve Kondikae271bc2015-11-15 02:50:53 +0100706#if NCURSES_WIDECHAR
707 size_t n;
micky3879b9f5e72025-07-08 18:04:53 -0400708 int source_pair = GetPair(*source);
709 int previous_pair = GetPair(*previous);
Steve Kondikae271bc2015-11-15 02:50:53 +0100710
711 *target = '\0';
micky3879b9f5e72025-07-08 18:04:53 -0400712 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);
Steve Kondikae271bc2015-11-15 02:50:53 +0100718 }
719 target += strlen(target);
720#if NCURSES_EXT_COLORS
721 if (previous->ext_color != source->ext_color) {
micky3879b9f5e72025-07-08 18:04:53 -0400722 _nc_SPRINTF(target, CUR_SLIMIT
723 "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
Steve Kondikae271bc2015-11-15 02:50:53 +0100724 }
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) {
micky3879b9f5e72025-07-08 18:04:53 -0400736 _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch);
Steve Kondikae271bc2015-11-15 02:50:53 +0100737 } else if (uch > 0xff) {
micky3879b9f5e72025-07-08 18:04:53 -0400738 _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch);
Steve Kondikae271bc2015-11-15 02:50:53 +0100739 } else if (uch < 32 || uch >= 127) {
micky3879b9f5e72025-07-08 18:04:53 -0400740 _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff);
Steve Kondikae271bc2015-11-15 02:50:53 +0100741 } else {
742 switch (uch) {
743 case ' ':
micky3879b9f5e72025-07-08 18:04:53 -0400744 _nc_STRCPY(target, "s", limit);
Steve Kondikae271bc2015-11-15 02:50:53 +0100745 break;
746 case MARKER:
747 *target++ = MARKER;
748 *target = '\0';
749 break;
750 default:
micky3879b9f5e72025-07-08 18:04:53 -0400751 --target;
752 _nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
Steve Kondikae271bc2015-11-15 02:50:53 +0100753 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)) {
micky3879b9f5e72025-07-08 18:04:53 -0400763 encode_attr(target, CUR_SLIMIT
764 AttrOfD(source),
765 AttrOfD(previous),
766 GetPair(source),
767 GetPair(previous));
Steve Kondikae271bc2015-11-15 02:50:53 +0100768 }
769 target += strlen(target);
770 *target++ = MARKER;
771 if (ch < 32 || ch >= 127) {
micky3879b9f5e72025-07-08 18:04:53 -0400772 _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
Steve Kondikae271bc2015-11-15 02:50:53 +0100773 } else {
774 switch (ch) {
775 case ' ':
micky3879b9f5e72025-07-08 18:04:53 -0400776 _nc_STRCPY(target, "s", limit);
Steve Kondikae271bc2015-11-15 02:50:53 +0100777 break;
778 case MARKER:
779 *target++ = MARKER;
780 *target = '\0';
781 break;
782 default:
micky3879b9f5e72025-07-08 18:04:53 -0400783 --target;
784 _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
Steve Kondikae271bc2015-11-15 02:50:53 +0100785 break;
786 }
787 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100788#endif
789}
790#endif
791
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530792NCURSES_EXPORT(int)
793putwin(WINDOW *win, FILE *filep)
794{
795 int code = ERR;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530796
Steve Kondikae271bc2015-11-15 02:50:53 +0100797 T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530798
Steve Kondikae271bc2015-11-15 02:50:53 +0100799#if NCURSES_EXT_PUTWIN
800 if (win != 0) {
801 const char *version = curses_version();
802 char buffer[1024];
803 NCURSES_CH_T last_cell;
micky3879b9f5e72025-07-08 18:04:53 -0400804 int y;
Steve Kondikae271bc2015-11-15 02:50:53 +0100805
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;
micky3879b9f5e72025-07-08 18:04:53 -0400822 attr_t attr;
Steve Kondikae271bc2015-11-15 02:50:53 +0100823
824 *buffer = '\0';
micky3879b9f5e72025-07-08 18:04:53 -0400825 if (!strncmp(name, "_pad.", (size_t) 5) && !IS_PAD(win)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100826 continue;
827 }
828 switch (scr_params[y].type) {
829 case pATTR:
micky3879b9f5e72025-07-08 18:04:53 -0400830 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);
Steve Kondikae271bc2015-11-15 02:50:53 +0100836 break;
837 case pBOOL:
838 if (!(*(const bool *) data)) {
839 continue;
840 }
micky3879b9f5e72025-07-08 18:04:53 -0400841 _nc_STRCPY(buffer, name, sizeof(buffer));
Steve Kondikae271bc2015-11-15 02:50:53 +0100842 name = "flag";
843 break;
844 case pCHAR:
micky3879b9f5e72025-07-08 18:04:53 -0400845 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);
Steve Kondikae271bc2015-11-15 02:50:53 +0100851 break;
852 case pINT:
853 if (!(*(const int *) dp))
854 continue;
micky3879b9f5e72025-07-08 18:04:53 -0400855 _nc_SPRINTF(buffer, TOP_SLIMIT
856 "%d", *(const int *) dp);
Steve Kondikae271bc2015-11-15 02:50:53 +0100857 break;
858 case pSHORT:
859 if (!(*(const short *) dp))
860 continue;
micky3879b9f5e72025-07-08 18:04:53 -0400861 _nc_SPRINTF(buffer, TOP_SLIMIT
862 "%d", *(const short *) dp);
Steve Kondikae271bc2015-11-15 02:50:53 +0100863 break;
864 case pSIZE:
865 if (!(*(const NCURSES_SIZE_T *) dp))
866 continue;
micky3879b9f5e72025-07-08 18:04:53 -0400867 _nc_SPRINTF(buffer, TOP_SLIMIT
868 "%d", *(const NCURSES_SIZE_T *) dp);
Steve Kondikae271bc2015-11-15 02:50:53 +0100869 break;
870#if NCURSES_WIDECHAR
871 case pCCHAR:
micky3879b9f5e72025-07-08 18:04:53 -0400872 encode_cell(buffer, TOP_SLIMIT
873 (CARG_CH_T) dp, CHREF(last_cell));
Steve Kondikae271bc2015-11-15 02:50:53 +0100874 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
micky3879b9f5e72025-07-08 18:04:53 -0400896 int len = _nc_wacs_width(data[x].chars[0]);
897 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
Steve Kondikae271bc2015-11-15 02:50:53 +0100898 last_cell = data[x];
899 PUTS(buffer);
900 if (len > 1)
901 x += (len - 1);
902#else
micky3879b9f5e72025-07-08 18:04:53 -0400903 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
Steve Kondikae271bc2015-11-15 02:50:53 +0100904 last_cell = data[x];
905 PUTS(buffer);
906#endif
907 }
908 PUTS("\n");
909 }
micky3879b9f5e72025-07-08 18:04:53 -0400910 code = OK;
Steve Kondikae271bc2015-11-15 02:50:53 +0100911 }
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 */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530919 if (win != 0) {
920 size_t len = (size_t) (win->_maxx + 1);
micky3879b9f5e72025-07-08 18:04:53 -0400921 int y;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530922
923 clearerr(filep);
Steve Kondikae271bc2015-11-15 02:50:53 +0100924 if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530925 || ferror(filep))
926 returnCode(code);
927
Steve Kondikae271bc2015-11-15 02:50:53 +0100928 for (y = 0; y <= win->_maxy; y++) {
929 if (fwrite(win->_line[y].text,
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530930 sizeof(NCURSES_CH_T), len, filep) != len
931 || ferror(filep)) {
932 returnCode(code);
933 }
934 }
935 code = OK;
936 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100937#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530938 returnCode(code);
939}
940
micky3879b9f5e72025-07-08 18:04:53 -0400941/*
942 * Replace a window covering the whole screen, i.e., newscr or curscr.
943 */
944static WINDOW *
945replace_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
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530966NCURSES_EXPORT(int)
Steve Kondikae271bc2015-11-15 02:50:53 +0100967NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530968{
969 FILE *fp = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +0100970 int code = ERR;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530971
Steve Kondikae271bc2015-11-15 02:50:53 +0100972 T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530973
Steve Kondikae271bc2015-11-15 02:50:53 +0100974 if (_nc_access(file, R_OK) >= 0
micky3879b9f5e72025-07-08 18:04:53 -0400975 && (fp = safe_fopen(file, BIN_R)) != 0) {
976 NewScreen(SP_PARM) = replace_window(NewScreen(SP_PARM), fp);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530977#if !USE_REENTRANT
Steve Kondikae271bc2015-11-15 02:50:53 +0100978 newscr = NewScreen(SP_PARM);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530979#endif
980 (void) fclose(fp);
Steve Kondikae271bc2015-11-15 02:50:53 +0100981 if (NewScreen(SP_PARM) != 0) {
982 code = OK;
983 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530984 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100985 returnCode(code);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530986}
987
Steve Kondikae271bc2015-11-15 02:50:53 +0100988#if NCURSES_SP_FUNCS
989NCURSES_EXPORT(int)
990scr_restore(const char *file)
991{
992 return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
993}
994#endif
995
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530996NCURSES_EXPORT(int)
997scr_dump(const char *file)
998{
Steve Kondikae271bc2015-11-15 02:50:53 +0100999 int result;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301000 FILE *fp = 0;
1001
1002 T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
1003
1004 if (_nc_access(file, W_OK) < 0
micky3879b9f5e72025-07-08 18:04:53 -04001005 || (fp = safe_fopen(file, BIN_W)) == 0) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001006 result = ERR;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301007 } else {
1008 (void) putwin(newscr, fp);
1009 (void) fclose(fp);
Steve Kondikae271bc2015-11-15 02:50:53 +01001010 result = OK;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301011 }
Steve Kondikae271bc2015-11-15 02:50:53 +01001012 returnCode(result);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301013}
1014
1015NCURSES_EXPORT(int)
Steve Kondikae271bc2015-11-15 02:50:53 +01001016NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
1017{
Steve Kondikae271bc2015-11-15 02:50:53 +01001018 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 ) {
micky3879b9f5e72025-07-08 18:04:53 -04001029 FILE *fp = 0;
1030
Steve Kondikae271bc2015-11-15 02:50:53 +01001031 if (_nc_access(file, R_OK) >= 0
micky3879b9f5e72025-07-08 18:04:53 -04001032 && (fp = safe_fopen(file, BIN_R)) != 0) {
1033 CurScreen(SP_PARM) = replace_window(CurScreen(SP_PARM), fp);
Steve Kondikae271bc2015-11-15 02:50:53 +01001034#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
1047NCURSES_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301048scr_init(const char *file)
1049{
Steve Kondikae271bc2015-11-15 02:50:53 +01001050 return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1051}
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301052#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01001053
1054NCURSES_EXPORT(int)
1055NCURSES_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 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301070 }
Steve Kondikae271bc2015-11-15 02:50:53 +01001071 returnCode(code);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301072}
1073
Steve Kondikae271bc2015-11-15 02:50:53 +01001074#if NCURSES_SP_FUNCS
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301075NCURSES_EXPORT(int)
1076scr_set(const char *file)
1077{
Steve Kondikae271bc2015-11-15 02:50:53 +01001078 return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301079}
Steve Kondikae271bc2015-11-15 02:50:53 +01001080#endif