]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libreadline/undo.c
Fix what I think is an off-by-one in certain worst-case scenarios
[FreeBSD/FreeBSD.git] / contrib / libreadline / undo.c
1 /* readline.c -- a general facility for reading lines of input
2    with emacs style editing and completion. */
3
4 /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
5
6    This file is part of the GNU Readline Library, a library for
7    reading lines of text with interactive input and history editing.
8
9    The GNU Readline Library is free software; you can redistribute it
10    and/or modify it under the terms of the GNU General Public License
11    as published by the Free Software Foundation; either version 2, or
12    (at your option) any later version.
13
14    The GNU Readline Library is distributed in the hope that it will be
15    useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    The GNU General Public License is often shipped with GNU software, and
20    is generally kept in a file called COPYING or LICENSE.  If you do not
21    have a copy of the license, write to the Free Software Foundation,
22    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
23 #define READLINE_LIBRARY
24
25 #if defined (HAVE_CONFIG_H)
26 #  include <config.h>
27 #endif
28
29 #include <sys/types.h>
30
31 #if defined (HAVE_UNISTD_H)
32 #  include <unistd.h>           /* for _POSIX_VERSION */
33 #endif /* HAVE_UNISTD_H */
34
35 #if defined (HAVE_STDLIB_H)
36 #  include <stdlib.h>
37 #else
38 #  include "ansi_stdlib.h"
39 #endif /* HAVE_STDLIB_H */
40
41 #include <stdio.h>
42
43 /* System-specific feature definitions and include files. */
44 #include "rldefs.h"
45
46 /* Some standard library routines. */
47 #include "readline.h"
48 #include "history.h"
49
50 #include "rlprivate.h"
51
52 #define SWAP(s, e)  do { int t; t = s; s = e; e = t; } while (0)
53
54 /* Non-zero tells rl_delete_text and rl_insert_text to not add to
55    the undo list. */
56 int _rl_doing_an_undo = 0;
57
58 /* How many unclosed undo groups we currently have. */
59 int _rl_undo_group_level = 0;
60
61 /* The current undo list for THE_LINE. */
62 UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL;
63
64 /* **************************************************************** */
65 /*                                                                  */
66 /*                      Undo, and Undoing                           */
67 /*                                                                  */
68 /* **************************************************************** */
69
70 /* Remember how to undo something.  Concatenate some undos if that
71    seems right. */
72 void
73 rl_add_undo (what, start, end, text)
74      enum undo_code what;
75      int start, end;
76      char *text;
77 {
78   UNDO_LIST *temp = (UNDO_LIST *)xmalloc (sizeof (UNDO_LIST));
79   temp->what = what;
80   temp->start = start;
81   temp->end = end;
82   temp->text = text;
83   temp->next = rl_undo_list;
84   rl_undo_list = temp;
85 }
86
87 /* Free the existing undo list. */
88 void
89 rl_free_undo_list ()
90 {
91   while (rl_undo_list)
92     {
93       UNDO_LIST *release = rl_undo_list;
94       rl_undo_list = rl_undo_list->next;
95
96       if (release->what == UNDO_DELETE)
97         free (release->text);
98
99       free (release);
100     }
101   rl_undo_list = (UNDO_LIST *)NULL;
102 }
103
104 /* Undo the next thing in the list.  Return 0 if there
105    is nothing to undo, or non-zero if there was. */
106 int
107 rl_do_undo ()
108 {
109   UNDO_LIST *release;
110   int waiting_for_begin, start, end;
111
112 #define TRANS(i) ((i) == -1 ? rl_point : ((i) == -2 ? rl_end : (i)))
113
114   start = end = waiting_for_begin = 0;
115   do
116     {
117       if (!rl_undo_list)
118         return (0);
119
120       _rl_doing_an_undo = 1;
121       RL_SETSTATE(RL_STATE_UNDOING);
122
123       /* To better support vi-mode, a start or end value of -1 means
124          rl_point, and a value of -2 means rl_end. */
125       if (rl_undo_list->what == UNDO_DELETE || rl_undo_list->what == UNDO_INSERT)
126         {
127           start = TRANS (rl_undo_list->start);
128           end = TRANS (rl_undo_list->end);
129         }
130
131       switch (rl_undo_list->what)
132         {
133         /* Undoing deletes means inserting some text. */
134         case UNDO_DELETE:
135           rl_point = start;
136           rl_insert_text (rl_undo_list->text);
137           free (rl_undo_list->text);
138           break;
139
140         /* Undoing inserts means deleting some text. */
141         case UNDO_INSERT:
142           rl_delete_text (start, end);
143           rl_point = start;
144           break;
145
146         /* Undoing an END means undoing everything 'til we get to a BEGIN. */
147         case UNDO_END:
148           waiting_for_begin++;
149           break;
150
151         /* Undoing a BEGIN means that we are done with this group. */
152         case UNDO_BEGIN:
153           if (waiting_for_begin)
154             waiting_for_begin--;
155           else
156             rl_ding ();
157           break;
158         }
159
160       _rl_doing_an_undo = 0;
161       RL_UNSETSTATE(RL_STATE_UNDOING);
162
163       release = rl_undo_list;
164       rl_undo_list = rl_undo_list->next;
165       free (release);
166     }
167   while (waiting_for_begin);
168
169   return (1);
170 }
171 #undef TRANS
172
173 int
174 _rl_fix_last_undo_of_type (type, start, end)
175      int type, start, end;
176 {
177   UNDO_LIST *rl;
178
179   for (rl = rl_undo_list; rl; rl = rl->next)
180     {
181       if (rl->what == type)
182         {
183           rl->start = start;
184           rl->end = end;
185           return 0;
186         }
187     }
188   return 1;
189 }
190
191 /* Begin a group.  Subsequent undos are undone as an atomic operation. */
192 int
193 rl_begin_undo_group ()
194 {
195   rl_add_undo (UNDO_BEGIN, 0, 0, 0);
196   _rl_undo_group_level++;
197   return 0;
198 }
199
200 /* End an undo group started with rl_begin_undo_group (). */
201 int
202 rl_end_undo_group ()
203 {
204   rl_add_undo (UNDO_END, 0, 0, 0);
205   _rl_undo_group_level--;
206   return 0;
207 }
208
209 /* Save an undo entry for the text from START to END. */
210 int
211 rl_modifying (start, end)
212      int start, end;
213 {
214   if (start > end)
215     {
216       SWAP (start, end);
217     }
218
219   if (start != end)
220     {
221       char *temp = rl_copy_text (start, end);
222       rl_begin_undo_group ();
223       rl_add_undo (UNDO_DELETE, start, end, temp);
224       rl_add_undo (UNDO_INSERT, start, end, (char *)NULL);
225       rl_end_undo_group ();
226     }
227   return 0;
228 }
229
230 /* Revert the current line to its previous state. */
231 int
232 rl_revert_line (count, key)
233      int count, key;
234 {
235   if (!rl_undo_list)
236     rl_ding ();
237   else
238     {
239       while (rl_undo_list)
240         rl_do_undo ();
241     }
242   return 0;
243 }
244
245 /* Do some undoing of things that were done. */
246 int
247 rl_undo_command (count, key)
248      int count, key;
249 {
250   if (count < 0)
251     return 0;   /* Nothing to do. */
252
253   while (count)
254     {
255       if (rl_do_undo ())
256         count--;
257       else
258         {
259           rl_ding ();
260           break;
261         }
262     }
263   return 0;
264 }