From decwrl!wuarchive!wugate!uunet!allbery Sun Nov 26 14:26:44 PST 1989 Article 1202 of comp.sources.misc: Path: decwrl!wuarchive!wugate!uunet!allbery From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Newsgroups: comp.sources.misc Subject: v09i036: "ed" clone Message-ID: <72742@uunet.UU.NET> Date: 26 Nov 89 20:42:24 GMT Sender: allbery@uunet.UU.NET Reply-To: unknown@unknown.UUCP (Ian Phillipps) Lines: 1870 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 9, Issue 36 Submitted-by: unknown@unknown.UUCP (Ian Phillipps) Archive-name: ed_ip [Attributions and addresses lost -- please don't send compressed tar files without prior arrangement! ++bsa] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'ed.c' <<'END_OF_FILE' X/* X * ed - standard editor X * ~~ X * Authors: Brian Beattie, Kees Bot, and others X * X * Copyright 1987 Brian Beattie Rights Reserved. X * Permission to copy or distribute granted under the following conditions: X * 1). No charge may be made other than reasonable charges for reproduction. X * 2). This notice must remain intact. X * 3). No further restrictions may be added. X * 4). Except meaningless ones. X * X * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - X * TurboC mods and cleanup 8/17/88 RAMontante. X * Further information (posting headers, etc.) at end of file. X * RE stuff replaced with Spencerian version, sundry other bugfix+speedups X * Ian Phillipps. Version incremented to "5". X * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X */ X Xint version = 5; /* used only in the "set" function, for i.d. */ X X#include X/* Regexp is Henry Spencer's package. WARNING: regsub is modified to return X * a pointer to the \0 after the destination string, and this program refers X * to the "private" reganch field in the struct regexp. X */ X#include X X#ifdef __TURBOC__ X#include X#include X#include X#include X#include X X#define _cleanup() ; X X#endif /* ifdef __TURBOC__ */ X X X/* X * #defines for non-printing ASCII characters X */ X#define NUL 0x00 /* ^@ */ X#define EOS 0x00 /* end of string */ X#define SOH 0x01 /* ^A */ X#define STX 0x02 /* ^B */ X#define ETX 0x03 /* ^C */ X#define EOT 0x04 /* ^D */ X#define ENQ 0x05 /* ^E */ X#define ACK 0x06 /* ^F */ X#define BEL 0x07 /* ^G */ X#define BS 0x08 /* ^H */ X#define HT 0x09 /* ^I */ X#define LF 0x0a /* ^J */ X#define NL '\n' X#define VT 0x0b /* ^K */ X#define FF 0x0c /* ^L */ X#define CR 0x0d /* ^M */ X#define SO 0x0e /* ^N */ X#define SI 0x0f /* ^O */ X#define DLE 0x10 /* ^P */ X#define DC1 0x11 /* ^Q */ X#define DC2 0x12 /* ^R */ X#define DC3 0x13 /* ^S */ X#define DC4 0x14 /* ^T */ X#define NAK 0x15 /* ^U */ X#define SYN 0x16 /* ^V */ X#define ETB 0x17 /* ^W */ X#define CAN 0x18 /* ^X */ X#define EM 0x19 /* ^Y */ X#define SUB 0x1a /* ^Z */ X#define ESC 0x1b /* ^[ */ X#define FS 0x1c /* ^\ */ X#define GS 0x1d /* ^] */ X#define RS 0x1e /* ^^ */ X#define US 0x1f /* ^_ */ X#define SP 0x20 /* space */ X#define DEL 0x7f /* DEL*/ X#define ESCAPE '\\' X X X#define TRUE 1 X#define FALSE 0 X#define ERR -2 X#define FATAL (ERR-1) X#define CHANGED (ERR-2) X#define SET_FAIL (ERR-3) X#define SUB_FAIL (ERR-4) X#define MEM_FAIL (ERR-5) X X X#define BUFFER_SIZE 2048 /* stream-buffer size: == 1 hd cluster */ X X#define LINFREE 1 /* entry not in use */ X#define LGLOB 2 /* line marked global */ X X#define MAXLINE 512 /* max number of chars per line */ X#define MAXPAT 256 /* max number of chars per replacement pattern */ X#define MAXFNAME 256 /* max file name size */ X X X/** Global variables **/ X Xstruct line { X int l_stat; /* empty, mark */ X struct line *l_prev; X struct line *l_next; X char l_buff[1]; X}; Xtypedef struct line LINE; X X Xint diag = 1; /* diagnostic-output? flag */ Xint truncflg = 1; /* truncate long line flag */ Xint eightbit = 1; /* save eighth bit */ Xint nonascii; /* count of non-ascii chars read */ Xint nullchar; /* count of null chars read */ Xint truncated; /* count of lines truncated */ Xchar fname[MAXFNAME]; Xint fchanged; /* file-changed? flag */ Xint nofname; Xint mark['z'-'a'+1]; Xregexp *oldpat; X XLINE Line0; Xint CurLn = 0; XLINE *CurPtr = &Line0; /* CurLn and CurPtr must be kept in step */ Xint LastLn = 0; Xchar inlin[MAXLINE]; Xint pflag; Xint Line1, Line2, nlines; Xint nflg; /* print line number flag */ Xint lflg; /* print line in verbose mode */ Xint pflg; /* print current line after each command */ Xchar *inptr; /* tty input buffer */ X Xstruct tbl { X char *t_str; X int *t_ptr; X int t_val; X} *t, tbl[] = { X "number", &nflg, TRUE, X "nonumber", &nflg, FALSE, X "list", &lflg, TRUE, X "nolist", &lflg, FALSE, X "eightbit", &eightbit, TRUE, X "noeightbit", &eightbit, FALSE, X 0 X}; X X X/*-------------------------------------------------------------------------*/ X X#ifdef __TURBOC__ /* prototypes (unneeded?) */ X Xvoid prntln(char *, int, int); Xvoid putcntl(char , FILE *); Xint doprnt(int, int); XLINE *getptr(int); Xint del(int, int); Xint ins(char *); Xint append(int, int); Xint deflt(int, int); Xregexp *optpat(void); Xint egets( char*, int, FILE* ); Xint ckglob( void ); Xint doglob( void ); Xint docmd( int ); Xint dolst( int, int ); Xint doread( int, char* ); Xint dowrite( int, int, char*, int ); Xint find( regexp*, int ); Xchar* getfn( void ); Xint getlst( void ); Xint getnum( int ); Xint getone( void ); Xint getrhs( char* ); Xint join( int, int ); Xint move( int ); Xint set( void ); Xint subst( regexp*, char*, int, int ); Xint transfer( int ); X X#else /* !__TURBOC__ */ X Xextern char *strcpy(); Xextern int *malloc(); Xextern LINE *getptr(); Xextern char *gettxt(); Xextern char *gettxtl(); Xextern char *catsub(); Xregexp *optpat(); X X#endif /* __TURBOC__ */ X X X/*________ Macros ________________________________________________________*/ X X#ifndef max X# define max(a,b) ((a) > (b) ? (a) : (b)) X#endif X X#ifndef min X# define min(a,b) ((a) < (b) ? (a) : (b)) X#endif X X#ifndef toupper X# define toupper(c) ((c >= 'a' && c <= 'z') ? c-32 : c ) X#endif X X#define nextln(l) ((l)+1 > LastLn ? 0 : (l)+1) X#define prevln(l) ((l)-1 < 0 ? LastLn : (l)-1) X X#define gettxtl(lin) ((lin)->l_buff) X#define gettxt(num) (gettxtl( getptr(num) )) X X#define getnextptr(p) ((p)->l_next) X#define getprevptr(p) ((p)->l_prev) X X#define setCurLn( lin ) ( CurPtr = getptr( CurLn = (lin) ) ) X#define nextCurLn() ( CurLn = nextln(CurLn), CurPtr = getnextptr( CurPtr ) ) X#define prevCurLn() ( CurLn = prevln(CurLn), CurPtr = getprevptr( CurPtr ) ) X X#define clrbuf() del(1, LastLn) X X#define Skip_White_Space {while (*inptr==SP || *inptr==HT) inptr++;} X X#define relink(a, x, y, b) { (x)->l_prev = (a); (y)->l_next = (b); } X X X/*________ functions ______________________________________________________*/ X X X/* append.c */ X Xappend(line, glob) Xint line, glob; X{ X char lin[MAXLINE]; X X if(glob) X return(ERR); X setCurLn( line ); X for (;;) { X char *p = lin; X if(nflg) X printf("%6d. ",CurLn+1); X while( p < &lin[ MAXLINE ] ) { X int ch = getchar(); X if( ch == EOF ) X return EOF; X if( ch == '\n' ) { X *p = EOS; break; X } X *p++ = ch; X } X if(lin[0] == '.' && lin[1] == '\0') X return(0); X if( ins(lin) < 0) X return( MEM_FAIL ); X } X} X X/* ckglob.c */ X Xckglob() X{ X regexp *glbpat; X char c, delim, *lin; X int num; X LINE *ptr; X X c = *inptr; X X if(c != 'g' && c != 'v') X return(0); X if (deflt(1, LastLn) < 0) X return(ERR); X X delim = *++inptr; X if(delim <= ' ') X return(ERR); X X glbpat = optpat(); X if(*inptr == delim) X inptr++; X ptr = getptr(1); X for (num=1; num<=LastLn; num++) { X ptr->l_stat &= ~LGLOB; X if (Line1 <= num && num <= Line2) { X lin = gettxtl(ptr); X if(regexec(glbpat, lin )) { X if (c=='g') ptr->l_stat |= LGLOB; X } else { X if (c=='v') ptr->l_stat |= LGLOB; X } X ptr = getnextptr(ptr); X } X } X return(1); X} X X X/* deflt.c X * Set Line1 & Line2 (the command-range delimiters) if the file is X * empty; Test whether they have valid values. X */ X Xint deflt(def1, def2) Xint def1, def2; X{ X if(nlines == 0) { X Line1 = def1; X Line2 = def2; X } X return ( (Line1>Line2 || Line1<=0) ? ERR : 0 ); X} X X X/* del.c */ X X/* One of the calls to this function tests its return value for an error X * condition. But del doesn't return any error value, and it isn't obvious X * to me what errors might be detectable/reportable. To silence a warning X * message, I've added a constant return statement. -- RAM X * ... It could check to<=LastLn ... igp X */ X Xdel(from, to) Xint from, to; X{ X LINE *first, *last, *next, *tmp; X X if(from < 1) X from = 1; X first = getprevptr( getptr( from ) ); X last = getnextptr( getptr( to ) ); X next = first->l_next; X while(next != last && next != &Line0) { X tmp = next->l_next; X free(next); X next = tmp; X } X relink(first, last, first, last); X LastLn -= (to - from)+1; X setCurLn( prevln(from) ); X return(0); X} X X Xint dolst(line1, line2) Xint line1, line2; X{ X int oldlflg=lflg, p; X X lflg = 1; X p = doprnt(line1, line2); X lflg = oldlflg; X return p; X} X X X/* esc.c X * Map escape sequences into their equivalent symbols. Returns the X * correct ASCII character. If no escape prefix is present then s X * is untouched and *s is returned, otherwise **s is advanced to point X * at the escaped character and the translated character is returned. X */ Xesc(s) Xchar **s; X{ X register int rval; X X if (**s != ESCAPE) { X rval = **s; X } else { X (*s)++; X switch(toupper(**s)) { X case '\000': X rval = ESCAPE; break; X case 'S': X rval = ' '; break; X case 'N': X rval = '\n'; break; X case 'T': X rval = '\t'; break; X case 'B': X rval = '\b'; break; X case 'R': X rval = '\r'; break; X default: X rval = **s; break; X } X } X return (rval); X} X X X/* doprnt.c */ X Xint doprnt(from, to) Xint from, to; X{ X from = (from < 1) ? 1 : from; X to = (to > LastLn) ? LastLn : to; X X if(to != 0) { X setCurLn( from ); X while( CurLn <= to ) { X prntln( gettxtl( CurPtr ), lflg, (nflg ? CurLn : 0)); X if( CurLn == to ) X break; X nextCurLn(); X } X } X return(0); X} X X Xvoid prntln(str, vflg, lin) Xchar *str; Xint vflg, lin; X{ X if(lin) X printf("%7d ",lin); X while(*str && *str != NL) { X if(*str < ' ' || *str >= 0x7f) { X switch(*str) { X case '\t': X if(vflg) X putcntl(*str, stdout); X else X putc(*str, stdout); X break; X X case DEL: X putc('^', stdout); X putc('?', stdout); X break; X X default: X putcntl(*str, stdout); X break; X } X } else X putc(*str, stdout); X str++; X } X if(vflg) X putchar('$'); X putchar('\n'); X} X X Xvoid putcntl(c, stream) Xchar c; XFILE *stream; X{ X putc('^', stream); X putc((c&31)|'@', stream); X} X X X/* egets.c */ X Xegets(str,size,stream) Xchar *str; Xint size; XFILE *stream; X{ X int c, count; X char *cp; X X for(count = 0, cp = str; size > count;) { X c = getc(stream); X if(c == EOF) { X *cp = EOS; X if(count) X puts("[Incomplete last line]"); X return(count); X } X else if(c == NL) { X *cp = EOS; X return(++count); X } X else if (c == 0) X nullchar++; /* count nulls */ X else { X if(c > 127) { X if(!eightbit) /* if not saving eighth bit */ X c = c&127; /* strip eigth bit */ X nonascii++; /* count it */ X } X *cp++ = c; /* not null, keep it */ X count++; X } X } X str[count-1] = EOS; X if(c != NL) { X puts("truncating line"); X truncated++; X while((c = getc(stream)) != EOF) X if(c == NL) X break; X } X return(count); X} /* egets */ X X Xdoread(lin, fname) Xint lin; Xchar *fname; X{ X FILE *fp; X int err; X unsigned long bytes; X unsigned int lines; X static char str[MAXLINE]; X X err = 0; X nonascii = nullchar = truncated = 0; X X if (diag) printf("\"%s\" ",fname); X if( (fp = fopen(fname, "r")) == NULL ) { X puts(" isn't readable."); X return( ERR ); X } X setvbuf(fp, NULL, _IOFBF, BUFFER_SIZE); X setCurLn( lin ); X for(lines = 0, bytes = 0;(err = egets(str,MAXLINE,fp)) > 0;) { X bytes += err; X if(ins(str) < 0) { X err = MEM_FAIL; X break; X } X lines++; X } X fclose(fp); X if(err < 0) X return(err); X if (diag) { X printf("%u lines %u bytes",lines,bytes); X if(nonascii) X printf(" [%d non-ascii]",nonascii); X if(nullchar) X printf(" [%d nul]",nullchar); X if(truncated) X printf(" [%d lines truncated]",truncated); X putchar('\n'); X } X return( err ); X} /* doread */ X X Xint dowrite(from, to, fname, apflg) Xint from, to; Xchar *fname; Xint apflg; X{ X FILE *fp; X int lin, err; X unsigned int lines; X unsigned long bytes; X char *str; X LINE *lptr; X X err = 0; X lines = bytes = 0; X X printf("\"%s\" ",fname); X if((fp = fopen(fname,(apflg?"a":"w"))) == NULL) { X puts(" can't be opened for writing!"); X return( ERR ); X } X X setvbuf(fp, NULL, _IOFBF, BUFFER_SIZE); X lptr = getptr(from); X for(lin = from; lin <= to; lin++) { X str = lptr->l_buff; X lines++; X bytes += strlen(str) + 1; /* str + '\n' */ X if(fputs(str, fp) == EOF) { X puts("file write error"); X err++; X break; X } X fputc('\n', fp); X lptr = lptr->l_next; X } X printf("%u lines %lu bytes\n",lines,bytes); X fclose(fp); X return( err ); X} /* dowrite */ X X X/* find.c */ X Xfind(pat, dir) Xregexp *pat; Xint dir; X{ X int i, num; X LINE *lin; X X num = CurLn; X lin = CurPtr; X for(i=0; i= '0' && *inptr <= '9') { /* line number */ X for(num = 0; *inptr >= '0' && *inptr <= '9'; ++inptr) { X num = (num * 10) + (*inptr - '0'); X } X return num; X } X X switch(c = *inptr) { X case '.': X inptr++; X return (CurLn); X X case '$': X inptr++; X return (LastLn); X X case '/': X case '?': X srchpat = optpat(); X if(*inptr == c) X *inptr++; X return(find(srchpat,c == '/'?1:0)); X X case '-': X case '+': X return(first ? CurLn : 1); X X case '\'': X inptr++; X if (*inptr < 'a' || *inptr > 'z') X return(EOF); X return mark[ *inptr++ - 'a' ]; X X default: X return ( first ? EOF : 1 ); /* unknown address */ X } X} /* getnum */ X X X/* getone.c X * Parse a number (or arithmetic expression) off the command line. X */ X#define FIRST 1 X#define NOTFIRST 0 X Xint getone() X{ X int c, i, num; X X if((num = getnum(FIRST)) >= 0) { X for (;;) { X Skip_White_Space; X if(*inptr != '+' && *inptr != '-') X break; /* exit infinite loop */ X X c = *inptr++; X if((i = getnum(NOTFIRST)) < 0) X return ( i ); X if(c == '+') X num += i; X else X num -= i; X } X } X return ( num>LastLn ? ERR : num ); X} /* getone */ X X Xgetlst() X{ X int num; X X Line2 = 0; X for(nlines = 0; (num = getone()) >= 0;) X { X Line1 = Line2; X Line2 = num; X nlines++; X if(*inptr != ',' && *inptr != ';') X break; X if(*inptr == ';') X setCurLn( num ); X inptr++; X } X nlines = min(nlines, 2); X if(nlines == 0) X Line2 = CurLn; X if(nlines <= 1) X Line1 = Line2; X X return ( (num == ERR) ? num : nlines ); X} /* getlst */ X X X/* getptr.c */ X XLINE *getptr(num) Xint num; X{ X LINE *ptr; X int j; X X if (2*num>LastLn && num<=LastLn) { /* high line numbers */ X ptr = Line0.l_prev; X for (j = LastLn; j>num; j--) X ptr = ptr->l_prev; X } else { /* low line numbers */ X ptr = &Line0; X for(j = 0; j < num; j++) X ptr = ptr->l_next; X } X return(ptr); X} X X X/* getrhs.c */ X Xint getrhs(sub) Xchar *sub; X{ X char delim = *inptr++; X char *outmax = sub + MAXPAT; X if( delim == NL || *inptr == NL) /* check for eol */ X return( ERR ); X while( *inptr != delim && *inptr != NL ) { X if ( sub > outmax ) X return ERR; X if ( *inptr == ESCAPE ) { X switch ( *++inptr ) { X case 'r': X *sub++ = '\r'; X inptr++; X break; X case ESCAPE: X *sub++ = ESCAPE; X *sub++ = ESCAPE; X inptr++; X case 'n': X *sub++ = '\n'; X inptr++; X break; X case 'b': X *sub++ = '\b'; X inptr++; X break; X case '0': { X int i=3; X *sub = 0; X do { X if (*++inptr<'0' || *inptr >'7') X break; X *sub = (*sub<<3) | (*inptr-'0'); X } while (--i!=0); X sub++; X } break; X default: X if ( *inptr != delim ) X *sub++ = ESCAPE; X *sub++ = *inptr; X if ( *inptr != NL ) X inptr++; X } X } X else *sub++ = *inptr++; X } X *sub = '\0'; X X inptr++; /* skip over delimter */ X Skip_White_Space; X if(*inptr == 'g') { X *inptr++; X return( 1 ); X } X return( 0 ); X} X X/* ins.c */ X Xins(str) Xchar *str; X{ X char *cp; X LINE *new, *nxt; X int len; X X do { X for ( cp = str; *cp && *cp != NL; cp++ ) X ; X len = cp - str; X /* cp now points to end of first or only line */ X X if((new = (LINE *)malloc(sizeof(LINE)+len)) == NULL) X return( MEM_FAIL ); /* no memory */ X X new->l_stat=0; X strncpy(new->l_buff,str,len); /* build new line */ X new->l_buff[len] = EOS; X nxt = getnextptr(CurPtr); /* get next line */ X relink(CurPtr, new, new, nxt); /* add to linked list */ X relink(new, nxt, CurPtr, new); X LastLn++; X CurLn++; X CurPtr = new; X str = cp + 1; X } X while( *cp != EOS ); X return 1; X} X X X/* join.c */ X Xint join(first, last) Xint first, last; X{ X char buf[MAXLINE]; X char *cp=buf, *str; X LINE *lin; X int num; X X if (first<=0 || first>last || last>LastLn) X return(ERR); X if (first==last) { X setCurLn( first ); X return 0; X } X lin = getptr(first); X for (num=first; num<=last; num++) { X str=gettxtl(lin); X while ( *str ) { X if (cp >= buf + MAXLINE-1 ) { X puts("line too long"); X return(ERR); X } X *cp++ = *str++; X } X lin = getnextptr(lin); X } X *cp = EOS; X del(first, last); X if( ins(buf) < 0 ) X return MEM_FAIL; X fchanged = TRUE; X return 0; X} X X X/* move.c X * Unlink the block of lines from Line1 to Line2, and relink them X * after line "num". X */ X Xint move(num) Xint num; X{ X int range; X LINE *before, *first, *last, *after; X X if( Line1 <= num && num <= Line2 ) X return( ERR ); X range = Line2 - Line1 + 1; X before = getptr(prevln(Line1)); X first = getptr(Line1); X last = getptr(Line2); X after = getptr(nextln(Line2)); X X relink(before, after, before, after); X LastLn -= range; /* per AST's posted patch 2/2/88 */ X if (num > Line1) X num -= range; X X before = getptr(num); X after = getptr(nextln(num)); X relink(before, first, last, after); X relink(last, after, before, first); X LastLn += range; /* per AST's posted patch 2/2/88 */ X setCurLn( num + range ); X return( 1 ); X} X X Xint transfer(num) Xint num; X{ X int mid, lin, ntrans; X X if (Line1<=0 || Line1>Line2) X return(ERR); X X mid= numt_str; t++) { X if(strcmp(word,t->t_str) == 0) { X *t->t_ptr = t->t_val; X return(0); X } X } X return SET_FAIL; X} X X#ifndef relink Xvoid relink(a, x, y, b) XLINE *a, *x, *y, *b; X{ X x->l_prev = a; X y->l_next = b; X} X#endif X X Xvoid set_ed_buf(void) X{ X relink(&Line0, &Line0, &Line0, &Line0); X CurLn = LastLn = 0; X CurPtr = &Line0; X} X X X/* subst.c */ X Xint subst(pat, sub, gflg, pflag) Xregexp *pat; Xchar *sub; Xint gflg, pflag; X{ X int nchngd = 0; X char *txtptr; X char *new, buf[MAXLINE]; X int still_running = 1; X LINE *lastline = getptr( Line2 ); X X if(Line1 <= 0) X return( SUB_FAIL ); X nchngd = 0; /* reset count of lines changed */ X X for( setCurLn( prevln( Line1 ) ); still_running; ) { X nextCurLn(); X new = buf; X if ( CurPtr == lastline ) X still_running = 0; X if ( regexec( pat, txtptr = gettxtl( CurPtr ) ) ) { X do X { X /* Copy leading text */ X int diff = pat->startp[0] - txtptr; X strncpy( new, txtptr, diff ); X new += diff; X /* Do substitution */ X new = regsub( pat, sub, new ); X txtptr = pat->endp[0]; X } X while( gflg && !pat->reganch && regexec( pat, txtptr )); X X /* Copy trailing chars */ X while( *txtptr ) *new++ = *txtptr++; X X if(new >= buf+MAXLINE) X return( SUB_FAIL ); X *new++ = EOS; X del(CurLn,CurLn); X if( ins(buf) < 0 ) X return MEM_FAIL; X nchngd++; X if(pflag) X doprnt(CurLn, CurLn); X } X } X return (( nchngd == 0 && !gflg ) ? SUB_FAIL : nchngd); X} X X X/* system.c */ X#ifndef __TURBOC__ X X#define SHELL "/bin/sh" X Xsystem(c) Xchar *c; { X int pid, status; X X switch (pid = fork()) { X case -1: X return -1; X case 0: X execl(SHELL, "sh", "-c", c, (char *) 0); X exit(-1); X default: X while (wait(&status) != pid) X ; X } X return status; X} X#endif /* ifndef __TURBOC__ */ X X X/* docmd.c X * Perform the command specified in the input buffer, as pointed to X * by inptr. Actually, this finds the command letter first. X */ X Xint docmd(glob) Xint glob; X{ X static char rhs[MAXPAT]; X regexp *subpat; X int c, err, line3; X int apflg, pflag, gflag; X int nchng; X char *fptr; X X pflag = FALSE; X Skip_White_Space; X X c = *inptr++; X switch(c) { X case NL: X if( nlines == 0 && (Line2 = nextln(CurLn)) == 0 ) X return(ERR); X setCurLn( Line2 ); X return (1); X X case '=': X printf("%d\n",Line2); X break; X X case 'a': X if(*inptr != NL || nlines > 1) X return(ERR); X X if(append(Line1, glob) < 0) X return(ERR); X fchanged = TRUE; X break; X X case 'c': X if(*inptr != NL) X return(ERR); X X if(deflt(CurLn, CurLn) < 0) X return(ERR); X X if(del(Line1, Line2) < 0) X return(ERR); X if(append(CurLn, glob) < 0) X return(ERR); X fchanged = TRUE; X break; X X case 'd': X if(*inptr != NL) X return(ERR); X X if(deflt(CurLn, CurLn) < 0) X return(ERR); X X if(del(Line1, Line2) < 0) X return(ERR); X if(nextln(CurLn) != 0) X nextCurLn(); X fchanged = TRUE; X break; X X case 'e': X if(nlines > 0) X return(ERR); X if(fchanged) X return CHANGED; X /*FALL THROUGH*/ X case 'E': X if(nlines > 0) X return(ERR); X X if(*inptr != ' ' && *inptr != HT && *inptr != NL) X return(ERR); X X if((fptr = getfn()) == NULL) X return(ERR); X X clrbuf(); X if((err = doread(0, fptr)) < 0) X return(err); X X strcpy(fname, fptr); X fchanged = FALSE; X break; X X case 'f': X if(nlines > 0) X return(ERR); X X if(*inptr != ' ' && *inptr != HT && *inptr != NL) X return(ERR); X X if((fptr = getfn()) == NULL) X return(ERR); X X if (nofname) X printf("%s\n", fname); X else X strcpy(fname, fptr); X break; X X case 'i': X if(*inptr != NL || nlines > 1) X return(ERR); X X if(append(prevln(Line1), glob) < 0) X return(ERR); X fchanged = TRUE; X break; X X case 'j': X if (*inptr != NL || deflt(CurLn, CurLn+1)<0) X return(ERR); X X if (join(Line1, Line2) < 0) X return(ERR); X break; X X case 'k': X Skip_White_Space; X X if (*inptr < 'a' || *inptr > 'z') X return ERR; X c= *inptr++; X X if(*inptr != ' ' && *inptr != HT && *inptr != NL) X return(ERR); X X mark[c-'a'] = Line1; X break; X X case 'l': X if(*inptr != NL) X return(ERR); X if(deflt(CurLn,CurLn) < 0) X return(ERR); X if (dolst(Line1,Line2) < 0) X return(ERR); X break; X X case 'm': X if((line3 = getone()) < 0) X return(ERR); X if(deflt(CurLn,CurLn) < 0) X return(ERR); X if(move(line3) < 0) X return(ERR); X fchanged = TRUE; X break; X X case 'P': X case 'p': X if(*inptr != NL) X return(ERR); X if(deflt(CurLn,CurLn) < 0) X return(ERR); X if(doprnt(Line1,Line2) < 0) X return(ERR); X break; X X case 'q': X if(fchanged) X return CHANGED; X /*FALL THROUGH*/ X case 'Q': X if(*inptr == NL && nlines == 0 && !glob) X return(EOF); X else X return(ERR); X X case 'r': X if(nlines > 1) X return(ERR); X X if(nlines == 0) /* The original code tested */ X Line2 = LastLn; /* if(nlines = 0) */ X /* which looks wrong. RAM */ X X if(*inptr != ' ' && *inptr != HT && *inptr != NL) X return(ERR); X X if((fptr = getfn()) == NULL) X return(ERR); X X if((err = doread(Line2, fptr)) < 0) X return(err); X fchanged = TRUE; X break; X X case 's': X if(*inptr == 'e') X return(set()); X Skip_White_Space; X if((subpat = optpat()) == NULL) X return(ERR); X if((gflag = getrhs(rhs)) < 0) X return(ERR); X if(*inptr == 'p') X pflag++; X if(deflt(CurLn, CurLn) < 0) X return(ERR); X if((nchng = subst(subpat, rhs, gflag, pflag)) < 0) X return(ERR); X if(nchng) X fchanged = TRUE; X break; X X case 't': X if((line3 = getone()) < 0) X return(ERR); X if(deflt(CurLn,CurLn) < 0) X return(ERR); X if(transfer(line3) < 0) X return(ERR); X fchanged = TRUE; X break; X X case 'W': X case 'w': X apflg = (c=='W'); X X if(*inptr != ' ' && *inptr != HT && *inptr != NL) X return(ERR); X X if((fptr = getfn()) == NULL) X return(ERR); X X if(deflt(1, LastLn) < 0) X return(ERR); X if(dowrite(Line1, Line2, fptr, apflg) < 0) X return(ERR); X fchanged = FALSE; X break; X X case 'x': X if(*inptr == NL && nlines == 0 && !glob) { X if((fptr = getfn()) == NULL) X return(ERR); X if(dowrite(1, LastLn, fptr, 0) >= 0) X return(EOF); X } X return(ERR); X X case 'z': X if(deflt(CurLn,CurLn) < 0) X return(ERR); X X switch(*inptr) { X case '-': X if(doprnt(Line1-21,Line1) < 0) X return(ERR); X break; X X case '.': X if(doprnt(Line1-11,Line1+10) < 0) X return(ERR); X break; X X case '+': X case '\n': X if(doprnt(Line1,Line1+21) < 0) X return(ERR); X break; X } X break; X X default: X return(ERR); X } X return (0); X} /* docmd */ X X X/* doglob.c */ Xdoglob() X{ X int lin, stat; X char *cmd; X LINE *ptr; X X cmd = inptr; X X for (;;) { X ptr = getptr(1); X for (lin=1; lin<=LastLn; lin++) { X if (ptr->l_stat & LGLOB) X break; X ptr = getnextptr(ptr); X } X if (lin > LastLn) X break; X X ptr->l_stat &= ~LGLOB; X CurLn = lin; CurPtr = ptr; X inptr = cmd; X if((stat = getlst()) < 0) X return(stat); X if((stat = docmd(1)) < 0) X return(stat); X } X return(CurLn); X} /* doglob */ X X X/* X * Software signal 2 or SIGINT is caught and the result is to resume X * the main loop at a command prompt. X */ X#include Xjmp_buf env; X X#ifndef __TURBOC__ Xintr() X{ X puts("intr()?"); X longjmp(env, 1); X} X X#else X Xvoid Catcher(void) X{ X longjmp(env, 1); X} X#endif /* !__TURBOC__ */ X X X/*-------- main ---------------------------------------------------------*/ X#ifndef _Cdecl X# define _Cdecl X#endif X Xvoid _Cdecl main(argc,argv) Xint argc; Xchar **argv; X{ X int stat, i, doflush; X X set_ed_buf(); X doflush=isatty(1); X X if (argc>1 && argv[1][0]=='-' && argv[1][1]==0) { X diag = 0; X argc--; X argv++; X } X if(argc > 1) { X for(i = 1; i < argc; i++) { X if(doread(0,argv[i])==0) { X setCurLn( 1 ); X strcpy(fname, argv[i]); X break; X } X } X } X X for (;;) { X X setjmp(env); X putchar(':'); /* The command-line prompt */ X X#ifndef __TURBOC__ X signal(2, intr); X#else /* __TURBOC__ */ X signal(SIGINT, Catcher); X#endif /* !__TURBOC__ */ X X if (doflush) X fflush(stdout); X if (fgets(inlin, sizeof(inlin),stdin) == NULL) { X puts("Null input."); X break; X } X if(*inlin == '!') { X for(inptr = inlin; *inptr != NL; inptr++) X ; X *inptr = EOS; X system(inlin+1); X continue; X } X inptr = inlin; X if(getlst() >= 0) X if((stat = ckglob()) != 0) { X if(stat >= 0 && (stat = doglob()) >= 0) { X setCurLn( stat ); X continue; X } X } else { X if((stat = docmd(0)) >= 0) { X if(stat == 1) X doprnt(CurLn, CurLn); X continue; X } X } X switch (stat) { X case EOF: X _cleanup(); exit(0); X case FATAL: X fputs("FATAL ERROR\n",stderr); X _cleanup(); exit(1); X case CHANGED: X puts("File has been changed."); X break; X case SET_FAIL: X puts("`set' command failed."); X break; X case SUB_FAIL: X puts("string substitution failed."); X break; X case MEM_FAIL: X puts("Out of memory: text may have been lost." ); X break; X default: X puts("Oops?"); X /* Unrecognized or failed command (this */ X /* is SOOOO much better than "?" :-) */ X } X } X} /* main */ X/*________ end of source code ____________________________________________*/ END_OF_FILE if test 27843 -ne `wc -c <'ed.c'`; then echo shar: \"'ed.c'\" unpacked with wrong size! fi # end of 'ed.c' fi if test -f 'ed.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ed.man'\" else echo shar: Extracting \"'ed.man'\" \(6675 characters\) sed "s/^X//" >'ed.man' <<'END_OF_FILE' Xed - line-oriented text editor, similar to UN*X V7 ed X Xsyntax: X ed [existing-file] X X existing-file Only an existing file can be opened. Otherwise X a nameless edit buffer is opened; a name can be used with X the `f' or `w' commands. X XAs a line editor, ed operates in one of two modes: COMMAND mode, in which Xa colon is displayed at the screen's bottom to prompt for a command; and XINPUT mode, in which all keyboard input is added to the file (edit buffer). XFrom COMMAND mode, INPUT mode is entered by either the `i' or `a' commands. XFrom INPUT mode, the COMMAND mode is restored by entering a line consisting Xof a single period by itself. If such a line is desired in the file, it Xcan be created by entering (for instance) two periods, then using the `s' Xcommand to change these to only one period. X XA command consists of an optional line-range specification, a single char- Xacter indicating the command, and for some commands an optional third Xargument. The line-range specification is either a single line number or Xa first-line number and a last-line number separated by a comma. The Xcharacter `^' means the first line of the file; `$' means the last line Xof the file. X XCommands: X X If a line is specified, make that the new current line. X Otherwise advance the current-line-pointer by one line. X = Print the line number of the current line. X . (by itself) Print the current line. X - (by itself) Move the current-line-pointer back one, and print X the new current line. X + (by itself) Move the current-line-pointer forward one, and X print the new current line. X ! Execute a shell command and return. X a Go into INPUT mode with a new line following the current line. X (INPUT mode is terminated by an input line containing only a X period in the first column.) X i Go into INPUT mode with a new line preceding the current line. X (INPUT mode is terminated by an input line containing only a X period in the first column.) X c Delete the specified lines (or the current line) and then X add new lines in their place. This is equivalent to a `d' X command followed by an `i' command. X d Delete the specified range of lines (or the current line). X Leave the current-line-pointer at the following line. X e Clear the edit buffer and begin editing a new file. This X command fails if the buffer contains changes (or new lines) X which have not been written out. To discard these changes X and edit a new file, use `E' instead of `e'. X E Clear the edit buffer and begin editing a new file, regardless X of any changes to the current edit buffer. X f Print the filename, or set it to a new name if specified. X g Perform the following command on all matching lines in range X j Join the addressed lines together (or the current line to the X previous line). X k Mark the addressed line with the specified letter. Example: X `17ka' puts mark "a" on line 17. X l List the addressed lines, showing all non-printing characters X and indicating the end-of-line. X m Move the specified range of lines to follow the line number X given. Example: `5,7m3' moves lines 5 through 7 "up", to X follow line 3. X p,P Print the specified lines. X q Quit the editor. This fails if the edit buffer contains any X changes. If so, use `Q' instead. X Q Quit the editor absolutely. Any changes are discarded. X r Read in a file, adding it after the current line. X s Substitute text on the current line. Example: `s/alpha/beta/' X finds the string "alpha" and replaces it with "beta". X t Transfer (copy) the specified range of lines to follow the line X number given. Example: `5,7t7' puts a copy of lines 5 through X 7 after line 7. X v Perform the following command on all non-matching lines in range X w,W Write the edit buffer out. If a filename is given, it is used X and becomes the current filename. If a range of lines is X specified, only those lines are written. X x Write the entire buffer out to its file, and terminate. X z Print 21 lines. `-', `.', or `+' may be given, and mean X "start 21 lines previous, end at current line", X "start 11 lines previous, end 10 lines hence", or X "start at current line, end 21 lines from here", respectively. X XThe syntax of g and v is g /re/ X XBUGS and COMMENTS X XThere's no "u" command. Reading files is still pretty slow. IGP. X X[Here's an example of how to split a line: .s/BUGS/BU\nGS/ XThe backslash n (\n) ends the current line after the U and starts another Xline beginning with G. -Ed L] X XThe code started out as many small files. It is now one file, but the Xresult isn't as clean as it could be. I've cleaned it up some, but in Xconverting it to TurboC compliancy I've probably lost generic C compliancy. XHeaders describing its distribution history are appended to the code. X X[The i variable in function doprnt() has been eliminated and replaced by Xthe global variable CurLn. MSDOS signal code has been added to intercept XCTRL C and CTRL [BREAK] so that the program does not abort your work when Xyou press either of these key sequences. -Ed L] X XAUTHORS X XBrian Beattie seems to be the original author. Kees Bot is associated Xwith it. Andy Tanenbaum ported it to MINIX, and posted it to Usenet. XBob Montante ported it to MSDOS and did some minor dressing-up. XSomebody called Ed L added some comments above. X XThis version is derived from the one posted in comp.binaries.ibm.pc in ?early X1988. XIan Phillipps replaced the regexp stuff with Henry Spencer's version, and Xdid some other speed-ups and tinkerings. See below. XLewis Carroll wrote Alice's Adventures in Wonderland; XJames Joyce wrote Finnegans Wake; they are clearly the spiritual wellsprings. X[I think Dennis Ritchie deserves a mention here, too. IGP] X X[The ed.c source contains an untoggled switch for displaying line Xnumbers and another for stripping high bits off characters. If you Xtype "se" at the ":" prompt, you will get a decimal version number. X-Ed L] X XChanges by IGP since the last comp.binaries.ibm.pc posting: X XRegular expression code replaced with Henry Spencer's (lightly hacked). XUnneccessary text copying eliminated, and the remaining quadratic-time Xalgorithms to track line numbers removed. The program now remembers a pointer Xto the current line. XAll this has speeded up "g" by a factor of 20-50, so now I've documented it :-). XLogic fixed in egets to eliminate redundant tests. XA bug in s/.../\n/ has been fixed. XAll out-of-memory errors should now be reported (many were ignored before). XA Turbo-C dependency ("cdecl") has been #ifdefed. XAll routines now have prototypes. XIan Phillipps (probably contactable via ex-igp@camcon.co.uk, Xbut don't bank on it) END_OF_FILE if test 6675 -ne `wc -c <'ed.man'`; then echo shar: \"'ed.man'\" unpacked with wrong size! fi # end of 'ed.man' fi if test -f 'read.me' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'read.me'\" else echo shar: Extracting \"'read.me'\" \(3023 characters\) sed "s/^X//" >'read.me' <<'END_OF_FILE' XHere is a version of the unix "ed" editor. XFor its history, please see the manual page. X XThis source uses a very slightly hacked version of Henry Spencer's XRegexp package. The only difference from Henry's original is that regsub Xnow returns a value, so it should be compatible with other uses of this Xpackage. X XI'm sure that Henry's package is in an archive server somewhere, but I Xjust mailed him (for his address, look in /usr/spool/news/.... :-) Xand was sent them by return. Don't you all do the same, though! X XHere are the diffs from Henry's original: X X*** regexp.h.orig Fri Sep 29 11:23:29 1989 X--- regexp.h Wed Nov 22 12:31:41 1989 X*************** X*** 15,21 **** X char program[1]; /* Unwarranted chumminess with compiler. */ X } regexp; X X extern regexp *regcomp(); X extern int regexec(); X! extern void regsub(); X extern void regerror(); X--- 15,29 ---- X char program[1]; /* Unwarranted chumminess with compiler. */ X } regexp; X X+ #ifdef __STDC__ X+ extern regexp *regcomp( char * ); X+ extern int regexec( regexp*, char* ); X+ extern char *regsub( regexp*, char*, char* ); X+ extern void regerror( char* ); X+ #else X extern regexp *regcomp(); X extern int regexec(); X! extern char *regsub(); X extern void regerror(); X+ #endif X*** regsub.c.orig Fri Sep 29 11:23:33 1989 X--- regsub.c Wed Nov 22 12:18:52 1989 X*************** X*** 18,23 **** X--- 18,27 ---- X * X * 3. Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software. X+ * X+ * This version modified by Ian Phillipps to return pointer to terminating X+ * NUL on substitution string. [ Temp mail address ex-igp@camcon.co.uk ] X+ * X */ X #include X #include X*************** X*** 32,38 **** X /* X - regsub - perform substitutions after a regexp match X */ X! void X regsub(prog, source, dest) X regexp *prog; X char *source; X--- 36,43 ---- X /* X - regsub - perform substitutions after a regexp match X */ X! char * X regsub(prog, source, dest) X regexp *prog; X char *source; X*************** X*** 47,57 **** X X if (prog == NULL || source == NULL || dest == NULL) { X regerror("NULL parm to regsub"); X! return; X } X if (UCHARAT(prog->program) != MAGIC) { X regerror("damaged regexp fed to regsub"); X! return; X } X X src = source; X--- 52,62 ---- X X if (prog == NULL || source == NULL || dest == NULL) { X regerror("NULL parm to regsub"); X! return NULL; X } X if (UCHARAT(prog->program) != MAGIC) { X regerror("damaged regexp fed to regsub"); X! return NULL; X } X X src = source; X*************** X*** 74,82 **** X dst += len; X if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ X regerror("damaged match string"); X! return; X } X } X } X! *dst++ = '\0'; X } X--- 79,89 ---- X dst += len; X if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ X regerror("damaged match string"); X! return NULL; X } X } X } X! *dst = '\0'; X! return dst; X } X*************** end of diffs ***************** END_OF_FILE if test 3023 -ne `wc -c <'read.me'`; then echo shar: \"'read.me'\" unpacked with wrong size! fi # end of 'read.me' fi echo shar: End of shell archive. exit 0