diff options
Diffstat (limited to 'kill.c')
-rw-r--r-- | kill.c | 694 |
1 files changed, 694 insertions, 0 deletions
@@ -0,0 +1,694 @@ +/* kill.c -- kill ring management. */ + +/* Copyright (C) 1994 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library 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 2, or + (at your option) any later version. + + The GNU Readline Library 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. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> /* for _POSIX_VERSION */ +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <stdio.h> + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +/* **************************************************************** */ +/* */ +/* Killing Mechanism */ +/* */ +/* **************************************************************** */ + +/* What we assume for a max number of kills. */ +#define DEFAULT_MAX_KILLS 10 + +/* The real variable to look at to find out when to flush kills. */ +static int rl_max_kills = DEFAULT_MAX_KILLS; + +/* Where to store killed text. */ +static char **rl_kill_ring = (char **)NULL; + +/* Where we are in the kill ring. */ +static int rl_kill_index; + +/* How many slots we have in the kill ring. */ +static int rl_kill_ring_length; + +static int _rl_copy_to_kill_ring PARAMS((char *, int)); +static int region_kill_internal PARAMS((int)); +static int _rl_copy_word_as_kill PARAMS((int, int)); +static int rl_yank_nth_arg_internal PARAMS((int, int, int)); + +/* How to say that you only want to save a certain amount + of kill material. */ +int +rl_set_retained_kills (num) + int num; +{ + return 0; +} + +/* Add TEXT to the kill ring, allocating a new kill ring slot as necessary. + This uses TEXT directly, so the caller must not free it. If APPEND is + non-zero, and the last command was a kill, the text is appended to the + current kill ring slot, otherwise prepended. */ +static int +_rl_copy_to_kill_ring (text, append) + char *text; + int append; +{ + char *old, *new; + int slot; + + /* First, find the slot to work with. */ + if (_rl_last_command_was_kill == 0) + { + /* Get a new slot. */ + if (rl_kill_ring == 0) + { + /* If we don't have any defined, then make one. */ + rl_kill_ring = (char **) + xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *)); + rl_kill_ring[slot = 0] = (char *)NULL; + } + else + { + /* We have to add a new slot on the end, unless we have + exceeded the max limit for remembering kills. */ + slot = rl_kill_ring_length; + if (slot == rl_max_kills) + { + register int i; + free (rl_kill_ring[0]); + for (i = 0; i < slot; i++) + rl_kill_ring[i] = rl_kill_ring[i + 1]; + } + else + { + slot = rl_kill_ring_length += 1; + rl_kill_ring = (char **)xrealloc (rl_kill_ring, slot * sizeof (char *)); + } + rl_kill_ring[--slot] = (char *)NULL; + } + } + else + slot = rl_kill_ring_length - 1; + + /* If the last command was a kill, prepend or append. */ + if (_rl_last_command_was_kill && rl_editing_mode != vi_mode) + { + old = rl_kill_ring[slot]; + new = (char *)xmalloc (1 + strlen (old) + strlen (text)); + + if (append) + { + strcpy (new, old); + strcat (new, text); + } + else + { + strcpy (new, text); + strcat (new, old); + } + free (old); + free (text); + rl_kill_ring[slot] = new; + } + else + rl_kill_ring[slot] = text; + + rl_kill_index = slot; + return 0; +} + +/* The way to kill something. This appends or prepends to the last + kill, if the last command was a kill command. if FROM is less + than TO, then the text is appended, otherwise prepended. If the + last command was not a kill command, then a new slot is made for + this kill. */ +int +rl_kill_text (from, to) + int from, to; +{ + char *text; + + /* Is there anything to kill? */ + if (from == to) + { + _rl_last_command_was_kill++; + return 0; + } + + text = rl_copy_text (from, to); + + /* Delete the copied text from the line. */ + rl_delete_text (from, to); + + _rl_copy_to_kill_ring (text, from < to); + + _rl_last_command_was_kill++; + return 0; +} + +/* Now REMEMBER! In order to do prepending or appending correctly, kill + commands always make rl_point's original position be the FROM argument, + and rl_point's extent be the TO argument. */ + +/* **************************************************************** */ +/* */ +/* Killing Commands */ +/* */ +/* **************************************************************** */ + +/* Delete the word at point, saving the text in the kill ring. */ +int +rl_kill_word (count, key) + int count, key; +{ + int orig_point; + + if (count < 0) + return (rl_backward_kill_word (-count, key)); + else + { + orig_point = rl_point; + rl_forward_word (count, key); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + + rl_point = orig_point; + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; +} + +/* Rubout the word before point, placing it on the kill ring. */ +int +rl_backward_kill_word (count, ignore) + int count, ignore; +{ + int orig_point; + + if (count < 0) + return (rl_kill_word (-count, ignore)); + else + { + orig_point = rl_point; + rl_backward_word (count, ignore); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; +} + +/* Kill from here to the end of the line. If DIRECTION is negative, kill + back to the line start instead. */ +int +rl_kill_line (direction, ignore) + int direction, ignore; +{ + int orig_point; + + if (direction < 0) + return (rl_backward_kill_line (1, ignore)); + else + { + orig_point = rl_point; + rl_end_of_line (1, ignore); + if (orig_point != rl_point) + rl_kill_text (orig_point, rl_point); + rl_point = orig_point; + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; +} + +/* Kill backwards to the start of the line. If DIRECTION is negative, kill + forwards to the line end instead. */ +int +rl_backward_kill_line (direction, ignore) + int direction, ignore; +{ + int orig_point; + + if (direction < 0) + return (rl_kill_line (1, ignore)); + else + { + if (!rl_point) + rl_ding (); + else + { + orig_point = rl_point; + rl_beg_of_line (1, ignore); + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + } + return 0; +} + +/* Kill the whole line, no matter where point is. */ +int +rl_kill_full_line (count, ignore) + int count, ignore; +{ + rl_begin_undo_group (); + rl_point = 0; + rl_kill_text (rl_point, rl_end); + rl_mark = 0; + rl_end_undo_group (); + return 0; +} + +/* The next two functions mimic unix line editing behaviour, except they + save the deleted text on the kill ring. This is safer than not saving + it, and since we have a ring, nobody should get screwed. */ + +/* This does what C-w does in Unix. We can't prevent people from + using behaviour that they expect. */ +int +rl_unix_word_rubout (count, key) + int count, key; +{ + int orig_point; + + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + if (count <= 0) + count = 1; + + while (count--) + { + while (rl_point && whitespace (rl_line_buffer[rl_point - 1])) + rl_point--; + + while (rl_point && (whitespace (rl_line_buffer[rl_point - 1]) == 0)) + rl_point--; + } + + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + + return 0; +} + +/* This deletes one filename component in a Unix pathname. That is, it + deletes backward to directory separator (`/') or whitespace. */ +int +rl_unix_filename_rubout (count, key) + int count, key; +{ + int orig_point, c; + + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + if (count <= 0) + count = 1; + + while (count--) + { + c = rl_line_buffer[rl_point - 1]; + while (rl_point && (whitespace (c) || c == '/')) + { + rl_point--; + c = rl_line_buffer[rl_point - 1]; + } + + while (rl_point && (whitespace (c) == 0) && c != '/') + { + rl_point--; + c = rl_line_buffer[rl_point - 1]; + } + } + + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + + return 0; +} + +/* Here is C-u doing what Unix does. You don't *have* to use these + key-bindings. We have a choice of killing the entire line, or + killing from where we are to the start of the line. We choose the + latter, because if you are a Unix weenie, then you haven't backspaced + into the line at all, and if you aren't, then you know what you are + doing. */ +int +rl_unix_line_discard (count, key) + int count, key; +{ + if (rl_point == 0) + rl_ding (); + else + { + rl_kill_text (rl_point, 0); + rl_point = 0; + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; +} + +/* Copy the text in the `region' to the kill ring. If DELETE is non-zero, + delete the text from the line as well. */ +static int +region_kill_internal (delete) + int delete; +{ + char *text; + + if (rl_mark != rl_point) + { + text = rl_copy_text (rl_point, rl_mark); + if (delete) + rl_delete_text (rl_point, rl_mark); + _rl_copy_to_kill_ring (text, rl_point < rl_mark); + } + + _rl_last_command_was_kill++; + return 0; +} + +/* Copy the text in the region to the kill ring. */ +int +rl_copy_region_to_kill (count, ignore) + int count, ignore; +{ + return (region_kill_internal (0)); +} + +/* Kill the text between the point and mark. */ +int +rl_kill_region (count, ignore) + int count, ignore; +{ + int r, npoint; + + npoint = (rl_point < rl_mark) ? rl_point : rl_mark; + r = region_kill_internal (1); + _rl_fix_point (1); + rl_point = npoint; + return r; +} + +/* Copy COUNT words to the kill ring. DIR says which direction we look + to find the words. */ +static int +_rl_copy_word_as_kill (count, dir) + int count, dir; +{ + int om, op, r; + + om = rl_mark; + op = rl_point; + + if (dir > 0) + rl_forward_word (count, 0); + else + rl_backward_word (count, 0); + + rl_mark = rl_point; + + if (dir > 0) + rl_backward_word (count, 0); + else + rl_forward_word (count, 0); + + r = region_kill_internal (0); + + rl_mark = om; + rl_point = op; + + return r; +} + +int +rl_copy_forward_word (count, key) + int count, key; +{ + if (count < 0) + return (rl_copy_backward_word (-count, key)); + + return (_rl_copy_word_as_kill (count, 1)); +} + +int +rl_copy_backward_word (count, key) + int count, key; +{ + if (count < 0) + return (rl_copy_forward_word (-count, key)); + + return (_rl_copy_word_as_kill (count, -1)); +} + +/* Yank back the last killed text. This ignores arguments. */ +int +rl_yank (count, ignore) + int count, ignore; +{ + if (rl_kill_ring == 0) + { + _rl_abort_internal (); + return -1; + } + + _rl_set_mark_at_pos (rl_point); + rl_insert_text (rl_kill_ring[rl_kill_index]); + return 0; +} + +/* If the last command was yank, or yank_pop, and the text just + before point is identical to the current kill item, then + delete that text from the line, rotate the index down, and + yank back some other text. */ +int +rl_yank_pop (count, key) + int count, key; +{ + int l, n; + + if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) || + !rl_kill_ring) + { + _rl_abort_internal (); + return -1; + } + + l = strlen (rl_kill_ring[rl_kill_index]); + n = rl_point - l; + if (n >= 0 && STREQN (rl_line_buffer + n, rl_kill_ring[rl_kill_index], l)) + { + rl_delete_text (n, rl_point); + rl_point = n; + rl_kill_index--; + if (rl_kill_index < 0) + rl_kill_index = rl_kill_ring_length - 1; + rl_yank (1, 0); + return 0; + } + else + { + _rl_abort_internal (); + return -1; + } +} + +/* Yank the COUNTh argument from the previous history line, skipping + HISTORY_SKIP lines before looking for the `previous line'. */ +static int +rl_yank_nth_arg_internal (count, ignore, history_skip) + int count, ignore, history_skip; +{ + register HIST_ENTRY *entry; + char *arg; + int i, pos; + + pos = where_history (); + + if (history_skip) + { + for (i = 0; i < history_skip; i++) + entry = previous_history (); + } + + entry = previous_history (); + + history_set_pos (pos); + + if (entry == 0) + { + rl_ding (); + return -1; + } + + arg = history_arg_extract (count, count, entry->line); + if (!arg || !*arg) + { + rl_ding (); + FREE (arg); + return -1; + } + + rl_begin_undo_group (); + + _rl_set_mark_at_pos (rl_point); + +#if defined (VI_MODE) + /* Vi mode always inserts a space before yanking the argument, and it + inserts it right *after* rl_point. */ + if (rl_editing_mode == vi_mode) + { + rl_vi_append_mode (1, ignore); + rl_insert_text (" "); + } +#endif /* VI_MODE */ + + rl_insert_text (arg); + free (arg); + + rl_end_undo_group (); + return 0; +} + +/* Yank the COUNTth argument from the previous history line. */ +int +rl_yank_nth_arg (count, ignore) + int count, ignore; +{ + return (rl_yank_nth_arg_internal (count, ignore, 0)); +} + +/* Yank the last argument from the previous history line. This `knows' + how rl_yank_nth_arg treats a count of `$'. With an argument, this + behaves the same as rl_yank_nth_arg. */ +int +rl_yank_last_arg (count, key) + int count, key; +{ + static int history_skip = 0; + static int explicit_arg_p = 0; + static int count_passed = 1; + static int direction = 1; + static int undo_needed = 0; + int retval; + + if (rl_last_func != rl_yank_last_arg) + { + history_skip = 0; + explicit_arg_p = rl_explicit_arg; + count_passed = count; + direction = 1; + } + else + { + if (undo_needed) + rl_do_undo (); + if (count < 1) + direction = -direction; + history_skip += direction; + if (history_skip < 0) + history_skip = 0; + } + + if (explicit_arg_p) + retval = rl_yank_nth_arg_internal (count_passed, key, history_skip); + else + retval = rl_yank_nth_arg_internal ('$', key, history_skip); + + undo_needed = retval == 0; + return retval; +} + +/* A special paste command for users of Cygnus's cygwin32. */ +#if defined (__CYGWIN__) +#include <windows.h> + +int +rl_paste_from_clipboard (count, key) + int count, key; +{ + char *data, *ptr; + int len; + + if (OpenClipboard (NULL) == 0) + return (0); + + data = (char *)GetClipboardData (CF_TEXT); + if (data) + { + ptr = strchr (data, '\r'); + if (ptr) + { + len = ptr - data; + ptr = (char *)xmalloc (len + 1); + ptr[len] = '\0'; + strncpy (ptr, data, len); + } + else + ptr = data; + _rl_set_mark_at_pos (rl_point); + rl_insert_text (ptr); + if (ptr != data) + free (ptr); + CloseClipboard (); + } + return (0); +} +#endif /* __CYGWIN__ */ |