]> ncurses.scripts.mit.edu Git - ncurses.git/blobdiff - ncurses/base/lib_addch.c
ncurses 6.2 - patch 20210220
[ncurses.git] / ncurses / base / lib_addch.c
index 63b63bdd20e63dbcf27d96aebbbdc0b683c85627..378c2881faac5d6d4d689f441d3986dfd5f7b341 100644 (file)
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
+ * Copyright 2019-2020,2021 Thomas E. Dickey                                *
+ * Copyright 1998-2016,2017 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            *
@@ -36,7 +37,9 @@
 #include <curses.priv.h>
 #include <ctype.h>
 
-MODULE_ID("$Id: lib_addch.c,v 1.80 2004/02/07 18:20:46 tom Exp $")
+MODULE_ID("$Id: lib_addch.c,v 1.137 2021/02/20 22:24:34 tom Exp $")
+
+static const NCURSES_CH_T blankchar = NewChar(BLANK_TEXT);
 
 /*
  * Ugly microtweaking alert.  Everything from here to end of module is
@@ -49,29 +52,44 @@ MODULE_ID("$Id: lib_addch.c,v 1.80 2004/02/07 18:20:46 tom Exp $")
  */
 
 /* Return bit mask for clearing color pair number if given ch has color */
-#define COLOR_MASK(ch) (~(attr_t)((ch)&A_COLOR?A_COLOR:0))
+#define COLOR_MASK(ch) (~(attr_t)(((ch) & A_COLOR) ? A_COLOR : 0))
 
-static inline NCURSES_CH_T
+static NCURSES_INLINE NCURSES_CH_T
 render_char(WINDOW *win, NCURSES_CH_T ch)
 /* compute a rendition of the given char correct for the current context */
 {
-    attr_t a = win->_attrs;
+    attr_t a = WINDOW_ATTRS(win);
+    int pair = GetPair(ch);
 
-    if (ISBLANK(ch) && AttrOf(ch) == A_NORMAL) {
-       /* color in attrs has precedence over bkgrnd */
+    if (ISBLANK(ch)
+       && AttrOf(ch) == A_NORMAL
+       && pair == 0) {
+       /* color/pair in attrs has precedence over bkgrnd */
        ch = win->_nc_bkgd;
-       SetAttr(ch, a | (AttrOf(win->_nc_bkgd) & COLOR_MASK(a)));
+       SetAttr(ch, a | AttrOf(win->_nc_bkgd));
+       if ((pair = GET_WINDOW_PAIR(win)) == 0)
+           pair = GetPair(win->_nc_bkgd);
+       SetPair(ch, pair);
     } else {
        /* color in attrs has precedence over bkgrnd */
        a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a);
        /* color in ch has precedence */
+       if (pair == 0) {
+           if ((pair = GET_WINDOW_PAIR(win)) == 0)
+               pair = GetPair(win->_nc_bkgd);
+       }
        AddAttr(ch, (a & COLOR_MASK(AttrOf(ch))));
+       SetPair(ch, pair);
     }
 
-    TR(TRACE_VIRTPUT, ("render_char bkg %s, attrs %s -> ch %s",
-                      _tracech_t2(1, CHREF(win->_nc_bkgd)),
-                      _traceattr(win->_attrs),
-                      _tracech_t2(3, CHREF(ch))));
+    TR(TRACE_VIRTPUT,
+       ("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)",
+       _tracech_t2(1, CHREF(win->_nc_bkgd)),
+       GetPair(win->_nc_bkgd),
+       _traceattr(WINDOW_ATTRS(win)),
+       GET_WINDOW_PAIR(win),
+       _tracech_t2(3, CHREF(ch)),
+       GetPair(ch)));
 
     return (ch);
 }
@@ -99,9 +117,141 @@ _nc_render(WINDOW *win, NCURSES_CH_T ch)
 #define CHECK_POSITION(win, x, y)      /* nothing */
 #endif
 
