From 544cd46ce0a7e659d963a74299b72e8736e25aff Mon Sep 17 00:00:00 2001 From: Mark Horton Date: Fri, 1 Aug 1980 06:00:15 -0800 Subject: [PATCH] date and time created 80/07/31 23:00:15 by mark SCCS-vsn: usr.bin/ex/ex_io.c 1.1 --- usr/src/usr.bin/ex/ex_io.c | 1039 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1039 insertions(+) create mode 100644 usr/src/usr.bin/ex/ex_io.c diff --git a/usr/src/usr.bin/ex/ex_io.c b/usr/src/usr.bin/ex/ex_io.c new file mode 100644 index 0000000000..d89f1fff02 --- /dev/null +++ b/usr/src/usr.bin/ex/ex_io.c @@ -0,0 +1,1039 @@ +/* Copyright (c) 1979 Regents of the University of California */ +#include "ex.h" +#include "ex_argv.h" +#include "ex_temp.h" +#include "ex_tty.h" +#include "ex_vis.h" + +/* + * File input/output, unix escapes, source, filtering preserve and recover + */ + +/* + * Following remember where . was in the previous file for return + * on file switching. + */ +short altdot; +short oldadot; +bool wasalt; + +long cntch; /* Count of characters on unit io */ +short cntln; /* Count of lines " */ +long cntnull; /* Count of nulls " */ +long cntodd; /* Count of non-ascii characters " */ + +/* + * 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. + */ +filename(comm) + int comm; +{ + register int c = comm, d; + register int i; + + d = getchar(); + if (endcmd(d)) { + if (savedfile[0] == 0 && comm != 'f') + error("No file|No current filename"); + CP(file, savedfile); + wasalt = 0; + oldadot = altdot; + 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 (!edited) + printf(" [Not edited]"); + if (tchng) + printf(" [Modified]"); + } + flush(); + } else + printf("No file "); + if (comm == 'f') { + if (!(i = lineDOL())) + i++; + printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(), + (long) 100 * lineDOT() / i); + } +} + +/* + * Get the argument words for a command into genbuf + * expanding # and %. + */ +getargs() +{ + 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("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("Argument buffer overflow"); + *cp++ = c; + break; + + case '#': + fp = altfile; + if (*fp == 0) + error("No alternate filename@to substitute for #"); + goto filexp; + + case '%': + fp = savedfile; + if (*fp == 0) + error("No current filename@to substitute for %%"); +filexp: + while (*fp) { + if (cp > &genbuf[LBSIZE - 2]) + goto flong; + *cp++ = *fp++; + } + break; + } + } + *cp = 0; + return (1); +} + +/* + * Glob the argument words in genbuf, or if no globbing + * is implied, just split them up directly. + */ +glob(gp) + 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)) + v++; + if (!*v) + break; + *argv++ = cp; + while (*v && !isspace(*v)) + *cp++ = *v++; + *cp++ = 0; + gp->argc0++; + } + *argv = 0; + return; + } + if (pipe(pvec) < 0) + error("Can't make pipe to glob"); + pid = fork(); + io = pvec[0]; + if (pid < 0) { + close(pvec[1]); + error("Can't fork to do glob"); + } + if (pid == 0) { + int oerrno; + + close(1); + dup(pvec[1]); + close(pvec[0]); + execl(svalue(SHELL), "sh", "-c", genbuf, 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("Arg list too long"); + } + if (cp != *argv) { + --nleft; + *cp++ = 0; + gp->argc0++; + if (gp->argc0 >= NARGS) + error("Arg list too long"); + argv++; + } + } while (c >= 0); + waitfor(); + if (gp->argc0 == 0) + error(NOSTR); +} + +/* + * Scan genbuf for shell metacharacters. + * Set is union of v7 shell and csh metas. + */ +gscan() +{ + register char *cp; + + for (cp = genbuf; *cp; cp++) + if (any(*cp, "~{[*?$`'\"\\")) + return (1); + return (0); +} + +/* + * Parse one filename into file. + */ +getone() +{ + register char *str; + struct glob G; + + if (getargs() == 0) + error("Missing filename"); + glob(&G); + if (G.argc0 > 1) + error("Ambiguous|Too many file names"); + str = G.argv[G.argc0 - 1]; + if (strlen(str) > FNSIZE - 4) + error("Filename too long"); +samef: + CP(file, str); +} + +/* + * Read a file from the world. + * C is command, 'e' if this really an edit (or a recover). + */ +rop(c) + int c; +{ + register int i; + struct stat stbuf; + short magic; + + io = open(file, 0); + if (io < 0) { + if (c == 'e' && errno == ENOENT) + edited++; + syserror(); + } + if (fstat(io, &stbuf)) + syserror(); + switch (stbuf.st_mode & S_IFMT) { + + case S_IFBLK: + error(" Block special file"); + + case S_IFCHR: + if (isatty(io)) + error(" Teletype"); + if (samei(&stbuf, "/dev/null")) + break; + error(" Character special file"); + + case S_IFDIR: + error(" Directory"); + + case S_IFREG: + i = read(io, (char *) &magic, sizeof(magic)); + lseek(io, 0l, 0); + if (i != sizeof(magic)) + break; + switch (magic) { + + case 0405: + case 0407: + case 0410: + case 0411: + error(" Executable"); + + case 0177545: + case 0177555: + error(" Archive"); + + default: + if (magic & 0100200) + error(" Non-ascii file"); + break; + } + } + if (c == 'r') + setdot(); + else + setall(); + if (inopen && c == 'r') + undap1 = undap2 = dot + 1; + rop2(); + rop3(c); +} + +rop2() +{ + + deletenone(); + clrstats(); + ignore(append(getfile, addr2)); +} + +rop3(c) + 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); + } + undkind = UNDNONE; + if (inopen) { + vcline = 0; + vreplace(0, LINES, lineDOL()); + } + } + if (laste) { + laste = 0; + sync(); + } +} + +/* + * Are these two really the same inode? + */ +samei(sp, cp) + 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); +} + +/* Returns from edited() */ +#define EDF 0 /* Edited file */ +#define NOTEDF -1 /* Not edited file */ +#define PARTBUF 1 /* Write of partial buffer to Edited file */ + +/* + * Write a file. + */ +wop(dofname) +bool dofname; /* if 1 call filename, else use savedfile */ +{ + register int c, exclam, nonexist; + line *saddr1, *saddr2; + 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("Write forms are 'w' and 'w>>'"); + filename('w'); + } else { + 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)) 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, 1); + if (io < 0) + syserror(); + if (!isatty(io)) + serror(" File exists| File exists - use \"w! %s\" to overwrite", file); + close(io); + break; + + case PARTBUF: + error(" Use \"w!\" to write partial buffer"); + } +cre: + synctmp(); +#ifdef V6 + io = creat(file, 0644); +#else + io = creat(file, 0666); +#endif + if (io < 0) + syserror(); + if (hush == 0) + if (nonexist) + printf(" [New file]"); + else if (value(WRITEANY) && edfile() != EDF) + printf(" [Existing file]"); + break; + + case 2: + io = open(file, 1); + if (io < 0) { + if (exclam || value(WRITEANY)) + goto cre; + syserror(); + } + lseek(io, 0l, 2); + break; + } + putfile(); + ignore(iostats()); + if (c != 2 && addr1 == one && addr2 == dol) { + if (eq(file, savedfile)) + edited = 1; + sync(); + } + if (!dofname) { + addr1 = saddr1; + addr2 = saddr2; + } +} + +/* + * Is file the edited file? + * Work here is that it is not considered edited + * if this is a partial buffer, and distinguish + * all cases. + */ +edfile() +{ + + if (!edited || !eq(file, savedfile)) + return (NOTEDF); + return (addr1 == one && addr2 == dol ? EDF : PARTBUF); +} + +/* + * First part of a shell escape, + * parse the line, expanding # and % and ! and printing if implied. + */ +unix0(warn) + bool warn; +{ + register char *up, *fp; + register short c; + char printub, puxb[UXBSIZE + sizeof (int)]; + + printub = 0; + CP(puxb, uxb); + c = getchar(); + if (c == '\n' || c == EOF) + error("Incomplete shell escape command@- use 'shell' to get a shell"); + up = uxb; + do { + switch (c) { + + case '\\': + if (any(peekchar(), "%#!")) + c = getchar(); + default: + if (up >= &uxb[UXBSIZE]) { +tunix: + uxb[0] = 0; + error("Command too long"); + } + *up++ = c; + break; + + case '!': + fp = puxb; + if (*fp == 0) { + uxb[0] = 0; + error("No previous command@to substitute for !"); + } + printub++; + while (*fp) { + if (up >= &uxb[UXBSIZE]) + goto tunix; + *up++ = *fp++; + } + break; + + case '#': + fp = altfile; + if (*fp == 0) { + uxb[0] = 0; + error("No alternate filename@to substitute for #"); + } + goto uexp; + + case '%': + fp = savedfile; + if (*fp == 0) { + uxb[0] = 0; + error("No filename@to substitute for %%"); + } +uexp: + printub++; + while (*fp) { + if (up >= &uxb[UXBSIZE]) + goto tunix; + *up++ = *fp++ | QUOTE; + } + break; + } + c = getchar(); + } while (c == '|' || !endcmd(c)); + if (c == EOF) + ungetchar(c); + *up = 0; + if (!inopen) + resetflav(); + if (warn) + ckaw(); + if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) { + xchng = chng; + vnfl(); + printf(mesg("[No write]|[No write since last change]")); + noonl(); + flush(); + } else + warn = 0; + if (printub) { + if (uxb[0] == 0) + error("No previous command@to repeat"); + if (inopen) { + splitw++; + vclean(); + vgoto(WECHO, 0); + } + if (warn) + vnfl(); + if (hush == 0) + lprintf("!%s", uxb); + if (inopen) { + vclreol(); + vgoto(WECHO, 0); + } else + putnl(); + flush(); + } +} + +/* + * Do the real work for execution of a shell escape. + * Mode is like the number passed to open system calls + * and indicates filtering. If input is implied, newstdin + * must have been setup already. + */ +unixex(opt, up, newstdin, mode) + char *opt, *up; + int newstdin, mode; +{ + int pvec[2], f; + + signal(SIGINT, SIG_IGN); + if (inopen) + f = setty(normf); + if ((mode & 1) && pipe(pvec) < 0) { + /* Newstdin should be io so it will be closed */ + if (inopen) + setty(f); + error("Can't make pipe for filter"); + } +#ifndef VFORK + pid = fork(); +#else + pid = vfork(); +#endif + if (pid < 0) { + if (mode & 1) { + close(pvec[0]); + close(pvec[1]); + } + setrupt(); + error("No more processes"); + } + if (pid == 0) { + if (mode & 2) { + close(0); + dup(newstdin); + close(newstdin); + } + if (mode & 1) { + close(pvec[0]); + close(1); + dup(pvec[1]); + if (inopen) { + close(2); + dup(1); + } + close(pvec[1]); + } + if (io) + close(io); + if (tfile) + close(tfile); + close(erfile); + signal(SIGHUP, oldhup); + signal(SIGQUIT, oldquit); + if (ruptible) + signal(SIGINT, SIG_DFL); + execl(svalue(SHELL), "sh", opt, up, (char *) 0); + printf("No %s!\n", svalue(SHELL)); + error(NOSTR); + } + if (mode & 1) { + io = pvec[0]; + close(pvec[1]); + } + if (newstdin) + close(newstdin); + return (f); +} + +/* + * Wait for the command to complete. + * F is for restoration of tty mode if from open/visual. + * C flags suppression of printing. + */ +unixwt(c, f) + bool c; + int f; +{ + + waitfor(); + if (inopen) + setty(f); + setrupt(); + if (!inopen && c && hush == 0) { + printf("!\n"); + flush(); + termreset(); + gettmode(); + } +} + +/* + * Setup a pipeline for the filtration implied by mode + * which is like a open number. If input is required to + * the filter, then a child editor is created to write it. + * If output is catch it from io which is created by unixex. + */ +filter(mode) + register int mode; +{ + static int pvec[2]; + register int f; + register int lines = lineDOL(); + + mode++; + if (mode & 2) { + signal(SIGINT, SIG_IGN); + if (pipe(pvec) < 0) + error("Can't make pipe"); + pid = fork(); + io = pvec[0]; + if (pid < 0) { + setrupt(); + close(pvec[1]); + error("No more processes"); + } + if (pid == 0) { + setrupt(); + io = pvec[1]; + close(pvec[0]); + putfile(); + exit(0); + } + close(pvec[1]); + io = pvec[0]; + setrupt(); + } + f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode); + if (mode == 3) { + delete(0); + addr2 = addr1 - 1; + } + if (mode & 1) { + undap1 = undap2 = addr2+1; + ignore(append(getfile, addr2)); + } + close(io); + io = -1; + unixwt(!inopen, f); + netchHAD(lines); +} + +/* + * Set up to do a recover, getting io to be a pipe from + * the recover process. + */ +recover() +{ + static int pvec[2]; + + if (pipe(pvec) < 0) + error(" Can't make pipe for recovery"); + pid = fork(); + io = pvec[0]; + if (pid < 0) { + close(pvec[1]); + error(" Can't fork to execute recovery"); + } + if (pid == 0) { + close(2); + dup(1); + close(1); + dup(pvec[1]); + close(pvec[1]); + execl(EXRECOVER, "exrecover", svalue(DIRECTORY), file, (char *) 0); + close(1); + dup(2); + error(" No recovery routine"); + } + close(pvec[1]); +} + +/* + * Wait for the process (pid an external) to complete. + */ +waitfor() +{ + + do + rpid = wait(&status); + while (rpid != pid && rpid != -1); + status = (status >> 8) & 0377; +} + +/* + * The end of a recover operation. If the process + * exits non-zero, force not edited; otherwise force + * a write. + */ +revocer() +{ + + waitfor(); + if (pid == rpid && status != 0) + edited = 0; + else + change(); +} + +/* + * Extract the next line from the io stream. + */ +static char *nextip; + +getfile() +{ + register short c; + register char *lp, *fp; + + lp = linebuf; + fp = nextip; + do { + if (--ninbuf < 0) { + ninbuf = read(io, genbuf, LBSIZE) - 1; + if (ninbuf < 0) { + if (lp != linebuf) { + printf(" [Incomplete last line]"); + break; + } + return (EOF); + } + fp = genbuf; + } + if (lp >= &linebuf[LBSIZE]) { + error(" Line too long"); + } + c = *fp++; + if (c == 0) { + cntnull++; + continue; + } + if (c & QUOTE) { + cntodd++; + c &= TRIM; + if (c == 0) + continue; + } + *lp++ = c; + } while (c != '\n'); + cntch += lp - linebuf; + *--lp = 0; + nextip = fp; + cntln++; + return (0); +} + +/* + * Write a range onto the io stream. + */ +putfile() +{ + line *a1; + register char *fp, *lp; + register int nib; + + a1 = addr1; + clrstats(); + cntln = addr2 - a1 + 1; + if (cntln == 0) + return; + nib = BUFSIZ; + fp = genbuf; + do { + getline(*a1++); + lp = linebuf; + for (;;) { + if (--nib < 0) { + nib = fp - genbuf; + if (write(io, genbuf, nib) != nib) { + wrerror(); + } + cntch += nib; + nib = BUFSIZ - 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. + */ +wrerror() +{ + + if (eq(file, savedfile) && edited) + change(); + syserror(); +} + +/* + * Source command, handles nested sources. + * Traps errors since it mungs unit 0 during the source. + */ +static short slevel; + +source(fil, okfail) + char *fil; + bool okfail; +{ + jmp_buf osetexit; + register int saveinp, ointty, oerrno; + int oprompt; + + signal(SIGINT, SIG_IGN); + saveinp = dup(0); + if (saveinp < 0) + error("Too many nested sources"); + close(0); + if (open(fil, 0) < 0) { + oerrno = errno; + setrupt(); + dup(saveinp); + close(saveinp); + 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); + slevel--; + resexit(osetexit); + reset(); + } + intty = ointty; + value(PROMPT) = oprompt; + close(0); + dup(saveinp); + close(saveinp); + slevel--; + resexit(osetexit); +} + +/* + * Clear io statistics before a read or write. + */ +clrstats() +{ + + ninbuf = 0; + cntch = 0; + cntln = 0; + cntnull = 0; + cntodd = 0; +} + +/* + * Io is finished, close the unit and print statistics. + */ +iostats() +{ + + close(io); + io = -1; + if (hush == 0) { + if (value(TERSE)) + printf(" %d/%D", cntln, cntch); + else + printf(" %d line%s, %D character%s", cntln, plural((long) cntln), + cntch, plural(cntch)); + if (cntnull || cntodd) { + printf(" ("); + if (cntnull) { + printf("%D null", cntnull); + if (cntodd) + printf(", "); + } + if (cntodd) + printf("%D non-ASCII", cntodd); + putchar(')'); + } + noonl(); + flush(); + } + return (cntnull != 0 || cntodd != 0); +} -- 2.20.1