1702 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1702 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* text.c -- text handling commands for readline. */
 | |
| 
 | |
| /* Copyright (C) 1987-2010 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.      
 | |
| 
 | |
|    Readline is free software: you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation, either version 3 of the License, or
 | |
|    (at your option) any later version.
 | |
| 
 | |
|    Readline is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with Readline.  If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| 
 | |
| #define READLINE_LIBRARY
 | |
| 
 | |
| #if defined (HAVE_CONFIG_H)
 | |
| #  include <config.h>
 | |
| #endif
 | |
| 
 | |
| #if defined (HAVE_UNISTD_H)
 | |
| #  include <unistd.h>
 | |
| #endif /* HAVE_UNISTD_H */
 | |
| 
 | |
| #if defined (HAVE_STDLIB_H)
 | |
| #  include <stdlib.h>
 | |
| #else
 | |
| #  include "ansi_stdlib.h"
 | |
| #endif /* HAVE_STDLIB_H */
 | |
| 
 | |
| #if defined (HAVE_LOCALE_H)
 | |
| #  include <locale.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| 
 | |
| /* System-specific feature definitions and include files. */
 | |
| #include "rldefs.h"
 | |
| #include "rlmbutil.h"
 | |
| 
 | |
| #if defined (__EMX__)
 | |
| #  define INCL_DOSPROCESS
 | |
| #  include <os2.h>
 | |
| #endif /* __EMX__ */
 | |
| 
 | |
| /* Some standard library routines. */
 | |
| #include "readline.h"
 | |
| #include "history.h"
 | |
| 
 | |
| #include "rlprivate.h"
 | |
| #include "rlshell.h"
 | |
| #include "xmalloc.h"
 | |
| 
 | |
| /* Forward declarations. */
 | |
| static int rl_change_case PARAMS((int, int));
 | |
| static int _rl_char_search PARAMS((int, int, int));
 | |
| 
 | |
| #if defined (READLINE_CALLBACKS)
 | |
| static int _rl_insert_next_callback PARAMS((_rl_callback_generic_arg *));
 | |
| static int _rl_char_search_callback PARAMS((_rl_callback_generic_arg *));
 | |
| #endif
 | |
| 
 | |
| /* The largest chunk of text that can be inserted in one call to
 | |
|    rl_insert_text.  Text blocks larger than this are divided. */
 | |
| #define TEXT_COUNT_MAX	1024
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Insert and Delete			    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| /* Insert a string of text into the line at point.  This is the only
 | |
|    way that you should do insertion.  _rl_insert_char () calls this
 | |
|    function.  Returns the number of characters inserted. */
 | |
| int
 | |
| rl_insert_text (string)
 | |
|      const char *string;
 | |
| {
 | |
|   register int i, l;
 | |
| 
 | |
|   l = (string && *string) ? strlen (string) : 0;
 | |
|   if (l == 0)
 | |
|     return 0;
 | |
| 
 | |
|   if (rl_end + l >= rl_line_buffer_len)
 | |
|     rl_extend_line_buffer (rl_end + l);
 | |
| 
 | |
|   for (i = rl_end; i >= rl_point; i--)
 | |
|     rl_line_buffer[i + l] = rl_line_buffer[i];
 | |
|   strncpy (rl_line_buffer + rl_point, string, l);
 | |
| 
 | |
|   /* Remember how to undo this if we aren't undoing something. */
 | |
|   if (_rl_doing_an_undo == 0)
 | |
|     {
 | |
|       /* If possible and desirable, concatenate the undos. */
 | |
|       if ((l == 1) &&
 | |
| 	  rl_undo_list &&
 | |
| 	  (rl_undo_list->what == UNDO_INSERT) &&
 | |
| 	  (rl_undo_list->end == rl_point) &&
 | |
| 	  (rl_undo_list->end - rl_undo_list->start < 20))
 | |
| 	rl_undo_list->end++;
 | |
|       else
 | |
| 	rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL);
 | |
|     }
 | |
|   rl_point += l;
 | |
|   rl_end += l;
 | |
|   rl_line_buffer[rl_end] = '\0';
 | |
|   return l;
 | |
| }
 | |
| 
 | |
| /* Delete the string between FROM and TO.  FROM is inclusive, TO is not.
 | |
|    Returns the number of characters deleted. */
 | |
| int
 | |
| rl_delete_text (from, to)
 | |
|      int from, to;
 | |
| {
 | |
|   register char *text;
 | |
|   register int diff, i;
 | |
| 
 | |
|   /* Fix it if the caller is confused. */
 | |
|   if (from > to)
 | |
|     SWAP (from, to);
 | |
| 
 | |
|   /* fix boundaries */
 | |
|   if (to > rl_end)
 | |
|     {
 | |
|       to = rl_end;
 | |
|       if (from > to)
 | |
| 	from = to;
 | |
|     }
 | |
|   if (from < 0)
 | |
|     from = 0;
 | |
| 
 | |
|   text = rl_copy_text (from, to);
 | |
| 
 | |
|   /* Some versions of strncpy() can't handle overlapping arguments. */
 | |
|   diff = to - from;
 | |
|   for (i = from; i < rl_end - diff; i++)
 | |
|     rl_line_buffer[i] = rl_line_buffer[i + diff];
 | |
| 
 | |
|   /* Remember how to undo this delete. */
 | |
|   if (_rl_doing_an_undo == 0)
 | |
|     rl_add_undo (UNDO_DELETE, from, to, text);
 | |
|   else
 | |
|     xfree (text);
 | |
| 
 | |
|   rl_end -= diff;
 | |
|   rl_line_buffer[rl_end] = '\0';
 | |
|   return (diff);
 | |
| }
 | |
| 
 | |
| /* Fix up point so that it is within the line boundaries after killing
 | |
|    text.  If FIX_MARK_TOO is non-zero, the mark is forced within line
 | |
|    boundaries also. */
 | |
| 
 | |
| #define _RL_FIX_POINT(x) \
 | |
| 	do { \
 | |
| 	if (x > rl_end) \
 | |
| 	  x = rl_end; \
 | |
| 	else if (x < 0) \
 | |
| 	  x = 0; \
 | |
| 	} while (0)
 | |
| 
 | |
| void
 | |
| _rl_fix_point (fix_mark_too)
 | |
|      int fix_mark_too;
 | |
| {
 | |
|   _RL_FIX_POINT (rl_point);
 | |
|   if (fix_mark_too)
 | |
|     _RL_FIX_POINT (rl_mark);
 | |
| }
 | |
| #undef _RL_FIX_POINT
 | |
| 
 | |
| /* Replace the contents of the line buffer between START and END with
 | |
|    TEXT.  The operation is undoable.  To replace the entire line in an
 | |
|    undoable mode, use _rl_replace_text(text, 0, rl_end); */
 | |
