* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
static char *sccsid
= "@(#)ex_vmain.c 7.8 (Berkeley) %G%";
* 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.
#define forbid(a) { if (a) goto fonfon; }
int onumber
, olist
, (*OPline
)(), (*OPutchar
)();
* If we started as a vi command (on the command line)
* then go process initial commands (recover, next or tag).
* 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.
* Decode a visual command.
* First sync the temp file if there has been a reasonable
* amount of change. Clear state for decoding of next
* Gobble up counts and named buffer specifications.
fprintf(trace
, "pc=%c",peekkey());
if (isdigit(peekkey()) && peekkey() != '0') {
ignore(getkey()), c
= getkey();
* 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).
forbid (c
== '0' || !isalpha(c
) && !isdigit(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.
fprintf(trace
,"pcb=%c,",peekkey());
* Keep mapping the char as long as it changes.
* This allows for double mappings, e.g., q to #,
fprintf(trace
,"pca=%c,",c
);
* Maybe the mapped to char is a count. If so, we have
* to go back to the "for" to interpret it. Likewise
if ((isdigit(c
) && c
!='0') || c
== '"') {
error("Infinite macro loop");
* 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/when completely specified.
* First level command decode.
* ^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
if (c
== CTRL(l
) || (KR
&& *KR
==CTRL(l
))) {
* Get a clean line, throw away the
* memory of what is displayed now,
* and move back onto the current line.
* 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).
* $ Escape just cancels the current command
* with a little feedback.
* @ Macros. Bring in the macro and put it
* in vmacbuf, point vglobp there and punt.
regbuf(c
,tmpbuf
,sizeof(vmacbuf
));
* . Repeat the last (modifying) open/visual command.
* 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.
forbid (lastcmd
[0] == 0);
else if (isdigit(lastreg
) && lastreg
< '9')
* ^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.
ind
= vcline
, cnt
+= ind
;
* ^D Scroll down. Like scroll up.
fprintf(trace
, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot
), lineno(wdot
), lineno(dol
));
ind
= vcnt
- vcline
- 1, cnt
+= ind
;
fprintf(trace
, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot
), lineno(wdot
), lineno(dol
));
fprintf(trace
, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot
), lineno(wdot
), lineno(dol
));
* ^E Glitch the screen down (one) line.
* Cursor left on same line in file.
/* Bottom line of file already on screen */
forbid(lineDOL()-lineDOT() <= vcnt
-1-vcline
);
ind
= vcnt
- vcline
- 1 + cnt
;
forbid(lineDOT()-1<=vcline
); /* line 1 already there */
* 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.
* Getesc is generally used when a character
* is read as a latter part of a command
* to allow one to hit rubout/escape to cancel
* what you have typed so far. These characters
* are mapped to 0 by the subroutine.
* Markreg checks that argument is a letter
* and also maps ' and ` to the end of the range
* to allow '' or `` to reference the previous
names
[c
- 'a'] = (*dot
&~ 01);
* ^F Window forwards, with 2 lines of continuity.
addr
= dot
+ (vcnt
- vcline
) - 2 + (cnt
-1)*basWLINES
;
* ^B Window backwards, with 2 lines of continuity.
if (one
+ vcline
!= dot
&& vcnt
> 2) {
addr
= dot
- vcline
+ 2 - (cnt
-1)*basWLINES
;
* z Screen adjustment, taking a following character:
* z<CR> current line to top
* 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
* 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.
* J Join lines, 2 by default. Count is number
* of lines to join (no join operator sorry.)
if (cnt
> (i
= dol
- dot
+ 1))
cursor
= strend(linebuf
);
vreplace(vcline
, cnt
, 1);
if (!*cursor
&& cursor
> linebuf
)
* S Substitute text for whole lines, abbrev for c_.
* Count is number of lines to change.
* 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.
* C Change text to end of line, short for c$.
* ~ Switch case of letter under cursor
mbuf
[2] = cursor
[1]==0 ? 0 : ' ';
mbuf
[1] ^= ' '; /* toggle the case */
* A Append at end of line, short for $a.
* a Appends text after cursor. Text can continue
* through arbitrary number of lines.
* I Insert at beginning of whitespace of line,
* 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.
* i Insert text to an escape in the buffer.
* Text is arbitrary. This command reminds of
* the i command in bare teco.
* 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.
doomed
= c
== 'R' ? 10000 : 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.
* ^? An attention, normally a ^?, just beeps.
* If you are a vi command within ex, then
* two ATTN's will drop you back to command mode.
if (initev
|| peekkey() != ATTN
)
* ^\ A quit always gets command mode.
* Have to be careful if we were called
* since a return will just start up again.
* So we simulate an interrupt.
* q Quit back to command mode, unless called as
* vi on command line in which case dont do it
error("Q gets ex command mode, :q leaves vi");
* Q Is like q, but always gets to command mode
* even if command line invocation was as vi.
* If we are in the middle of a macro, throw away
* the rest and fix up undo.
* This code copied from getbr().
if (inopen
== -1) /* don't screw up undo for esc esc */
inopen
= 1; /* restore old setting now that macro done */
* 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
* p Like P but after rather than before.
forbid (!vreg
&& value(UNDOMACRO
) && inopen
< 0);
* If previous delete was partial line, use an
* append or insert to put it back so as to
* use insert mode on intelligent terminals.
forbid ((DEL
[0] & (QUOTE
|TRIM
)) == OVERBUF
);
ungetkey(c
== 'p' ? 'a' : 'i');
* If a register wasn't specified, then make
* sure there is something to put back.
forbid (!vreg
&& unddol
== dol
);
* If we just did a macro the whole buffer is in
* the undo save area. We don't want to put THAT.
forbid (vundkind
== VMANY
&& undkind
==UNDALL
);
if (vreg
&& partreg(vreg
) || !vreg
&& pkill
[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.
* 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.
* 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.
addr
= dol
; /* old dol */
vremote(1, vreg
? putreg
: put
, vreg
);
* Increment undap1, undap2 to make up
* for their incorrect initialization in the
* routine vremote before calling put/putreg.
* 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.
fprintf(trace
, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline
, i
, nlput
, lineno(undap1
), lineno(undap2
), lineno(dot
));
vreplace(vcline
, i
, nlput
);
* 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.
vjumpto(dot
, cursor
, '.');
* ^^ Return to previous file.
* Like a :e #, and thus can be used after a
* ^] Takes word after cursor as tag, and then does
* tag command. Read ``go right to''.
* ^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
* ^Z: suspend editor session and temporarily return
* to shell. Only works with Berkeley/IIASA process
forbid(dosusp
== 0 || !ldisc
);
* : Read a command from the echo area and
* execute it in command mode.
* Use the visual undo buffer to store the global
* string for command mode, since it is idle right now.
oglobp
= globp
; strcpy(vutmp
, genbuf
+1); globp
= vutmp
;
* Have to finagle around not to lose last
* character after this command (when run from ex
* command mode). This is clumsy.
* So after a "Hit return..." ":", we do
* another "Hit return..." the next time
* Save old values of options so we can
* notice when they change; switch into
* cooked mode so we are interruptible.
if (dot
== zero
&& dol
> zero
)
copy(esave
, vtube
[WECHO
], TUBECOLS
);
* If we ended up with no lines in the buffer, make
* a line, and don't consider the buffer changed.
* Special case: did list/number options change?
if (onumber
!= value(NUMBER
))
ignorf(setnumb(value(NUMBER
)));
if (olist
!= value(LIST
))
ignorf(setlist(value(LIST
)));
* If a change occurred, other than
* a write which clears changes, then
* we should allow an undo even if .
* BUG: You can make this wrong by
* tricking around with multiple commands
* on one line of : escape, and including
* a write command there, but its not
if (FIXUNDO
&& tchng
&& tchng
!= i
)
vundkind
= VMANY
, cursor
= 0;
* If we are about to do another :, hold off
if (vcnt
< 0 && Peek_key
== ':') {
* 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.
copy(esave
, vtube
[WECHO
], TUBECOLS
);
* If the current line moved reset the cursor position.
* If current line is not on screen or if we are
* in open mode and . moved, then redraw.
i
= vcline
+ (dot
- addr
);
if (i
< 0 || i
>= vcnt
&& i
>= -vcnt
|| state
!= VISUAL
&& dot
!= addr
) {
vjumpto(dot
, (char *) 0, '.');
* Current line IS on screen.
* If we did a [Hit return...] then
* restore vcnt and clear screen if in visual
else if (state
== CRTOPEN
) {
* Limit max value of vcnt based on $
i
= vcline
+ lineDOL() - lineDOT() + 1;
* If in visual, put back the echo area
int sdc
= destcol
, sdl
= destline
;
for (i
= 0; i
< TUBECOLS
- 1; i
++) {
* u undo the last changing command.
* U restore current line to initial state.
inopen
= 1; /* might have been -1 */
* Rest of commands are decoded by the operate
* Grab the word after the cursor so we can look for it as a tag.
if (dp
< &lasttag
[sizeof lasttag
- 2])
} while (isalpha(*cp
) || isdigit(*cp
) || *cp
== '_'
|| (value(LISP
) && *cp
== '-')
* Before appending lines, set up addr1 and
* the command mode undo information.
* Execute function f with the address bounds addr1
* and addr2 surrounding cnt lines starting at dot.
register int oing
= inglobal
;
* Save the current contents of linebuf, if it has changed.
if (FIXUNDO
&& vundkind
== VCHNG
|| vundkind
== VCAPU
) {
* 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.
* 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.
if (strcmp(linebuf
, temp
) == 0)
#define forbid(a) if (a) { beep(); return; }
* Code here is rather long, and very uninteresting.
* Z from open; always like a z=.
* This code is a mess and should be cleaned up.
zop2(Xhadcnt
? Xcnt
: value(WINDOW
) - 1, '=');
addr
= dot
+ vcnt
- vcline
;