* 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_vops2.c 6.9 (Berkeley) %G%";
* Low level routines for operations sequences,
* and mostly, insert mode (and a subroutine
* to read an input line, including in the echo area.)
extern char *vUA1
, *vUA2
; /* mjm: extern; also in ex_vops.c */
extern char *vUD1
, *vUD2
; /* mjm: extern; also in ex_vops.c */
* Obleeperate characters in hardcopy
ex_putchar('\\' | QUOTE
);
* Common code for middle part of delete
* and change operating on parts of lines.
vundkind
= VCHNG
, CP(vutmp
, linebuf
);
cp
= wcursor
, wcursor
= cursor
, cursor
= cp
;
vUD1
= vUA1
= vUA2
= cursor
; vUD2
= wcursor
;
return (column(wcursor
- 1));
* Take text from linebuf and stick it
* in the VBSIZE buffer BUF. Used to save
* deleted text of part of line.
if ((BUF
[0] & (QUOTE
|TRIM
)) == OVERBUF
)
* Are we at the end of the printed representation of the
* line? Used internally in hardcopy open.
register char *cp
= vtube
[destline
] + destcol
;
for (i
= WCOLS
- destcol
; i
> 0; i
--) {
if (c
!= ' ' && (c
& QUOTE
) == 0)
* 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.
int ch
; /* mjm: char --> int */
* 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
if (ch
!= 'o' && state
== HARDOPEN
&& (rubble
|| !ateopr())) {
* Handle replace character by (eventually)
* limiting the number of input characters allowed
* in the vgetline routine.
* 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.
if (value(AUTOINDENT
) && indent
!= 0) {
gcursor
= genindent(indent
);
vgotoCL(qcolumn(cursor
- 1, genbuf
));
* 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.)
if ((vglobp
&& *vglobp
== 0) || peekbr()) {
if ((INS
[0] & (QUOTE
|TRIM
)) == OVERBUF
) {
* An escape will be generated at end of string.
* Hold off n^^2 type update on dumb terminals.
* Not a repeated command, get
* a new inserted text for repeat.
* 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.
oldmask
= sigblock(sigmask(SIGWINCH
));
* New text goes into genbuf starting at gcursor.
* cursor preserves place in linebuf where text will eventually go.
if (*cursor
== 0 || state
== CRTOPEN
)
if (ch
== 'r' && repcnt
== 0)
gcursor
= vgetline(repcnt
, gcursor
, &escape
, ch
);
* 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
addtext("\204"), CDCNT
--;
* Smash the generated and preexisting indents together
* and generate one cleanly made out of tabs and spaces
* if we are using autoindent.
if (!vaifirst
&& value(AUTOINDENT
)) {
gcursor
= strend(genbuf
);
* Limit the repetition count based on maximum
* possible line length; do output implied
* by further count (> 1) and cons up the new line
cursor
+= gcursor
- genbuf
;
* If doomed characters remain, clobber them,
* and reopen the line to get the display exact.
register int cind
= cindent();
physdc(cind
, cind
+ doomed
);
i
= vreopen(LINE(vcline
), lineDOT(), vcline
);
fprintf(trace
, "restoring doomed from %d to %d\n", doomed
, savedoomed
);
* All done unless we are continuing on to another line.
* 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.
addtext(gobblebl
? " " : "\n");
indent
= lindent(dot
+ 1);
indent
= whitecnt(linebuf
);
strcLIN(vpastwh(gcursor
+ 1));
gcursor
= genindent(indent
);
if (gcursor
+ strlen(linebuf
) > &genbuf
[LBSIZE
- 2])
* 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
if (FIXUNDO
&& vundkind
== VCHNG
) {
* Now do the append of the new line in the buffer,
* and update the display. If slowopen
vgotoCL(qcolumn(cursor
- 1, genbuf
));
* All done with insertion, position the cursor
else if (cursor
> linebuf
)
(void)sigsetmask(oldmask
);
* 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).
vgoto(destline
- 1, WCOLS
+ destcol
- 1);
* 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
* ended with an ESCAPE or a newline/return.
* 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.
vgetline(cnt
, gcursor
, aescaped
, commch
)
int x
, y
, iwhite
, backsl
=0;
* 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.
iwhite
= whitecnt(genbuf
);
* Carefully avoid using vinschar in the echo area.
if (vglobp
== 0 && Peek_key
== 0 && commch
!= 'r')
while ((ch
= map(c
, immacs
)) != c
) {
error("Infinite macro loop");
* 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.
else if (c
== tty
.sg_kill
)
if (c
== tty
.c_cc
[VERASE
])
else if (c
== tty
.c_cc
[VKILL
])
* ^? 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.
* ^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.
* Backspacing over readecho
* prompt. Pretend delete but
* ^W Back up a white/non-white word.
for (cp
= gcursor
; cp
> ogcursor
&& isspace(cp
[-1]); cp
--)
cp
> ogcursor
&& wordof(c
, cp
- 1); cp
--)
* users kill Kill input on this line, back to
vgotoCL(qcolumn(cursor
- 1, genbuf
));
* \ Followed by erase or kill
* maps to just the erase or kill.
x
= destcol
, y
= destline
;
if (c
== tty
.sg_erase
|| c
== tty
.sg_kill
)
if (c
== tty
.c_cc
[VERASE
]
* ^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.
x
= destcol
, y
= destline
;
* If we get a blank not in the echo area
* consider splitting the window in the wrapmargin.
if (c
!= NL
&& !splitw
) {
if (c
== ' ' && gobblebl
) {
(outcol
>= OCOLUMNS
- value(WRAPMARGIN
) ||
* At end of word and hit wrapmargin.
* Move the word to next line and keep going.
* Find end of previous word if we are past it.
for (cp
=gcursor
; cp
>ogcursor
&& isspace(cp
[-1]); cp
--)
if (outcol
+(backsl
?OCOLUMNS
:0) - (gcursor
-cp
) >= OCOLUMNS
- value(WRAPMARGIN
)) {
* Find beginning of previous word.
for (; cp
>ogcursor
&& !isspace(cp
[-1]); cp
--)
* There is a single word that
* is too long to fit. Just
* let it pass, but beep for
* each new letter to warn
* Erase white space before the word.
while (cp
> ogcursor
&& isspace(cp
[-1]))
* Word abbreviation mode.
if (anyabbrs
&& gcursor
> ogcursor
&& !wordch(cstr
) && wordch(gcursor
-1)) {
for (wdtype
= wordch(cp
- 1);
cp
> ogcursor
&& wordof(wdtype
, cp
- 1); cp
--)
for (abno
=0; abbrevs
[abno
].mapto
; abno
++) {
if (eq(cp
, abbrevs
[abno
].cap
)) {
macpush(abbrevs
[abno
].mapto
, 1);
* ^M Except in repeat maps to \n.
* escape End insert unless repeat and more to repeat.
* ^T Software forward tab.
* Unless in repeat where this means these
* ^D|QUOTE Is a backtab (in a repeated command).
* ^t just generates new indent replacing
* current white space rounded up to soft
* BUG: Don't hack ^T except
cp
= genindent(iwhite
= backtab(c
+ value(SHIFTWIDTH
) + 1));
* ^D works only if we are at the (end of) the
* generated autoindent. We count the ^D for repeat
if (c
== iwhite
&& c
!= 0)
ogcursor
= cp
= genindent(iwhite
);
} else if (&cp
[1] == gcursor
&&
(*cp
== '^' || *cp
== '0')) {
* ^^D moves to margin, then back
* to current indent on next line.
* 0^D moves to margin and then
if (vglobp
&& vglobp
- iglobp
>= 2 &&
(vglobp
[-2] == '^' || vglobp
[-2] == '0')
&& gcursor
== ogcursor
+ 1)
* Possibly discard control inputs.
if (!vglobp
&& junk(c
)) {
if (gcursor
> &genbuf
[LBSIZE
- 2])
if (value(SHOWMATCH
) && !iglobp
)
if (c
== ')' || c
== '}')
* Append the line in buffer at lp
* to the buffer after dot.
register int oing
= inglobal
;
ignore(append(vgetsplit
, dot
));
* Subroutine for vdoappend to pass to append.
* Vmaxrep determines the maximum repetitition factor
* allowed that will yield total line length less than
* LBSIZE characters and also does hacks for the R command.
register int len
, replen
;
CP(cursor
, cursor
+ len
);
if (len
+ cnt
* replen
<= LBSIZE
- 2)
cnt
= (LBSIZE
- 2 - len
) / replen
;