1 /****************************************************************************
2 * Copyright 2019-2021,2023 Thomas E. Dickey *
3 * Copyright 1998-2017,2018 Free Software Foundation, Inc. *
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: *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
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. *
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 *
28 ****************************************************************************/
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 ****************************************************************************/
37 #include <curses.priv.h>
42 #define CUR SP_TERMTYPE
45 MODULE_ID("$Id: lib_screen.c,v 1.105 2023/04/28 20:58:54 tom Exp $")
47 #define MAX_SIZE 0x3fff /* 16k is big enough for a window or pad */
55 #if USE_STRING_HACKS && HAVE_SNPRINTF
56 #define ARG_SLIMIT(name) size_t name,
58 #define ARG_SLIMIT(name) /* nothing */
61 #define CUR_SLIMIT _nc_SLIMIT(limit - (size_t) (target - base))
62 #define TOP_SLIMIT _nc_SLIMIT(sizeof(buffer))
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).
69 static const char my_magic[] =
70 {'\210', '\210', '\210', '\210', 0};
72 #if NCURSES_EXT_PUTWIN
79 ,pSIZE /* NCURSES_SIZE_T */
96 #define DATA(name) { { #name }, A_##name }
97 static const SCR_ATTRS scr_attrs[] =
122 #define DATA(name, type) { { #name }, type, offsetof(WINDOW, name) }
124 static const SCR_PARAMS scr_params[] =
132 DATA(_flags, pSHORT),
135 DATA(_notimeout, pBOOL),
137 DATA(_leaveok, pBOOL),
138 DATA(_scroll, pBOOL),
143 DATA(_use_keypad, pBOOL),
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),
155 DATA(_bkgrnd, pCCHAR),
156 #if NCURSES_EXT_COLORS
163 static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
166 * Allocate and read a line of text. Caller must free it.
172 char *result = malloc(limit);
182 if (used + 2 >= limit) {
184 buffer = realloc(result, limit);
195 result[used++] = (char) ch;
197 } while (ch != '\n');
200 result[--used] = '\0';
201 TR(TRACE_IEVENT, ("READ:%s", result));
202 } else if (used == 0) {
211 decode_attr(char *source, attr_t *target, int *color)
215 TR(TRACE_IEVENT, ("decode_attr '%s'", source));
218 if (source[0] == MARKER && source[1] == L_CURL) {
221 } else if (source[0] == R_CURL) {
228 if (source[0] == GUTTER) {
230 } else if (*next == 'C') {
234 while (isdigit(UChar(*next))) {
235 value = value * 10 + (*next++ - '0');
238 pair = (unsigned) ((value > 256)
240 : COLOR_PAIR(value));
244 while (isalnum(UChar(*next))) {
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;
267 decode_char(char *source, int *target)
271 const char digits[] = "0123456789abcdef";
273 TR(TRACE_IEVENT, ("decode_char '%s'", source));
306 while (limit-- > 0) {
307 char *find = strchr(digits, *source++);
308 int ch = (find != 0) ? (int) (find - digits) : -1;
310 if (ch >= 0 && ch < base) {
324 decode_chtype(char *source, chtype fillin, chtype *target)
326 attr_t attr = ChAttrOf(fillin);
327 int color = PAIR_NUMBER((int) attr);
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 */
340 decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
343 attr_t attr = fillin->attr;
344 wchar_t chars[CCHARW_MAX];
348 TR(TRACE_IEVENT, ("decode_cchar '%s'", source));
350 #if NCURSES_EXT_COLORS
351 color = fillin->ext_color;
353 color = (int) PAIR_NUMBER(attr);
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) {
362 source = decode_char(source, &value);
363 if (++append < CCHARW_MAX) {
364 chars[append] = (wchar_t) value;
367 setcchar(target, chars, attr, (short) color, &color);
373 read_win(WINDOW *win, FILE *fp)
383 memset(win, 0, sizeof(WINDOW));
387 char *txt = read_txt(fp);
391 if (!strcmp(txt, "rows:")) {
396 if ((value = strchr(txt, '=')) == 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);
406 switch (scr_params[n].type) {
408 (void) decode_attr(value, data, &color);
411 *(bool *) data = TRUE;
415 decode_chtype(value, prior2, data);
418 *(int *) data = atoi(value);
421 *(short *) data = (short) atoi(value);
424 *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value);
429 decode_cchar(value, &prior, data);
442 read_row(char *source, NCURSES_CH_T *prior, NCURSES_CH_T *target, int length)
444 while (*source != '\0' && length > 0) {
448 source = decode_cchar(source, prior, target);
449 len = _nc_wacs_width(target->chars[0]);
453 SetWidecExt(CHDEREF(target), 0);
454 for (n = 1; n < len; ++n) {
455 target[n] = target[0];
456 SetWidecExt(CHDEREF(target), n);
462 source = decode_chtype(source, *prior, target);
468 while (length-- > 0) {
471 /* FIXME - see what error conditions should apply if I need to return ERR */
474 #endif /* NCURSES_EXT_PUTWIN */
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.
483 read_block(void *target, size_t length, FILE *fp)
486 char *buffer = target;
489 while (length-- != 0) {
495 *buffer++ = (char) ch;
500 NCURSES_EXPORT(WINDOW *)
501 NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
504 bool old_format = FALSE;
506 T((T_CALLED("getwin(%p)"), (void *) filep));
513 * Read the first 4 bytes to determine first if this is an old-format
514 * screen-dump, or new-format.
516 if (read_block(&tmp, (size_t) 4, filep) < 0) {
520 * If this is a new-format file, and we do not support it, give up.
522 if (!memcmp(&tmp, my_magic, (size_t) 4)) {
523 #if NCURSES_EXT_PUTWIN
524 if (read_win(&tmp, filep) < 0)
527 } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
534 * Check the window-size:
536 if (tmp._maxy == 0 ||
537 tmp._maxy > MAX_SIZE ||
539 tmp._maxx > MAX_SIZE) {
544 nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
548 nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
550 tmp._maxx + 1, 0, 0);
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.
560 size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
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);
571 WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp);
572 nwin->_nc_bkgd = tmp._nc_bkgd;
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;
585 nwin->_regtop = tmp._regtop;
586 nwin->_regbottom = tmp._regbottom;
589 nwin->_pad = tmp._pad;
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) {
600 #if NCURSES_EXT_PUTWIN
604 NCURSES_CH_T prior = blank;
606 T(("reading new-format screen dump"));
607 for (n = 0; n <= nwin->_maxy; n++) {
611 if ((txt = read_txt(filep)) == 0) {
612 T(("...failed to read string for row %d", n + 1));
616 row = strtol(txt, &next, 10);
617 if (row != (n + 1) || *next != ':') {
618 T(("...failed to read row-number %d", n + 1));
623 if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx
625 T(("...failed to read cells for row %d", n + 1));
646 NCURSES_EXPORT(WINDOW *)
649 return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
653 #if NCURSES_EXT_PUTWIN
655 encode_attr(char *target, ARG_SLIMIT(limit)
661 #if USE_STRING_HACKS && HAVE_SNPRINTF
664 source &= ~A_CHARTEXT;
665 prior &= ~A_CHARTEXT;
668 if ((source != prior) || (source_color != prior_color)) {
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))) {
684 _nc_STRCPY(target, scr_attrs[n].name, limit);
685 target += strlen(target);
688 if (source_color != prior_color) {
691 _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
692 target += strlen(target);
701 encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
703 #if USE_STRING_HACKS && HAVE_SNPRINTF
708 int source_pair = GetPair(*source);
709 int previous_pair = GetPair(*previous);
712 if ((previous->attr != source->attr) || (previous_pair != source_pair)) {
713 encode_attr(target, CUR_SLIMIT
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);
726 for (n = 0; n < SIZEOF(source->chars); ++n) {
727 unsigned uch = (unsigned) source->chars[n];
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);
744 _nc_STRCPY(target, "s", limit);
752 _nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
756 target += strlen(target);
759 chtype ch = CharOfD(source);
762 if (AttrOfD(previous) != AttrOfD(source)) {
763 encode_attr(target, CUR_SLIMIT
769 target += strlen(target);
771 if (ch < 32 || ch >= 127) {
772 _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
776 _nc_STRCPY(target, "s", limit);
784 _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
793 putwin(WINDOW *win, FILE *filep)
797 T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
799 #if NCURSES_EXT_PUTWIN
801 const char *version = curses_version();
803 NCURSES_CH_T last_cell;
806 memset(&last_cell, 0, sizeof(last_cell));
811 * Our magic number is technically nonprinting, but aside from that,
812 * all of the file is printable ASCII.
814 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
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;
825 if (!strncmp(name, "_pad.", (size_t) 5) && !IS_PAD(win)) {
828 switch (scr_params[y].type) {
830 attr = (*(const attr_t *) dp) & ~A_CHARTEXT;
831 encode_attr(buffer, TOP_SLIMIT
832 (*(const attr_t *) dp) & ~A_CHARTEXT,
834 COLOR_PAIR((int) attr),
838 if (!(*(const bool *) data)) {
841 _nc_STRCPY(buffer, name, sizeof(buffer));
845 attr = (*(const attr_t *) dp);
846 encode_attr(buffer, TOP_SLIMIT
847 * (const attr_t *) dp,
849 COLOR_PAIR((int) attr),
853 if (!(*(const int *) dp))
855 _nc_SPRINTF(buffer, TOP_SLIMIT
856 "%d", *(const int *) dp);
859 if (!(*(const short *) dp))
861 _nc_SPRINTF(buffer, TOP_SLIMIT
862 "%d", *(const short *) dp);
865 if (!(*(const NCURSES_SIZE_T *) dp))
867 _nc_SPRINTF(buffer, TOP_SLIMIT
868 "%d", *(const NCURSES_SIZE_T *) dp);
872 encode_cell(buffer, TOP_SLIMIT
873 (CARG_CH_T) dp, CHREF(last_cell));
878 * Only write non-default data.
880 if (*buffer != '\0') {
881 if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
887 fprintf(filep, "rows:\n");
888 for (y = 0; y <= win->_maxy; y++) {
889 NCURSES_CH_T *data = win->_line[y].text;
891 if (fprintf(filep, "%d:", y + 1) <= 0
894 for (x = 0; x <= win->_maxx; x++) {
896 int len = _nc_wacs_width(data[x].chars[0]);
897 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
903 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
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.
920 size_t len = (size_t) (win->_maxx + 1);
924 if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
928 for (y = 0; y <= win->_maxy; y++) {
929 if (fwrite(win->_line[y].text,
930 sizeof(NCURSES_CH_T), len, filep) != len
942 * Replace a window covering the whole screen, i.e., newscr or curscr.
945 replace_window(WINDOW *target, FILE *source)
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,
954 1 + getmaxx(target));
967 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
972 T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
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);
978 newscr = NewScreen(SP_PARM);
981 if (NewScreen(SP_PARM) != 0) {
990 scr_restore(const char *file)
992 return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
997 scr_dump(const char *file)
1002 T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
1004 if (_nc_access(file, W_OK) < 0
1005 || (fp = safe_fopen(file, BIN_W)) == 0) {
1008 (void) putwin(newscr, fp);
1016 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
1020 T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1023 #ifdef USE_TERM_DRIVER
1024 InfoOf(SP_PARM).caninit
1026 !(exit_ca_mode && non_rev_rmcup)
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);
1035 curscr = CurScreen(SP_PARM);
1038 if (CurScreen(SP_PARM) != 0) {
1046 #if NCURSES_SP_FUNCS
1048 scr_init(const char *file)
1050 return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1055 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
1059 T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1061 if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
1062 delwin(NewScreen(SP_PARM));
1063 NewScreen(SP_PARM) = dupwin(curscr);
1065 newscr = NewScreen(SP_PARM);
1067 if (NewScreen(SP_PARM) != 0) {
1074 #if NCURSES_SP_FUNCS
1076 scr_set(const char *file)
1078 return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);