diff options
Diffstat (limited to 'readline/display.c')
-rw-r--r-- | readline/display.c | 669 |
1 files changed, 443 insertions, 226 deletions
diff --git a/readline/display.c b/readline/display.c index cfe8164..b39f282 100644 --- a/readline/display.c +++ b/readline/display.c @@ -1,6 +1,6 @@ /* display.c -- readline redisplay facility. */ -/* Copyright (C) 1987-2015 Free Software Foundation, Inc. +/* Copyright (C) 1987-2017 Free Software Foundation, Inc. This file is part of the GNU Readline Library (Readline), a library for reading lines of text with interactive input and history editing. @@ -124,7 +124,6 @@ static int _rl_col_width PARAMS((const char *, int, int, int)); #define PROMPT_ENDING_INDEX \ ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) ? prompt_physical_chars : prompt_last_invisible+1) - /* **************************************************************** */ /* */ /* Display stuff */ @@ -159,9 +158,6 @@ rl_voidfunc_t *rl_redisplay_function = rl_redisplay; /* What YOU turn on when you have handled all redisplay yourself. */ int rl_display_fixed = 0; -int _rl_suppress_redisplay = 0; -int _rl_want_redisplay = 0; - /* The stuff that gets printed out before the actual text of the line. This is usually pointing to rl_prompt. */ char *rl_display_prompt = (char *)NULL; @@ -178,6 +174,10 @@ int _rl_vi_cmd_modestr_len; /* Pseudo-global variables declared here. */ +/* Hints for other parts of readline to give to the display engine. */ +int _rl_suppress_redisplay = 0; +int _rl_want_redisplay = 0; + /* The visible cursor position. If you print some text, adjust this. */ /* NOTE: _rl_last_c_pos is used as a buffer index when not in a locale supporting multibyte characters, and an absolute cursor position when @@ -186,14 +186,24 @@ int _rl_vi_cmd_modestr_len; int _rl_last_c_pos = 0; int _rl_last_v_pos = 0; +/* Number of physical lines consumed by the current line buffer currently + on screen minus 1. */ +int _rl_vis_botlin = 0; + +/* This is a hint update_line gives to rl_redisplay that it has adjusted the + value of _rl_last_c_pos *and* taken the presence of any invisible chars in + the prompt into account. rl_redisplay notes this and does not do the + adjustment itself. */ static int cpos_adjusted; + +/* The index into the line buffer corresponding to the cursor position */ static int cpos_buffer_position; + +/* A flag to note when we're displaying the first line of the prompt */ static int displaying_prompt_first_line; +/* The number of multibyte characters in the prompt, if any */ static int prompt_multibyte_chars; -/* Number of lines currently on screen minus 1. */ -int _rl_vis_botlin = 0; - static int _rl_inv_botlin = 0; /* Variables used only in this file. */ @@ -216,7 +226,11 @@ static int line_size = 1024; static char *local_prompt, *local_prompt_prefix; static int local_prompt_len; -static int prompt_visible_length, prompt_prefix_length; +static int prompt_prefix_length; +/* Number of chars in the buffer that contribute to visible chars on the screen. + This might be different from the number of physical chars in the presence + of multibyte characters */ +static int prompt_visible_length; /* The number of invisible characters in the line currently being displayed on the screen. */ @@ -242,16 +256,26 @@ static int prompt_last_screen_line; static int prompt_physical_chars; +/* An array of indexes into the prompt string where we will break physical + screen lines. It's easier to compute in expand_prompt and use later in + rl_redisplay instead of having rl_redisplay try to guess about invisible + characters in the prompt or use heuristics about where they are. */ +static int *local_prompt_newlines; + /* set to a non-zero value by rl_redisplay if we are marking modified history lines and the current line is so marked. */ static int modmark; +static int line_totbytes; + /* Variables to save and restore prompt and display information. */ /* These are getting numerous enough that it's time to create a struct. */ static char *saved_local_prompt; static char *saved_local_prefix; +static int *saved_local_prompt_newlines; + static int saved_last_invisible; static int saved_visible_length; static int saved_prefix_length; @@ -262,8 +286,7 @@ static int saved_physical_chars; /* Return a string indicating the editing mode, for use in the prompt. */ static char * -prompt_modestr (lenp) - int *lenp; +prompt_modestr (int *lenp) { if (rl_editing_mode == emacs_mode) { @@ -291,7 +314,8 @@ prompt_modestr (lenp) index of the last invisible character in the returned string. NIFLP, if non-zero, is a place to store the number of invisible characters in the first prompt line. The previous are used as byte counts -- indexes - into a character buffer. */ + into a character buffer. *VLP gets the number of physical characters in + the expanded prompt (visible length) */ /* Current implementation: \001 (^A) start non-visible characters @@ -304,15 +328,16 @@ prompt_modestr (lenp) PMT_MULTILINE caller indicates that this is part of a multiline prompt */ +/* This approximates the number of lines the prompt will take when displayed */ +#define APPROX_DIV(n, d) (((n) < (d)) ? 1 : ((n) / (d)) + 1) + static char * -expand_prompt (pmt, flags, lp, lip, niflp, vlp) - char *pmt; - int flags; - int *lp, *lip, *niflp, *vlp; +expand_prompt (char *pmt, int flags, int *lp, int *lip, int *niflp, int *vlp) { char *r, *ret, *p, *igstart, *nprompt, *ms; int l, rl, last, ignoring, ninvis, invfl, invflset, ind, pind, physchars; - int mlen; + int mlen, newlines, newlines_guess, bound; + int mb_cur_max; /* We only expand the mode string for the last line of a multiline prompt (a prompt with embedded newlines). */ @@ -327,29 +352,54 @@ expand_prompt (pmt, flags, lp, lip, niflp, vlp) else nprompt = pmt; - /* Short-circuit if we can. */ - if ((MB_CUR_MAX <= 1 || rl_byte_oriented) && strchr (nprompt, RL_PROMPT_START_IGNORE) == 0) + mb_cur_max = MB_CUR_MAX; + + if (_rl_screenwidth == 0) + _rl_get_screen_size (0, 0); /* avoid division by zero */ + + /* Short-circuit if we can. We can do this if we are treating the prompt as + a sequence of bytes and there are no invisible characters in the prompt + to deal with. Since we populate local_prompt_newlines, we have to run + through the rest of the function if this prompt looks like it's going to + be longer than one screen line. */ + if ((mb_cur_max <= 1 || rl_byte_oriented) && strchr (nprompt, RL_PROMPT_START_IGNORE) == 0) { - r = (nprompt == pmt) ? savestring (pmt) : nprompt; + l = strlen (nprompt); + if (l < (_rl_screenwidth > 0 ? _rl_screenwidth : 80)) + { + r = (nprompt == pmt) ? savestring (pmt) : nprompt; + if (lp) + *lp = l; + if (lip) + *lip = 0; + if (niflp) + *niflp = 0; + if (vlp) + *vlp = l; + + local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * 2); + local_prompt_newlines[0] = 0; + local_prompt_newlines[1] = -1; - if (lp) - *lp = strlen (r); - if (lip) - *lip = 0; - if (niflp) - *niflp = 0; - if (vlp) - *vlp = lp ? *lp : strlen (r); - return r; + return r; + } } l = strlen (nprompt); /* XXX */ r = ret = (char *)xmalloc (l + 1); + /* Guess at how many screen lines the prompt will take to size the array that + keeps track of where the line wraps happen */ + newlines_guess = (_rl_screenwidth > 0) ? APPROX_DIV(l, _rl_screenwidth) : APPROX_DIV(l, 80); + local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * (newlines_guess + 1)); + local_prompt_newlines[newlines = 0] = 0; + for (rl = 1; rl <= newlines_guess; rl++) + local_prompt_newlines[rl] = -1; + rl = physchars = 0; /* mode string now part of nprompt */ - invfl = 0; /* invisible chars in first line of prompt */ - invflset = 0; /* we only want to set invfl once */ - igstart = 0; + invfl = 0; /* invisible chars in first line of prompt */ + invflset = 0; /* we only want to set invfl once */ + igstart = 0; /* we're not ignoring any characters yet */ for (ignoring = last = ninvis = 0, p = nprompt; p && *p; p++) { @@ -371,7 +421,7 @@ expand_prompt (pmt, flags, lp, lip, niflp, vlp) else { #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { pind = p - nprompt; ind = _rl_find_next_mbchar (nprompt, pind, 1, MB_FIND_NONZERO); @@ -405,11 +455,29 @@ expand_prompt (pmt, flags, lp, lip, niflp, vlp) ninvis++; /* invisible chars byte counter */ } - if (invflset == 0 && rl >= _rl_screenwidth) + if (invflset == 0 && physchars >= _rl_screenwidth) { invfl = ninvis; invflset = 1; } + + if (physchars >= (bound = (newlines + 1) * _rl_screenwidth) && local_prompt_newlines[newlines+1] == -1) + { + int new; + if (physchars > bound) /* should rarely happen */ + { +#if defined (HANDLE_MULTIBYTE) + *r = '\0'; /* need null-termination for strlen */ + if (mb_cur_max > 1 && rl_byte_oriented == 0) + new = _rl_find_prev_mbchar (ret, r - ret, MB_FIND_ANY); + else +#endif + new = r - ret - (physchars - bound); /* XXX */ + } + else + new = r - ret; + local_prompt_newlines[++newlines] = new; + } } } @@ -435,8 +503,7 @@ expand_prompt (pmt, flags, lp, lip, niflp, vlp) /* Just strip out RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE from PMT and return the rest of PMT. */ char * -_rl_strip_prompt (pmt) - char *pmt; +_rl_strip_prompt (char *pmt) { char *ret; @@ -445,7 +512,7 @@ _rl_strip_prompt (pmt) } void -_rl_reset_prompt () +_rl_reset_prompt (void) { rl_visible_prompt_length = rl_expand_prompt (rl_prompt); } @@ -461,15 +528,20 @@ _rl_reset_prompt () * prompt_visible_length = number of visible characters in local_prompt * prompt_prefix_length = number of visible characters in local_prompt_prefix * + * It also tries to keep track of the number of invisible characters in the + * prompt string, and where they are. + * * This function is called once per call to readline(). It may also be * called arbitrarily to expand the primary prompt. * * The return value is the number of visible characters on the last line - * of the (possibly multi-line) prompt. + * of the (possibly multi-line) prompt. In this case, multi-line means + * there are embedded newlines in the prompt string itself, not that the + * number of physical characters exceeds the screen width and the prompt + * wraps. */ int -rl_expand_prompt (prompt) - char *prompt; +rl_expand_prompt (char *prompt) { char *p, *t; int c; @@ -527,8 +599,7 @@ rl_expand_prompt (prompt) increased. If the lines have already been allocated, this ensures that they can hold at least MINSIZE characters. */ static void -init_line_structures (minsize) - int minsize; +init_line_structures (int minsize) { register int n; @@ -578,9 +649,9 @@ init_line_structures (minsize) line_structures_initialized = 1; } -/* Basic redisplay algorithm. */ +/* Basic redisplay algorithm. See comments inline. */ void -rl_redisplay () +rl_redisplay (void) { register int in, out, c, linenum, cursor_linenum; register char *line; @@ -643,6 +714,10 @@ rl_redisplay () one passed to readline()), use the values we have already expanded. If not, use what's already in rl_display_prompt. WRAP_OFFSET is the number of non-visible characters in the prompt string. */ + /* This is where we output the characters in the prompt before the last + newline, if any. If there aren't any embedded newlines, we don't + write anything. Copy the last line of the prompt string into the line in + any case */ if (rl_display_prompt == rl_prompt || local_prompt) { if (local_prompt_prefix && forced_display) @@ -697,6 +772,7 @@ rl_redisplay () wrap_offset = prompt_invis_chars_first_line = 0; } +#if defined (HANDLE_MULTIBYTE) #define CHECK_INV_LBREAKS() \ do { \ if (newlines >= (inv_lbsize - 2)) \ @@ -704,7 +780,22 @@ rl_redisplay () inv_lbsize *= 2; \ inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ } \ + if (newlines >= (line_state_invisible->wbsize - 2)) \ + { \ + line_state_invisible->wbsize *= 2; \ + line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \ + } \ } while (0) +#else +#define CHECK_INV_LBREAKS() \ + do { \ + if (newlines >= (inv_lbsize - 2)) \ + { \ + inv_lbsize *= 2; \ + inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ + } \ + } while (0) +#endif /* !HANDLE_MULTIBYTE */ #if defined (HANDLE_MULTIBYTE) #define CHECK_LPOS() \ @@ -718,7 +809,7 @@ rl_redisplay () inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ } \ inv_lbreaks[++newlines] = out; \ - if (newlines >= (line_state_invisible->wbsize - 1)) \ + if (newlines >= (line_state_invisible->wbsize - 2)) \ { \ line_state_invisible->wbsize *= 2; \ line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \ @@ -747,7 +838,10 @@ rl_redisplay () /* inv_lbreaks[i] is where line i starts in the buffer. */ inv_lbreaks[newlines = 0] = 0; /* lpos is a physical cursor position, so it needs to be adjusted by the - number of invisible characters in the prompt, per line */ + number of invisible characters in the prompt, per line. We compute + the line breaks in the prompt string in expand_prompt, taking invisible + characters into account, and if lpos exceeds the screen width, we copy + the data in the loop below. */ lpos = prompt_physical_chars + modmark; #if defined (HANDLE_MULTIBYTE) @@ -757,92 +851,38 @@ rl_redisplay () /* prompt_invis_chars_first_line is the number of invisible characters in the first physical line of the prompt. - wrap_offset - prompt_invis_chars_first_line is the number of invis - chars on the second (or, more generally, last) line. */ + wrap_offset - prompt_invis_chars_first_line is usually the number of + invis chars on the second (or, more generally, last) line. */ /* This is zero-based, used to set the newlines */ prompt_lines_estimate = lpos / _rl_screenwidth; /* what if lpos is already >= _rl_screenwidth before we start drawing the contents of the command line? */ - while (lpos >= _rl_screenwidth) - { - int z, p; - int nocorrect, wadjust; - - nocorrect = 0; - /* Adjust depending on the invisible characters in the line. We use a - heuristic based on experience: invisible characters nearly always - appear in the first and last lines of the prompt */ - wadjust = (newlines == 0) - ? prompt_invis_chars_first_line - : ((newlines == prompt_lines_estimate) - ? (wrap_offset - prompt_invis_chars_first_line) - : 0); - - /* fix from Darin Johnson <darin@acuson.com> for prompt string with - invisible characters that is longer than the screen width. The - prompt_invis_chars_first_line variable could be made into an array - saying how many invisible characters there are per line, but that's - probably too much work for the benefit gained. How many people have - prompts that exceed two physical lines? - Additional logic fix from Edward Catmur <ed@catmur.co.uk> */ -#if defined (HANDLE_MULTIBYTE) - if (mb_cur_max > 1 && rl_byte_oriented == 0 && prompt_multibyte_chars > 0) - { - nocorrect = 1; - n0 = num; - temp = local_prompt_len; - while (num < temp) - { - /* This has to take invisible characters in the prompt into - account. */ - z = _rl_col_width (local_prompt, n0, num, 1) - wadjust; - if (z > _rl_screenwidth) - { - num = _rl_find_prev_mbchar (local_prompt, num, MB_FIND_ANY); - break; - } - else if (z == _rl_screenwidth) - { - /* If we are in the middle or at the end of a multibyte - character, we want to move to the start, then find out - where it ends so we know where to insert the newline. - If this isn't a multibyte character, its the same as num++ */ - p = _rl_find_prev_mbchar (local_prompt, num, MB_FIND_ANY); - num = _rl_find_next_mbchar (local_prompt, p, 1, MB_FIND_ANY); - break; - } - num++; - } - temp = num; - } - else -#endif /* !HANDLE_MULTIBYTE */ - temp = ((newlines + 1) * _rl_screenwidth); + if (lpos >= _rl_screenwidth) + { + temp = 0; - /* Now account for invisible characters in the current line. */ - /* XXX - this assumes that the invisible characters may be split, but only - between the first and the last lines. */ - if (nocorrect == 0) - temp += wadjust; + /* first copy the linebreaks array we computed in expand_prompt */ + while (local_prompt_newlines[newlines+1] != -1) + { + temp = local_prompt_newlines[newlines+1]; + inv_lbreaks[++newlines] = temp; + } - inv_lbreaks[++newlines] = temp; -#if defined (HANDLE_MULTIBYTE) - /* lpos is a physical cursor position, so it needs to take the invisible - characters into account. */ + /* Now set lpos from the last newline */ if (mb_cur_max > 1 && rl_byte_oriented == 0 && prompt_multibyte_chars > 0) - lpos -= _rl_col_width (local_prompt, n0, num, 1) - wadjust; + lpos = _rl_col_width (local_prompt, temp, local_prompt_len, 1) - (wrap_offset - prompt_invis_chars_first_line); else -#endif - lpos -= _rl_screenwidth; /* all physical cursor positions */ + lpos -= (_rl_screenwidth * newlines); } prompt_last_screen_line = newlines; /* Draw the rest of the line (after the prompt) into invisible_line, keeping - track of where the cursor is (cpos_buffer_position), the number of the line containing - the cursor (lb_linenum), the last line number (lb_botlin and inv_botlin). + track of where the cursor is (cpos_buffer_position), the number of the + line containing the cursor (lb_linenum), the last line number (lb_botlin + and inv_botlin). It maintains an array of line breaks for display (inv_lbreaks). This handles expanding tabs for display and displaying meta characters. */ lb_linenum = 0; @@ -851,8 +891,13 @@ rl_redisplay () if (mb_cur_max > 1 && rl_byte_oriented == 0) { memset (&ps, 0, sizeof (mbstate_t)); - /* XXX - what if wc_bytes ends up <= 0? check for MB_INVALIDCH */ - wc_bytes = mbrtowc (&wc, rl_line_buffer, rl_end, &ps); + if (_rl_utf8locale && UTF8_SINGLEBYTE(rl_line_buffer[0])) + { + wc = (wchar_t)rl_line_buffer[0]; + wc_bytes = 1; + } + else + wc_bytes = mbrtowc (&wc, rl_line_buffer, rl_end, &ps); } else wc_bytes = 1; @@ -914,6 +959,9 @@ rl_redisplay () temp = _rl_screenwidth - lpos; CHECK_INV_LBREAKS (); inv_lbreaks[++newlines] = out + temp; +#if defined (HANDLE_MULTIBYTE) + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; +#endif lpos = 4 - temp; } else @@ -944,6 +992,9 @@ rl_redisplay () temp2 = _rl_screenwidth - lpos; CHECK_INV_LBREAKS (); inv_lbreaks[++newlines] = out + temp2; +#if defined (HANDLE_MULTIBYTE) + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; +#endif lpos = temp - temp2; while (out < newout) line[out++] = ' '; @@ -961,6 +1012,9 @@ rl_redisplay () line[out++] = '\0'; /* XXX - sentinel */ CHECK_INV_LBREAKS (); inv_lbreaks[++newlines] = out; +#if defined (HANDLE_MULTIBYTE) + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; +#endif lpos = 0; } else if (CTRL_CHAR (c) || c == RUBOUT) @@ -1012,15 +1066,21 @@ rl_redisplay () if (mb_cur_max > 1 && rl_byte_oriented == 0) { in += wc_bytes; - /* XXX - what if wc_bytes ends up <= 0? check for MB_INVALIDCH */ - wc_bytes = mbrtowc (&wc, rl_line_buffer + in, rl_end - in, &ps); + if (_rl_utf8locale && UTF8_SINGLEBYTE(rl_line_buffer[in])) + { + wc = (wchar_t)rl_line_buffer[in]; + wc_bytes = 1; + memset (&ps, 0, sizeof (mbstate_t)); /* re-init state */ + } + else + wc_bytes = mbrtowc (&wc, rl_line_buffer + in, rl_end - in, &ps); } else in++; #endif - } line[out] = '\0'; + line_totbytes = out; if (cpos_buffer_position < 0) { cpos_buffer_position = out; @@ -1030,6 +1090,10 @@ rl_redisplay () inv_botlin = lb_botlin = _rl_inv_botlin = newlines; CHECK_INV_LBREAKS (); inv_lbreaks[newlines+1] = out; +#if defined (HANDLE_MULTIBYTE) + /* This should be 0 anyway */ + line_state_invisible->wrapped_line[newlines+1] = _rl_wrapped_multicolumn; +#endif cursor_linenum = lb_linenum; /* CPOS_BUFFER_POSITION == position in buffer where cursor should be placed. @@ -1097,8 +1161,8 @@ rl_redisplay () /* update_line potentially changes _rl_last_c_pos, but doesn't take invisible characters into account, since _rl_last_c_pos - is an absolute cursor position in a multibyte locale. See - if compensating here is the right thing, or if we have to + is an absolute cursor position in a multibyte locale. We + choose to (mostly) compensate for that here, rather than change update_line itself. There are several cases in which update_line adjusts _rl_last_c_pos itself (so it can pass _rl_move_cursor_relative accurate values); it communicates @@ -1109,11 +1173,16 @@ rl_redisplay () wrap_offset. */ if (linenum == 0 && (mb_cur_max > 1 && rl_byte_oriented == 0) && OLD_CPOS_IN_PROMPT()) _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */ - else if (linenum == prompt_last_screen_line && prompt_physical_chars > _rl_screenwidth && + else if (cpos_adjusted == 0 && + linenum == prompt_last_screen_line && + prompt_physical_chars > _rl_screenwidth && (mb_cur_max > 1 && rl_byte_oriented == 0) && - cpos_adjusted == 0 && _rl_last_c_pos != o_cpos && - _rl_last_c_pos > (prompt_last_invisible - _rl_screenwidth - prompt_invis_chars_first_line)) + _rl_last_c_pos > (prompt_last_invisible - _rl_screenwidth - prompt_invis_chars_first_line)) /* XXX - rethink this last one */ + /* This assumes that all the invisible characters are split + between the first and last lines of the prompt, if the + prompt consumes more than two lines. It's usually right */ + /* XXX - not sure this is ever executed */ _rl_last_c_pos -= (wrap_offset-prompt_invis_chars_first_line); /* If this is the line with the prompt, we might need to @@ -1183,6 +1252,9 @@ rl_redisplay () _rl_last_c_pos += wrap_offset; } + /* Now we move the cursor to where it needs to be. First, make + sure we are on the correct line (cursor_linenum). */ + /* We have to reprint the prompt if it contains invisible characters, since it's not generally OK to just reprint the characters from the current cursor position. But we @@ -1224,9 +1296,10 @@ rl_redisplay () position that doesn't take invisible characters in the prompt into account. We use a fudge factor to compensate. */ - /* Since _rl_backspace() doesn't know about invisible characters in the - prompt, and there's no good way to tell it, we compensate for - those characters here and call _rl_backspace() directly. */ + /* Since _rl_backspace() doesn't know about invisible characters in + the prompt, and there's no good way to tell it, we compensate for + those characters here and call _rl_backspace() directly if + necessary */ if (wrap_offset && cursor_linenum == 0 && nleft < _rl_last_c_pos) { /* TX == new physical cursor position in multibyte locale. */ @@ -1251,7 +1324,7 @@ rl_redisplay () _rl_move_cursor_relative (nleft, &invisible_line[pos]); } } - else /* Do horizontal scrolling. */ + else /* Do horizontal scrolling. Much simpler */ { #define M_OFFSET(margin, offset) ((margin) == 0 ? offset : 0) int lmargin, ndisp, nleft, phys_c_pos, t; @@ -1311,7 +1384,7 @@ rl_redisplay () right edge of the screen. If LMARGIN is 0, we need to take the wrap offset into account. */ t = lmargin + M_OFFSET (lmargin, wrap_offset) + _rl_screenwidth; - if (t < out) + if (t > 0 && t < out) line[t - 1] = '>'; if (rl_display_fixed == 0 || forced_display || lmargin != last_lmargin) @@ -1389,9 +1462,7 @@ new: eddie> Oh, my little buggy says to me, as lurgid as Could be made even smarter, but this works well enough */ static void -update_line (old, new, current_line, omax, nmax, inv_botlin) - register char *old, *new; - int current_line, omax, nmax, inv_botlin; +update_line (char *old, char *new, int current_line, int omax, int nmax, int inv_botlin) { register char *ofd, *ols, *oe, *nfd, *nls, *ne; int temp, lendiff, wsatend, od, nd, twidth, o_cpos; @@ -1416,52 +1487,145 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) if (temp == _rl_screenwidth && _rl_term_autowrap && !_rl_horizontal_scroll_mode && _rl_last_v_pos == current_line - 1) { + /* We're going to wrap around by writing the first character of NEW to + the screen and dealing with changes to what's visible by modifying + OLD to match it. Complicated by the presence of multi-width + characters at the end of the line or beginning of the new one. */ + /* old is always somewhere in visible_line; new is always somewhere in + invisible_line. These should always be null-terminated. */ #if defined (HANDLE_MULTIBYTE) if (mb_cur_max > 1 && rl_byte_oriented == 0) { wchar_t wc; mbstate_t ps; - int tempwidth, bytes; + int oldwidth, newwidth; + int oldbytes, newbytes; size_t ret; /* This fixes only double-column characters, but if the wrapped character consumes more than three columns, spaces will be inserted in the string buffer. */ - if (current_line < line_state_visible->wbsize && line_state_visible->wrapped_line[current_line] > 0) - _rl_clear_to_eol (line_state_visible->wrapped_line[current_line]); + /* XXX remember that we are working on the invisible line right now; + we don't swap visible and invisible until just before rl_redisplay + returns */ + /* This will remove the extra placeholder space we added with + _rl_wrapped_multicolumn */ + if (current_line < line_state_invisible->wbsize && line_state_invisible->wrapped_line[current_line] > 0) + _rl_clear_to_eol (line_state_invisible->wrapped_line[current_line]); + + /* 1. how many screen positions does first char in old consume? */ + memset (&ps, 0, sizeof (mbstate_t)); + ret = mbrtowc (&wc, old, mb_cur_max, &ps); + oldbytes = ret; + if (MB_INVALIDCH (ret)) + { + oldwidth = 1; + oldbytes = 1; + } + else if (MB_NULLWCH (ret)) + oldwidth = 0; + else + oldwidth = WCWIDTH (wc); + if (oldwidth < 0) + oldwidth = 1; + /* 2. how many screen positions does the first char in new consume? */ memset (&ps, 0, sizeof (mbstate_t)); ret = mbrtowc (&wc, new, mb_cur_max, &ps); + newbytes = ret; if (MB_INVALIDCH (ret)) { - tempwidth = 1; - ret = 1; + newwidth = 1; + newbytes = 1; } else if (MB_NULLWCH (ret)) - tempwidth = 0; + newwidth = 0; else - tempwidth = WCWIDTH (wc); + newwidth = WCWIDTH (wc); + if (newwidth < 0) + newwidth = 1; + + /* 3. if the new width is less than the old width, we need to keep + going in new until we have consumed at least that many screen + positions, and figure out how many bytes that will take */ + while (newbytes < nmax && newwidth < oldwidth) + { + int t; - if (tempwidth > 0) + ret = mbrtowc (&wc, new+newbytes, mb_cur_max, &ps); + if (MB_INVALIDCH (ret)) + { + newwidth += 1; + newbytes += 1; + } + else if (MB_NULLWCH (ret)) + break; + else + { + t = WCWIDTH (wc); + newwidth += (t >= 0) ? t : 1; + newbytes += ret; + } + } + /* 4. If the new width is more than the old width, keep going in old + until we have consumed exactly that many screen positions, and + figure out how many bytes that will take. This is an optimization */ + while (oldbytes < omax && oldwidth < newwidth) { - int count, i; - bytes = ret; - for (count = 0; count < bytes; count++) - putc (new[count], rl_outstream); - _rl_last_c_pos = tempwidth; + int t; + + ret = mbrtowc (&wc, old+oldbytes, mb_cur_max, &ps); + if (MB_INVALIDCH (ret)) + { + oldwidth += 1; + oldbytes += 1; + } + else if (MB_NULLWCH (ret)) + break; + else + { + t = WCWIDTH (wc); + oldwidth += (t >= 0) ? t : 1; + oldbytes += ret; + } + } + /* 5. write the first newbytes of new, which takes newwidth. This is + where the screen wrapping takes place, and we are now writing + characters onto the new line. We need to fix up old so it + accurately reflects what is on the screen after the + _rl_output_some_chars below. */ + if (newwidth > 0) + { + int count, i, j; + char *optr; + + _rl_output_some_chars (new, newbytes); + _rl_last_c_pos = newwidth; _rl_last_v_pos++; - memset (&ps, 0, sizeof (mbstate_t)); - ret = mbrtowc (&wc, old, mb_cur_max, &ps); - if (ret != 0 && bytes != 0) + + /* 5a. If the number of screen positions doesn't match, punt + and do a dumb update. */ + if (newwidth != oldwidth) { - if (MB_INVALIDCH (ret)) - ret = 1; - memmove (old+bytes, old+ret, strlen (old+ret)); - memcpy (old, new, bytes); + ne = new + nmax; + nd = newbytes; + nfd = new + nd; + goto dumb_update; + } + if (oldbytes != 0 && newbytes != 0) + { + /* We have written as many bytes from new as we need to + consume the first character of old. Fix up `old' so it + reflects the new screen contents. We use +1 in the + memmove call to copy the trailing NUL. */ + memmove (old+newbytes, old+oldbytes, strlen (old+oldbytes) + 1); + memcpy (old, new, newbytes); + j = newbytes - oldbytes; + + omax += j; /* Fix up indices if we copy data from one line to another */ - omax += bytes - ret; for (i = current_line+1; i <= inv_botlin+1; i++) - vis_lbreaks[i] += bytes - ret; + vis_lbreaks[i] += j; } } else @@ -1487,7 +1651,6 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) } } - /* Find first difference. */ #if defined (HANDLE_MULTIBYTE) if (mb_cur_max > 1 && rl_byte_oriented == 0) @@ -1506,6 +1669,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) memset (&ps_new, 0, sizeof(mbstate_t)); memset (&ps_old, 0, sizeof(mbstate_t)); + /* Are the old and new lines the same? */ if (omax == nmax && STREQN (new, old, omax)) { old_offset = omax; @@ -1515,6 +1679,8 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) } else { + /* Go through the line from the beginning and find the first + difference. */ new_offset = old_offset = 0; for (ofd = old, nfd = new; (ofd - old < omax) && *ofd && @@ -1570,6 +1736,8 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) wsatend = 1; /* flag for trailing whitespace */ #if defined (HANDLE_MULTIBYTE) + /* Find the last character that is the same between the two lines. This + bounds the region that needs to change. */ if (mb_cur_max > 1 && rl_byte_oriented == 0) { ols = old + _rl_find_prev_mbchar (old, oe - old, MB_FIND_ANY); @@ -1729,12 +1897,29 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) nd = lendiff; /* Do a dumb update and return */ +dumb_update: temp = ne - nfd; if (temp > 0) { _rl_output_some_chars (nfd, temp); if (mb_cur_max > 1 && rl_byte_oriented == 0) - _rl_last_c_pos += _rl_col_width (new, nd, ne - new, 1); + { + _rl_last_c_pos += _rl_col_width (new, nd, ne - new, 1); + /* Need to adjust here based on wrap_offset. Guess that if + this is the line containing the last line of the prompt + we need to adjust by + wrap_offset-prompt_invis_chars_first_line + on the assumption that this is the number of invisible + characters in the last line of the prompt. */ + if (wrap_offset > prompt_invis_chars_first_line && + current_line == prompt_last_screen_line && + prompt_physical_chars > _rl_screenwidth && + _rl_horizontal_scroll_mode == 0) + { + _rl_last_c_pos -= wrap_offset - prompt_invis_chars_first_line; + cpos_adjusted = 1; + } + } else _rl_last_c_pos += temp; } @@ -1809,6 +1994,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) { /* Non-zero if we're increasing the number of lines. */ int gl = current_line >= _rl_vis_botlin && inv_botlin > _rl_vis_botlin; + /* If col_lendiff is > 0, implying that the new string takes up more screen real estate than the old, but lendiff is < 0, meaning that it takes fewer bytes, we need to just output the characters starting @@ -1824,7 +2010,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) cpos_adjusted to let the caller know. */ if (current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) { - _rl_last_c_pos -= wrap_offset; + _rl_last_c_pos -= wrap_offset; /* XXX - prompt_invis_chars_first_line? */ cpos_adjusted = 1; } return; @@ -1877,7 +2063,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) and set cpos_adjusted to let the caller know. */ if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) { - _rl_last_c_pos -= wrap_offset; + _rl_last_c_pos -= wrap_offset; /* XXX - prompt_invis_chars_first_line? */ cpos_adjusted = 1; } return; @@ -1890,7 +2076,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) and set cpos_adjusted to let the caller know. */ if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) { - _rl_last_c_pos -= wrap_offset; + _rl_last_c_pos -= wrap_offset; /* XXX - prompt_invis_chars_first_line? */ cpos_adjusted = 1; } } @@ -1957,12 +2143,26 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); if (current_line == 0 && wrap_offset && displaying_prompt_first_line && - _rl_last_c_pos > wrap_offset && + _rl_last_c_pos >= wrap_offset && /* XXX was > */ ((nfd - new) <= prompt_last_invisible)) { - _rl_last_c_pos -= wrap_offset; + _rl_last_c_pos -= wrap_offset; /* XXX - prompt_invis_chars_first_line? */ cpos_adjusted = 1; } + +#if 1 +#ifdef HANDLE_MULTIBYTE + /* If we write a non-space into the last screen column, + remove the note that we added a space to compensate for + a multibyte double-width character that didn't fit, since + it's only valid for what was previously there. */ + /* XXX - watch this */ + if (_rl_last_c_pos == _rl_screenwidth && + line_state_invisible->wrapped_line[current_line+1] && + nfd[bytes_to_insert-1] != ' ') + line_state_invisible->wrapped_line[current_line+1] = 0; +#endif +#endif } else _rl_last_c_pos += bytes_to_insert; @@ -1994,7 +2194,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) _rl_last_c_pos > wrap_offset && ((nfd - new) <= prompt_last_invisible)) { - _rl_last_c_pos -= wrap_offset; + _rl_last_c_pos -= wrap_offset; /* XXX - prompt_invis_chars_first_line? */ cpos_adjusted = 1; } } @@ -2024,7 +2224,7 @@ clear_rest_of_line: /* Tell the update routines that we have moved onto a new (empty) line. */ int -rl_on_new_line () +rl_on_new_line (void) { if (visible_line) visible_line[0] = '\0'; @@ -2040,7 +2240,7 @@ rl_on_new_line () /* Clear all screen lines occupied by the current readline line buffer (visible line) */ int -rl_clear_visible_line () +rl_clear_visible_line (void) { int curr_line; @@ -2068,9 +2268,10 @@ rl_clear_visible_line () /* Tell the update routines that we have moved onto a new line with the prompt already displayed. Code originally from the version of readline distributed with CLISP. rl_expand_prompt must have already been called - (explicitly or implicitly). This still doesn't work exactly right. */ + (explicitly or implicitly). This still doesn't work exactly right; it + should use expand_prompt() */ int -rl_on_new_line_with_prompt () +rl_on_new_line_with_prompt (void) { int prompt_size, i, l, real_screenwidth, newlines; char *prompt_last_line, *lprompt; @@ -2126,7 +2327,7 @@ rl_on_new_line_with_prompt () /* Actually update the display, period. */ int -rl_forced_update_display () +rl_forced_update_display (void) { register char *temp; @@ -2144,7 +2345,7 @@ rl_forced_update_display () /* Redraw only the last line of a multi-line prompt. */ void -rl_redraw_prompt_last_line () +rl_redraw_prompt_last_line (void) { char *t; @@ -2162,9 +2363,7 @@ rl_redraw_prompt_last_line () the movement is being done. DATA is always the visible line or the invisible line */ void -_rl_move_cursor_relative (new, data) - int new; - const char *data; +_rl_move_cursor_relative (int new, const char *data) { register int i; int woff; /* number of invisible chars on current line */ @@ -2321,8 +2520,7 @@ _rl_move_cursor_relative (new, data) /* PWP: move the cursor up or down. */ void -_rl_move_vert (to) - int to; +_rl_move_vert (int to) { register int delta, i; @@ -2362,8 +2560,7 @@ _rl_move_vert (to) /* Physically print C on rl_outstream. This is for functions which know how to optimize the display. Return the number of characters output. */ int -rl_show_char (c) - int c; +rl_show_char (int c) { int n = 1; if (META_CHAR (c) && (_rl_output_meta_chars == 0)) @@ -2390,8 +2587,7 @@ rl_show_char (c) } int -rl_character_len (c, pos) - register int c, pos; +rl_character_len (int c, int pos) { unsigned char uc; @@ -2526,7 +2722,7 @@ rl_message (format, arg1, arg2) /* How to clear things from the "echo-area". */ int -rl_clear_message () +rl_clear_message (void) { rl_display_prompt = rl_prompt; if (msg_saved_prompt) @@ -2539,7 +2735,7 @@ rl_clear_message () } int -rl_reset_line_state () +rl_reset_line_state (void) { rl_on_new_line (); @@ -2548,8 +2744,12 @@ rl_reset_line_state () return 0; } +/* Save all of the variables associated with the prompt and its display. Most + of the complexity is dealing with the invisible characters in the prompt + string and where they are. There are enough of these that I should consider + a struct. */ void -rl_save_prompt () +rl_save_prompt (void) { saved_local_prompt = local_prompt; saved_local_prefix = local_prompt_prefix; @@ -2559,22 +2759,28 @@ rl_save_prompt () saved_visible_length = prompt_visible_length; saved_invis_chars_first_line = prompt_invis_chars_first_line; saved_physical_chars = prompt_physical_chars; + saved_local_prompt_newlines = local_prompt_newlines; local_prompt = local_prompt_prefix = (char *)0; local_prompt_len = 0; + local_prompt_newlines = (int *)0; + prompt_last_invisible = prompt_visible_length = prompt_prefix_length = 0; prompt_invis_chars_first_line = prompt_physical_chars = 0; } void -rl_restore_prompt () +rl_restore_prompt (void) { FREE (local_prompt); FREE (local_prompt_prefix); + FREE (local_prompt_newlines); local_prompt = saved_local_prompt; local_prompt_prefix = saved_local_prefix; local_prompt_len = saved_local_length; + local_prompt_newlines = saved_local_prompt_newlines; + prompt_prefix_length = saved_prefix_length; prompt_last_invisible = saved_last_invisible; prompt_visible_length = saved_visible_length; @@ -2586,11 +2792,11 @@ rl_restore_prompt () saved_local_length = 0; saved_last_invisible = saved_visible_length = saved_prefix_length = 0; saved_invis_chars_first_line = saved_physical_chars = 0; + saved_local_prompt_newlines = 0; } char * -_rl_make_prompt_for_search (pchar) - int pchar; +_rl_make_prompt_for_search (int pchar) { int len; char *pmt, *p; @@ -2628,8 +2834,7 @@ _rl_make_prompt_for_search (pchar) /* Quick redisplay hack when erasing characters at the end of the line. */ void -_rl_erase_at_end_of_line (l) - int l; +_rl_erase_at_end_of_line (int l) { register int i; @@ -2643,10 +2848,10 @@ _rl_erase_at_end_of_line (l) } /* Clear to the end of the line. COUNT is the minimum - number of character spaces to clear, */ + number of character spaces to clear, but we use a terminal escape + sequence if available. */ void -_rl_clear_to_eol (count) - int count; +_rl_clear_to_eol (int count) { #ifndef __MSDOS__ if (_rl_term_clreol) @@ -2660,36 +2865,33 @@ _rl_clear_to_eol (count) /* Clear to the end of the line using spaces. COUNT is the minimum number of character spaces to clear, */ static void -space_to_eol (count) - int count; +space_to_eol (int count) { register int i; for (i = 0; i < count; i++) - putc (' ', rl_outstream); + putc (' ', rl_outstream); _rl_last_c_pos += count; } void -_rl_clear_screen () +_rl_clear_screen (void) { -#ifndef __DJGPP__ +#if defined (__DJGPP__) + ScreenClear (); + ScreenSetCursor (0, 0); +#else if (_rl_term_clrpag) tputs (_rl_term_clrpag, 1, _rl_output_character_function); else rl_crlf (); -#else - ScreenClear (); - ScreenSetCursor (0, 0); #endif /* __DJGPP__ */ } /* Insert COUNT characters from STRING to the output stream at column COL. */ static void -insert_some_chars (string, count, col) - char *string; - int count, col; +insert_some_chars (char *string, int count, int col) { open_some_spaces (col); _rl_output_some_chars (string, count); @@ -2699,8 +2901,7 @@ insert_some_chars (string, count, col) ncurses documentation and use either im/ei with explicit spaces, or IC/ic by itself. We assume there will either be ei or we don't need to use it. */ static void -open_some_spaces (col) - int col; +open_some_spaces (int col) { #if !defined (__MSDOS__) && (!defined (__MINGW32__) || defined (NCURSES_VERSION)) char *buffer; @@ -2736,8 +2937,7 @@ open_some_spaces (col) /* Delete COUNT characters from the display line. */ static void -delete_chars (count) - int count; +delete_chars (int count) { if (count > _rl_screenwidth) /* XXX */ return; @@ -2759,9 +2959,9 @@ delete_chars (count) } void -_rl_update_final () +_rl_update_final (void) { - int full_lines; + int full_lines, woff, botline_length; full_lines = 0; /* If the cursor is the only thing on an otherwise-blank last line, @@ -2773,26 +2973,34 @@ _rl_update_final () full_lines = 1; } _rl_move_vert (_rl_vis_botlin); + woff = W_OFFSET(_rl_vis_botlin, wrap_offset); + botline_length = VIS_LLEN(_rl_vis_botlin) - woff; /* If we've wrapped lines, remove the final xterm line-wrap flag. */ - if (full_lines && _rl_term_autowrap && (VIS_LLEN(_rl_vis_botlin) == _rl_screenwidth)) + if (full_lines && _rl_term_autowrap && botline_length == _rl_screenwidth) { char *last_line; - last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; + /* LAST_LINE includes invisible characters, so if you want to get the + last character of the first line, you have to take WOFF into account. + This needs to be done for both calls to _rl_move_cursor_relative, + which takes a buffer position as the first argument, and any direct + subscripts of LAST_LINE. */ + last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; /* = VIS_CHARS(_rl_vis_botlin); */ cpos_buffer_position = -1; /* don't know where we are in buffer */ - _rl_move_cursor_relative (_rl_screenwidth - 1, last_line); /* XXX */ + _rl_move_cursor_relative (_rl_screenwidth - 1 + woff, last_line); /* XXX */ _rl_clear_to_eol (0); - putc (last_line[_rl_screenwidth - 1], rl_outstream); + putc (last_line[_rl_screenwidth - 1 + woff], rl_outstream); } _rl_vis_botlin = 0; - rl_crlf (); + if (botline_length > 0 || _rl_last_c_pos > 0) + rl_crlf (); fflush (rl_outstream); rl_display_fixed++; } /* Move to the start of the current line. */ static void -cr () +cr (void) { if (_rl_term_cr) { @@ -2809,8 +3017,7 @@ cr () terminal escape sequences. Called with the cursor at column 0 of the line to draw the prompt on. */ static void -redraw_prompt (t) - char *t; +redraw_prompt (char *t) { char *oldp; @@ -2834,7 +3041,7 @@ redraw_prompt (t) /* Redisplay the current line after a SIGWINCH is received. */ void -_rl_redisplay_after_sigwinch () +_rl_redisplay_after_sigwinch (void) { char *t; @@ -2879,7 +3086,7 @@ _rl_redisplay_after_sigwinch () } void -_rl_clean_up_for_exit () +_rl_clean_up_for_exit (void) { if (_rl_echoing_p) { @@ -2892,7 +3099,7 @@ _rl_clean_up_for_exit () } void -_rl_erase_entire_line () +_rl_erase_entire_line (void) { cr (); _rl_clear_to_eol (0); @@ -2901,7 +3108,7 @@ _rl_erase_entire_line () } void -_rl_ttyflush () +_rl_ttyflush (void) { fflush (rl_outstream); } @@ -2909,7 +3116,7 @@ _rl_ttyflush () /* return the `current display line' of the cursor -- the number of lines to move up to get to the first screen line of the current readline line. */ int -_rl_current_display_line () +_rl_current_display_line (void) { int ret, nleft; @@ -2933,9 +3140,7 @@ _rl_current_display_line () In the case of multibyte characters with stateful encoding, we have to scan from the beginning of the string to take the state into account. */ static int -_rl_col_width (str, start, end, flags) - const char *str; - int start, end, flags; +_rl_col_width (const char *str, int start, int end, int flags) { wchar_t wc; mbstate_t ps; @@ -2968,7 +3173,13 @@ _rl_col_width (str, start, end, flags) while (point < start) { - tmp = mbrlen (str + point, max, &ps); + if (_rl_utf8locale && UTF8_SINGLEBYTE(str[point])) + { + memset (&ps, 0, sizeof (mbstate_t)); + tmp = 1; + } + else + tmp = mbrlen (str + point, max, &ps); if (MB_INVALIDCH ((size_t)tmp)) { /* In this case, the bytes are invalid or too short to compose a @@ -2997,7 +3208,13 @@ _rl_col_width (str, start, end, flags) while (point < end) { - tmp = mbrtowc (&wc, str + point, max, &ps); + if (_rl_utf8locale && UTF8_SINGLEBYTE(str[point])) + { + tmp = 1; + wc = (wchar_t) str[point]; + } + else + tmp = mbrtowc (&wc, str + point, max, &ps); if (MB_INVALIDCH ((size_t)tmp)) { /* In this case, the bytes are invalid or too short to compose a |