| int
 | |
| _rl_replace_text (text, start, end)
 | |
|      const char *text;
 | |
|      int start, end;
 | |
| {
 | |
|   int n;
 | |
| 
 | |
|   n = 0;
 | |
|   rl_begin_undo_group ();
 | |
|   if (start <= end)
 | |
|     rl_delete_text (start, end + 1);
 | |
|   rl_point = start;
 | |
|   if (*text)
 | |
|     n = rl_insert_text (text);
 | |
|   rl_end_undo_group ();
 | |
| 
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| /* Replace the current line buffer contents with TEXT.  If CLEAR_UNDO is
 | |
|    non-zero, we free the current undo list. */
 | |
| void
 | |
| rl_replace_line (text, clear_undo)
 | |
|      const char *text;
 | |
|      int clear_undo;
 | |
| {
 | |
|   int len;
 | |
| 
 | |
|   len = strlen (text);
 | |
|   if (len >= rl_line_buffer_len)
 | |
|     rl_extend_line_buffer (len);
 | |
|   strcpy (rl_line_buffer, text);
 | |
|   rl_end = len;
 | |
| 
 | |
|   if (clear_undo)
 | |
|     rl_free_undo_list ();
 | |
| 
 | |
|   _rl_fix_point (1);
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Readline character functions		    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| /* This is not a gap editor, just a stupid line input routine.  No hair
 | |
|    is involved in writing any of the functions, and none should be. */
 | |
| 
 | |
| /* Note that:
 | |
| 
 | |
|    rl_end is the place in the string that we would place '\0';
 | |
|    i.e., it is always safe to place '\0' there.
 | |
| 
 | |
|    rl_point is the place in the string where the cursor is.  Sometimes
 | |
|    this is the same as rl_end.
 | |
| 
 | |
|    Any command that is called interactively receives two arguments.
 | |
|    The first is a count: the numeric arg pased to this command.
 | |
|    The second is the key which invoked this command.
 | |
| */
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Movement Commands			    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| /* Note that if you `optimize' the display for these functions, you cannot
 | |
|    use said functions in other functions which do not do optimizing display.
 | |
|    I.e., you will have to update the data base for rl_redisplay, and you
 | |
|    might as well let rl_redisplay do that job. */
 | |
| 
 | |
| /* Move forward COUNT bytes. */
 | |
| int
 | |
| rl_forward_byte (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (count < 0)
 | |
|     return (rl_backward_byte (-count, key));
 | |
| 
 | |
|   if (count > 0)
 | |
|     {
 | |
|       int end, lend;
 | |
| 
 | |
|       end = rl_point + count;
 | |
| #if defined (VI_MODE)
 | |
|       lend = rl_end > 0 ? rl_end - (VI_COMMAND_MODE()) : rl_end;
 | |
| #else
 | |
|       lend = rl_end;
 | |
| #endif
 | |
| 
 | |
|       if (end > lend)
 | |
| 	{
 | |
| 	  rl_point = lend;
 | |
| 	  rl_ding ();
 | |
| 	}
 | |
|       else
 | |
| 	rl_point = end;
 | |
|     }
 | |
| 
 | |
|   if (rl_end < 0)
 | |
|     rl_end = 0;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| _rl_forward_char_internal (count)
 | |
|      int count;
 | |
| {
 | |
|   int point;
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
 | |
| 
 | |
| #if defined (VI_MODE)
 | |
|   if (point >= rl_end && VI_COMMAND_MODE())
 | |
|     point = _rl_find_prev_mbchar (rl_line_buffer, rl_end, MB_FIND_NONZERO);
 | |
| #endif
 | |
| 
 | |
|     if (rl_end < 0)
 | |
| 	rl_end = 0;
 | |
| #else
 | |
|   point = rl_point + count;
 | |
|   if (point > rl_end)
 | |
|     point = rl_end;
 | |
| #endif
 | |
| 
 | |
|   return (point);
 | |
| }
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
| /* Move forward COUNT characters. */
 | |
| int
 | |
| rl_forward_char (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int point;
 | |
| 
 | |
|   if (MB_CUR_MAX == 1 || rl_byte_oriented)
 | |
|     return (rl_forward_byte (count, key));
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (rl_backward_char (-count, key));
 | |
| 
 | |
|   if (count > 0)
 | |
|     {
 | |
|       if (rl_point == rl_end && EMACS_MODE())
 | |
| 	{
 | |
| 	  rl_ding ();
 | |
| 	  return 0;
 | |
| 	}
 | |
| 
 | |
|       point = _rl_forward_char_internal (count);
 | |
| 
 | |
|       if (rl_point == point)
 | |
| 	rl_ding ();
 | |
| 
 | |
|       rl_point = point;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| #else /* !HANDLE_MULTIBYTE */
 | |
| int
 | |
| rl_forward_char (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (rl_forward_byte (count, key));
 | |
| }
 | |
| #endif /* !HANDLE_MULTIBYTE */
 | |
|   
 | |
| /* Backwards compatibility. */
 | |
| int
 | |
| rl_forward (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (rl_forward_char (count, key));
 | |
| }
 | |
| 
 | |
| /* Move backward COUNT bytes. */
 | |
| int
 | |
| rl_backward_byte (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (count < 0)
 | |
|     return (rl_forward_byte (-count, key));
 | |
| 
 | |
|   if (count > 0)
 | |
|     {
 | |
|       if (rl_point < count)
 | |
| 	{
 | |
| 	  rl_point = 0;
 | |
| 	  rl_ding ();
 | |
| 	}
 | |
|       else
 | |
| 	rl_point -= count;
 | |
|     }
 | |
| 
 | |
|   if (rl_point < 0)
 | |
|     rl_point = 0;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
| /* Move backward COUNT characters. */
 | |
| int
 | |
| rl_backward_char (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int point;
 | |
| 
 | |
|   if (MB_CUR_MAX == 1 || rl_byte_oriented)
 | |
|     return (rl_backward_byte (count, key));
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (rl_forward_char (-count, key));
 | |
| 
 | |
|   if (count > 0)
 | |
|     {
 | |
|       point = rl_point;
 | |
| 
 | |
|       while (count > 0 && point > 0)
 | |
| 	{
 | |
| 	  point = _rl_find_prev_mbchar (rl_line_buffer, point, MB_FIND_NONZERO);
 | |
| 	  count--;
 | |
| 	}
 | |
|       if (count > 0)
 | |
| 	{
 | |
| 	  rl_point = 0;
 | |
| 	  rl_ding ();
 | |
| 	}
 | |
|       else
 | |
|         rl_point = point;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| #else
 | |
| int
 | |
| rl_backward_char (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (rl_backward_byte (count, key));
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Backwards compatibility. */
 | |
| int
 | |
| rl_backward (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (rl_backward_char (count, key));
 | |
| }
 | |
| 
 | |
| /* Move to the beginning of the line. */
 | |
| int
 | |
| rl_beg_of_line (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   rl_point = 0;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Move to the end of the line. */
 | |
| int
 | |
| rl_end_of_line (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   rl_point = rl_end;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Move forward a word.  We do what Emacs does.  Handles multibyte chars. */
 | |
| int
 | |
| rl_forward_word (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int c;
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (rl_backward_word (-count, key));
 | |
| 
 | |
|   while (count)
 | |
|     {
 | |
|       if (rl_point == rl_end)
 | |
| 	return 0;
 | |
| 
 | |
|       /* If we are not in a word, move forward until we are in one.
 | |
| 	 Then, move forward until we hit a non-alphabetic character. */
 | |
|       c = _rl_char_value (rl_line_buffer, rl_point);
 | |
| 
 | |
|       if (_rl_walphabetic (c) == 0)
 | |
| 	{
 | |
| 	  rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
 | |
| 	  while (rl_point < rl_end)
 | |
| 	    {
 | |
| 	      c = _rl_char_value (rl_line_buffer, rl_point);
 | |
| 	      if (_rl_walphabetic (c))
 | |
| 		break;
 | |
| 	      rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       if (rl_point == rl_end)
 | |
| 	return 0;
 | |
| 
 | |
|       rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
 | |
|       while (rl_point < rl_end)
 | |
| 	{
 | |
| 	  c = _rl_char_value (rl_line_buffer, rl_point);
 | |
| 	  if (_rl_walphabetic (c) == 0)
 | |
| 	    break;
 | |
| 	  rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
 | |
| 	}
 | |
| 
 | |
|       --count;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Move backward a word.  We do what Emacs does.  Handles multibyte chars. */
 | |
| int
 | |
| rl_backward_word (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int c, p;
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (rl_forward_word (-count, key));
 | |
| 
 | |
|   while (count)
 | |
|     {
 | |
|       if (rl_point == 0)
 | |
| 	return 0;
 | |
| 
 | |
|       /* Like rl_forward_word (), except that we look at the characters
 | |
| 	 just before point. */
 | |
| 
 | |
|       p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
 | |
|       c = _rl_char_value (rl_line_buffer, p);
 | |
| 
 | |
|       if (_rl_walphabetic (c) == 0)
 | |
| 	{
 | |
| 	  rl_point = p;
 | |
| 	  while (rl_point > 0)
 | |
| 	    {
 | |
| 	      p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
 | |
| 	      c = _rl_char_value (rl_line_buffer, p);
 | |
| 	      if (_rl_walphabetic (c))
 | |
| 		break;
 | |
| 	      rl_point = p;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       while (rl_point)
 | |
| 	{
 | |
| 	  p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
 | |
| 	  c = _rl_char_value (rl_line_buffer, p);	  
 | |
| 	  if (_rl_walphabetic (c) == 0)
 | |
| 	    break;
 | |
| 	  else
 | |
| 	    rl_point = p;
 | |
| 	}
 | |
| 
 | |
|       --count;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Clear the current line.  Numeric argument to C-l does this. */
 | |
| int
 | |
| rl_refresh_line (ignore1, ignore2)
 | |
|      int ignore1, ignore2;
 | |
| {
 | |
|   int curr_line;
 | |
| 
 | |
|   curr_line = _rl_current_display_line ();
 | |
| 
 | |
|   _rl_move_vert (curr_line);
 | |
|   _rl_move_cursor_relative (0, rl_line_buffer);   /* XXX is this right */
 | |
| 
 | |
|   _rl_clear_to_eol (0);		/* arg of 0 means to not use spaces */
 | |
| 
 | |
|   rl_forced_update_display ();
 | |
|   rl_display_fixed = 1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* C-l typed to a line without quoting clears the screen, and then reprints
 | |
|    the prompt and the current input line.  Given a numeric arg, redraw only
 | |
|    the current line. */
 | |
| int
 | |
| rl_clear_screen (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (rl_explicit_arg)
 | |
|     {
 | |
|       rl_refresh_line (count, key);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   _rl_clear_screen ();		/* calls termcap function to clear screen */
 | |
|   rl_forced_update_display ();
 | |
|   rl_display_fixed = 1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| rl_skip_csi_sequence (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int ch;
 | |
| 
 | |
|   RL_SETSTATE (RL_STATE_MOREINPUT);
 | |
|   do
 | |
|     ch = rl_read_key ();
 | |
|   while (ch >= 0x20 && ch < 0x40);
 | |
|   RL_UNSETSTATE (RL_STATE_MOREINPUT);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| rl_arrow_keys (count, c)
 | |
|      int count, c;
 | |
| {
 | |
|   int ch;
 | |
| 
 | |
|   RL_SETSTATE(RL_STATE_MOREINPUT);
 | |
|   ch = rl_read_key ();
 | |
|   RL_UNSETSTATE(RL_STATE_MOREINPUT);
 | |
| 
 | |
|   switch (_rl_to_upper (ch))
 | |
|     {
 | |
|     case 'A':
 | |
|       rl_get_previous_history (count, ch);
 | |
|       break;
 | |
| 
 | |
|     case 'B':
 | |
|       rl_get_next_history (count, ch);
 | |
|       break;
 | |
| 
 | |
|     case 'C':
 | |
|       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
 | |
| 	rl_forward_char (count, ch);
 | |
|       else
 | |
| 	rl_forward_byte (count, ch);
 | |
|       break;
 | |
| 
 | |
|     case 'D':
 | |
|       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
 | |
| 	rl_backward_char (count, ch);
 | |
|       else
 | |
| 	rl_backward_byte (count, ch);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       rl_ding ();
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Text commands				    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| #ifdef HANDLE_MULTIBYTE
 | |
| static char pending_bytes[MB_LEN_MAX];
 | |
| static int pending_bytes_length = 0;
 | |
| static mbstate_t ps = {0};
 | |
| #endif
 | |
| 
 | |
| /* Insert the character C at the current location, moving point forward.
 | |
|    If C introduces a multibyte sequence, we read the whole sequence and
 | |
|    then insert the multibyte char into the line buffer. */
 | |
| int
 | |
| _rl_insert_char (count, c)
 | |
|      int count, c;
 | |
| {
 | |
|   register int i;
 | |
|   char *string;
 | |
| #ifdef HANDLE_MULTIBYTE
 | |
|   int string_size;
 | |
|   char incoming[MB_LEN_MAX + 1];
 | |
|   int incoming_length = 0;
 | |
|   mbstate_t ps_back;
 | |
|   static int stored_count = 0;
 | |
| #endif
 | |
| 
 | |
|   if (count <= 0)
 | |
|     return 0;
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   if (MB_CUR_MAX == 1 || rl_byte_oriented)
 | |
|     {
 | |
|       incoming[0] = c;
 | |
|       incoming[1] = '\0';
 | |
|       incoming_length = 1;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       wchar_t wc;
 | |
|       size_t ret;
 | |
| 
 | |
|       if (stored_count <= 0)
 | |
| 	stored_count = count;
 | |
|       else
 | |
| 	count = stored_count;
 | |
| 
 | |
|       ps_back = ps;
 | |
|       pending_bytes[pending_bytes_length++] = c;
 | |
|       ret = mbrtowc (&wc, pending_bytes, pending_bytes_length, &ps);
 | |
| 
 | |
|       if (ret == (size_t)-2)
 | |
| 	{
 | |
| 	  /* Bytes too short to compose character, try to wait for next byte.
 | |
| 	     Restore the state of the byte sequence, because in this case the
 | |
| 	     effect of mbstate is undefined. */
 | |
| 	  ps = ps_back;
 | |
| 	  return 1;
 | |
| 	}
 | |
|       else if (ret == (size_t)-1)
 | |
| 	{
 | |
| 	  /* Invalid byte sequence for the current locale.  Treat first byte
 | |
| 	     as a single character. */
 | |
| 	  incoming[0] = pending_bytes[0];
 | |
| 	  incoming[1] = '\0';
 | |
| 	  incoming_length = 1;
 | |
| 	  pending_bytes_length--;
 | |
| 	  memmove (pending_bytes, pending_bytes + 1, pending_bytes_length);
 | |
| 	  /* Clear the state of the byte sequence, because in this case the
 | |
| 	     effect of mbstate is undefined. */
 | |
| 	  memset (&ps, 0, sizeof (mbstate_t));
 | |
| 	}
 | |
|       else if (ret == (size_t)0)
 | |
| 	{
 | |
| 	  incoming[0] = '\0';
 | |
| 	  incoming_length = 0;
 | |
| 	  pending_bytes_length--;
 | |
| 	  /* Clear the state of the byte sequence, because in this case the
 | |
| 	     effect of mbstate is undefined. */
 | |
| 	  memset (&ps, 0, sizeof (mbstate_t));
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  /* We successfully read a single multibyte character. */
 | |
| 	  memcpy (incoming, pending_bytes, pending_bytes_length);
 | |
| 	  incoming[pending_bytes_length] = '\0';
 | |
| 	  incoming_length = pending_bytes_length;
 | |
| 	  pending_bytes_length = 0;
 | |
| 	}
 | |
|     }
 | |
| #endif /* HANDLE_MULTIBYTE */
 | |
| 	  
 | |
|   /* If we can optimize, then do it.  But don't let people crash
 | |
|      readline because of extra large arguments. */
 | |
|   if (count > 1 && count <= TEXT_COUNT_MAX)
 | |
|     {
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|       string_size = count * incoming_length;
 | |
|       string = (char *)xmalloc (1 + string_size);
 | |
| 
 | |
|       i = 0;
 | |
|       while (i < string_size)
 | |
| 	{
 | |
| 	  strncpy (string + i, incoming, incoming_length);
 | |
| 	  i += incoming_length;
 | |
| 	}
 | |
|       incoming_length = 0;
 | |
|       stored_count = 0;
 | |
| #else /* !HANDLE_MULTIBYTE */
 | |
|       string = (char *)xmalloc (1 + count);
 | |
| 
 | |
|       for (i = 0; i < count; i++)
 | |
| 	string[i] = c;
 | |
| #endif /* !HANDLE_MULTIBYTE */
 | |
| 
 | |
|       string[i] = '\0';
 | |
|       rl_insert_text (string);
 | |
|       xfree (string);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   if (count > TEXT_COUNT_MAX)
 | |
|     {
 | |
|       int decreaser;
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|       string_size = incoming_length * TEXT_COUNT_MAX;
 | |
|       string = (char *)xmalloc (1 + string_size);
 | |
| 
 | |
|       i = 0;
 | |
|       while (i < string_size)
 | |
| 	{
 | |
| 	  strncpy (string + i, incoming, incoming_length);
 | |
| 	  i += incoming_length;
 | |
| 	}
 | |
| 
 | |
|       while (count)
 | |
| 	{
 | |
| 	  decreaser = (count > TEXT_COUNT_MAX) ? TEXT_COUNT_MAX : count;
 | |
| 	  string[decreaser*incoming_length] = '\0';
 | |
| 	  rl_insert_text (string);
 | |
| 	  count -= decreaser;
 | |
| 	}
 | |
| 
 | |
|       xfree (string);
 | |
|       incoming_length = 0;
 | |
|       stored_count = 0;
 | |
| #else /* !HANDLE_MULTIBYTE */
 | |
|       char str[TEXT_COUNT_MAX+1];
 | |
| 
 | |
|       for (i = 0; i < TEXT_COUNT_MAX; i++)
 | |
| 	str[i] = c;
 | |
| 
 | |
|       while (count)
 | |
| 	{
 | |
| 	  decreaser = (count > TEXT_COUNT_MAX ? TEXT_COUNT_MAX : count);
 | |
| 	  str[decreaser] = '\0';
 | |
| 	  rl_insert_text (str);
 | |
| 	  count -= decreaser;
 | |
| 	}
 | |
| #endif /* !HANDLE_MULTIBYTE */
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   if (MB_CUR_MAX == 1 || rl_byte_oriented)
 | |
|     {
 | |
|       /* We are inserting a single character.
 | |
| 	 If there is pending input, then make a string of all of the
 | |
| 	 pending characters that are bound to rl_insert, and insert
 | |
| 	 them all.  Don't do this if we're current reading input from
 | |
| 	 a macro. */
 | |
|       if ((RL_ISSTATE (RL_STATE_MACROINPUT) == 0) && _rl_any_typein ())
 | |
| 	_rl_insert_typein (c);
 | |
|       else
 | |
| 	{
 | |
| 	  /* Inserting a single character. */
 | |
| 	  char str[2];
 | |
| 
 | |
| 	  str[1] = '\0';
 | |
| 	  str[0] = c;
 | |
| 	  rl_insert_text (str);
 | |
| 	}
 | |
|     }
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   else
 | |
|     {
 | |
|       rl_insert_text (incoming);
 | |
|       stored_count = 0;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Overwrite the character at point (or next COUNT characters) with C.
 | |
|    If C introduces a multibyte character sequence, read the entire sequence
 | |
|    before starting the overwrite loop. */
 | |
| int
 | |
| _rl_overwrite_char (count, c)
 | |
|      int count, c;
 | |
| {
 | |
|   int i;
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   char mbkey[MB_LEN_MAX];
 | |
|   int k;
 | |
| 
 | |
|   /* Read an entire multibyte character sequence to insert COUNT times. */
 | |
|   if (count > 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
 | |
|     k = _rl_read_mbstring (c, mbkey, MB_LEN_MAX);
 | |
| #endif
 | |
| 
 | |
|   rl_begin_undo_group ();
 | |
| 
 | |
|   for (i = 0; i < count; i++)
 | |
|     {
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
 | |
| 	rl_insert_text (mbkey);
 | |
|       else
 | |
| #endif
 | |
| 	_rl_insert_char (1, c);
 | |
| 
 | |
|       if (rl_point < rl_end)
 | |
| 	rl_delete (1, c);
 | |
|     }
 | |
| 
 | |
|   rl_end_undo_group ();
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| rl_insert (count, c)
 | |
|      int count, c;
 | |
| {
 | |
|   return (rl_insert_mode == RL_IM_INSERT ? _rl_insert_char (count, c)
 | |
|   					 : _rl_overwrite_char (count, c));
 | |
| }
 | |
| 
 | |
| /* Insert the next typed character verbatim. */
 | |
| static int
 | |
| _rl_insert_next (count)
 | |
|      int count;
 | |
| {
 | |
|   int c;
 | |
| 
 | |
|   RL_SETSTATE(RL_STATE_MOREINPUT);
 | |
|   c = rl_read_key ();
 | |
|   RL_UNSETSTATE(RL_STATE_MOREINPUT);
 | |
| 
 | |
|   if (c < 0)
 | |
|     return -1;
 | |
| 
 | |
| #if defined (HANDLE_SIGNALS)
 | |
|   if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
 | |
|     _rl_restore_tty_signals ();
 | |
| #endif
 | |
| 
 | |
|   return (_rl_insert_char (count, c));  
 | |
| }
 | |
| 
 | |
| #if defined (READLINE_CALLBACKS)
 | |
| static int
 | |
| _rl_insert_next_callback (data)
 | |
|      _rl_callback_generic_arg *data;
 | |
| {
 | |
|   int count;
 | |
| 
 | |
|   count = data->count;
 | |
| 
 | |
|   /* Deregister function, let rl_callback_read_char deallocate data */
 | |
|   _rl_callback_func = 0;
 | |
|   _rl_want_redisplay = 1;
 | |
|  
 | |
|   return _rl_insert_next (count);
 | |
| }
 | |
| #endif
 | |
|   
 | |
| int
 | |
| rl_quoted_insert (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   /* Let's see...should the callback interface futz with signal handling? */
 | |
| #if defined (HANDLE_SIGNALS)
 | |
|   if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
 | |
|     _rl_disable_tty_signals ();
 | |
| #endif
 | |
| 
 | |
| #if defined (READLINE_CALLBACKS)
 | |
|   if (RL_ISSTATE (RL_STATE_CALLBACK))
 | |
|     {
 | |
|       _rl_callback_data = _rl_callback_data_alloc (count);
 | |
|       _rl_callback_func = _rl_insert_next_callback;
 | |
|       return (0);
 | |
|     }
 | |
| #endif
 | |
|       
 | |
|   return _rl_insert_next (count);
 | |
| }
 | |
| 
 | |
| /* Insert a tab character. */
 | |
| int
 | |
| rl_tab_insert (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (_rl_insert_char (count, '\t'));
 | |
| }
 | |
| 
 | |
| /* What to do when a NEWLINE is pressed.  We accept the whole line.
 | |
|    KEY is the key that invoked this command.  I guess it could have
 | |
|    meaning in the future. */
 | |
| int
 | |
| rl_newline (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   rl_done = 1;
 | |
| 
 | |
|   if (_rl_history_preserve_point)
 | |
|     _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
 | |
| 
 | |
|   RL_SETSTATE(RL_STATE_DONE);
 | |
| 
 | |
| #if defined (VI_MODE)
 | |
|   if (rl_editing_mode == vi_mode)
 | |
|     {
 | |
|       _rl_vi_done_inserting ();
 | |
|       if (_rl_vi_textmod_command (_rl_vi_last_command) == 0)	/* XXX */
 | |
| 	_rl_vi_reset_last ();
 | |
|     }
 | |
| #endif /* VI_MODE */
 | |
| 
 | |
|   /* If we've been asked to erase empty lines, suppress the final update,
 | |
|      since _rl_update_final calls rl_crlf(). */
 | |
|   if (rl_erase_empty_line && rl_point == 0 && rl_end == 0)
 | |
|     return 0;
 | |
| 
 | |
|   if (_rl_echoing_p)
 | |
|     _rl_update_final ();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* What to do for some uppercase characters, like meta characters,
 | |
|    and some characters appearing in emacs_ctlx_keymap.  This function
 | |
|    is just a stub, you bind keys to it and the code in _rl_dispatch ()
 | |
|    is special cased. */
 | |
| int
 | |
| rl_do_lowercase_version (ignore1, ignore2)
 | |
|      int ignore1, ignore2;
 | |
| {
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* This is different from what vi does, so the code's not shared.  Emacs
 | |
|    rubout in overwrite mode has one oddity:  it replaces a control
 | |
|    character that's displayed as two characters (^X) with two spaces. */
 | |
| int
 | |
| _rl_overwrite_rubout (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int opoint;
 | |
|   int i, l;
 | |
| 
 | |
|   if (rl_point == 0)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   opoint = rl_point;
 | |
| 
 | |
|   /* L == number of spaces to insert */
 | |
|   for (i = l = 0; i < count; i++)
 | |
|     {
 | |
|       rl_backward_char (1, key);
 | |
|       l += rl_character_len (rl_line_buffer[rl_point], rl_point);	/* not exactly right */
 | |
|     }
 | |
| 
 | |
|   rl_begin_undo_group ();
 | |
| 
 | |
|   if (count > 1 || rl_explicit_arg)
 | |
|     rl_kill_text (opoint, rl_point);
 | |
|   else
 | |
|     rl_delete_text (opoint, rl_point);
 | |
| 
 | |
|   /* Emacs puts point at the beginning of the sequence of spaces. */
 | |
|   if (rl_point < rl_end)
 | |
|     {
 | |
|       opoint = rl_point;
 | |
|       _rl_insert_char (l, ' ');
 | |
|       rl_point = opoint;
 | |
|     }
 | |
| 
 | |
|   rl_end_undo_group ();
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
|   
 | |
| /* Rubout the character behind point. */
 | |
| int
 | |
| rl_rubout (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (count < 0)
 | |
|     return (rl_delete (-count, key));
 | |
| 
 | |
|   if (!rl_point)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (rl_insert_mode == RL_IM_OVERWRITE)
 | |
|     return (_rl_overwrite_rubout (count, key));
 | |
| 
 | |
|   return (_rl_rubout_char (count, key));
 | |
| }
 | |
| 
 | |
| int
 | |
| _rl_rubout_char (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int orig_point;
 | |
|   unsigned char c;
 | |
| 
 | |
|   /* Duplicated code because this is called from other parts of the library. */
 | |
|   if (count < 0)
 | |
|     return (rl_delete (-count, key));
 | |
| 
 | |
|   if (rl_point == 0)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   orig_point = rl_point;
 | |
|   if (count > 1 || rl_explicit_arg)
 | |
|     {
 | |
|       rl_backward_char (count, key);
 | |
|       rl_kill_text (orig_point, rl_point);
 | |
|     }
 | |
|   else if (MB_CUR_MAX == 1 || rl_byte_oriented)
 | |
|     {
 | |
|       c = rl_line_buffer[--rl_point];
 | |
|       rl_delete_text (rl_point, orig_point);
 | |
|       /* The erase-at-end-of-line hack is of questionable merit now. */
 | |
|       if (rl_point == rl_end && ISPRINT (c) && _rl_last_c_pos)
 | |
| 	{
 | |
| 	  int l;
 | |
| 	  l = rl_character_len (c, rl_point);
 | |
| 	  _rl_erase_at_end_of_line (l);
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
 | |
|       rl_delete_text (rl_point, orig_point);
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Delete the character under the cursor.  Given a numeric argument,
 | |
|    kill that many characters instead. */
 | |
| int
 | |
| rl_delete (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   int xpoint;
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (_rl_rubout_char (-count, key));
 | |
| 
 | |
|   if (rl_point == rl_end)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (count > 1 || rl_explicit_arg)
 | |
|     {
 | |
|       xpoint = rl_point;
 | |
|       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
 | |
| 	rl_forward_char (count, key);
 | |
|       else
 | |
| 	rl_forward_byte (count, key);
 | |
| 
 | |
|       rl_kill_text (xpoint, rl_point);
 | |
|       rl_point = xpoint;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       xpoint = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
 | |
|       rl_delete_text (rl_point, xpoint);
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Delete the character under the cursor, unless the insertion
 | |
|    point is at the end of the line, in which case the character
 | |
|    behind the cursor is deleted.  COUNT is obeyed and may be used
 | |
|    to delete forward or backward that many characters. */      
 | |
| int
 | |
| rl_rubout_or_delete (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (rl_end != 0 && rl_point == rl_end)
 | |
|     return (_rl_rubout_char (count, key));
 | |
|   else
 | |
|     return (rl_delete (count, key));
 | |
| }  
 | |
| 
 | |
| /* Delete all spaces and tabs around point. */
 | |
| int
 | |
| rl_delete_horizontal_space (count, ignore)
 | |
|      int count, ignore;
 | |
| {
 | |
|   int start;
 | |
| 
 | |
|   while (rl_point && whitespace (rl_line_buffer[rl_point - 1]))
 | |
|     rl_point--;
 | |
| 
 | |
|   start = rl_point;
 | |
| 
 | |
|   while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
 | |
|     rl_point++;
 | |
| 
 | |
|   if (start != rl_point)
 | |
|     {
 | |
|       rl_delete_text (start, rl_point);
 | |
|       rl_point = start;
 | |
|     }
 | |
| 
 | |
|   if (rl_point < 0)
 | |
|     rl_point = 0;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Like the tcsh editing function delete-char-or-list.  The eof character
 | |
|    is caught before this is invoked, so this really does the same thing as
 | |
|    delete-char-or-list-or-eof, as long as it's bound to the eof character. */
 | |
| int
 | |
| rl_delete_or_show_completions (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (rl_end != 0 && rl_point == rl_end)
 | |
|     return (rl_possible_completions (count, key));
 | |
|   else
 | |
|     return (rl_delete (count, key));
 | |
| }
 | |
| 
 | |
| #ifndef RL_COMMENT_BEGIN_DEFAULT
 | |
| #define RL_COMMENT_BEGIN_DEFAULT "#"
 | |
| #endif
 | |
| 
 | |
| /* Turn the current line into a comment in shell history.
 | |
|    A K*rn shell style function. */
 | |
| int
 | |
| rl_insert_comment (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   char *rl_comment_text;
 | |
|   int rl_comment_len;
 | |
| 
 | |
|   rl_beg_of_line (1, key);
 | |
|   rl_comment_text = _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT;
 | |
| 
 | |
|   if (rl_explicit_arg == 0)
 | |
|     rl_insert_text (rl_comment_text);
 | |
|   else
 | |
|     {
 | |
|       rl_comment_len = strlen (rl_comment_text);
 | |
|       if (STREQN (rl_comment_text, rl_line_buffer, rl_comment_len))
 | |
| 	rl_delete_text (rl_point, rl_point + rl_comment_len);
 | |
|       else
 | |
| 	rl_insert_text (rl_comment_text);
 | |
|     }
 | |
| 
 | |
|   (*rl_redisplay_function) ();
 | |
|   rl_newline (1, '\n');
 | |
| 
 | |
|   return (0);
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Changing Case				    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| /* The three kinds of things that we know how to do. */
 | |
| #define UpCase 1
 | |
| #define DownCase 2
 | |
| #define CapCase 3
 | |
| 
 | |
| /* Uppercase the word at point. */
 | |
| int
 | |
| rl_upcase_word (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (rl_change_case (count, UpCase));
 | |
| }
 | |
| 
 | |
| /* Lowercase the word at point. */
 | |
| int
 | |
| rl_downcase_word (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (rl_change_case (count, DownCase));
 | |
| }
 | |
| 
 | |
| /* Upcase the first letter, downcase the rest. */
 | |
| int
 | |
| rl_capitalize_word (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|  return (rl_change_case (count, CapCase));
 | |
| }
 | |
| 
 | |
| /* The meaty function.
 | |
|    Change the case of COUNT words, performing OP on them.
 | |
|    OP is one of UpCase, DownCase, or CapCase.
 | |
|    If a negative argument is given, leave point where it started,
 | |
|    otherwise, leave it where it moves to. */
 | |
| static int
 | |
| rl_change_case (count, op)
 | |
|      int count, op;
 | |
| {
 | |
|   int start, next, end;
 | |
|   int inword, c, nc, nop;
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   wchar_t wc, nwc;
 | |
|   char mb[MB_LEN_MAX+1];
 | |
|   int mlen;
 | |
|   size_t m;
 | |
|   mbstate_t mps;
 | |
| #endif
 | |
| 
 | |
|   start = rl_point;
 | |
|   rl_forward_word (count, 0);
 | |
|   end = rl_point;
 | |
| 
 | |
|   if (op != UpCase && op != DownCase && op != CapCase)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (count < 0)
 | |
|     SWAP (start, end);
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   memset (&mps, 0, sizeof (mbstate_t));
 | |
| #endif
 | |
| 
 | |
|   /* We are going to modify some text, so let's prepare to undo it. */
 | |
|   rl_modifying (start, end);
 | |
| 
 | |
|   inword = 0;
 | |
|   while (start < end)
 | |
|     {
 | |
|       c = _rl_char_value (rl_line_buffer, start);
 | |
|       /*  This assumes that the upper and lower case versions are the same width. */
 | |
|       next = MB_NEXTCHAR (rl_line_buffer, start, 1, MB_FIND_NONZERO);
 | |
| 
 | |
|       if (_rl_walphabetic (c) == 0)
 | |
| 	{
 | |
| 	  inword = 0;
 | |
| 	  start = next;
 | |
| 	  continue;
 | |
| 	}
 | |
| 
 | |
|       if (op == CapCase)
 | |
| 	{
 | |
| 	  nop = inword ? DownCase : UpCase;
 | |
| 	  inword = 1;
 | |
| 	}
 | |
|       else
 | |
| 	nop = op;
 | |
|       if (MB_CUR_MAX == 1 || rl_byte_oriented || isascii (c))
 | |
| 	{
 | |
| 	  nc = (nop == UpCase) ? _rl_to_upper (c) : _rl_to_lower (c);
 | |
| 	  rl_line_buffer[start] = nc;
 | |
| 	}
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|       else
 | |
| 	{
 | |
| 	  m = mbrtowc (&wc, rl_line_buffer + start, end - start, &mps);
 | |
| 	  if (MB_INVALIDCH (m))
 | |
| 	    wc = (wchar_t)rl_line_buffer[start];
 | |
| 	  else if (MB_NULLWCH (m))
 | |
| 	    wc = L'\0';
 | |
| 	  nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc);
 | |
| 	  if  (nwc != wc)	/*  just skip unchanged characters */
 | |
| 	    {
 | |
| 	      mlen = wcrtomb (mb, nwc, &mps);
 | |
| 	      if (mlen > 0)
 | |
| 		mb[mlen] = '\0';
 | |
| 	      /* Assume the same width */
 | |
| 	      strncpy (rl_line_buffer + start, mb, mlen);
 | |
| 	    }
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
|       start = next;
 | |
|     }
 | |
| 
 | |
|   rl_point = end;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Transposition				    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| /* Transpose the words at point.  If point is at the end of the line,
 | |
|    transpose the two words before point. */
 | |
| int
 | |
| rl_transpose_words (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   char *word1, *word2;
 | |
|   int w1_beg, w1_end, w2_beg, w2_end;
 | |
|   int orig_point = rl_point;
 | |
| 
 | |
|   if (!count)
 | |
|     return 0;
 | |
| 
 | |
|   /* Find the two words. */
 | |
|   rl_forward_word (count, key);
 | |
|   w2_end = rl_point;
 | |
|   rl_backward_word (1, key);
 | |
|   w2_beg = rl_point;
 | |
|   rl_backward_word (count, key);
 | |
|   w1_beg = rl_point;
 | |
|   rl_forward_word (1, key);
 | |
|   w1_end = rl_point;
 | |
| 
 | |
|   /* Do some check to make sure that there really are two words. */
 | |
|   if ((w1_beg == w2_beg) || (w2_beg < w1_end))
 | |
|     {
 | |
|       rl_ding ();
 | |
|       rl_point = orig_point;
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* Get the text of the words. */
 | |
|   word1 = rl_copy_text (w1_beg, w1_end);
 | |
|   word2 = rl_copy_text (w2_beg, w2_end);
 | |
| 
 | |
|   /* We are about to do many insertions and deletions.  Remember them
 | |
|      as one operation. */
 | |
|   rl_begin_undo_group ();
 | |
| 
 | |
|   /* Do the stuff at word2 first, so that we don't have to worry
 | |
|      about word1 moving. */
 | |
|   rl_point = w2_beg;
 | |
|   rl_delete_text (w2_beg, w2_end);
 | |
|   rl_insert_text (word1);
 | |
| 
 | |
|   rl_point = w1_beg;
 | |
|   rl_delete_text (w1_beg, w1_end);
 | |
|   rl_insert_text (word2);
 | |
| 
 | |
|   /* This is exactly correct since the text before this point has not
 | |
|      changed in length. */
 | |
|   rl_point = w2_end;
 | |
| 
 | |
|   /* I think that does it. */
 | |
|   rl_end_undo_group ();
 | |
|   xfree (word1);
 | |
|   xfree (word2);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Transpose the characters at point.  If point is at the end of the line,
 | |
|    then transpose the characters before point. */
 | |
| int
 | |
| rl_transpose_chars (count, key)
 | |
|      int count, key;
 | |
| {
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   char *dummy;
 | |
|   int i;
 | |
| #else
 | |
|   char dummy[2];
 | |
| #endif
 | |
|   int char_length, prev_point;
 | |
| 
 | |
|   if (count == 0)
 | |
|     return 0;
 | |
| 
 | |
|   if (!rl_point || rl_end < 2)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   rl_begin_undo_group ();
 | |
| 
 | |
|   if (rl_point == rl_end)
 | |
|     {
 | |
|       rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
 | |
|       count = 1;
 | |
|     }
 | |
| 
 | |
|   prev_point = rl_point;
 | |
|   rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   char_length = prev_point - rl_point;
 | |
|   dummy = (char *)xmalloc (char_length + 1);
 | |
|   for (i = 0; i < char_length; i++)
 | |
|     dummy[i] = rl_line_buffer[rl_point + i];
 | |
|   dummy[i] = '\0';
 | |
| #else
 | |
|   dummy[0] = rl_line_buffer[rl_point];
 | |
|   dummy[char_length = 1] = '\0';
 | |
| #endif
 | |
| 
 | |
|   rl_delete_text (rl_point, rl_point + char_length);
 | |
| 
 | |
|   rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
 | |
| 
 | |
|   _rl_fix_point (0);
 | |
|   rl_insert_text (dummy);
 | |
|   rl_end_undo_group ();
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   xfree (dummy);
 | |
| #endif
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*			Character Searching			    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| int
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
| _rl_char_search_internal (count, dir, smbchar, len)
 | |
|      int count, dir;
 | |
|      char *smbchar;
 | |
|      int len;
 | |
| #else
 | |
| _rl_char_search_internal (count, dir, schar)
 | |
|      int count, dir, schar;
 | |
| #endif
 | |
| {
 | |
|   int pos, inc;
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|   int prepos;
 | |
| #endif
 | |
| 
 | |
|   if (dir == 0)
 | |
|     return -1;
 | |
| 
 | |
|   pos = rl_point;
 | |
|   inc = (dir < 0) ? -1 : 1;
 | |
|   while (count)
 | |
|     {
 | |
|       if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= rl_end))
 | |
| 	{
 | |
| 	  rl_ding ();
 | |
| 	  return -1;
 | |
| 	}
 | |
| 
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|       pos = (inc > 0) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)
 | |
| 		      : _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
 | |
| #else
 | |
|       pos += inc;
 | |
| #endif
 | |
|       do
 | |
| 	{
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
| 	  if (_rl_is_mbchar_matched (rl_line_buffer, pos, rl_end, smbchar, len))
 | |
| #else
 | |
| 	  if (rl_line_buffer[pos] == schar)
 | |
| #endif
 | |
| 	    {
 | |
| 	      count--;
 | |
| 	      if (dir < 0)
 | |
| 	        rl_point = (dir == BTO) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)
 | |
| 					: pos;
 | |
| 	      else
 | |
| 		rl_point = (dir == FTO) ? _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)
 | |
| 					: pos;
 | |
| 	      break;
 | |
| 	    }
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
| 	  prepos = pos;
 | |
| #endif
 | |
| 	}
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
|       while ((dir < 0) ? (pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)) != prepos
 | |
| 		       : (pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)) != prepos);
 | |
| #else
 | |
|       while ((dir < 0) ? pos-- : ++pos < rl_end);
 | |
| #endif
 | |
|     }
 | |
|   return (0);
 | |
| }
 | |
| 
 | |
| /* Search COUNT times for a character read from the current input stream.
 | |
|    FDIR is the direction to search if COUNT is non-negative; otherwise
 | |
|    the search goes in BDIR.  So much is dependent on HANDLE_MULTIBYTE
 | |
|    that there are two separate versions of this function. */
 | |
| #if defined (HANDLE_MULTIBYTE)
 | |
| static int
 | |
| _rl_char_search (count, fdir, bdir)
 | |
|      int count, fdir, bdir;
 | |
| {
 | |
|   char mbchar[MB_LEN_MAX];
 | |
|   int mb_len;
 | |
| 
 | |
|   mb_len = _rl_read_mbchar (mbchar, MB_LEN_MAX);
 | |
| 
 | |
|   if (mb_len <= 0)
 | |
|     return -1;
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (_rl_char_search_internal (-count, bdir, mbchar, mb_len));
 | |
|   else
 | |
|     return (_rl_char_search_internal (count, fdir, mbchar, mb_len));
 | |
| }
 | |
| #else /* !HANDLE_MULTIBYTE */
 | |
| static int
 | |
| _rl_char_search (count, fdir, bdir)
 | |
|      int count, fdir, bdir;
 | |
| {
 | |
|   int c;
 | |
| 
 | |
|   RL_SETSTATE(RL_STATE_MOREINPUT);
 | |
|   c = rl_read_key ();
 | |
|   RL_UNSETSTATE(RL_STATE_MOREINPUT);
 | |
| 
 | |
|   if (c < 0)
 | |
|     return -1;
 | |
| 
 | |
|   if (count < 0)
 | |
|     return (_rl_char_search_internal (-count, bdir, c));
 | |
|   else
 | |
|     return (_rl_char_search_internal (count, fdir, c));
 | |
| }
 | |
| #endif /* !HANDLE_MULTIBYTE */
 | |
| 
 | |
| #if defined (READLINE_CALLBACKS)
 | |
| static int
 | |
| _rl_char_search_callback (data)
 | |
|      _rl_callback_generic_arg *data;
 | |
| {
 | |
|   _rl_callback_func = 0;
 | |
|   _rl_want_redisplay = 1;
 | |
| 
 | |
|   return (_rl_char_search (data->count, data->i1, data->i2));
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int
 | |
| rl_char_search (count, key)
 | |
|      int count, key;
 | |
| {
 | |
| #if defined (READLINE_CALLBACKS)
 | |
|   if (RL_ISSTATE (RL_STATE_CALLBACK))
 | |
|     {
 | |
|       _rl_callback_data = _rl_callback_data_alloc (count);
 | |
|       _rl_callback_data->i1 = FFIND;
 | |
|       _rl_callback_data->i2 = BFIND;
 | |
|       _rl_callback_func = _rl_char_search_callback;
 | |
|       return (0);
 | |
|     }
 | |
| #endif
 | |
|   
 | |
|   return (_rl_char_search (count, FFIND, BFIND));
 | |
| }
 | |
| 
 | |
| int
 | |
| rl_backward_char_search (count, key)
 | |
|      int count, key;
 | |
| {
 | |
| #if defined (READLINE_CALLBACKS)
 | |
|   if (RL_ISSTATE (RL_STATE_CALLBACK))
 | |
|     {
 | |
|       _rl_callback_data = _rl_callback_data_alloc (count);
 | |
|       _rl_callback_data->i1 = BFIND;
 | |
|       _rl_callback_data->i2 = FFIND;
 | |
|       _rl_callback_func = _rl_char_search_callback;
 | |
|       return (0);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   return (_rl_char_search (count, BFIND, FFIND));
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*		   The Mark and the Region.			    */
 | |
| /*								    */
 | |
| /* **************************************************************** */
 | |
| 
 | |
| /* Set the mark at POSITION. */
 | |
| int
 | |
| _rl_set_mark_at_pos (position)
 | |
|      int position;
 | |
| {
 | |
|   if (position > rl_end)
 | |
|     return -1;
 | |
| 
 | |
|   rl_mark = position;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* A bindable command to set the mark. */
 | |
| int
 | |
| rl_set_mark (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   return (_rl_set_mark_at_pos (rl_explicit_arg ? count : rl_point));
 | |
| }
 | |
| 
 | |
| /* Exchange the position of mark and point. */
 | |
| int
 | |
| rl_exchange_point_and_mark (count, key)
 | |
|      int count, key;
 | |
| {
 | |
|   if (rl_mark > rl_end)
 | |
|     rl_mark = -1;
 | |
| 
 | |
|   if (rl_mark == -1)
 | |
|     {
 | |
|       rl_ding ();
 | |
|       return -1;
 | |
|     }
 | |
|   else
 | |
|     SWAP (rl_point, rl_mark);
 | |
| 
 | |
|   return 0;
 | |
| }
 | 
