/* * ed - standard editor * ~~ * Authors: Brian Beattie, Kees Bot, and others * * Copyright 1987 Brian Beattie Rights Reserved. * Permission to copy or distribute granted under the following conditions: * 1). No charge may be made other than reasonable charges for reproduction. * 2). This notice must remain intact. * 3). No further restrictions may be added. * 4). Except meaningless ones. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * TurboC mods and cleanup 8/17/88 RAMontante. * Further information (posting headers, etc.) at end of file. * RE stuff replaced with Spencerian version, sundry other bugfix+speedups * Ian Phillipps. Version incremented to "5". * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ int version = 5; /* used only in the "set" function, for i.d. */ #include "os.h" /* Regexp is Henry Spencer's package. WARNING: regsub is modified to return * a pointer to the \0 after the destination string, and this program refers * to the "private" reganch field in the struct regexp. */ #include "regexp.h" #include "object.h" #include "config.h" /* * #defines for non-printing ASCII characters */ #define NUL 0x00 /* ^@ */ #define EOS 0x00 /* end of string */ #define SOH 0x01 /* ^A */ #define STX 0x02 /* ^B */ #define ETX 0x03 /* ^C */ #define EOT 0x04 /* ^D */ #define ENQ 0x05 /* ^E */ #define ACK 0x06 /* ^F */ #define BEL 0x07 /* ^G */ #define BS 0x08 /* ^H */ #define HT 0x09 /* ^I */ #define LF 0x0a /* ^J */ #define NL '\n' #define VT 0x0b /* ^K */ #define FF 0x0c /* ^L */ #define CR 0x0d /* ^M */ #define SO 0x0e /* ^N */ #define SI 0x0f /* ^O */ #define DLE 0x10 /* ^P */ #define DC1 0x11 /* ^Q */ #define DC2 0x12 /* ^R */ #define DC3 0x13 /* ^S */ #define DC4 0x14 /* ^T */ #define NAK 0x15 /* ^U */ #define SYN 0x16 /* ^V */ #define ETB 0x17 /* ^W */ #define CAN 0x18 /* ^X */ #define EM 0x19 /* ^Y */ #define SUB 0x1a /* ^Z */ #define ESC 0x1b /* ^[ */ #define FS 0x1c /* ^\ */ #define GS 0x1d /* ^] */ #define RS 0x1e /* ^^ */ #define US 0x1f /* ^_ */ #define SP 0x20 /* space */ #define DEL 0x7f /* DEL */ #define ESCAPE '\\' #define TRUE 1 #define FALSE 0 #define ERR -2 #define FATAL (ERR-1) #define CHANGED (ERR-2) #define SET_FAIL (ERR-3) #define SUB_FAIL (ERR-4) #define MEM_FAIL (ERR-5) #define BUFFER_SIZE 2048 /* stream-buffer size: == 1 hd cluster */ #define LINFREE 1 /* entry not in use */ #define LGLOB 2 /* line marked global */ #define MAXLINE 512 /* max number of chars per line */ #define MAXPAT 256 /* max number of chars per replacement pattern */ #define MAXFNAME 256 /* max file name size */ /** Global variables **/ struct line { int l_stat; /* empty, mark */ struct line *l_prev; struct line *l_next; char l_buff[1]; }; typedef struct line LINE; extern struct object *command_giver; /* comm1.c */ extern void set_prompt(char *str); extern void add_message(char *fmt, ...); /* simulate.c */ extern void error(char *fmt, ...); extern char *check_file_name(char *file, int writeflg); /* main.c */ extern void *xalloc(int size); extern char *escape_path (char *str); #define P_DIAG (command_giver->ed_buffer->diag) #define P_TRUNCFLG (command_giver->ed_buffer->truncflg) #define P_EIGHTBIT (command_giver->ed_buffer->eightbit) #define P_NONASCII (command_giver->ed_buffer->nonascii) #define P_NULLCHAR (command_giver->ed_buffer->nullchar) #define P_TRUNCATED (command_giver->ed_buffer->truncated) #define P_FNAME (command_giver->ed_buffer->fname) #define P_FCHANGED (command_giver->ed_buffer->fchanged) #define P_NOFNAME (command_giver->ed_buffer->nofname) #define P_MARK (command_giver->ed_buffer->mark) #define P_OLDPAT (command_giver->ed_buffer->oldpat) #define P_LINE0 (command_giver->ed_buffer->Line0) #define P_LINE0 (command_giver->ed_buffer->Line0) #define P_CURLN (command_giver->ed_buffer->CurLn) #define P_CURPTR (command_giver->ed_buffer->CurPtr) #define P_LASTLN (command_giver->ed_buffer->LastLn) #define P_LINE1 (command_giver->ed_buffer->Line1) #define P_LINE2 (command_giver->ed_buffer->Line2) #define P_NLINES (command_giver->ed_buffer->nlines) #define P_PFLAG (command_giver->ed_buffer->pflag) #define P_NFLG (command_giver->ed_buffer->nflg) #define P_LFLG (command_giver->ed_buffer->lflg) #define P_PFLG (command_giver->ed_buffer->pflg) #define P_APPENDING (command_giver->ed_buffer->appending) char inlin[MAXLINE]; char *inptr; /* tty input buffer */ struct ed_buffer { int diag; /* diagnostic-output? flag */ int truncflg; /* truncate long line flag */ int eightbit; /* save eighth bit */ int nonascii; /* count of non-ascii chars read */ int nullchar; /* count of null chars read */ int truncated; /* count of lines truncated */ char fname[MAXFNAME]; int fchanged; /* file-changed? flag */ int nofname; int mark['z' - 'a' + 1]; regexp *oldpat; LINE Line0; int CurLn; LINE *CurPtr; /* CurLn and CurPtr must be kept in step */ int LastLn; int pflag; int Line1, Line2, nlines; int nflg; /* print line number flag */ int lflg; /* print line in verbose mode */ int pflg; /* print current line after each command */ int appending; }; #if 0 struct tbl { char *t_str; int *t_ptr; int t_val; } *t, tbl[] = { "number", &nflg, TRUE, "nonumber", &nflg, FALSE, "list", &lflg, TRUE, "nolist", &lflg, FALSE, "eightbit", &eightbit, TRUE, "noeightbit", &eightbit, FALSE, 0}; #endif /*-------------------------------------------------------------------------*/ /* ed.c */ static int append (int line, int glob); static int more_append (char *str); static int ckglob (void); static int deflt (int def1, int def2); static int del (int from, int to); static int dolst (int line1, int line2); static int esc (char **s); static int doprnt (int from, int to); static void prntln (char *str, int vflg, int lin); static void putcntl (char c, FILE * stream); static int egets (char *str, int size, FILE * stream); static int doread (int lin, char *fname); static int dowrite (int from, int to, char *fname, int apflg); static int find (regexp * pat, int dir); static char *getfn (int writeflg); static int getnum (int first); static int getone (void); static int getlst (void); static LINE *getptr (int num); static int getrhs (char *sub); static int ins (char *str); static int join (int first, int last); static int move (int num); static int transfer (int num); static regexp *optpat (void); void regerror (char *s); static int set (void); static void set_ed_buf (void); static int subst (regexp * pat, char *sub, int gflg, int pflag); static int docmd (int glob); static int doglob (void); void ed_start (char *file_arg); void ed_cmd (char *str); /*________ Macros ________________________________________________________*/ #ifndef max # define max(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef min # define min(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef toupper # define toupper(c) ((c >= 'a' && c <= 'z') ? c-32 : c ) #endif #define nextln(l) ((l)+1 > P_LASTLN ? 0 : (l)+1) #define prevln(l) ((l)-1 < 0 ? P_LASTLN : (l)-1) #define gettxtl(lin) ((lin)->l_buff) #define gettxt(num) (gettxtl( getptr(num) )) #define getnextptr(p) ((p)->l_next) #define getprevptr(p) ((p)->l_prev) #define setCurLn( lin ) ( P_CURPTR = getptr( P_CURLN = (lin) ) ) #define nextCurLn() ( P_CURLN = nextln(P_CURLN), P_CURPTR = getnextptr( P_CURPTR ) ) #define prevCurLn() ( P_CURLN = prevln(P_CURLN), P_CURPTR = getprevptr( P_CURPTR ) ) #define clrbuf() del(1, P_LASTLN) #define Skip_White_Space {while (*inptr==SP || *inptr==HT) inptr++;} #define relink(a, x, y, b) { (x)->l_prev = (a); (y)->l_next = (b); } /*________ functions ______________________________________________________*/ /* append.c */ static int append (int line, int glob) { if (glob) return (ERR); setCurLn (line); P_APPENDING = 1; set_prompt ("*\b"); return 0; } static int more_append (char *str) { if (P_NFLG) add_message ("%6d. ", P_CURLN + 1); if (str[0] == '.' && str[1] == '\0') { P_APPENDING = 0; set_prompt (":"); return (0); } if (ins (str) < 0) return (MEM_FAIL); return 0; } /* ckglob.c */ static int ckglob (void) { regexp *glbpat; char c, delim, *lin; int num; LINE *ptr; c = *inptr; if (c != 'g' && c != 'v') return (0); if (deflt (1, P_LASTLN) < 0) return (ERR); delim = *++inptr; if (delim <= ' ') return (ERR); glbpat = optpat (); if (*inptr == delim) inptr++; ptr = getptr (1); for (num = 1; num <= P_LASTLN; num++) { ptr->l_stat &= ~LGLOB; if (P_LINE1 <= num && num <= P_LINE2) { lin = gettxtl (ptr); if (regexec (glbpat, lin)) { if (c == 'g') ptr->l_stat |= LGLOB; } else { if (c == 'v') ptr->l_stat |= LGLOB; } ptr = getnextptr (ptr); } } return (1); } /* deflt.c * Set P_LINE1 & P_LINE2 (the command-range delimiters) if the file is * empty; Test whether they have valid values. */ static int deflt (int def1, int def2) { if (P_NLINES == 0) { P_LINE1 = def1; P_LINE2 = def2; } return ((P_LINE1 > P_LINE2 || P_LINE1 <= 0) ? ERR : 0); } /* del.c */ /* One of the calls to this function tests its return value for an error * condition. But del doesn't return any error value, and it isn't obvious * to me what errors might be detectable/reportable. To silence a warning * message, I've added a constant return statement. -- RAM * ... It could check to<=P_LASTLN ... igp */ static int del (int from, int to) { LINE *first, *last, *next, *tmp; if (from < 1) from = 1; first = getprevptr (getptr (from)); last = getnextptr (getptr (to)); next = first->l_next; while (next != last && next != &P_LINE0) { tmp = next->l_next; free (next); next = tmp; } relink (first, last, first, last); P_LASTLN -= (to - from) + 1; setCurLn (prevln (from)); return (0); } static int dolst (int line1, int line2) { int oldlflg = P_LFLG, p; P_LFLG = 1; p = doprnt (line1, line2); P_LFLG = oldlflg; return p; } /* esc.c * Map escape sequences into their equivalent symbols. Returns the * correct ASCII character. If no escape prefix is present then s * is untouched and *s is returned, otherwise **s is advanced to point * at the escaped character and the translated character is returned. */ static int esc (char **s) { register int rval; if (**s != ESCAPE) { rval = **s; } else { (*s)++; switch (toupper (**s)) { case '\000': rval = ESCAPE; break; case 'S': rval = ' '; break; case 'N': rval = '\n'; break; case 'T': rval = '\t'; break; case 'B': rval = '\b'; break; case 'R': rval = '\r'; break; default: rval = **s; break; } } return (rval); } /* doprnt.c */ static int doprnt (int from, int to) { from = (from < 1) ? 1 : from; to = (to > P_LASTLN) ? P_LASTLN : to; if (to != 0) { setCurLn (from); while (P_CURLN <= to) { prntln (gettxtl (P_CURPTR), P_LFLG, (P_NFLG ? P_CURLN : 0)); if (P_CURLN == to) break; nextCurLn (); } } return (0); } static void prntln (char *str, int vflg, int lin) { if (lin) add_message ("%7d ", lin); while (*str && *str != NL) { if (*str < ' ' || *str >= 0x7f) { switch (*str) { case '\t': if (vflg) putcntl (*str, stdout); else add_message ("%c", *str); break; case DEL: putc ('^', stdout); putc ('?', stdout); break; default: putcntl (*str, stdout); break; } } else add_message ("%c", *str); str++; } if (vflg) add_message ("$"); add_message ("\n"); } static void putcntl (char c, FILE * stream) { putc ('^', stream); putc ((c & 31) | '@', stream); } /* egets.c */ static int egets (char *str, int size, FILE * stream) { int c = 0, count; char *cp; for (count = 0, cp = str; size > count;) { c = getc (stream); if (c == EOF) { *cp = EOS; if (count) add_message ("[Incomplete last line]\n"); return (count); } else if (c == NL) { *cp = EOS; return (++count); } else if (c == 0) P_NULLCHAR++; /* count nulls */ else { if (c > 127) { if (!P_EIGHTBIT) /* if not saving eighth bit */ c = c & 127; /* strip eigth bit */ P_NONASCII++; /* count it */ } *cp++ = c; /* not null, keep it */ count++; } } str[count - 1] = EOS; if (c != NL) { add_message ("truncating line\n"); P_TRUNCATED++; while ((c = getc (stream)) != EOF) if (c == NL) break; } return (count); } /* egets */ static int doread (int lin, char *fname) { FILE *fp; int err; unsigned long bytes; unsigned int lines; static char str[MAXLINE]; P_NONASCII = P_NULLCHAR = P_TRUNCATED = 0; if (P_DIAG) add_message ("\"%s\" ", fname); escape_path (fname); if ((fp = fopen (fname, "r")) == NULL) { add_message (" isn't readable.\n"); return (ERR); } setCurLn (lin); for (lines = 0, bytes = 0; (err = egets (str, MAXLINE, fp)) > 0;) { bytes += err; if (ins (str) < 0) { err = MEM_FAIL; break; } lines++; } fclose (fp); if (err < 0) return (err); if (P_DIAG) { add_message ("%u lines %u bytes", lines, bytes); if (P_NONASCII) add_message (" [%d non-ascii]", P_NONASCII); if (P_NULLCHAR) add_message (" [%d nul]", P_NULLCHAR); if (P_TRUNCATED) add_message (" [%d lines truncated]", P_TRUNCATED); add_message ("\n"); } return (err); } /* doread */ static int dowrite (int from, int to, char *fname, int apflg) { FILE *fp; int lin, err; unsigned int lines; unsigned long bytes; char *str; LINE *lptr; err = 0; lines = bytes = 0; add_message ("\"%s\" ", fname); escape_path (fname); if ((fp = fopen (fname, (apflg ? "a" : "w"))) == NULL) { add_message (" can't be opened for writing!\n"); return (ERR); } lptr = getptr (from); for (lin = from; lin <= to; lin++) { str = lptr->l_buff; lines++; bytes += strlen (str) + 1; /* str + '\n' */ if (fputs (str, fp) == EOF) { add_message ("file write error\n"); err++; break; } fputc ('\n', fp); lptr = lptr->l_next; } add_message ("%u lines %lu bytes\n", lines, bytes); fclose (fp); return (err); } /* dowrite */ /* find.c */ static int find (regexp * pat, int dir) { int i, num; LINE *lin; num = P_CURLN; lin = P_CURPTR; for (i = 0; i < P_LASTLN; i++) { if (regexec (pat, gettxtl (lin))) return (num); if (dir) num = nextln (num), lin = getnextptr (lin); else num = prevln (num), lin = getprevptr (lin); } return (ERR); } /* getfn.c */ static char *getfn (int writeflg) { static char file[256]; char *cp; if (*inptr == NL) { P_NOFNAME = TRUE; strcpy (file, P_FNAME); } else { char *file2; P_NOFNAME = FALSE; Skip_White_Space; cp = file; while (*inptr && *inptr != NL && *inptr != SP && *inptr != HT) *cp++ = *inptr++; *cp = '\0'; if (strlen (file) == 0) { add_message ("bad file name\n"); return (NULL); } file2 = check_file_name (file, writeflg); if (!file2) return (NULL); strcpy (file, file2); } if (strlen (file) == 0) { add_message ("no file name\n"); return (NULL); } return (file); } /* getfn */ static int getnum (int first) { regexp *srchpat; int num; char c; Skip_White_Space; if (*inptr >= '0' && *inptr <= '9') { /* line number */ for (num = 0; *inptr >= '0' && *inptr <= '9'; ++inptr) { num = (num * 10) + (*inptr - '0'); } return num; } switch (c = *inptr) { case '.': inptr++; return (P_CURLN); case '$': inptr++; return (P_LASTLN); case '/': case '?': srchpat = optpat (); if (*inptr == c) inptr++; return (find (srchpat, c == '/' ? 1 : 0)); case '-': case '+': return (first ? P_CURLN : 1); case '\'': inptr++; if (*inptr < 'a' || *inptr > 'z') return (EOF); return P_MARK[*inptr++ - 'a']; default: return (first ? EOF : 1); /* unknown address */ } } /* getnum */ /* getone.c * Parse a number (or arithmetic expression) off the command line. */ #define FIRST 1 #define NOTFIRST 0 static int getone (void) { int c, i, num; if ((num = getnum (FIRST)) >= 0) { for (;;) { Skip_White_Space; if (*inptr != '+' && *inptr != '-') break; /* exit infinite loop */ c = *inptr++; if ((i = getnum (NOTFIRST)) < 0) return (i); if (c == '+') num += i; else num -= i; } } return (num > P_LASTLN ? ERR : num); } /* getone */ static int getlst (void) { int num; P_LINE2 = 0; for (P_NLINES = 0; (num = getone ()) >= 0;) { P_LINE1 = P_LINE2; P_LINE2 = num; P_NLINES++; if (*inptr != ',' && *inptr != ';') break; if (*inptr == ';') setCurLn (num); inptr++; } P_NLINES = min (P_NLINES, 2); if (P_NLINES == 0) P_LINE2 = P_CURLN; if (P_NLINES <= 1) P_LINE1 = P_LINE2; return ((num == ERR) ? num : P_NLINES); } /* getlst */ /* getptr.c */ static LINE *getptr (int num) { LINE *ptr; int j; if (2 * num > P_LASTLN && num <= P_LASTLN) { /* high line numbers */ ptr = P_LINE0.l_prev; for (j = P_LASTLN; j > num; j--) ptr = ptr->l_prev; } else { /* low line numbers */ ptr = &P_LINE0; for (j = 0; j < num; j++) ptr = ptr->l_next; } return (ptr); } /* getrhs.c */ static int getrhs (char *sub) { char delim = *inptr++; char *outmax = sub + MAXPAT; if (delim == NL || *inptr == NL) /* check for eol */ return (ERR); while (*inptr != delim && *inptr != NL) { if (sub > outmax) return ERR; if (*inptr == ESCAPE) { switch (*++inptr) { case 'r': *sub++ = '\r'; inptr++; break; case ESCAPE: *sub++ = ESCAPE; *sub++ = ESCAPE; inptr++; case 'n': *sub++ = '\n'; inptr++; break; case 'b': *sub++ = '\b'; inptr++; break; case '0':{ int i = 3; *sub = 0; do { if (*++inptr < '0' || *inptr > '7') break; *sub = (*sub << 3) | (*inptr - '0'); } while (--i != 0); sub++; } break; default: if (*inptr != delim) *sub++ = ESCAPE; *sub++ = *inptr; if (*inptr != NL) inptr++; } } else *sub++ = *inptr++; } *sub = '\0'; inptr++; /* skip over delimter */ Skip_White_Space; if (*inptr == 'g') { inptr++; return (1); } return (0); } /* ins.c */ static int ins (char *str) { char *cp; LINE *new, *nxt; int len; do { for (cp = str; *cp && *cp != NL; cp++); len = cp - str; /* cp now points to end of first or only line */ if ((new = (LINE *) xalloc (sizeof (LINE) + len)) == NULL) return (MEM_FAIL); /* no memory */ new->l_stat = 0; strncpy (new->l_buff, str, len); /* build new line */ new->l_buff[len] = EOS; nxt = getnextptr (P_CURPTR); /* get next line */ relink (P_CURPTR, new, new, nxt); /* add to linked list */ relink (new, nxt, P_CURPTR, new); P_LASTLN++; P_CURLN++; P_CURPTR = new; str = cp + 1; } while (*cp != EOS); return 1; } /* join.c */ static int join (int first, int last) { char buf[MAXLINE]; char *cp = buf, *str; LINE *lin; int num; if (first <= 0 || first > last || last > P_LASTLN) return (ERR); if (first == last) { setCurLn (first); return 0; } lin = getptr (first); for (num = first; num <= last; num++) { str = gettxtl (lin); while (*str) { if (cp >= buf + MAXLINE - 1) { add_message ("line too long\n"); return (ERR); } *cp++ = *str++; } lin = getnextptr (lin); } *cp = EOS; del (first, last); if (ins (buf) < 0) return MEM_FAIL; P_FCHANGED = TRUE; return 0; } /* move.c * Unlink the block of lines from P_LINE1 to P_LINE2, and relink them * after line "num". */ static int move (int num) { int range; LINE *before, *first, *last, *after; if (P_LINE1 <= num && num <= P_LINE2) return (ERR); range = P_LINE2 - P_LINE1 + 1; before = getptr (prevln (P_LINE1)); first = getptr (P_LINE1); last = getptr (P_LINE2); after = getptr (nextln (P_LINE2)); relink (before, after, before, after); P_LASTLN -= range; /* per AST's posted patch 2/2/88 */ if (num > P_LINE1) num -= range; before = getptr (num); after = getptr (nextln (num)); relink (before, first, last, after); relink (last, after, before, first); P_LASTLN += range; /* per AST's posted patch 2/2/88 */ setCurLn (num + range); return (1); } static int transfer (int num) { int mid, lin, ntrans; if (P_LINE1 <= 0 || P_LINE1 > P_LINE2) return (ERR); mid = num < P_LINE2 ? num : P_LINE2; setCurLn (num); ntrans = 0; for (lin = P_LINE1; lin <= mid; lin++) { if (ins (gettxt (lin)) < 0) return MEM_FAIL; ntrans++; } lin += ntrans; P_LINE2 += ntrans; for (; lin <= P_LINE2; lin += 2) { if (ins (gettxt (lin)) < 0) return MEM_FAIL; P_LINE2++; } return (1); } /* optpat.c */ static regexp *optpat (void) { char delim, str[MAXPAT], *cp; delim = *inptr++; if (delim == NL) return P_OLDPAT; cp = str; while (*inptr != delim && *inptr != NL && *inptr != EOS && cp < str + MAXPAT - 1) { if (*inptr == ESCAPE && inptr[1] != NL) *cp++ = *inptr++; *cp++ = *inptr++; } *cp = EOS; if (*str == EOS) return (P_OLDPAT); if (P_OLDPAT) free (P_OLDPAT); return P_OLDPAT = regcomp (str); } /* regerror.c */ void regerror (char *s) { add_message ("ed: %s\n", s); } static int set (void) { char word[16]; int i; if (*(++inptr) != 't') { if (*inptr != SP && *inptr != HT && *inptr != NL) return (ERR); } else inptr++; if ((*inptr == NL)) { add_message ("ed version %d.%d\n", version / 100, version % 100); add_message ("number %s, list %s\n", P_NFLG ? "ON" : "OFF", P_LFLG ? "ON" : "OFF"); return (0); } Skip_White_Space; for (i = 0; *inptr != SP && *inptr != HT && *inptr != NL;) word[i++] = *inptr++; word[i] = EOS; #if 0 for (t = tbl; t->t_str; t++) { if (strcmp (word, t->t_str) == 0) { *t->t_ptr = t->t_val; return (0); } } #endif return SET_FAIL; } #ifndef relink void relink (a, x, y, b) LINE *a, *x, *y, *b; { x->l_prev = a; y->l_next = b; } #endif static void set_ed_buf (void) { relink (&P_LINE0, &P_LINE0, &P_LINE0, &P_LINE0); P_CURLN = P_LASTLN = 0; P_CURPTR = &P_LINE0; } /* subst.c */ static int subst (regexp * pat, char *sub, int gflg, int pflag) { int nchngd; char *txtptr; char *new, buf[MAXLINE]; int still_running = 1; LINE *lastline = getptr (P_LINE2); if (P_LINE1 <= 0) return (SUB_FAIL); nchngd = 0; /* reset count of lines changed */ for (setCurLn (prevln (P_LINE1)); still_running;) { nextCurLn (); new = buf; if (P_CURPTR == lastline) still_running = 0; if (regexec (pat, txtptr = gettxtl (P_CURPTR))) { do { /* Copy leading text */ int diff = pat->startp[0] - txtptr; strncpy (new, txtptr, diff); new += diff; /* Do substitution */ new = regsub (pat, sub, new); txtptr = pat->endp[0]; } while (gflg && !pat->reganch && regexec (pat, txtptr)); /* Copy trailing chars */ while (*txtptr) { *new = *txtptr; new++; txtptr++; } if (new >= buf + MAXLINE) return (SUB_FAIL); *new = EOS; del (P_CURLN, P_CURLN); if (ins (buf) < 0) return MEM_FAIL; nchngd++; if (pflag) doprnt (P_CURLN, P_CURLN); } } return ((nchngd == 0 && !gflg) ? SUB_FAIL : nchngd); } /* docmd.c * Perform the command specified in the input buffer, as pointed to * by inptr. Actually, this finds the command letter first. */ static int docmd (int glob) { static char rhs[MAXPAT]; regexp *subpat; int c, err, line3; int apflg, pflag, gflag; int nchng; char *fptr; pflag = FALSE; Skip_White_Space; c = *inptr++; switch (c) { case NL: if (P_NLINES == 0 && (P_LINE2 = nextln (P_CURLN)) == 0) return (ERR); setCurLn (P_LINE2); return (1); case '=': add_message ("%d\n", P_LINE2); break; case 'a': if (*inptr != NL || P_NLINES > 1) return (ERR); if (append (P_LINE1, glob) < 0) return (ERR); P_FCHANGED = TRUE; break; case 'c': if (*inptr != NL) return (ERR); if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if (del (P_LINE1, P_LINE2) < 0) return (ERR); if (append (P_CURLN, glob) < 0) return (ERR); P_FCHANGED = TRUE; break; case 'd': if (*inptr != NL) return (ERR); if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if (del (P_LINE1, P_LINE2) < 0) return (ERR); if (nextln (P_CURLN) != 0) nextCurLn (); P_FCHANGED = TRUE; break; case 'e': if (P_NLINES > 0) return (ERR); if (P_FCHANGED) return CHANGED; /*FALL THROUGH */ case 'E': if (P_NLINES > 0) return (ERR); if (*inptr != ' ' && *inptr != HT && *inptr != NL) return (ERR); if ((fptr = getfn (1)) == NULL) return (ERR); clrbuf (); (void) doread (0, fptr); strcpy (P_FNAME, fptr); P_FCHANGED = FALSE; break; case 'f': if (P_NLINES > 0) return (ERR); if (*inptr != ' ' && *inptr != HT && *inptr != NL) return (ERR); if ((fptr = getfn (1)) == NULL) return (ERR); if (P_NOFNAME) add_message ("%s\n", P_FNAME); else strcpy (P_FNAME, fptr); break; case 'i': if (*inptr != NL || P_NLINES > 1) return (ERR); if (append (prevln (P_LINE1), glob) < 0) return (ERR); P_FCHANGED = TRUE; break; case 'j': if (*inptr != NL || deflt (P_CURLN, P_CURLN + 1) < 0) return (ERR); if (join (P_LINE1, P_LINE2) < 0) return (ERR); break; case 'k': Skip_White_Space; if (*inptr < 'a' || *inptr > 'z') return ERR; c = *inptr++; if (*inptr != ' ' && *inptr != HT && *inptr != NL) return (ERR); P_MARK[c - 'a'] = P_LINE1; break; case 'l': if (*inptr != NL) return (ERR); if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if (dolst (P_LINE1, P_LINE2) < 0) return (ERR); break; case 'm': if ((line3 = getone ()) < 0) return (ERR); if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if (move (line3) < 0) return (ERR); P_FCHANGED = TRUE; break; case 'P': case 'p': if (*inptr != NL) return (ERR); if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if (doprnt (P_LINE1, P_LINE2) < 0) return (ERR); break; case 'q': if (P_FCHANGED) return CHANGED; /*FALL THROUGH */ case 'Q': clrbuf (); if (*inptr == NL && P_NLINES == 0 && !glob) return (EOF); else return (ERR); case 'r': if (P_NLINES > 1) return (ERR); if (P_NLINES == 0) /* The original code tested */ P_LINE2 = P_LASTLN; /* if(P_NLINES = 0) */ /* which looks wrong. RAM */ if (*inptr != ' ' && *inptr != HT && *inptr != NL) return (ERR); if ((fptr = getfn (0)) == NULL) return (ERR); if ((err = doread (P_LINE2, fptr)) < 0) return (err); P_FCHANGED = TRUE; break; case 's': if (*inptr == 'e') return (set ()); Skip_White_Space; if ((subpat = optpat ()) == NULL) return (ERR); if ((gflag = getrhs (rhs)) < 0) return (ERR); if (*inptr == 'p') pflag++; if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if ((nchng = subst (subpat, rhs, gflag, pflag)) < 0) return (ERR); if (nchng) P_FCHANGED = TRUE; break; case 't': if ((line3 = getone ()) < 0) return (ERR); if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); if (transfer (line3) < 0) return (ERR); P_FCHANGED = TRUE; break; case 'W': case 'w': apflg = (c == 'W'); if (*inptr != ' ' && *inptr != HT && *inptr != NL) return (ERR); if ((fptr = getfn (1)) == NULL) return (ERR); if (deflt (1, P_LASTLN) < 0) return (ERR); if (dowrite (P_LINE1, P_LINE2, fptr, apflg) < 0) return (ERR); P_FCHANGED = FALSE; break; case 'x': if (*inptr == NL && P_NLINES == 0 && !glob) { if ((fptr = getfn (1)) == NULL) return (ERR); if (dowrite (1, P_LASTLN, fptr, 0) >= 0) return (EOF); } return (ERR); case 'z': if (deflt (P_CURLN, P_CURLN) < 0) return (ERR); switch (*inptr) { case '-': if (doprnt (P_LINE1 - 21, P_LINE1) < 0) return (ERR); break; case '.': if (doprnt (P_LINE1 - 11, P_LINE1 + 10) < 0) return (ERR); break; case '+': case '\n': if (doprnt (P_LINE1, P_LINE1 + 21) < 0) return (ERR); break; } break; default: return (ERR); } return (0); } /* docmd */ /* doglob.c */ static int doglob (void) { int lin, stat; char *cmd; LINE *ptr; cmd = inptr; for (;;) { ptr = getptr (1); for (lin = 1; lin <= P_LASTLN; lin++) { if (ptr->l_stat & LGLOB) break; ptr = getnextptr (ptr); } if (lin > P_LASTLN) break; ptr->l_stat &= ~LGLOB; P_CURLN = lin; P_CURPTR = ptr; inptr = cmd; if ((stat = getlst ()) < 0) return (stat); if ((stat = docmd (1)) < 0) return (stat); } return (P_CURLN); } /* doglob */ void ed_start (char *file_arg) { if (command_giver->ed_buffer) error ("Tried to start an ed session, when already active.\n"); if (command_giver != current_object) error ("Illegal start of ed.\n"); command_giver->ed_buffer = (struct ed_buffer *) xalloc (sizeof (struct ed_buffer)); memset ((char *) command_giver->ed_buffer, '\0', sizeof (struct ed_buffer)); command_giver->ed_buffer->truncflg = 1; command_giver->ed_buffer->eightbit = 1; command_giver->ed_buffer->CurPtr = &command_giver->ed_buffer->Line0; set_ed_buf (); if (file_arg && doread (0, file_arg) == 0) { setCurLn (1); strcpy (P_FNAME, file_arg); set_prompt (":"); return; } set_prompt (":"); } void ed_cmd (char *str) { int stat =0; if (P_APPENDING) { more_append (str); return; } strcat (str, "\n"); strcpy (inlin, str); inptr = inlin; if (getlst () >= 0) if ((stat = ckglob ()) != 0) { if (stat >= 0 && (stat = doglob ()) >= 0) { setCurLn (stat); return; } } else { if ((stat = docmd (0)) >= 0) { if (stat == 1) doprnt (P_CURLN, P_CURLN); return; } } switch (stat) { case EOF: free (command_giver->ed_buffer); command_giver->ed_buffer = 0; add_message ("Exit from ed.\n"); set_prompt ("> "); return; case FATAL: free (command_giver->ed_buffer); command_giver->ed_buffer = 0; add_message ("FATAL ERROR\n"); set_prompt ("> "); return; case CHANGED: add_message ("File has been changed.\n"); break; case SET_FAIL: add_message ("`set' command failed.\n"); break; case SUB_FAIL: add_message ("string substitution failed.\n"); break; case MEM_FAIL: add_message ("Out of memory: text may have been lost.\n"); break; default: add_message ("Unrecognized or failed command.\n"); /* Unrecognized or failed command (this */ /* is SOOOO much better than "?" :-) */ } }