mirror of https://github.com/tildeclub/ex-vi.git
1126 lines
23 KiB
C
1126 lines
23 KiB
C
/*
|
|
* 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_io.c 1.40 (gritter) 2/17/05";
|
|
#endif
|
|
#endif
|
|
|
|
/* from ex_io.c 7.11.1.1 (Berkeley) 8/12/86 */
|
|
|
|
#include "ex.h"
|
|
#include "ex_argv.h"
|
|
#include "ex_temp.h"
|
|
#include "ex_tty.h"
|
|
#include "ex_vis.h"
|
|
|
|
/*
|
|
* File input/output, source, preserve and recover
|
|
*/
|
|
|
|
/*
|
|
* Following remember where . was in the previous file for return
|
|
* on file switching.
|
|
*/
|
|
int altdot;
|
|
int oldadot;
|
|
bool wasalt;
|
|
short isalt;
|
|
|
|
long cntch; /* Count of characters on unit io */
|
|
#ifndef VMUNIX
|
|
short cntln; /* Count of lines " */
|
|
#else
|
|
int cntln;
|
|
#endif
|
|
long cntnull; /* Count of nulls " */
|
|
#ifndef BIT8
|
|
long cntodd; /* Count of non-ascii characters " */
|
|
#endif
|
|
|
|
/*
|
|
* Parse file name for command encoded by comm.
|
|
* If comm is E then command is doomed and we are
|
|
* parsing just so user won't have to retype the name.
|
|
*/
|
|
void
|
|
filename(int comm)
|
|
{
|
|
register int c = comm, d;
|
|
register int i;
|
|
|
|
d = getchar();
|
|
if (endcmd(d)) {
|
|
if (savedfile[0] == 0 && comm != 'f')
|
|
error(catgets(catd, 1, 72,
|
|
"No file|No current filename"));
|
|
CP(file, savedfile);
|
|
wasalt = (isalt > 0) ? isalt-1 : 0;
|
|
isalt = 0;
|
|
oldadot = altdot;
|
|
if (c == 'e' || c == 'E')
|
|
altdot = lineDOT();
|
|
if (d == EOF)
|
|
ungetchar(d);
|
|
} else {
|
|
ungetchar(d);
|
|
getone();
|
|
eol();
|
|
if (savedfile[0] == 0 && c != 'E' && c != 'e') {
|
|
c = 'e';
|
|
edited = 0;
|
|
}
|
|
wasalt = strcmp(file, altfile) == 0;
|
|
oldadot = altdot;
|
|
switch (c) {
|
|
|
|
case 'f':
|
|
edited = 0;
|
|
/* fall into ... */
|
|
|
|
case 'e':
|
|
if (savedfile[0]) {
|
|
altdot = lineDOT();
|
|
CP(altfile, savedfile);
|
|
}
|
|
CP(savedfile, file);
|
|
break;
|
|
|
|
default:
|
|
if (file[0]) {
|
|
if (c != 'E')
|
|
altdot = lineDOT();
|
|
CP(altfile, file);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (hush && comm != 'f' || comm == 'E')
|
|
return;
|
|
if (file[0] != 0) {
|
|
lprintf("\"%s\"", file);
|
|
if (comm == 'f') {
|
|
if (value(READONLY))
|
|
printf(catgets(catd, 1, 73, " [Read only]"));
|
|
if (!edited)
|
|
printf(catgets(catd, 1, 74, " [Not edited]"));
|
|
if (tchng)
|
|
printf(catgets(catd, 1, 75, " [Modified]"));
|
|
}
|
|
flush();
|
|
} else
|
|
printf(catgets(catd, 1, 76, "No file "));
|
|
if (comm == 'f') {
|
|
if (!(i = lineDOL()))
|
|
i++;
|
|
printf(catgets(catd, 1, 77,
|
|
" line %d of %d --%ld%%--"), lineDOT(), lineDOL(),
|
|
(long) 100 * lineDOT() / i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the argument words for a command into genbuf
|
|
* expanding # and %.
|
|
*/
|
|
int
|
|
getargs(void)
|
|
{
|
|
register int c;
|
|
register char *cp, *fp;
|
|
static char fpatbuf[32]; /* hence limit on :next +/pat */
|
|
|
|
pastwh();
|
|
if (peekchar() == '+') {
|
|
for (cp = fpatbuf;;) {
|
|
c = *cp++ = getchar();
|
|
if (cp >= &fpatbuf[sizeof(fpatbuf)])
|
|
error(catgets(catd, 1, 78, "Pattern too long"));
|
|
if (c == '\\' && isspace(peekchar()))
|
|
c = getchar();
|
|
if (c == EOF || isspace(c)) {
|
|
ungetchar(c);
|
|
*--cp = 0;
|
|
firstpat = &fpatbuf[1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (skipend())
|
|
return (0);
|
|
CP(genbuf, "echo "); cp = &genbuf[5];
|
|
for (;;) {
|
|
c = getchar();
|
|
if (endcmd(c)) {
|
|
ungetchar(c);
|
|
break;
|
|
}
|
|
switch (c) {
|
|
|
|
case '\\':
|
|
if (any(peekchar(), "#%|"))
|
|
c = getchar();
|
|
/* fall into... */
|
|
|
|
default:
|
|
if (cp > &genbuf[LBSIZE - 2])
|
|
flong:
|
|
error(catgets(catd, 1, 79,
|
|
"Argument buffer overflow"));
|
|
*cp++ = c;
|
|
break;
|
|
|
|
case '#':
|
|
fp = altfile;
|
|
if (*fp == 0)
|
|
error(catgets(catd, 1, 80,
|
|
"No alternate filename@to substitute for #"));
|
|
goto filexp;
|
|
|
|
case '%':
|
|
fp = savedfile;
|
|
if (*fp == 0)
|
|
error(catgets(catd, 1, 81,
|
|
"No current filename@to substitute for %%"));
|
|
filexp:
|
|
while (*fp) {
|
|
if (cp > &genbuf[LBSIZE - 2])
|
|
goto flong;
|
|
*cp++ = *fp++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
*cp = 0;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Scan genbuf for shell metacharacters.
|
|
* Set is union of v7 shell and csh metas.
|
|
*/
|
|
int
|
|
gscan(void)
|
|
{
|
|
register char *cp;
|
|
|
|
for (cp = genbuf; *cp; cp++)
|
|
if (any(*cp, "~{[*?$`'\"\\"))
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Glob the argument words in genbuf, or if no globbing
|
|
* is implied, just split them up directly.
|
|
*/
|
|
void
|
|
gglob(struct glob *gp)
|
|
{
|
|
int pvec[2];
|
|
register char **argv = gp->argv;
|
|
register char *cp = gp->argspac;
|
|
register int c;
|
|
char ch;
|
|
int nleft = NCARGS;
|
|
|
|
gp->argc0 = 0;
|
|
if (gscan() == 0) {
|
|
register char *v = genbuf + 5; /* strlen("echo ") */
|
|
|
|
for (;;) {
|
|
while (isspace(*v&0377))
|
|
v++;
|
|
if (!*v)
|
|
break;
|
|
*argv++ = cp;
|
|
while (*v && !isspace(*v&0377))
|
|
*cp++ = *v++;
|
|
*cp++ = 0;
|
|
gp->argc0++;
|
|
}
|
|
*argv = 0;
|
|
return;
|
|
}
|
|
if (pipe(pvec) < 0)
|
|
error(catgets(catd, 1, 82, "Can't make pipe to glob"));
|
|
pid = fork();
|
|
io = pvec[0];
|
|
if (pid < 0) {
|
|
close(pvec[1]);
|
|
error(catgets(catd, 1, 83, "Can't fork to do glob"));
|
|
}
|
|
if (pid == 0) {
|
|
int oerrno;
|
|
|
|
close(1);
|
|
dup(pvec[1]);
|
|
close(pvec[0]);
|
|
close(2); /* so errors don't mess up the screen */
|
|
open("/dev/null", O_WRONLY);
|
|
execl(svalue(SHELL), "sh", "-c", genbuf, (char *)0);
|
|
oerrno = errno; close(1); dup(2); errno = oerrno;
|
|
filioerr(svalue(SHELL));
|
|
}
|
|
close(pvec[1]);
|
|
do {
|
|
*argv = cp;
|
|
for (;;) {
|
|
if (read(io, &ch, 1) != 1) {
|
|
close(io);
|
|
c = -1;
|
|
} else
|
|
c = ch & TRIM;
|
|
if (c <= 0 || isspace(c))
|
|
break;
|
|
*cp++ = c;
|
|
if (--nleft <= 0)
|
|
error(catgets(catd, 1, 84,
|
|
"Arg list too long"));
|
|
}
|
|
if (cp != *argv) {
|
|
--nleft;
|
|
*cp++ = 0;
|
|
gp->argc0++;
|
|
if (gp->argc0 >= NARGS)
|
|
error(catgets(catd, 1, 85,
|
|
"Arg list too long"));
|
|
argv++;
|
|
}
|
|
} while (c >= 0);
|
|
waitfor();
|
|
if (gp->argc0 == 0)
|
|
error(catgets(catd, 1, 86, "No match"));
|
|
}
|
|
|
|
/*
|
|
* Parse one filename into file.
|
|
*/
|
|
struct glob G;
|
|
void
|
|
getone(void)
|
|
{
|
|
register char *str;
|
|
|
|
if (getargs() == 0)
|
|
missing:
|
|
error(catgets(catd, 1, 87, "Missing filename"));
|
|
gglob(&G);
|
|
if (G.argc0 > 1)
|
|
error(catgets(catd, 1, 88, "Ambiguous|Too many file names"));
|
|
if ((str = G.argv[G.argc0 - 1]) == NULL)
|
|
goto missing;
|
|
if (strlen(str) > FNSIZE - 4)
|
|
error(catgets(catd, 1, 89, "Filename too long"));
|
|
/* samef: */
|
|
CP(file, str);
|
|
}
|
|
|
|
/*
|
|
* Are these two really the same inode?
|
|
*/
|
|
int
|
|
samei(struct stat *sp, char *cp)
|
|
{
|
|
struct stat stb;
|
|
|
|
if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
|
|
return (0);
|
|
return (sp->st_ino == stb.st_ino);
|
|
}
|
|
|
|
/*
|
|
* Read a file from the world.
|
|
* C is command, 'e' if this really an edit (or a recover).
|
|
*/
|
|
void
|
|
rop(int c)
|
|
{
|
|
register int i;
|
|
struct stat stbuf;
|
|
char magic[4];
|
|
static int ovro; /* old value(READONLY) */
|
|
static int denied; /* 1 if READONLY was set due to file permissions */
|
|
|
|
io = open(file, O_RDONLY);
|
|
if (io < 0) {
|
|
if (c == 'e' && errno == ENOENT) {
|
|
edited++;
|
|
/*
|
|
* If the user just did "ex foo" he is probably
|
|
* creating a new file. Don't be an error, since
|
|
* this is ugly, and it screws up the + option.
|
|
*
|
|
* POSIX.2 specifies that this be done for all
|
|
* "edit" commands, not just for the first one.
|
|
*/
|
|
if (1 || !seenprompt) {
|
|
printf(catgets(catd, 1, 90, " [New file]"));
|
|
noonl();
|
|
return;
|
|
}
|
|
}
|
|
failed = 1;
|
|
syserror();
|
|
}
|
|
if (fstat(io, &stbuf))
|
|
syserror();
|
|
switch (stbuf.st_mode & S_IFMT) {
|
|
|
|
case S_IFBLK:
|
|
error(catgets(catd, 1, 91, " Block special file"));
|
|
|
|
case S_IFCHR:
|
|
if (isatty(io))
|
|
error(catgets(catd, 1, 92, " Teletype"));
|
|
if (samei(&stbuf, "/dev/null"))
|
|
break;
|
|
error(catgets(catd, 1, 93, " Character special file"));
|
|
|
|
case S_IFDIR:
|
|
error(catgets(catd, 1, 94, " Directory"));
|
|
|
|
#ifdef S_IFSOCK
|
|
case S_IFSOCK:
|
|
error(catgets(catd, 1, 95, " Socket"));
|
|
#endif
|
|
#ifdef S_IFIFO
|
|
case S_IFIFO:
|
|
error(catgets(catd, 1, 96, " Named pipe"));
|
|
#endif
|
|
|
|
case S_IFREG:
|
|
/*
|
|
* The magics are checked byte-wise now to avoid
|
|
* endianness problems. Some quite old types
|
|
* were omitted.
|
|
*
|
|
* Feel free too add more magics here, but do not
|
|
* make this a copy of the `file' program.
|
|
*
|
|
* GR
|
|
*/
|
|
i = read(io, magic, sizeof(magic));
|
|
lseek(io, (off_t) 0, SEEK_SET);
|
|
if (i != sizeof(magic))
|
|
break;
|
|
switch (magic[0]&0377) {
|
|
|
|
case 01: /* big endian a.out */
|
|
if (magic[1] != 05 && magic[1] != 07
|
|
&& magic[1] != 010 && magic[1] != 011
|
|
&& magic[1] != 013 && magic[1] != 030
|
|
&& magic[1] != 031)
|
|
break;
|
|
goto is_exec;
|
|
case 0314: /* Linux/ia32 QMAGIC */
|
|
if (magic[1] != 0 || magic[2] != 0144)
|
|
break;
|
|
goto is_exec;
|
|
case 05: /* data overlay on exec */
|
|
case 07: /* unshared */
|
|
case 010: /* shared text */
|
|
case 011: /* separate I/D */
|
|
case 013: /* VM/Unix demand paged */
|
|
case 030: /* PDP-11 Overlay shared */
|
|
case 031: /* PDP-11 Overlay sep I/D */
|
|
if (magic[1] == 01)
|
|
is_exec:
|
|
error(catgets(catd, 1, 97, " Executable"));
|
|
break;
|
|
|
|
case 037:
|
|
switch (magic[1]&0377) {
|
|
case 036: /* pack */
|
|
case 037: /* compact */
|
|
case 0235: /* compress */
|
|
case 0213: /* gzip */
|
|
/*
|
|
* We omit bzip2 here since it has
|
|
* an ASCII header.
|
|
*/
|
|
error(catgets(catd, 1, 98, " Compressed Data"));
|
|
}
|
|
break;
|
|
|
|
case 0177:
|
|
if (magic[1] == 'E' && magic[2] == 'L'
|
|
&& magic[3] == 'F')
|
|
error(catgets(catd, 1, 99, " ELF object"));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef notdef
|
|
/*
|
|
* We do not forbid the editing of portable archives
|
|
* because it is reasonable to edit them, especially
|
|
* if they are archives of text files. This is
|
|
* especially useful if you archive source files together
|
|
* and copy them to another system with ~%take, since
|
|
* the files sometimes show up munged and must be fixed.
|
|
*/
|
|
case 0177545:
|
|
case 0177555:
|
|
error(catgets(catd, 1, 100, " Archive"));
|
|
|
|
default:
|
|
#ifdef mbb
|
|
/* C/70 has a 10 bit byte */
|
|
if (magic & 03401600)
|
|
#else
|
|
/* Everybody else has an 8 bit byte */
|
|
if (magic & 0100200)
|
|
#endif
|
|
error(catgets(catd, 1, 101, " Non-ascii file"));
|
|
break;
|
|
}
|
|
#endif /* notdef */
|
|
}
|
|
if (c != 'r') {
|
|
if (value(READONLY) && denied) {
|
|
value(READONLY) = ovro;
|
|
denied = 0;
|
|
}
|
|
if ((stbuf.st_mode & 0222) == 0 || access(file, W_OK) < 0) {
|
|
ovro = value(READONLY);
|
|
denied = 1;
|
|
value(READONLY) = 1;
|
|
}
|
|
}
|
|
if (value(READONLY) && !hush) {
|
|
printf(catgets(catd, 1, 102, " [Read only]"));
|
|
flush();
|
|
}
|
|
if (c == 'r')
|
|
setdot();
|
|
else
|
|
setall();
|
|
if (FIXUNDO && inopen && c == 'r')
|
|
undap1 = undap2 = addr1 + 1;
|
|
rop2();
|
|
rop3(c);
|
|
}
|
|
|
|
void
|
|
rop2(void)
|
|
{
|
|
line *first, *last, *a;
|
|
struct stat statb;
|
|
|
|
deletenone();
|
|
clrstats();
|
|
first = addr2 + 1;
|
|
if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
|
|
bsize = LBSIZE;
|
|
else {
|
|
bsize = statb.st_blksize;
|
|
if (bsize <= 0)
|
|
bsize = LBSIZE;
|
|
}
|
|
ignore(append(getfile, addr2));
|
|
last = dot;
|
|
/*
|
|
* if the modelines variable is set,
|
|
* check the first and last five lines of the file
|
|
* for a mode line.
|
|
*/
|
|
if (value(MODELINES)) {
|
|
for (a=first; a<=last; a++) {
|
|
if (a==first+5 && last-first > 10)
|
|
a = last - 4;
|
|
getline(*a);
|
|
checkmodeline(linebuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Io is finished, close the unit and print statistics.
|
|
*/
|
|
int
|
|
iostats(void)
|
|
{
|
|
|
|
fsync(io);
|
|
close(io);
|
|
io = -1;
|
|
if (hush == 0) {
|
|
if (value(TERSE))
|
|
printf(catgets(catd, 1, 103,
|
|
" %d/%d"), cntln, (int)cntch);
|
|
else
|
|
printf(catgets(catd, 1, 104,
|
|
" %d line%s, %d character%s"), cntln, plural((long) cntln),
|
|
(int)cntch, plural((int)cntch));
|
|
if (cntnull
|
|
#ifndef BIT8
|
|
|| cntodd
|
|
#endif
|
|
) {
|
|
printf(catgets(catd, 1, 105, " ("));
|
|
if (cntnull) {
|
|
printf(catgets(catd, 1, 106,
|
|
"%d null"), (int)cntnull);
|
|
#ifndef BIT8
|
|
if (cntodd)
|
|
printf(catgets(catd, 1, 107, ", "));
|
|
#endif
|
|
}
|
|
#ifndef BIT8
|
|
if (cntodd)
|
|
printf(catgets(catd, 1, 108,
|
|
"%d non-ASCII"), (int)cntodd);
|
|
#endif
|
|
putchar(')');
|
|
}
|
|
noonl();
|
|
flush();
|
|
}
|
|
return (cntnull != 0
|
|
#ifndef BIT8
|
|
|| cntodd != 0
|
|
#endif
|
|
);
|
|
}
|
|
|
|
void
|
|
rop3(int c)
|
|
{
|
|
|
|
if (iostats() == 0 && c == 'e')
|
|
edited++;
|
|
if (c == 'e') {
|
|
if (wasalt || firstpat) {
|
|
register line *addr = zero + oldadot;
|
|
|
|
if (addr > dol)
|
|
addr = dol;
|
|
if (firstpat) {
|
|
globp = (*firstpat) ? firstpat : "$";
|
|
commands(1,1);
|
|
firstpat = 0;
|
|
} else if (addr >= one) {
|
|
if (inopen)
|
|
dot = addr;
|
|
markpr(addr);
|
|
} else
|
|
goto other;
|
|
} else
|
|
other:
|
|
if (dol > zero) {
|
|
if (inopen)
|
|
dot = one;
|
|
markpr(one);
|
|
}
|
|
if(FIXUNDO)
|
|
undkind = UNDNONE;
|
|
if (inopen) {
|
|
vcline = 0;
|
|
vreplace(0, TLINES, lineDOL());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Returns from edited() */
|
|
#define EDF 0 /* Edited file */
|
|
#define NOTEDF -1 /* Not edited file */
|
|
#define PARTBUF 1 /* Write of partial buffer to Edited file */
|
|
|
|
/*
|
|
* Is file the edited file?
|
|
* Work here is that it is not considered edited
|
|
* if this is a partial buffer, and distinguish
|
|
* all cases.
|
|
*/
|
|
int
|
|
edfile(void)
|
|
{
|
|
|
|
if (!edited || !eq(file, savedfile))
|
|
return (NOTEDF);
|
|
return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
|
|
}
|
|
|
|
/*
|
|
* Write a file.
|
|
*/
|
|
void
|
|
wop(bool dofname)
|
|
/*bool dofname; /\* if 1 call filename, else use savedfile */
|
|
{
|
|
register int c, exclam, nonexist;
|
|
line *saddr1 = NULL, *saddr2 = NULL;
|
|
struct stat stbuf;
|
|
|
|
c = 0;
|
|
exclam = 0;
|
|
if (dofname) {
|
|
if (peekchar() == '!')
|
|
exclam++, ignchar();
|
|
ignore(skipwh());
|
|
while (peekchar() == '>')
|
|
ignchar(), c++, ignore(skipwh());
|
|
if (c != 0 && c != 2)
|
|
error(catgets(catd, 1, 109,
|
|
"Write forms are 'w' and 'w>>'"));
|
|
filename('w');
|
|
} else {
|
|
if (savedfile[0] == 0)
|
|
error(catgets(catd, 1, 110,
|
|
"No file|No current filename"));
|
|
saddr1=addr1;
|
|
saddr2=addr2;
|
|
addr1=one;
|
|
addr2=dol;
|
|
CP(file, savedfile);
|
|
if (inopen) {
|
|
vclrech(0);
|
|
splitw++;
|
|
}
|
|
lprintf("\"%s\"", file);
|
|
}
|
|
nonexist = stat(file, &stbuf);
|
|
switch (c) {
|
|
|
|
case 0:
|
|
if (!exclam && (!value(WRITEANY) || value(READONLY)))
|
|
switch (edfile()) {
|
|
|
|
case NOTEDF:
|
|
if (nonexist)
|
|
break;
|
|
if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
|
|
if (samei(&stbuf, "/dev/null"))
|
|
break;
|
|
if (samei(&stbuf, "/dev/tty"))
|
|
break;
|
|
}
|
|
io = open(file, O_WRONLY);
|
|
if (io < 0)
|
|
syserror();
|
|
if (!isatty(io))
|
|
serror(catgets(catd, 1, 111,
|
|
" File exists| File exists - use \"w! %s\" to overwrite"), file);
|
|
close(io);
|
|
break;
|
|
|
|
case EDF:
|
|
if (value(READONLY))
|
|
error(catgets(catd, 1, 112,
|
|
" File is read only"));
|
|
break;
|
|
|
|
case PARTBUF:
|
|
if (value(READONLY))
|
|
error(catgets(catd, 1, 113,
|
|
" File is read only"));
|
|
error(catgets(catd, 1, 114,
|
|
" Use \"w!\" to write partial buffer"));
|
|
}
|
|
cre:
|
|
/*
|
|
synctmp();
|
|
*/
|
|
io = creat(file, 0666);
|
|
if (io < 0)
|
|
syserror();
|
|
writing = 1;
|
|
if (hush == 0)
|
|
if (nonexist)
|
|
printf(catgets(catd, 1, 115, " [New file]"));
|
|
else if (value(WRITEANY) && edfile() != EDF)
|
|
printf(catgets(catd, 1, 116,
|
|
" [Existing file]"));
|
|
break;
|
|
|
|
case 2:
|
|
io = open(file, O_WRONLY);
|
|
if (io < 0) {
|
|
if (exclam || value(WRITEANY))
|
|
goto cre;
|
|
syserror();
|
|
}
|
|
lseek(io, (off_t) 0, SEEK_END);
|
|
break;
|
|
}
|
|
putfile(0);
|
|
ignore(iostats());
|
|
if (c != 2 && addr1 == one && addr2 == dol) {
|
|
if (eq(file, savedfile))
|
|
edited = 1;
|
|
synced();
|
|
}
|
|
if (!dofname) {
|
|
addr1 = saddr1;
|
|
addr2 = saddr2;
|
|
}
|
|
writing = 0;
|
|
}
|
|
|
|
/*
|
|
* Extract the next line from the io stream.
|
|
*/
|
|
char *nextip;
|
|
|
|
int
|
|
getfile(void)
|
|
{
|
|
register short c;
|
|
register char *lp, *fp;
|
|
|
|
lp = linebuf;
|
|
fp = nextip;
|
|
do {
|
|
if (--ninbuf < 0) {
|
|
ninbuf = read(io, genbuf, bsize) - 1;
|
|
if (ninbuf < 0) {
|
|
if (lp != linebuf) {
|
|
lp++;
|
|
printf(catgets(catd, 1, 117,
|
|
" [Incomplete last line]"));
|
|
break;
|
|
}
|
|
return (EOF);
|
|
}
|
|
fp = genbuf;
|
|
cntch += ninbuf+1;
|
|
}
|
|
if (lp >= &linebuf[LBSIZE]) {
|
|
synced();
|
|
error(catgets(catd, 1, 118, " Line too long"));
|
|
}
|
|
c = *fp++;
|
|
if (c == 0) {
|
|
cntnull++;
|
|
#ifndef BIT8
|
|
continue;
|
|
#else
|
|
c = 0200;
|
|
#endif
|
|
}
|
|
#ifndef BIT8
|
|
if (c & QUOTE) {
|
|
cntodd++;
|
|
c &= TRIM;
|
|
if (c == 0)
|
|
continue;
|
|
}
|
|
#endif
|
|
*lp++ = c;
|
|
} while (c != '\n');
|
|
*--lp = 0;
|
|
nextip = fp;
|
|
cntln++;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Write a range onto the io stream.
|
|
*/
|
|
void
|
|
putfile(int isfilter)
|
|
{
|
|
line *a1;
|
|
register char *fp, *lp;
|
|
register int nib;
|
|
struct stat statb;
|
|
|
|
a1 = addr1;
|
|
clrstats();
|
|
cntln = fixedzero ? 0 : addr2 - a1 + 1;
|
|
if (cntln == 0 || fixedzero)
|
|
return;
|
|
if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
|
|
bsize = LBSIZE;
|
|
else {
|
|
bsize = statb.st_blksize;
|
|
if (bsize <= 0)
|
|
bsize = LBSIZE;
|
|
}
|
|
nib = bsize;
|
|
fp = genbuf;
|
|
do {
|
|
getline(*a1++);
|
|
lp = linebuf;
|
|
for (;;) {
|
|
if (--nib < 0) {
|
|
nib = fp - genbuf;
|
|
if (write(io, genbuf, nib) != nib) {
|
|
wrerror();
|
|
}
|
|
cntch += nib;
|
|
nib = bsize - 1;
|
|
fp = genbuf;
|
|
}
|
|
if ((*fp++ = *lp++) == 0) {
|
|
fp[-1] = '\n';
|
|
break;
|
|
}
|
|
}
|
|
} while (a1 <= addr2);
|
|
nib = fp - genbuf;
|
|
if (write(io, genbuf, nib) != nib) {
|
|
wrerror();
|
|
}
|
|
cntch += nib;
|
|
}
|
|
|
|
/*
|
|
* A write error has occurred; if the file being written was
|
|
* the edited file then we consider it to have changed since it is
|
|
* now likely scrambled.
|
|
*/
|
|
void
|
|
wrerror(void)
|
|
{
|
|
|
|
if (eq(file, savedfile) && edited)
|
|
change();
|
|
syserror();
|
|
}
|
|
|
|
/*
|
|
* Source command, handles nested sources.
|
|
* Traps errors since it mungs unit 0 during the source.
|
|
*/
|
|
short slevel;
|
|
short ttyindes;
|
|
|
|
void
|
|
source(char *fil, bool okfail)
|
|
{
|
|
JMP_BUF osetexit;
|
|
register int saveinp, ointty, oerrno;
|
|
char *saveglobp, *saveinput;
|
|
char saveinline[BUFSIZ];
|
|
int savepeekc, savelastc;
|
|
|
|
signal(SIGINT, SIG_IGN);
|
|
saveinp = dup(0);
|
|
savepeekc = peekc;
|
|
savelastc = lastc;
|
|
saveglobp = globp;
|
|
saveinput = input;
|
|
if (input)
|
|
strcpy(saveinline, input);
|
|
peekc = 0; lastc = 0; globp = 0; input = 0;
|
|
if (saveinp < 0)
|
|
error(catgets(catd, 1, 119, "Too many nested sources"));
|
|
if (slevel <= 0)
|
|
ttyindes = saveinp;
|
|
close(0);
|
|
if (open(fil, O_RDONLY) < 0) {
|
|
oerrno = errno;
|
|
setrupt();
|
|
dup(saveinp);
|
|
close(saveinp);
|
|
input = saveinput;
|
|
if (input)
|
|
strcpy(input, saveinline);
|
|
lastc = savelastc;
|
|
errno = oerrno;
|
|
if (!okfail)
|
|
filioerr(fil);
|
|
return;
|
|
}
|
|
slevel++;
|
|
ointty = intty;
|
|
intty = isatty(0);
|
|
oprompt = value(PROMPT);
|
|
value(PROMPT) &= intty;
|
|
getexit(osetexit);
|
|
setrupt();
|
|
if (setexit() == 0)
|
|
commands(1, 1);
|
|
else if (slevel > 1) {
|
|
close(0);
|
|
dup(saveinp);
|
|
close(saveinp);
|
|
input = saveinput;
|
|
if (input)
|
|
strcpy(input, saveinline);
|
|
lastc = savelastc;
|
|
slevel--;
|
|
resexit(osetexit);
|
|
reset();
|
|
}
|
|
intty = ointty;
|
|
value(PROMPT) = oprompt;
|
|
close(0);
|
|
dup(saveinp);
|
|
close(saveinp);
|
|
globp = saveglobp;
|
|
input = saveinput;
|
|
if (input)
|
|
strcpy(input, saveinline);
|
|
peekc = savepeekc;
|
|
lastc = savelastc;
|
|
slevel--;
|
|
resexit(osetexit);
|
|
}
|
|
|
|
/*
|
|
* Clear io statistics before a read or write.
|
|
*/
|
|
void
|
|
clrstats(void)
|
|
{
|
|
|
|
ninbuf = 0;
|
|
cntch = 0;
|
|
cntln = 0;
|
|
cntnull = 0;
|
|
#ifndef BIT8
|
|
cntodd = 0;
|
|
#endif
|
|
}
|
|
|
|
/* It's so wonderful how we all speak the same language... */
|
|
# define index strchr
|
|
# define rindex strrchr
|
|
|
|
void
|
|
checkmodeline(char *lin)
|
|
{
|
|
char *beg, *end;
|
|
char cmdbuf[BUFSIZ];
|
|
|
|
beg = index(lin, ':');
|
|
if (beg == NULL)
|
|
return;
|
|
if (&beg[-2] < lin)
|
|
return;
|
|
if (!((beg[-2] == 'e' && beg[-1] == 'x')
|
|
|| (beg[-2] == 'v' && beg[-1] == 'i'))) return;
|
|
strncpy(cmdbuf, beg+1, sizeof cmdbuf);
|
|
end = rindex(cmdbuf, ':');
|
|
if (end == NULL)
|
|
return;
|
|
*end = 0;
|
|
globp = cmdbuf;
|
|
commands(1, 1);
|
|
}
|
|
|
|
#ifdef MB
|
|
int
|
|
mbtowi(int *cp, const char *s, size_t n)
|
|
{
|
|
wchar_t wc;
|
|
int i;
|
|
|
|
i = mbtowc(&wc, s, n);
|
|
if (i >= 0 && widthok(wc) && !(wc & 0x70000000))
|
|
*cp = wc;
|
|
else {
|
|
*cp = *s&0377 | INVBIT;
|
|
i = 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int
|
|
widthok(int c)
|
|
{
|
|
return inopen ? wcwidth(c) <= 2 : 1;
|
|
}
|
|
#endif /* MB */
|
|
|
|
int
|
|
GETWC(char *mb)
|
|
{
|
|
int c, n;
|
|
|
|
n = 1;
|
|
mb[0] = c = getchar();
|
|
mb[1] = '\0';
|
|
#ifdef MB
|
|
if (mb_cur_max > 1 && c & 0200 && c != EOF) {
|
|
int m;
|
|
wchar_t wc;
|
|
while ((m = mbtowc(&wc, mb, mb_cur_max)) < 0 && n<mb_cur_max) {
|
|
mb[n++] = c = getchar();
|
|
mb[n] = '\0';
|
|
if (c == '\n' || c == EOF)
|
|
break;
|
|
}
|
|
if (m != n || c & 0x70000000)
|
|
error("illegal multibyte sequence");
|
|
return wc;
|
|
} else
|
|
#endif /* MB */
|
|
return c;
|
|
}
|