1 /****************************************************************************
2 * Copyright (c) 1998-2011,2015 Free Software Foundation, Inc. *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
31 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
32 * and: Thomas E. Dickey 1996 on *
33 * and: Juergen Pfeifer 2009 *
34 ****************************************************************************/
36 #include <curses.priv.h>
41 #define CUR SP_TERMTYPE
44 MODULE_ID("$Id: lib_screen.c,v 1.77 2015/07/04 22:54:14 tom Exp $")
46 #define MAX_SIZE 0x3fff /* 16k is big enough for a window or pad */
55 * Use 0x8888 as the magic number for new-format files, since it cannot be
56 * mistaken for the _cury/_curx pair of 16-bit numbers which start the old
57 * format. It happens to be unused in the file 5.22 database (2015/03/07).
59 static const char my_magic
[] =
60 {'\210', '\210', '\210', '\210'};
62 #if NCURSES_EXT_PUTWIN
69 ,pSIZE
/* NCURSES_SIZE_T */
87 #define DATA(name) { { #name }, A_##name }
88 static const SCR_ATTRS scr_attrs
[] =
113 #define sizeof2(type,name) sizeof(((type *)0)->name)
114 #define DATA(name, type) { { #name }, type, sizeof2(WINDOW, name), offsetof(WINDOW, name) }
116 static const SCR_PARAMS scr_params
[] =
124 DATA(_flags
, pSHORT
),
127 DATA(_notimeout
, pBOOL
),
129 DATA(_leaveok
, pBOOL
),
130 DATA(_scroll
, pBOOL
),
135 DATA(_use_keypad
, pBOOL
),
137 DATA(_regtop
, pSIZE
),
138 DATA(_regbottom
, pSIZE
),
139 DATA(_pad
._pad_y
, pSIZE
),
140 DATA(_pad
._pad_x
, pSIZE
),
141 DATA(_pad
._pad_top
, pSIZE
),
142 DATA(_pad
._pad_left
, pSIZE
),
143 DATA(_pad
._pad_bottom
, pSIZE
),
144 DATA(_pad
._pad_right
, pSIZE
),
145 DATA(_yoffset
, pSIZE
),
147 DATA(_bkgrnd
, pCCHAR
),
148 #if NCURSES_EXT_COLORS
155 static const NCURSES_CH_T blank
= NewChar(BLANK_TEXT
);
158 * Allocate and read a line of text. Caller must free it.
165 char *result
= malloc(limit
);
174 if (used
+ 2 >= limit
) {
176 buffer
= realloc(result
, limit
);
187 result
[used
++] = (char) ch
;
189 } while (ch
!= '\n');
192 result
[--used
] = '\0';
193 T(("READ:%s", result
));
194 } else if (used
== 0) {
203 decode_attr(char *source
, attr_t
*target
, int *color
)
207 T(("decode_attr '%s'", source
));
210 if (source
[0] == MARKER
&& source
[1] == L_CURL
) {
213 } else if (source
[0] == R_CURL
) {
220 if (source
[0] == GUTTER
) {
222 } else if (*next
== 'C') {
226 while (isdigit(UChar(*next
))) {
227 value
= value
* 10 + (*next
++ - '0');
230 pair
= (unsigned) ((value
> 256)
232 : COLOR_PAIR(value
));
236 while (isalnum(UChar(*next
))) {
239 for (n
= 0; n
< SIZEOF(scr_attrs
); ++n
) {
240 if ((size_t) (next
- source
) == strlen(scr_attrs
[n
].name
)) {
241 if (scr_attrs
[n
].attr
) {
242 *target
|= scr_attrs
[n
].attr
;
259 decode_char(char *source
, int *target
)
263 const char digits
[] = "0123456789abcdef";
265 T(("decode_char '%s'", source
));
298 while (limit
-- > 0) {
299 char *find
= strchr(digits
, *source
++);
300 int ch
= (find
!= 0) ? (int) (find
- digits
) : -1;
302 if (ch
>= 0 && ch
< base
) {
316 decode_chtype(char *source
, chtype fillin
, chtype
*target
)
318 attr_t attr
= ChAttrOf(fillin
);
319 int color
= PAIR_NUMBER((int) attr
);
322 T(("decode_chtype '%s'", source
));
323 source
= decode_attr(source
, &attr
, &color
);
324 source
= decode_char(source
, &value
);
325 *target
= (ChCharOf(value
) | attr
| (chtype
) COLOR_PAIR(color
));
326 /* FIXME - ignore combining characters */
332 decode_cchar(char *source
, cchar_t
*fillin
, cchar_t
*target
)
335 attr_t attr
= fillin
->attr
;
336 wchar_t chars
[CCHARW_MAX
];
340 T(("decode_cchar '%s'", source
));
342 #if NCURSES_EXT_COLORS
343 color
= fillin
->ext_color
;
345 color
= (int) PAIR_NUMBER(attr
);
347 source
= decode_attr(source
, &attr
, &color
);
348 memset(chars
, 0, sizeof(chars
));
349 source
= decode_char(source
, &value
);
350 chars
[0] = (wchar_t) value
;
351 /* handle combining characters */
352 while (source
[0] == MARKER
&& source
[1] == APPEND
) {
354 source
= decode_char(source
, &value
);
355 if (++append
< CCHARW_MAX
) {
356 chars
[append
] = (wchar_t) value
;
359 setcchar(target
, chars
, attr
, (short) color
, NULL
);
365 read_win(WINDOW
*win
, FILE *fp
)
378 memset(win
, 0, sizeof(WINDOW
));
383 if (!strcmp(txt
, "rows:")) {
388 if ((value
= strchr(txt
, '=')) == 0) {
393 name
= !strcmp(txt
, "flag") ? value
: txt
;
394 for (n
= 0; n
< SIZEOF(scr_params
); ++n
) {
395 if (!strcmp(name
, scr_params
[n
].name
)) {
396 void *data
= (void *) ((char *) win
+ scr_params
[n
].offset
);
398 switch (scr_params
[n
].type
) {
400 (void) decode_attr(value
, data
, &color
);
403 *(bool *) data
= TRUE
;
407 decode_chtype(value
, prior2
, data
);
410 *(int *) data
= atoi(value
);
413 *(short *) data
= (short) atoi(value
);
416 *(NCURSES_SIZE_T
*) data
= (NCURSES_SIZE_T
) atoi(value
);
421 decode_cchar(value
, &prior
, data
);
434 read_row(char *source
, NCURSES_CH_T
* prior
, NCURSES_CH_T
* target
, int length
)
436 while (*source
!= '\0' && length
> 0) {
439 source
= decode_cchar(source
, prior
, target
);
440 len
= wcwidth(target
->chars
[0]);
442 SetWidecExt(CHDEREF(target
), 0);
443 for (n
= 1; n
< len
; ++n
) {
444 target
[n
] = target
[0];
445 SetWidecExt(CHDEREF(target
), n
);
451 source
= decode_chtype(source
, *prior
, target
);
457 while (length
-- > 0) {
460 /* FIXME - see what error conditions should apply if I need to return ERR */
463 #endif /* NCURSES_EXT_PUTWIN */
466 * Originally, getwin/putwin used fread/fwrite, because they used binary data.
467 * The new format uses printable ASCII, which does not have as predictable
468 * sizes. Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need
469 * special handling if we want to read screen dumps from an older library.
472 read_block(void *target
, size_t length
, FILE *fp
)
475 char *buffer
= target
;
478 while (length
-- != 0) {
484 *buffer
++ = (char) ch
;
489 NCURSES_EXPORT(WINDOW
*)
490 NCURSES_SP_NAME(getwin
) (NCURSES_SP_DCLx
FILE *filep
)
494 bool old_format
= FALSE
;
496 T((T_CALLED("getwin(%p)"), (void *) filep
));
503 * Read the first 4 bytes to determine first if this is an old-format
504 * screen-dump, or new-format.
506 if (read_block(&tmp
, 4, filep
) < 0) {
510 * If this is a new-format file, and we do not support it, give up.
512 if (!memcmp(&tmp
, my_magic
, 4)) {
513 #if NCURSES_EXT_PUTWIN
514 if (read_win(&tmp
, filep
) < 0)
517 } else if (read_block(((char *) &tmp
) + 4, sizeof(WINDOW
) - 4, filep
) < 0) {
524 * Check the window-size:
526 if (tmp
._maxy
== 0 ||
527 tmp
._maxy
> MAX_SIZE
||
529 tmp
._maxx
> MAX_SIZE
) {
533 if (tmp
._flags
& _ISPAD
) {
534 nwin
= NCURSES_SP_NAME(newpad
) (NCURSES_SP_ARGx
538 nwin
= NCURSES_SP_NAME(newwin
) (NCURSES_SP_ARGx
540 tmp
._maxx
+ 1, 0, 0);
544 * We deliberately do not restore the _parx, _pary, or _parent
545 * fields, because the window hierarchy within which they
546 * made sense is probably gone.
549 size_t linesize
= sizeof(NCURSES_CH_T
) * (size_t) (tmp
._maxx
+ 1);
551 nwin
->_curx
= tmp
._curx
;
552 nwin
->_cury
= tmp
._cury
;
553 nwin
->_maxy
= tmp
._maxy
;
554 nwin
->_maxx
= tmp
._maxx
;
555 nwin
->_begy
= tmp
._begy
;
556 nwin
->_begx
= tmp
._begx
;
557 nwin
->_yoffset
= tmp
._yoffset
;
558 nwin
->_flags
= tmp
._flags
& ~(_SUBWIN
);
560 WINDOW_ATTRS(nwin
) = WINDOW_ATTRS(&tmp
);
561 nwin
->_nc_bkgd
= tmp
._nc_bkgd
;
563 nwin
->_notimeout
= tmp
._notimeout
;
564 nwin
->_clear
= tmp
._clear
;
565 nwin
->_leaveok
= tmp
._leaveok
;
566 nwin
->_idlok
= tmp
._idlok
;
567 nwin
->_idcok
= tmp
._idcok
;
568 nwin
->_immed
= tmp
._immed
;
569 nwin
->_scroll
= tmp
._scroll
;
570 nwin
->_sync
= tmp
._sync
;
571 nwin
->_use_keypad
= tmp
._use_keypad
;
572 nwin
->_delay
= tmp
._delay
;
574 nwin
->_regtop
= tmp
._regtop
;
575 nwin
->_regbottom
= tmp
._regbottom
;
577 if (tmp
._flags
& _ISPAD
)
578 nwin
->_pad
= tmp
._pad
;
581 T(("reading old-format screen dump"));
582 for (n
= 0; n
<= nwin
->_maxy
; n
++) {
583 if (read_block(nwin
->_line
[n
].text
, linesize
, filep
) < 0) {
589 #if NCURSES_EXT_PUTWIN
593 NCURSES_CH_T prior
= blank
;
595 T(("reading new-format screen dump"));
596 for (n
= 0; n
<= nwin
->_maxy
; n
++) {
600 if ((txt
= read_txt(filep
)) == 0) {
601 T(("...failed to read string for row %d", n
+ 1));
605 row
= strtol(txt
, &next
, 10);
606 if (row
!= (n
+ 1) || *next
!= ':') {
607 T(("...failed to read row-number %d", n
+ 1));
612 if (read_row(++next
, &prior
, nwin
->_line
[n
].text
, tmp
._maxx
614 T(("...failed to read cells for row %d", n
+ 1));
635 NCURSES_EXPORT(WINDOW
*)
638 return NCURSES_SP_NAME(getwin
) (CURRENT_SCREEN
, filep
);
642 #if NCURSES_EXT_PUTWIN
644 encode_attr(char *target
, attr_t source
, attr_t prior
)
646 source
&= ~A_CHARTEXT
;
647 prior
&= ~A_CHARTEXT
;
650 if (source
!= prior
) {
657 for (n
= 0; n
< SIZEOF(scr_attrs
); ++n
) {
658 if ((source
& scr_attrs
[n
].attr
) != 0 ||
659 ((source
& ALL_BUT_COLOR
) == 0 &&
660 (scr_attrs
[n
].attr
== A_NORMAL
))) {
666 strcpy(target
, scr_attrs
[n
].name
);
667 target
+= strlen(target
);
670 if ((source
& A_COLOR
) != (prior
& A_COLOR
)) {
673 sprintf(target
, "C%d", PAIR_NUMBER((int) source
));
674 target
+= strlen(target
);
683 encode_cell(char *target
, CARG_CH_T source
, CARG_CH_T previous
)
689 if (previous
->attr
!= source
->attr
) {
690 encode_attr(target
, source
->attr
, previous
->attr
);
692 target
+= strlen(target
);
693 #if NCURSES_EXT_COLORS
694 if (previous
->ext_color
!= source
->ext_color
) {
695 sprintf(target
, "%c%cC%d%c", MARKER
, L_CURL
, source
->ext_color
, R_CURL
);
698 for (n
= 0; n
< SIZEOF(source
->chars
); ++n
) {
699 unsigned uch
= (unsigned) source
->chars
[n
];
708 sprintf(target
, "U%08x", uch
);
709 } else if (uch
> 0xff) {
710 sprintf(target
, "u%04x", uch
);
711 } else if (uch
< 32 || uch
>= 127) {
712 sprintf(target
, "%03o", uch
& 0xff);
723 sprintf(--target
, "%c", uch
);
727 target
+= strlen(target
);
730 chtype ch
= CharOfD(source
);
733 if (AttrOfD(previous
) != AttrOfD(source
)) {
734 encode_attr(target
, AttrOfD(source
), AttrOfD(previous
));
736 target
+= strlen(target
);
738 if (ch
< 32 || ch
>= 127) {
739 sprintf(target
, "%03o", UChar(ch
));
750 sprintf(--target
, "%c", UChar(ch
));
754 target
+= strlen(target
);
760 putwin(WINDOW
*win
, FILE *filep
)
765 T((T_CALLED("putwin(%p,%p)"), (void *) win
, (void *) filep
));
767 #if NCURSES_EXT_PUTWIN
769 const char *version
= curses_version();
771 NCURSES_CH_T last_cell
;
773 memset(&last_cell
, 0, sizeof(last_cell
));
778 * Our magic number is technically nonprinting, but aside from that,
779 * all of the file is printable ASCII.
781 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
785 for (y
= 0; y
< (int) SIZEOF(scr_params
); ++y
) {
786 const char *name
= scr_params
[y
].name
;
787 const char *data
= (char *) win
+ scr_params
[y
].offset
;
788 const void *dp
= (const void *) data
;
791 if (!strncmp(name
, "_pad.", 5) && !(win
->_flags
& _ISPAD
)) {
794 switch (scr_params
[y
].type
) {
796 encode_attr(buffer
, (*(const attr_t
*) dp
) & ~A_CHARTEXT
, A_NORMAL
);
799 if (!(*(const bool *) data
)) {
802 strcpy(buffer
, name
);
806 encode_attr(buffer
, *(const attr_t
*) dp
, A_NORMAL
);
809 if (!(*(const int *) dp
))
811 sprintf(buffer
, "%d", *(const int *) dp
);
814 if (!(*(const short *) dp
))
816 sprintf(buffer
, "%d", *(const short *) dp
);
819 if (!(*(const NCURSES_SIZE_T
*) dp
))
821 sprintf(buffer
, "%d", *(const NCURSES_SIZE_T
*) dp
);
825 encode_cell(buffer
, (CARG_CH_T
) dp
, CHREF(last_cell
));
830 * Only write non-default data.
832 if (*buffer
!= '\0') {
833 if (fprintf(filep
, "%s=%s\n", name
, buffer
) <= 0
839 fprintf(filep
, "rows:\n");
840 for (y
= 0; y
<= win
->_maxy
; y
++) {
841 NCURSES_CH_T
*data
= win
->_line
[y
].text
;
843 if (fprintf(filep
, "%d:", y
+ 1) <= 0
846 for (x
= 0; x
<= win
->_maxx
; x
++) {
848 int len
= wcwidth(data
[x
].chars
[0]);
849 encode_cell(buffer
, CHREF(data
[x
]), CHREF(last_cell
));
855 encode_cell(buffer
, CHREF(data
[x
]), CHREF(last_cell
));
865 * This is the original putwin():
866 * A straight binary dump is simple, but its format can depend on whether
867 * ncurses is compiled with wide-character support, and also may depend
868 * on the version of ncurses, e.g., if the WINDOW structure is extended.
871 size_t len
= (size_t) (win
->_maxx
+ 1);
874 if (fwrite(win
, sizeof(WINDOW
), (size_t) 1, filep
) != 1
878 for (y
= 0; y
<= win
->_maxy
; y
++) {
879 if (fwrite(win
->_line
[y
].text
,
880 sizeof(NCURSES_CH_T
), len
, filep
) != len
892 NCURSES_SP_NAME(scr_restore
) (NCURSES_SP_DCLx
const char *file
)
897 T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM
, _nc_visbuf(file
)));
899 if (_nc_access(file
, R_OK
) >= 0
900 && (fp
= fopen(file
, "rb")) != 0) {
901 delwin(NewScreen(SP_PARM
));
902 NewScreen(SP_PARM
) = getwin(fp
);
904 newscr
= NewScreen(SP_PARM
);
907 if (NewScreen(SP_PARM
) != 0) {
916 scr_restore(const char *file
)
918 return NCURSES_SP_NAME(scr_restore
) (CURRENT_SCREEN
, file
);
923 scr_dump(const char *file
)
928 T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file
)));
930 if (_nc_access(file
, W_OK
) < 0
931 || (fp
= fopen(file
, "wb")) == 0) {
934 (void) putwin(newscr
, fp
);
942 NCURSES_SP_NAME(scr_init
) (NCURSES_SP_DCLx
const char *file
)
947 T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM
, _nc_visbuf(file
)));
950 #ifdef USE_TERM_DRIVER
951 InfoOf(SP_PARM
).caninit
953 !(exit_ca_mode
&& non_rev_rmcup
)
956 if (_nc_access(file
, R_OK
) >= 0
957 && (fp
= fopen(file
, "rb")) != 0) {
958 delwin(CurScreen(SP_PARM
));
959 CurScreen(SP_PARM
) = getwin(fp
);
961 curscr
= CurScreen(SP_PARM
);
964 if (CurScreen(SP_PARM
) != 0) {
974 scr_init(const char *file
)
976 return NCURSES_SP_NAME(scr_init
) (CURRENT_SCREEN
, file
);
981 NCURSES_SP_NAME(scr_set
) (NCURSES_SP_DCLx
const char *file
)
985 T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM
, _nc_visbuf(file
)));
987 if (NCURSES_SP_NAME(scr_init
) (NCURSES_SP_ARGx file
) == OK
) {
988 delwin(NewScreen(SP_PARM
));
989 NewScreen(SP_PARM
) = dupwin(curscr
);
991 newscr
= NewScreen(SP_PARM
);
993 if (NewScreen(SP_PARM
) != 0) {
1000 #if NCURSES_SP_FUNCS
1002 scr_set(const char *file
)
1004 return NCURSES_SP_NAME(scr_set
) (CURRENT_SCREEN
, file
);