This is zle_vi.c in view mode; [Download] [Up]
/*
* $Id: zle_vi.c,v 2.11 1996/10/15 20:16:35 hzoli Exp $
*
* zle_vi.c - vi-specific functions
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1996 Paul Falstad
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*
*/
#define ZLE
#include "zsh.h"
static int lastmult, lastbuf, lastgotmult, lastgotbuf, inrepeat, vichgrepeat;
static void startvichange _((int im));
static void
startvichange(int im)
{
if (im != -1) {
insmode = im;
vichgflag = 1;
}
if (inrepeat) {
zmult = lastmult;
vibufspec = lastbuf;
gotmult = lastgotmult;
gotvibufspec = lastgotbuf;
inrepeat = vichgflag = 0;
vichgrepeat = 1;
} else {
lastmult = zmult;
lastbuf = vibufspec;
lastgotmult = gotmult;
lastgotbuf = gotvibufspec;
if (vichgbuf)
free(vichgbuf);
vichgbuf = (char *)zalloc(vichgbufsz = 16);
vichgbuf[0] = c;
vichgbufptr = 1;
vichgrepeat = 0;
}
}
static void startvitext _((int im));
static void
startvitext(int im)
{
startvichange(im);
bindtab = mainbindtab;
undoing = 0;
viinsbegin = cs;
}
/**/
int
vigetkey(void)
{
int cmd;
if((c = getkey(0)) == EOF) {
feep();
return -1;
}
cmd = mainbindtab[c];
if(cmd == z_prefix) {
char buf[2];
Key ky;
buf[0] = c;
buf[1] = 0;
ky = (Key) keybindtab->getnode(keybindtab, buf);
if(!ky)
cmd = z_undefinedkey;
else
cmd = ky->func;
}
if (cmd < 0 || cmd == z_sendbreak) {
feep();
return -1;
} else if (cmd == z_quotedinsert) {
if ((c = getkey(0)) == EOF) {
feep();
return -1;
}
} else if(cmd == z_viquotedinsert) {
char sav = line[cs];
line[cs] = '^';
refresh();
c = getkey(0);
line[cs] = sav;
if(c == EOF) {
feep();
return -1;
}
} else if (cmd == z_vicmdmode)
return -1;
return c;
}
/**/
int
getvirange(int wf)
{
int k2, t0, startline, endline, obeep;
int mult1, gotmult1;
vilinerange = 0;
startline = findbol();
endline = findeol();
/* get arguments for the command, and then the command key */
mult1 = zmult;
gotmult1 = gotmult;
zmult = 1;
gotmult = 0;
lastcmd &= ~(ZLE_NEGARG | ZLE_DIGIT);
while(1) {
if ((k2 = getkeycmd()) < 0 || k2 == z_sendbreak) {
feep();
return -1;
}
if (!(zlecmds[k2].flags & ZLE_ARG))
break;
(*zlecmds[k2].func) ();
lastcmd = zlecmds[k2].flags;
}
/* double counts, such as in 3d4j, get multiplied, unless we're repeating */
if(vichgrepeat && gotmult1) {
zmult = mult1;
gotmult = 1;
} else if (gotmult1) {
zmult *= mult1;
gotmult = 1;
}
/* can't handle an empty file */
if (!ll) {
feep();
return -1;
}
/* This bit handles repeated command keys, such as dd. A number *
* of lines is taken as the range. The current line is included. *
* If the argument is positive, the lines go downward, otherwise *
* vice versa. The argument gives the number of lines. */
if (k2 == bindk) {
vilinerange = 1;
if (!zmult) {
feep();
return -1;
}
t0 = cs;
if (zmult > 0) {
while(zmult-- && cs <= ll)
cs = findeol() + 1;
if (zmult != -1) {
cs = t0;
feep();
return -1;
}
t0 = cs - 1;
cs = startline;
return t0;
} else {
while(zmult++ && cs >= 0)
cs = findbol() - 1;
if (zmult != 1) {
cs = t0;
feep();
return -1;
}
cs++;
return endline;
}
}
/* Not a movement?! No, you can't do yd. */
if (!(zlecmds[k2].flags & ZLE_MOVEMENT)) {
feep();
return -1;
}
/* Now we need to execute the movement command, to see where it *
* actually goes. virangeflag here indicates to the movement *
* function that it should place the cursor at the end of the *
* range, rather than where the cursor would actually go if it *
* were executed normally. This makes a difference to some *
* commands, but not all. For example, if searching forward *
* for a character, under normal circumstances the cursor lands *
* on the character. For a range, the range must include the *
* character, so the cursor gets placed after the character if *
* virangeflag is set. vi-match-bracket needs to change the *
* value of virangeflag under some circumstances, meaning that *
* we need to change the *starting* position. */
t0 = cs;
virangeflag = 1;
wordflag = wf;
obeep = opts[BEEP];
opts[BEEP] = 0;
(*zlecmds[k2].func) ();
wordflag = 0;
opts[BEEP] = obeep;
if (cs == t0) {
/* An error occured -- couldn't move. The movement command didn't *
* feep, because we set NO_BEEP for the duration of the command. */
feep();
virangeflag = 0;
return -1;
}
if(virangeflag == -1)
t0++;
virangeflag = 0;
/* get the range the right way round */
if (cs > t0) {
int tmp = cs;
cs = t0;
t0 = tmp;
}
/* Was it a line-oriented move? In this case, entire lines are taken. *
* The terminating newline is left out of the range, which the real *
* command must deal with appropriately. At this point we just need *
* to make the range encompass entire lines. */
if (zlecmds[k2].flags & ZLE_LINEMOVE) {
int newcs = findbol();
cs = t0;
t0 = findeol();
cs = newcs;
vilinerange = 1;
}
return t0;
}
/**/
void
viaddnext(void)
{
if (cs != findeol())
cs++;
startvitext(1);
}
/**/
void
viaddeol(void)
{
cs = findeol();
startvitext(1);
}
/**/
void
viinsert(void)
{
startvitext(1);
}
/**/
void
viinsertbol(void)
{
vifirstnonblank();
startvitext(1);
}
/**/
void
videlete(void)
{
int c2;
startvichange(1);
if ((c2 = getvirange(0)) != -1) {
forekill(c2 - cs, 0);
if (vilinerange && ll) {
if (cs == ll)
cs--;
foredel(1);
vifirstnonblank();
}
}
vichgflag = vilinerange = 0;
}
/**/
void
videletechar(void)
{
startvichange(-1);
/* handle negative argument */
if (zmult < 0) {
zmult = -zmult;
vibackwarddeletechar();
return;
}
/* it is an error to be on the end of line */
if (cs == ll || line[cs] == '\n') {
feep();
return;
}
/* Put argument into the acceptable range -- it is not an error to *
* specify a greater count than the number of available characters. */
if (zmult > findeol() - cs)
zmult = findeol() - cs;
/* do the deletion */
forekill(zmult, 0);
}
/**/
void
vichange(void)
{
int c2;
startvichange(1);
if ((c2 = getvirange(1)) != -1) {
forekill(c2 - cs, 0);
bindtab = mainbindtab;
viinsbegin = cs;
undoing = 0;
}
vilinerange = 0;
}
/**/
void
visubstitute(void)
{
startvichange(1);
if (zmult < 0) {
feep();
return;
}
/* it is an error to be on the end of line */
if (cs == ll || line[cs] == '\n') {
feep();
return;
}
/* Put argument into the acceptable range -- it is not an error to *
* specify a greater count than the number of available characters. */
if (zmult > findeol() - cs)
zmult = findeol() - cs;
/* do the substitution */
forekill(zmult, 0);
startvitext(1);
}
/**/
void
vichangeeol(void)
{
forekill(findeol() - cs, 0);
startvitext(1);
}
/**/
void
vichangewholeline(void)
{
vifirstnonblank();
vichangeeol();
}
/**/
void
viyank(void)
{
int oldcs = cs, c2;
startvichange(1);
if ((c2 = getvirange(0)) != -1)
cut(cs, c2 - cs, 0);
vichgflag = vilinerange = 0;
cs = oldcs;
}
/**/
void
viyankeol(void)
{
int x = findeol();
startvichange(-1);
if (x == cs) {
feep();
return;
}
cut(cs, x - cs, 0);
}
/**/
void
viyankwholeline(void)
{
int bol = findbol(), oldcs = cs;
startvichange(-1);
if (zmult < 1)
return;
while(zmult--) {
if (cs > ll) {
feep();
cs = oldcs;
return;
}
cs = findeol() + 1;
}
vilinerange = 1;
cut(bol, cs - bol - 1, 0);
cs = oldcs;
}
/**/
void
vireplace(void)
{
startvitext(0);
}
/* vi-replace-chars has some oddities relating to vi-repeat-change. In *
* the real vi, if one does 3r at the end of a line, it feeps without *
* reading the argument. A successful rx followed by 3. at the end of *
* a line (or 3rx followed by . at the end of a line) will obviously *
* feep after the ., even though it has the argument available. Here *
* repeating is tied very closely to argument reading, such that we *
* can't do that. The solution is to just read the argument even if *
* the command will fail -- not exactly vi compatible, but it is more *
* consistent (consider dd in an empty file in vi). */
/**/
void
vireplacechars(void)
{
int ch;
startvichange(1);
/* get key */
if((ch = vigetkey()) == -1) {
vichgflag = 0;
feep();
return;
}
/* check argument range */
if (zmult < 0 || zmult + cs > findeol()) {
vichgflag = 0;
feep();
return;
}
/* do change */
if (ch == '\r' || ch == '\n') {
/* <return> handled specially */
cs += zmult - 1;
backkill(zmult - 1, 0);
line[cs++] = '\n';
} else {
while (zmult--)
line[cs++] = ch;
cs--;
}
vichgflag = 0;
}
/**/
void
vicmdmode(void)
{
if (bindtab == altbindtab)
feep();
else {
bindtab = altbindtab;
undoing = 1;
vichgflag = 0;
if (cs != findbol())
cs--;
}
}
/**/
void
viopenlinebelow(void)
{
cs = findeol();
spaceinline(1);
line[cs++] = '\n';
startvitext(1);
}
/**/
void
viopenlineabove(void)
{
cs = findbol();
spaceinline(1);
line[cs] = '\n';
startvitext(1);
}
/**/
void
vioperswapcase(void)
{
int oldcs, c2;
/* get the range */
startvichange(1);
if ((c2 = getvirange(0)) != -1) {
oldcs = cs;
/* swap the case of all letters within range */
while (cs < c2) {
if (islower(line[cs]))
line[cs] = tuupper(line[cs]);
else if (isupper(line[cs]))
line[cs] = tulower(line[cs]);
cs++;
}
/* go back to the first line of the range */
cs = oldcs;
vifirstnonblank();
}
vichgflag = vilinerange = 0;
}
/**/
void
virepeatchange(void)
{
/* make sure we have a change to repeat */
if (!vichgbuf || vichgflag) {
feep();
return;
}
/* restore or update the saved count and buffer */
if (gotmult) {
lastmult = zmult;
lastgotmult = 1;
}
if (gotvibufspec) {
lastbuf = vibufspec;
lastgotbuf = 1;
}
/* repeat the command */
inrepeat = 1;
ungetkeys(vichgbuf, vichgbufptr);
}
/**/
void
viindent(void)
{
int oldcs = cs, c2;
/* get the range */
startvichange(1);
if ((c2 = getvirange(0)) == -1) {
vichgflag = vilinerange = 0;
return;
}
vichgflag = 0;
/* must be a line range */
if (!vilinerange) {
feep();
cs = oldcs;
return;
}
vilinerange = 0;
oldcs = cs;
/* add a tab to the beginning of each line within range */
while (cs < c2) {
spaceinline(1);
line[cs] = '\t';
cs = findeol() + 1;
}
/* go back to the first line of the range */
cs = oldcs;
vifirstnonblank();
}
/**/
void
viunindent(void)
{
int oldcs = cs, c2;
/* get the range */
startvichange(1);
if ((c2 = getvirange(0)) == -1) {
vichgflag = vilinerange = 0;
return;
}
vichgflag = 0;
/* must be a line range */
if (!vilinerange) {
feep();
cs = oldcs;
return;
}
vilinerange = 0;
oldcs = cs;
/* remove a tab from the beginning of each line within range */
while (cs < c2) {
if (line[cs] == '\t')
foredel(1);
cs = findeol() + 1;
}
/* go back to the first line of the range */
cs = oldcs;
vifirstnonblank();
}
/**/
void
vibackwarddeletechar(void)
{
if (bindtab == altbindtab)
startvichange(-1);
/* handle negative argument */
if (zmult < 0) {
zmult = -zmult;
videletechar();
return;
}
/* It is an error to be at the beginning of the line, or (in *
* insert mode) to delete past the beginning of insertion. */
if ((bindtab != altbindtab && cs - zmult < viinsbegin) || cs == findbol()) {
feep();
return;
}
/* Put argument into the acceptable range -- it is not an error to *
* specify a greater count than the number of available characters. */
if (zmult > cs - findbol())
zmult = cs - findbol();
/* do the deletion */
backkill(zmult, 1);
}
/**/
void
vikillline(void)
{
if (viinsbegin > cs) {
feep();
return;
}
backdel(cs - viinsbegin);
}
/**/
void
viputbefore(void)
{
Cutbuffer buf = &cutbuf;
startvichange(-1);
if (zmult < 0)
return;
if (gotvibufspec)
buf = &vibuf[vibufspec];
if (!buf->buf) {
feep();
return;
}
vilinerange = !!(buf->flags & CUTBUFFER_LINE);
if (vilinerange) {
cs = findbol();
spaceinline(buf->len + 1);
memcpy((char *)line + cs, buf->buf, buf->len);
line[cs + buf->len] = '\n';
vifirstnonblank();
} else {
while (zmult--) {
spaceinline(buf->len);
memcpy((char *)line + cs, buf->buf, buf->len);
cs += buf->len;
}
if (cs)
cs--;
}
}
/**/
void
viputafter(void)
{
Cutbuffer buf = &cutbuf;
startvichange(-1);
if (zmult < 0)
return;
if (gotvibufspec)
buf = &vibuf[vibufspec];
if (!buf->buf) {
feep();
return;
}
vilinerange = !!(buf->flags & CUTBUFFER_LINE);
if (vilinerange) {
cs = findeol();
spaceinline(buf->len + 1);
line[cs++] = '\n';
memcpy((char *)line + cs, buf->buf, buf->len);
vifirstnonblank();
} else {
if (cs != findeol())
cs++;
while (zmult--) {
spaceinline(buf->len);
memcpy((char *)line + cs, buf->buf, buf->len);
cs += buf->len;
}
if (cs)
cs--;
}
}
/**/
void
vijoin(void)
{
int x;
startvichange(-1);
if ((x = findeol()) == ll) {
feep();
return;
}
cs = x + 1;
for (x = 1; cs != ll && iblank(line[cs]); cs++, x++);
backdel(x);
if (cs && iblank(line[cs-1]))
cs--;
else {
spaceinline(1);
line[cs] = ' ';
}
}
/**/
void
viswapcase(void)
{
int eol;
startvichange(-1);
if (zmult < 1)
return;
eol = findeol();
while (cs < eol && zmult--) {
if (islower(line[cs]))
line[cs] = tuupper(line[cs]);
else if (isupper(line[cs]))
line[cs] = tulower(line[cs]);
cs++;
}
if (cs && cs == eol)
cs--;
}
/**/
void
vicapslockpanic(void)
{
feep();
statusline = "press a lowercase key to continue";
statusll = strlen(statusline);
refresh();
while (!islower(getkey(0)));
statusline = NULL;
}
/**/
void
visetbuffer(void)
{
int ch;
if (gotvibufspec ||
(((ch = getkey(0)) < '1' || ch > '9') &&
(ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) {
feep();
return;
}
if (ch >= 'A' && ch <= 'Z') /* needed in cut() */
vibufappend = 1;
else
vibufappend = 0;
vibufspec = tulower(ch) + (idigit(ch) ? -'1' + 26 : -'a');
gotvibufspec = 1;
}
/**/
void
vikilleol(void)
{
int n = findeol() - cs;
startvichange(-1);
if (!n) {
/* error -- line already empty */
feep();
return;
}
/* delete to end of line */
forekill(findeol() - cs, 0);
}
/**/
void
vipoundinsert(void)
{
int oldcs = cs;
startvichange(-1);
vifirstnonblank();
if(line[cs] != '#') {
spaceinline(1);
line[cs] = '#';
if(cs <= viinsbegin)
viinsbegin++;
cs = oldcs + (cs <= oldcs);
} else {
foredel(1);
if (cs < viinsbegin)
viinsbegin--;
cs = oldcs - (cs < oldcs);
}
}
/**/
void
viquotedinsert(void)
{
#ifndef HAS_TIO
struct sgttyb sob;
#endif
spaceinline(1);
line[cs] = '^';
refresh();
#ifndef HAS_TIO
sob = shttyinfo.sgttyb;
sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
ioctl(SHTTY, TIOCSETN, &sob);
#endif
c = getkey(0);
#ifndef HAS_TIO
setterm();
#endif
foredel(1);
if(c < 0)
feep();
else
selfinsert();
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.