ncurses 6.1 - patch 20190914
[ncurses.git] / ncurses / base / lib_screen.c
index 63ef7f5bcf1737ececa1c1f8297a3f69ca0b9e6a..9e942eaf8494013f9c240551302c1121133cab70 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2015 Free Software Foundation, Inc.              *
+ * Copyright (c) 1998-2018,2019 Free Software Foundation, Inc.              *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_screen.c,v 1.64 2015/03/21 23:59:32 tom Exp $")
+MODULE_ID("$Id: lib_screen.c,v 1.96 2019/07/20 20:23:21 tom Exp $")
 
 #define MAX_SIZE 0x3fff                /* 16k is big enough for a window or pad */
 
 #define MARKER '\\'
+#define APPEND '+'
 #define GUTTER '|'
 #define L_CURL '{'
 #define R_CURL '}'
 
-#define L_MARK "\\{"
-#define R_MARK "}"
+#if USE_STRING_HACKS && HAVE_SNPRINTF
+#define ARG_SLIMIT(name) size_t name,
+#else
+#define ARG_SLIMIT(name)       /* nothing */
+#endif
+
+#define CUR_SLIMIT _nc_SLIMIT(limit - (target - base))
+#define TOP_SLIMIT _nc_SLIMIT(sizeof(buffer))
 
 /*
- * Use 0x8888 as the magic number for new-format files, since it cannot be
+ * Use 0x888888 as the magic number for new-format files, since it cannot be
  * mistaken for the _cury/_curx pair of 16-bit numbers which start the old
  * format.  It happens to be unused in the file 5.22 database (2015/03/07).
  */
-static char my_magic[] =
+static const char my_magic[] =
 {'\210', '\210', '\210', '\210'};
 
