/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Editor */ #include <crypt.h> #include <libgen.h> #include <wait.h> #include <string.h> #include <sys/types.h> #include <locale.h> #include <regexpr.h> #include <regex.h> #include <errno.h> #include <paths.h> static const char *msgtab[] = { "write or open on pipe failed", /* 0 */ "warning: expecting `w'", /* 1 */ "mark not lower case ascii", /* 2 */ "Cannot open input file", /* 3 */ "PWB spec problem", /* 4 */ "nothing to undo", /* 5 */ "restricted shell", /* 6 */ "cannot create output file", /* 7 */ "filesystem out of space!", /* 8 */ "cannot open file", /* 9 */ "cannot link", /* 10 */ "Range endpoint too large", /* 11 */ "unknown command", /* 12 */ "search string not found", /* 13 */ "-", /* 14 */ "line out of range", /* 15 */ "bad number", /* 16 */ "bad range", /* 17 */ "Illegal address count", /* 18 */ "incomplete global expression", /* 19 */ "illegal suffix", /* 20 */ "illegal or missing filename", /* 21 */ "no space after command", /* 22 */ "fork failed - try again", /* 23 */ "maximum of 64 characters in file names", /* 24 */ "`\\digit' out of range", /* 25 */ "interrupt", /* 26 */ "line too long", /* 27 */ "illegal character in input file", /* 28 */ "write error", /* 29 */ "out of memory for append", /* 30 */ "temp file too big", /* 31 */ "I/O error on temp file", /* 32 */ "multiple globals not allowed", /* 33 */ "global too long", /* 34 */ "no match", /* 35 */ "illegal or missing delimiter", /* 36 */ "-", /* 37 */ "replacement string too long", /* 38 */ "illegal move destination", /* 39 */ "-", /* 40 */ "no remembered search string", /* 41 */ "'\\( \\)' imbalance", /* 42 */ "Too many `\\(' s", /* 43 */ "more than 2 numbers given", /* 44 */ "'\\}' expected", /* 45 */ "first number exceeds second", /* 46 */ "incomplete substitute", /* 47 */ "newline unexpected", /* 48 */ "'[ ]' imbalance", /* 49 */ "regular expression overflow", /* 50 */ "regular expression error", /* 51 */ "command expected", /* 52 */ "a, i, or c not allowed in G", /* 53 */ "end of line expected", /* 54 */ "no remembered replacement string", /* 55 */ "no remembered command", /* 56 */ "illegal redirection", /* 57 */ "possible concurrent update", /* 58 */ "-", /* 59 */ "the x command has become X (upper case)", /* 60 */ "Warning: 'w' may destroy input file " "(due to `illegal char' read earlier)", /* 61 */ "Caution: 'q' may lose data in buffer;" " 'w' may destroy input file", /* 62 */ "Encryption of string failed", /* 63 */ "Encryption facility not available", /* 64 */ "Cannot encrypt temporary file", /* 65 */ "Enter key:", /* 66 */ "Illegal byte sequence", /* 67 */ "File does not exist", /* 68 */ "tempnam failed", /* 69 */ "Cannot open temporary file", /* 70 */ 0 }; #include <stdlib.h> #include <limits.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/statvfs.h> #include <unistd.h> #include <termio.h> #include <ctype.h> #include <setjmp.h> #include <fcntl.h> #include <wchar.h> /* I18N */ #include <wctype.h> /* I18N */ #include <widec.h> /* I18N */ #define FTYPE(A) (A.st_mode) #define FMODE(A) (A.st_mode) #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino) #define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK) #define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR) #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR) #define ISFIFO(A) ((A.st_mode & S_IFMT) == S_IFIFO) #define ISREG(A) ((A.st_mode & S_IFMT) == S_IFREG) #define PUTM() if (xcode >= 0) puts(gettext(msgtab[xcode])) #define UNGETC(c) (peekc = c) #define FNSIZE PATH_MAX #define LBSIZE LINE_MAX /* size of substitution replacement pattern buffer */ #define RHSIZE (LINE_MAX*2) #define KSIZE 8 #define READ 0 #define WRITE 1 extern char *optarg; /* Value of argument */ extern int optind; /* Indicator of argument */ extern int __xpg4; /* defined in xpg4.c; 0 if not xpg4-compiled program */ struct Fspec { char Ftabs[22]; char Fdel; unsigned char Flim; char Fmov; char Ffill; }; static struct Fspec fss; static char *fsp; static int fsprtn; static char line[70]; static char *linp = line; static int sig; static int Xqt = 0; static int lastc; static char savedfile[FNSIZE]; static char file[FNSIZE]; static char funny[FNSIZE]; static int funlink = 0; static char linebuf[LBSIZE]; static char *tstring = linebuf; static char *expbuf; static char rhsbuf[RHSIZE]; struct lin { long cur; long sav; }; typedef struct lin *LINE; static LINE zero; static LINE dot; static LINE dol; static LINE endcore; static LINE fendcore; static LINE addr1; static LINE addr2; static LINE savdol, savdot; static int globflg; static int initflg; static char genbuf[LBSIZE]; static long count; static int numpass; /* Number of passes thru dosub(). */ static int gsubf; /* Occurrence value. LBSIZE-1=all. */ static int ocerr1; /* Allows lines NOT changed by dosub() to NOT be put */ /* out. Retains last line changed as current line. */ static int ocerr2; /* Flags if ANY line changed by substitute(). 0=nc. */ static char *nextip; static char *linebp; static int ninbuf; static int peekc; static int io; static void (*oldhup)(), (*oldintr)(); static void (*oldquit)(), (*oldpipe)(); static void quit(int); static int vflag = 1; static int xflag; static int xtflag; static int kflag; static int crflag; /* Flag for determining if file being read is encrypted */ static int hflag; static int xcode = -1; static char crbuf[LBSIZE]; static int perm[2]; static int tperm[2]; static int permflag; static int tpermflag; static int col; static char *globp; static int tfile = -1; static int tline; static char *tfname; extern char *locs; static char ibuff[LBSIZE]; static int iblock = -1; static char obuff[LBSIZE]; static int oblock = -1; static int ichanged; static int nleft; static long savnames[26], names[26]; static int anymarks; static long subnewa; static int fchange; static int nline; static int fflg, shflg; static char prompt[16] = "*"; static int rflg; static int readflg; static int eflg; static int qflg = 0; static int ncflg; static int listn; static int listf; static int pflag; static int flag28 = 0; /* Prevents write after a partial read */ static int save28 = 0; /* Flag whether buffer empty at start of read */ static long savtime; static char *name = "SHELL"; static char *rshell = "/usr/lib/rsh"; static char *val; static char *home; static int nodelim; int makekey(int *); int _mbftowc(char *, wchar_t *, int (*)(), int *); static int error(int code); static void tlist(struct Fspec *); static void tstd(struct Fspec *); static void gdelete(void); static void delete(void); static void exfile(void); static void filename(int comm); static void newline(void); static int gettty(void); static void commands(void); static void undo(void); static void save(void); static void strcopy(char *source, char *dest); static int strequal(char **scan1, char *str); static int stdtab(char *, char *); static int lenchk(char *, struct Fspec *); static void clear(struct Fspec *); static int expnd(char *, char *, int *, struct Fspec *); static void tincr(int, struct Fspec *); static void targ(struct Fspec *); static int numb(void); static int fspec(char *, struct Fspec *, int); static void red(char *); static void newtime(void); static void chktime(void); static void getime(void); static void mkfunny(void); static int eopen(char *, int); static void eclose(int f); static void globaln(int); static char *getkey(const char *); static int execute(int, LINE); static void error1(int); static int getcopy(void); static void move(int); static void dosub(void); static int getsub(void); static int compsub(void); static void substitute(int); static void join(void); static void global(int); static void init(void); static void rdelete(LINE, LINE); static void append(int (*)(void), LINE); static int getfile(void); static void putfile(void); static void onpipe(int); static void onhup(int); static void onintr(int); static void setdot(void); static void setall(void); static void setnoaddr(void); static void nonzero(void); static void setzeroasone(void); static long putline(void); static LINE address(void); static char *getaline(long); static char *getblock(long, long); static char *place(char *, char *, char *); static void comple(wchar_t); static void putchr(unsigned char); static void putwchr(wchar_t); static int getchr(void); static void unixcom(void); static void blkio(int, char *, ssize_t (*)()); static void reverse(LINE, LINE); static void putd(); static wchar_t get_wchr(void); static struct stat Fl, Tf; #ifndef RESEARCH static struct statvfs U; static int Short = 0; static mode_t oldmask; /* No umask while writing */ #endif static jmp_buf savej; #ifdef NULLS int nulls; /* Null count */ #endif static long ccount; static int errcnt = 0; static void onpipe(int sig) { (int)error(0); } int main(int argc, char **argv) { char *p1, *p2; int c; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); oldquit = signal(SIGQUIT, SIG_IGN); oldhup = signal(SIGHUP, SIG_IGN); oldintr = signal(SIGINT, SIG_IGN); oldpipe = signal(SIGPIPE, onpipe); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, quit); p1 = *argv; while (*p1++); while (--p1 >= *argv) if (*p1 == '/') break; *argv = p1 + 1; /* if SHELL set in environment and is /usr/lib/rsh, set rflg */ if ((val = getenv(name)) != NULL) if (strcmp(val, rshell) == 0) rflg++; if (**argv == 'r') rflg++; home = getenv("HOME"); while (1) { while ((c = getopt(argc, argv, "sp:qxC")) != EOF) { switch (c) { case 's': vflag = 0; break; case 'p': strncpy(prompt, optarg, sizeof (prompt)-1); shflg = 1; break; case 'q': signal(SIGQUIT, SIG_DFL); vflag = 1; break; case 'x': crflag = -1; xflag = 1; break; case 'C': crflag = 1; xflag = 1; break; case '?': (void) fprintf(stderr, gettext( "Usage: ed [- | -s] [-p string] [-x] [-C] [file]\n" " red [- | -s] [-p string] [-x] [-C] [file]\n")); exit(2); } } if (argv[optind] && strcmp(argv[optind], "-") == 0 && strcmp(argv[optind-1], "--") != 0) { vflag = 0; optind++; continue; } break; } argc = argc - optind; argv = &argv[optind]; if (xflag) { if (permflag) crypt_close(perm); permflag = 1; if ((kflag = run_setkey(&perm[0], getkey(msgtab[66]))) == -1) { puts(gettext(msgtab[64])); xflag = 0; kflag = 0; } if (kflag == 0) crflag = 0; } if (argc > 0) { p1 = *argv; if (strlen(p1) >= (size_t)FNSIZE) { puts(gettext("file name too long")); if (kflag) crypt_close(perm); exit(2); } p2 = savedfile; while (*p2++ = *p1++); globp = "e"; fflg++; } else /* editing with no file so set savtime to 0 */ savtime = 0; eflg++; if ((tfname = tempnam("", "ea")) == NULL) { puts(gettext(msgtab[69])); exit(2); } fendcore = (LINE)sbrk(0); init(); if (oldintr != SIG_IGN) signal(SIGINT, onintr); if (oldhup != SIG_IGN) signal(SIGHUP, onhup); setjmp(savej); commands(); quit(sig); return (0); } static void commands(void) { LINE a1; int c; char *p1, *p2; int fsave, m, n; for (;;) { nodelim = 0; if (pflag) { pflag = 0; addr1 = addr2 = dot; goto print; } if (shflg && globp == 0) write(1, gettext(prompt), strlen(gettext(prompt))); addr1 = 0; addr2 = 0; if ((c = getchr()) == ',') { addr1 = zero + 1; addr2 = dol; #ifdef XPG6 /* XPG4 - it was an error if the second address was */ /* input and the first address was ommitted */ /* Parse second address */ if ((a1 = address()) != 0) { addr2 = a1; } #endif c = getchr(); goto swch; } else if (c == ';') { addr1 = dot; addr2 = dol; #ifdef XPG6 /* XPG4 - it was an error if the second address was */ /* input and the first address was ommitted */ /* Parse second address */ if ((a1 = address()) != 0) { addr2 = a1; } #endif c = getchr(); goto swch; } else peekc = c; do { addr1 = addr2; if ((a1 = address()) == 0) { c = getchr(); break; } addr2 = a1; if ((c = getchr()) == ';') { c = ','; dot = a1; } } while (c == ','); if (addr1 == 0) addr1 = addr2; swch: switch (c) { case 'a': setdot(); newline(); if (!globflg) save(); append(gettty, addr2); continue; case 'c': #ifdef XPG6 setzeroasone(); #endif delete(); append(gettty, addr1-1); /* XPG4 - If no new lines are inserted, then the current */ /* line becomes the line after the lines deleted. */ if (((linebuf[0] != '.') || (dot == (addr1-1))) && (addr2 <= dol)) dot = addr1; continue; case 'd': delete(); continue; case 'E': fchange = 0; c = 'e'; case 'e': fflg++; setnoaddr(); if (vflag && fchange) { fchange = 0; (void) error(1); } filename(c); eflg++; init(); addr2 = zero; goto caseread; case 'f': setnoaddr(); filename(c); if (!ncflg) /* there is a filename */ getime(); else ncflg--; puts(savedfile); continue; case 'g': global(1); continue; case 'G': globaln(1); continue; case 'h': newline(); setnoaddr(); PUTM(); continue; case 'H': newline(); setnoaddr(); if (!hflag) { hflag = 1; PUTM(); } else hflag = 0; continue; case 'i': #ifdef XPG6 setzeroasone(); #endif setdot(); nonzero(); newline(); if (!globflg) save(); append(gettty, addr2-1); if (dot == addr2-1) dot += 1; continue; case 'j': if (addr2 == 0) { addr1 = dot; addr2 = dot+1; } setdot(); newline(); nonzero(); if (!globflg) save(); join(); continue; case 'k': if ((c = getchr()) < 'a' || c > 'z') (void) error(2); newline(); setdot(); nonzero(); names[c-'a'] = addr2->cur & ~01; anymarks |= 01; continue; case 'm': move(0); continue; case '\n': if (addr2 == 0) addr2 = dot+1; addr1 = addr2; goto print; case 'n': listn++; newline(); goto print; case 'l': listf++; case 'p': newline(); print: setdot(); nonzero(); a1 = addr1; do { if (listn) { count = a1 - zero; putd(); putchr('\t'); } puts(getaline((a1++)->cur)); } while (a1 <= addr2); dot = addr2; pflag = 0; listn = 0; listf = 0; continue; case 'Q': fchange = 0; case 'q': setnoaddr(); newline(); quit(sig); case 'r': filename(c); caseread: readflg = 1; save28 = (dol != fendcore); if (crflag == 2 || crflag == -2) crflag = -1; /* restore crflag for next file */ errno = 0; if ((io = eopen(file, O_RDONLY)) < 0) { lastc = '\n'; /* if first entering editor and file does not exist */ /* set saved access time to 0 */ if (eflg) { savtime = 0; eflg = 0; if (c == 'e' && vflag == 0) qflg = 1; } if (errno == ENOENT) { (void) error(68); } else { (void) error(3); } } /* get last mod time of file */ /* eflg - entered editor with ed or e */ if (eflg) { eflg = 0; getime(); } setall(); ninbuf = 0; n = zero != dol; #ifdef NULLS nulls = 0; #endif if (!globflg && (c == 'r')) save(); append(getfile, addr2); exfile(); readflg = 0; fchange = n; continue; case 's': setdot(); nonzero(); if (!globflg) save(); substitute(globp != 0); continue; case 't': move(1); continue; case 'u': setdot(); newline(); if (!initflg) undo(); else (void) error(5); fchange = 1; continue; case 'v': global(0); continue; case 'V': globaln(0); continue; case 'W': case 'w': if (flag28) { flag28 = 0; fchange = 0; (void) error(61); } setall(); /* on NULL-RE condition do not generate error */ if ((linebuf[0] != '.') && (zero != dol) && (addr1 <= zero || addr2 > dol)) (void) error(15); filename(c); if (Xqt) { io = eopen(file, O_WRONLY); n = 1; /* set n so newtime will not execute */ } else { struct stat lFl; fstat(tfile, &Tf); if (stat(file, &Fl) < 0) { if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP |S_IWGRP|S_IROTH|S_IWOTH)) < 0) (void) error(7); fstat(io, &Fl); Fl.st_mtime = 0; lFl = Fl; close(io); } else { #ifndef RESEARCH oldmask = umask(0); /* * Must determine if file is * a symbolic link */ lstat(file, &lFl); #endif } #ifndef RESEARCH /* * Determine if there are enough free blocks on system */ if (!Short && statvfs(file, &U) == 0 && U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) { Short = 1; (void) error(8); } Short = 0; #endif p1 = savedfile; /* The current filename */ p2 = file; m = strcmp(p1, p2); if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) { if (close(open(file, O_WRONLY)) < 0) (void) error(9); if (!(n = m)) chktime(); mkfunny(); /* * If funlink equals one it means that * funny points to a valid file which must * be unlinked when interrupted. */ funlink = 1; if ((io = creat(funny, FMODE(Fl))) >= 0) { chown(funny, Fl.st_uid, Fl.st_gid); chmod(funny, FMODE(Fl)); putfile(); exfile(); if (rename(funny, file)) (void) error(10); funlink = 0; /* if filenames are the same */ if (!n) newtime(); /* check if entire buffer was written */ fsave = fchange; fchange = (((addr1 == zero) || (addr1 == (zero + 1))) && (addr2 == dol)) ? 0 : 1; if (fchange == 1 && m != 0) fchange = fsave; continue; } } else n = 1; /* set n so newtime will not execute */ if ((io = open(file, (c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC : O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR |S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) (void) error(7); } putfile(); exfile(); if (!n) newtime(); fsave = fchange; fchange = (((addr1 == zero) || (addr1 == (zero + 1))) && (addr2 == dol)) ? 0 : 1; /* Leave fchange alone if partial write was to another file */ if (fchange == 1 && m != 0) fchange = fsave; continue; case 'C': crflag = 1; /* * C is same as X, but always assume input files are * ciphertext */ goto encrypt; case 'X': crflag = -1; encrypt: setnoaddr(); newline(); xflag = 1; if (permflag) (void) crypt_close(perm); permflag = 1; if ((kflag = run_setkey(&perm[0], getkey(msgtab[66]))) == -1) { xflag = 0; kflag = 0; crflag = 0; (void) error(64); } if (kflag == 0) crflag = 0; continue; case '=': setall(); newline(); count = (addr2-zero)&077777; putd(); putchr('\n'); continue; case '!': unixcom(); continue; case EOF: return; case 'P': setnoaddr(); newline(); if (shflg) shflg = 0; else shflg++; continue; } if (c == 'x') (void) error(60); else (void) error(12); } } LINE address(void) { int minus, c; LINE a1; int n, relerr, retval; 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 '/': comple(c); a1 = dot; for (;;) { if (c == '/') { a1++; if (a1 > dol) a1 = zero; } else { a1--; if (a1 < zero) a1 = dol; } if (execute(0, a1)) break; if (a1 == dot) (void) error(13); } break; case '$': a1 = dol; break; case '.': a1 = dot; break; case '\'': if ((c = getchr()) < 'a' || c > 'z') (void) error(2); for (a1 = zero; a1 <= dol; a1++) if (names[c-'a'] == (a1->cur & ~01)) break; break; default: peekc = c; if (a1 == 0) return (0); a1 += minus; /* on NULL-RE condition do not generate error */ if ((linebuf[0] != '.') && (a1 < zero || a1 > dol)) (void) error(15); return (a1); } if (relerr) (void) error(16); } } static void setdot(void) { if (addr2 == 0) addr1 = addr2 = dot; if (addr1 > addr2) (void) error(17); } static void setall(void) { if (addr2 == 0) { addr1 = zero+1; addr2 = dol; if (dol == zero) addr1 = zero; } setdot(); } static void setnoaddr(void) { if (addr2) (void) error(18); } static void nonzero(void) { /* on NULL-RE condition do not generate error */ if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol)) (void) error(15); } static void setzeroasone(void) { /* for the c and i commands 0 equal to 1 address */ if (addr1 == zero) { addr1 = zero+1; } if (addr2 == zero) { addr2 = zero+1; } } static void newline(void) { int c; if ((c = getchr()) == '\n') return; if (c == 'p' || c == 'l' || c == 'n') { pflag++; if (c == 'l') listf++; if (c == 'n') listn++; if ((c = getchr()) == '\n') return; } (void) error(20); } static void filename(int comm) { char *p1, *p2; int c; int i = 0; count = 0; c = getchr(); if (c == '\n' || c == EOF) { p1 = savedfile; if (*p1 == 0 && comm != 'f') (void) error(21); /* ncflg set means do not get mod time of file */ /* since no filename followed f */ if (comm == 'f') ncflg++; p2 = file; while (*p2++ = *p1++); red(savedfile); return; } if (c != ' ') (void) error(22); while ((c = getchr()) == ' '); if (c == '!') ++Xqt, c = getchr(); if (c == '\n') (void) error(21); p1 = file; do { if (++i >= FNSIZE) (void) error(24); *p1++ = c; if (c == EOF || (c == ' ' && !Xqt)) (void) error(21); } while ((c = getchr()) != '\n'); *p1++ = 0; if (Xqt) if (comm == 'f') { --Xqt; (void) error(57); } else return; if (savedfile[0] == 0 || comm == 'e' || comm == 'f') { p1 = savedfile; p2 = file; while (*p1++ = *p2++); } red(file); } static void exfile(void) { #ifdef NULLS int c; #endif #ifndef RESEARCH if (oldmask) { umask(oldmask); oldmask = 0; } #endif eclose(io); io = -1; if (vflag) { putd(); putchr('\n'); #ifdef NULLS if (nulls) { c = count; count = nulls; nulls = 0; putd(); puts(gettext(" nulls replaced by '\\0'")); count = c; } #endif } } static void onintr(int sig) { signal(SIGINT, onintr); putchr('\n'); lastc = '\n'; globflg = 0; if (funlink) unlink(funny); /* remove tmp file */ /* if interrupted a read, only part of file may be in buffer */ if (readflg) { sprintf(tstring, "\007read may be incomplete - beware!\007"); puts(gettext(tstring)); fchange = 0; } (void) error(26); } static void onhup(int sig) { signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); /* * if there are lines in file and file was not written * since last update, save in ed.hup, or $HOME/ed.hup */ if (dol > zero && fchange == 1) { addr1 = zero+1; addr2 = dol; io = creat("ed.hup", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (io < 0 && home) { char *fn; fn = (char *)calloc(strlen(home) + 8, sizeof (char)); if (fn) { strcpy(fn, home); strcat(fn, "/ed.hup"); io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP |S_IROTH|S_IWOTH); free(fn); } } if (io > 0) putfile(); } fchange = 0; ++errcnt; quit(sig); } static int error(int code) { int c; if (code == 28 && save28 == 0) { fchange = 0; flag28++; } readflg = 0; ++errcnt; listf = listn = 0; pflag = 0; #ifndef RESEARCH if (oldmask) { umask(oldmask); oldmask = 0; } #endif #ifdef NULLS /* Not really nulls, but close enough */ /* This is a bug because of buffering */ if (code == 28) /* illegal char. */ putd(); #endif /* Cant open file or file does not exist */ if ((code == 3) || (code == 68)) { if (qflg == 0) { putchr('?'); puts(file); } else qflg = 0; } else { putchr('?'); putchr('\n'); } count = 0; lseek(0, (long)0, 2); if (globp) lastc = '\n'; globp = 0; peekc = lastc; if (lastc) while ((c = getchr()) != '\n' && c != EOF); if (io) { eclose(io); io = -1; } xcode = code; if (hflag) PUTM(); if (code == 4) return (0); /* Non-fatal error. */ longjmp(savej, 1); /* NOTREACHED */ } static int getchr(void) { char c; if (lastc = peekc) { peekc = 0; return (lastc); } if (globp) { if ((lastc = (unsigned char)*globp++) != 0) return (lastc); globp = 0; return (EOF); } if (read(0, &c, 1) <= 0) return (lastc = EOF); lastc = (unsigned char)c; return (lastc); } static int gettty(void) { int c; char *gf; char *p; p = linebuf; gf = globp; while ((c = getchr()) != '\n') { if (c == EOF) { if (gf) peekc = c; return (c); } if (c == 0) continue; *p++ = c; if (p > &linebuf[LBSIZE-1]) (void) error(27); } *p++ = 0; if (linebuf[0] == '.' && linebuf[1] == 0) return (EOF); /* * POSIX.2/XPG4 explicitly says no to this: * * in Solaris backslash followed by special character "." is * special character "." itself; (so terminating input mode can be * "\.\n"). * * however, POSIX2/XPG4 says, input mode is terminated by * entering line consisting of only 2 characters: ".\n" * * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) { * linebuf[0] = '.'; * linebuf[1] = 0; * } */ return (0); } static int getfile(void) { char c; char *lp, *fp; lp = linebuf; fp = nextip; do { if (--ninbuf < 0) { if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0) if (lp > linebuf) { puts(gettext("'\\n' appended")); *genbuf = '\n'; } else return (EOF); if (crflag == -1) { if (isencrypt(genbuf, ninbuf + 1)) crflag = 2; else crflag = -2; } fp = genbuf; if (crflag > 0) if (run_crypt(count, genbuf, ninbuf+1, perm) == -1) (void) error(63); } if (lp >= &linebuf[LBSIZE]) { lastc = '\n'; (void) error(27); } if ((*lp++ = c = *fp++) == 0) { #ifdef NULLS lp[-1] = '\\'; *lp++ = '0'; nulls++; #else lp--; continue; #endif } count++; } while (c != '\n'); *--lp = 0; nextip = fp; if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) { write(1, gettext("line too long: lno = "), strlen(gettext("line too long: lno = "))); ccount = count; count = (++dot-zero)&077777; dot--; putd(); count = ccount; putchr('\n'); } return (0); } static void putfile(void) { int n; LINE a1; char *fp, *lp; int nib; nib = LBSIZE; fp = genbuf; a1 = addr1; do { lp = getaline(a1++->cur); if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) { write(1, gettext("line too long: lno = "), strlen(gettext("line too long: lno = "))); ccount = count; count = (a1-zero-1)&077777; putd(); count = ccount; putchr('\n'); } for (;;) { if (--nib < 0) { n = fp-genbuf; if (kflag) if (run_crypt(count-n, genbuf, n, perm) == -1) (void) error(63); if (write(io, genbuf, n) != n) (void) error(29); nib = LBSIZE - 1; fp = genbuf; } if (dol->cur == 0L)break; /* Allow write of null file */ count++; if ((*fp++ = *lp++) == 0) { fp[-1] = '\n'; break; } } } while (a1 <= addr2); n = fp-genbuf; if (kflag) if (run_crypt(count-n, genbuf, n, perm) == -1) (void) error(63); if (write(io, genbuf, n) != n) (void) error(29); } static void append(int (*f)(void), LINE a) { LINE a1, a2, rdot; long tl; nline = 0; dot = a; while ((*f)() == 0) { if (dol >= endcore) { if ((int)sbrk(512 * sizeof (struct lin)) == -1) { lastc = '\n'; (void) error(30); } endcore += 512; } tl = putline(); nline++; a1 = ++dol; a2 = a1+1; rdot = ++dot; while (a1 > rdot) (--a2)->cur = (--a1)->cur; rdot->cur = tl; } } static void unixcom(void) { void (*savint)(); pid_t pid, rpid; int retcode; static char savcmd[LBSIZE]; /* last command */ char curcmd[LBSIZE]; /* current command */ char *psavcmd, *pcurcmd, *psavedfile; int endflg = 1, shflg = 0; wchar_t c; int len; setnoaddr(); if (rflg) (void) error(6); pcurcmd = curcmd; /* read command til end */ /* * a '!' found in beginning of command is replaced with the saved * command. a '%' found in command is replaced with the current * filename */ c = getchr(); if (c == '!') { if (savcmd[0] == 0) (void) error(56); else { psavcmd = savcmd; while (*pcurcmd++ = *psavcmd++); --pcurcmd; shflg = 1; } } else UNGETC(c); /* put c back */ while (endflg == 1) { while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') { if ((len = wctomb(pcurcmd, c)) <= 0) { *pcurcmd = (unsigned char)c; len = 1; } pcurcmd += len; } if (c == '%') { if (savedfile[0] == 0) (void) error(21); else { psavedfile = savedfile; while (pcurcmd < curcmd + LBSIZE && (*pcurcmd++ = *psavedfile++)); --pcurcmd; shflg = 1; } } else if (c == '\\') { c = get_wchr(); if (c != '%') *pcurcmd++ = '\\'; if ((len = wctomb(pcurcmd, c)) <= 0) { *pcurcmd = (unsigned char)c; len = 1; } pcurcmd += len; } else /* end of command hit */ endflg = 0; } *pcurcmd++ = 0; if (shflg == 1) puts(curcmd); /* save command */ strcpy(savcmd, curcmd); if ((pid = fork()) == 0) { signal(SIGHUP, oldhup); signal(SIGQUIT, oldquit); close(tfile); execlp(_PATH_BSHELL, "sh", "-c", curcmd, (char *)0); exit(0100); } savint = signal(SIGINT, SIG_IGN); while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1); signal(SIGINT, savint); if (vflag) puts("!"); } static void quit(int sig) { if (vflag && fchange) { fchange = 0; if (flag28) { flag28 = 0; (void) error(62); } /* * For case where user reads in BOTH a good * file & a bad file */ (void) error(1); } unlink(tfname); if (kflag) crypt_close(perm); if (xtflag) crypt_close(tperm); exit(errcnt? 2: 0); } static void delete(void) { setdot(); newline(); nonzero(); if (!globflg) save(); rdelete(addr1, addr2); } static void rdelete(LINE ad1, LINE ad2) { LINE a1, a2, a3; a1 = ad1; a2 = ad2+1; a3 = dol; dol -= a2 - a1; do (a1++)->cur = (a2++)->cur; while (a2 <= a3); a1 = ad1; if (a1 > dol) a1 = dol; dot = a1; fchange = 1; } static void gdelete(void) { LINE a1, a2, a3; a3 = dol; for (a1 = zero+1; (a1->cur&01) == 0; a1++) if (a1 >= a3) return; for (a2 = a1 + 1; a2 <= a3; ) { if (a2->cur & 01) { a2++; dot = a1; } else (a1++)->cur = (a2++)->cur; } dol = a1-1; if (dot > dol) dot = dol; fchange = 1; } static char * getaline(long tl) { char *bp, *lp; int nl; lp = linebuf; bp = getblock(tl, READ); nl = nleft; tl &= ~0377; while (*lp++ = *bp++) if (--nl == 0) { bp = getblock(tl += 0400, READ); nl = nleft; } return (linebuf); } static long putline(void) { char *bp, *lp; int nl; long tl; fchange = 1; lp = linebuf; tl = tline; bp = getblock(tl, WRITE); nl = nleft; tl &= ~0377; while (*bp = *lp++) { if (*bp++ == '\n') { *--bp = 0; linebp = lp; break; } if (--nl == 0) { bp = getblock(tl += 0400, WRITE); nl = nleft; } } nl = tline; tline += (((lp-linebuf)+03)>>1)&077776; return (nl); } static char * getblock(long atl, long iof) { int bno, off; char *p1, *p2; int n; bno = atl >> 8; off = (atl<<1)&0774; /* bno is limited to 16 bits */ if (bno >= 65535) { lastc = '\n'; (void) error(31); } nleft = 512 - off; if (bno == iblock) { ichanged |= iof; return (ibuff+off); } if (bno == oblock) return (obuff+off); if (iof == READ) { if (ichanged) { if (xtflag) if (run_crypt(0L, ibuff, 512, tperm) == -1) (void) error(63); blkio(iblock, ibuff, write); } ichanged = 0; iblock = bno; blkio(bno, ibuff, read); if (xtflag) if (run_crypt(0L, ibuff, 512, tperm) == -1) (void) error(63); return (ibuff+off); } if (oblock >= 0) { if (xtflag) { p1 = obuff; p2 = crbuf; n = 512; while (n--) *p2++ = *p1++; if (run_crypt(0L, crbuf, 512, tperm) == -1) (void) error(63); blkio(oblock, crbuf, write); } else blkio(oblock, obuff, write); } oblock = bno; return (obuff+off); } static void blkio(int b, char *buf, ssize_t (*iofcn)()) { lseek(tfile, (long)b<<9, 0); if ((*iofcn)(tfile, buf, 512) != 512) { if (dol != zero) (void) error(32); /* Bypass this if writing null file */ } } static void init(void) { long *markp; mode_t omask; if (tfile != -1) { (void) close(tfile); (void) unlink(tfname); } tline = 2; for (markp = names; markp < &names[26]; ) *markp++ = 0L; subnewa = 0L; anymarks = 0; iblock = -1; oblock = -1; ichanged = 0; initflg = 1; omask = umask(0); if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR)) < 0) { puts(gettext(msgtab[70])); exit(2); } umask(omask); if (xflag) { xtflag = 1; if (tpermflag) (void) crypt_close(tperm); tpermflag = 1; if (makekey(tperm)) { xtflag = 0; puts(gettext(msgtab[65])); } } brk((char *)fendcore); dot = zero = dol = savdot = savdol = fendcore; flag28 = save28 = 0; endcore = fendcore - sizeof (struct lin); } static void global(int k) { char *gp; wchar_t l; char multic[MB_LEN_MAX]; wchar_t c; LINE a1; char globuf[LBSIZE]; int n; int len; if (globp) (void) error(33); setall(); nonzero(); if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0) (void) error(67); if (l == '\n') (void) error(19); save(); comple(l); gp = globuf; while ((c = get_wchr()) != '\n') { if (c == EOF) (void) error(19); /* '\\' has special meaning only if preceding a '\n' */ if (c == '\\') { c = get_wchr(); if (c != '\n') *gp++ = '\\'; } if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1]) (void) error(34); if ((len = wctomb(gp, c)) <= 0) { *gp = (unsigned char)c; len = 1; } gp += len; } if (gp == globuf) *gp++ = 'p'; *gp++ = '\n'; *gp++ = 0; for (a1 = zero; a1 <= dol; a1++) { a1->cur &= ~01; if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k) a1->cur |= 01; } /* * Special case: g/.../d (avoid n^2 algorithm) */ if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') { gdelete(); return; } for (a1 = zero; a1 <= dol; a1++) { if (a1->cur & 01) { a1->cur &= ~01; dot = a1; globp = globuf; globflg = 1; commands(); globflg = 0; a1 = zero; } } } static void join(void) { char *gp, *lp; LINE a1; if (addr1 == addr2) return; gp = genbuf; for (a1 = addr1; a1 <= addr2; a1++) { lp = getaline(a1->cur); while (*gp = *lp++) if (gp++ > &genbuf[LBSIZE-1]) (void) error(27); } lp = linebuf; gp = genbuf; while (*lp++ = *gp++); addr1->cur = putline(); if (addr1 < addr2) rdelete(addr1+1, addr2); dot = addr1; } static void substitute(int inglob) { int nl; LINE a1; long *markp; int ingsav; /* For saving arg. */ ingsav = inglob; ocerr2 = 0; gsubf = compsub(); for (a1 = addr1; a1 <= addr2; a1++) { if (execute(0, a1) == 0) continue; numpass = 0; ocerr1 = 0; inglob |= 01; dosub(); if (gsubf) { while (*loc2) { if (execute(1, (LINE)0) == 0) break; dosub(); } } if (ocerr1 == 0)continue; /* Don't put out-not changed. */ subnewa = putline(); a1->cur &= ~01; if (anymarks) { for (markp = names; markp < &names[26]; markp++) if (*markp == a1->cur) *markp = subnewa; } a1->cur = subnewa; append(getsub, a1); nl = nline; a1 += nl; addr2 += nl; } if (ingsav) return; /* Was in global-no error msg allowed. */ if (inglob == 0) (void) error(35); /* Not in global, but not found. */ if (ocerr2 == 0) (void) error(35); /* RE found, but occurrence match failed. */ } static int compsub(void) { int c; wchar_t seof; char *p; char multic[MB_LEN_MAX]; int n; static char remem[RHSIZE]; static int remflg = -1; int i; if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0) (void) error(67); if (seof == '\n' || seof == ' ') (void) error(36); comple(seof); p = rhsbuf; for (;;) { wchar_t cl; if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0) (void) error(67); if (cl == '\\') { *p++ = '\\'; if (p >= &rhsbuf[RHSIZE]) (void) error(38); if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0) (void) error(67); } else if (cl == '\n') { if (nodelim == 1) { nodelim = 0; (void) error(36); } if (!(globp && globp[0])) { UNGETC('\n'); pflag++; break; } } else if (cl == seof) break; if (p + n > &rhsbuf[RHSIZE]) (void) error(38); (void) strncpy(p, multic, n); p += n; } *p++ = 0; if (rhsbuf[0] == '%' && rhsbuf[1] == 0) /* * If there isn't a remembered string, it is an error; * otherwise the right hand side is the previous right * hand side. */ if (remflg == -1) (void) error(55); else strcpy(rhsbuf, remem); else { strcpy(remem, rhsbuf); remflg = 0; } c = 0; peekc = getchr(); /* Gets char after third delimiter. */ if (peekc == 'g') { c = LBSIZE; peekc = 0; } if (peekc >= '1' && peekc <= '9') { c = peekc-'0'; peekc = 0; /* Allows getchr() to get next char. */ while (1) { i = getchr(); if (i < '0' || i > '9') break; c = c*10 + i-'0'; if (c > LBSIZE-1) (void) error(20); /* "Illegal suffix" */ } peekc = i; /* Effectively an unget. */ } newline(); return (c); /* * Returns occurrence value. 0 & 1 both do first occurrence * only: c = 0 if ordinary substitute; c = 1 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal. */ } static int getsub(void) { char *p1, *p2; p1 = linebuf; if ((p2 = linebp) == 0) return (EOF); while (*p1++ = *p2++); linebp = 0; return (0); } static void dosub(void) { char *lp, *sp, *rp; int c; if (gsubf > 0 && gsubf < LBSIZE) { numpass++; if (gsubf != numpass) return; } ocerr1++; ocerr2++; lp = linebuf; sp = genbuf; rp = rhsbuf; while (lp < loc1) *sp++ = *lp++; while (c = *rp++) { if (c == '&') { sp = place(sp, loc1, loc2); continue; } else if (c == '\\') { c = *rp++; if (c >= '1' && c < nbra + '1') { sp = place(sp, braslist[c-'1'], braelist[c-'1']); continue; } } *sp++ = c; if (sp >= &genbuf[LBSIZE]) (void) error(27); } lp = loc2; loc2 = sp - genbuf + linebuf; while (*sp++ = *lp++) if (sp >= &genbuf[LBSIZE]) (void) error(27); lp = linebuf; sp = genbuf; while (*lp++ = *sp++); } static char * place(char *sp, char *l1, char *l2) { while (l1 < l2) { *sp++ = *l1++; if (sp >= &genbuf[LBSIZE]) (void) error(27); } return (sp); } static void comple(wchar_t seof) { int cclass = 0; wchar_t c; int n; char *cp = genbuf; char multic[MB_LEN_MAX]; while (1) { if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0) error1(67); if (n == 0 || c == '\n') { if (cclass) error1(49); else break; } if (c == seof && !cclass) break; if (cclass && c == ']') { cclass = 0; if (cp > &genbuf[LBSIZE-1]) error1(50); *cp++ = ']'; continue; } if (c == '[' && !cclass) { cclass = 1; if (cp > &genbuf[LBSIZE-1]) error1(50); *cp++ = '['; if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0) error1(67); if (n == 0 || c == '\n') error1(49); } if (c == '\\' && !cclass) { if (cp > &genbuf[LBSIZE-1]) error1(50); *cp++ = '\\'; if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0) error1(67); if (n == 0 || c == '\n') error1(36); } if (cp + n > &genbuf[LBSIZE-1]) error1(50); (void) strncpy(cp, multic, n); cp += n; } *cp = '\0'; if (n != 0 && c == '\n') UNGETC('\n'); if (n == 0 || c == '\n') nodelim = 1; /* * NULL RE: do not compile a null regular expression; but process * input with last regular expression encountered */ if (genbuf[0] != '\0') { if (expbuf) free(expbuf); expbuf = compile(genbuf, (char *)0, (char *)0); } if (regerrno) error1(regerrno); } static void move(int cflag) { LINE adt, ad1, ad2; setdot(); nonzero(); if ((adt = address()) == 0) (void) error(39); newline(); if (!globflg) save(); if (cflag) { ad1 = dol; append(getcopy, ad1++); ad2 = dol; } else { ad2 = addr2; for (ad1 = addr1; ad1 <= ad2; ) (ad1++)->cur &= ~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 (void) error(39); fchange = 1; } static void reverse(LINE a1, LINE a2) { long t; for (;;) { t = (--a2)->cur; if (a2 <= a1) return; a2->cur = a1->cur; (a1++)->cur = t; } } static int getcopy(void) { if (addr1 > addr2) return (EOF); (void) getaline((addr1++)->cur); return (0); } /* * Handles error code returned from comple() routine: regular expression * compile and match routines */ static void error1(int code) { nbra = 0; (void) error(code); } static int execute(int gf, LINE addr) { char *p1; int c; for (c = 0; c < nbra; c++) { braslist[c] = 0; braelist[c] = 0; } if (gf) locs = p1 = loc2; else { if (addr == zero) return (0); p1 = getaline(addr->cur); locs = 0; } return (step(p1, expbuf)); } static void putd() { int r; r = (int)(count%10); count /= 10; if (count) putd(); putchr(r + '0'); } int puts(const char *sp) { int n; wchar_t c; int sz, i; if (fss.Ffill && (listf == 0)) { /* deliberate attempt to remove constness of sp because */ /* it needs to be expanded */ if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) { write(1, funny, fss.Flim & 0377); putchr('\n'); write(1, gettext("too long"), strlen(gettext("too long"))); } else write(1, funny, sz); putchr('\n'); if (i == -2) write(1, gettext("tab count\n"), strlen(gettext("tab count\n"))); return (0); } col = 0; while (*sp) { n = mbtowc(&c, sp, MB_LEN_MAX); if (listf) { if (n < 1) (void) error(28); else if (n == 1) putchr((unsigned char)*sp++); else { sp += n; putwchr(c); } } else { putchr((unsigned char)*sp++); } } #ifndef XPG6 if (listf) putchr('$'); /* end of line is marked with a $ */ #else if (listf) { /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */ /* by doing a putchr() with listf=0, thereby avoiding the $ case */ /* statement in putchr() */ listf = 0; putchr('$'); /* end of line is marked with a $ */ listf++; } #endif putchr('\n'); return (1); } static void putwchr(wchar_t ac) { char buf[MB_LEN_MAX], *p; char *lp; wchar_t c; short len; lp = linp; c = ac; if (listf) { if (!iswprint(c)) { p = &buf[0]; if ((len = wctomb(p, c)) <= 0) { *p = (unsigned char)c; len = 1; }; while (len--) { if (col + 4 >= 72) { col = 0; *lp++ = '\\'; *lp++ = '\n'; } (void) sprintf(lp, "\\%03o", *(unsigned char *)p++); col += 4; lp += 4; } } else { if ((len = wcwidth(c)) <= 0) len = 0; if (col + len >= 72) { col = 0; *lp++ = '\\'; *lp++ = '\n'; } col += len; if ((len = wctomb(lp, c)) <= 0) { *lp = (unsigned char)c; len = 1; } lp += len; } } else { if ((len = wctomb(lp, c)) <= 0) { *lp = (unsigned char)c; len = 1; } lp += len; } if (c == '\n' || lp >= &line[64]) { linp = line; len = lp - line; write(1, line, len); return; } linp = lp; } static void putchr(unsigned char c) { char *lp; int len; lp = linp; if (listf && c != '\n') { switch (c) { case '\\' : *lp++ = '\\'; *lp++ = '\\'; col += 2; break; case '\007' : *lp++ = '\\'; *lp++ = 'a'; col += 2; break; case '\b' : *lp++ = '\\'; *lp++ = 'b'; col += 2; break; case '\f' : *lp++ = '\\'; *lp++ = 'f'; col += 2; break; case '\r' : *lp++ = '\\'; *lp++ = 'r'; col += 2; break; case '\t' : *lp++ = '\\'; *lp++ = 't'; col += 2; break; case '\v' : *lp++ = '\\'; *lp++ = 'v'; col += 2; break; #ifdef XPG6 /* if $ characters are within the line preceed with \ */ case '$' : *lp++ = '\\'; *lp++ = '$'; col += 2; break; #endif default: if (isprint(c)) { *lp++ = c; col += 1; } else { (void) sprintf(lp, "\\%03o", c); col += 4; lp += 4; } break; } /* * long lines are folded w/ pt of folding indicated by writing * backslash/newline character */ if (col + 1 >= 72) { col = 0; *lp++ = '\\'; *lp++ = '\n'; } } else *lp++ = c; if (c == '\n' || lp >= &line[64]) { linp = line; len = lp - line; (void) write(1, line, len); return; } linp = lp; } static char * getkey(const char *prompt) { struct termio b; int save; void (*sig)(); static char key[KSIZE+1]; char *p; int c; sig = signal(SIGINT, SIG_IGN); ioctl(0, TCGETA, &b); save = b.c_lflag; b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); ioctl(0, TCSETAW, &b); write(1, gettext(prompt), strlen(gettext(prompt))); p = key; while (((c = getchr()) != EOF) && (c != '\n')) { if (p < &key[KSIZE]) *p++ = c; } *p = 0; write(1, "\n", 1); b.c_lflag = save; ioctl(0, TCSETAW, &b); signal(SIGINT, sig); return (key); } static void globaln(int k) { char *gp; int c; int n; wchar_t cl; LINE a1; int nfirst; char globuf[LBSIZE]; char multic[MB_LEN_MAX]; int len; int pflag_save = 0; int listf_save = 0; int listn_save = 0; if (globp) (void) error(33); setall(); nonzero(); if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0) (void) error(67); if (cl == '\n') (void) error(19); save(); comple(cl); for (a1 = zero; a1 <= dol; a1++) { a1->cur &= ~01; if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k) a1->cur |= 01; } nfirst = 0; newline(); /* * preserve the p, l, and n suffix commands of the G and V * commands during the interactive section and restore * on completion of the G and V command. */ pflag_save = pflag; listf_save = listf; listn_save = listn; pflag = 0; listf = 0; listn = 0; for (a1 = zero; a1 <= dol; a1++) { if (a1->cur & 01) { a1->cur &= ~01; dot = a1; puts(getaline(a1->cur)); if ((c = get_wchr()) == EOF) (void) error(52); if (c == 'a' || c == 'i' || c == 'c') (void) error(53); if (c == '\n') { a1 = zero; continue; } if (c != '&') { gp = globuf; if ((len = wctomb(gp, c)) <= 0) { *gp = (unsigned char)c; len = 1; } gp += len; while ((c = get_wchr()) != '\n') { /* '\\' has special meaning only if preceding a '\n' */ if (c == '\\') { c = get_wchr(); if (c != '\n') *gp++ = '\\'; } if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1]) (void) error(34); if ((len = wctomb(gp, c)) <= 0) { *gp = (unsigned char)c; len = 1; } gp += len; } *gp++ = '\n'; *gp++ = 0; nfirst = 1; } else if ((c = get_wchr()) != '\n') (void) error(54); globp = globuf; if (nfirst) { globflg = 1; commands(); globflg = 0; } else (void) error(56); globp = 0; a1 = zero; } } pflag = pflag_save; listf = listf_save; listn = listn_save; } static int eopen(char *string, int rw) { #define w_or_r(a, b) (rw ? a : b) int pf[2]; pid_t i; int io; int chcount; /* # of char read. */ if (rflg) { /* restricted shell */ if (Xqt) { Xqt = 0; (void) error(6); } } if (!Xqt) { if ((io = open(string, rw)) >= 0) { if (fflg) { chcount = read(io, crbuf, LBSIZE); if (crflag == -1) { if (isencrypt(crbuf, chcount)) crflag = 2; else crflag = -2; } if (crflag > 0) if (run_crypt(0L, crbuf, chcount, perm) == -1) (void) error(63); if (fspec(crbuf, &fss, 0) < 0) { fss.Ffill = 0; fflg = 0; (void) error(4); } lseek(io, 0L, 0); } } fflg = 0; return (io); } if (pipe(pf) < 0) xerr: (void) error(0); if ((i = fork()) == 0) { signal(SIGHUP, oldhup); signal(SIGQUIT, oldquit); signal(SIGPIPE, oldpipe); signal(SIGINT, (void (*)()) 0); close(w_or_r(pf[1], pf[0])); close(w_or_r(0, 1)); dup(w_or_r(pf[0], pf[1])); close(w_or_r(pf[0], pf[1])); execlp(_PATH_BSHELL, "sh", "-c", string, (char *)0); exit(1); } if (i == (pid_t)-1) goto xerr; close(w_or_r(pf[0], pf[1])); return (w_or_r(pf[1], pf[0])); } static void eclose(int f) { close(f); if (Xqt) Xqt = 0, wait((int *)0); } static void mkfunny(void) { char *p, *p1, *p2; p2 = p1 = funny; p = file; /* * Go to end of file name */ while (*p) p++; while (*--p == '/') /* delete trailing slashes */ *p = '\0'; /* * go back to beginning of file */ p = file; /* * Copy file name to funny setting p2 at * basename of file. */ while (*p1++ = *p) if (*p++ == '/') p2 = p1; /* * Set p1 to point to basename of tfname. */ p1 = strrchr(tfname, '/'); if (strlen(tfname) > (size_t)6) p1 = &tfname[strlen(tfname)-6]; p1++; *p2 = '\007'; /* add unprintable char for funny a unique name */ /* * Copy tfname to file. */ while (*++p2 = *p1++); } static void getime(void) /* get modified time of file and save */ { if (stat(file, &Fl) < 0) savtime = 0; else savtime = Fl.st_mtime; } static void chktime(void) /* check saved mod time against current mod time */ { if (savtime != 0 && Fl.st_mtime != 0) { if (savtime != Fl.st_mtime) (void) error(58); } } static void newtime(void) /* get new mod time and save */ { stat(file, &Fl); savtime = Fl.st_mtime; } static void red(char *op) /* restricted - check for '/' in name */ /* and delete trailing '/' */ { char *p; p = op; while (*p) if (*p++ == '/'&& rflg) { *op = 0; (void) error(6); } /* delete trailing '/' */ while (p > op) { if (*--p == '/') *p = '\0'; else break; } } /* * Searches thru beginning of file looking for a string of the form * <: values... :> * * where "values" are * * \b ignored * s<num> sets the Flim to <num> * t??? sets tab stop stuff * d ignored * m<num> ignored * e ignored */ static int fspec(char line[], struct Fspec *f, int up) { struct termio arg; int havespec, n; int len; if (!up) clear(f); havespec = fsprtn = 0; for (fsp = line; *fsp && *fsp != '\n'; fsp += len) { if ((len = mblen(fsp, MB_CUR_MAX)) <= 0) len = 1; switch (*fsp) { case '<': if (havespec) return (-1); if (*(fsp+1) == ':') { havespec = 1; clear(f); if (!ioctl(1, TCGETA, &arg) && ((arg.c_oflag&TAB3) == TAB3)) f->Ffill = 1; fsp++; continue; } case ' ': continue; case 's': if (havespec && (n = numb()) >= 0) f->Flim = n; continue; case 't': if (havespec) targ(f); continue; case 'd': continue; case 'm': if (havespec) n = numb(); continue; case 'e': continue; case ':': if (!havespec) continue; if (*(fsp+1) != '>') fsprtn = -1; return (fsprtn); default: if (!havespec) continue; return (-1); } } return (1); } static int numb(void) { int n; n = 0; while (*++fsp >= '0' && *fsp <= '9') n = 10*n + *fsp-'0'; fsp--; return (n); } static void targ(struct Fspec *f) { if (*++fsp == '-') { if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f); else tstd(f); return; } if (*fsp >= '0' && *fsp <= '9') { tlist(f); return; } fsprtn = -1; fsp--; } static void tincr(int n, struct Fspec *f) { int l, i; l = 1; for (i = 0; i < 20; i++) f->Ftabs[i] = l += n; f->Ftabs[i] = 0; } static void tstd(struct Fspec *f) { char std[3]; std[0] = *++fsp; if (*(fsp+1) >= '0' && *(fsp+1) <= '9') { std[1] = *++fsp; std[2] = '\0'; } else std[1] = '\0'; fsprtn = stdtab(std, f->Ftabs); } static void tlist(struct Fspec *f) { int n, last, i; fsp--; last = i = 0; do { if ((n = numb()) <= last || i >= 20) { fsprtn = -1; return; } f->Ftabs[i++] = last = n; } while (*++fsp == ','); f->Ftabs[i] = 0; fsp--; } static int expnd(char line[], char buf[], int *sz, struct Fspec *f) { char *l, *t; int b; l = line - 1; b = 1; t = f->Ftabs; fsprtn = 0; while (*++l && *l != '\n' && b < 511) { if (*l == '\t') { while (*t && b >= *t) t++; if (*t == 0) fsprtn = -2; do buf[b-1] = ' '; while (++b < *t); } else buf[b++ - 1] = *l; } buf[b] = '\0'; *sz = b; if (*l != '\0' && *l != '\n') { buf[b-1] = '\n'; return (-1); } buf[b-1] = *l; if (f->Flim && (b-1 > (int)f->Flim)) return (-1); return (fsprtn); } static void clear(struct Fspec *f) { f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0; f->Flim = 0; } static int lenchk(char line[], struct Fspec *f) { char *l, *t; int b; l = line - 1; b = 1; t = f->Ftabs; while (*++l && *l != '\n' && b < 511) { if (*l == '\t') { while (*t && b >= *t) t++; while (++b < *t); } else b++; } if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim))) return (-1); return (0); } #define NTABS 21 /* * stdtabs: standard tabs table * format: option code letter(s), null, tabs, null */ static char stdtabs[] = { 'a', 0, 1, 10, 16, 36, 72, 0, /* IBM 370 Assembler */ 'a', '2', 0, 1, 10, 16, 40, 72, 0, /* IBM Assembler alternative */ 'c', 0, 1, 8, 12, 16, 20, 55, 0, /* COBOL, normal */ 'c', '2', 0, 1, 6, 10, 14, 49, 0, /* COBOL, crunched */ 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 67, 0, 'f', 0, 1, 7, 11, 15, 19, 23, 0, /* FORTRAN */ 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0, /* PL/I */ 's', 0, 1, 10, 55, 0, /* SNOBOL */ 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */ 0 }; /* * stdtab: return tab list for any "canned" tab option. * entry: option points to null-terminated option string * tabvect points to vector to be filled in * exit: return (0) if legal, tabvect filled, ending with zero * return (-1) if unknown option */ static int stdtab(char option[], char tabvect[NTABS]) { char *scan; tabvect[0] = 0; scan = stdtabs; while (*scan) { if (strequal(&scan, option)) { strcopy(scan, tabvect); break; } else while (*scan++); /* skip over tab specs */ } /* later: look up code in /etc/something */ return (tabvect[0] ? 0 : -1); } /* * strequal: checks strings for equality * entry: scan1 points to scan pointer, str points to string * exit: return (1) if equal, return (0) if not * *scan1 is advanced to next nonzero byte after null */ static int strequal(char **scan1, char *str) { char c, *scan; scan = *scan1; while ((c = *scan++) == *str && c) str++; *scan1 = scan; if (c == 0 && *str == 0) return (1); if (c) while (*scan++); *scan1 = scan; return (0); } /* strcopy: copy source to destination */ static void strcopy(char *source, char *dest) { while (*dest++ = *source++); } /* This is called before a buffer modifying command so that the */ /* current array of line ptrs is saved in sav and dot and dol are saved */ static void save(void) { LINE i; int j; savdot = dot; savdol = dol; for (j = 0; j <= 25; j++) savnames[j] = names[j]; for (i = zero + 1; i <= dol; i++) i->sav = i->cur; initflg = 0; } /* The undo command calls this to restore the previous ptr array sav */ /* and swap with cur - dot and dol are swapped also. This allows user to */ /* undo an undo */ static void undo(void) { int j; long tmp; LINE i, tmpdot, tmpdol; tmpdot = dot; dot = savdot; savdot = tmpdot; tmpdol = dol; dol = savdol; savdol = tmpdol; /* swap arrays using the greater of dol or savdol as upper limit */ for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) { tmp = i->cur; i->cur = i->sav; i->sav = tmp; } /* * If the current text lines are swapped with the * text lines in the save buffer, then swap the current * marks with those in the save area. */ for (j = 0; j <= 25; j++) { tmp = names[j]; names[j] = savnames[j]; savnames[j] = tmp; } } static wchar_t get_wchr() { wchar_t wc; char multi[MB_LEN_MAX]; if (_mbftowc(multi, &wc, getchr, &peekc) <= 0) wc = getchr(); return (wc); }