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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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