summaryrefslogtreecommitdiff
path: root/ex_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'ex_io.c')
-rw-r--r--ex_io.c1125
1 files changed, 1125 insertions, 0 deletions
diff --git a/ex_io.c b/ex_io.c
new file mode 100644
index 0000000..8769a9d
--- /dev/null
+++ b/ex_io.c
@@ -0,0 +1,1125 @@
+/*
+ * 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;
+}