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.70 2015/03/29 00:16:00 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 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 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 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);
186 result[used++] = (char) ch;
188 } while (ch != '\n');
191 result[--used] = '\0';
192 T(("READ:%s", result));
193 } else if (used == 0) {
202 decode_attr(char *source, attr_t *target, int *color)
206 T(("decode_attr '%s'", source));
209 if (source[0] == MARKER && source[1] == L_CURL) {
212 } else if (source[0] == R_CURL) {
219 if (source[0] == GUTTER) {
221 } else if (*next == 'C') {
224 while (isdigit(UChar(*next))) {
225 value = value * 10 + (*next++ - '0');
229 *target |= COLOR_PAIR(255);
231 *target |= COLOR_PAIR(value);
235 while (isalnum(UChar(*next))) {
238 for (n = 0; n < SIZEOF(scr_attrs); ++n) {
239 if ((size_t) (next - source) == strlen(scr_attrs[n].name)) {
240 if (scr_attrs[n].attr) {
241 *target |= scr_attrs[n].attr;
258 decode_char(char *source, int *target)
262 const char digits[] = "0123456789abcdef";
264 T(("decode_char '%s'", source));
297 while (limit-- > 0) {
298 char *find = strchr(digits, *source++);
299 int ch = (find != 0) ? (int) (find - digits) : -1;
301 if (ch >= 0 && ch < base) {
315 decode_chtype(char *source, chtype fillin, chtype *target)
317 attr_t attr = ChAttrOf(fillin);
318 int color = PAIR_NUMBER(attr);
321 T(("decode_chtype '%s'", source));
322 source = decode_attr(source, &attr, &color);
323 source = decode_char(source, &value);
324 *target = ChCharOf(value) | attr | COLOR_PAIR(color);
325 /* FIXME - ignore combining characters */
331 decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
334 attr_t attr = fillin->attr;
335 wchar_t chars[CCHARW_MAX];
338 T(("decode_cchar '%s'", source));
340 #if NCURSES_EXT_COLORS
341 color = fillin->ext_color;
343 color = (int) PAIR_NUMBER(attr);
345 source = decode_attr(source, &attr, &color);
346 memset(chars, 0, sizeof(chars));
347 source = decode_char(source, &chars[0]);
348 /* handle combining characters */
349 while (source[0] == MARKER && source[1] == APPEND) {
352 source = decode_char(source, &value);
353 if (append++ < CCHARW_MAX) {
354 chars[append] = value;
357 setcchar(target, chars, attr, (short) color, NULL);
363 read_win(WINDOW *win, FILE *fp)
376 memset(win, 0, sizeof(WINDOW));
381 if (!strcmp(txt, "rows:")) {
386 if ((value = strchr(txt, '=')) == 0) {
391 name = !strcmp(txt, "flag") ? value : txt;
392 for (n = 0; n < SIZEOF(scr_params); ++n) {
393 if (!strcmp(name, scr_params[n].name)) {
394 void *data = (void *) ((char *) win + scr_params[n].offset);
396 switch (scr_params[n].type) {
398 (void) decode_attr(value, data, &color);
401 *(bool *) data = TRUE;
405 decode_chtype(value, prior2, data);
408 *(int *) data = atoi(value);
411 *(short *) data = (short) atoi(value);
414 *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value);
419 decode_cchar(value, &prior, data);
432 read_row(char *source, NCURSES_CH_T * prior, NCURSES_CH_T * target, int length)
434 while (*source != '\0' && length > 0) {
437 source = decode_cchar(source, prior, target);
438 len = wcwidth(target->chars[0]);
440 SetWidecExt(CHDEREF(target), 0);
441 for (n = 1; n < len; ++n) {
442 target[n] = target[0];
443 SetWidecExt(CHDEREF(target), n);
449 source = decode_chtype(source, *prior, target);
455 while (length-- > 0) {
458 /* FIXME - see what error conditions should apply if I need to return ERR */
461 #endif /* NCURSES_EXT_PUTWIN */
464 * Originally, getwin/putwin used fread/fwrite, because they used binary data.
465 * The new format uses printable ASCII, which does not have as predictable
466 * sizes. Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need
467 * special handling if we want to read screen dumps from an older library.
470 read_block(void *target, size_t length, FILE *fp)
473 char *buffer = target;
476 while (length-- != 0) {
482 *buffer++ = (char) ch;
487 NCURSES_EXPORT(WINDOW *)
488 NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
492 bool old_format = FALSE;
494 T((T_CALLED("getwin(%p)"), (void *) filep));
501 * Read the first 4 bytes to determine first if this is an old-format
502 * screen-dump, or new-format.
504 if (read_block(&tmp, 4, filep) < 0) {
508 * If this is a new-format file, and we do not support it, give up.
510 if (!memcmp(&tmp, my_magic, 4)) {
511 #if NCURSES_EXT_PUTWIN
512 if (read_win(&tmp, filep) < 0)
515 } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
522 * Check the window-size:
524 if (tmp._maxy == 0 ||
525 tmp._maxy > MAX_SIZE ||
527 tmp._maxx > MAX_SIZE) {
531 if (tmp._flags & _ISPAD) {
532 nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
536 nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
538 tmp._maxx + 1, 0, 0);
542 * We deliberately do not restore the _parx, _pary, or _parent
543 * fields, because the window hierarchy within which they
544 * made sense is probably gone.
547 size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
549 nwin->_curx = tmp._curx;
550 nwin->_cury = tmp._cury;
551 nwin->_maxy = tmp._maxy;
552 nwin->_maxx = tmp._maxx;
553 nwin->_begy = tmp._begy;
554 nwin->_begx = tmp._begx;
555 nwin->_yoffset = tmp._yoffset;
556 nwin->_flags = tmp._flags & ~(_SUBWIN);
558 WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp);
559 nwin->_nc_bkgd = tmp._nc_bkgd;
561 nwin->_notimeout = tmp._notimeout;
562 nwin->_clear = tmp._clear;
563 nwin->_leaveok = tmp._leaveok;
564 nwin->_idlok = tmp._idlok;
565 nwin->_idcok = tmp._idcok;
566 nwin->_immed = tmp._immed;
567 nwin->_scroll = tmp._scroll;
568 nwin->_sync = tmp._sync;
569 nwin->_use_keypad = tmp._use_keypad;
570 nwin->_delay = tmp._delay;
572 nwin->_regtop = tmp._regtop;
573 nwin->_regbottom = tmp._regbottom;
575 if (tmp._flags & _ISPAD)
576 nwin->_pad = tmp._pad;
579 T(("reading old-format screen dump"));
580 for (n = 0; n <= nwin->_maxy; n++) {
581 if (read_block(nwin->_line[n].text, linesize, filep) < 0) {
587 #if NCURSES_EXT_PUTWIN
591 NCURSES_CH_T prior = blank;
593 T(("reading new-format screen dump"));
594 for (n = 0; n <= nwin->_maxy; n++) {
598 if ((txt = read_txt(filep)) == 0) {
599 T(("...failed to read string for row %d", n + 1));
603 row = strtol(txt, &next, 10);
604 if (row != (n + 1) || *next != ':') {
605 T(("...failed to read row-number %d", n + 1));
610 if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx
612 T(("...failed to read cells for row %d", n + 1));
633 NCURSES_EXPORT(WINDOW *)
636 return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
640 #if NCURSES_EXT_PUTWIN
642 encode_attr(char *target, attr_t source, attr_t prior)
644 source &= ~A_CHARTEXT;
645 prior &= ~A_CHARTEXT;
648 if (source != prior) {
655 for (n = 0; n < SIZEOF(scr_attrs); ++n) {
656 if ((source & scr_attrs[n].attr) != 0 ||
657 ((source & ALL_BUT_COLOR) == 0 &&
658 (scr_attrs[n].attr == A_NORMAL))) {
664 strcpy(target, scr_attrs[n].name);
665 target += strlen(target);
668 if ((source & A_COLOR) != (prior & A_COLOR)) {
671 sprintf(target, "C%d", PAIR_NUMBER(source));
672 target += strlen(target);
681 encode_cell(char *target, CARG_CH_T source, CARG_CH_T previous)
687 if (previous->attr != source->attr) {
688 encode_attr(target, source->attr, previous->attr);
690 target += strlen(target);
691 #if NCURSES_EXT_COLORS
692 if (previous->ext_color != source->ext_color) {
693 sprintf(target, "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
696 for (n = 0; n < SIZEOF(source->chars); ++n) {
697 if (source->chars[n] == 0)
704 if (source->chars[n] > 0xffff) {
705 sprintf(target, "U%08x", source->chars[n]);
706 } else if (source->chars[n] > 0xff) {
707 sprintf(target, "u%04x", source->chars[n]);
708 } else if (source->chars[n] < 32 || source->chars[n] >= 127) {
709 sprintf(target, "%03o", source->chars[n] & 0xff);
711 switch (source->chars[n]) {
720 sprintf(--target, "%c", source->chars[n]);
724 target += strlen(target);
727 chtype ch = CharOfD(source);
730 if (AttrOfD(previous) != AttrOfD(source)) {
731 encode_attr(target, AttrOfD(source), AttrOfD(previous));
733 target += strlen(target);
735 if (ch < 32 || ch >= 127) {
736 sprintf(target, "%03o", ch);
747 sprintf(--target, "%c", ch);
751 target += strlen(target);
757 putwin(WINDOW *win, FILE *filep)
762 T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
764 #if NCURSES_EXT_PUTWIN
766 const char *version = curses_version();
768 NCURSES_CH_T last_cell;
770 memset(&last_cell, 0, sizeof(last_cell));
775 * Our magic number is technically nonprinting, but aside from that,
776 * all of the file is printable ASCII.
778 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
782 for (y = 0; y < (int) SIZEOF(scr_params); ++y) {
783 const char *name = scr_params[y].name;
784 const char *data = (char *) win + scr_params[y].offset;
785 const void *dp = (const void *) data;
788 if (!strncmp(name, "_pad.", 5) && !(win->_flags & _ISPAD)) {
791 switch (scr_params[y].type) {
793 encode_attr(buffer, (*(const attr_t *) dp) & ~A_CHARTEXT, A_NORMAL);
796 if (!(*(const bool *) data)) {
799 strcpy(buffer, name);
803 encode_attr(buffer, *(const attr_t *) dp, A_NORMAL);
806 if (!(*(const int *) dp))
808 sprintf(buffer, "%d", *(const int *) dp);
811 if (!(*(const short *) dp))
813 sprintf(buffer, "%d", *(const short *) dp);
816 if (!(*(const NCURSES_SIZE_T *) dp))
818 sprintf(buffer, "%d", *(const NCURSES_SIZE_T *) dp);
822 encode_cell(buffer, (CARG_CH_T) dp, CHREF(last_cell));
827 * Only write non-default data.
829 if (*buffer != '\0') {
830 if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
836 fprintf(filep, "rows:\n");
837 for (y = 0; y <= win->_maxy; y++) {
838 NCURSES_CH_T *data = win->_line[y].text;
840 if (fprintf(filep, "%d:", y + 1) <= 0
843 for (x = 0; x <= win->_maxx; x++) {
844 int len = wcwidth(data[x].chars[0]);
845 encode_cell(buffer, CHREF(data[x]), CHREF(last_cell));
856 * This is the original putwin():
857 * A straight binary dump is simple, but its format can depend on whether
858 * ncurses is compiled with wide-character support, and also may depend
859 * on the version of ncurses, e.g., if the WINDOW structure is extended.
862 size_t len = (size_t) (win->_maxx + 1);
865 if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
869 for (y = 0; y <= win->_maxy; y++) {
870 if (fwrite(win->_line[y].text,
871 sizeof(NCURSES_CH_T), len, filep) != len
883 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
888 T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
890 if (_nc_access(file, R_OK) >= 0
891 && (fp = fopen(file, "rb")) != 0) {
892 delwin(NewScreen(SP_PARM));
893 NewScreen(SP_PARM) = getwin(fp);
895 newscr = NewScreen(SP_PARM);
898 if (NewScreen(SP_PARM) != 0) {
907 scr_restore(const char *file)
909 return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
914 scr_dump(const char *file)
919 T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
921 if (_nc_access(file, W_OK) < 0
922 || (fp = fopen(file, "wb")) == 0) {
925 (void) putwin(newscr, fp);
933 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
938 T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
941 #ifdef USE_TERM_DRIVER
942 InfoOf(SP_PARM).caninit
944 !(exit_ca_mode && non_rev_rmcup)
947 if (_nc_access(file, R_OK) >= 0
948 && (fp = fopen(file, "rb")) != 0) {
949 delwin(CurScreen(SP_PARM));
950 CurScreen(SP_PARM) = getwin(fp);
952 curscr = CurScreen(SP_PARM);
955 if (CurScreen(SP_PARM) != 0) {
965 scr_init(const char *file)
967 return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
972 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
976 T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
978 if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
979 delwin(NewScreen(SP_PARM));
980 NewScreen(SP_PARM) = dupwin(curscr);
982 newscr = NewScreen(SP_PARM);
984 if (NewScreen(SP_PARM) != 0) {
993 scr_set(const char *file)
995 return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);