/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1981 Regents of the University of California */
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
#ifndef PRESUNEUC
#include <wctype.h>
#ifdef putchar
#endif
#ifdef getchar
#endif
#endif /* PRESUNEUC */
/*
* This is the main routine for visual.
* We here decode the count and possible named buffer specification
* preceding a command and interpret a few of the commands.
* Commands which involve a target (i.e. an operator) are decoded
* in the routine operate in ex_voperate.c.
*/
extern int windowchg;
extern int sigok;
#ifdef XPG6
#endif
void redraw(), windowinit();
#ifdef XPG4
extern int P_cursor_offset;
#endif
void
vmain(void)
{
int c, cnt, i;
unsigned char *oglobp;
short d;
int shouldpo = 0;
int tag_reset_wrap = 0;
ixlatctl(0);
/*
* If we started as a vi command (on the command line)
* then go process initial commands (recover, next or tag).
*/
if (initev) {
i = tchng;
goto doinit;
}
/*
* NB:
*
* The current line is always in the line buffer linebuf,
* and the cursor at the position cursor. You should do
* a vsave() before moving off the line to make sure the disk
* copy is updated if it has changed, and a getDOT() to get
* the line back if you mung linebuf. The motion
* routines in ex_vwind.c handle most of this.
*/
for (;;) {
/*
* Decode a visual command.
* First sync the temp file if there has been a reasonable
* amount of change. Clear state for decoding of next
* command.
*/
TSYNC();
vglobp = 0;
vreg = 0;
hold = 0;
seenprompt = 1;
wcursor = 0;
splitw = 0;
sigok = 1;
(void)peekkey();
sigok = 0;
}
holdupd = 0;
/*
if (LINE(0) < ZERO) {
vclear();
vcnt = 0;
i = 3;
}
*/
vcnt = 0;
vsave();
} else if (i == 3)
else
vfixcurs();
} else if(windowchg)
redraw();
#ifdef XPG6
if (redisplay) {
/* XPG6 assertion 313 & 254 : after [count]r\n */
fixdisplay();
}
redisplay = 0;
#endif
/*
* Gobble up counts and named buffer specifications.
*/
for (;;) {
#ifdef MDEBUG
if (trace)
#endif
sigok = 1;
c = peekkey();
sigok = 0;
hadcnt = 1;
}
if (peekkey() != '"')
break;
/*
* Buffer names be letters or digits.
* But not '0' as that is the source of
* an 'empty' named buffer spec in the routine
* kshift (see ex_temp.c).
*/
/* get rest of character */
ungetkey(c);
}
vreg = c;
}
/*
* Come to reread from below after some macro expansions.
* The call to map allows use of function key pads
* by performing a terminal dependent mapping of inputs.
*/
#ifdef MDEBUG
if (trace)
#endif
maphopcnt = 0;
do {
/*
* Keep mapping the char as long as it changes.
* This allows for double mappings, e.g., q to #,
* #1 to something else.
*/
c = op;
#ifdef MDEBUG
if (trace)
#endif
/*
* Maybe the mapped to char is a count. If so, we have
* to go back to the "for" to interpret it. Likewise
* for a buffer name.
*/
ungetkey(c);
goto looptop;
}
c = op;
break;
}
if (++maphopcnt > 256)
} while (c != op);
/*
* Begin to build an image of this command for possible
* later repeat in the buffer workcmd. It will be copied
* to lastcmd by the routine setLAST
*/
if (!vglobp)
*lastcp++ = c;
/*
* First level command decode.
*/
switch (c) {
/*
* ^L Clear screen e.g. after transmission error.
*/
/*
* ^R Retype screen, getting rid of @ lines.
* If in open, equivalent to ^L.
* On terminals where the right arrow key sends
* ^L we make ^R act like ^L, since there is no
* way to get ^L. These terminals (adm31, tvi)
* are intelligent so ^R is useless. Soroc
* will probably foul this up, but nobody has
* one of them.
*/
case CTRL('l'):
case CTRL('r'):
vclear();
}
/*
* Get a clean line, throw away the
* memory of what is displayed now,
* and move back onto the current line.
*/
vclean();
vcnt = 0;
continue;
}
/*
* Weird glitch -- when we enter visual
* in a very small window we may end up with
* no lines on the screen because the line
* at the top is too long. This forces the screen
* to be expanded to make room for it (after
* we have printed @'s ick showing we goofed).
*/
if (vcnt == 0)
vfixcurs();
continue;
/*
* $ Escape just cancels the current command
* with a little feedback.
*/
case ESCAPE:
(void) beep();
continue;
/*
* @ Macros. Bring in the macro and put it
* in vmacbuf, point vglobp there and punt.
*/
case '@':
c = getesc();
if (c == 0)
continue;
if (c == '@')
c = lastmac;
if (isupper(c))
c = tolower(c);
lastmac = c;
vsave();
lastmac = 0;
splitw = 0;
getDOT();
continue;
goto reread;
/*
*/
case '.':
/*
* Check that there was a last command, and
* take its count and named buffer unless they
* were given anew. Special case if last command
* referenced a numeric named buffer -- increment
* the number and go to a named buffer again.
* This allows a sequence like "1pu.u.u...
* to successively look for stuff in the kill chain
* much as one does in EMACS with C-Y and M-Y.
*/
if (hadcnt)
if (vreg)
lastreg++;
goto reread;
/*
* ^U Scroll up. A count sticks around for
* future scrolls as the scroll amount.
* Attempt to hold the indentation from the
* top of the screen (in logical lines).
*
* BUG: A ^U near the bottom of the screen
* on a dumb terminal (which can't roll back)
* causes the screen to be cleared and then
* redrawn almost as it was. In this case
* one should simply move the cursor.
*/
case CTRL('u'):
if (hadcnt)
else
ind = 0;
vmoving = 0;
continue;
/*
* ^D Scroll down. Like scroll up.
*/
case CTRL('d'):
#ifdef TRACE
if (trace)
fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
if (hadcnt)
else
ind = 0;
vmoving = 0;
#ifdef TRACE
if (trace)
fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
#ifdef TRACE
if (trace)
fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
continue;
/*
* ^E Glitch the screen down (one) line.
* Cursor left on same line in file.
*/
case CTRL('e'):
continue;
if (!hadcnt)
cnt = 1;
/* Bottom line of file already on screen */
continue;
/*
* ^Y Like ^E but up
*/
case CTRL('y'):
continue;
if (!hadcnt)
cnt = 1;
continue;
/*
* m Mark position in mark register given
* by following letter. Return is
* accomplished via ' or `; former
* to beginning of line where mark
* was set, latter to column where marked.
*/
case 'm':
/*
* Getesc is generally used when a character
* is read as a latter part of a command
* what you have typed so far. These characters
* are mapped to 0 by the subroutine.
*/
c = getesc();
if (c == 0)
continue;
/*
* Markreg checks that argument is a letter
* and also maps ' and ` to the end of the range
* to allow '' or `` to reference the previous
* context mark.
*/
c = markreg(c);
forbid (c == 0);
vsave();
anymarks = 1;
continue;
/*
* ^F Window forwards, with 2 lines of continuity.
* Count repeats.
*/
case CTRL('f'):
vsave();
if (vcnt > 2) {
}
vzop(0, 0, '+');
continue;
/*
* ^B Window backwards, with 2 lines of continuity.
* Inverse of ^F.
*/
case CTRL('b'):
vsave();
}
vzop(0, 0, '^');
continue;
/*
* z Screen adjustment, taking a following character:
* zcarriage_return current line to top
* z<NL> like zcarriage_return
* z- current line to bottom
* also z+, z^ like ^F and ^B.
* A preceding count is line to use rather
* than current line. A count between z and
* specifier character changes the screen size
* for the redraw.
*
*/
case 'z':
i = vgetcnt();
if (i > 0)
vsetsiz(i);
c = getesc();
if (c == 0)
continue;
}
vsave();
continue;
/*
* Y Yank lines, abbreviation for y_ or yy.
* Yanked lines can be put later if no
* changes intervene, or can be put in named
* buffers and put anytime in this session.
*/
case 'Y':
ungetkey('_');
c = 'y';
break;
/*
* J Join lines, 2 by default. Count is number
* of lines to join (no join operator sorry.)
*/
case 'J':
if (cnt == 1)
cnt = 2;
cnt = i;
vsave();
vmacchng(1);
setLAST();
notenam = (unsigned char *)"join";
vmoving = 0;
killU();
cursor--;
if (notecnt == 2)
notecnt = 0;
continue;
/*
* S Substitute text for whole lines, abbrev for c_.
* Count is number of lines to change.
*/
case 'S':
ungetkey('_');
c = 'c';
break;
/*
* O Create a new line above current and accept new
* input text, to an escape, there.
* A count specifies, for dumb terminals when
* slowopen is not set, the number of physical
* line space to open on the screen.
*
* o Like O, but opens lines below.
*/
case 'O':
case 'o':
vmacchng(1);
continue;
/*
* C Change text to end of line, short for c$.
*/
case 'C':
if (*cursor) {
break;
}
goto appnd;
/*
* ~ Switch case of letter under cursor
*/
case '~':
{
#ifdef PRESUNEUC
#else
#endif /* PRESUNEUC */
unsigned char tmp1;
setLAST();
/*
* Use multiple 'r' commands to replace
* alpha with alternate case.
*/
if(cnt-- <= 0)
break;
#ifdef PRESUNEUC
#else
#endif /* PRESUNEUC */
#ifdef PRESUNEUC
if(length > 1) {
#else
#endif /* PRESUNEUC */
tmp++;
#ifdef PRESUNEUC
#else
#endif /* PRESUNEUC */
continue;
}
#ifdef PRESUNEUC
#else
#endif /* PRESUNEUC */
/*
* If pointing to an alpha character,
* change the case.
*/
#ifdef PRESUNEUC
else
#else
else
#endif /* PRESUNEUC */
if(*ccursor)
/*
* If at end of line do not advance
* to the next character, else use a
* space to advance 1 column.
*/
else {
tmp +=3;
break;
}
tmp += 3;
}
}
continue;
/*
* A Append at end of line, short for $a.
*/
case 'A':
c = 'a';
/* fall into ... */
/*
* a Appends text after cursor. Text can continue
* through arbitrary number of lines.
*/
case 'a':
if (*cursor) {
if(length < 0) {
putoctal = 1;
putoctal = 0;
} else
}
if(length < 0)
cursor++;
else
}
goto insrt;
/*
* I Insert at beginning of whitespace of line,
* short for ^i.
*/
case 'I':
c = 'i';
/* fall into ... */
/*
* R Replace characters, one for one, by input
* (logically), like repeated r commands.
*
* BUG: This is like the typeover mode of many other
* editors, and is only rarely useful. Its
* implementation is a hack in a low level
* routine and it doesn't work very well, e.g.
* you can't move around within a R, etc.
*/
case 'R':
/* fall into... */
/*
* i Insert text to an escape in the buffer.
* Text is arbitrary. This command reminds of
* the i command in bare teco.
*/
case 'i':
/*
* Common code for all the insertion commands.
* Save for redo, position cursor, prepare for append
* at command and in visual undo. Note that nothing
* is doomed, unless R when all is, and save the
* current line in a the undo temporary buffer.
*/
vmacchng(1);
setLAST();
prepapp();
vnoapp();
if(FIXUNDO)
vmoving = 0;
/*
* If this is a repeated command, then suppress
* fake insert mode on dumb terminals which looks
* ridiculous and wastes lots of time even at 9600B.
*/
if (vglobp)
continue;
/*
* An attention, normally a DEL, just beeps.
* If you are a vi command within ex, then
* two ATTN's will drop you back to command mode.
*/
case ATTN:
(void) beep();
continue;
/* fall into... */
/*
* ^\ A quit always gets command mode.
*/
case QUIT:
/*
* Have to be careful if we were called
* since a return will just start up again.
* So we simulate an interrupt.
*/
if (inglobal)
onintr(0);
/* fall into... */
#ifdef notdef
/*
* q Quit back to command mode, unless called as
* vi on command line in which case dont do it
*/
case 'q': /* quit */
if (initev) {
vsave();
splitw = 0;
getDOT();
continue;
}
#endif
/* fall into... */
/*
* Q Is like q, but always gets to command mode
* even if command line invocation was as vi.
*/
case 'Q':
vsave();
/*
* If we are in the middle of a macro, throw away
* the rest and fix up undo.
* This code copied from getbr().
*/
if (vmacp) {
vmacp = 0;
}
ixlatctl(1);
return;
/*
* ZZ Like :x
*/
case 'Z':
globp = (unsigned char *)"x";
vclrech(0);
goto gogo;
/*
* P Put back text before cursor or before current
* line. If text was whole lines goes back
* as whole lines. If part of a single line
* or parts of whole lines splits up current
* line to form many new lines.
* May specify a named buffer, or the delete
* saving buffers 1-9.
*
* p Like P but after rather than before.
*/
case 'P':
case 'p':
vmoving = 0;
#ifdef XPG4
P_cursor_offset = 0;
#endif
#ifdef notdef
#endif
/*
* If previous delete was partial line, use an
* append or insert to put it back so as to
* use insert mode on intelligent terminals.
*/
setLAST();
goto reread;
}
/*
* If a register wasn't specified, then make
* sure there is something to put back.
*/
/*
* If we just did a macro the whole buffer is in
* the undo save area. We don't want to put THAT.
*/
vsave();
vmacchng(1);
setLAST();
i = 0;
/*
* Restoring multiple lines which were partial
* lines; will leave cursor in middle
* of line after shoving restored text in to
* split the current line.
*/
i++;
if (c == 'p' && *cursor)
} else {
/*
* In whole line case, have to back up dot
* for P; also want to clear cursor so
* cursor will eventually be positioned
* at the beginning of the first put line.
*/
cursor = 0;
if (c == 'P') {
c = 'p';
}
}
killU();
/*
* The call to putreg can potentially
* bomb since there may be nothing in a named buffer.
* We thus put a catch in here. If we didn't and
* there was an error we would end up in command mode.
*/
vremote(1,
if (vreg == -1) {
splitw = 0;
if (op == 'P')
goto pfixup;
}
splitw = 0;
if (!i) {
/*
* Increment undap1, undap2 to make up
* for their incorrect initialization in the
*/
if (FIXUNDO)
vcline++;
nlput--;
/*
* After a put want current line first line,
* and dot was made the last line put in code
* run so far. This is why we increment vcline
* above and decrease dot here.
*/
}
#ifdef TRACE
if (trace)
fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
#endif
#ifdef XPG4
if (op == 'P' && i > 0) {
}
#endif
/*
* Special case in open mode.
* Force action on the screen when a single
* line is put even if it is identical to
* the current line, e.g. on YP; otherwise
* you can't tell anything happened.
*/
continue;
}
vfixcurs();
continue;
/*
* ^^ Return to previous file.
* Like a :e #, and thus can be used after a
* "No Write" diagnostic.
*/
case CTRL('^'):
vsave();
ckaw();
globp = (unsigned char *)"e! #";
else
globp = (unsigned char *)"e #";
goto gogo;
#ifdef TAG_STACK
/*
* ^T Pop the tag stack if enabled or else reset it
* if not.
*/
case CTRL('t'):
vsave();
globp = (unsigned char *) "pop";
goto gogo;
#endif
/*
* ^] Takes word after cursor as tag, and then does
* tag command. Read ``go right to''.
* This is not a search, so the wrapscan setting
* must be ignored. If set, then it is unset
* here and restored later.
*/
case CTRL(']'):
grabtag();
if (value(vi_WRAPSCAN) == 0) {
tag_reset_wrap = 1;
}
globp = (unsigned char *)"tag";
goto gogo;
/*
* & Like :&
*/
case '&':
globp = (unsigned char *)"&";
goto gogo;
/*
* ^G Bring up a status line at the bottom of
* the screen, like a :file command.
*
* BUG: Was ^S but doesn't work in cbreak mode
*/
case CTRL('g'):
globp = (unsigned char *)"file";
gogo:
vsave();
goto doinit;
#ifdef SIGTSTP
/*
* ^Z: suspend editor session and temporarily return
* control in kernel.
*/
case CTRL('z'):
vsave();
globp = (unsigned char *)"stop";
goto gogo;
#endif
/*
* : Read a command from the echo area and
* execute it in command mode.
*/
case ':':
vsave();
i = tchng;
if (readecho(c)) {
esave[0] = 0;
goto fixup;
}
getDOT();
/*
* Use the visual undo buffer to store the global
* string for command mode, since it is idle right now.
*/
esave[0] = 0;
fixech();
/*
* Have to finagle around not to lose last
* character after this command (when run from ex
* command mode). This is clumsy.
*/
if (shouldpo) {
/*
* So after a "Hit return..." ":", we do
* another "Hit return..." the next time
*/
pofix();
shouldpo = 0;
}
/*
* Save old values of options so we can
* notice when they change; switch into
* cooked mode so we are interruptible.
*/
#ifndef CBREAK
vcook();
#endif
#ifndef CBREAK
vraw();
#endif
#ifndef CBREAK
vraw();
#endif
fixol();
ungetchar(d);
/*
* If we ended up with no lines in the buffer, make
* a line.
*/
fixzero();
}
splitw = 0;
/*
*/
/*
* If a change occurred, other than
* a write which clears changes, then
* we should allow an undo even if .
* didn't move.
*
* BUG: You can make this wrong by
* tricking around with multiple commands
* on one line of : escape, and including
* a write command there, but it's not
* worth worrying about.
*/
/*
* If we are about to do another :, hold off
* updating of screen.
*/
getDOT();
shouldpo = 1;
continue;
}
shouldpo = 0;
/*
* In the case where the file being edited is
* new; e.g. if the initial state hasn't been
* saved yet, then do so now.
*/
if (!inglobal)
savevis();
vcnt = 0;
if (esave[0] == 0)
}
/*
* If the current line moved reset the cursor position.
*/
vmoving = 0;
cursor = 0;
}
/*
* If current line is not on screen or if we are
* in open mode and . moved, then redraw.
*/
if(windowchg)
windowinit();
vup1();
if (vcnt > 0)
vcnt = 0;
} else {
/*
* Current line IS on screen.
* If we did a [Hit return...] then
* restore vcnt and clear screen if in visual
*/
vcline = i;
if (vcnt < 0) {
vclear();
vcnt = 0;
}
}
/*
* Limit max value of vcnt based on $
*/
if (i < vcnt)
vcnt = i;
/*
* Dirty and repaint.
*/
}
/*
* If in visual, put back the echo area
* if it was clobbered.
*/
splitw++;
for (i = 0; i < TUBECOLS - 1; i++) {
if (esave[i] == 0)
break;
}
splitw = 0;
}
if (tag_reset_wrap == 1) {
tag_reset_wrap = 0;
value(vi_WRAPSCAN) = 0;
}
continue;
/*
* u undo the last changing command.
*/
case 'u':
vundo(1);
continue;
/*
* U restore current line to initial state.
*/
case 'U':
vUndo();
continue;
(void) beep();
vmacp = 0;
continue;
}
/*
* Rest of commands are decoded by the operate
* routine.
*/
}
}
/*
* Grab the word after the cursor so we can look for it as a tag.
*/
void
grabtag(void)
{
if (*cp) {
do {
cp++;
/* only allow ascii alphabetics */
*dp++ = 0;
}
}
/*
* Before appending lines, set up addr1 and
* the command mode undo information.
*/
void
prepapp(void)
{
deletenone();
addr1++;
appendnone();
}
/*
* Execute function f with the address bounds addr1
* and addr2 surrounding cnt lines starting at dot.
*/
void
{
inglobal = 0;
if (FIXUNDO)
(*f)(arg);
if (FIXUNDO)
/*
* XPG6 assertion 273: For the following commands, don't set vmcurs
* to 0, so that undo positions the cursor column correctly when
* we've moved off the initial line that was changed eg. when G has
* moved us off the line, or when a multi-line change was done.
*/
vmcurs = 0;
}
}
/*
* Save the current contents of linebuf, if it has changed.
*/
void
vsave(void)
{
/*
* If the undo state is saved in the temporary buffer
* vutmp, then we sync this into the temp file so that
* we will be able to undo even after we have moved off
* the line. It would be possible to associate a line
* with vutmp but we assume that vutmp is only associated
* with line dot (e.g. in case ':') above, so beware.
*/
prepapp();
notecnt = 0;
}
/*
* Get the line out of the temp file and do nothing if it hasn't
* changed. This may seem like a loss, but the line will
* almost always be in a read buffer so this may well avoid disk i/o.
*/
getDOT();
return;
}
/*
* Do a z operation.
* Code here is rather long, and very uninteresting.
*/
void
{
/*
* Z from open; always like a z=.
* This code is a mess and should be cleaned up.
*/
setoutt();
vclear();
putnl();
putNFL();
termreset();
(void)ostart();
vcnt = 0;
return;
}
if (hadcnt) {
} else
switch (c) {
case '+':
break;
case '^':
c = '-';
break;
default:
break;
}
switch (c) {
case '.':
case '-':
break;
case '^':
break;
case '+':
/* fall into ... */
case CR:
case NL:
c = CR;
break;
default:
(void) beep();
return;
}
vmoving = 0;
}