Blame | Last modification | View Log | RSS feed
/*
* This code contains changes by
* Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
*
* Conditions 1, 2, and 4 and the no-warranty notice below apply
* to these changes.
*
*
* Copyright (c) 1980, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* Redistributions of source code and documentation must retain the
* above copyright notice, this list of conditions and the following
* disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed or owned by Caldera
* International, Inc.
* Neither the name of Caldera International, Inc. nor the names of
* other contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lint
#ifdef DOSCCS
static char sccsid[] = "@(#)ex_subr.c 1.37 (gritter) 2/15/05";
#endif
#endif
/* from ex_subr.c 7.10.1 (2.11BSD) 1996/3/22 */
#include "ex.h"
#include "ex_re.h"
#include "ex_tty.h"
#include "ex_vis.h"
static short lastsc;
/*
* Random routines, in alphabetical order.
*/
int
any(int c, register char *s)
{
register int x;
while (x = *s++)
if (x == c)
return (1);
return (0);
}
int
backtab(register int i)
{
register int j;
j = i % value(SHIFTWIDTH);
if (j == 0)
j = value(SHIFTWIDTH);
i -= j;
if (i < 0)
i = 0;
return (i);
}
void
change(void)
{
tchng++;
chng = tchng;
fixedzero = 0;
}
/*
* Column returns the number of
* columns occupied by printing the
* characters through position cp of the
* current line.
*/
int
column(register char *cp)
{
if (cp == 0)
cp = &linebuf[LBSIZE - 2];
return (qcolumn(cp, NULL));
}
int
lcolumn(register char *cp)
{
return column(cp) - (lastsc - 1);
}
/*
* Ignore a comment to the end of the line.
* This routine eats the trailing newline so don't call newline().
*/
void
comment(void)
{
register int c;
do {
c = getchar();
} while (c != '\n' && c != EOF);
if (c == EOF)
ungetchar(c);
}
void
Copy(register char *to, register char *from, register int size)
{
if (size > 0)
do
*to++ = *from++;
while (--size > 0);
}
void
copyw(register line *to, register line *from, register int size)
{
if (size > 0)
do
*to++ = *from++;
while (--size > 0);
}
void
copywR(register line *to, register line *from, register int size)
{
while (--size >= 0)
to[size] = from[size];
}
int
ctlof(int c)
{
return (c == DELETE ? '?' : c | ('A' - 1));
}
void
dingdong(void)
{
if (VB)
putpad(VB);
else if (value(ERRORBELLS))
putch('\207');
}
int
fixindent(int indent)
{
register int i;
register char *cp;
i = whitecnt(genbuf);
cp = vpastwh(genbuf);
if (*cp == 0 && i == indent && linebuf[0] == 0) {
genbuf[0] = 0;
return (i);
}
CP(genindent(i), cp);
return (i);
}
void
filioerr(char *cp)
{
register int oerrno = errno;
lprintf("\"%s\"", cp);
errno = oerrno;
syserror();
}
char *
genindent(register int indent)
{
register char *cp;
for (cp = genbuf; indent >= value(TABSTOP); indent -= value(TABSTOP))
*cp++ = '\t';
for (; indent > 0; indent--)
*cp++ = ' ';
return (cp);
}
void
getDOT(void)
{
getline(*dot);
}
line *
getmark(register int c)
{
register line *addr;
for (addr = one; addr <= dol; addr++)
if (names[c - 'a'] == (*addr &~ 01)) {
return (addr);
}
return (0);
}
int
getn(register char *cp)
{
register int i = 0;
while (isdigit(*cp&0377))
i = i * 10 + *cp++ - '0';
if (*cp)
return (0);
return (i);
}
void
ignnEOF(void)
{
register int c = getchar();
if (c == EOF)
ungetchar(c);
else if (c=='"')
comment();
}
int
is_white(int c)
{
#ifndef BIT8
return (c == ' ' || c == '\t');
#else
return (isspace(c&0377) && c != '\n' && c != '\r'
&& c != '\f' && c != '\v');
#endif
}
int
junk(register int c)
{
if (c && !value(BEAUTIFY))
return (0);
#ifndef BIT8
if (c >= ' ' && c != DELETE)
#else
if (printable(c))
#endif
return (0);
switch (c) {
case '\t':
case '\n':
case '\f':
return (0);
default:
return (1);
}
}
void
killed(void)
{
killcnt(addr2 - addr1 + 1);
}
void
killcnt(register int cnt)
{
if (inopen) {
notecnt = cnt;
notenam = notesgn = "";
return;
}
if (!notable(cnt))
return;
printf(catgets(catd, 1, 170, "%d lines"), cnt);
if (value(TERSE) == 0) {
printf(catgets(catd, 1, 171, " %c%s"),
Command[0] | ' ', Command + 1);
if (Command[strlen(Command) - 1] != 'e')
putchar('e');
putchar('d');
}
putNFL();
}
int
lineno(line *a)
{
return (a - zero);
}
int
lineDOL(void)
{
return (lineno(dol));
}
int
lineDOT(void)
{
return (lineno(dot));
}
void
markDOT(void)
{
markpr(dot);
}
void
markpr(line *which)
{
if ((inglobal == 0 || inopen) && which <= endcore) {
names['z'-'a'+1] = *which & ~01;
if (inopen)
ncols['z'-'a'+1] = cursor;
}
}
int
markreg(register int c)
{
if (c == '\'' || c == '`')
return ('z' + 1);
if (c >= 'a' && c <= 'z')
return (c);
return (0);
}
/*
* Mesg decodes the terse/verbose strings. Thus
* 'xxx@yyy' -> 'xxx' if terse, else 'xxx yyy'
* 'xxx|yyy' -> 'xxx' if terse, else 'yyy'
* All others map to themselves.
*/
char *
mesg(register char *str)
{
register char *cp;
str = strcpy(genbuf, str);
for (cp = str; *cp; cp++)
switch (*cp) {
case '@':
if (value(TERSE))
*cp = 0;
else
*cp = ' ';
break;
case '|':
if (value(TERSE) == 0)
return (cp + 1);
*cp = 0;
break;
}
return (str);
}
void
merror1(intptr_t seekpt)
{
#ifdef VMUNIX
strcpy(linebuf, (char *)seekpt);
#else
lseek(erfile, (off_t) seekpt, SEEK_SET);
if (read(erfile, linebuf, 128) < 2)
CP(linebuf, "ERROR");
#endif
}
/*VARARGS2*/
void
vmerror(char *seekpt, va_list ap)
{
register char *cp = linebuf;
if (seekpt == 0)
return;
merror1((intptr_t)seekpt);
if (*cp == '\n')
putnl(), cp++;
if (inopen > 0 && CE)
vclreol();
if (SO && SE)
putpad(SO);
vprintf(mesg(cp), ap);
if (SO && SE)
putpad(SE);
}
void
merror(char *cp, ...)
{
va_list ap;
if (cp == NULL)
return;
va_start(ap, cp);
vmerror(cp, ap);
va_end(ap);
}
int
morelines(void)
{
#ifdef _SC_PAGESIZE
static long pg;
if (pg == 0) {
pg = sysconf(_SC_PAGESIZE);
if (pg <= 0 || pg >= 65536)
pg = 4096;
pg /= sizeof (line);
}
if ((char *)sbrk(pg * sizeof (line)) == (char *)-1)
return (-1);
endcore += pg;
return (0);
#else /* !_SC_PAGESIZE */
if (sbrk(1024 * sizeof (line)) == (char *)-1)
return (-1);
endcore += 1024;
return (0);
#endif /* !_SC_PAGESIZE */
}
void
nonzero(void)
{
if (addr1 == zero) {
notempty();
error(catgets(catd, 1, 172,
"Nonzero address required@on this command"));
}
}
int
notable(int i)
{
return (hush == 0 && !inglobal && i > value(REPORT));
}
void
notempty(void)
{
if (dol == zero)
error(catgets(catd, 1, 173, "No lines@in the buffer"));
}
void
netchHAD(int cnt)
{
netchange(lineDOL() - cnt);
}
void
netchange(register int i)
{
register char *cp;
if (i > 0)
notesgn = cp = catgets(catd, 1, 174, "more ");
else
notesgn = cp = catgets(catd, 1, 175, "fewer "), i = -i;
if (inopen) {
notecnt = i;
notenam = catgets(catd, 1, 176, "");
return;
}
if (!notable(i))
return;
printf(mesg(catgets(catd, 1, 177, "%d %slines@in file after %s")),
i, cp, Command);
putNFL();
}
/*
* Print an escape sequence corresponding to c.
*/
#ifdef BIT8
int
printof(int c)
{
char *nums = "01234567";
int d;
#ifdef MB
if (mb_cur_max > 1 && (c & INVBIT) == 0 && c & ~0177) {
char mb[MB_LEN_MAX];
int i, n, x = EOF;
if ((n = wctomb(mb, c & TRIM)) <= 0) {
n = 1;
*mb = 0;
}
for (i = 0; i < n; i++) {
x = printof(mb[i] | INVBIT);
if (i+1 < n)
normchar(x);
}
return x;
}
#endif /* MB */
c &= 0377;
if (c < 040 || c == DELETE) {
normchar('^');
return (c == DELETE ? '?' : c | ('A' - 1));
}
normchar('\\');
normchar(nums[(c & ~077) >> 6]);
c &= 077;
d = c & 07;
if (c > d)
normchar(nums[(c - d) >> 3]);
else
normchar(nums[0]);
return nums[d];
}
#endif
void
putmark(line *addr)
{
putmk1(addr, putline());
}
void
putmk1(register line *addr, int n)
{
register line *markp;
register int oldglobmk;
oldglobmk = *addr & 1;
*addr &= ~1;
for (markp = (anymarks ? names : &names['z'-'a'+1]);
markp <= &names['z'-'a'+1]; markp++)
if (*markp == *addr)
*markp = n;
*addr = n | oldglobmk;
}
char *
plural(long i)
{
return (i == 1 ? catgets(catd, 1, 178, "")
: catgets(catd, 1, 179, "s"));
}
static short vcntcol;
int
qcolumn(register char *lim, register char *gp)
{
register int x = 0, n = 1;
int c, i;
int (*OO)();
OO = Outchar;
Outchar = qcount;
vcntcol = 0;
if (lim != NULL) {
if (lim < linebuf) {
lim = linebuf;
n = 0;
} else
n = skipright(linebuf, lim);
x = lim[n], lim[n] = 0;
}
pline(0);
if (lim != NULL)
lim[n] = x;
if (gp)
while (*gp) {
nextc(c, gp, i);
putchar(c);
gp += i;
}
Outchar = OO;
return (vcntcol);
}
int
qcount(int c)
{
if (c == '\t') {
vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP);
lastsc = 1;
return c;
}
/*
* Take account of filler characters inserted at the end of
* the visual line if a multi-column character does not fit.
*/
lastsc = colsc(c&TRIM&~MULTICOL);
while (vcntcol < WCOLS && vcntcol + lastsc - 1 >= WCOLS)
vcntcol++;
vcntcol += c & MULTICOL ? 1 : lastsc;
return c;
}
void
reverse(register line *a1, register line *a2)
{
register line t;
for (;;) {
t = *--a2;
if (a2 <= a1)
return;
*a2 = *a1;
*a1++ = t;
}
}
void
save(line *a1, register line *a2)
{
register int more;
if (!FIXUNDO)
return;
#ifdef TRACE
if (trace)
vudump("before save");
#endif
undkind = UNDNONE;
undadot = dot;
more = (a2 - a1 + 1) - (unddol - dol);
while (more > (endcore - truedol))
if (morelines() < 0)
error(catgets(catd, 1, 180,
"Out of memory@saving lines for undo - try using ed"));
if (more)
(*(more > 0 ? copywR : copyw))(unddol + more + 1, unddol + 1,
(truedol - unddol));
unddol += more;
truedol += more;
copyw(dol + 1, a1, a2 - a1 + 1);
undkind = UNDALL;
unddel = a1 - 1;
undap1 = a1;
undap2 = a2 + 1;
#ifdef TRACE
if (trace)
vudump("after save");
#endif
}
void
save12(void)
{
save(addr1, addr2);
}
void
saveall(void)
{
save(one, dol);
}
int
span(void)
{
return (addr2 - addr1 + 1);
}
void
synced(void)
{
chng = 0;
tchng = 0;
xchng = 0;
}
int
skipwh(void)
{
register int wh;
wh = 0;
while (is_white(peekchar())) {
wh++;
ignchar();
}
return (wh);
}
void
vsmerror(char *seekpt, va_list ap)
{
if (seekpt == 0)
return;
merror1((intptr_t)seekpt);
if (inopen && CE)
vclreol();
if (SO && SE)
putpad(SO);
vlprintf(mesg(linebuf), ap);
if (SO && SE)
putpad(SE);
}
void
smerror(char *seekpt, ...)
{
va_list ap;
if (seekpt == NULL)
return;
va_start(ap, seekpt);
vsmerror(seekpt, ap);
va_end(ap);
}
char *
strend(register char *cp)
{
while (*cp)
cp++;
return (cp);
}
void
strcLIN(char *dp)
{
CP(linebuf, dp);
}
void
syserror(void)
{
dirtcnt = 0;
putchar(' ');
error("%s", strerror(errno));
}
/*
* Return the column number that results from being in column col and
* hitting a tab, where tabs are set every ts columns. Work right for
* the case where col > TCOLUMNS, even if ts does not divide TCOLUMNS.
*/
int
tabcol(int col, int ts)
{
int offset, result;
if (col >= TCOLUMNS) {
offset = TCOLUMNS * (col/TCOLUMNS);
col -= offset;
} else
offset = 0;
result = col + ts - (col % ts) + offset;
return (result);
}
char *
vfindcol(int i)
{
register char *cp;
register int (*OO)() = Outchar;
int c, n = 0;
Outchar = qcount;
ignore(qcolumn(linebuf - 1, NOSTR));
for (cp = linebuf; *cp && vcntcol < i; cp += n) {
nextc(c, cp, n);
putchar(c);
}
if (cp != linebuf)
cp -= n;
Outchar = OO;
return (cp);
}
char *
vskipwh(register char *cp)
{
while (is_white(*cp) && cp[1])
cp++;
return (cp);
}
char *
vpastwh(register char *cp)
{
while (is_white(*cp))
cp++;
return (cp);
}
int
whitecnt(register char *cp)
{
register int i;
i = 0;
for (;;)
switch (*cp++) {
case '\t':
i += value(TABSTOP) - i % value(TABSTOP);
break;
case ' ':
i++;
break;
default:
return (i);
}
}
void
markit(line *addr)
{
if (addr != dot && addr >= one && addr <= dol)
markDOT();
}
#ifdef SIGEMT
/*
* The following code is defensive programming against a bug in the
* pdp-11 overlay implementation. Sometimes it goes nuts and asks
* for an overlay with some garbage number, which generates an emt
* trap. This is a less than elegant solution, but it is somewhat
* better than core dumping and losing your work, leaving your tty
* in a weird state, etc.
*/
int _ovno;
void
onemt(int signum)
{
int oovno;
oovno = _ovno;
/* 2 and 3 are valid on 11/40 type vi, so */
if (_ovno < 0 || _ovno > 3)
_ovno = 0;
error(catgets(catd, 1, 181, "emt trap, _ovno is %d @ - try again"));
}
#endif
/*
* When a hangup occurs our actions are similar to a preserve
* command. If the buffer has not been [Modified], then we do
* nothing but remove the temporary files and exit.
* Otherwise, we sync the temp file and then attempt a preserve.
* If the preserve succeeds, we unlink our temp files.
* If the preserve fails, we leave the temp files as they are
* as they are a backup even without preservation if they
* are not removed.
*/
void
onhup(int signum)
{
/*
* USG tty driver can send multiple HUP's!!
*/
signal(SIGINT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
if (chng == 0) {
cleanup(1);
exitex(0);
}
if (setexit() == 0) {
if (preserve()) {
cleanup(1);
exitex(0);
}
}
exitex(1);
}
/*
* An interrupt occurred. Drain any output which
* is still in the output buffering pipeline.
* Catch interrupts again. Unless we are in visual
* reset the output state (out of -nl mode, e.g).
* Then like a normal error (with the \n before Interrupt
* suppressed in visual mode).
*/
void
onintr(int signum)
{
alarm(0); /* in case we were called from map */
draino();
if (!inopen) {
pstop();
setlastchar('\n');
}
error(catgets(catd, 1, 182, "\nInterrupt") + inopen);
}
/*
* If we are interruptible, enable interrupts again.
* In some critical sections we turn interrupts off,
* but not very often.
*/
void
setrupt(void)
{
if (ruptible) {
signal(SIGINT, inopen ? vintr : onintr);
#ifdef SIGTSTP
if (dosusp)
signal(SIGTSTP, onsusp);
#endif
}
}
int
preserve(void)
{
#ifdef INCORB
tflush();
#endif
synctmp();
pid = fork();
if (pid < 0)
return (0);
if (pid == 0) {
close(0);
dup(tfile);
execl(EXPRESERVE, "expreserve", (char *)0);
exitex(1);
}
waitfor();
if (rpid == pid && status == 0)
return (1);
return (0);
}
int
exitex(int i)
{
# ifdef TRACE
if (trace)
fclose(trace);
# endif
if (failed != 0 && i == 0)
i = failed;
_exit(i);
/*NOTREACHED*/
return 0;
}
#ifdef SIGTSTP
/*
* We have just gotten a susp. Suspend and prepare to resume.
*/
void
onsusp(int signum)
{
struct termios f;
/* int omask; */
#ifdef TIOCGWINSZ
struct winsize win;
#endif
sigset_t set;
f = setty(normf);
vnfl();
putpad(TE);
flush();
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
signal(SIGTSTP, SIG_DFL);
kill(0, SIGTSTP);
/* the pc stops here */
signal(SIGTSTP, onsusp);
vcontin(0);
setty(f);
if (!inopen)
error(0);
#ifdef TIOCGWINSZ
else {
if (ioctl(0, TIOCGWINSZ, &win) >= 0)
if (win.ws_row != winsz.ws_row ||
win.ws_col != winsz.ws_col)
onwinch(SIGWINCH);
if (vcnt < 0) {
vcnt = -vcnt;
if (state == VISUAL)
vclear();
else if (state == CRTOPEN)
vcnt = 0;
}
vdirty(0, TLINES);
vrepaint(cursor);
}
#endif /* TIOCGWINSZ */
}
#endif /* SIGTSTP */
/*
* For regular strcpy(), source and destination may not overlap.
*/
char *
movestr(char *s1, const char *s2)
{
char *cp = s1;
while (*s1++ = *s2++);
return cp;
}
/*
* strcpy() checking the maximum size of s1, printing msg in case of overflow.
*/
char *
safecp(char *s1, const char *s2, size_t max, char *msg, ...)
{
va_list ap;
char *cp = s1;
while (max--)
if ((*s1++ = *s2++) == '\0')
return cp;
va_start(ap, msg);
verror(msg, ap);
va_end(ap);
exitex(0175);
/*NOTREACHED*/
return NULL;
}
/*
* strcat() checking the maximum size of s1, printing msg in case of overflow.
*/
char *
safecat(char *s1, const char *s2, size_t max, char *msg, ...)
{
va_list ap;
char *cp = s1;
while (max && *s1)
max--, s1++;
while (max--)
if ((*s1++ = *s2++) == '\0')
return cp;
va_start(ap, msg);
verror(msg, ap);
va_end(ap);
exitex(0175);
/*NOTREACHED*/
return NULL;
}