+static bool
+newline_forces_scroll(WINDOW *win, NCURSES_SIZE_T *ypos)
+{
+    bool result = FALSE;
+
+    if (*ypos >= win->_regtop && *ypos <= win->_regbottom) {
+       if (*ypos == win->_regbottom) {
+           *ypos = win->_regbottom;
+           result = TRUE;
+       } else if (*ypos < win->_maxy) {
+           *ypos = (NCURSES_SIZE_T) (*ypos + 1);
+       }
+    } else if (*ypos < win->_maxy) {
+       *ypos = (NCURSES_SIZE_T) (*ypos + 1);
+    }
+    return result;
+}
+
+/*
+ * The _WRAPPED flag is useful only for telling an application that we've just
+ * wrapped the cursor.  We don't do anything with this flag except set it when
+ * wrapping, and clear it whenever we move the cursor.  If we try to wrap at
+ * the lower-right corner of a window, we cannot move the cursor (since that
+ * wouldn't be legal).  So we return an error (which is what SVr4 does). 
+ * Unlike SVr4, we can successfully add a character to the lower-right corner
+ * (Solaris 2.6 does this also, however).
+ */
+static int
+wrap_to_next_line(WINDOW *win)
+{
+    win->_flags |= _WRAPPED;
+    if (newline_forces_scroll(win, &(win->_cury))) {
+       win->_curx = win->_maxx;
+       if (!win->_scroll)
+           return (ERR);
+       scroll(win);
+    }
+    win->_curx = 0;
+    return (OK);
+}
+
+#if USE_WIDEC_SUPPORT
+static int waddch_literal(WINDOW *, NCURSES_CH_T);
+/*
+ * Fill the given number of cells with blanks using the current background
+ * rendition.  This saves/restores the current x-position.
+ */
+static void
+fill_cells(WINDOW *win, int count)
+{
+    NCURSES_CH_T blank = blankchar;
+    int save_x = win->_curx;
+    int save_y = win->_cury;
+
+    while (count-- > 0) {
+       if (waddch_literal(win, blank) == ERR)
+           break;
+    }
+    win->_curx = (NCURSES_SIZE_T) save_x;
+    win->_cury = (NCURSES_SIZE_T) save_y;
+}
+#endif
+
+/*
+ * Build up the bytes for a multibyte character, returning the length when
+ * complete (a positive number), -1 for error and -2 for incomplete.
+ */
+#if USE_WIDEC_SUPPORT
+NCURSES_EXPORT(int)
+_nc_build_wch(WINDOW *win, ARG_CH_T ch)
+{
+    char *buffer = WINDOW_EXT(win, addch_work);
+    int len;
+    int x = win->_curx;
+    int y = win->_cury;
+    mbstate_t state;
+    wchar_t result;
+
+    if ((WINDOW_EXT(win, addch_used) != 0) &&
+       (WINDOW_EXT(win, addch_x) != x ||
+        WINDOW_EXT(win, addch_y) != y)) {
+       /* discard the incomplete multibyte character */
+       WINDOW_EXT(win, addch_used) = 0;
+       TR(TRACE_VIRTPUT,
+          ("Alert discarded multibyte on move (%d,%d) -> (%d,%d)",
+           WINDOW_EXT(win, addch_y), WINDOW_EXT(win, addch_x),
+           y, x));
+    }
+    WINDOW_EXT(win, addch_x) = x;
+    WINDOW_EXT(win, addch_y) = y;
+
+    /*
+     * If the background character is a wide-character, that may interfere with
+     * processing multibyte characters in this function.
+     */
+    if (!is8bits(CharOf(CHDEREF(ch)))) {
+       if (WINDOW_EXT(win, addch_used) != 0) {
+           /* discard the incomplete multibyte character */
+           WINDOW_EXT(win, addch_used) = 0;
+           TR(TRACE_VIRTPUT,
+              ("Alert discarded incomplete multibyte"));
+       }
+       return 1;
+    }
+
+    init_mb(state);
+    buffer[WINDOW_EXT(win, addch_used)] = (char) CharOf(CHDEREF(ch));
+    WINDOW_EXT(win, addch_used) += 1;
+    buffer[WINDOW_EXT(win, addch_used)] = '\0';
+    if ((len = (int) mbrtowc(&result,
+                            buffer,
+                            (size_t) WINDOW_EXT(win, addch_used),
+                            &state)) > 0) {
+       attr_t attrs = AttrOf(CHDEREF(ch));
+       if_EXT_COLORS(int pair = GetPair(CHDEREF(ch)));
+       SetChar(CHDEREF(ch), result, attrs);
+       if_EXT_COLORS(SetPair(CHDEREF(ch), pair));
+       WINDOW_EXT(win, addch_used) = 0;
+    } else if (len == -1) {
+       /*
+        * An error occurred.  We could either discard everything,
+        * or assume that the error was in the previous input.
+        * Try the latter.
+        */
+       TR(TRACE_VIRTPUT, ("Alert! mbrtowc returns error"));
+       /* handle this with unctrl() */
+       WINDOW_EXT(win, addch_used) = 0;
+    }
+    return len;
+}
+#endif /* USE_WIDEC_SUPPORT */
+
 static
 #if !USE_WIDEC_SUPPORT         /* cannot be inline if it is recursive */
