1 /* $OpenBSD: emacs.c,v 1.18 2023/03/08 04:43:05 guenther Exp $ */
2 /* $NetBSD: emacs.c,v 1.35 2016/04/18 17:01:19 christos Exp $ */
3
4 /*-
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include "config.h"
37
38 /*
39 * emacs.c: Emacs functions
40 */
41 #include <ctype.h>
42
43 #include "el.h"
44 #include "emacs.h"
45 #include "fcns.h"
46
47 /* em_delete_or_list():
48 * Delete character under cursor or list completions if at end of line
49 * [^D]
50 */
51 protected el_action_t
em_delete_or_list(EditLine * el,wint_t c)52 em_delete_or_list(EditLine *el, wint_t c)
53 {
54
55 if (el->el_line.cursor == el->el_line.lastchar) {
56 /* if I'm at the end */
57 if (el->el_line.cursor == el->el_line.buffer) {
58 /* and the beginning */
59 terminal_writec(el, c); /* then do an EOF */
60 return CC_EOF;
61 } else {
62 /*
63 * Here we could list completions, but it is an
64 * error right now
65 */
66 terminal_beep(el);
67 return CC_ERROR;
68 }
69 } else {
70 if (el->el_state.doingarg)
71 c_delafter(el, el->el_state.argument);
72 else
73 c_delafter1(el);
74 if (el->el_line.cursor > el->el_line.lastchar)
75 el->el_line.cursor = el->el_line.lastchar;
76 /* bounds check */
77 return CC_REFRESH;
78 }
79 }
80
81
82 /* em_delete_next_word():
83 * Cut from cursor to end of current word
84 * [M-d]
85 */
86 protected el_action_t
em_delete_next_word(EditLine * el,wint_t c)87 em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
88 {
89 wchar_t *cp, *p, *kp;
90
91 if (el->el_line.cursor == el->el_line.lastchar)
92 return CC_ERROR;
93
94 cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
95 el->el_state.argument, ce__isword);
96
97 for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
98 /* save the text */
99 *kp++ = *p;
100 el->el_chared.c_kill.last = kp;
101
102 c_delafter(el, (int)(cp - el->el_line.cursor)); /* delete after dot */
103 if (el->el_line.cursor > el->el_line.lastchar)
104 el->el_line.cursor = el->el_line.lastchar;
105 /* bounds check */
106 return CC_REFRESH;
107 }
108
109
110 /* em_yank():
111 * Paste cut buffer at cursor position
112 * [^Y]
113 */
114 protected el_action_t
em_yank(EditLine * el,wint_t c)115 em_yank(EditLine *el, wint_t c __attribute__((__unused__)))
116 {
117 wchar_t *kp, *cp;
118
119 if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf)
120 return CC_NORM;
121
122 if (el->el_line.lastchar +
123 (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
124 el->el_line.limit)
125 return CC_ERROR;
126
127 el->el_chared.c_kill.mark = el->el_line.cursor;
128 cp = el->el_line.cursor;
129
130 /* open the space, */
131 c_insert(el,
132 (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf));
133 /* copy the chars */
134 for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
135 *cp++ = *kp;
136
137 /* if an arg, cursor at beginning else cursor at end */
138 if (el->el_state.argument == 1)
139 el->el_line.cursor = cp;
140
141 return CC_REFRESH;
142 }
143
144
145 /* em_kill_line():
146 * Cut the entire line and save in cut buffer
147 * [^U]
148 */
149 protected el_action_t
em_kill_line(EditLine * el,wint_t c)150 em_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
151 {
152 wchar_t *kp, *cp;
153
154 cp = el->el_line.buffer;
155 kp = el->el_chared.c_kill.buf;
156 while (cp < el->el_line.lastchar)
157 *kp++ = *cp++; /* copy it */
158 el->el_chared.c_kill.last = kp;
159 /* zap! -- delete all of it */
160 el->el_line.lastchar = el->el_line.buffer;
161 el->el_line.cursor = el->el_line.buffer;
162 return CC_REFRESH;
163 }
164
165
166 /* em_kill_region():
167 * Cut area between mark and cursor and save in cut buffer
168 * [^W]
169 */
170 protected el_action_t
em_kill_region(EditLine * el,wint_t c)171 em_kill_region(EditLine *el, wint_t c __attribute__((__unused__)))
172 {
173 wchar_t *kp, *cp;
174
175 if (!el->el_chared.c_kill.mark)
176 return CC_ERROR;
177
178 if (el->el_chared.c_kill.mark > el->el_line.cursor) {
179 cp = el->el_line.cursor;
180 kp = el->el_chared.c_kill.buf;
181 while (cp < el->el_chared.c_kill.mark)
182 *kp++ = *cp++; /* copy it */
183 el->el_chared.c_kill.last = kp;
184 c_delafter(el, (int)(cp - el->el_line.cursor));
185 } else { /* mark is before cursor */
186 cp = el->el_chared.c_kill.mark;
187 kp = el->el_chared.c_kill.buf;
188 while (cp < el->el_line.cursor)
189 *kp++ = *cp++; /* copy it */
190 el->el_chared.c_kill.last = kp;
191 c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark));
192 el->el_line.cursor = el->el_chared.c_kill.mark;
193 }
194 return CC_REFRESH;
195 }
196
197
198 /* em_copy_region():
199 * Copy area between mark and cursor to cut buffer
200 * [M-W]
201 */
202 protected el_action_t
em_copy_region(EditLine * el,wint_t c)203 em_copy_region(EditLine *el, wint_t c __attribute__((__unused__)))
204 {
205 wchar_t *kp, *cp;
206
207 if (!el->el_chared.c_kill.mark)
208 return CC_ERROR;
209
210 if (el->el_chared.c_kill.mark > el->el_line.cursor) {
211 cp = el->el_line.cursor;
212 kp = el->el_chared.c_kill.buf;
213 while (cp < el->el_chared.c_kill.mark)
214 *kp++ = *cp++; /* copy it */
215 el->el_chared.c_kill.last = kp;
216 } else {
217 cp = el->el_chared.c_kill.mark;
218 kp = el->el_chared.c_kill.buf;
219 while (cp < el->el_line.cursor)
220 *kp++ = *cp++; /* copy it */
221 el->el_chared.c_kill.last = kp;
222 }
223 return CC_NORM;
224 }
225
226
227 /* em_gosmacs_transpose():
228 * Exchange the two characters before the cursor
229 * Gosling emacs transpose chars [^T]
230 */
231 protected el_action_t
em_gosmacs_transpose(EditLine * el,wint_t c)232 em_gosmacs_transpose(EditLine *el, wint_t c)
233 {
234
235 if (el->el_line.cursor > &el->el_line.buffer[1]) {
236 /* must have at least two chars entered */
237 c = el->el_line.cursor[-2];
238 el->el_line.cursor[-2] = el->el_line.cursor[-1];
239 el->el_line.cursor[-1] = c;
240 return CC_REFRESH;
241 } else
242 return CC_ERROR;
243 }
244
245
246 /* em_next_word():
247 * Move next to end of current word
248 * [M-f]
249 */
250 protected el_action_t
em_next_word(EditLine * el,wint_t c)251 em_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
252 {
253 if (el->el_line.cursor == el->el_line.lastchar)
254 return CC_ERROR;
255
256 el->el_line.cursor = c__next_word(el->el_line.cursor,
257 el->el_line.lastchar,
258 el->el_state.argument,
259 ce__isword);
260
261 if (el->el_map.type == MAP_VI)
262 if (el->el_chared.c_vcmd.action != NOP) {
263 cv_delfini(el);
264 return CC_REFRESH;
265 }
266 return CC_CURSOR;
267 }
268
269
270 /* em_upper_case():
271 * Uppercase the characters from cursor to end of current word
272 * [M-u]
273 */
274 protected el_action_t
em_upper_case(EditLine * el,wint_t c)275 em_upper_case(EditLine *el, wint_t c __attribute__((__unused__)))
276 {
277 wchar_t *cp, *ep;
278
279 ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
280 el->el_state.argument, ce__isword);
281
282 for (cp = el->el_line.cursor; cp < ep; cp++)
283 if (iswlower(*cp))
284 *cp = towupper(*cp);
285
286 el->el_line.cursor = ep;
287 if (el->el_line.cursor > el->el_line.lastchar)
288 el->el_line.cursor = el->el_line.lastchar;
289 return CC_REFRESH;
290 }
291
292
293 /* em_capitol_case():
294 * Capitalize the characters from cursor to end of current word
295 * [M-c]
296 */
297 protected el_action_t
em_capitol_case(EditLine * el,wint_t c)298 em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__)))
299 {
300 wchar_t *cp, *ep;
301
302 ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
303 el->el_state.argument, ce__isword);
304
305 for (cp = el->el_line.cursor; cp < ep; cp++) {
306 if (iswalpha(*cp)) {
307 if (iswlower(*cp))
308 *cp = towupper(*cp);
309 cp++;
310 break;
311 }
312 }
313 for (; cp < ep; cp++)
314 if (iswupper(*cp))
315 *cp = towlower(*cp);
316
317 el->el_line.cursor = ep;
318 if (el->el_line.cursor > el->el_line.lastchar)
319 el->el_line.cursor = el->el_line.lastchar;
320 return CC_REFRESH;
321 }
322
323
324 /* em_lower_case():
325 * Lowercase the characters from cursor to end of current word
326 * [M-l]
327 */
328 protected el_action_t
em_lower_case(EditLine * el,wint_t c)329 em_lower_case(EditLine *el, wint_t c __attribute__((__unused__)))
330 {
331 wchar_t *cp, *ep;
332
333 ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
334 el->el_state.argument, ce__isword);
335
336 for (cp = el->el_line.cursor; cp < ep; cp++)
337 if (iswupper(*cp))
338 *cp = towlower(*cp);
339
340 el->el_line.cursor = ep;
341 if (el->el_line.cursor > el->el_line.lastchar)
342 el->el_line.cursor = el->el_line.lastchar;
343 return CC_REFRESH;
344 }
345
346
347 /* em_set_mark():
348 * Set the mark at cursor
349 * [^@]
350 */
351 protected el_action_t
em_set_mark(EditLine * el,wint_t c)352 em_set_mark(EditLine *el, wint_t c __attribute__((__unused__)))
353 {
354
355 el->el_chared.c_kill.mark = el->el_line.cursor;
356 return CC_NORM;
357 }
358
359
360 /* em_exchange_mark():
361 * Exchange the cursor and mark
362 * [^X^X]
363 */
364 protected el_action_t
em_exchange_mark(EditLine * el,wint_t c)365 em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__)))
366 {
367 wchar_t *cp;
368
369 cp = el->el_line.cursor;
370 el->el_line.cursor = el->el_chared.c_kill.mark;
371 el->el_chared.c_kill.mark = cp;
372 return CC_CURSOR;
373 }
374
375
376 /* em_universal_argument():
377 * Universal argument (argument times 4)
378 * [^U]
379 */
380 protected el_action_t
em_universal_argument(EditLine * el,wint_t c)381 em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__)))
382 { /* multiply current argument by 4 */
383
384 if (el->el_state.argument > 1000000)
385 return CC_ERROR;
386 el->el_state.doingarg = 1;
387 el->el_state.argument *= 4;
388 return CC_ARGHACK;
389 }
390
391
392 /* em_meta_next():
393 * Add 8th bit to next character typed
394 * [<ESC>]
395 */
396 protected el_action_t
em_meta_next(EditLine * el,wint_t c)397 em_meta_next(EditLine *el, wint_t c __attribute__((__unused__)))
398 {
399
400 el->el_state.metanext = 1;
401 return CC_ARGHACK;
402 }
403
404
405 /* em_toggle_overwrite():
406 * Switch from insert to overwrite mode or vice versa
407 */
408 protected el_action_t
em_toggle_overwrite(EditLine * el,wint_t c)409 em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__)))
410 {
411
412 el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
413 MODE_REPLACE : MODE_INSERT;
414 return CC_NORM;
415 }
416
417
418 /* em_copy_prev_word():
419 * Copy current word to cursor
420 */
421 protected el_action_t
em_copy_prev_word(EditLine * el,wint_t c)422 em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
423 {
424 wchar_t *cp, *oldc, *dp;
425
426 if (el->el_line.cursor == el->el_line.buffer)
427 return CC_ERROR;
428
429 oldc = el->el_line.cursor;
430 /* does a bounds check */
431 cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
432 el->el_state.argument, ce__isword);
433
434 c_insert(el, (int)(oldc - cp));
435 for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
436 *dp++ = *cp;
437
438 el->el_line.cursor = dp;/* put cursor at end */
439
440 return CC_REFRESH;
441 }
442
443
444 /* em_inc_search_next():
445 * Emacs incremental next search
446 */
447 protected el_action_t
em_inc_search_next(EditLine * el,wint_t c)448 em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
449 {
450
451 el->el_search.patlen = 0;
452 return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY);
453 }
454
455
456 /* em_inc_search_prev():
457 * Emacs incremental reverse search
458 */
459 protected el_action_t
em_inc_search_prev(EditLine * el,wint_t c)460 em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
461 {
462
463 el->el_search.patlen = 0;
464 return ce_inc_search(el, ED_SEARCH_PREV_HISTORY);
465 }
466
467
468 /* em_delete_prev_char():
469 * Delete the character to the left of the cursor
470 * [^?]
471 */
472 protected el_action_t
em_delete_prev_char(EditLine * el,wint_t c)473 em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
474 {
475
476 if (el->el_line.cursor <= el->el_line.buffer)
477 return CC_ERROR;
478
479 if (el->el_state.doingarg)
480 c_delbefore(el, el->el_state.argument);
481 else
482 c_delbefore1(el);
483 el->el_line.cursor -= el->el_state.argument;
484 if (el->el_line.cursor < el->el_line.buffer)
485 el->el_line.cursor = el->el_line.buffer;
486 return CC_REFRESH;
487 }
488