mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-11-04 07:21:06 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			925 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			925 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * zle_vi.c - vi-specific functions
 | 
						|
 *
 | 
						|
 * This file is part of zsh, the Z shell.
 | 
						|
 *
 | 
						|
 * Copyright (c) 1992-1997 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.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include "zle.mdh"
 | 
						|
#include "zle_vi.pro"
 | 
						|
 | 
						|
/* != 0 if we're getting a vi range */
 | 
						|
 | 
						|
/**/
 | 
						|
int virangeflag;
 | 
						|
 | 
						|
/* kludge to get cw and dw to work right */
 | 
						|
 | 
						|
/**/
 | 
						|
int wordflag;
 | 
						|
 | 
						|
/* != 0 if we're killing lines into a buffer, vi-style */
 | 
						|
 | 
						|
/**/
 | 
						|
int vilinerange;
 | 
						|
 | 
						|
/* last vi change buffer, for vi change repetition */
 | 
						|
 | 
						|
/**/
 | 
						|
int vichgbufsz, vichgbufptr, vichgflag;
 | 
						|
 | 
						|
/**/
 | 
						|
char *vichgbuf;
 | 
						|
 | 
						|
/* point where vi insert mode was last entered */
 | 
						|
 | 
						|
/**/
 | 
						|
int viinsbegin;
 | 
						|
 | 
						|
static struct modifier lastmod;
 | 
						|