-#if NCURSES_EXT_SCREEN_DUMP
+#if NCURSES_EXT_PUTWIN
 typedef enum {
     pINT                       /* int */
     ,pSHORT                    /* short */
@@ -75,19 +82,19 @@ typedef enum {
 } PARAM_TYPE;
 
 typedef struct {
-    const char *name;
+    const char name[11];
     attr_t attr;
 } SCR_ATTRS;
 
 typedef struct {
-    const char *name;
+    const char name[17];
     PARAM_TYPE type;
     size_t size;
     size_t offset;
 } SCR_PARAMS;
 
-#define DATA(name) { #name, A_##name }
-static SCR_ATTRS scr_attrs[] =
+#define DATA(name) { { #name }, A_##name }
+static const SCR_ATTRS scr_attrs[] =
 {
     DATA(NORMAL),
     DATA(STANDOUT),
@@ -113,9 +120,9 @@ static SCR_ATTRS scr_attrs[] =
 #undef DATA
 
 #define sizeof2(type,name) sizeof(((type *)0)->name)
-#define DATA(name, type) { #name, type, sizeof2(WINDOW, name), offsetof(WINDOW, name) }
+#define DATA(name, type) { { #name }, type, sizeof2(WINDOW, name), offsetof(WINDOW, name) }
 
-static SCR_PARAMS scr_params[] =
+static const SCR_PARAMS scr_params[] =
 {
     DATA(_cury, pSIZE),
     DATA(_curx, pSIZE),
@@ -163,12 +170,12 @@ static char *
 read_txt(FILE *fp)
 {
     size_t limit = 1024;
-    size_t used = 0;
     char *result = malloc(limit);
     char *buffer;
 
     if (result != 0) {
        int ch = 0;
+       size_t used = 0;
 
        clearerr(fp);
        result[used] = '\0';
@@ -181,6 +188,7 @@ read_txt(FILE *fp)
                    result = 0;
                    break;
                }
+               result = buffer;
            }
            ch = fgetc(fp);
            if (ch == EOF)
@@ -222,16 +230,16 @@ decode_attr(char *source, attr_t *target, int *color)
                ++next;
            } else if (*next == 'C') {
                int value = 0;
+               unsigned pair;
                next++;
                while (isdigit(UChar(*next))) {
                    value = value * 10 + (*next++ - '0');
                }
                *target &= ~A_COLOR;
-               if (value > 256) {
-                   *target |= COLOR_PAIR(255);
-               } else {
-                   *target |= COLOR_PAIR(value);
-               }
+               pair = (unsigned) ((value > 256)
+                                  ? COLOR_PAIR(255)
+                                  : COLOR_PAIR(value));
+               *target |= pair;
                *color = value;
            } else {
                while (isalnum(UChar(*next))) {
@@ -266,10 +274,12 @@ decode_char(char *source, int *target)
     T(("decode_char   '%s'", source));
     *target = ' ';
     switch (*source) {
-    case '\\':
+    case MARKER:
        switch (*++source) {
-       case '\\':
-           *target = '\\';
+       case APPEND:
+           break;
+       case MARKER:
+           *target = MARKER;
            ++source;
            break;
        case 's':
@@ -315,13 +325,14 @@ static char *
 decode_chtype(char *source, chtype fillin, chtype *target)
 {
     attr_t attr = ChAttrOf(fillin);
-    int color = PAIR_NUMBER(attr);
+    int color = PAIR_NUMBER((int) attr);
     int value;
 
     T(("decode_chtype '%s'", source));
     source = decode_attr(source, &attr, &color);
     source = decode_char(source, &value);
-    *target = ChCharOf(value) | attr | COLOR_PAIR(color);
+    *target = (ChCharOf(value) | attr | (chtype) COLOR_PAIR(color));
+    /* FIXME - ignore combining characters */
     return source;
 }
 
@@ -332,6 +343,8 @@ decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
     int color;
     attr_t attr = fillin->attr;
     wchar_t chars[CCHARW_MAX];
+    int append = 0;
+    int value = 0;
 
     T(("decode_cchar  '%s'", source));
     *target = blank;
@@ -342,9 +355,17 @@ decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
 #endif
     source = decode_attr(source, &attr, &color);
     memset(chars, 0, sizeof(chars));
-    source = decode_char(source, &chars[0]);
-    /* FIXME - handle combining characters at this point */
-    setcchar(target, chars, attr, (short) color, NULL);
+    source = decode_char(source, &value);
+    chars[0] = (wchar_t) value;
+    /* handle combining characters */
+    while (source[0] == MARKER && source[1] == APPEND) {
+       source += 2;
+       source = decode_char(source, &value);
+       if (++append < CCHARW_MAX) {
+           chars[append] = (wchar_t) value;
+       }
+    }
+    setcchar(target, chars, attr, (short) color, &color);
     return source;
 }
 #endif
@@ -353,9 +374,6 @@ static int
 read_win(WINDOW *win, FILE *fp)
 {
     int code = ERR;
-    char *txt;
-    char *name;
-    char *value;
     size_t n;
     int color;
 #if NCURSES_WIDECHAR
@@ -365,7 +383,10 @@ read_win(WINDOW *win, FILE *fp)
 
     memset(win, 0, sizeof(WINDOW));
     for (;;) {
-       txt = read_txt(fp);
+       char *name;
+       char *value;
+       char *txt = read_txt(fp);
+
        if (txt == 0)
            break;
        if (!strcmp(txt, "rows:")) {
@@ -423,7 +444,21 @@ read_row(char *source, NCURSES_CH_T * prior, NCURSES_CH_T * target, int length)
 {
     while (*source != '\0' && length > 0) {
 #if NCURSES_WIDECHAR
+       int len;
+
        source = decode_cchar(source, prior, target);
+       len = _nc_wacs_width(target->chars[0]);
+       if (len > 1) {
+           int n;
+
+           SetWidecExt(CHDEREF(target), 0);
+           for (n = 1; n < len; ++n) {
+               target[n] = target[0];
+               SetWidecExt(CHDEREF(target), n);
+           }
+           target += (len - 1);
+           length -= (len - 1);
+       }
 #else
        source = decode_chtype(source, *prior, target);
 #endif
@@ -437,7 +472,7 @@ read_row(char *source, NCURSES_CH_T * prior, NCURSES_CH_T * target, int length)
     /* FIXME - see what error conditions should apply if I need to return ERR */
     return 0;
 }
-#endif /* NCURSES_EXT_SCREEN_DUMP */
+#endif /* NCURSES_EXT_PUTWIN */
 
 /*
  * Originally, getwin/putwin used fread/fwrite, because they used binary data.
@@ -467,7 +502,6 @@ NCURSES_EXPORT(WINDOW *)
 NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
 {
     WINDOW tmp, *nwin;
-    int n;
     bool old_format = FALSE;
 
     T((T_CALLED("getwin(%p)"), (void *) filep));
@@ -480,14 +514,14 @@ NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
      * Read the first 4 bytes to determine first if this is an old-format
      * screen-dump, or new-format.
      */
-    if (read_block(&tmp, 4, filep) < 0) {
+    if (read_block(&tmp, (size_t) 4, filep) < 0) {
        returnWin(0);
     }
     /*
      * If this is a new-format file, and we do not support it, give up.
      */
-    if (!memcmp(&tmp, my_magic, 4)) {
-#if NCURSES_EXT_SCREEN_DUMP
+    if (!memcmp(&tmp, my_magic, (size_t) 4)) {
+#if NCURSES_EXT_PUTWIN
        if (read_win(&tmp, filep) < 0)
 #endif
            returnWin(0);
@@ -523,6 +557,7 @@ NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
      * made sense is probably gone.
      */
     if (nwin != 0) {
+       int n;
        size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
 
        nwin->_curx = tmp._curx;
@@ -563,9 +598,9 @@ NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
                }
            }
        }
-#if NCURSES_EXT_SCREEN_DUMP
+#if NCURSES_EXT_PUTWIN
        else {
-           char *txt;
+           char *txt = 0;
            bool success = TRUE;
            NCURSES_CH_T prior = blank;
 
@@ -616,17 +651,27 @@ getwin(FILE *filep)
 }
 #endif
 
-#if NCURSES_EXT_SCREEN_DUMP
+#if NCURSES_EXT_PUTWIN
 static void
-encode_attr(char *target, attr_t source, attr_t prior)
+encode_attr(char *target, ARG_SLIMIT(limit)
+           attr_t source,
+           attr_t prior,
+           int source_color,
+           int prior_color)
 {
+#if USE_STRING_HACKS && HAVE_SNPRINTF
+    char *base = target;
+#endif
+    source &= ~A_CHARTEXT;
+    prior &= ~A_CHARTEXT;
+
     *target = '\0';
-    if (source != prior) {
+    if ((source != prior) || (source_color != prior_color)) {
        size_t n;
        bool first = TRUE;
 
-       strcpy(target, L_MARK);
-       target += strlen(target);
+       *target++ = MARKER;
+       *target++ = L_CURL;
 
        for (n = 0; n < SIZEOF(scr_attrs); ++n) {
            if ((source & scr_attrs[n].attr) != 0 ||
@@ -637,56 +682,75 @@ encode_attr(char *target, attr_t source, attr_t prior)
                } else {
                    *target++ = '|';
                }
-               strcpy(target, scr_attrs[n].name);
+               _nc_STRCPY(target, scr_attrs[n].name, limit);
                target += strlen(target);
            }
        }
-       if ((source & A_COLOR) != (prior & A_COLOR)) {
+       if (source_color != prior_color) {
            if (!first)
                *target++ = '|';
-           sprintf(target, "C%d", PAIR_NUMBER(source));
+           _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
            target += strlen(target);
        }
 
-       strcpy(target, R_MARK);
+       *target++ = R_CURL;
+       *target = '\0';
     }
 }
 
 static void
-encode_cell(char *target, CARG_CH_T source, CARG_CH_T previous)
+encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
 {
+#if USE_STRING_HACKS && HAVE_SNPRINTF
+    char *base = target;
+#endif
 #if NCURSES_WIDECHAR
     size_t n;
+    int source_pair = GetPair(*source);
+    int previous_pair = GetPair(*previous);
 
     *target = '\0';
-    if (previous->attr != source->attr) {
-       encode_attr(target, source->attr, previous->attr);
+    if ((previous->attr != source->attr) || (previous_pair != source_pair)) {
+       encode_attr(target, CUR_SLIMIT
+                   source->attr,
+                   previous->attr,
+                   source_pair,
+                   previous_pair);
     }
     target += strlen(target);
 #if NCURSES_EXT_COLORS
     if (previous->ext_color != source->ext_color) {
-       sprintf(target, "%sC%d%s", L_MARK, source->ext_color, R_MARK);
+       _nc_SPRINTF(target, CUR_SLIMIT
+                   "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
     }
 #endif
     for (n = 0; n < SIZEOF(source->chars); ++n) {
-       if (source->chars[n] == 0)
+       unsigned uch = (unsigned) source->chars[n];
+       if (uch == 0)
            continue;
-       if (source->chars[n] > 0xffff) {
-           sprintf(target, "\\U%08x", source->chars[n]);
-       } else if (source->chars[n] > 0xff) {
-           sprintf(target, "\\u%04x", source->chars[n]);
-       } else if (source->chars[n] < 32 || source->chars[n] >= 127) {
-           sprintf(target, "\\%03o", source->chars[n] & 0xff);
+       if (n) {
+           *target++ = MARKER;
+           *target++ = APPEND;
+       }
+       *target++ = MARKER;
+       if (uch > 0xffff) {
+           _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch);
+       } else if (uch > 0xff) {
+           _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch);
+       } else if (uch < 32 || uch >= 127) {
+           _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff);
        } else {
-           switch (source->chars[n]) {
+           switch (uch) {
            case ' ':
-               strcpy(target, "\\s");
+               _nc_STRCPY(target, "s", limit);
                break;
-           case '\\':
-               strcpy(target, "\\\\");
+           case MARKER:
+               *target++ = MARKER;
+               *target = '\0';
                break;
            default:
-               sprintf(target, "%c", source->chars[n]);
+               --target;
+               _nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
                break;
            }
        }
@@ -697,25 +761,31 @@ encode_cell(char *target, CARG_CH_T source, CARG_CH_T previous)
 
     *target = '\0';
     if (AttrOfD(previous) != AttrOfD(source)) {
-       encode_attr(target, AttrOfD(source), AttrOfD(previous));
+       encode_attr(target, CUR_SLIMIT
+                   AttrOfD(source),
+                   AttrOfD(previous),
+                   GetPair(source),
+                   GetPair(previous));
     }
     target += strlen(target);
+    *target++ = MARKER;
     if (ch < 32 || ch >= 127) {
-       sprintf(target, "\\%03o", ch);
+       _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
     } else {
        switch (ch) {
        case ' ':
-           strcpy(target, "\\s");
+           _nc_STRCPY(target, "s", limit);
            break;
-       case '\\':
-           strcpy(target, "\\\\");
+       case MARKER:
+           *target++ = MARKER;
+           *target = '\0';
            break;
        default:
-           sprintf(target, "%c", ch);
+           --target;
+           _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
            break;
        }
     }
-    target += strlen(target);
 #endif
 }
 #endif
@@ -724,15 +794,15 @@ NCURSES_EXPORT(int)
 putwin(WINDOW *win, FILE *filep)
 {
     int code = ERR;
-    int y;
 
     T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
 
-#if NCURSES_EXT_SCREEN_DUMP
+#if NCURSES_EXT_PUTWIN
     if (win != 0) {
        const char *version = curses_version();
        char buffer[1024];
        NCURSES_CH_T last_cell;
+       int y;
 
        memset(&last_cell, 0, sizeof(last_cell));
 
@@ -750,43 +820,58 @@ putwin(WINDOW *win, FILE *filep)
            const char *name = scr_params[y].name;
            const char *data = (char *) win + scr_params[y].offset;
            const void *dp = (const void *) data;
+           attr_t attr;
 
            *buffer = '\0';
-           if (!strncmp(name, "_pad.", 5) && !(win->_flags & _ISPAD)) {
+           if (!strncmp(name, "_pad.", (size_t) 5) && !(win->_flags & _ISPAD)) {
                continue;
            }
            switch (scr_params[y].type) {
            case pATTR:
-               encode_attr(buffer, (*(const attr_t *) dp) & ~A_CHARTEXT, A_NORMAL);
+               attr = (*(const attr_t *) dp) & ~A_CHARTEXT;
+               encode_attr(buffer, TOP_SLIMIT
+                           (*(const attr_t *) dp) & ~A_CHARTEXT,
+                           A_NORMAL,
+                           COLOR_PAIR((int) attr),
+                           0);
                break;
            case pBOOL:
                if (!(*(const bool *) data)) {
                    continue;
                }
-               strcpy(buffer, name);
+               _nc_STRCPY(buffer, name, sizeof(buffer));
                name = "flag";
                break;
            case pCHAR:
-               encode_attr(buffer, *(const attr_t *) dp, A_NORMAL);
+               attr = (*(const attr_t *) dp);
+               encode_attr(buffer, TOP_SLIMIT
+                           * (const attr_t *) dp,
+                           A_NORMAL,
+                           COLOR_PAIR((int) attr),
+                           0);
                break;
            case pINT:
                if (!(*(const int *) dp))
                    continue;
-               sprintf(buffer, "%d", *(const int *) dp);
+               _nc_SPRINTF(buffer, TOP_SLIMIT
+                           "%d", *(const int *) dp);
                break;
            case pSHORT:
                if (!(*(const short *) dp))
                    continue;
-               sprintf(buffer, "%d", *(const short *) dp);
+               _nc_SPRINTF(buffer, TOP_SLIMIT
+                           "%d", *(const short *) dp);
                break;
            case pSIZE:
                if (!(*(const NCURSES_SIZE_T *) dp))
                    continue;
-               sprintf(buffer, "%d", *(const NCURSES_SIZE_T *) dp);
+               _nc_SPRINTF(buffer, TOP_SLIMIT
+                           "%d", *(const NCURSES_SIZE_T *) dp);
                break;
 #if NCURSES_WIDECHAR
            case pCCHAR:
-               encode_cell(buffer, (CARG_CH_T) dp, CHREF(last_cell));
+               encode_cell(buffer, TOP_SLIMIT
+                           (CARG_CH_T) dp, CHREF(last_cell));
                break;
 #endif
            }
@@ -808,12 +893,22 @@ putwin(WINDOW *win, FILE *filep)
                || ferror(filep))
                returnCode(code);
            for (x = 0; x <= win->_maxx; x++) {
-               encode_cell(buffer, CHREF(data[x]), CHREF(last_cell));
+#if NCURSES_WIDECHAR
+               int len = _nc_wacs_width(data[x].chars[0]);
+               encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
                last_cell = data[x];
                PUTS(buffer);
+               if (len > 1)
+                   x += (len - 1);
+#else
+               encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
+               last_cell = data[x];
+               PUTS(buffer);
+#endif
            }
            PUTS("\n");
        }
+       code = OK;
     }
 #else
     /*
@@ -824,6 +919,7 @@ putwin(WINDOW *win, FILE *filep)
      */
     if (win != 0) {
        size_t len = (size_t) (win->_maxx + 1);
+       int y;
 
        clearerr(filep);
        if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
@@ -852,7 +948,7 @@ NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
     T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
 
     if (_nc_access(file, R_OK) >= 0
-       && (fp = fopen(file, "rb")) != 0) {
+       && (fp = fopen(file, BIN_R)) != 0) {
        delwin(NewScreen(SP_PARM));
        NewScreen(SP_PARM) = getwin(fp);
 #if !USE_REENTRANT
@@ -883,7 +979,7 @@ scr_dump(const char *file)
     T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
 
     if (_nc_access(file, W_OK) < 0
-       || (fp = fopen(file, "wb")) == 0) {
+       || (fp = fopen(file, BIN_W)) == 0) {
        result = ERR;
     } else {
        (void) putwin(newscr, fp);
@@ -896,7 +992,6 @@ scr_dump(const char *file)
 NCURSES_EXPORT(int)
 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
 {
-    FILE *fp = 0;
     int code = ERR;
 
     T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
@@ -908,8 +1003,10 @@ NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
        !(exit_ca_mode && non_rev_rmcup)
 #endif
        ) {
+       FILE *fp = 0;
+
        if (_nc_access(file, R_OK) >= 0
-           && (fp = fopen(file, "rb")) != 0) {
+           && (fp = fopen(file, BIN_R)) != 0) {
            delwin(CurScreen(SP_PARM));
            CurScreen(SP_PARM) = getwin(fp);
 #if !USE_REENTRANT