/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1981 Regents of the University of California */
#include "ex.h"
#include "ex_argv.h"
#include "ex_temp.h"
#include "ex_tty.h"
#include "ex_vis.h"
#ifdef STDIO
#include <stdio.h>
#endif
/*
* Command mode subroutines implementing
* append, args, copy, delete, join, move, put,
* shift, tag, yank, z and undo
*/
static int jnoop(void);
static void splitit(void);
int tags_flag;
/*
* Append after line a lines returned by function f.
* Be careful about intermediate states to avoid scramble
* if an interrupt comes in.
*/
int
{
int nline;
nline = 0;
dot = a;
}
while ((*f)() == 0) {
if (morelines() < 0) {
}
gettext("Out of memory- too many lines in file"));
}
}
nline++;
dot++;
undap2++;
dol++;
unddol++;
truedol++;
*rdot = 0;
if (f == gettty) {
dirtcnt++;
TSYNC();
}
}
return (nline);
}
void
appendnone(void)
{
if(FIXUNDO) {
}
}
/*
* Print out the argument list, with []'s around the current name.
*/
void
pargs(void)
{
int ac;
if (ac != 0)
putchar(' ');
viprintf("[");
viprintf("]");
}
noonl();
}
/*
* Delete lines; two cases are if we are really deleting,
* more commonly we are just moving lines to the undo save area.
*/
int
{
nonzero();
if(FIXUNDO) {
void (*dsavint)();
#ifdef UNDOTRACE
if (trace)
vudump("before delete");
#endif
change();
squish();
}
#ifdef UNDOTRACE
if (trace)
vudump("after delete");
#endif
} else {
int i;
change();
unddol -= i;
undap2 -= i;
dol -= i;
truedol -= i;
do
}
if (!hush)
killed();
return (0);
}
void
deletenone(void)
{
if(FIXUNDO) {
squish();
}
}
/*
* save area down in its place.
*/
void
squish(void)
{
if(FIXUNDO) {
if (inopen == -1)
return;
do
}
}
/*
* Join lines. Special hacks put in spaces, two spaces if
* preceding line ends with '.', or no spaces if next line starts with ).
*/
static int jcount;
int
join(int c)
{
#ifndef PRESUNEUC
unsigned char *pcp;
int n;
#endif /* PRESUNEUC */
*cp = 0;
cp1++;
#ifndef PRESUNEUC
/*
* insert locale-specific word delimiter if
* either of end-of-former-line or
* top-of-latter-line is non-ASCII.
*/
goto gotprev;
}
goto mberror;
MB_CUR_MAX) <= 0)
goto mberror;
}
while (*delim)
*delim++);
*cp = 0;
} else
#endif /* PRESUNEUC */
if (*cp1 != ')') {
*cp++ = ' ';
*cp++ = ' ';
}
}
}
gettext("Result line of join would be too long"));
cp--;
}
(void) delete(0);
jcount = 1;
if (FIXUNDO)
if (FIXUNDO)
return (0);
}
static int
jnoop(void)
{
return(--jcount);
}
/*
* Move and copy lines. Hard work is done by move1 which
* is also called by undo.
*/
int getcopy();
void
vi_move(void)
{
bool iscopy = 0;
if (Command[0] == 'm') {
setdot1();
} else {
iscopy++;
setdot();
}
nonzero();
if (adt == 0)
(unsigned char *)gettext("%s where?") :
(unsigned char *)gettext("%s requires a trailing address"),
Command);
donewline();
killed();
}
void
{
int nlines;
if (cflag) {
} else {
*ad1++ &= ~01;
}
ad2++;
}
} else
change();
if (!inglobal)
if(FIXUNDO) {
if (cflag) {
deletenone();
} else {
squish();
}
}
}
int
getcopy(void)
{
return (EOF);
return (0);
}
/*
* Put lines in the buffer from the undo save area.
*/
int
getput(void)
{
return (EOF);
tad1++;
return (0);
}
int
put(void)
{
int cnt;
if (!FIXUNDO)
pragged(1);
return (0);
}
return (0);
}
/*
* A tricky put, of a group of lines in the middle
* Argument says pkills have meaning, e.g. called from
* put; it is 0 on calls from putreg.
*/
void
{
extern unsigned char *cursor;
#ifdef XPG4
extern int P_cursor_offset;
#endif
/*
* Assume the editor has:
*
* cursor is on 'c'
*
* file is: 1) abcd
* 2) efgh
*
* undo area: 3) 1
* 4) 2
* 5) 3
*/
if (!kill)
getDOT();
/*
* Copy "abcd" into genbuf.
* Note that gp points to 'c'.
*/
/*
* Get last line of undo area ("3") into linebuf.
*/
if (kill)
*pkill[1] = 0;
/*
* Concatenate trailing end of current line
* into the last line of undo area:
* linebuf = "3cd"
*/
#ifdef XPG4
#endif
/*
* Replace the last line with what is now in linebuf.
* So unddol = "3cd"
*/
/*
* Get the first line of the undo save area into linebuf.
* So linebuf = "1"
*/
if (kill)
/*
* Copy the first line of the undo save area
* over what is pointed to by sp.
* genbuf = "ab1"
*/
/*
* Now copy genbuf back into linebuf.
* linebuf = "ab1"
*/
/*
* Now put linebuf back into the first line
* of the undo save area.
*/
/*
* Prepare to perform an undo which will actually
* do a put of multiple lines in the middle of
* the current line.
*/
undo(1);
}
/*
* Shift lines, based on c.
* If c is neither < nor >, then this is a lisp aligning =.
*/
void
{
unsigned char *cp;
unsigned char *dp;
int i;
if(FIXUNDO)
continue;
getDOT();
switch (c) {
case '>':
if (linebuf[0] == 0)
continue;
break;
case '<':
if (i == 0)
continue;
i -= cnt;
break;
default:
getDOT();
break;
}
gettext("Result line after shift would be too long"));
}
killed();
}
/*
* Find a tag in the tags file.
* Most work here is in parsing the tags file itself.
*/
void
bool quick;
{
int c, d;
int tfcount = 0;
#ifdef STDIO /* was VMUNIX */
/*
* We have lots of room so we bring in stdio and do
* a binary search on the tags file.
*/
#endif
if (!skipend()) {
else
ignchar();
*lp++ = 0;
gettext("Give one tag per line"));
} else if (lasttag[0] == 0)
c = getchar();
if (!endcmd(c))
goto badtag;
if (c == EOF)
ungetchar(c);
clrstats();
/*
* Loop once for each file in tags "path".
*
* System tags array limits to 4k (tags[ONMSZ]) long,
* therefore, tagfbuf should be able to hold all tags.
*/
while (fne) {
fne++;
if (*fne == 0)
fne = 0; /* done, quit after this time */
else
*fne = 0; /* null terminate filename */
#ifdef STDIO /* was VMUNIX */
continue;
tfcount++;
top = -1L;
bot = 0L;
/* loop for each tags file entry */
unsigned char *oglobp;
if (mid > 0) /* to get first tag in file to work */
/* scan to next \n */
goto goleft;
/* get the line itself */
goto goleft;
/*
* This if decides whether there is a tag match.
* A positive taglength means that a
* match is found if the tag given matches at least
* taglength chars of the tag found.
* A taglength of greater than 511 means that a
* match is found even if the tag given is a proper
* prefix of the tag found. i.e. "ab" matches "abcd"
*/
/*
* Found a match. Force selection to be
* the first possible.
*/
; /* found first possible match */
}
else {
/* postpone final decision. */
continue;
}
}
else {
else
continue;
}
/*
* We found the tag. Decode the line in the file.
*/
/* Rest of tag if abbreviated */
cp++;
/* name of file */
cp++;
if (!*cp)
serror((unsigned char *)
gettext("%s: Bad tags file entry"),
lasttag);
cp++;
}
*lp++ = 0;
if (*cp == 0)
goto badtags;
/*
* Save current position in 't for ^^ in visual.
*/
if (inopen) {
extern unsigned char *cursor;
}
}
#ifdef TAG_STACK
if (*savedfile) {
}
#endif
/* Different file. Do autowrite & get it. */
if (!quick) {
ckaw();
#ifdef TAG_STACK
unsavetag();
#endif
}
}
peekc = d;
samef = 0;
}
/*
* Look for pattern in the current file.
*/
if (samef)
/*
* BUG: if it isn't found (user edited header
* line) we get left in nomagic mode.
*/
peekc = d;
return;
} /* end of "for each tag in file" */
#endif /* STDIO */
/*
* Binary search failed, so try linear search if -S is on.
* -S is needed for tags files that are not sorted.
*/
/*
* Avoid stdio and scan tag file linearly.
*/
if (tags_flag == 0)
continue;
if (io < 0)
continue;
/* tfcount++; */
while (getfile() == 0) {
/* loop for each tags file entry */
unsigned char *oglobp;
/*
* This if decides whether there is a tag match.
* A positive taglength means that a
* match is found if the tag given matches at least
* taglength chars of the tag found.
* A taglength of greater than 511 means that a
* match is found even if the tag given is a proper
* prefix of the tag found. i.e. "ab" matches "abcd"
*/
; /* Found it. */
}
else {
/* Not this tag. Try the next */
continue;
}
/*
* We found the tag. Decode the line in the file.
*/
/* Rest of tag if abbreviated */
cp++;
/* name of file */
cp++;
if (!*cp)
serror((unsigned char *)
gettext("%s: Bad tags file entry"),
lasttag);
cp++;
}
*lp++ = 0;
if (*cp == 0)
goto badtags2;
/*
* Save current position in 't for ^^ in visual.
*/
if (inopen) {
extern unsigned char *cursor;
}
}
#ifdef TAG_STACK
if (*savedfile) {
}
#endif
/* Different file. Do autowrite & get it. */
if (!quick) {
ckaw();
#ifdef TAG_STACK
unsavetag();
#endif
}
}
peekc = d;
samef = 0;
}
/*
* Look for pattern in the current file.
*/
if (samef)
/*
* BUG: if it isn't found (user edited header
* line) we get left in nomagic mode.
*/
peekc = d;
return;
} /* end of "for each tag in file" */
/*
* No such tag in this file. Close it and try the next.
*/
#ifdef STDIO /* was VMUNIX */
#else
#endif
} /* end of "for each file in path" */
if (tfcount <= 0)
else
(unsigned char *)gettext("%s: No such tag") :
(unsigned char *)gettext("%s: No such tag in tags file"),
lasttag);
}
/*
* Save lines from addr1 thru addr2 as though
* they had been deleted.
*/
int
yank(void)
{
if (!FIXUNDO)
save12();
return (0);
}
/*
* z command; print windows of text in the file.
*
* If this seems unreasonably arcane, the reasons
* are historical. This is one of the first commands
* added to the first ex (then called en) and the
* number of facilities here were the major advantage
* of en over ed since they allowed more use to be
* made of fast terminals w/o typing .,.22p all the time.
*/
bool zhadpr;
bool znoclear;
short zweight;
void
{
bool excl;
notempty();
znoclear = 0;
zweight = 0;
case '^':
zweight = 1;
case '-':
case '+':
ignchar();
zweight++;
}
case '=':
case '.':
c = getchar();
break;
case EOF:
znoclear++;
break;
default:
op = 0;
break;
}
if (isdigit(c)) {
nlines = c - '0';
for(;;) {
c = getchar();
if (!isdigit(c))
break;
nlines *= 10;
nlines += c - '0';
}
znoclear++;
if (op == '=')
nlines += 2;
}
else {
}
ungetchar(c);
donewline();
}
setdot();
}
void
{
switch (op) {
case EOF:
case '+':
addr2++;
default:
break;
case '=':
case '.':
znoclear = 0;
nlines--;
nlines >>= 1;
if (op == '=')
nlines--;
if (op == '=')
if (op == '.') {
markDOT();
}
break;
case '^':
case '-':
nlines--;
break;
}
return;
shudclob = 1;
flush1();
vclear();
}
pstart();
if (split) {
splitit();
splitit();
}
}
static void
splitit(void)
{
int l;
putchar('-');
putnl();
}
void
{
pofix();
if (inopen)
if (movedot)
}
}
void
pofix(void)
{
vnfl();
setoutt();
}
}
/*
* Command level undo works easily because
* the editor has a unique temporary file
* index for every line which ever existed.
* We don't have to save large blocks of text,
* only the indices which are small. We do this
* by moving them to after the last line in the
* line buffer array, and marking down info
* about whence they came.
*
* Undo is its own inverse.
*/
void
undo(bool c)
{
int i, k;
#ifdef UNDOTRACE
if (trace)
vudump("before undo");
#endif
gettext("Can't undo in global commands"));
/*
* Unless flag indicates a forced undo, make sure
* there really was a change before trying to undo it.
*/
if (!c)
somechange();
/*
* Update change flags.
*/
change();
/*
* Command to be undone is a move command.
* This is handled as a special case by noting that
* a move "a,b m c" can be inverted by another move.
*/
/*
* when c > b inverse is a+(c-b),c m a-1
*/
} else {
/*
* when b > c inverse is c+1,c+1+(b-a) m b
*/
}
Command = (unsigned char *)"move";
killed();
} else {
int cnt;
/*
* Command to be undone is a non-move.
* All such commands are treated as a combination of
* a delete command and a append command.
* We first move the lines appended by the last command
* from undap1 to undap2-1 so that they are just before the
* saved deleted lines.
*
* Assume the editor has:
*
* cursor is on 'c'
*
* (just change lines 5-8)
*
* file is: 1) ab
* 2) cd
* 3) ef
* 4) gh
* undap1: 5) 12
* 6) 34
* 7) 56
* 8) 78
* undap2: 9) qr
* 10) st
* 11) uv
* 12) wx
* dol: 13) yz
*
* UNDO AREA:
* dol+1: 5) ij
* 6) kl
* 7) mn
* unddol: 8) op
*/
/*
* then we must move the text between undap1 and undap2
* and it must not be at the bottom of the file
*/
/*
* FILE: LINE INITIAL REV1 REV2 REV3
*
* 1) ab ab ab ab
* 2) cd cd cd cd
* 3) ef ef ef ef
* unddel: 4) gh gh gh gh
* undap1: 5) 12 78 78 qr
* 6) 34 56 56 st
* 7) 56 34 34 uv
* 8) 78 12 12 wx
* undap2: 9) qr qr yz yz
* 10) st st wx 12
* 11) uv uv uv 34
* 12) wx wx st 56
* dol: 13) yz yz qr 78
*
* UNDO AREA:
* dol+1: 5) ij ij ij ij
* 6) kl kl kl kl
* 7) mn mn mn mn
* unddol: 8) op op op op
*/
}
/*
* Unddel, the line just before the spot where this
* test was deleted, may have moved. Account for
* this in restoration of saved deleted lines.
*/
unddel -= i;
/*
* The last line (dol) may have changed,
* account for this.
*/
newdol -= i;
/*
* For the case where no lines are restored, dot
* is the line before the first line deleted.
*/
}
/*
* Now put the deleted lines, if any, back where they were.
* Basic operation is: dol+1,unddol m unddel
*/
squish();
}
/*
* Set jp to the line where deleted text is to be added.
*/
/*
* Set kp to end of undo save area.
*
* If there is any deleted text to be added, do reverses.
*/
/*
* If deleted lines are not to be appended
* to the bottom of the file...
*/
/*
* FILE: LINE START REV1 REV2 REV3
* 1) ab ab ab ab
* 2) cd cd cd cd
* 3) ef ef ef ef
* unddel: 4) gh gh gh gh
* undap1: 5) qr 78 78 ij
* 6) st 56 56 kl
* 7) uv 34 34 mn
* 8) wx 12 12 op
* undap2: 9) yz yz yz qr
* 10) 12 wx wx st
* 11) 34 uv uv uv
* 12) 56 st st wx
* dol: 13) 78 qr qr yz
*
* UNDO AREA:
* dol+1: 5) ij ij op 12
* 6) kl kl mn 34
* 7) mn mn kl 56
* unddol: 8) op op ij 78
*/
}
/*
* Account for possible forward motion of the target
* (where the deleted lines were restored) for after
* restoration of the deleted lines.
*/
undap1 += i;
/*
* Dot is the first resurrected line.
*/
/*
* Account for a shift in the last line (dol).
*/
newdol += i;
}
/*
* Clean up so we are invertible
*/
} else
/*
* Now relocate all marks for lines that were modified,
* since the marks point to lines whose address has
* been modified from the save area to the current
* area
*/
for (k=0; k<=25; k++)
if (names[k] == *(j))
}
/*
* Defensive programming - after a munged undadot.
* Also handle empty buffer case.
*/
#ifdef UNDOTRACE
if (trace)
vudump("after undo");
#endif
}
/*
* Be (almost completely) sure there really
* was a change, before claiming to undo.
*/
void
somechange(void)
{
switch (undkind) {
case UNDMOVE:
return;
case UNDCHANGE:
break;
return;
case UNDPUT:
return;
break;
case UNDALL:
return;
return;
break;
case UNDNONE:
}
gettext("Last undoable command didn't change anything"));
}
/*
* Map command:
* map src dest
*
* un is true if this is unmap command
* ab is true if this is abbr command
*/
void
{
unsigned char *p;
int c; /* char --> int */
unsigned char *dname;
if (skipend()) {
int i;
/* print current mapping values */
ignchar();
if (un)
if (inopen)
pofix();
putchar('\t');
putchar('\t');
putNFL();
}
return;
}
(void)skipwh();
for (p=lhs; ; ) {
c = getchar();
if (c == CTRL('v')) {
c = getchar();
/* End of lhs */
break;
} else if (endcmd(c) && c!='"') {
ungetchar(c);
if (un) {
donewline();
*p = 0;
return;
} else
}
*p++ = c;
}
*p = 0;
if (skipend())
for (p=rhs; ; ) {
c = getchar();
if (c == CTRL('v')) {
c = getchar();
} else if (endcmd(c) && c!='"') {
ungetchar(c);
break;
}
*p++ = c;
}
*p = 0;
donewline();
/*
* Special hack for function keys: #1 means key f1, etc.
* If the terminal doesn't have function keys, we just use #1.
*/
if (lhs[0] == '#') {
unsigned char *fnkey;
unsigned char *fkey();
if (fnkey)
} else {
}
}
/*
* Add a macro definition to those that already exist. The sequence of
* chars "src" is mapped into "dest". If src is already mapped into something
* this overrides the mapping. There is no recursion. Unmap is done by
* using NOSTR for dest. Dname is what to show in listings. mp is
* the structure to affect (arrows, etc).
*/
void
{
#ifdef UNDOTRACE
if (trace)
#endif
/*
* Prevent tail recursion. We really should be
* checking to see if src is a suffix of dest
* but this makes mapping involving escapes that
* is reasonable mess up.
*/
/*
* We don't let the user rob himself of ":", and making
* multi char words is a bad idea so we don't allow it.
* Note that if user sets mapinput and maps all of return,
* linefeed, and escape, he can hurt himself. This is
* so weird I don't bother to check for it.
*/
}
else if (dest) {
/* check for tail recursion in input mode: fussier */
}
/*
* If the src were null it would cause the dest to
* be mapped always forever. This is not good.
*/
/* see if we already have a def for src */
zer = -1;
break; /* if so, reuse slot */
} else {
}
}
/* unmap */
} else {
gettext("That macro wasn't mapped"));
}
return;
}
/* reuse empty slot, if we found one and src isn't already defined */
/* if not, append to end */
if (msnext == 0) /* first time */
/* Check is a bit conservative, we charge for dname even if reusing src */
if (dname) {
} else {
/* default descr to string user enters */
}
}
/*
* Implements macros from command mode. c is the buffer to
* get the macro from.
*/
void
cmdmac(c)
unsigned char c;
{
unsigned char *oglobp;
short pk;
bool oinglobal;
lastmac = c;
if (inglobal < 2)
inglobal = 1;
}
}
unsigned char *
char *prompt;
{
unsigned char *p;
int c;
/* In ex mode, let the system hassle with setting no echo */
if (!inopen)
if (p < &pbuf[8])
*p++ = c;
}
*p = '\0';
return(pbuf);
}
#ifdef TAG_STACK
struct tagstack {
char *tag_file;
static int tag_depth = 0;
void
{
if( !value(vi_TAGSTACK) )
return;
if(tag_depth >= TSTACKSIZE) {
}
}
;
}
/*
* Undo a "savetag".
*/
void
unsavetag(void)
{
if (!value(vi_TAGSTACK))
return;
if (tag_depth > 0)
}
void
bool quick;
{
unsigned char *oglobp;
int d;
d = tag_depth;
tag_depth = 0;
if (d == 0)
else
return;
}
if (!tag_depth)
/* change to old file */
if (!quick) {
ckaw();
}
peekc = d;
}
/* set line number */
}
#endif