1/* $NetBSD: emacs.c,v 1.5 2005/06/09 16:48:57 lukem Exp $ */ 2/* from NetBSD: emacs.c,v 1.19 2004/10/28 21:14:52 dsl 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 "tnftp.h" 37#include "sys.h" 38 39/* 40 * emacs.c: Emacs functions 41 */ 42#include "el.h" 43 44/* em_delete_or_list(): 45 * Delete character under cursor or list completions if at end of line 46 * [^D] 47 */ 48protected el_action_t 49/*ARGSUSED*/ 50em_delete_or_list(EditLine *el, int c __attribute__((__unused__))) 51{ 52 53 if (el->el_line.cursor == el->el_line.lastchar) { 54 /* if I'm at the end */ 55 if (el->el_line.cursor == el->el_line.buffer) { 56 /* and the beginning */ 57 term_overwrite(el, STReof, 4); /* then do a EOF */ 58 term__flush(); 59 return (CC_EOF); 60 } else { 61 /* 62 * Here we could list completions, but it is an 63 * error right now 64 */ 65 term_beep(el); 66 return (CC_ERROR); 67 } 68 } else { 69 if (el->el_state.doingarg) 70 c_delafter(el, el->el_state.argument); 71 else 72 c_delafter1(el); 73 if (el->el_line.cursor > el->el_line.lastchar) 74 el->el_line.cursor = el->el_line.lastchar; 75 /* bounds check */ 76 return (CC_REFRESH); 77 } 78} 79 80 81/* em_delete_next_word(): 82 * Cut from cursor to end of current word 83 * [M-d] 84 */ 85protected el_action_t 86/*ARGSUSED*/ 87em_delete_next_word(EditLine *el, int c __attribute__((__unused__))) 88{ 89 char *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, 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 */ 114protected el_action_t 115/*ARGSUSED*/ 116em_yank(EditLine *el, int c __attribute__((__unused__))) 117{ 118 char *kp, *cp; 119 120 if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) 121 return (CC_NORM); 122 123 if (el->el_line.lastchar + 124 (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >= 125 el->el_line.limit) 126 return (CC_ERROR); 127 128 el->el_chared.c_kill.mark = el->el_line.cursor; 129 cp = el->el_line.cursor; 130 131 /* open the space, */ 132 c_insert(el, 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 */ 149protected el_action_t 150/*ARGSUSED*/ 151em_kill_line(EditLine *el, int c __attribute__((__unused__))) 152{ 153 char *kp, *cp; 154 155 cp = el->el_line.buffer; 156 kp = el->el_chared.c_kill.buf; 157 while (cp < el->el_line.lastchar) 158 *kp++ = *cp++; /* copy it */ 159 el->el_chared.c_kill.last = kp; 160 /* zap! -- delete all of it */ 161 el->el_line.lastchar = el->el_line.buffer; 162 el->el_line.cursor = el->el_line.buffer; 163 return (CC_REFRESH); 164} 165 166 167/* em_kill_region(): 168 * Cut area between mark and cursor and save in cut buffer 169 * [^W] 170 */ 171protected el_action_t 172/*ARGSUSED*/ 173em_kill_region(EditLine *el, int c __attribute__((__unused__))) 174{ 175 char *kp, *cp; 176 177 if (!el->el_chared.c_kill.mark) 178 return (CC_ERROR); 179 180 if (el->el_chared.c_kill.mark > el->el_line.cursor) { 181 cp = el->el_line.cursor; 182 kp = el->el_chared.c_kill.buf; 183 while (cp < el->el_chared.c_kill.mark) 184 *kp++ = *cp++; /* copy it */ 185 el->el_chared.c_kill.last = kp; 186 c_delafter(el, cp - el->el_line.cursor); 187 } else { /* mark is before cursor */ 188 cp = el->el_chared.c_kill.mark; 189 kp = el->el_chared.c_kill.buf; 190 while (cp < el->el_line.cursor) 191 *kp++ = *cp++; /* copy it */ 192 el->el_chared.c_kill.last = kp; 193 c_delbefore(el, cp - el->el_chared.c_kill.mark); 194 el->el_line.cursor = el->el_chared.c_kill.mark; 195 } 196 return (CC_REFRESH); 197} 198 199 200/* em_copy_region(): 201 * Copy area between mark and cursor to cut buffer 202 * [M-W] 203 */ 204protected el_action_t 205/*ARGSUSED*/ 206em_copy_region(EditLine *el, int c __attribute__((__unused__))) 207{ 208 char *kp, *cp; 209 210 if (!el->el_chared.c_kill.mark) 211 return (CC_ERROR); 212 213 if (el->el_chared.c_kill.mark > el->el_line.cursor) { 214 cp = el->el_line.cursor; 215 kp = el->el_chared.c_kill.buf; 216 while (cp < el->el_chared.c_kill.mark) 217 *kp++ = *cp++; /* copy it */ 218 el->el_chared.c_kill.last = kp; 219 } else { 220 cp = el->el_chared.c_kill.mark; 221 kp = el->el_chared.c_kill.buf; 222 while (cp < el->el_line.cursor) 223 *kp++ = *cp++; /* copy it */ 224 el->el_chared.c_kill.last = kp; 225 } 226 return (CC_NORM); 227} 228 229 230/* em_gosmacs_transpose(): 231 * Exchange the two characters before the cursor 232 * Gosling emacs transpose chars [^T] 233 */ 234protected el_action_t 235em_gosmacs_transpose(EditLine *el, int c) 236{ 237 238 if (el->el_line.cursor > &el->el_line.buffer[1]) { 239 /* must have at least two chars entered */ 240 c = el->el_line.cursor[-2]; 241 el->el_line.cursor[-2] = el->el_line.cursor[-1]; 242 el->el_line.cursor[-1] = c; 243 return (CC_REFRESH); 244 } else 245 return (CC_ERROR); 246} 247 248 249/* em_next_word(): 250 * Move next to end of current word 251 * [M-f] 252 */ 253protected el_action_t 254/*ARGSUSED*/ 255em_next_word(EditLine *el, int c __attribute__((__unused__))) 256{ 257 if (el->el_line.cursor == el->el_line.lastchar) 258 return (CC_ERROR); 259 260 el->el_line.cursor = c__next_word(el->el_line.cursor, 261 el->el_line.lastchar, 262 el->el_state.argument, 263 ce__isword); 264 265 if (el->el_map.type == MAP_VI) 266 if (el->el_chared.c_vcmd.action != NOP) { 267 cv_delfini(el); 268 return (CC_REFRESH); 269 } 270 return (CC_CURSOR); 271} 272 273 274/* em_upper_case(): 275 * Uppercase the characters from cursor to end of current word 276 * [M-u] 277 */ 278protected el_action_t 279/*ARGSUSED*/ 280em_upper_case(EditLine *el, int c __attribute__((__unused__))) 281{ 282 char *cp, *ep; 283 284 ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, 285 el->el_state.argument, ce__isword); 286 287 for (cp = el->el_line.cursor; cp < ep; cp++) 288 if (islower((unsigned char)*cp)) 289 *cp = toupper((unsigned char)*cp); 290 291 el->el_line.cursor = ep; 292 if (el->el_line.cursor > el->el_line.lastchar) 293 el->el_line.cursor = el->el_line.lastchar; 294 return (CC_REFRESH); 295} 296 297 298/* em_capitol_case(): 299 * Capitalize the characters from cursor to end of current word 300 * [M-c] 301 */ 302protected el_action_t 303/*ARGSUSED*/ 304em_capitol_case(EditLine *el, int c __attribute__((__unused__))) 305{ 306 char *cp, *ep; 307 308 ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, 309 el->el_state.argument, ce__isword); 310 311 for (cp = el->el_line.cursor; cp < ep; cp++) { 312 if (isalpha((unsigned char)*cp)) { 313 if (islower((unsigned char)*cp)) 314 *cp = toupper((unsigned char)*cp); 315 cp++; 316 break; 317 } 318 } 319 for (; cp < ep; cp++) 320 if (isupper((unsigned char)*cp)) 321 *cp = tolower((unsigned char)*cp); 322 323 el->el_line.cursor = ep; 324 if (el->el_line.cursor > el->el_line.lastchar) 325 el->el_line.cursor = el->el_line.lastchar; 326 return (CC_REFRESH); 327} 328 329 330/* em_lower_case(): 331 * Lowercase the characters from cursor to end of current word 332 * [M-l] 333 */ 334protected el_action_t 335/*ARGSUSED*/ 336em_lower_case(EditLine *el, int c __attribute__((__unused__))) 337{ 338 char *cp, *ep; 339 340 ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, 341 el->el_state.argument, ce__isword); 342 343 for (cp = el->el_line.cursor; cp < ep; cp++) 344 if (isupper((unsigned char)*cp)) 345 *cp = tolower((unsigned char)*cp); 346 347 el->el_line.cursor = ep; 348 if (el->el_line.cursor > el->el_line.lastchar) 349 el->el_line.cursor = el->el_line.lastchar; 350 return (CC_REFRESH); 351} 352 353 354/* em_set_mark(): 355 * Set the mark at cursor 356 * [^@] 357 */ 358protected el_action_t 359/*ARGSUSED*/ 360em_set_mark(EditLine *el, int c __attribute__((__unused__))) 361{ 362 363 el->el_chared.c_kill.mark = el->el_line.cursor; 364 return (CC_NORM); 365} 366 367 368/* em_exchange_mark(): 369 * Exchange the cursor and mark 370 * [^X^X] 371 */ 372protected el_action_t 373/*ARGSUSED*/ 374em_exchange_mark(EditLine *el, int c __attribute__((__unused__))) 375{ 376 char *cp; 377 378 cp = el->el_line.cursor; 379 el->el_line.cursor = el->el_chared.c_kill.mark; 380 el->el_chared.c_kill.mark = cp; 381 return (CC_CURSOR); 382} 383 384 385/* em_universal_argument(): 386 * Universal argument (argument times 4) 387 * [^U] 388 */ 389protected el_action_t 390/*ARGSUSED*/ 391em_universal_argument(EditLine *el, int c __attribute__((__unused__))) 392{ /* multiply current argument by 4 */ 393 394 if (el->el_state.argument > 1000000) 395 return (CC_ERROR); 396 el->el_state.doingarg = 1; 397 el->el_state.argument *= 4; 398 return (CC_ARGHACK); 399} 400 401 402/* em_meta_next(): 403 * Add 8th bit to next character typed 404 * [<ESC>] 405 */ 406protected el_action_t 407/*ARGSUSED*/ 408em_meta_next(EditLine *el, int c __attribute__((__unused__))) 409{ 410 411 el->el_state.metanext = 1; 412 return (CC_ARGHACK); 413} 414 415 416/* em_toggle_overwrite(): 417 * Switch from insert to overwrite mode or vice versa 418 */ 419protected el_action_t 420/*ARGSUSED*/ 421em_toggle_overwrite(EditLine *el, int c __attribute__((__unused__))) 422{ 423 424 el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ? 425 MODE_REPLACE : MODE_INSERT; 426 return (CC_NORM); 427} 428 429 430/* em_copy_prev_word(): 431 * Copy current word to cursor 432 */ 433protected el_action_t 434/*ARGSUSED*/ 435em_copy_prev_word(EditLine *el, int c __attribute__((__unused__))) 436{ 437 char *cp, *oldc, *dp; 438 439 if (el->el_line.cursor == el->el_line.buffer) 440 return (CC_ERROR); 441 442 oldc = el->el_line.cursor; 443 /* does a bounds check */ 444 cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, 445 el->el_state.argument, ce__isword); 446 447 c_insert(el, oldc - cp); 448 for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++) 449 *dp++ = *cp; 450 451 el->el_line.cursor = dp;/* put cursor at end */ 452 453 return (CC_REFRESH); 454} 455 456 457/* em_inc_search_next(): 458 * Emacs incremental next search 459 */ 460protected el_action_t 461/*ARGSUSED*/ 462em_inc_search_next(EditLine *el, int c __attribute__((__unused__))) 463{ 464 465 el->el_search.patlen = 0; 466 return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY)); 467} 468 469 470/* em_inc_search_prev(): 471 * Emacs incremental reverse search 472 */ 473protected el_action_t 474/*ARGSUSED*/ 475em_inc_search_prev(EditLine *el, int c __attribute__((__unused__))) 476{ 477 478 el->el_search.patlen = 0; 479 return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY)); 480} 481 482 483/* em_delete_prev_char(): 484 * Delete the character to the left of the cursor 485 * [^?] 486 */ 487protected el_action_t 488/*ARGSUSED*/ 489em_delete_prev_char(EditLine *el, int c __attribute__((__unused__))) 490{ 491 492 if (el->el_line.cursor <= el->el_line.buffer) 493 return (CC_ERROR); 494 495 if (el->el_state.doingarg) 496 c_delbefore(el, el->el_state.argument); 497 else 498 c_delbefore1(el); 499 el->el_line.cursor -= el->el_state.argument; 500 if (el->el_line.cursor < el->el_line.buffer) 501 el->el_line.cursor = el->el_line.buffer; 502 return (CC_REFRESH); 503} 504