static int inrepeat, vichgrepeat;
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
startvichange(int im)
 | 
						|
{
 | 
						|
    if (im != -1) {
 | 
						|
	insmode = im;
 | 
						|
	vichgflag = 1;
 | 
						|
    }
 | 
						|
    if (inrepeat) {
 | 
						|
	zmod = lastmod;
 | 
						|
	inrepeat = vichgflag = 0;
 | 
						|
	vichgrepeat = 1;
 | 
						|
    } else {
 | 
						|
	lastmod = zmod;
 | 
						|
	if (vichgbuf)
 | 
						|
	    free(vichgbuf);
 | 
						|
	vichgbuf = (char *)zalloc(vichgbufsz = 16);
 | 
						|
	vichgbuf[0] = c;
 | 
						|
	vichgbufptr = 1;
 | 
						|
	vichgrepeat = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
startvitext(int im)
 | 
						|
{
 | 
						|
    startvichange(im);
 | 
						|
    selectkeymap("main", 1);
 | 
						|
    undoing = 0;
 | 
						|
    viinsbegin = cs;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vigetkey(void)
 | 
						|
{
 | 
						|
    Keymap mn = openkeymap("main");
 | 
						|
    char m[3], *str;
 | 
						|
    Thingy cmd;
 | 
						|
 | 
						|
    if((c = getkey(0)) == EOF)
 | 
						|
	return -1;
 | 
						|
 | 
						|
    m[0] = c;
 | 
						|
    metafy(m, 1, META_NOALLOC);
 | 
						|
    if(mn)
 | 
						|
	cmd = keybind(mn, m, &str);
 | 
						|
    else
 | 
						|
	cmd = t_undefinedkey;
 | 
						|
 | 
						|
    if (!cmd || cmd == Th(z_sendbreak)) {
 | 
						|
	return -1;
 | 
						|
    } else if (cmd == Th(z_quotedinsert)) {
 | 
						|
	if ((c = getkey(0)) == EOF)
 | 
						|
	    return -1;
 | 
						|
    } else if(cmd == Th(z_viquotedinsert)) {
 | 
						|
	char sav = line[cs];
 | 
						|
 | 
						|
	line[cs] = '^';
 | 
						|
	zrefresh();
 | 
						|
	c = getkey(0);
 | 
						|
	line[cs] = sav;
 | 
						|
	if(c == EOF)
 | 
						|
	    return -1;
 | 
						|
    } else if (cmd == Th(z_vicmdmode))
 | 
						|
	return -1;
 | 
						|
    return c;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
getvirange(int wf)
 | 
						|
{
 | 
						|
    int pos = cs, ret = 0;
 | 
						|
    int mult1 = zmult, hist1 = histline;
 | 
						|
    Thingy k2;
 | 
						|
 | 
						|
    virangeflag = 1;
 | 
						|
    wordflag = wf;
 | 
						|
    /* 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.                   */
 | 
						|
    zmod.flags &= ~MOD_TMULT;
 | 
						|
    do {
 | 
						|
	vilinerange = 0;
 | 
						|
	prefixflag = 0;
 | 
						|
	if (!(k2 = getkeycmd()) || (k2->flags & DISABLED) ||
 | 
						|
		k2 == Th(z_sendbreak)) {
 | 
						|
	    wordflag = 0;
 | 
						|
	    virangeflag = 0;
 | 
						|
	    return -1;
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * With k2 == bindk, the command key is repeated:
 | 
						|
	 * a number of lines is used.  If the function used
 | 
						|
	 * returns 1, we fail.
 | 
						|
	 */
 | 
						|
	if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs))
 | 
						|
	    ret = -1;
 | 
						|
	if(vichgrepeat)
 | 
						|
	    zmult = mult1;
 | 
						|
	else
 | 
						|
	    zmult = mult1 * zmod.tmult;
 | 
						|
    } while(prefixflag && !ret);
 | 
						|
    wordflag = 0;
 | 
						|
    virangeflag = 0;
 | 
						|
 | 
						|
    /* It is an error to use a non-movement command to delimit the *
 | 
						|
     * range.  We here reject the case where the command modified  *
 | 
						|
     * the line, or selected a different history line.             */
 | 
						|
    if (histline != hist1 || ll != lastll || memcmp(line, lastline, ll)) {
 | 
						|
	histline = hist1;
 | 
						|
	memcpy(line, lastline, ll = lastll);
 | 
						|
	cs = pos;
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Can't handle an empty file.  Also, if the movement command *
 | 
						|
     * failed, or didn't move, it is an error.                    */
 | 
						|
    if (!ll || (cs == pos && virangeflag != 2) || ret == -1)
 | 
						|
	return -1;
 | 
						|
 | 
						|
    /* vi-match-bracket changes the value of virangeflag when *
 | 
						|
     * moving to the opening bracket, meaning that we need to *
 | 
						|
     * change the *starting* position.                        */
 | 
						|
    if(virangeflag == -1)
 | 
						|
	pos++;
 | 
						|
 | 
						|
    /* Get the range the right way round.  cs is placed at the *
 | 
						|
     * start of the range, and pos (the return value of this   *
 | 
						|
     * function) is the end.                                   */
 | 
						|
    if (cs > pos) {
 | 
						|
	int tmp = cs;
 | 
						|
	cs = pos;
 | 
						|
	pos = tmp;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Was it a line-oriented move?  If so, the command will have set *
 | 
						|
     * the vilinerange flag.  In this case, entire lines are taken,   *
 | 
						|
     * rather than just the sequence of characters delimited by pos   *
 | 
						|
     * and cs.  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(vilinerange) {
 | 
						|
	int newcs = findbol();
 | 
						|
	cs = pos;
 | 
						|
	pos = findeol();
 | 
						|
	cs = newcs;
 | 
						|
    }
 | 
						|
    return pos;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
dovilinerange(void)
 | 
						|
{
 | 
						|
    int pos = cs, n = zmult;
 | 
						|
 | 
						|
    /* A number of lines is taken as the range.  The current line *
 | 
						|
     * is included.  If the repeat count is positive the lines go *
 | 
						|
     * downward, otherwise upward.  The repeat count gives the    *
 | 
						|
     * number of lines.                                           */
 | 
						|
    vilinerange = 1;
 | 
						|
    if (!n)
 | 
						|
	return 1;
 | 
						|
    if (n > 0) {
 | 
						|
	while(n-- && cs <= ll)
 | 
						|
	    cs = findeol() + 1;
 | 
						|
	if (n != -1) {
 | 
						|
	    cs = pos;
 | 
						|
	    return 1;
 | 
						|
	}
 | 
						|
	cs--;
 | 
						|
    } else {
 | 
						|
	while(n++ && cs >= 0)
 | 
						|
	    cs = findbol() - 1;
 | 
						|
	if (n != 1) {
 | 
						|
	    cs = pos;
 | 
						|
	    return 1;
 | 
						|
	}
 | 
						|
	cs++;
 | 
						|
    }
 | 
						|
    virangeflag = 2;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viaddnext(char **args)
 | 
						|
{
 | 
						|
    if (cs != findeol())
 | 
						|
	cs++;
 | 
						|
    startvitext(1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viaddeol(char **args)
 | 
						|
{
 | 
						|
    cs = findeol();
 | 
						|
    startvitext(1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viinsert(char **args)
 | 
						|
{
 | 
						|
    startvitext(1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viinsertbol(char **args)
 | 
						|
{
 | 
						|
    vifirstnonblank(zlenoargs);
 | 
						|
    startvitext(1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
videlete(char **args)
 | 
						|
{
 | 
						|
    int c2, ret = 1;
 | 
						|
 | 
						|
    startvichange(1);
 | 
						|
    if ((c2 = getvirange(0)) != -1) {
 | 
						|
	forekill(c2 - cs, 0);
 | 
						|
	ret = 0;
 | 
						|
	if (vilinerange && ll) {
 | 
						|
	    if (cs == ll)
 | 
						|
		cs--;
 | 
						|
	    foredel(1);
 | 
						|
	    vifirstnonblank(zlenoargs);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    vichgflag = 0;
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
videletechar(char **args)
 | 
						|
{
 | 
						|
    int n = zmult;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    /* handle negative argument */
 | 
						|
    if (n < 0) {
 | 
						|
	int ret;
 | 
						|
	zmult = -n;
 | 
						|
	ret = vibackwarddeletechar(args);
 | 
						|
	zmult = n;
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
    /* it is an error to be on the end of line */
 | 
						|
    if (cs == ll || line[cs] == '\n')
 | 
						|
	return 1;
 | 
						|
    /* Put argument into the acceptable range -- it is not an error to  *
 | 
						|
     * specify a greater count than the number of available characters. */
 | 
						|
    if (n > findeol() - cs)
 | 
						|
	n = findeol() - cs;
 | 
						|
    /* do the deletion */
 | 
						|
    forekill(n, 0);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vichange(char **args)
 | 
						|
{
 | 
						|
    int c2, ret = 1;
 | 
						|
 | 
						|
    startvichange(1);
 | 
						|
    if ((c2 = getvirange(1)) != -1) {
 | 
						|
	ret = 0;
 | 
						|
	forekill(c2 - cs, 0);
 | 
						|
	selectkeymap("main", 1);
 | 
						|
	viinsbegin = cs;
 | 
						|
	undoing = 0;
 | 
						|
    }
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
visubstitute(char **args)
 | 
						|
{
 | 
						|
    int n = zmult;
 | 
						|
 | 
						|
    startvichange(1);
 | 
						|
    if (n < 0)
 | 
						|
	return 1;
 | 
						|
    /* it is an error to be on the end of line */
 | 
						|
    if (cs == ll || line[cs] == '\n')
 | 
						|
	return 1;
 | 
						|
    /* Put argument into the acceptable range -- it is not an error to  *
 | 
						|
     * specify a greater count than the number of available characters. */
 | 
						|
    if (n > findeol() - cs)
 | 
						|
	n = findeol() - cs;
 | 
						|
    /* do the substitution */
 | 
						|
    forekill(n, 0);
 | 
						|
    startvitext(1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vichangeeol(char **args)
 | 
						|
{
 | 
						|
    forekill(findeol() - cs, 0);
 | 
						|
    startvitext(1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vichangewholeline(char **args)
 | 
						|
{
 | 
						|
    vifirstnonblank(args);
 | 
						|
    return vichangeeol(zlenoargs);
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viyank(char **args)
 | 
						|
{
 | 
						|
    int oldcs = cs, c2, ret = 1;
 | 
						|
 | 
						|
    startvichange(1);
 | 
						|
    if ((c2 = getvirange(0)) != -1) {
 | 
						|
	cut(cs, c2 - cs, 0);
 | 
						|
	ret = 0;
 | 
						|
    }
 | 
						|
    vichgflag = 0;
 | 
						|
    cs = oldcs;
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viyankeol(char **args)
 | 
						|
{
 | 
						|
    int x = findeol();
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if (x == cs)
 | 
						|
	return 1;
 | 
						|
    cut(cs, x - cs, 0);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viyankwholeline(char **args)
 | 
						|
{
 | 
						|
    int bol = findbol(), oldcs = cs;
 | 
						|
    int n = zmult;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if (n < 1)
 | 
						|
	return 1;
 | 
						|
    while(n--) {
 | 
						|
     if (cs > ll) {
 | 
						|
	cs = oldcs;
 | 
						|
	return 1;
 | 
						|
     }
 | 
						|
     cs = findeol() + 1;
 | 
						|
    }
 | 
						|
    vilinerange = 1;
 | 
						|
    cut(bol, cs - bol - 1, 0);
 | 
						|
    cs = oldcs;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vireplace(char **args)
 | 
						|
{
 | 
						|
    startvitext(0);
 | 
						|
    return 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, and won't repeat the action.  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, so some trickery is needed to emulate this.  When repeating *
 | 
						|
 * a change, we always read the argument normally, even if the count    *
 | 
						|
 * was bad.  When recording a change for repeating, and a bad count is  *
 | 
						|
 * given, we squash the repeat buffer to avoid repeating the partial    *
 | 
						|
 * command; we've lost the previous change, but that can't be avoided   *
 | 
						|
 * without a rewrite of the repeat code.                                */
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vireplacechars(char **args)
 | 
						|
{
 | 
						|
    int ch, n = zmult;
 | 
						|
 | 
						|
    startvichange(1);
 | 
						|
    /* check argument range */
 | 
						|
    if (n < 1 || n + cs > findeol()) {
 | 
						|
	if(vichgrepeat)
 | 
						|
	    vigetkey();
 | 
						|
	if(vichgflag) {
 | 
						|
	    free(vichgbuf);
 | 
						|
	    vichgbuf = NULL;
 | 
						|
	    vichgflag = 0;
 | 
						|
	}
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    /* get key */
 | 
						|
    if((ch = vigetkey()) == -1) {
 | 
						|
	vichgflag = 0;
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    /* do change */
 | 
						|
    if (ch == '\r' || ch == '\n') {
 | 
						|
	/* <return> handled specially */
 | 
						|
	cs += n - 1;
 | 
						|
	backkill(n - 1, 0);
 | 
						|
	line[cs++] = '\n';
 | 
						|
    } else {
 | 
						|
	while (n--)
 | 
						|
	    line[cs++] = ch;
 | 
						|
	cs--;
 | 
						|
    }
 | 
						|
    vichgflag = 0;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vicmdmode(char **args)
 | 
						|
{
 | 
						|
    if (invicmdmode() || selectkeymap("vicmd", 0))
 | 
						|
	return 1;
 | 
						|
    undoing = 1;
 | 
						|
    vichgflag = 0;
 | 
						|
    if (cs != findbol())
 | 
						|
	cs--;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viopenlinebelow(char **args)
 | 
						|
{
 | 
						|
    cs = findeol();
 | 
						|
    spaceinline(1);
 | 
						|
    line[cs++] = '\n';
 | 
						|
    startvitext(1);
 | 
						|
    clearlist = 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viopenlineabove(char **args)
 | 
						|
{
 | 
						|
    cs = findbol();
 | 
						|
    spaceinline(1);
 | 
						|
    line[cs] = '\n';
 | 
						|
    startvitext(1);
 | 
						|
    clearlist = 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vioperswapcase(char **args)
 | 
						|
{
 | 
						|
    int oldcs, c2, ret = 1;
 | 
						|
 | 
						|
    /* 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;
 | 
						|
	ret = 0;
 | 
						|
#if 0
 | 
						|
	vifirstnonblank();
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    vichgflag = 0;
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
virepeatchange(char **args)
 | 
						|
{
 | 
						|
    /* make sure we have a change to repeat */
 | 
						|
    if (!vichgbuf || vichgflag)
 | 
						|
	return 1;
 | 
						|
    /* restore or update the saved count and buffer */
 | 
						|
    if (zmod.flags & MOD_MULT) {
 | 
						|
	lastmod.mult = zmod.mult;
 | 
						|
	lastmod.flags |= MOD_MULT;
 | 
						|
    }
 | 
						|
    if (zmod.flags & MOD_VIBUF) {
 | 
						|
	lastmod.vibuf = zmod.vibuf;
 | 
						|
	lastmod.flags = (lastmod.flags & ~MOD_VIAPP) |
 | 
						|
	    MOD_VIBUF | (zmod.flags & MOD_VIAPP);
 | 
						|
    }
 | 
						|
    /* repeat the command */
 | 
						|
    inrepeat = 1;
 | 
						|
    ungetkeys(vichgbuf, vichgbufptr);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viindent(char **args)
 | 
						|
{
 | 
						|
    int oldcs = cs, c2;
 | 
						|
 | 
						|
    /* get the range */
 | 
						|
    startvichange(1);
 | 
						|
    if ((c2 = getvirange(0)) == -1) {
 | 
						|
	vichgflag = 0;
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    vichgflag = 0;
 | 
						|
    /* must be a line range */
 | 
						|
    if (!vilinerange) {
 | 
						|
	cs = oldcs;
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    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(zlenoargs);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viunindent(char **args)
 | 
						|
{
 | 
						|
    int oldcs = cs, c2;
 | 
						|
 | 
						|
    /* get the range */
 | 
						|
    startvichange(1);
 | 
						|
    if ((c2 = getvirange(0)) == -1) {
 | 
						|
	vichgflag = 0;
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    vichgflag = 0;
 | 
						|
    /* must be a line range */
 | 
						|
    if (!vilinerange) {
 | 
						|
	cs = oldcs;
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    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(zlenoargs);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vibackwarddeletechar(char **args)
 | 
						|
{
 | 
						|
    int n = zmult;
 | 
						|
 | 
						|
    if (invicmdmode())
 | 
						|
	startvichange(-1);
 | 
						|
    /* handle negative argument */
 | 
						|
    if (n < 0) {
 | 
						|
	int ret;
 | 
						|
	zmult = -n;
 | 
						|
	ret = videletechar(args);
 | 
						|
	zmult = n;
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
    /* It is an error to be at the beginning of the line, or (in *
 | 
						|
     * insert mode) to delete past the beginning of insertion.   */
 | 
						|
    if ((!invicmdmode() && cs - n < viinsbegin) || cs == findbol()) {
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    /* Put argument into the acceptable range -- it is not an error to  *
 | 
						|
     * specify a greater count than the number of available characters. */
 | 
						|
    if (n > cs - findbol())
 | 
						|
	n = cs - findbol();
 | 
						|
    /* do the deletion */
 | 
						|
    backkill(n, 1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vikillline(char **args)
 | 
						|
{
 | 
						|
    if (viinsbegin > cs)
 | 
						|
	return 1;
 | 
						|
    backdel(cs - viinsbegin);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viputbefore(char **args)
 | 
						|
{
 | 
						|
    Cutbuffer buf = &cutbuf;
 | 
						|
    int n = zmult;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if (n < 0)
 | 
						|
	return 1;
 | 
						|
    if (zmod.flags & MOD_VIBUF)
 | 
						|
	buf = &vibuf[zmod.vibuf];
 | 
						|
    if (!buf->buf)
 | 
						|
	return 1;
 | 
						|
    if(buf->flags & CUTBUFFER_LINE) {
 | 
						|
	cs = findbol();
 | 
						|
	spaceinline(buf->len + 1);
 | 
						|
	memcpy((char *)line + cs, buf->buf, buf->len);
 | 
						|
	line[cs + buf->len] = '\n';
 | 
						|
	vifirstnonblank(zlenoargs);
 | 
						|
    } else {
 | 
						|
	while (n--) {
 | 
						|
	    spaceinline(buf->len);
 | 
						|
	    memcpy((char *)line + cs, buf->buf, buf->len);
 | 
						|
	    cs += buf->len;
 | 
						|
	}
 | 
						|
	if (cs)
 | 
						|
	    cs--;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viputafter(char **args)
 | 
						|
{
 | 
						|
    Cutbuffer buf = &cutbuf;
 | 
						|
    int n = zmult;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if (n < 0)
 | 
						|
	return 1;
 | 
						|
    if (zmod.flags & MOD_VIBUF)
 | 
						|
	buf = &vibuf[zmod.vibuf];
 | 
						|
    if (!buf->buf)
 | 
						|
	return 1;
 | 
						|
    if(buf->flags & CUTBUFFER_LINE) {
 | 
						|
	cs = findeol();
 | 
						|
	spaceinline(buf->len + 1);
 | 
						|
	line[cs++] = '\n';
 | 
						|
	memcpy((char *)line + cs, buf->buf, buf->len);
 | 
						|
	vifirstnonblank(zlenoargs);
 | 
						|
    } else {
 | 
						|
	if (cs != findeol())
 | 
						|
	    cs++;
 | 
						|
	while (n--) {
 | 
						|
	    spaceinline(buf->len);
 | 
						|
	    memcpy((char *)line + cs, buf->buf, buf->len);
 | 
						|
	    cs += buf->len;
 | 
						|
	}
 | 
						|
	if (cs)
 | 
						|
	    cs--;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vijoin(char **args)
 | 
						|
{
 | 
						|
    int x;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if ((x = findeol()) == ll)
 | 
						|
	return 1;
 | 
						|
    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] = ' ';
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viswapcase(char **args)
 | 
						|
{
 | 
						|
    int eol, n = zmult;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if (n < 1)
 | 
						|
	return 1;
 | 
						|
    eol = findeol();
 | 
						|
    while (cs < eol && n--) {
 | 
						|
	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--;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vicapslockpanic(char **args)
 | 
						|
{
 | 
						|
    clearlist = 1;
 | 
						|
    zbeep();
 | 
						|
    statusline = "press a lowercase key to continue";
 | 
						|
    statusll = strlen(statusline);
 | 
						|
    zrefresh();
 | 
						|
    while (!islower(getkey(0)));
 | 
						|
    statusline = NULL;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
visetbuffer(char **args)
 | 
						|
{
 | 
						|
    int ch;
 | 
						|
 | 
						|
    if ((zmod.flags & MOD_VIBUF) ||
 | 
						|
	(((ch = getkey(0)) < '1' || ch > '9') &&
 | 
						|
	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')))
 | 
						|
	return 1;
 | 
						|
    if (ch >= 'A' && ch <= 'Z')	/* needed in cut() */
 | 
						|
	zmod.flags |= MOD_VIAPP;
 | 
						|
    else
 | 
						|
	zmod.flags &= ~MOD_VIAPP;
 | 
						|
    zmod.vibuf = tulower(ch) + (idigit(ch) ? -'1' + 26 : -'a');
 | 
						|
    zmod.flags |= MOD_VIBUF;
 | 
						|
    prefixflag = 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vikilleol(char **args)
 | 
						|
{
 | 
						|
    int n = findeol() - cs;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    if (!n) {
 | 
						|
	/* error -- line already empty */
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    /* delete to end of line */
 | 
						|
    forekill(findeol() - cs, 0);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vipoundinsert(char **args)
 | 
						|
{
 | 
						|
    int oldcs = cs;
 | 
						|
 | 
						|
    startvichange(-1);
 | 
						|
    vifirstnonblank(zlenoargs);
 | 
						|
    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);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
viquotedinsert(char **args)
 | 
						|
{
 | 
						|
#ifndef HAS_TIO
 | 
						|
    struct sgttyb sob;
 | 
						|
#endif
 | 
						|
 | 
						|
    spaceinline(1);
 | 
						|
    line[cs] = '^';
 | 
						|
    zrefresh();
 | 
						|
#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
 | 
						|
    zsetterm();
 | 
						|
#endif
 | 
						|
    foredel(1);
 | 
						|
    if(c < 0)
 | 
						|
	return 1;
 | 
						|
    else
 | 
						|
	return selfinsert(args);
 | 
						|
}
 | 
						|
 | 
						|
/* the 0 key in vi: continue a repeat count in the manner of      *
 | 
						|
 * digit-argument if possible, otherwise do vi-beginning-of-line. */
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
vidigitorbeginningofline(char **args)
 | 
						|
{
 | 
						|
    if(zmod.flags & MOD_TMULT)
 | 
						|
	return digitargument(args);
 | 
						|
    else {
 | 
						|
	removesuffix();
 | 
						|
	invalidatelist();
 | 
						|
	return vibeginningofline(args);
 | 
						|
    }
 | 
						|
}
 |