-  inline
+NCURSES_INLINE
 #endif
 int
 waddch_literal(WINDOW *win, NCURSES_CH_T ch)
@@ -115,18 +265,6 @@ waddch_literal(WINDOW *win, NCURSES_CH_T ch)
 
     CHECK_POSITION(win, x, y);
 
-    /*
-     * If we're trying to add a character at the lower-right corner more
-     * than once, fail.  (Moving the cursor will clear the flag).
-     */
-#if 0                          /* Solaris 2.6 allows updating the corner more than once */
-    if (win->_flags & _WRAPPED) {
-       if (x >= win->_maxx)
-           return (ERR);
-       win->_flags &= ~_WRAPPED;
-    }
-#endif
-
     ch = render_char(win, ch);
 
     line = win->_line + y;
@@ -136,146 +274,188 @@ waddch_literal(WINDOW *win, NCURSES_CH_T ch)
     /*
      * Build up multibyte characters until we have a wide-character.
      */
+#if NCURSES_SP_FUNCS
+#define DeriveSP() SCREEN *sp = _nc_screen_of(win);
+#else
+#define DeriveSP()             /*nothing */
+#endif
     if_WIDEC({
-       if (WINDOW_EXT(win, addch_used) == 0 && Charable(ch)) {
-           WINDOW_EXT(win, addch_used) = 0;
-       } else {
-           char *buffer = WINDOW_EXT(win, addch_work);
-           int len;
-           mbstate_t state;
-           wchar_t result;
-
-           if ((WINDOW_EXT(win, addch_used) != 0) &&
-               (WINDOW_EXT(win, addch_x) != x ||
-                WINDOW_EXT(win, addch_y) != y)) {
-               /* discard the incomplete multibyte character */
-               WINDOW_EXT(win, addch_used) = 0;
-           }
-           WINDOW_EXT(win, addch_x) = x;
-           WINDOW_EXT(win, addch_y) = y;
-
-           memset(&state, 0, sizeof(state));
-           buffer[WINDOW_EXT(win, addch_used)] = CharOf(ch);
-           WINDOW_EXT(win, addch_used) += 1;
-           buffer[WINDOW_EXT(win, addch_used)] = '\0';
-           if ((len = mbrtowc(&result,
-                              buffer,
-                              WINDOW_EXT(win, addch_used), &state)) > 0) {
-               attr_t attrs = AttrOf(ch);
-               SetChar(ch, result, attrs);
-               WINDOW_EXT(win, addch_used) = 0;
-               if (CharOf(ch) < 256) {
-                   const char *s = unctrl(CharOf(ch));
-                   if (s[1] != 0) {
-                       return waddstr(win, s);
+       DeriveSP();
+       if (WINDOW_EXT(win, addch_used) != 0 || !Charable(ch)) {
+           int len = _nc_build_wch(win, CHREF(ch));
+
+           if (len >= -1) {
+               attr_t attr = AttrOf(ch);
+
+               /* handle EILSEQ (i.e., when len >= -1) */
+               if (len == -1 && is8bits(CharOf(ch))) {
+                   const char *s = NCURSES_SP_NAME(unctrl)
+                     (NCURSES_SP_ARGx (chtype) CharOf(ch));
+
+                   if (s[1] != '\0') {
+                       int rc = OK;
+                       while (*s != '\0') {
+                           rc = waddch(win, UChar(*s) | attr);
+                           if (rc != OK)
+                               break;
+                           ++s;
+                       }
+                       return rc;
                    }
                }
+               if (len == -1)
+                   return waddch(win, ' ' | attr);
            } else {
-               if (len == -1) {
-                   /*
-                    * An error occurred.  We could either discard everything,
-                    * or assume that the error was in the previous input.
-                    * Try the latter.
-                    */
-                   TR(TRACE_VIRTPUT, ("Alert! mbrtowc returns error"));
-                   buffer[0] = CharOf(ch);
-                   WINDOW_EXT(win, addch_used) = 1;
-               }
                return OK;
            }
        }
     });
 
     /*
-     * Handle non-spacing characters
+     * Non-spacing characters are added to the current cell.
+     *
+     * Spacing characters that are wider than one column require some display
+     * adjustments.
      */
     if_WIDEC({
-       if (wcwidth(CharOf(ch)) == 0) {
-           int i;
+       int len = _nc_wacs_width(CharOf(ch));
+       int i;
+       int j;
+       wchar_t *chars;
+
+       if (len == 0) {         /* non-spacing */
            if ((x > 0 && y >= 0)
-               || ((y = win->_cury - 1) >= 0 &&
-                   (x = win->_maxx) > 0)) {
-               wchar_t *chars = (win->_line[y].text[x - 1].chars);
+               || (win->_maxx >= 0 && win->_cury >= 1)) {
+               if (x > 0 && y >= 0)
+                   chars = (win->_line[y].text[x - 1].chars);
+               else
+                   chars = (win->_line[y - 1].text[win->_maxx].chars);
                for (i = 0; i < CCHARW_MAX; ++i) {
                    if (chars[i] == 0) {
+                       TR(TRACE_VIRTPUT,
+                          ("added non-spacing %d: %x",
+                           x, (int) CharOf(ch)));
                        chars[i] = CharOf(ch);
                        break;
                    }
                }
            }
            goto testwrapping;
+       } else if (len > 1) {   /* multi-column characters */
+           /*
+            * Check if the character will fit on the current line.  If it does
+            * not fit, fill in the remainder of the line with blanks.  and
+            * move to the next line.
+            */
+           if (len > win->_maxx + 1) {
+               TR(TRACE_VIRTPUT, ("character will not fit"));
+               return ERR;
+           } else if (x + len > win->_maxx + 1) {
+               int count = win->_maxx + 1 - x;
+               TR(TRACE_VIRTPUT, ("fill %d remaining cells", count));
+               fill_cells(win, count);
+               if (wrap_to_next_line(win) == ERR)
+                   return ERR;
+               x = win->_curx;
+               y = win->_cury;
+               CHECK_POSITION(win, x, y);
+               line = win->_line + y;
+           }
+           /*
+            * Check for cells which are orphaned by adding this character, set
+            * those to blanks.
+            *
+            * FIXME: this actually could fill j-i cells, more complicated to
+            * setup though.
+            */
+           for (i = 0; i < len; ++i) {
+               if (isWidecBase(win->_line[y].text[x + i])) {
+                   break;
+               } else if (isWidecExt(win->_line[y].text[x + i])) {
+                   for (j = i; x + j <= win->_maxx; ++j) {
+                       if (!isWidecExt(win->_line[y].text[x + j])) {
+                           TR(TRACE_VIRTPUT, ("fill %d orphan cells", j));
+                           fill_cells(win, j);
+                           break;
+                       }
+                   }
+                   break;
+               }
+           }
+           /*
+            * Finally, add the cells for this character.
+            */
+           for (i = 0; i < len; ++i) {
+               NCURSES_CH_T value = ch;
+               SetWidecExt(value, i);
+               TR(TRACE_VIRTPUT, ("multicolumn %d:%d (%d,%d)",
+                                  i + 1, len,
+                                  win->_begy + y, win->_begx + x));
+               line->text[x] = value;
+               CHANGED_CELL(line, x);
+               ++x;
+           }
+           goto testwrapping;
        }
     });
+
+    /*
+     * Single-column characters.
+     */
     line->text[x++] = ch;
     /*
-     * Provide for multi-column characters
+     * This label is used only for wide-characters.
      */
-    if_WIDEC({
-       int len = wcwidth(CharOf(ch));
-       while (len-- > 1) {
-           if (x + (len - 1) > win->_maxx) {
-               NCURSES_CH_T blank = NewChar2(BLANK_TEXT, BLANK_ATTR);
-               AddAttr(blank, AttrOf(ch));
-               if (waddch_literal(win, blank) != ERR)
-                   return waddch_literal(win, ch);
-               return ERR;
-           }
-           AddAttr(line->text[x++], WA_NAC);
-           TR(TRACE_VIRTPUT, ("added NAC %d", x - 1));
-       }
-    }
+    if_WIDEC(
   testwrapping:
     );
 
-    TR(TRACE_VIRTPUT, ("(%d, %d) = %s", win->_cury, x, _tracech_t(CHREF(ch))));
+    TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s",
+                      (long) win->_cury, (long) win->_curx, x - 1,
+                      _tracech_t(CHREF(ch))));
+
     if (x > win->_maxx) {
-       /*
-        * The _WRAPPED flag is useful only for telling an application that
-        * we've just wrapped the cursor.  We don't do anything with this flag
-        * except set it when wrapping, and clear it whenever we move the
-        * cursor.  If we try to wrap at the lower-right corner of a window, we
-        * cannot move the cursor (since that wouldn't be legal).  So we return
-        * an error (which is what SVr4 does).  Unlike SVr4, we can
-        * successfully add a character to the lower-right corner (Solaris 2.6
-        * does this also, however).
-        */
-       win->_flags |= _WRAPPED;
-       if (++win->_cury > win->_regbottom) {
-           win->_cury = win->_regbottom;
-           win->_curx = win->_maxx;
-           if (!win->_scroll)
-               return (ERR);
-           scroll(win);
-       }
-       win->_curx = 0;
-       return (OK);
+       return wrap_to_next_line(win);
     }
-    win->_curx = x;
+    win->_curx = (NCURSES_SIZE_T) x;
     return OK;
 }
 
-static inline int
+static NCURSES_INLINE int
 waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
 /* the workhorse function -- add a character to the given window */
 {
-    int x, y;
-    chtype t = CharOf(ch);
-    const char *s = 0;
+    NCURSES_SIZE_T x, y;
+    chtype t = (chtype) CharOf(ch);
+#if USE_WIDEC_SUPPORT || NCURSES_SP_FUNCS || USE_REENTRANT
+    SCREEN *sp = _nc_screen_of(win);
+#endif
+    const char *s = NCURSES_SP_NAME(unctrl) (NCURSES_SP_ARGx t);
+    int tabsize = 8;
 
     /*
      * If we are using the alternate character set, forget about locale.
      * Otherwise, if unctrl() returns a single-character or the locale
-     * claims the code is printable, treat it that way.
+     * claims the code is printable (and not also a control character),
+     * treat it that way.
      */
     if ((AttrOf(ch) & A_ALTCHARSET)
-       || ((s = unctrl(t))[1] == 0 ||
-               (
-               isprint(t)
+       || (
+#if USE_WIDEC_SUPPORT
+              (sp != 0 && sp->_legacy_coding) &&
+#endif
+              s[1] == 0
+       )
+       || (
+              (isprint((int) t) && !iscntrl((int) t))
 #if USE_WIDEC_SUPPORT
-               || WINDOW_EXT(win, addch_used)
+              || ((sp == 0 || !sp->_legacy_coding) &&
+                  (WINDOW_EXT(win, addch_used)
+                   || !_nc_is_charable(CharOf(ch))))
 #endif
-               )))
+       )) {
        return waddch_literal(win, ch);
+    }
 
     /*
      * Handle carriage control and other codes that are not printable, or are
@@ -283,18 +463,23 @@ waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
      */
     x = win->_curx;
     y = win->_cury;
+    CHECK_POSITION(win, x, y);
 
     switch (t) {
     case '\t':
-       x += (TABSIZE - (x % TABSIZE));
-
+#if USE_REENTRANT
+       tabsize = *ptrTabsize(sp);
+#else
+       tabsize = TABSIZE;
+#endif
+       x = (NCURSES_SIZE_T) (x + (tabsize - (x % tabsize)));
        /*
         * Space-fill the tab on the bottom line so that we'll get the
         * "correct" cursor position.
         */
        if ((!win->_scroll && (y == win->_regbottom))
            || (x <= win->_maxx)) {
-           NCURSES_CH_T blank = NewChar2(BLANK_TEXT, BLANK_ATTR);
+           NCURSES_CH_T blank = blankchar;
            AddAttr(blank, AttrOf(ch));
            while (win->_curx < x) {
                if (waddch_literal(win, blank) == ERR)
@@ -304,9 +489,8 @@ waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
        } else {
            wclrtoeol(win);
            win->_flags |= _WRAPPED;
-           if (++y > win->_regbottom) {
+           if (newline_forces_scroll(win, &y)) {
                x = win->_maxx;
-               y--;
                if (win->_scroll) {
                    scroll(win);
                    x = 0;
@@ -318,8 +502,7 @@ waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
        break;
     case '\n':
        wclrtoeol(win);
-       if (++y > win->_regbottom) {
-           y--;
+       if (newline_forces_scroll(win, &y)) {
            if (win->_scroll)
                scroll(win);
            else
@@ -339,7 +522,8 @@ waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
     default:
        while (*s) {
            NCURSES_CH_T sch;
-           SetChar(sch, *s++, AttrOf(ch));
+           SetChar(sch, UChar(*s++), AttrOf(ch));
+           if_EXT_COLORS(SetPair(sch, GetPair(ch)));
            if (waddch_literal(win, sch) == ERR)
                return ERR;
        }
@@ -360,7 +544,7 @@ _nc_waddch_nosync(WINDOW *win, const NCURSES_CH_T c)
 }
 
 /*
- * The versions below call _nc_synhook().  We wanted to avoid this in the
+ * The versions below call _nc_synchook().  We wanted to avoid this in the
  * version exported for string puts; they'll call _nc_synchook once at end
  * of run.
  */
@@ -374,7 +558,7 @@ waddch(WINDOW *win, const chtype ch)
     NCURSES_CH_T wch;
     SetChar2(wch, ch);
 
-    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win,
+    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), (void *) win,
                                      _tracechtype(ch)));
 
     if (win && (waddch_nosync(win, wch) != ERR)) {
@@ -393,7 +577,8 @@ wechochar(WINDOW *win, const chtype ch)
     NCURSES_CH_T wch;
     SetChar2(wch, ch);
 
-    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win,
+    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"),
+                                     (void *) win,
                                      _tracechtype(ch)));
 
     if (win && (waddch_nosync(win, wch) != ERR)) {