Mercurial > heirloom-ed
diff ed.c @ 0:1493bea5ac22 0.1
Initial version of the standalone heirloom-ed
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Mon, 05 Sep 2011 16:31:35 +0200 |
parents | |
children | a09d0630f05b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ed.c Mon Sep 05 16:31:35 2011 +0200 @@ -0,0 +1,2822 @@ +/* + * Editor + */ + +/* + * Changes by Gunnar Ritter, Freiburg i. Br., Germany, July 2003. + */ +/* from Unix 32V /usr/src/cmd/ed.c */ +/* + * 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. + */ + +#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4 +#define USED __attribute__ ((used)) +#elif defined __GNUC__ +#define USED __attribute__ ((unused)) +#else +#define USED +#endif +#if defined (SU3) +static const char sccsid[] USED = "@(#)ed_su3.sl 1.99 (gritter) 7/27/06"; +#elif defined (SUS) +static const char sccsid[] USED = "@(#)ed_sus.sl 1.99 (gritter) 7/27/06"; +#elif defined (S42) +static const char sccsid[] USED = "@(#)ed_s42.sl 1.99 (gritter) 7/27/06"; +#else /* !SU3, !SUS, !S42 */ +static const char sccsid[] USED = "@(#)ed.sl 1.99 (gritter) 7/27/06"; +#endif /* !SU3, !SUS, !S42 */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include "sigset.h" +#include <termios.h> +#include <setjmp.h> +#include <libgen.h> +#include <inttypes.h> +#include <locale.h> +#include <wchar.h> +#include <ctype.h> +#include <wctype.h> +#include <limits.h> +#include <termios.h> +static int FNSIZE; +static int LBSIZE; +static int RHSIZE; +#define ESIZE 2048 +static int GBSIZE; +#undef EOF +#define EOF -1 +#define puts(s) xxputs(s) +#define getline(t, n) xxgetline(t, n) + +#if (LONG_MAX > 017777777777L) +#define MAXCNT 0777777777777777777777L /* 2^63-1 */ +#else +#define MAXCNT 017777777777L /* 2^31-1 */ +#endif +#define BLKMSK (MAXCNT>>8) /* was 0377 */ + +#define READ 0 +#define WRITE 1 +#define EXIST 2 + +struct tabulator { + struct tabulator *t_nxt; /* next list element */ + const char *t_str; /* tabulator string */ + int t_tab; /* tab stop position */ + int t_rep; /* repetitive tab count */ +}; + +static int peekc; +static int lastc; +static char *savedfile; +static char *file; +static struct stat fstbuf; +static char *linebuf; +static char *rhsbuf; +static char expbuf[ESIZE + 4]; +static long *zero; +static long *undzero; +static long *dot; +static long *unddot; +static long *dol; +static long *unddol; +static long *addr1; +static long *addr2; +static char *genbuf; +static long count; +static char *linebp; +static int ninbuf; +static int io; +static int ioeof; +static int pflag; +static char *wrtemp; +static uid_t myuid; +static void (*oldhup)(int); +static void (*oldquit)(int); +static void (*oldpipe)(int); +static int vflag = 1; +static int listf; +static int numbf; +static char *globp; +static int tfile = -1; +static long tline; +static char tfname[64]; +static char ibuff[512]; +static int iblock = -1; +static char obuff[512]; +static int oblock = -1; +static int ichanged; +static int nleft; +static long *names; +static long *undnames; +static int anymarks; +static int subnewa; +static int fchange; +static int wrapp; +static unsigned nlall = 128; +static const char *progname; +static const char *prompt = "*"; +static int Pflag; +static int prhelp; +static const char *prvmsg; +static int lastsig; +static int pipid = -1; +static int readop; +static int status; +static int mb_cur_max; +static int needsub; +static int insub; +static struct tabulator *tabstops; +static int maxlength; +static int rspec; +static int Nflag; +static int bcount = 22; +static int ocount = 11; + +static jmp_buf savej; + +static void usage(char, int); +static void commands(void); +static long *address(void); +static void setdot(void); +static void setall(void); +static void setnoaddr(void); +static void nonzero(void); +static void newline(void); +static void filename(int); +static void exfile(void); +static void onintr(int); +static void onhup(int); +static void onpipe(int); +static void error(const char *); +static void error2(const char *, const char *); +static void errput(const char *, const char *); +static int getchr(void); +static int gettty(void); +static long getnum(void); +static int getfile(void); +static void putfile(void); +static int append(int (*)(void), long *); +static void callunix(void); +static char *readcmd(void); +static void quit(int); +static void delete(void); +static void rdelete(long *, long *); +static void gdelete(void); +static char *getline(long, int); +static int putline(void); +static char *getblock(long, long); +static void blkio(long, char *, int); +static void init(void); +static void global(int, int); +static void globrd(char **, int); +static void join(void); +static void substitute(int); +static int compsub(void); +static int getsub(void); +static int dosub(int); +static int place(int, const char *, const char *); +static void move(int); +static void reverse(long *, long *); +static int getcopy(void); +static int execute(int, long *, int); +static void cmplerr(int); +static void doprnt(long *, long *); +static void putd(long); +static void puts(const char *); +static void nlputs(const char *); +static void list(const char *); +static int lstchr(int); +static void putstr(const char *); +static void putchr(int); +static void checkpoint(void); +static void undo(void); +static int maketf(int); +static int creatf(const char *); +static int sopen(const char *, int); +static void sclose(int); +static void fspec(const char *); +static const char *ftok(const char **); +static struct tabulator *tabstring(const char *); +static void freetabs(void); +static void expand(const char *); +static void growlb(const char *); +static void growrhs(const char *); +static void growfn(const char *); +static void help(void); + +#define INIT +#define GETC() getchr() +#define UNGETC(c) (peekc = c) +#define PEEKC() (peekc = getchr()) +#define RETURN(c) return c +#define ERROR(c) cmplerr(c) +static wint_t GETWC(char *); + +#if defined (SUS) || defined (S42) || defined (SU3) + +#include <regex.h> + +#define NBRA 9 + +static char *braslist[NBRA]; +static char *braelist[NBRA]; +static char *loc1, *loc2, *locs; +static int nbra; +static int circf; +static int nodelim; + +static char *compile(char *, char *, const char *, int); +static int step(const char *, const char *); + +#else /* !SUS, !S42, !SU3 */ + +#include <regexp.h> + +#endif /* !SUS, !S42, !SU3 */ + +int +main(int argc, char **argv) +{ + register int i; + void (*oldintr)(int); + + progname = basename(argv[0]); +#if defined (SUS) || defined (S42) || defined (SU3) + setlocale(LC_COLLATE, ""); +#endif + setlocale(LC_CTYPE, ""); + mb_cur_max = MB_CUR_MAX; + myuid = getuid(); + oldquit = sigset(SIGQUIT, SIG_IGN); + oldhup = sigset(SIGHUP, SIG_IGN); + oldintr = sigset(SIGINT, SIG_IGN); + if (sigset(SIGTERM, SIG_IGN) != SIG_IGN) + sigset(SIGTERM, quit); + oldpipe = sigset(SIGPIPE, onpipe); + argv++; + while (argc > 1 && **argv=='-') { + if ((*argv)[1] == '\0') { + vflag = 0; + goto next; + } else if ((*argv)[1] == '-' && (*argv)[2] == '\0') { + argv++; + argc--; + break; + } + letter: switch((*argv)[1]) { + + case 's': + vflag = 0; + break; + + case 'q': + sigset(SIGQUIT, SIG_DFL); + vflag = 1; + break; + + case 'p': + if ((*argv)[2]) + prompt = &(*argv)[2]; + else if (argv[1]) { + prompt = argv[1]; + argv++; + argc--; + } else + usage((*argv)[1], 1); + Pflag = 1; + goto next; + + default: + usage((*argv)[1], 0); + } + if ((*argv)[2]) { + (*argv)++; + goto letter; + } + next: argv++; + argc--; + } + + growfn("no space"); + if (argc>1) { + i = -1; + do + if (++i >= FNSIZE) + growfn("maximum of characters in " + "file names reached"); + while (savedfile[i] = (*argv)[i]); + globp = "e"; + } + names = malloc(26*sizeof *names); + undnames = malloc(26*sizeof *undnames); + zero = malloc(nlall*sizeof *zero); + if ((undzero = malloc(nlall*sizeof *undzero)) == NULL) + puts("no memory for undo"); + growlb("no space"); + growrhs("no space"); + init(); + if (oldintr != SIG_IGN) + sigset(SIGINT, onintr); + if (oldhup != SIG_IGN) + sigset(SIGHUP, onhup); + setjmp(savej); + if (lastsig) { + sigrelse(lastsig); + lastsig = 0; + } + commands(); + quit(0); + /*NOTREACHED*/ + return 0; +} + +static void +usage(char c, int misarg) +{ + if (c) { + write(2, progname, strlen(progname)); + if (misarg) + write(2, ": option requires an argument -- ", 33); + else + write(2, ": illegal option -- ", 20); + write(2, &c, 1); + write(2, "\n", 1); + } + write(2, "usage: ", 7); + write(2, progname, strlen(progname)); + write(2, " [- | -s] [-p string] [file]\n", 29); + exit(2); +} + +static void +commands(void) +{ + register long *a1; + register int c; + int n; + + for (;;) { + if (pflag) { + pflag = 0; + addr1 = addr2 = dot; + goto print; + } + if (Pflag && globp == NULL) + write(1, prompt, strlen(prompt)); + addr1 = 0; + addr2 = 0; + switch (c = getchr()) { + case ',': + case ';': + addr2 = c == ',' ? zero+1 : dot; + if (((peekc = getchr()) < '0' || peekc > '9') && + peekc != ' ' && peekc != '\t' && + peekc != '+' && peekc != '-' && + peekc != '^' && peekc != '?' && + peekc != '/' && peekc != '$' && + peekc != '.' && peekc != '\'') { + addr1 = addr2; + a1 = dol; + goto loop; + } + break; + default: + peekc = c; + } + do { + addr1 = addr2; + if ((a1 = address())==0) { + c = getchr(); + break; + } + loop: addr2 = a1; + if ((c=getchr()) == ';') { + c = ','; + dot = a1; + } + } while (c==','); + if (addr1==0) + addr1 = addr2; + switch(c) { + + case 'a': + setdot(); + newline(); + checkpoint(); + append(gettty, addr2); + continue; + + case 'c': +#if defined (SU3) + if (addr1 == zero && addr1+1 <= dol) { + if (addr1 == addr2) + addr2++; + addr1++; + } +#endif /* SU3 */ + delete(); + append(gettty, addr1-1); +#if defined (SUS) || defined (SU3) + if (dot == addr1-1 && addr1 <= dol) + dot = addr1; +#endif /* SUS || SU3 */ + continue; + + case 'd': + delete(); + continue; + + case 'E': + fchange = 0; + c = 'e'; + case 'e': + setnoaddr(); + if (vflag && fchange) { + fchange = 0; + error("warning: expecting `w'"); + } + filename(c); + init(); + addr2 = zero; + goto caseread; + + case 'f': + setnoaddr(); + filename(c); + puts(savedfile); + continue; + + case 'g': + global(1, 0); + continue; + + case 'G': + global(1, 1); + continue; + + case 'H': + prhelp = !prhelp; + /*FALLTHRU*/ + + case 'h': + if ((peekc = getchr()) == 'e') { + peekc = 0; + if (getchr() != 'l' || getchr() != 'p' || + getchr() != '\n') + error("illegal suffix"); + setnoaddr(); + help(); + continue; + } + newline(); + setnoaddr(); + if (prvmsg) + puts(prvmsg); + continue; + + case 'i': + setdot(); +#if defined (SU3) + if (addr1 == zero) { + if (addr1 == addr2) + addr2++; + addr1++; + if (dol != zero) + nonzero(); + } else +#endif /* SU3 */ + nonzero(); + newline(); + checkpoint(); + append(gettty, addr2-1); + if (dot == addr2-1) + dot++; + continue; + + + case 'j': + if (addr2==0) { + addr1 = dot; + addr2 = dot+1; + } + setdot(); + newline(); + nonzero(); + checkpoint(); + if (addr1 != addr2) + join(); + continue; + + case 'k': + if ((c = getchr()) < 'a' || c > 'z') + error("mark not lower case"); + newline(); + setdot(); + nonzero(); + names[c-'a'] = *addr2 & ~01; + anymarks |= 01; + continue; + + case 'm': + move(0); + continue; + + case '\n': + if (addr2==0) + addr2 = dot+1; + addr1 = addr2; + goto print; + + case 'n': + numbf = 1; + newline(); + goto print; + + case 'N': + newline(); + setnoaddr(); + Nflag = !Nflag; + continue; + + case 'b': + case 'o': + n = getnum(); + newline(); + setdot(); + nonzero(); + if (n >= 0) { + if (c == 'b') + bcount = n; + else + ocount = n; + } + if (c == 'b') { + a1 = addr2+bcount > dol ? dol : addr2 + bcount; + doprnt(addr1, a1); + dot = a1; + } else { + a1 = addr2+ocount > dol ? dol : addr2 + ocount; + doprnt(addr2-ocount<zero+1?zero+1:addr2-ocount, a1); + dot = addr2; + } + continue; + + case 'l': + listf++; + case 'p': + newline(); + print: + setdot(); + nonzero(); + doprnt(addr1, addr2); + dot = addr2; + continue; + + case 'P': + setnoaddr(); + newline(); + Pflag = !Pflag; + continue; + + case 'Q': + fchange = 0; + case 'q': + setnoaddr(); + newline(); + quit(0); + + case 'r': + filename(c); + caseread: + if ((io = sopen(file, READ)) < 0) { + lastc = '\n'; + error2("cannot open input file", file); + } + ioeof = 0; + setall(); + ninbuf = 0; + if (c == 'r') + checkpoint(); + n = zero != dol; + rspec = (c == 'e' || !n) && file[0] != '!'; + append(getfile, addr2); + rspec = 0; + exfile(); + fchange = n; + continue; + + case 's': + setdot(); + nonzero(); + substitute(globp!=0); + continue; + + case 't': + move(1); + continue; + + case 'u': + setdot(); + newline(); + if (unddot == NULL) + error("nothing to undo"); + undo(); + continue; + + case 'v': + global(0, 0); + continue; + + case 'V': + global(0, 1); + continue; + + case 'W': + wrapp++; + case 'w': + write: + setall(); + if (zero != dol) + nonzero(); + filename(c); + if(!wrapp || + ((io = open(file,O_WRONLY|O_APPEND)) == -1) || + ((lseek(io, 0, SEEK_END)) == -1)) { + struct stat st; + if (lstat(file, &st) == 0 && + (st.st_mode&S_IFMT) == S_IFREG && + st.st_nlink == 1 && + (myuid==0 || myuid==st.st_uid)) { + char *cp, *tp; + int nio; + if ((io = sopen(file, EXIST)) < 0) + error("cannot create output file"); + if ((wrtemp = malloc(strlen(file)+8)) == NULL) + error("out of memory"); + for (cp = file, tp = wrtemp; *cp; cp++) + *tp++ = *cp; + while (tp > wrtemp && tp[-1] != '/') + tp--; + for (cp = "\7XXXXXX"; *cp; cp++) + *tp++ = *cp; + *tp = '\0'; + if ((nio = mkstemp(wrtemp)) < 0) { + free(wrtemp); + wrtemp = NULL; + ftruncate(io, 0); + } else { + close(io); + io = nio; + } + } else { + if ((io = sopen(file, WRITE)) < 0) + error("cannot create output file"); + } + } + if (zero != dol) { + ioeof = 0; + wrapp = 0; + putfile(); + } + exfile(); + if (addr1==zero+1 && addr2==dol || addr1==addr2 && dol==zero) + fchange = 0; + if (c == 'z') + quit(0); + continue; + + case 'z': + if ((peekc=getchr()) != '\n') + error("illegal suffix"); + setnoaddr(); + goto write; + + case '=': + setall(); + newline(); + putd((addr2-zero)&MAXCNT); + putchr('\n'); + continue; + + case '!': + callunix(); + continue; + + case EOF: + return; + + } + error("unknown command"); + } +} + +static long * +address(void) +{ + register long *a1; + register int minus, c; + int n, relerr; + + minus = 0; + a1 = 0; + for (;;) { + c = getchr(); + if ('0'<=c && c<='9') { + n = 0; + do { + n *= 10; + n += c - '0'; + } while ((c = getchr())>='0' && c<='9'); + peekc = c; + if (a1==0) + a1 = zero; + if (minus<0) + n = -n; + a1 += n; + minus = 0; + continue; + } + relerr = 0; + if (a1 || minus) + relerr++; + switch(c) { + case ' ': + case '\t': + continue; + + case '+': + minus++; + if (a1==0) + a1 = dot; + continue; + + case '-': + case '^': + minus--; + if (a1==0) + a1 = dot; + continue; + + case '?': + case '/': + compile(NULL, expbuf, &expbuf[ESIZE], c); + a1 = dot; + for (;;) { + if (c=='/') { + a1++; + if (a1 > dol) + a1 = zero; + } else { + a1--; + if (a1 < zero) + a1 = dol; + } + if (execute(0, a1, 0)) + break; + if (a1==dot) + error("search string not found"); + } + break; + + case '$': + a1 = dol; + break; + + case '.': + a1 = dot; + break; + + case '\'': + if ((c = getchr()) < 'a' || c > 'z') + error("mark not lower case"); + for (a1=zero; a1<=dol; a1++) + if (names[c-'a'] == (*a1 & ~01)) + break; + break; + + default: + peekc = c; + if (a1==0) + return(0); + a1 += minus; + if (a1<zero || a1>dol) + error("line out of range"); + return(a1); + } + if (relerr) + error("bad number"); + } +} + +static void +setdot(void) +{ + if (addr2 == 0) + addr1 = addr2 = dot; + if (addr1 > addr2) + error("bad range"); +} + +static void +setall(void) +{ + if (addr2==0) { + addr1 = zero+1; + addr2 = dol; + if (dol==zero) + addr1 = zero; + } + setdot(); +} + +static void +setnoaddr(void) +{ + if (addr2) + error("Illegal address count"); +} + +static void +nonzero(void) +{ + if (addr1<=zero || addr2>dol) + error("line out of range"); +} + +static void +newline(void) +{ + register int c; + + if ((c = getchr()) == '\n') + return; + if (c=='p' || c=='l' || c=='n') { + pflag++; + if (c=='l') + listf++; + else if (c=='n') + numbf = 1; + if (getchr() == '\n') + return; + } + error("illegal suffix"); +} + +static void +filename(int comm) +{ + register char *p1, *p2; + register int c, i; + + count = 0; + c = getchr(); + if (c=='\n' || c==EOF) { + p1 = savedfile; + if (*p1==0 && comm!='f') + error("illegal or missing filename"); + p2 = file; + while (*p2++ = *p1++) + ; + return; + } + if (c!=' ') + error("no space after command"); + while ((c = getchr()) == ' ') + ; + if (c=='\n') + error("illegal or missing filename"); + i = 0; + do { + if (i >= FNSIZE) + growfn("maximum of characters in file names reached"); + file[i++] = c; + if (c==' ' && file[0] != '!' || c==EOF) + error("illegal or missing filename"); + } while ((c = getchr()) != '\n'); + file[i++] = 0; + if ((savedfile[0]==0 || comm=='e' || comm=='f') && file[0] != '!') { + p1 = savedfile; + p2 = file; + while (*p1++ = *p2++) + ; + } +} + +static void +exfile(void) +{ + sclose(io); + io = -1; + if (wrtemp) { + extern int rename(const char *, const char *); + if (rename(wrtemp, file) < 0) + error("cannot create output file"); + if (myuid == 0) + chown(file, fstbuf.st_uid, fstbuf.st_gid); + chmod(file, fstbuf.st_mode & 07777); + free(wrtemp); + wrtemp = NULL; + } + if (vflag) { + putd(count); + putchr('\n'); + } +} + +static void +onintr(int signo) +{ + lastsig = signo; + putchr('\n'); + lastc = '\n'; + if (readop) { + puts("\007read may be incomplete - beware!\007"); + fchange = 0; + } + error("interrupt"); +} + +static void +onhup(int signo) +{ + if (dol > zero && fchange) { + addr1 = zero+1; + addr2 = dol; + io = creat("ed.hup", 0666); + if (io < 0) { + char *home = getenv("HOME"); + if (home) { + char *fn = malloc(strlen(home) + 10); + if (fn) { + strcpy(fn, home); + strcat(fn, "/ed.hup"); + io = creat(fn, 0666); + } + } + } + if (io >= 0) + putfile(); + } + fchange = 0; + status = 0200 | signo; + quit(0); +} + +static void +onpipe(int signo) +{ + lastsig = signo; + error("write or open on pipe failed"); +} + +static void +error(const char *s) +{ + error2(s, NULL); +} + +static void +error2(const char *s, const char *fn) +{ + register int c; + + wrapp = 0; + listf = 0; + numbf = 0; + errput(s, fn); + count = 0; + if (lseek(0, 0, SEEK_END) > 0) + status = 2; + pflag = 0; + if (globp) + lastc = '\n'; + globp = 0; + peekc = lastc; + if(lastc) + while ((c = getchr()) != '\n' && c != EOF) + ; + if (io > 0) { + sclose(io); + io = -1; + } + if (wrtemp) { + unlink(wrtemp); + free(wrtemp); + wrtemp = NULL; + } + longjmp(savej, 1); +} + +static void +errput(const char *s, const char *fn) +{ + prvmsg = s; + if (fn) { + putchr('?'); + puts(fn); + } else + puts("?"); + if (prhelp) + puts(s); +} + +static int +getchr(void) +{ + char c; + if (lastc=peekc) { + peekc = 0; + return(lastc); + } + if (globp) { + if ((lastc = *globp++) != 0) + return(lastc); + globp = 0; + return(EOF); + } + if (read(0, &c, 1) <= 0) + return(lastc = EOF); + lastc = c; + return(lastc); +} + +static int +gettty(void) +{ + register int c, i; + register char *gf; + + i = 0; + gf = globp; + while ((c = getchr()) != '\n') { + if (c==EOF) { + if (gf) + peekc = c; + return(c); + } + if (c == 0) + continue; + if (i >= LBSIZE) + growlb("line too long"); + linebuf[i++] = c; + } + if (i >= LBSIZE-2) + growlb("line too long"); + linebuf[i++] = 0; + if (linebuf[0]=='.' && linebuf[1]==0) + return(EOF); +#if !defined (SUS) && !defined (SU3) + if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) + linebuf[0]='.', linebuf[1]=0; +#endif + return(0); +} + +static long +getnum(void) +{ + char scount[20]; + int i; + + i = 0; + while ((peekc=getchr()) >= '0' && peekc <= '9' && i < sizeof scount) { + scount[i++] = peekc; + peekc = 0; + } + scount[i] = '\0'; + return i ? atol(scount) : -1; +} + +static int +getfile(void) +{ + register int c, i, j; + static int nextj; + + i = 0; + j = nextj; + do { + if (--ninbuf < 0) { + if (ioeof || (ninbuf=read(io, genbuf, LBSIZE)-1) < 0) { + if (ioeof == 0 && ninbuf < -1) { + puts("input error"); + status = 1; + } + if (i > 0) { + puts("'\\n' appended"); + c = '\n'; + ioeof = 1; + goto wrc; + } + return(EOF); + } + j = 0; + } + c = genbuf[j++]&0377; + wrc: if (i >= LBSIZE) { + lastc = '\n'; + growlb("line too long"); + } + linebuf[i++] = c ? c : '\n'; + count++; + } while (c != '\n'); + linebuf[--i] = 0; + nextj = j; + if (rspec && dot == zero) + fspec(linebuf); + if (maxlength && i > maxlength) { + putstr("line too long: lno = "); + putd((dot - zero+1)&MAXCNT); + putchr('\n'); + } + return(0); +} + +static void +putfile(void) +{ + long *a1; + int n; + register char *fp, *lp; + register int nib; + + nib = 512; + fp = genbuf; + a1 = addr1; + do { + lp = getline(*a1++, 0); + if (maxlength) { + for (n = 0; lp[n]; n++); + if (n > maxlength) { + putstr("line too long: lno = "); + putd((a1-1 - zero)&MAXCNT); + putchr('\n'); + } + } + for (;;) { + if (--nib < 0) { + n = fp-genbuf; + if(write(io, genbuf, n) != n) + error("write error"); + nib = 511; + fp = genbuf; + } + count++; + if ((*fp++ = *lp++) == 0) { + fp[-1] = '\n'; + break; + } else if (fp[-1] == '\n') + fp[-1] = '\0'; + } + } while (a1 <= addr2); + n = fp-genbuf; + if(write(io, genbuf, n) != n) + error("write error"); +} + +static int +append(int (*f)(void), long *a) +{ + register long *a1, *a2, *rdot; + int nline, tl; + + nline = 0; + dot = a; + while ((*f)() == 0) { + if ((dol-zero)+1 >= nlall) { + long *ozero = zero; + nlall += 512; + if ((zero = realloc(zero, nlall*sizeof *zero))==NULL) { + lastc = '\n'; + zero = ozero; + error("out of memory for append"); + } + dot += zero - ozero; + dol += zero - ozero; + addr1 += zero - ozero; + addr2 += zero - ozero; + if (unddot) { + unddot += zero - ozero; + unddol += zero - ozero; + } + if (undzero) { + ozero = undzero; + if ((undzero = realloc(undzero, + nlall*sizeof *undzero)) == 0) { + puts("no memory for undo"); + free(ozero); + } + } + } + tl = putline(); + nline++; + a1 = ++dol; + a2 = a1+1; + rdot = ++dot; + while (a1 > rdot) + *--a2 = *--a1; + *rdot = tl; + } + return(nline); +} + +static void +callunix(void) +{ + char *line; + void (*savint)(int); + pid_t pid, rpid; + int retcode; + + setnoaddr(); + line = readcmd(); + if ((pid = fork()) == 0) { + sigset(SIGHUP, oldhup); + sigset(SIGQUIT, oldquit); + sigset(SIGPIPE, oldpipe); + execl(SHELL, "sh", "-c", line, NULL); + _exit(0100); + } else if (pid < 0) + error("fork failed - try again"); + savint = sigset(SIGINT, SIG_IGN); + while ((rpid = wait(&retcode)) != pid && rpid != -1) + ; + sigset(SIGINT, savint); + if (vflag) + puts("!"); +} + +#define cmadd(c) ((i>=cmsize ? \ + ((line=realloc(line,cmsize+=128)) == 0 ? \ + (error("line too long"),0) : 0, 0) \ + : 0), line[i++]=(c)) + +static char * +readcmd(void) +{ + static char *line, *prev; + static int cmsize, pvsize; + char *pp; + int c, mod = 0, i; + + i = 0; + if ((c = getchr()) == '!') { + for (pp = prev; *pp; pp++) + line[i++] = *pp; + mod = 1; + c = getchr(); + } + while (c != '\n' && c != EOF) { + if (c == '\\') { + c = getchr(); + if (c != '%') + cmadd('\\'); + cmadd(c); + } else if (c == '%') { + for (pp = savedfile; *pp; pp++) + cmadd(*pp); + mod = 1; + } else + cmadd(c); + c = getchr(); + } + cmadd('\0'); + if (pvsize < cmsize && (prev = realloc(prev, pvsize=cmsize)) == 0) + error("line too long"); + strcpy(prev, line); + if (mod) + nlputs(line); + return line; +} + +static void +quit(int signo) +{ + lastsig = signo; + if (vflag && fchange) { + fchange = 0; + error("warning: expecting `w'"); + } + if (wrtemp) + unlink(wrtemp); + unlink(tfname); + exit(status); +} + +static void +delete(void) +{ + setdot(); + newline(); + nonzero(); + checkpoint(); + rdelete(addr1, addr2); +} + +static void +rdelete(long *ad1, long *ad2) +{ + register long *a1, *a2, *a3; + + a1 = ad1; + a2 = ad2+1; + a3 = dol; + dol -= a2 - a1; + do { + *a1++ = *a2++; + } while (a2 <= a3); + a1 = ad1; + if (a1 > dol) + a1 = dol; + dot = a1; + fchange = 1; +} + +static void +gdelete(void) +{ + register long *a1, *a2, *a3; + + a3 = dol; + for (a1=zero+1; (*a1&01)==0; a1++) + if (a1>=a3) + return; + for (a2=a1+1; a2<=a3;) { + if (*a2&01) { + a2++; + dot = a1; + } else + *a1++ = *a2++; + } + dol = a1-1; + if (dot>dol) + dot = dol; + fchange = 1; +} + +static char * +getline(long tl, int nulterm) +{ + register char *bp, *lp; + register long nl; + + lp = linebuf; + bp = getblock(tl, READ); + nl = nleft; + tl &= ~0377; + while (*lp++ = *bp++) { + if (lp[-1] == '\n' && nulterm) { + lp[-1] = '\0'; + break; + } + if (--nl == 0) { + bp = getblock(tl+=0400, READ); + nl = nleft; + } + } + return(linebuf); +} + +static int +putline(void) +{ + register char *bp, *lp; + register long nl; + long tl; + + fchange = 1; + lp = linebuf; + tl = tline; + bp = getblock(tl, WRITE); + nl = nleft; + tl &= ~0377; + while (*bp = *lp++) { + if (*bp++ == '\n' && insub) { + *--bp = 0; + linebp = lp; + break; + } + if (--nl == 0) { + bp = getblock(tl+=0400, WRITE); + nl = nleft; + } + } + nl = tline; + tline += (((lp-linebuf)+03)>>1)&(MAXCNT-1); + return(nl); +} + +static char * +getblock(long atl, long iof) +{ + register long bno, off; + + bno = (atl>>8)&BLKMSK; + off = (atl<<1)&0774; + if (bno >= BLKMSK) { + lastc = '\n'; + error("temp file too big"); + } + nleft = 512 - off; + if (bno==iblock) { + ichanged |= iof; + return(ibuff+off); + } + if (bno==oblock) + return(obuff+off); + if (iof==READ) { + if (ichanged) + blkio(iblock, ibuff, 1); + ichanged = 0; + iblock = bno; + blkio(bno, ibuff, 0); + return(ibuff+off); + } + if (oblock>=0) + blkio(oblock, obuff, 1); + oblock = bno; + return(obuff+off); +} + +static void +blkio(long b, char *buf, int wr) +{ + lseek(tfile, b<<9, SEEK_SET); + if ((wr ? write(tfile, buf, 512) : read (tfile, buf, 512)) != 512) { + status = 1; + error("I/O error on temp file"); + } +} + +static void +init(void) +{ + register long *markp; + + tline = 2; + for (markp = names; markp < &names[26]; markp++) + *markp = 0; + for (markp = undnames; markp < &undnames[26]; markp++) + *markp = 0; + subnewa = 0; + anymarks = 0; + iblock = -1; + oblock = -1; + ichanged = 0; + tfile = maketf(tfile); + dot = dol = zero; + unddot = NULL; +} + +static void +global(int k, int ia) +{ + register int c; + register long *a1; + static char *globuf; + char mb[MB_LEN_MAX+1]; + int spflag = 0; + + if (globp) + error("multiple globals not allowed"); + setall(); + nonzero(); + if ((c=GETWC(mb))=='\n') + error("incomplete global expression"); + compile(NULL, expbuf, &expbuf[ESIZE], c); + if (!ia) { + globrd(&globuf, EOF); + if (globuf[0] == '\n') + globuf[0] = 'p', globuf[1] = '\n', globuf[2] = '\0'; + } else { + newline(); + spflag = pflag; + pflag = 0; + } + checkpoint(); + for (a1=zero; a1<=dol; a1++) { + *a1 &= ~01; + if (a1>=addr1 && a1<=addr2 && execute(0, a1, 0)==k) + *a1 |= 01; + } + /* + * Special case: g/.../d (avoid n^2 algorithm) + */ + if (!ia && globuf[0]=='d' && globuf[1]=='\n' && globuf[2]=='\0') { + gdelete(); + return; + } + for (a1=zero; a1<=dol; a1++) { + if (*a1 & 01) { + *a1 &= ~01; + dot = a1; + if (ia) { + puts(getline(*a1, 0)); + if ((c = getchr()) == EOF) + error("command expected"); + if (c == 'a' || c == 'c' || c == 'i') + error("a, i, or c not allowed in G"); + else if (c == '&') { + if ((c = getchr()) != '\n') + error("end of line expected"); + if (globuf == 0 || *globuf == 0) + error("no remembered command"); + } else if (c == '\n') { + a1 = zero; + continue; + } else + globrd(&globuf, c); + } + globp = globuf; + commands(); + globp = NULL; + a1 = zero; + } + } + if (ia) + pflag = spflag; +} + +static void +globrd(char **globuf, register int c) +{ + register int i; + + if (*globuf == 0 && (*globuf = malloc(GBSIZE=256)) == 0) + error("global too long"); + i = 0; + if (c != EOF) + (*globuf)[i++] = c; + while ((c = getchr()) != '\n') { + if (c==EOF) + error("incomplete global expression"); + if (c=='\\') { + c = getchr(); + if (c!='\n') + (*globuf)[i++] = '\\'; + } + (*globuf)[i++] = c; + if (i>=GBSIZE-4 && (*globuf=realloc(*globuf,GBSIZE+=256)) == 0) + error("global too long"); + } + (*globuf)[i++] = '\n'; + (*globuf)[i++] = 0; +} + +static void +join(void) +{ + register int i, j; + register long *a1; + + j = 0; + for (a1=addr1; a1<=addr2; a1++) { + i = getline(*a1, 0) - linebuf; + while (genbuf[j] = linebuf[i++]) + if (j++ >= LBSIZE-2) + growlb("line too long"); + } + i = 0; + j = 0; + while (linebuf[i++] = genbuf[j++]) + ; + *addr1 = putline(); + if (addr1<addr2) + rdelete(addr1+1, addr2); + dot = addr1; +} + +static void +substitute(int inglob) +{ + register long *markp; + register long *a1; + intptr_t nl; + int gsubf; + + checkpoint(); + gsubf = compsub(); + insub = 1; + for (a1 = addr1; a1 <= addr2; a1++) { + long *ozero; + if (execute(0, a1, 1)==0) + continue; + inglob |= dosub(gsubf < 2); + if (gsubf) { + int i = 1; + + while (*loc2) { + if (execute(1, NULL, 1)==0) + break; + inglob |= dosub(gsubf == -1 || ++i == gsubf); + } + } + subnewa = putline(); + *a1 &= ~01; + if (anymarks) { + for (markp = names; markp < &names[26]; markp++) + if (*markp == *a1) + *markp = subnewa; + } + *a1 = subnewa; + ozero = zero; + nl = append(getsub, a1); + nl += zero-ozero; + a1 += nl; + addr2 += nl; + } + insub = 0; + if (inglob==0) + error("no match"); +} + +static int +compsub(void) +{ + register int seof, c, i; + static char *oldrhs; + static int orhssz; + char mb[MB_LEN_MAX+1]; + + if ((seof = GETWC(mb)) == '\n' || seof == ' ') + error("illegal or missing delimiter"); + nodelim = 0; + compile(NULL, expbuf, &expbuf[ESIZE], seof); + i = 0; + for (;;) { + c = GETWC(mb); + if (c=='\\') { + if (i >= RHSIZE-2) + growrhs("replacement string too long"); + rhsbuf[i++] = c; + c = GETWC(mb); + } else if (c=='\n') { + if (globp && *globp) { + if (i >= RHSIZE-2) + growrhs("replacement string too long"); + rhsbuf[i++] = '\\'; + } + else if (nodelim) + error("illegal or missing delimiter"); + else { + peekc = c; + pflag++; + break; + } + } else if (c==seof) + break; + for (c = 0; c==0 || mb[c]; c++) { + if (i >= RHSIZE-2) + growrhs("replacement string too long"); + rhsbuf[i++] = mb[c]; + } + } + rhsbuf[i++] = 0; + if (rhsbuf[0] == '%' && rhsbuf[1] == 0) { + if (orhssz == 0) + error("no remembered replacement string"); + strcpy(rhsbuf, oldrhs); + } else { + if (orhssz < RHSIZE && + (oldrhs = realloc(oldrhs, orhssz=RHSIZE)) == 0) + error("replacement string too long"); + strcpy(oldrhs, rhsbuf); + } + if ((peekc = getchr()) == 'g') { + peekc = 0; + newline(); + return(-1); + } else if (peekc >= '0' && peekc <= '9') { + c = getnum(); + if (c < 1 || c > LBSIZE) + error("invalid count"); + newline(); + return c; + } + newline(); + return(0); +} + +static int +getsub(void) +{ + register char *p1, *p2; + + p1 = linebuf; + if ((p2 = linebp) == 0) + return(EOF); + while (*p1++ = *p2++) + ; + linebp = 0; + return(0); +} + +static int +dosub(int really) +{ + register char *lp, *sp; + register int i, j, k; + int c; + + if (!really) + goto copy; + i = 0; + j = 0; + k = 0; + while (&linebuf[i] < loc1) + genbuf[j++] = linebuf[i++]; + while (c = rhsbuf[k++]&0377) { + if (c=='&') { + j = place(j, loc1, loc2); + continue; + } else if (c == '\\') { + c = rhsbuf[k++]&0377; + if (c >='1' && c < nbra+'1') { + j = place(j, braslist[c-'1'], braelist[c-'1']); + continue; + } + } + if (j >= LBSIZE) + growlb("line too long"); + genbuf[j++] = c; + } + i = loc2 - linebuf; + loc2 = j + linebuf; +#if defined (SUS) || defined (SU3) || defined (S42) + if (loc1 == &linebuf[i]) { + int n; + wchar_t wc; + if (mb_cur_max > 1 && (n = mbtowc(&wc, loc2, mb_cur_max)) > 0) + loc2 += n; + else + loc2++; + } +#endif /* SUS || SU3 || S42 */ + while (genbuf[j++] = linebuf[i++]) + if (j >= LBSIZE) + growlb("line too long"); + if (really) { + lp = linebuf; + sp = genbuf; + } else { + copy: sp = linebuf; + lp = genbuf; + } + while (*lp++ = *sp++) + ; + return really; +} + +static int +place(register int j, register const char *l1, register const char *l2) +{ + + while (l1 < l2) { + genbuf[j++] = *l1++; + if (j >= LBSIZE) + growlb("line too long"); + } + return(j); +} + +static void +move(int cflag) +{ + register long *adt, *ad1, *ad2; + + setdot(); + nonzero(); + if ((adt = address())==0) + error("illegal move destination"); + newline(); + checkpoint(); + if (cflag) { + long *ozero; + intptr_t delta; + ad1 = dol; + ozero = zero; + append(getcopy, ad1++); + ad2 = dol; + delta = zero - ozero; + ad1 += delta; + adt += delta; + } else { + ad2 = addr2; + for (ad1 = addr1; ad1 <= ad2;) + *ad1++ &= ~01; + ad1 = addr1; + } + ad2++; + if (adt<ad1) { + dot = adt + (ad2-ad1); + if ((++adt)==ad1) + return; + reverse(adt, ad1); + reverse(ad1, ad2); + reverse(adt, ad2); + } else if (adt >= ad2) { + dot = adt++; + reverse(ad1, ad2); + reverse(ad2, adt); + reverse(ad1, adt); + } else + error("illegal move destination"); + fchange = 1; +} + +static void +reverse(register long *a1, register long *a2) +{ + register int t; + + for (;;) { + t = *--a2; + if (a2 <= a1) + return; + *a2 = *a1; + *a1++ = t; + } +} + +static int +getcopy(void) +{ + if (addr1 > addr2) + return(EOF); + getline(*addr1++, 0); + return(0); +} + +static int +execute(int gf, long *addr, int subst) +{ + register char *p1, *p2, c; + + for (c=0; c<NBRA; c++) { + braslist[c&0377] = 0; + braelist[c&0377] = 0; + } + if (gf) { + if (circf) + return(0); + p1 = linebuf; + p2 = genbuf; + while (*p1++ = *p2++) + ; + locs = p1 = loc2; + } else { + if (addr==zero) + return(0); + p1 = getline(*addr, 1); + locs = 0; + } + needsub = subst; + return step(p1, expbuf); +} + +static void +cmplerr(int c) +{ + const char *msg; + +#if !defined (SUS) && !defined (S42) && !defined (SU3) + expbuf[0] = 0; +#endif + switch (c) { + case 11: + msg = "Range endpoint too large"; + break; + case 16: + msg = "bad number"; + break; + case 25: + msg = "`\\digit' out of range"; + break; + case 36: + msg = "illegal or missing delimiter"; + break; + case 41: + msg = "no remembered search string"; + break; + case 42: + msg = "'\\( \\)' imbalance"; + break; + case 43: + msg = "Too many `\\(' s"; + break; + case 44: + msg = "more than 2 numbers given"; + break; + case 45: + msg = "'\\}' expected"; + break; + case 46: + msg = "first number exceeds second"; + break; + case 49: + msg = "'[ ]' imbalance"; + break; + case 50: + msg = "regular expression overflow"; + break; + case 67: + msg = "illegal byte sequence"; + break; + default: + msg = "regular expression error"; + break; + } + error(msg); +} + +static void +doprnt(long *bot, long *top) +{ + long *a1; + + a1 = bot; + do { + if (numbf ^ Nflag) { + putd(a1-zero); + putchr('\t'); + } + nlputs(getline(*a1++, 0)); + } while (a1 <= top); + pflag = 0; + listf = 0; + numbf = 0; +} + +static void +putd(long c) +{ + register int r; + + r = c%10; + c /= 10; + if (c) + putd(c); + putchr(r + '0'); +} + +static void +nlputs(register const char *sp) +{ + if (listf) + list(sp); + else if (tabstops) + expand(sp); + else + puts(sp); +} + +static void +puts(register const char *sp) +{ + while (*sp) { + if (*sp != '\n') + putchr(*sp++ & 0377); + else + sp++, putchr('\0'); + } + putchr('\n'); +} + +static void +list(const char *lp) +{ + int col, n; + wchar_t c; + + col = numbf ^ Nflag ? 8 : 0; + while (*lp) { + if (mb_cur_max > 1 && *lp&0200) + n = mbtowc(&c, lp, mb_cur_max); + else { + n = 1; + c = *lp&0377; + } + if (col+1 >= 72) { + col = 0; + putchr('\\'); + putchr('\n'); + } + if (n<0 || +#if defined (SUS) || defined (S42) || defined (SU3) + c == '\\' || +#endif /* SUS || S42 || SU3 */ + !(mb_cur_max>1 ? iswprint(c) : isprint(c))) { + if (n<0) + n = 1; + while (n--) + col += lstchr(*lp++&0377); + } else if (mb_cur_max>1) { + col += wcwidth(c); + while (n--) + putchr(*lp++&0377); + } else { + putchr(*lp++&0377); + col++; + } + } +#if defined (SUS) || defined (S42) || defined (SU3) + putchr('$'); +#endif + putchr('\n'); +} + +static int +lstchr(int c) +{ + int cad = 1, d; + +#if !defined (SUS) && !defined (S42) && !defined (SU3) + if (c=='\t') { + c = '>'; + goto esc; + } + if (c=='\b') { + c = '<'; + esc: + putchr('-'); + putchr('\b'); + putchr(c); + } else if (c == '\n') { + putchr('\\'); + putchr('0'); + putchr('0'); + putchr('0'); + cad = 4; +#else /* !SUS, !S42, !SU3 */ + if (c == '\n') + c = '\0'; + if (c == '\\') { + putchr('\\'); + putchr('\\'); + cad = 2; + } else if (c == '\a') { + putchr('\\'); + putchr('a'); + cad = 2; + } else if (c == '\b') { + putchr('\\'); + putchr('b'); + cad = 2; + } else if (c == '\f') { + putchr('\\'); + putchr('f'); + cad = 2; + } else if (c == '\r') { + putchr('\\'); + putchr('r'); + cad = 2; + } else if (c == '\t') { + putchr('\\'); + putchr('t'); + cad = 2; + } else if (c == '\v') { + putchr('\\'); + putchr('v'); + cad = 2; +#endif /* !SUS, !S42, !SU3 */ + } else { + putchr('\\'); + putchr(((c&~077)>>6)+'0'); + c &= 077; + d = c & 07; + putchr(c > d ? ((c-d)>>3)+'0' : '0'); + putchr(d+'0'); + cad = 4; + } + return cad; +} + +static void +putstr(const char *s) +{ + while (*s) + putchr(*s++); +} + +static char line[70]; +static char *linp = line; + +static void +putchr(int ac) +{ + register char *lp; + register int c; + + lp = linp; + c = ac; + *lp++ = c; + if(c == '\n' || lp >= &line[64]) { + linp = line; + write(1, line, lp-line); + return; + } + linp = lp; +} + +static void +checkpoint(void) +{ + long *a1, *a2; + + if (undzero && globp == NULL) { + for (a1 = zero+1, a2 = undzero+1; a1 <= dol; a1++, a2++) + *a2 = *a1; + unddot = &undzero[dot-zero]; + unddol = &undzero[dol-zero]; + for (a1 = names, a2 = undnames; a1 < &names[26]; a1++, a2++) + *a2 = *a1; + } +} + +#define swap(a, b) (t = a, a = b, b = t) + +static void +undo(void) +{ + long *t; + + if (undzero == NULL) + error("no undo information saved"); + swap(zero, undzero); + swap(dot, unddot); + swap(dol, unddol); + swap(names, undnames); +} + +static int +maketf(int fd) +{ + char *tmpdir; + + if (fd == -1) { + if ((tmpdir = getenv("TMPDIR")) == NULL || + (fd = creatf(tmpdir)) < 0) + if ((fd = creatf("/var/tmp")) < 0 && + (fd = creatf("/tmp")) < 0) + error("cannot create temporary file"); + } else + ftruncate(fd, 0); /* blkio() will seek to 0 anyway */ + return fd; +} + +static int +creatf(const char *tmpdir) +{ + if (strlen(tmpdir) >= sizeof tfname - 9) + return -1; + strcpy(tfname, tmpdir); + strcat(tfname, "/eXXXXXX"); + return mkstemp(tfname); +} + +static int +sopen(const char *fn, int rdwr) +{ + int pf[2], fd = -1; + + if (fn[0] == '!') { + fn++; + if (pipe(pf) < 0) + error("write or open on pipe failed"); + switch (pipid = fork()) { + case 0: + if (rdwr == READ) + dup2(pf[1], 1); + else + dup2(pf[0], 0); + close(pf[0]); + close(pf[1]); + sigset(SIGHUP, oldhup); + sigset(SIGQUIT, oldquit); + sigset(SIGPIPE, oldpipe); + execl(SHELL, "sh", "-c", fn, NULL); + _exit(0100); + default: + close(pf[rdwr == READ ? 1 : 0]); + fd = pf[rdwr == READ ? 0 : 1]; + break; + case -1: + error("fork failed - try again"); + } + } else if (rdwr == READ) + fd = open(fn, O_RDONLY); + else if (rdwr == EXIST) + fd = open(fn, O_WRONLY); + else /*if (rdwr == WRITE)*/ + fd = creat(fn, 0666); + if (fd >= 0 && rdwr == READ) + readop = 1; + if (fd >= 0) + fstat(fd, &fstbuf); + return fd; +} + +static void +sclose(int fd) +{ + int status; + + close(fd); + if (pipid >= 0) { + while (wait(&status) != pipid); + pipid = -1; + } + readop = 0; +} + +static void +fspec(const char *lp) +{ + struct termios ts; + const char *cp; + + freetabs(); + maxlength = 0; + if (tcgetattr(1, &ts) < 0 +#ifdef TAB3 + || (ts.c_oflag&TAB3) == 0 +#endif + ) + return; + while (lp[0]) { + if (lp[0] == '<' && lp[1] == ':') + break; + lp++; + } + if (lp[0]) { + lp += 2; + while ((cp = ftok(&lp)) != NULL) { + switch (*cp) { + case 't': + freetabs(); + if ((tabstops = tabstring(&cp[1])) == NULL) + goto err; + break; + case 's': + maxlength = atoi(&cp[1]); + break; + case 'm': + case 'd': + case 'e': + break; + case ':': + if (cp[1] == '>') { + if (tabstops == NULL) + if ((tabstops = tabstring("0")) + == NULL) + goto err; + return; + } + /*FALLTHRU*/ + default: + err: freetabs(); + maxlength = 0; + errput("PWB spec problem", NULL); + return; + } + } + } +} + +static const char * +ftok(const char **lp) +{ + const char *cp; + + while (**lp && **lp != ':' && (**lp == ' ' || **lp == '\t')) + (*lp)++; + cp = *lp; + while (**lp && **lp != ':' && **lp != ' ' && **lp != '\t') + (*lp)++; + return cp; +} + +static struct tabulator * +repetitive(int repetition) +{ + struct tabulator *tp, *tabspec; + int col, i; + + if ((tp = tabspec = calloc(1, sizeof *tp)) == NULL) + return NULL; + tp->t_rep = repetition; + if (repetition > 0) { + for (col = 1+repetition, i = 0; i < 22; col += repetition) { + if ((tp->t_nxt = calloc(1, sizeof *tp)) == NULL) + return NULL; + tp = tp->t_nxt; + tp->t_tab = col; + } + } + return tabspec; +} + +#define blank(c) ((c) == ' ' || (c) == '\t') + +static struct tabulator * +tablist(const char *s) +{ + struct tabulator *tp, *tabspec; + char *x; + int prev = 0, val; + + if ((tp = tabspec = calloc(1, sizeof *tp)) == NULL) + return NULL; + for (;;) { + while (*s == ',') + s++; + if (*s == '\0' || blank(*s) || *s == ':') + break; + val = strtol(s, &x, 10); + if (*s == '+') + val += prev; + prev = val; + if (*s == '-' || (*x != ',' && !blank(*x) && *x != ':' && + *x != '\0')) + return NULL; + s = x; + if ((tp->t_nxt = calloc(1, sizeof *tp)) == NULL) + return NULL; + tp = tp->t_nxt; + tp->t_tab = val; + } + return tabspec; +} + +static struct tabulator * +tabstring(const char *s) +{ + const struct { + const char *c_nam; + const char *c_str; + } canned[] = { + { "a", "1,10,16,36,72" }, + { "a2", "1,10,16,40,72" }, + { "c", "1,8,12,16,20,55" }, + { "c2", "1,6,10,14,49" }, + { "c3", "1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67" }, + { "f", "1,7,11,15,19,23" }, + { "p", "1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61" }, + { "s", "1,10,55" }, + { "u", "1,12,20,44" }, + { 0, 0 } + }; + + int i, j; + + if (s[0] == '-') { + if (s[1] >= '0' && s[1] <= '9' && ((i = atoi(&s[1])) != 0)) + return repetitive(i); + for (i = 0; canned[i].c_nam; i++) { + for (j = 0; canned[i].c_nam[j]; j++) + if (s[j+1] != canned[i].c_nam[j]) + break; + if ((s[j+1]=='\0' || s[j+1]==':' || blank(s[j+1])) && + canned[i].c_nam[j] == '\0') + return tablist(canned[i].c_str); + } + return NULL; + } else + return tablist(s); +} + +static void +freetabs(void) +{ + struct tabulator *tp; + + tp = tabstops; + while (tp) { + tabstops = tp->t_nxt; + free(tp); + tp = tabstops; + } +} + +static void +expand(const char *s) +{ + struct tabulator *tp = tabstops; + int col = 0, n = 1, m, tabcnt = 0, nspc; + wchar_t wc; + + while (*s) { + nspc = 0; + switch (*s) { + case '\n': + putchr('\0'); + s++; + continue; + case '\t': + if (tp) { + if (tp->t_rep) { + if (col % tp->t_rep == 0) { + nspc++; + col++; + } + while (col % tp->t_rep) { + nspc++; + col++; + } + break; + } + while (tp && (col>tp->t_tab || tp->t_tab == 0)) + tp = tp->t_nxt; + if (tp && col == tp->t_tab) { + nspc++; + col++; + tp = tp->t_nxt; + } + if (tp) { + while (col < tp->t_tab) { + nspc++; + col++; + } + tp = tp->t_nxt; + break; + } + } + tabcnt = 1; + nspc++; + break; + default: + if (mb_cur_max>1 && (n=mbtowc(&wc, s, mb_cur_max))>0) { + if ((m = wcwidth(wc)) > 0) + col += m; + } else { + col++; + n = 1; + } + } + if (maxlength && col > maxlength) { + putstr("\ntoo long"); + break; + } + if (nspc) { + while (nspc--) + putchr(' '); + s++; + } else + while (n--) + putchr(*s++); + } + if (tabcnt) + putstr("\ntab count"); + putchr('\n'); +} + +static wint_t +GETWC(char *mb) +{ + int c, n; + + n = 1; + mb[0] = c = GETC(); + mb[1] = '\0'; + 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 = GETC(); + mb[n] = '\0'; + if (c == '\n' || c == EOF) + break; + } + if (m != n) + ERROR(67); + return wc; + } else + return c; +} + +static void +growlb(const char *msg) +{ + char *olb = linebuf; + int i; + + LBSIZE += 512; + if ((linebuf = realloc(linebuf, LBSIZE)) == NULL || + (genbuf = realloc(genbuf, LBSIZE)) == NULL) + error(msg); + if (linebuf != olb) { + loc1 += linebuf - olb; + loc2 += linebuf - olb; + for (i = 0; i < NBRA; i++) { + if (braslist[i]) + braslist[i] += linebuf - olb; + if (braelist[i]) + braelist[i] += linebuf - olb; + } + } +} + +static void +growrhs(const char *msg) +{ + RHSIZE += 256; + if ((rhsbuf = realloc(rhsbuf, RHSIZE)) == NULL) + error(msg); +} + +static void +growfn(const char *msg) +{ + FNSIZE += 64; + if ((savedfile = realloc(savedfile, FNSIZE)) == NULL || + (file = realloc(file, FNSIZE)) == NULL) + error(msg); + if (FNSIZE == 64) + file[0] = savedfile[0] = 0; +} + +#if defined (SUS) || defined (S42) || defined (SU3) +union ptrstore { + void *vp; + char bp[sizeof (void *)]; +}; + +static void * +fetchptr(const char *bp) +{ + union ptrstore u; + int i; + + for (i = 0; i < sizeof (void *); i++) + u.bp[i] = bp[i]; + return u.vp; +} + +static void +storeptr(void *vp, char *bp) +{ + union ptrstore u; + int i; + + u.vp = vp; + for (i = 0; i < sizeof (void *); i++) + bp[i] = u.bp[i]; +} + +#define add(c) ((i>=LBSIZE ? (growlb("regular expression overflow"),0) : 0), \ + genbuf[i++] = (c)) + +#define copy(s) { \ + int m; \ + for (m = 0; m==0 || s[m]; m++) \ + add(s[m]); \ +} + +static char * +compile(char *unused, char *ep, const char *endbuf, int seof) +{ + INIT + int c, d, i; + regex_t *rp; + char *op; + char mb[MB_LEN_MAX+1]; + + op = ep; + ep += 2; + if ((rp = fetchptr(ep)) == NULL) { + if ((rp = calloc(1, sizeof *rp)) == NULL) + ERROR(50); + storeptr(rp, ep); + } + ep += sizeof (void *); + i = 0; + nbra = 0; + do { + if ((c = GETWC(mb)) == seof) + add('\0'); + else if (c == '\\') { + copy(mb); + c = GETWC(mb); + if (c == '(') + nbra++; + goto normchar; + } else if (c == '[') { + add(c); + d = EOF; + do { + c = GETWC(mb); + if (c == EOF || c == '\n') + ERROR(49); + copy(mb); + if (d=='[' && (c==':' || c=='.' || c=='=')) { + d = c; + do { + c = GETWC(mb); + if (c == EOF || c == '\n') + ERROR(49); + copy(mb); + } while (c != d || PEEKC() != ']'); + c = GETWC(mb); + copy(mb); + c = EOF; + } + d = c; + } while (c != ']'); + } else { + if (c == EOF || c == '\n') { + if (c == '\n') + UNGETC(c); + mb[0] = c = '\0'; + } + if (c == '\0') + nodelim = 1; + normchar: copy(mb); + } + } while (genbuf[i-1] != '\0'); + if (genbuf[0]) { + int reflags = 0; + +#ifdef REG_ANGLES + reflags |= REG_ANGLES; +#endif +#if defined (SU3) && defined (REG_AVOIDNULL) + reflags |= REG_AVOIDNULL; +#endif + if (op[0]) + regfree(rp); + op[0] = 0; + switch (regcomp(rp, genbuf, reflags)) { + case 0: + break; + case REG_ESUBREG: + ERROR(25); + /*NOTREACHED*/ + case REG_EBRACK: + ERROR(49); + /*NOTREACHED*/ + case REG_EPAREN: + ERROR(42); + /*NOTREACHED*/ + case REG_BADBR: + case REG_EBRACE: + ERROR(45); + /*NOTREACHED*/ + case REG_ERANGE: + ERROR(11); + /*NOTREACHED*/ + case REG_ESPACE: + ERROR(50); + /*NOTREACHED*/ + default: + ERROR(-1); + } + op[0] = 1; + circf = op[1] = genbuf[0] == '^'; + } else if (op[0]) { + circf = op[1]; + } else + ERROR(41); + return ep + sizeof (void *); +} + +static int +step(const char *lp, const char *ep) +{ + regex_t *rp; + regmatch_t bralist[NBRA+1]; + int eflag = 0; + int res; + int i; + + rp = fetchptr(&ep[2]); + if (ep[0] == 0) + return 0; + if (locs) + eflag |= REG_NOTBOL; + if ((res = regexec(rp, lp, needsub? NBRA+1 : 0, bralist, eflag)) == 0 && + needsub) { + loc1 = (char *)lp + bralist[0].rm_so; + loc2 = (char *)lp + bralist[0].rm_eo; + for (i = 1; i <= NBRA; i++) { + if (bralist[i].rm_so != -1) { + braslist[i-1] = (char *)lp + bralist[i].rm_so; + braelist[i-1] = (char *)lp + bralist[i].rm_eo; + } else + braslist[i-1] = braelist[i-1] = NULL; + } + } + return res == 0; +} +#endif /* SUS || S42 || SU3 */ + +static void +help(void) +{ + const char *desc[] = { + "(.)a append up to .", + "(.)b[n] browse n lines", + "(.,.)c change up to .", + "(.,.)d delete lines", + "e [file] edit file", + "E [file] force edit", + "f [file] print or set file", + "(1,$)g/RE/cmd global cmd", + "(1,$)G/RE/ interactive global", + "h print last error", + "H toggle error messages", + "help print this screen", + "(.)i insert up to .", + "(.,.+1)j join lines", + "(.)kx mark line with x", + "(.,.)l list lines", + "(.,.)ma move lines to a", + "(.,.)n number lines", + "N revert n and p", + "(.)o[n] show n lines of context", + "(.,.)p print lines", + "P toggle prompt", + "q quit", + "Q force quit", + "($)r read file", + "(.,.)s/RE/repl/ search and replace", + "(.,.)s/RE/rp/g replace all occurrences", + "(.,.)s/RE/rp/n replace n-th occurrence", + "(.,.)ta transfer lines to a", + "u undo last change", + "(1,$)v/RE/cmd reverse global", + "(1,$)V/RE/ reverse i/a global", + "(1,$)w [file] write file", + "(1,$)W [file] append to file", + "z write buffer and quit", + "($)= print line number", + "!command execute shell command", + "(.+1)<newline> print one line", + "/RE find RE forwards", + "?RE find RE backwards", + "1 first line", + ". current line", + "$ last line", + ", 1,$", + "; .,$", + NULL + }; + char line[100]; + int c, half, i, k; + + half = (sizeof desc / sizeof *desc) / 2; + for (i = 0; i < half && desc[i]; i++) { + c = 0; + for (k = 0; desc[i][k]; k++) + line[c++] = desc[i][k]; + if (desc[i+half]) { + while (c < 40) + line[c++] = ' '; + for (k = 0; desc[i+half][k]; k++) + line[c++] = desc[i+half][k]; + } + line[c] = 0; + puts(line); + } +}