/****************************************************************************
- * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * Copyright (c) 1998-2013,2014 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 *
* authorization. *
****************************************************************************/
-/****************************************************************************
- * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
- * and: Eric S. Raymond <esr@snark.thyrsus.com> *
- ****************************************************************************/
-
/*
** lib_addch.c
**
#include <curses.priv.h>
#include <ctype.h>
-MODULE_ID("$Id: lib_addch.c,v 1.42 1999/07/24 20:01:05 tom Exp $")
+MODULE_ID("$Id: lib_addch.c,v 1.129 2014/08/16 20:41:04 tom Exp $")
+
+static const NCURSES_CH_T blankchar = NewChar(BLANK_TEXT);
/*
* Ugly microtweaking alert. Everything from here to end of module is
*/
/* Return bit mask for clearing color pair number if given ch has color */
-#define COLOR_MASK(ch) (~(chtype)((ch)&A_COLOR?A_COLOR:0))
+#define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0))
-static inline chtype render_char(WINDOW *win, chtype ch)
+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 */
{
- chtype a = win->_attrs;
-
- if (ch == ' ')
- {
- /* color in attrs has precedence over bkgd */
- ch = a | (win->_bkgd & COLOR_MASK(a));
+ attr_t a = WINDOW_ATTRS(win);
+ int pair = GetPair(ch);
+
+ 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));
+ 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);
}
- else
- {
- /* color in attrs has precedence over bkgd */
- a |= (win->_bkgd & A_ATTRIBUTES) & COLOR_MASK(a);
- /* color in ch has precedence */
- ch |= (a & COLOR_MASK(ch));
- }
-
- TR(TRACE_VIRTPUT, ("bkg = %lx, attrs = %lx -> ch = %lx", win->_bkgd,
- win->_attrs, ch));
-
- return(ch);
+ AddAttr(ch, (a & COLOR_MASK(AttrOf(ch))));
+ SetPair(ch, pair);
+ }
+
+ 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);
}
-chtype _nc_background(WINDOW *win)
+NCURSES_EXPORT(NCURSES_CH_T)
+_nc_render(WINDOW *win, NCURSES_CH_T ch)
/* make render_char() visible while still allowing us to inline it below */
{
- return (win->_bkgd);
-}
-
-chtype _nc_render(WINDOW *win, chtype ch)
-/* make render_char() visible while still allowing us to inline it below */
-{
- return render_char(win, ch);
+ return render_char(win, ch);
}
/* check if position is legal; if not, return error */
return(ERR); \
}
#else
-#define CHECK_POSITION(win, x, y) /* nothing */
+#define CHECK_POSITION(win, x, y) /* nothing */
#endif
-static inline
-int waddch_literal(WINDOW *win, chtype ch)
+static bool
+newline_forces_scroll(WINDOW *win, NCURSES_SIZE_T * ypos)
{
- int x;
- struct ldat *line;
+ bool result = FALSE;
+
+ if (*ypos >= win->_regtop && *ypos == win->_regbottom) {
+ *ypos = win->_regbottom;
+ result = TRUE;
+ } else {
+ *ypos = (NCURSES_SIZE_T) (*ypos + 1);
+ }
+ return result;
+}
- x = win->_curx;
+/*
+ * 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);
+}
- CHECK_POSITION(win, x, win->_cury);
+#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;
+
+ 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) {
/*
- * If we're trying to add a character at the lower-right corner more
- * than once, fail. (Moving the cursor will clear the flag).
+ * An error occurred. We could either discard everything,
+ * or assume that the error was in the previous input.
+ * Try the latter.
*/
- if (win->_flags & _WRAPPED) {
- if (x >= win->_maxx)
- return (ERR);
- win->_flags &= ~_WRAPPED;
- }
-
- ch = render_char(win, ch);
- TR(TRACE_VIRTPUT, ("win attr = %s", _traceattr(win->_attrs)));
-
- line = win->_line+win->_cury;
-
- CHANGED_CELL(line,x);
-
- line->text[x++] = ch;
-
- TR(TRACE_VIRTPUT, ("(%d, %d) = %s", win->_cury, x, _tracechtype(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.
- */
- 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);
- }
- win->_curx = x;
- return OK;
+ TR(TRACE_VIRTPUT, ("Alert! mbrtowc returns error"));
+ /* handle this with unctrl() */
+ WINDOW_EXT(win, addch_used) = 0;
+ }
+ return len;
}
+#endif /* USE_WIDEC_SUPPORT */
-static inline
-int waddch_nosync(WINDOW *win, const chtype ch)
-/* the workhorse function -- add a character to the given window */
+static
+#if !USE_WIDEC_SUPPORT /* cannot be inline if it is recursive */
+NCURSES_INLINE
+#endif
+int
+waddch_literal(WINDOW *win, NCURSES_CH_T ch)
{
- int x, y;
- int t = 0;
- const char *s = 0;
-
- if ((ch & A_ALTCHARSET)
- || ((t = TextOf(ch)) > 127)
- || ((s = unctrl(t))[1] == 0))
- return waddch_literal(win, ch);
-
- x = win->_curx;
- y = win->_cury;
-
- switch (t) {
- case '\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)) {
- chtype blank = (' ' | AttrOf(ch));
- while (win->_curx < x) {
- if (waddch_literal(win, blank) == ERR)
- return(ERR);
+ int x;
+ int y;
+ struct ldat *line;
+
+ x = win->_curx;
+ y = win->_cury;
+
+ CHECK_POSITION(win, x, y);
+
+ ch = render_char(win, ch);
+
+ line = win->_line + y;
+
+ CHANGED_CELL(line, x);
+
+ /*
+ * 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({
+ 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))) {
+ int rc = OK;
+ const char *s = NCURSES_SP_NAME(unctrl)
+ (NCURSES_SP_ARGx (chtype) CharOf(ch));
+
+ if (s[1] != '\0') {
+ while (*s != '\0') {
+ rc = waddch(win, UChar(*s) | attr);
+ if (rc != OK)
+ break;
+ ++s;
}
+ return rc;
+ }
+ }
+ if (len == -1)
+ return waddch(win, ' ' | attr);
+ } else {
+ return OK;
+ }
+ }
+ });
+
+ /*
+ * Non-spacing characters are added to the current cell.
+ *
+ * Spacing characters that are wider than one column require some display
+ * adjustments.
+ */
+ if_WIDEC({
+ int len = wcwidth(CharOf(ch));
+ int i;
+ int j;
+ wchar_t *chars;
+
+ if (len == 0) { /* non-spacing */
+ if ((x > 0 && y >= 0)
+ || (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;
- } else {
- wclrtoeol(win);
- win->_flags |= _WRAPPED;
- if (++y > win->_regbottom) {
- x = win->_maxx;
- y--;
- if (win->_scroll) {
- scroll(win);
- x = 0;
- }
- } else {
- x = 0;
+ }
+ }
+ }
+ 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;
+ 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;
}
- break;
- case '\n':
- wclrtoeol(win);
- if (++y > win->_regbottom) {
- y--;
- if (win->_scroll)
- scroll(win);
- else
- return (ERR);
+ }
+ /*
+ * 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;
+ /*
+ * This label is used only for wide-characters.
+ */
+ if_WIDEC(
+ testwrapping:
+ );
+
+ TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s",
+ (long) win->_cury, (long) win->_curx, x - 1,
+ _tracech_t(CHREF(ch))));
+
+ if (x > win->_maxx) {
+ return wrap_to_next_line(win);
+ }
+ win->_curx = (NCURSES_SIZE_T) x;
+ return OK;
+}
+
+static NCURSES_INLINE int
+waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
+/* the workhorse function -- add a character to the given window */
+{
+ 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 (and not also a control character),
+ * treat it that way.
+ */
+ if ((AttrOf(ch) & A_ALTCHARSET)
+ || (
+#if USE_WIDEC_SUPPORT
+ (sp != 0 && sp->_legacy_coding) &&
+#endif
+ s[1] == 0
+ )
+ || (
+ (isprint((int)t) && !iscntrl((int)t))
+#if USE_WIDEC_SUPPORT
+ || ((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
+ * known to expand to more than one character according to unctrl().
+ */
+ x = win->_curx;
+ y = win->_cury;
+
+ switch (t) {
+ case '\t':
+#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 = blankchar;
+ AddAttr(blank, AttrOf(ch));
+ while (win->_curx < x) {
+ if (waddch_literal(win, blank) == ERR)
+ return (ERR);
+ }
+ break;
+ } else {
+ wclrtoeol(win);
+ win->_flags |= _WRAPPED;
+ if (newline_forces_scroll(win, &y)) {
+ x = win->_maxx;
+ if (win->_scroll) {
+ scroll(win);
+ x = 0;
}
- /* FALLTHRU */
- case '\r':
+ } else {
x = 0;
- win->_flags &= ~_WRAPPED;
- break;
- case '\b':
- if (x == 0)
- return (OK);
- x--;
- win->_flags &= ~_WRAPPED;
- break;
- default:
- while (*s)
- if (waddch_literal(win, (*s++)|AttrOf(ch)) == ERR)
- return ERR;
- return(OK);
+ }
+ }
+ break;
+ case '\n':
+ wclrtoeol(win);
+ if (newline_forces_scroll(win, &y)) {
+ if (win->_scroll)
+ scroll(win);
+ else
+ return (ERR);
}
+ /* FALLTHRU */
+ case '\r':
+ x = 0;
+ win->_flags &= ~_WRAPPED;
+ break;
+ case '\b':
+ if (x == 0)
+ return (OK);
+ x--;
+ win->_flags &= ~_WRAPPED;
+ break;
+ default:
+ while (*s) {
+ NCURSES_CH_T sch;
+ SetChar(sch, UChar(*s++), AttrOf(ch));
+ if_EXT_COLORS(SetPair(sch, GetPair(ch)));
+ if (waddch_literal(win, sch) == ERR)
+ return ERR;
+ }
+ return (OK);
+ }
- win->_curx = x;
- win->_cury = y;
+ win->_curx = x;
+ win->_cury = y;
- return(OK);
+ return (OK);
}
-int _nc_waddch_nosync(WINDOW *win, const chtype c)
+NCURSES_EXPORT(int)
+_nc_waddch_nosync(WINDOW *win, const NCURSES_CH_T c)
/* export copy of waddch_nosync() so the string-put functions can use it */
{
- return(waddch_nosync(win, c));
+ return (waddch_nosync(win, 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.
*/
/* These are actual entry points */
-int waddch(WINDOW *win, const chtype ch)
+NCURSES_EXPORT(int)
+waddch(WINDOW *win, const chtype ch)
{
- int code = ERR;
+ int code = ERR;
+ NCURSES_CH_T wch;
+ SetChar2(wch, ch);
- TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win, _tracechtype(ch)));
+ TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), (void *) win,
+ _tracechtype(ch)));
- if (win && (waddch_nosync(win, ch) != ERR))
- {
- _nc_synchook(win);
- code = OK;
- }
+ if (win && (waddch_nosync(win, wch) != ERR)) {
+ _nc_synchook(win);
+ code = OK;
+ }
- TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_RETURN("%d"), code));
- return(code);
+ TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
+ return (code);
}
-int wechochar(WINDOW *win, const chtype ch)
+NCURSES_EXPORT(int)
+wechochar(WINDOW *win, const chtype ch)
{
- int code = ERR;
-
- TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win, _tracechtype(ch)));
-
- if (win && (waddch_nosync(win, ch) != ERR))
- {
- bool save_immed = win->_immed;
- win->_immed = TRUE;
- _nc_synchook(win);
- win->_immed = save_immed;
- code = OK;
- }
- TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_RETURN("%d"), code));
- return(code);
+ int code = ERR;
+ NCURSES_CH_T wch;
+ SetChar2(wch, ch);
+
+ TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"),
+ (void *) win,
+ _tracechtype(ch)));
+
+ if (win && (waddch_nosync(win, wch) != ERR)) {
+ bool save_immed = win->_immed;
+ win->_immed = TRUE;
+ _nc_synchook(win);
+ win->_immed = save_immed;
+ code = OK;
+ }
+ TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
+ return (code);
}