From 54e8bdce709830bed73457cfc97b7bb44c98a8de Mon Sep 17 00:00:00 2001 From: Bill Joy Date: Tue, 8 May 1979 23:19:20 -0800 Subject: [PATCH] BSD 2 development Work on file src/ex/ex_io.c Work on file src/ex/ex_put.c Synthesized-from: 2bsd --- src/ex/ex_io.c | 1001 +++++++++++++++++++++++++++++++++++++++++++++++ src/ex/ex_put.c | 830 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1831 insertions(+) create mode 100644 src/ex/ex_io.c create mode 100644 src/ex/ex_put.c diff --git a/src/ex/ex_io.c b/src/ex/ex_io.c new file mode 100644 index 0000000000..0a396fe11b --- /dev/null +++ b/src/ex/ex_io.c @@ -0,0 +1,1001 @@ +/* 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; + + 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); + die++; + 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.argv[0][0] == '+') { + firstln = getn(G.argv[0] + 1); + if (firstln == 0) + firstln = 20000; + if (G.argc0 == 1) { + str = savedfile; + goto samef; + } + } + else 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; + + if (firstln) + wasalt = 2, oldadot = firstln, firstln = 0; + 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) { + register line *addr = zero + oldadot; + + if (addr > dol) + addr = dol; + if (addr >= one) { + if (inopen || wasalt == 2) + 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() +{ + register int c, exclam, nonexist; + struct stat stbuf; + + c = 0; + exclam = 0; + 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'); + 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(); + } +} + +/* + * 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 && hush == 0 && chng && xchng != chng && value(WARN)) { + 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(); + 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"); + } + pid = fork(); + if (pid < 0) { + if (mode & 1) { + close(pvec[0]); + close(pvec[1]); + } + setrupt(); + error("No more processes"); + } + if (pid == 0) { + die++; + 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) { + die++; + 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) + 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); + die++; + 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; + + 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); + 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; + 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); +} diff --git a/src/ex/ex_put.c b/src/ex/ex_put.c new file mode 100644 index 0000000000..6f99e24342 --- /dev/null +++ b/src/ex/ex_put.c @@ -0,0 +1,830 @@ +/* Copyright (c) 1979 Regents of the University of California */ +#include "ex.h" +#include "ex_tty.h" + +/* + * Terminal driving and line formatting routines. + * Basic motion optimizations are done here as well + * as formatting of lines (printing of control characters, + * line numbering and the like). + */ + +/* + * The routines outchar, putchar and pline are actually + * variables, and these variables point at the current definitions + * of the routines. See the routine setflav. + * We sometimes make outchar be routines which catch the characters + * to be printed, e.g. if we want to see how long a line is. + * During open/visual, outchar and putchar will be set to + * routines in the file ex_vput.c (vputchar, vinschar, etc.). + */ +int (*Outchar)() = termchar; +int (*Putchar)() = normchar; +int (*Pline)() = normline; + +int (* +setlist(t))() + bool t; +{ + register int (*P)(); + + listf = t; + P = Putchar; + Putchar = t ? listchar : normchar; + return (P); +} + +int (* +setnumb(t))() + bool t; +{ + register int (*P)(); + + numberf = t; + P = Pline; + Pline = t ? numbline : normline; + return (P); +} + +/* + * Format c for list mode; leave things in common + * with normal print mode to be done by normchar. + */ +listchar(c) + register short c; +{ + + c &= (TRIM|QUOTE); + switch (c) { + + case '\t': + case '\b': + outchar('^'); + c = ctlof(c); + break; + + case '\n': + break; + + case '\n' | QUOTE: + outchar('$'); + break; + + default: + if (c & QUOTE) + break; + if (c < ' ' && c != '\n' || c == DELETE) + outchar('^'), c = ctlof(c); + break; + } + normchar(c); +} + +/* + * Format c for printing. Handle funnies of upper case terminals + * and crocky hazeltines which don't have ~. + */ +normchar(c) + register short c; +{ + register char *colp; + + c &= (TRIM|QUOTE); + if (c == '~' && HZ) { + normchar('\\'); + c = '^'; + } + if (c & QUOTE) + switch (c) { + + case ' ' | QUOTE: + case '\b' | QUOTE: + break; + + case QUOTE: + return; + + default: + c &= TRIM; + } + else if (c < ' ' && (c != '\b' || !OS) && c != '\n' && c != '\t' || c == DELETE) + putchar('^'), c = ctlof(c); + else if (UPPERCASE) + if (isupper(c)) { + outchar('\\'); + c = tolower(c); + } else { + colp = "({)}!|^~'`"; + while (*colp++) + if (c == *colp++) { + outchar('\\'); + c = colp[-2]; + break; + } + } + outchar(c); +} + +/* + * Print a line with a number. + */ +numbline(i) + int i; +{ + + if (shudclob) + slobber(' '); + printf("%6d ", i); + normline(); +} + +/* + * Normal line output, no numbering. + */ +normline() +{ + register char *cp; + + if (shudclob) + slobber(linebuf[0]); + /* pdp-11 doprnt is not reentrant so can't use "printf" here + in case we are tracing */ + for (cp = linebuf; *cp;) + putchar(*cp++); + if (!inopen) + putchar('\n' | QUOTE); +} + +/* + * Given c at the beginning of a line, determine whether + * the printing of the line will erase or otherwise obliterate + * the prompt which was printed before. If it won't, do it now. + */ +slobber(c) + int c; +{ + + shudclob = 0; + switch (c) { + + case '\t': + if (Putchar == listchar) + return; + break; + + default: + return; + + case ' ': + case 0: + break; + } + if (OS) + return; + flush(); + putch(' '); + if (BC) + tputs(BC, 0, putch); + else + putch('\b'); +} + +/* + * The output buffer is initialized with a useful error + * message so we don't have to keep it in data space. + */ +static char linb[66] = { + 'E', 'r', 'r', 'o', 'r', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', + 'f', 'i', 'l', 'e', ' ', 'n', 'o', 't', ' ', + 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', '\n', 0 +}; +static char *linp = linb + 33; + +/* + * Phadnl records when we have already had a complete line ending with \n. + * If another line starts without a flush, and the terminal suggests it, + * we switch into -nl mode so that we can send lineffeeds to avoid + * a lot of spacing. + */ +static bool phadnl; + +/* + * Indirect to current definition of putchar. + */ +putchar(c) + int c; +{ + + (*Putchar)(c); +} + +/* + * Termchar routine for command mode. + * Watch for possible switching to -nl mode. + * Otherwise flush into next level of buffering when + * small buffer fills or at a newline. + */ +termchar(c) + int c; +{ + + if (pfast == 0 && phadnl) + pstart(); + if (c == '\n') + phadnl = 1; + else if (linp >= &linb[63]) + flush1(); + *linp++ = c; + if (linp >= &linb[63]) { + fgoto(); + flush1(); + } +} + +flush() +{ + + flush1(); + flush2(); +} + +/* + * Flush from small line buffer into output buffer. + * Work here is destroying motion into positions, and then + * letting fgoto do the optimized motion. + */ +flush1() +{ + register char *lp; + register short c; + + *linp = 0; + lp = linb; + while (*lp) + switch (c = *lp++) { + + case '\r': + destline += destcol / COLUMNS; + destcol = 0; + continue; + + case '\b': + if (destcol) + destcol--; + continue; + + case ' ': + destcol++; + continue; + + case '\t': + destcol += value(TABSTOP) - destcol % value(TABSTOP); + continue; + + case '\n': + destline += destcol / COLUMNS + 1; + if (destcol != 0 && destcol % COLUMNS == 0) + destline--; + destcol = 0; + continue; + + default: + fgoto(); + for (;;) { + if (AM == 0 && outcol == COLUMNS) + fgoto(); + c &= TRIM; + putch(c); + if (c == '\b') { + outcol--; + destcol--; + } else if (c >= ' ' && c != DELETE) { + outcol++; + destcol++; + if (XN && outcol % COLUMNS == 0) + putch('\n'); + } + c = *lp++; + if (c <= ' ') + break; + } + --lp; + continue; + } + linp = linb; +} + +flush2() +{ + + fgoto(); + flusho(); + pstop(); +} + +/* + * Sync the position of the output cursor. + * Most work here is rounding for terminal boundaries getting the + * column position implied by wraparound or the lack thereof and + * rolling up the screen to get destline on the screen. + */ +fgoto() +{ + register int l, c; + + if (destcol > COLUMNS - 1) { + destline += destcol / COLUMNS; + destcol %= COLUMNS; + } + if (outcol > COLUMNS - 1) { + l = (outcol + 1) / COLUMNS; + outline += l; + outcol %= COLUMNS; + if (AM == 0) { + while (l > 0) { + if (pfast) + putch('\r'); + putch('\n'); + l--; + } + outcol = 0; + } + if (outline > LINES - 1) { + destline -= outline - (LINES - 1); + outline = LINES - 1; + } + } + if (destline > LINES - 1) { + l = destline; + destline = LINES - 1; + if (outline < LINES - 1) { + c = destcol; + if (pfast == 0 && (!CA || holdcm)) + destcol = 0; + fgoto(); + destcol = c; + } + while (l > LINES - 1) { + putch('\n'); + l--; + if (pfast == 0) + outcol = 0; + } + } + if (destline < outline && !(CA && !holdcm || UP != NOSTR)) + destline = outline; + if (motion()) + return; + if (CA && !holdcm) { + tputs(cgoto(), 0, putch); + outline = destline; + outcol = destcol; + } else + plod(); +} + +/* + * Tab to column col by flushing and then setting destcol. + * Used by "set all". + */ +tab(col) + int col; +{ + + flush1(); + destcol = col; +} + +/* + * Routine to decide how to do a basic motion, + * choosing between local motions and cursor addressing. + */ +motion() +{ + register int v, h; + /* + * V is vertical distance, then cost with cr. + * H is horizontal distance, then direct move cost. + */ + + if (!BS) + return (0); + v = destline - outline; + if (v < 0) + if (CA && !holdcm || UP) { + if (!UP) + return (0); + v = -v; + } else + destline = outline; + h = destcol; + if (!v || pfast) { + h -= outcol; + if (h < 0) + h = -h; + } + h += v; + if (pfast || !NONL) { + if (outcol) + v++; + v += destcol; + } else + v = 5; + if (v >= 4 && h >= 4) + return (0); + plod(); + return (1); +} + +/* + * Move (slowly) to destination. + * Hard thing here is using home cursor on really deficient terminals. + * Otherwise just use cursor motions, hacking use of tabs and overtabbing + * and backspace. + */ +plod() +{ + register int i, j, k; + + if (HO && (!CA || holdcm)) { + if (GT) + i = (destcol >> 3) + (destcol & 07); + else + i = destcol; + if (destcol >= outcol) + if (GT && (j = ((destcol - (outcol &~ 07)) >> 3))) + j += destcol & 07; + else + j = destcol - outcol; + else + if (outcol - destcol <= i && (BS || BC)) + i = j = outcol - destcol; + else + j = i + 1; + k = outline - destline; + if (k < 0) + k = -k; + j += k; + if (i + destline < j) { + tputs(HO, 0, putch); + outcol = outline = 0; + } else if (LL) { + k = (LINES - 1) - destline; + if (i + k + 2 < j) { + tputs(LL, 0, putch); + outcol = 0; + outline = LINES - 1; + } + } + } + if (GT && (!CA || holdcm)) + i = (destcol >> 3) + (destcol & 07); + else + i = destcol; + if ((!NONL || outline >= destline) && (!NC || outline < destline) && + (outcol - destcol > i + 1 || outcol > destcol && !BS && !BC)) { + putch('\r'); + if (NC) { + putch('\n'); + outline++; + } + outcol = 0; + } + while (outline < destline) { + outline++; + putch('\n'); + if (NONL || pfast == 0) + outcol = 0; + } + while (outcol > destcol) { + outcol--; + if (BC) + tputs(BC, 0, putch); + else + putch('\b'); + } + while (outline > destline) { + outline--; + putS(UP); + } + if (GT && destcol - outcol > 1) { + while ((i = ((outcol + 8) &~ 7)) <= destcol) { + if (TA) + tputs(TA, 0, putch); + else + putch('\t'); + outcol = i; + } + if (destcol - outcol > 4 && (BC || BS)) { + if (TA) + tputs(TA, 0, putch); + else + putch('\t'); + outcol = i; + while (outcol > destcol) { + outcol--; + if (BC) + tputs(BC, 0, putch); + else + putch('\b'); + } + } + } + while (outcol < destcol) { + if (inopen && ND) + putS(ND); + else + putch(' '); + outcol++; + } +} + +/* + * An input line arrived. + * Calculate new (approximate) screen line position. + * Approximate because kill character echoes newline with + * no feedback and also because of long input lines. + */ +noteinp() +{ + + outline++; + if (outline > LINES - 1) + outline = LINES - 1; + destline = outline; + destcol = outcol = 0; +} + +/* + * Something weird just happened and we + * lost track of whats happening out there. + * Since we cant, in general, read where we are + * we just reset to some known state. + * On cursor addressible terminals setting to unknown + * will force a cursor address soon. + */ +termreset() +{ + + endim(); + destcol = 0; + destline = LINES - 1; + if (CA) { + outcol = UKCOL; + outline = UKCOL; + } else { + outcol = destcol; + outline = destline; + } +} + +/* + * Low level buffering, with the ability to drain + * buffered output without printing it. + */ +char *obp = obuf; + +draino() +{ + + obp = obuf; +} + +flusho() +{ + + if (obp != obuf) { + write(1, obuf, obp - obuf); + obp = obuf; + } +} + +putnl() +{ + + putchar('\n'); +} + +putS(cp) + char *cp; +{ + + if (cp == NULL) + return; + while (*cp) + putch(*cp++); +} + + +putch(c) + int c; +{ + + *obp++ = c; + if (obp >= &obuf[sizeof obuf]) + flusho(); +} + +/* + * Miscellaneous routines related to output. + */ + +/* + * Cursor motion. + */ +char * +cgoto() +{ + + return (tgoto(CM, destcol, destline)); +} + +/* + * Put with padding + */ +putpad(cp) + char *cp; +{ + + flush(); + tputs(cp, 0, putch); +} + +/* + * Set output through normal command mode routine. + */ +setoutt() +{ + + Outchar = termchar; +} + +/* + * Printf (temporarily) in list mode. + */ +/*VARARGS2*/ +lprintf(cp, dp) + char *cp, *dp; +{ + register int (*P)(); + + P = setlist(1); + printf(cp, dp); + Putchar = P; +} + +/* + * Newline + flush. + */ +putNFL() +{ + + putnl(); + flush(); +} + +/* + * Try to start -nl mode. + */ +pstart() +{ + + if (NONL) + return; + if (!value(OPTIMIZE)) + return; + if (ruptible == 0 || pfast) + return; + fgoto(); + flusho(); + pfast = 1; + normtty++; + tty.sg_flags = normf & ~(ECHO|CRMOD); + sTTY(1); +} + +/* + * Stop -nl mode. + */ +pstop() +{ + + if (inopen) + return; + phadnl = 0; + linp = linb; + draino(); + normal(normf); + pfast &= ~1; +} + +/* + * Prep tty for open mode. + */ +ostart() +{ + int f; + + if (!intty) + error("Open and visual must be used interactively"); + gTTY(1); + normtty++; + f = tty.sg_flags; +#ifdef V6 + tty.sg_flags = (normf &~ (ECHO|CRMOD)) | RAW; +#else + tty.sg_flags = (normf &~ (ECHO|CRMOD)) | CBREAK; +#endif + sTTY(1); + putpad(VS); + pfast |= 2; + return (f); +} + +/* + * Stop open, restoring tty modes. + */ +ostop(f) + int f; +{ + + pfast = (f & CRMOD) == 0; + termreset(), fgoto(), flusho(); + normal(f); + putpad(VE); +} + +#ifdef V6 +/* + * Into cooked mode for interruptibility. + */ +vcook() +{ + + tty.sg_flags &= ~RAW; + sTTY(1); +} + +/* + * Back into raw mode. + */ +vraw() +{ + + tty.sg_flags |= RAW; + sTTY(1); +} +#endif + +/* + * Restore flags to normal state f. + */ +normal(f) + int f; +{ + + if (normtty > 0) { + setty(f); + normtty--; + } +} + +/* + * Straight set of flags to state f. + */ +setty(f) + int f; +{ + register int ot = tty.sg_flags; + + tty.sg_flags = f; + sTTY(1); + return (ot); +} + +gTTY(i) + int i; +{ + + ignore(gtty(i, &tty)); +} + +sTTY(i) + int i; +{ + +#ifdef V6 + stty(i, &tty); +#else + ioctl(i, TIOCSETN, &tty); +#endif +} + +/* + * Print newline, or blank if in open/visual + */ +noonl() +{ + + putchar(Outchar != termchar ? ' ' : '\n'); +} -- 2.20.1