080b4a0c0f
Compared to current version in base: - great improvements on the Unicode support - full support for filename completion including quoting which means we do not need anymore our custom addition) - Improved readline compatiblity Upgrading libedit has been a pain in the past, because somehow we never managed to properly cleanup the tree in lib/libedit and each merge has always been very painful. After years of fighting give up and refresh a merge from scrarch properly in contrib. Note that the switch to this version will be done in another commit.
513 lines
12 KiB
C
513 lines
12 KiB
C
/* $NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Christos Zoulas of Cornell University.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#if !defined(lint) && !defined(SCCSID)
|
|
#if 0
|
|
static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93";
|
|
#else
|
|
__RCSID("$NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $");
|
|
#endif
|
|
#endif /* not lint && not SCCSID */
|
|
|
|
/*
|
|
* emacs.c: Emacs functions
|
|
*/
|
|
#include <ctype.h>
|
|
|
|
#include "el.h"
|
|
#include "emacs.h"
|
|
#include "fcns.h"
|
|
|
|
/* em_delete_or_list():
|
|
* Delete character under cursor or list completions if at end of line
|
|
* [^D]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_delete_or_list(EditLine *el, wint_t c)
|
|
{
|
|
|
|
if (el->el_line.cursor == el->el_line.lastchar) {
|
|
/* if I'm at the end */
|
|
if (el->el_line.cursor == el->el_line.buffer) {
|
|
/* and the beginning */
|
|
terminal_writec(el, c); /* then do an EOF */
|
|
return CC_EOF;
|
|
} else {
|
|
/*
|
|
* Here we could list completions, but it is an
|
|
* error right now
|
|
*/
|
|
terminal_beep(el);
|
|
return CC_ERROR;
|
|
}
|
|
} else {
|
|
if (el->el_state.doingarg)
|
|
c_delafter(el, el->el_state.argument);
|
|
else
|
|
c_delafter1(el);
|
|
if (el->el_line.cursor > el->el_line.lastchar)
|
|
el->el_line.cursor = el->el_line.lastchar;
|
|
/* bounds check */
|
|
return CC_REFRESH;
|
|
}
|
|
}
|
|
|
|
|
|
/* em_delete_next_word():
|
|
* Cut from cursor to end of current word
|
|
* [M-d]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *cp, *p, *kp;
|
|
|
|
if (el->el_line.cursor == el->el_line.lastchar)
|
|
return CC_ERROR;
|
|
|
|
cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
|
|
el->el_state.argument, ce__isword);
|
|
|
|
for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
|
|
/* save the text */
|
|
*kp++ = *p;
|
|
el->el_chared.c_kill.last = kp;
|
|
|
|
c_delafter(el, (int)(cp - el->el_line.cursor)); /* delete after dot */
|
|
if (el->el_line.cursor > el->el_line.lastchar)
|
|
el->el_line.cursor = el->el_line.lastchar;
|
|
/* bounds check */
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_yank():
|
|
* Paste cut buffer at cursor position
|
|
* [^Y]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_yank(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *kp, *cp;
|
|
|
|
if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf)
|
|
return CC_NORM;
|
|
|
|
if (el->el_line.lastchar +
|
|
(el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
|
|
el->el_line.limit)
|
|
return CC_ERROR;
|
|
|
|
el->el_chared.c_kill.mark = el->el_line.cursor;
|
|
cp = el->el_line.cursor;
|
|
|
|
/* open the space, */
|
|
c_insert(el,
|
|
(int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf));
|
|
/* copy the chars */
|
|
for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
|
|
*cp++ = *kp;
|
|
|
|
/* if an arg, cursor at beginning else cursor at end */
|
|
if (el->el_state.argument == 1)
|
|
el->el_line.cursor = cp;
|
|
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_kill_line():
|
|
* Cut the entire line and save in cut buffer
|
|
* [^U]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *kp, *cp;
|
|
|
|
cp = el->el_line.buffer;
|
|
kp = el->el_chared.c_kill.buf;
|
|
while (cp < el->el_line.lastchar)
|
|
*kp++ = *cp++; /* copy it */
|
|
el->el_chared.c_kill.last = kp;
|
|
/* zap! -- delete all of it */
|
|
el->el_line.lastchar = el->el_line.buffer;
|
|
el->el_line.cursor = el->el_line.buffer;
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_kill_region():
|
|
* Cut area between mark and cursor and save in cut buffer
|
|
* [^W]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_kill_region(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *kp, *cp;
|
|
|
|
if (!el->el_chared.c_kill.mark)
|
|
return CC_ERROR;
|
|
|
|
if (el->el_chared.c_kill.mark > el->el_line.cursor) {
|
|
cp = el->el_line.cursor;
|
|
kp = el->el_chared.c_kill.buf;
|
|
while (cp < el->el_chared.c_kill.mark)
|
|
*kp++ = *cp++; /* copy it */
|
|
el->el_chared.c_kill.last = kp;
|
|
c_delafter(el, (int)(cp - el->el_line.cursor));
|
|
} else { /* mark is before cursor */
|
|
cp = el->el_chared.c_kill.mark;
|
|
kp = el->el_chared.c_kill.buf;
|
|
while (cp < el->el_line.cursor)
|
|
*kp++ = *cp++; /* copy it */
|
|
el->el_chared.c_kill.last = kp;
|
|
c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark));
|
|
el->el_line.cursor = el->el_chared.c_kill.mark;
|
|
}
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_copy_region():
|
|
* Copy area between mark and cursor to cut buffer
|
|
* [M-W]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_copy_region(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *kp, *cp;
|
|
|
|
if (!el->el_chared.c_kill.mark)
|
|
return CC_ERROR;
|
|
|
|
if (el->el_chared.c_kill.mark > el->el_line.cursor) {
|
|
cp = el->el_line.cursor;
|
|
kp = el->el_chared.c_kill.buf;
|
|
while (cp < el->el_chared.c_kill.mark)
|
|
*kp++ = *cp++; /* copy it */
|
|
el->el_chared.c_kill.last = kp;
|
|
} else {
|
|
cp = el->el_chared.c_kill.mark;
|
|
kp = el->el_chared.c_kill.buf;
|
|
while (cp < el->el_line.cursor)
|
|
*kp++ = *cp++; /* copy it */
|
|
el->el_chared.c_kill.last = kp;
|
|
}
|
|
return CC_NORM;
|
|
}
|
|
|
|
|
|
/* em_gosmacs_transpose():
|
|
* Exchange the two characters before the cursor
|
|
* Gosling emacs transpose chars [^T]
|
|
*/
|
|
libedit_private el_action_t
|
|
em_gosmacs_transpose(EditLine *el, wint_t c)
|
|
{
|
|
|
|
if (el->el_line.cursor > &el->el_line.buffer[1]) {
|
|
/* must have at least two chars entered */
|
|
c = el->el_line.cursor[-2];
|
|
el->el_line.cursor[-2] = el->el_line.cursor[-1];
|
|
el->el_line.cursor[-1] = c;
|
|
return CC_REFRESH;
|
|
} else
|
|
return CC_ERROR;
|
|
}
|
|
|
|
|
|
/* em_next_word():
|
|
* Move next to end of current word
|
|
* [M-f]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
if (el->el_line.cursor == el->el_line.lastchar)
|
|
return CC_ERROR;
|
|
|
|
el->el_line.cursor = c__next_word(el->el_line.cursor,
|
|
el->el_line.lastchar,
|
|
el->el_state.argument,
|
|
ce__isword);
|
|
|
|
if (el->el_map.type == MAP_VI)
|
|
if (el->el_chared.c_vcmd.action != NOP) {
|
|
cv_delfini(el);
|
|
return CC_REFRESH;
|
|
}
|
|
return CC_CURSOR;
|
|
}
|
|
|
|
|
|
/* em_upper_case():
|
|
* Uppercase the characters from cursor to end of current word
|
|
* [M-u]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_upper_case(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *cp, *ep;
|
|
|
|
ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
|
|
el->el_state.argument, ce__isword);
|
|
|
|
for (cp = el->el_line.cursor; cp < ep; cp++)
|
|
if (iswlower(*cp))
|
|
*cp = towupper(*cp);
|
|
|
|
el->el_line.cursor = ep;
|
|
if (el->el_line.cursor > el->el_line.lastchar)
|
|
el->el_line.cursor = el->el_line.lastchar;
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_capitol_case():
|
|
* Capitalize the characters from cursor to end of current word
|
|
* [M-c]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *cp, *ep;
|
|
|
|
ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
|
|
el->el_state.argument, ce__isword);
|
|
|
|
for (cp = el->el_line.cursor; cp < ep; cp++) {
|
|
if (iswalpha(*cp)) {
|
|
if (iswlower(*cp))
|
|
*cp = towupper(*cp);
|
|
cp++;
|
|
break;
|
|
}
|
|
}
|
|
for (; cp < ep; cp++)
|
|
if (iswupper(*cp))
|
|
*cp = towlower(*cp);
|
|
|
|
el->el_line.cursor = ep;
|
|
if (el->el_line.cursor > el->el_line.lastchar)
|
|
el->el_line.cursor = el->el_line.lastchar;
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_lower_case():
|
|
* Lowercase the characters from cursor to end of current word
|
|
* [M-l]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_lower_case(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *cp, *ep;
|
|
|
|
ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
|
|
el->el_state.argument, ce__isword);
|
|
|
|
for (cp = el->el_line.cursor; cp < ep; cp++)
|
|
if (iswupper(*cp))
|
|
*cp = towlower(*cp);
|
|
|
|
el->el_line.cursor = ep;
|
|
if (el->el_line.cursor > el->el_line.lastchar)
|
|
el->el_line.cursor = el->el_line.lastchar;
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_set_mark():
|
|
* Set the mark at cursor
|
|
* [^@]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_set_mark(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
|
|
el->el_chared.c_kill.mark = el->el_line.cursor;
|
|
return CC_NORM;
|
|
}
|
|
|
|
|
|
/* em_exchange_mark():
|
|
* Exchange the cursor and mark
|
|
* [^X^X]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *cp;
|
|
|
|
cp = el->el_line.cursor;
|
|
el->el_line.cursor = el->el_chared.c_kill.mark;
|
|
el->el_chared.c_kill.mark = cp;
|
|
return CC_CURSOR;
|
|
}
|
|
|
|
|
|
/* em_universal_argument():
|
|
* Universal argument (argument times 4)
|
|
* [^U]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{ /* multiply current argument by 4 */
|
|
|
|
if (el->el_state.argument > 1000000)
|
|
return CC_ERROR;
|
|
el->el_state.doingarg = 1;
|
|
el->el_state.argument *= 4;
|
|
return CC_ARGHACK;
|
|
}
|
|
|
|
|
|
/* em_meta_next():
|
|
* Add 8th bit to next character typed
|
|
* [<ESC>]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_meta_next(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
|
|
el->el_state.metanext = 1;
|
|
return CC_ARGHACK;
|
|
}
|
|
|
|
|
|
/* em_toggle_overwrite():
|
|
* Switch from insert to overwrite mode or vice versa
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
|
|
el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
|
|
MODE_REPLACE : MODE_INSERT;
|
|
return CC_NORM;
|
|
}
|
|
|
|
|
|
/* em_copy_prev_word():
|
|
* Copy current word to cursor
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
wchar_t *cp, *oldc, *dp;
|
|
|
|
if (el->el_line.cursor == el->el_line.buffer)
|
|
return CC_ERROR;
|
|
|
|
oldc = el->el_line.cursor;
|
|
/* does a bounds check */
|
|
cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
|
|
el->el_state.argument, ce__isword);
|
|
|
|
c_insert(el, (int)(oldc - cp));
|
|
for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
|
|
*dp++ = *cp;
|
|
|
|
el->el_line.cursor = dp;/* put cursor at end */
|
|
|
|
return CC_REFRESH;
|
|
}
|
|
|
|
|
|
/* em_inc_search_next():
|
|
* Emacs incremental next search
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
|
|
el->el_search.patlen = 0;
|
|
return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY);
|
|
}
|
|
|
|
|
|
/* em_inc_search_prev():
|
|
* Emacs incremental reverse search
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
|
|
el->el_search.patlen = 0;
|
|
return ce_inc_search(el, ED_SEARCH_PREV_HISTORY);
|
|
}
|
|
|
|
|
|
/* em_delete_prev_char():
|
|
* Delete the character to the left of the cursor
|
|
* [^?]
|
|
*/
|
|
libedit_private el_action_t
|
|
/*ARGSUSED*/
|
|
em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
|
|
{
|
|
|
|
if (el->el_line.cursor <= el->el_line.buffer)
|
|
return CC_ERROR;
|
|
|
|
if (el->el_state.doingarg)
|
|
c_delbefore(el, el->el_state.argument);
|
|
else
|
|
c_delbefore1(el);
|
|
el->el_line.cursor -= el->el_state.argument;
|
|
if (el->el_line.cursor < el->el_line.buffer)
|
|
el->el_line.cursor = el->el_line.buffer;
|
|
return CC_REFRESH;
|
|
}
|