/*
* 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_tty.h"
#include "ex_vis.h"
#ifndef PRESUNEUC
#include <wctype.h>
#ifdef putchar
#endif
#ifdef getchar
#endif
#endif /* PRESUNEUC */
/*
* Low level routines for operations sequences,
* and mostly, insert mode (and a subroutine
* to read an input line, including in the echo area.)
*/
#ifdef XPG6
/* XPG6 assertion 313 & 254 [count]r\n : Also used in ex_vmain.c */
extern int redisplay;
#endif
int vmaxrep(unsigned char, int);
static void imultlinerep(int, line *, int, int);
static void omultlinerep(int, line *, int);
#ifdef XPG6
static void rmultlinerep(int, int);
#endif
void fixdisplay(void);
/*
* Obleeperate characters in hardcopy
* open with \'s.
*/
void
{
do
while (--i >= 0);
rubble = 1;
}
/*
* Common code for middle part of delete
* and change operating on parts of lines.
*/
int
vdcMID(void)
{
unsigned char *cp;
squish();
setLAST();
if (FIXUNDO)
/*
* XPG6 assertion 273: Set vmcurs so that undo positions the
* cursor column correctly when we've moved off the initial line
* that was changed, as with the C, c, and s commands,
* when G has moved us off the line, or when a
* multi-line change was done.
*/
fixundo();
}
/*
* Take text from linebuf and stick it
* in the VBSIZE buffer BUF. Used to save
* deleted text of part of line.
*/
void
{
unsigned char *cp;
(void) beep();
return;
}
}
(void) beep();
}
/*
* Are we at the end of the printed representation of the
* line? Used internally in hardcopy open.
*/
int
ateopr(void)
{
wchar_t i, c;
c = *cp++;
if (c == 0) {
/*
* Optimization to consider returning early, saving
* CPU time. We have to make a special check that
* we aren't missing a mode indicator.
*/
return 0;
return (1);
}
if (c != ' ' && (c & QUOTE) == 0)
return (0);
}
return (1);
}
/*
* Append.
*
* This routine handles the top level append, doing work
* as each new line comes in, and arranging repeatability.
* It also handles append with repeat counts, and calculation
* of autoindents for new lines.
*/
bool vaifirst;
bool gobbled;
unsigned char *ogcursor;
* count of ^D's (backtabs) not seen yet when doing
* repeat of insertion
*/
void
{
int i;
unsigned char *gcursor;
bool escape;
int gotNL = 0;
int imultlinecnt = 0;
int omultlinecnt = 0;
omultlinecnt = 1;
}
#ifdef XPG6
imultlinecnt = 1;
#endif /* XPG6 */
/*
* Before a move in hardopen when the line is dirty
* or we are in the middle of the printed representation,
* we retype the line to the left of the cursor so the
* insert looks clean.
*/
rubble = 1;
i = *gcursor;
*gcursor = ' ';
(void) vmove();
*gcursor = i;
}
/*
* If vrep() passed indent = 0, this is the 'r' command,
* so don't autoindent until the last char.
*/
/*
* Handle replace character by (eventually)
* limiting the number of input characters allowed
* in the vgetline routine.
*/
if (ch == 'r')
repcnt = 2;
else
repcnt = 0;
/*
* If an autoindent is specified, then
* generate a mixture of blanks to tabs to implement
* it and place the cursor after the indent.
* Text read by the vgetline routine will be placed in genbuf,
* so the indent is generated there.
*/
unsigned char x;
*gcursor = 0;
} else {
*gcursor = 0;
if (ch == 'o')
vfixcurs();
}
/*
* Prepare for undo. Pointers delimit inserted portion of line.
*/
/*
* If we are not in a repeated command and a ^@ comes in
* then this means the previous inserted text.
* If there is none or it was too long to be saved,
* then beep() and also arrange to undo any damage done
* so far (e.g. if we are a change.)
*/
switch (ch) {
case 'r':
break;
case 'a':
/*
* TRANSLATION_NOTE
* "A" is a terse mode message corresponding to
* "APPEND MODE".
* Translated message of "A" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
break;
case 's':
/*
* TRANSLATION_NOTE
* "S" is a terse mode message corresponding to
* "SUBSTITUTE MODE".
* Translated message of "S" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
break;
case 'c':
/*
* TRANSLATION_NOTE
* "C" is a terse mode message corresponding to
* "CHANGE MODE".
* Translated message of "C" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
break;
case 'R':
/*
* TRANSLATION_NOTE
* "R" is a terse mode message corresponding to
* "REPLACE MODE".
* Translated message of "R" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
break;
case 'o':
/*
* TRANSLATION_NOTE
* "O" is a terse mode message corresponding to
* "OPEN MODE".
* Translated message of "O" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
break;
case 'i':
/*
* TRANSLATION_NOTE
* "I" is a terse mode message corresponding to
* "INSERT MODE" and the following "INPUT MODE".
* Translated message of "I" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
break;
default:
/*
* TRANSLATION_NOTE
* "I" is a terse mode message corresponding to
* "INPUT MODE" and the previous "INSERT MODE".
* Translated message of "I" must be 1 character (not byte).
* Or, just leave it.
*/
} else {
}
}
ixlatctl(1);
(void) beep();
if (!splitw)
ungetkey('u');
doomed = 0;
return;
}
/*
* Unread input from INS.
* An escape will be generated at end of string.
* Hold off n^^2 type update on dumb terminals.
*/
} else if (vglobp == 0) {
/*
* Not a repeated command, get
* a new inserted text for repeat.
*/
INS[0] = 0;
INS[128] = 0;
INSCDCNT = 0;
}
/*
* For wrapmargin to hack away second space after a '.'
* when the first space caused a line break we keep
* track that this happened in gobblebl, which says
* to gobble up a blank silently.
*/
gobblebl = 0;
startsrcline = dot;
/*
* Text gathering loop.
* New text goes into genbuf starting at gcursor.
* cursor preserves place in linebuf where text will eventually go.
*/
for (;;) {
escape = 0;
else {
ixlatctl(1);
/*
* When vgetline() returns, gcursor is
* pointing to '\0' and vgetline() has
* read an ESCAPE or NL.
*/
if (escape == '\n') {
gotNL = 1;
#ifdef XPG6
if (ch == 'r') {
/*
* XPG6 assertion 313 [count]r\n :
* Arrange to set cursor correctly.
*/
}
#endif /* XPG6 */
} else {
/*
* Upon escape, gcursor is pointing to '\0'
* terminating the string in genbuf.
*/
}
ixlatctl(0);
/*
* After an append, stick information
* about the ^D's and ^^D's and 0^D's in
* the repeated text buffer so repeated
* inserts of stuff indented with ^D as backtab's
* can work.
*/
if (HADUP)
addtext("^");
else if (HADZERO)
addtext("0");
if(!vglobp)
while (CDCNT > 0) {
addtext("\004");
CDCNT--;
}
if (gobbled)
addtext(" ");
}
repcnt = 0;
/*
* Smash the generated and preexisting indents together
* and generate one cleanly made out of tabs and spaces
* if we are using autoindent and this isn't 'r' command.
*/
if (!HADUP)
indent = i;
}
/*
* Set cnt to 1 to avoid repeating the text on the same line.
* Do this for commands 'i', 'I', 'a', and 'A', if we're
* inserting anything with a newline for XPG6. Always do this
* for commands 'o' and 'O'.
*/
cnt = 1;
}
/*
* Limit the repetition count based on maximum
* possible line length; do output implied
* by further count (> 1) and cons up the new line
* in linebuf.
*/
/*
* cursor points to linebuf
* Copy remaining old text (cursor) in original
* line to after new text (gcursor + 1) in genbuf.
*/
/*
* For [count] r \n command, when replacing [count] chars
* with '\n', this loop replaces [count] chars with "".
*/
do {
/* cp new text (genbuf) into linebuf (cursor) */
if (cnt > 1) {
}
/* point cursor after new text in linebuf */
} while (--cnt > 0);
endim();
/* add the remaining old text after the cursor */
if (escape != '\n')
/*
* If doomed characters remain, clobber them,
* and reopen the line to get the display exact.
* eg. c$ to change to end of line
*/
savedoomed = doomed;
if (doomed > 0) {
doomed = 0;
}
if(MB_CUR_MAX > 1)
if(MB_CUR_MAX > 1)
#ifdef TRACE
if (trace)
#endif
if (ch == 'R')
doomed = savedoomed;
}
/*
* Unless we are continuing on to another line
* (got a NL), break out of the for loop (got
* an ESCAPE).
*/
if (escape != '\n') {
vshowmode("");
break;
}
/*
* Set up for the new line.
* First save the current line, then construct a new
* first image for the continuation line consisting
* of any new autoindent plus the pushed ahead text.
*/
killU();
/* save vutmp (for undo state) into temp file */
vsave();
cnt = 1;
if (value(vi_AUTOINDENT)) {
else
vaifirst = 0;
*gcursor = 0;
} else {
/*
* Put gcursor at start of genbuf to wipe
* out previous line in preparation for
* the next vgetline() loop.
*/
}
/*
* If we started out as a single line operation and are now
* turning into a multi-line change, then we had better yank
* out dot before it changes so that undo will work
* correctly later.
*/
undap1--;
}
/*
* Now do the append of the new line in the buffer,
* and update the display, ie: append genbuf to
* the file after dot. If slowopen
* we don't do very much.
*/
vcline++;
else {
if (value(vi_SLOWOPEN))
vscrap();
else
}
switch (ch) {
case 'r':
break;
case 'a':
} else {
}
break;
case 's':
} else {
}
break;
case 'c':
} else {
}
break;
case 'R':
} else {
}
break;
case 'i':
} else {
}
break;
case 'o':
} else {
}
break;
default:
} else {
}
}
/* zero genbuf */
*gcursor = 0;
} /* end for (;;) loop in vappend() */
if (imultlinecnt && gotNL) {
} else if (omultlinecnt) {
#ifdef XPG6
/*
* XPG6 assertion 313 & 254 : Position cursor for [count]r\n
* then insert [count -1] newlines.
*/
#endif /* XPG6 */
}
/*
* All done with insertion, position the cursor
* and sync the screen.
*/
fixdisplay();
#ifdef XPG6
fixdisplay();
/*
* XPG6 assertion 313 & 254 [count]r\n : Set flag to call
* fixdisplay() after operate() has finished. To be sure that
* the text (after the last \n followed by an indent) is always
* displayed, fixdisplay() is called right before getting
* the next command.
*/
redisplay = 1;
#endif /* XPG6 */
#ifdef XPG6
/*
* XPG6 assertion 313 & 254 [count]r\n :
* For 'r' command, when the replacement char causes new
* lines to be created, point cursor to first non-blank.
* The old code, ie: cursor = lastchr(linebuf, cursor);
* set cursor to the blank before the first non-blank
* for r\n
*/
++cursor;
#endif /* XPG6 */
}
vsyncCL();
back1();
doomed = 0;
(void) vmove();
}
/*
* XPG6
* To repeat multi-line input for [count]a, [count]A, [count]i, [count]I,
* or a subsequent [count]. :
* insert input count-1 more times.
*/
static void
{
endsrcline = dot;
/* Save linebuf into temp file before moving off the line. */
vsave();
/*
* At this point the temp file contains the first iteration of
* a multi-line insert, and we need to repeat it savecnt - 1
* more times in the temp file. dot is the last line in the
* first iteration of the insert. Decrement dot so that
* vdoappend() will append each new line before the last line.
*/
--dot;
--vcline;
/*
* Use genbuf to rebuild the last line in the 1st iteration
* of the repeated insert, then copy this line to the temp file.
*/
}
vcline++;
/*
* Loop from the second line of the first iteration
* through endsrcline, appending after dot.
*/
++startsrcline;
++srcline) {
(srcline == endsrcline)) {
/*
* The last line is already in place,
* just make it the current line.
*/
vcline++;
dot++;
getDOT();
} else {
/* copy linebuf to temp file */
vcline++;
}
}
++tmpcnt;
}
}
/*
* To repeat input for [count]o, [count]O, or a subsequent [count]. :
* append input count-1 more times to the end of the already added
* text, each time starting on a new line.
*/
static void
{
endsrcline = dot;
/* Save linebuf into temp file before moving off the line. */
vsave();
/*
* Loop from the first line of the first iteration
* through endsrcline, appending after dot.
*/
/* copy linebuf to temp file */
vcline++;
}
++tmpcnt;
}
}
#ifdef XPG6
/*
* XPG6 assertion 313 & 254 : To repeat '\n' for [count]r\n
* insert '\n' savecnt-1 more times before the already added '\n'.
*/
static void
{
/* Save linebuf into temp file before moving off the line. */
vsave();
/*
* At this point the temp file contains the line followed by '\n',
* which is preceded by indentation if autoindent is set.
* '\n' must be repeated [savecnt - 1] more times in the temp file.
* dot is the current line containing the '\n'. Decrement dot so that
* vdoappend() will append each '\n' before the current '\n'.
* This will allow only the last line to contain any autoindent
* characters.
*/
--dot;
--vcline;
/*
* Append after dot.
*/
linebuf[0] = '\0';
/* append linebuf below current line in temp file */
vcline++;
++tmpcnt;
}
/* set the current line to the line after the last '\n' */
++dot;
++vcline;
/* point cursor after (linebuf + endsrccol) */
}
#endif /* XPG6 */
/*
* Similiar to a ctrl-l, however always vrepaint() in case the last line
* of the repeat would exceed the bottom of the screen.
*/
void
fixdisplay(void)
{
vclear();
vclean();
vcnt = 0;
} else {
vfixcurs();
}
}
/*
* Subroutine for vgetline to back up a single character position,
* backwards around end of lines (vgoto can't hack columns which are
* less than 0 in general).
*/
void
back1(void)
{
}
/*
* Get a line into genbuf after gcursor.
* Cnt limits the number of input characters
* accepted and is used for handling the replace
* single character command. Aescaped is the location
* where we stick a termination indicator (whether we
*
* We do erase-kill type processing here and also
* are careful about the way we do this so that it is
* repeatable. (I.e. so that your kill doesn't happen,
* when you repeat an insert if it was escaped with \ the
* first time you did it. commch is the command character
* involved, including the prompt for readline.
*/
unsigned char *
int cnt;
unsigned char *gcursor;
bool *aescaped;
unsigned char commch;
{
int c, ch;
unsigned char *iglobp;
unsigned char *p;
int len;
/*
* Clear the output state and counters
* for autoindent backwards motion (counts of ^D, etc.)
* Remember how much white space at beginning of line so
* as not to allow backspace over autoindent.
*/
*aescaped = 0;
flusho();
CDCNT = 0;
HADUP = 0;
HADZERO = 0;
gobbled = 0;
/*
* Clear abbreviation recursive-use count
*/
abbrepcnt = 0;
/*
* Carefully avoid using vinschar in the echo area.
*/
if (splitw)
else {
vprepins();
}
for (;;) {
length = 0;
backsl = 0;
if (gobblebl)
gobblebl--;
if (cnt != 0) {
cnt--;
if (cnt == 0)
goto vadone;
}
c = getkey();
if (c != ATTN)
c &= 0377;
ch = c;
maphopcnt = 0;
c = ch;
break;
if (++maphopcnt > 256)
}
if (!iglobp) {
/*
* Erase-kill type processing.
* Only happens if we were not reading
* from untyped input when we started.
* Map users erase to ^H, kill to -1 for switch.
*/
c = CTRL('h');
c = -1;
switch (c) {
/*
* ^? Interrupt drops you back to visual
* command mode with an unread interrupt
* still in the input buffer.
*
* ^\ Quit does the same as interrupt.
* If you are a ex command rather than
* a vi command this will drop you
* back to command mode for sure.
*/
case ATTN:
case QUIT:
ungetkey(c);
goto vadone;
/*
* ^H Backs up a character in the input.
*
* BUG: Can't back around line boundaries.
* This is hard because stuff has
* already been saved for repeat.
*/
case CTRL('h'):
if (splitw) {
/*
* Backspacing over readecho
* prompt. Pretend delete but
* don't beep.
*/
ungetkey(c);
goto vadone;
}
(void) beep();
continue;
}
goto vbackup;
/*
*/
case CTRL('w'):
wdkind = 1;
continue;
continue;
goto vbackup;
/*
* users kill Kill input on this line, back to
* the autoindent.
*/
case -1:
(void) beep();
continue;
}
endim();
*cp = 0;
c = cindent();
if (doomed >= 0)
continue;
/*
* \ Followed by erase or kill
* maps to just the erase or kill.
*/
case '\\':
putchar('\\');
vcsync();
c = getkey();
{
vgoto(y, x);
if (doomed >= 0)
doomed++;
length = 1;
goto def;
}
ungetkey(c), c = '\\';
backsl = 1;
break;
/*
* ^Q Super quote following character
* Only ^@ is verboten (trapped at
* a lower level) and \n forces a line
* split so doesn't really go in.
*
* ^V Synonym for ^Q
*/
case CTRL('q'):
case CTRL('v'):
putchar('^');
vgoto(y, x);
c = getkey();
#ifdef USG
if (c == ATTN)
#endif
if (c != NL) {
if (doomed >= 0)
doomed++;
length = 1;
goto def;
}
break;
}
}
/*
* If we get a blank not in the echo area
* consider splitting the window in the wrapmargin.
*/
if(!backsl) {
ungetkey(c);
(void) beep();
continue;
}
} else {
length = 1;
multic[0] = '\\';
}
if (c == ' ' && gobblebl) {
gobbled = 1;
continue;
}
if (value(vi_WRAPMARGIN) &&
commch != 'r') {
/*
* At end of word and hit wrapmargin.
* Move the word to next line and keep going.
*/
unsigned char *wp;
int bytelength;
#ifndef PRESUNEUC
unsigned char *tgcursor;
#endif /* PRESUNEUC */
wdkind = 1;
if (backsl) {
#ifdef PRESUNEUC
#else
#endif /* PRESUNEUC */
(void) beep();
continue;
}
}
*gcursor = 0;
/*
* Find end of previous word if we are past it.
*/
;
#ifdef PRESUNEUC
/* find screen width of previous word */
width = 0;
#else
/* count screen width of pending characters */
width = 0;
#endif /* PRESUNEUC */
width+=4;
wp++;
} else {
if(curwidth <= 0)
else
wp += bytelength;
}
#ifdef PRESUNEUC
#else
#endif /* PRESUNEUC */
/*
* Find beginning of previous word.
*/
#ifdef PRESUNEUC
;
#else
break;
}
if (!multibyte) {
cp--;
continue;
}
/* 7tabs */if (wc2) {
/* 7tabs */ wp++;
/* 7tabs */ wc1 = 0;
/* 7tabs */ continue;
/* 7tabs */ }
/* 7tabs */} else {
/* 7tabs */ wp++;
/* 7tabs */ wc2 = 0;
/* 7tabs */ continue;
/* 7tabs */ }
/* 7tabs */}
/* 7tabs */if (wc1) {
/* 7tabs */ goto ws;
/* 7tabs */ }
/* 7tabs */ }
/* 7tabs */ wc1 = 0;
/* 7tabs */ break;
/* 7tabs */} else {
/* 7tabs */ break;
/* 7tabs */}
}
cp--;
}
ws:
#endif /* PRESUNEUC */
/*
* There is a single word that
* is too long to fit. Just
* let it pass, but beep for
* each new letter to warn
* the luser.
*/
c = *gcursor;
*gcursor = 0;
(void) beep();
goto dontbreak;
}
/*
* Save it for next line.
*/
#ifdef PRESUNEUC
cp--;
#endif /* PRESUNEUC */
}
macpush("\n", 0);
/*
* Erase white space before the word.
*/
cp--; /* skip blank */
gobblebl = 3;
goto vbackup;
}
}
/*
* Word abbreviation mode.
*/
wdkind = 1;
;
*gcursor = 0;
if(abbrepcnt == 0) {
abbrepcnt = 1;
goto vbackup;
} else
abbrepcnt = 0;
}
}
}
switch (c) {
/*
* ^M Except in repeat maps to \n.
*/
case CR:
if (vglobp) {
length = 1;
goto def;
}
c = '\n';
/* presto chango ... */
/*
* \n Start new line.
*/
case NL:
*aescaped = c;
goto vadone;
/*
* escape End insert unless repeat and more to repeat.
*/
case ESCAPE:
if (lastvgk) {
length = 1;
goto def;
}
goto vadone;
/*
* ^D Backtab.
* ^T Software forward tab.
*
* Unless in repeat where this means these
* were superquoted in.
*/
case CTRL('t'):
if (vglobp) {
length = 1;
goto def;
}
/* fall into ... */
*gcursor = 0;
/*
* ^t just generates new indent replacing
* current white space rounded up to soft
* tab stop increment.
*/
/*
* BUG: Don't hack ^T except
* right after initial
* white space.
*/
continue;
goto vbackup;
}
/*
* ^D works only if we are at the (end of) the
* generated autoindent. We count the ^D for repeat
* purposes.
*/
case CTRL('d'):
/* check if ^d was superquoted in */
length = 1;
goto def;
}
if(vglobp)
inscdcnt--;
*gcursor = 0;
if (c == iwhite && c != 0)
CDCNT++;
goto vbackup;
/*
* ^^D moves to margin, then back
* to current indent on next line.
*
* 0^D moves to margin and then
* stays there.
*/
CDCNT = 1;
endim();
back1();
(void) vputchar(' ');
goto vbackup;
}
p = iglobp;
len = 1;
}
if ((p == &vglobp[-2]) &&
(*p == '^' || *p == '0') &&
goto bakchar;
}
continue;
default:
/*
* Possibly discard control inputs.
*/
(void) beep();
continue;
}
def:
if (!backsl) {
flush();
}
vcsync();
if (c == ')' || c == '}')
continue;
}
}
*gcursor = 0;
endim();
return (gcursor);
}
int vgetsplit();
unsigned char *vsplitpt;
/*
* Append the line in buffer at lp
* to the buffer after dot.
*/
void
{
inglobal = 1;
}
/*
* Subroutine for vdoappend to pass to append.
*/
int
vgetsplit(void)
{
if (vsplitpt == 0)
return (EOF);
vsplitpt = 0;
return (0);
}
/*
* Vmaxrep determines the maximum repetition factor
* allowed that will yield total line length less than
* LBSIZE characters and also does hacks for the R command.
*/
int
{
int len;
unsigned char *cp;
if (ch == 'R') {
oldcnt = 0;
oldcnt++;
}
repcnt = 0;
repcnt++;
}
/*
* if number of characters in replacement string
* (repcnt) is less than number of characters following
* cursor (oldcnt), find end of repcnt
* characters after cursor
*/
}
}
return (cnt);
if (cnt == 0) {
vsave();
}
return (cnt);
}
/*
* Determine how many occurrences of word 'CAP' are in 'MAPTO'. To be
* considered an occurrence there must be both a nonword-prefix, a
* complete match of 'CAP' within 'MAPTO', and a nonword-suffix.
* Note that the beginning and end of 'MAPTO' are considered to be
* valid nonword delimiters.
*/
int
{
cnt = 0;
for (i=0; i <= final; i++)
cnt++;
return (cnt);
}