/* ECCE -- Edinburgh Compatible Context Editor > adfs::4.$.GToal.Ecce.c.ecce Modified for Mouses, March 1978 K.Adam & R.Thonnes (April 1977) based on that for DEC PDP-9/15 by H.Dewar (June 1970) Hacked by Alan Culloch for VAX dynamic subsystem, June 1982 Hacked also by NJR into `C`, so that he can avoid using the ghastly Newcastle Screen Editor under Unix Altered by NJR: V/Text/ now not case-sensitive Altered by NJR: Stops with "No Changes" if file not altered, and OutFile = InFile Altered by NJR: Accepts '$' in commands, same meaning as '%S' */ static int Nick; #include #define BufferSize 0x100000 /* Characters */ #define MIn 1 /* Main input stream */ #define MOut 1 /* Main output stream */ #define SIn 2 /* Secondary input stream */ #define NL '\n' static FILE *InFile [3], *TempFile, *OutFile; /* static char *TempFName = "/tmp/e.XXXXXX"; */ static char *TempFName = "&.eccetmp"; /* ACORN */ /* static char *NullName = "/dev/null"; */ static char *NullName = "NULL:"; /* ACORN */ char *malloc(); /* Allocate store */ /* char *mktemp(); */ /* ACORN */ /* Generate unique filename */ extern exit(int rc); /* ACORN */ extern int remove(const char *fname); /* ACORN */ static int rcode; /* ACORN*/ #define putprompt(a) printf(a) #define Stop (-5000) /* Loop stop value */ static int In = MIn; /* Current input stream */ static int Mon = 1; /* Monitor indicator */ static int Print1 = 0, Print2 = 0; /* Print indicators */ static int Sym = NL; static int Changed; static char Map [128]; /* Upper/Lower case map */ int I, J, *JP, K, PP1; #define SExtra 122 /* Extra buffer for SIn */ static int *MainFP; /* == FP or MFP (for SIn) */ /* Bullet-proof writeout, since UNIX file error recovery is STUPID */ #define PrintSymbol(C, F) if (putc(C, F) == EOF) AbortOutput(F) AbortOutput(File) FILE *File; {printf("Error writing %s file; Aborting\n", (File == TempFile) ? "temporary" : "output"); exit(1); } /* The command input phase reads and checks a complete command line, forming up an internal representation as a sequence of fixed-length command units. The basic components of a command unit are: */ static int Code; /* Command code symbol */ static int *Text; /* Text pointer (0 if not relevant) */ static int Num; /* Repetition number */ /* Command units are stored sequentially from the start of array C (CBASE): text strings are stored backwards from the end of the same array (TBASE). Parentheses and commas occurring in commands are also transformed to command units. The TEXT component of open bracket and comma units points to the corresponding close bracket: the TEXT component of close bracket units points to the first enclosed unit. */ #define CellSize 560 static int C [CellSize]; static int *CBase = (int *) C; static int *TBase = (int *) &C [CellSize-1]; /* Const (Addr) */ static int *CI; /* Command Index (Addr) */ static int *TI; /* Text Index (Addr) */ static int CMax = 0; /* Command Max (Addr) */ /* Macro definitions (optional feature) */ static int Stored [193]; /* Defs of X, Y, Z */ static int Pos1 = 0, Pos2 = 0, Pos3 = 0; /* Def pointers */ static char *A; /* Text buffer */ static int Top = 1; /* Top of buff (const) */ static int Bot = BufferSize - 121; /* Bottom of buff (const) */ static int LBeg, /* Line start */ PP, /* Previous pointer */ FP, /* File pointer */ LEnd, /* Line end */ FEnd, /* End of file in buff */ MS = 0, /* Match start */ ML = 0, /* Match limit */ Case = 95; /* %U Default */ /* The (part of the) file held in the array A is stored as a top half (from TOP inclusive to PP exclusive) and a bottom half (from FP inclusive to FEND exclusive). The gap corresponds to the position of the file-pointer. Text location operations do not therefore simply involve moving a pointer: the text passed over is moved across the gap. The additional processing overhead is relatively small and is outweighed by the simplification of insertion and deletion operations that is achieved by this technique. Illustration of pointer significance: [NL] O N E NL T W . . . O NL N E X T NL . . NL L A S T NL [NL] ! ! ! ! ! ! T L P F L F O B P P E E P E N N G D D */ static int Type, Chain; static int Pend = 0; /* Symbol classification codes are 16X + Y, where Y is the basic code and X is used to sub-classify where necessary (eg percent command letters). */ static int SymType [96] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 3, 3, 10, 2, 3, 3, 11, 9, 64, 3, 12, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3, 64, 3, 18, 10, 26, 5, 8, 52, 10, 2, 6, 10, 10, 122, 56, 2, 13, 10, 50, 10, 22, 5, 117, 6, 2, 32, 32, 32, 3, 10, 3, 3, 3}; ReadSym() {if (Pend) {Sym = Pend; Pend = 0; } else {while (Pos3) /* Macro expansion */ {Sym = Stored [Pos3++]; if (Sym) return; Pos3 = Pos2; Pos2 = Pos1; Pos1 = 0; } if ((Sym = getchar()) == EOF) {Sym = '%'; Pend = 'C'; } } } ReadItem() /* Read command item (symbol or number), skipping leading spaces, standardising case and detecting macro letters. Set up the type (ie SYMTYPE) of the item in TYPE. For a 'numeric' item, assemble sequence of digits as necessary and return value in NUM. Note that NUM is not altered for a non-numeric item. */ {for (;;) {Type = 1; /* Default for NL */ do ReadSym(); while (Sym == ' '); if (Sym < ' ') return; /* Treat any control as NL */ if (Sym >= 96) Sym -= 32; /* Ensure upper case */ Type = SymType [Sym]; if (Type & 15) return; /* Return unless numeric or X, Y, Z */ if (Type != 32) break; /* X, Y, Z (macro letters) */ Pos1 = Pos2; Pos2 = Pos3; Pos3 = ((Sym - 'X') << 6) + 1; } if (!Type) /* Decimal digit */ {Num = Sym - '0'; for (;;) {ReadSym(); if (!('0' <= Sym && Sym <= '9')) break; Num = ((Num << 2) + Num << 1) - '0' + Sym; /* ie. Num*10*Sym-'0' */ } Pend = Sym; } else {Type = 0; Num = 0; if (Sym == '*') return; Num = Stop + 1; if (Sym == '?') return; Num = Stop; /* '!' */ } } UnChain() /* Use temporary links to locate last unmatched open bracket (if any), fixing up any comma units en route. */ {for (;;) {Text = Chain; if (!Text) return; Chain = Text [1]; Text [1] = (int) CI; if (*Text == '(') break; } } Stack(V) int V; {*CI = V; CI++; } ReadLine() {static int EOT [3]; int K; /* Read line from file (if bottom part of buffer empty) */ if (FP != FEnd) /* Line available */ {LEnd = FP; while (A [LEnd] != NL) LEnd++; } else {if (EOT [In]) goto EoF; FP = Bot - 121; do {if (FP != Bot) {if ((K = getc(InFile [In])) == EOF) goto EoF; } else K = NL; A [FP++] = K & 127; } while (K != NL); FEnd = FP; LEnd = FEnd - 1; FP = Bot - 121; goto L1; EoF: FP = Bot; LEnd = FP; FEnd = LEnd; A [FP] = NL; EOT [In] = -1; L1: MS = 0; Print1 = 0; Print2 = 0; } } Break() {A [PP++] = NL; LBeg = PP; } LeftStar() {for (;;) {if (PP == LBeg) return; FP--; PP--; A [FP] = A [PP]; } } RightStar() {for (;;) {if (FP == LEnd) return; A [PP] = A [FP]; PP++; FP++; } } MakeSpace() /* Check that sufficient breathing space remains in the internal file buffer. If not, expel part of the file */ {int K, P1, P2; if (*MainFP - PP > 240) return; P1 = Top; P2 = P1 + LBeg >> 1; /* About half */ if (Code == 'C' || Code == 'A') P2 = LBeg; /* but all if closing */ do {K = A [P1++]; PrintSymbol(K, TempFile); } while (!(K == NL && P1 >= P2)); LBeg = Top + LBeg - P1; P2 = PP; PP = Top; while (P1 != P2) /* Copy up remainder */ {A [PP] = A [P1]; PP++; P1++; } } Refresh() {FP++; MakeSpace(); ReadLine(); } PrintLine() {int P; Print1 = LEnd; Print2 = FP + PP; P = LBeg; for (;;) {char ch; if (P == PP) {if (P != LBeg && Num == 0) putchar('|'); P = FP; } if (P == LEnd) break; ch = A[P++]; if ((ch<32) | (ch>127)) { printf("<%02x>", ch); } else { putchar(ch); } } if (P == FEnd) printf("**End**"); putchar(NL); } Matched() /* Used for Find, Uncover, Delete, Traverse */ {int I, J, *JP, K, L, T1, FP1, Lim; Lim = CI [-3] >> 7; /* Search limit */ T1 = Map [*Text]; /* Extract first char for speed */ for (;;) {PP1 = PP; FP1 = FP; while (FP != LEnd) {K = A [FP]; if (Map [K] == T1 && (FP != MS || Code == 'D' || Code == 'T')) {I = FP; JP = Text; do {I++; JP--; L = *JP; if (L == 0) /* Complete string matched */ {MS = FP; ML = I; if (Code == 'Q') PrintLine(); return(1); } } while (Map [L] == Map [A [I]]); } A [PP] = K; PP++; FP++; } Lim--; if (Lim == 0 || FP == FEnd) break; if (Code == 'Q') {J = Num; Num = 1; PrintLine(); Num = J; } if (Code != 'U') Break(); else PP = PP1; Refresh(); } PP = PP1; FP = FP1; return(0); } SwitchInputs() {static int MFP, MLEnd, MEnd, SFP, SEnd; if (In == MIn) {LeftStar(); In = SIn; MFP = FP; MLEnd = LEnd; MEnd = FEnd; MainFP = &MFP; Bot += SExtra; FP = SFP; FEnd = SEnd; ReadLine(); } else {PP = LBeg; In = MIn; Bot -= SExtra; SFP = FP; SEnd = FEnd; FP = MFP; LEnd = MLEnd; FEnd = MEnd; MainFP = &FP; } } EqString(Str1, Str2) char *Str1, *Str2; {do {if (*Str1 != *Str2) return(0); } while (*(Str1++) != '\0' && *(Str2++) != '\0'); return(1); } char *ReadName() {static char S [128]; char *P; P = S; while ((*P = getchar()) != NL) P++; *P = '\0'; return(S); } main(ArgC, ArgV) int ArgC; char *ArgV []; {int J; FILE *fopen(); char *InFName, *OutFName, *SInFName; /* Open files */ if (ArgC < 2) {fprintf(stderr, "Usage: ecce [InFile] [OutFile] [SInFile]\n"); exit(1); } InFName = ArgV [1]; /* Primary input */ if (EqString(InFName, "-n")) InFName = NullName; if (ArgC < 3) OutFName = InFName; /* Editing to same file */ else {OutFName = ArgV [2]; if (EqString(OutFName, "-n")) OutFName = NullName; else if (EqString(OutFName, "-w")) /* e In -w SIn */ OutFName = InFName; } if (ArgC < 4) SInFName = NullName; /* Default %S */ else {SInFName = ArgV [3]; if (EqString(SInFName, "-n")) SInFName = NullName; } if (InFName != NullName) { if ((InFile [1] = fopen(InFName, "r")) == NULL) {fprintf(stderr, "Cannot access \"%s\"\n", InFName); exit(1); } } /* TempFName = mktemp(TempFName); */ /* Generate unique name "E.nnnnnn" */ /* ACORN */ /* we have a fixed temp file name */ if ((TempFile = fopen(TempFName, "w")) == NULL) {fprintf(stderr, "Cannot create \"%s\"\n", TempFName); exit(1); } if ((InFile [2] = fopen(SInFName, "r")) == NULL) {fprintf(stderr, "Cannot access \"%s\"\n", SInFName); exit(1); } if ((A = malloc(BufferSize + 1)) == NULL) {fprintf(stderr, "Cannot create text buffer\n"); exit(1); } putprompt(""); Changed = !EqString(InFName, OutFName); /* Always write of if diff. */ printf("Editing \"%s\"", InFName); if (ArgC > 3) printf(", \"%s\"", SInFName); putchar(NL); for (J = 0; J < 128; J++) Map [J] = J; for (J = 'A'; J <= 'Z'; J++) Map [J + 32] = J; /* Start with Lower=Upper (%U) */ Stored [(0 << 6) + 1] = 0; Stored [(1 << 6) + 1] = 0; Stored [(2 << 6) + 1] = 0; PP = Top - 1; Break(); /* For bouncing off */ MainFP = &FP; ReadLine(); goto Monitor; ReadCommand: CI = CBase; TI = TBase; Chain = 0; if (In != SIn) putprompt(">"); else putprompt(">>"); ReadItem(); if (Type == 1 && Sym == NL) {I = Num; Num = 0; PrintLine(); Num = I; } if (Type == 1) goto ReadCommand; /* Ignore blank lines */ if (Type == 0 && CMax != 0) {((int *) CMax) [2] = Num; ReadItem(); if (Type != 1) goto Er2; goto Execute; } if (Sym != '%') goto NextUnit; ReadSym(); if (Sym >= 96) Sym -= 32; Code = Sym; if (Code < 'A') goto Er5; ReadItem(); Nick = SymType [Code] >> 4; Switch: switch (Nick) /* Switch on ext type */ {case 1: goto T1; case 2: /* %X, %Y, %Z (macro definition) */ if (Sym != '=') goto Er1; I = (Code - 'X' << 6) + 1; for (;;) {ReadSym(); if (Sym == NL) break; Stored [I] = Sym; if ((I & 63) == 0) goto Er6; I++; } Stored [I] = 0; goto ReadCommand; case 3: /* %M, %F, %Q (monitor control) */ Mon = 'M' - Code; goto ReadCommand; case 7: Case = -32; if (Code == 'L') Case = 0; for (J = 'a'; J <= 'z'; J++) Map [J] = J + Case; goto ReadCommand; NextUnit: /* First item already found */ I = Type & 15; if (I < 4) goto Er2; Code = Sym; Text = 0; Num = 1; /* Default values */ ReadItem(); Nick = I; goto Switch; case 13: /* Observe */ Code = 'Q'; case 4: /* + Find */ if (Type != 0) Num = 0; case 5: /* + Del, Trav, Uncover */ Code = (Num << 7) + Code; /* Pack limit number */ Num = 1; /* Reset default repetition */ if (Type == 0) ReadItem(); case 6: /* + Insert, Subst, Verify */ if (Type != 3) goto Er4; /* Not legit quote symbol */ Text = TI; I = Sym; for (;;) {ReadSym(); if (Sym == I) break; if (Sym == NL) {Pend = Sym; break; } if (TI <= CI) goto Er6; *TI = Sym; TI--; } if ((Sym == NL || TI == Text) && Code != 'I' && Code != 'S') goto Er4; *TI = 0; TI--; /* Text end marker */ goto RI; case 8: /* Move, Erase */ if (Sym != '-') goto NQ; Code += 10; /* 'E' -> 'O' 'M' -> 'W' */ RI: ReadItem(); goto RN; case 9: /* Close bracket */ UnChain(); if (Text == 0) goto Er3; Text [2] = Num; Text += 3; case 10: /* + Get, Kill etc */ NQ: if (Type == 3) goto Er1; RN: if (Type == 0) ReadItem(); goto Put; case 12: /* Comma */ if (Type == 1) ReadItem(); /* Ignore following NL */ case 11: /* Open bracket */ Text = Chain; Chain = (int) CI; Num = 0; Put: Stack(Code); Stack(Text); Stack(Num); if (CI + 2 >= TI) goto Er6; if (Type != 1) goto NextUnit; UnChain(); if (Text != 0) goto Er3; CMax = (int) CI; Stack(')'); Stack((int) CBase); Stack(1); /* Extra ')' */ Stack(0); goto Execute; /* Command input error reports */ Er1: putchar(' '); putchar(Code); Er2: Code = Sym; goto Er5; Er3: printf(" ()"); goto Er7; Er4: printf(" Text for"); case 0: Er5: putchar(' '); putchar(Code & 127); goto Er7; Er6: printf(" Size"); Er7: putchar('?'); putchar(NL); if (CI != CBase) CMax = 0; while (Sym != NL) ReadSym(); goto ReadCommand; Execute: CI = CBase; Get: Code = *CI & 127; if (Code == 0) goto Monitor; Text = CI [1]; Num = CI [2]; CI += 3; Rep: Num--; } switch (Code) {case '\\': /* Invert */ No: if (Num < 0) goto Get; if (*CI == '\\') {CI += 3; goto Get; } Skp: I = *CI; if (I == '(') CI = CI [1]; CI += 3; if (I == ',' || I == ')') {Num = CI [-1] - 1; goto No; } if (I != 0) goto Skp; /* Execution error */ printf("Failure: "); if (Code == 'O' || Code == 'W') {putchar(Code - 10); Code = '-'; } putchar(Code); if (Text != 0) {putchar('/'); while (*Text != 0) {putchar(*Text); Text--; } putchar('/'); } putchar(NL); Print1 = 0; Monitor: if (Sym != NL || Mon < 0) goto ReadCommand; if (Print1 == LEnd && (Mon == 0 || Print2 == PP + FP)) goto ReadCommand; Num = 0; PrintLine(); goto ReadCommand; OK: if (!(Num == 0) ||(Num == Stop)) goto Rep; goto Get; /* Individual commands */ case '(': /* Open bracket */ /* Reset repetition number on close bracket */ Text [2] = Num + 1; goto Get; case ')': /* Close bracket */ if (Num == 0 || Num == Stop) goto Get; CI [-1] = Num; /* Update repetition number */ case ',': /* + Comma */ CI = Text; goto Get; case '$': /* Switch inputs */ SwitchInputs(); Changed = 1; goto OK; case 'C': /* Case shift */ J = A [FP]; if ('A' <= (J & 95) && (J & 95) <= 'Z') {J = J ^ 32; Changed = 1; } A [FP] = J; case 'R': /* + Right shift */ if (FP == LEnd) goto No; A [PP] = A [FP]; PP++; FP++; goto OK; case 'L': /* Left shift */ if (PP == LBeg || In == SIn) goto No; FP--; PP--; A [FP] = A [PP]; MS = 0; goto OK; case 'E': /* Erase */ if (FP == LEnd) goto No; FP++; Changed = 1; goto OK; case 'O': /* Erase back */ if (PP == LBeg) goto No; PP--; Changed = 1; goto OK; case 'V': /* Verify */ I = FP - 1; JP = Text + 1; V1: I++; JP -= 1; K = *JP; if (Map [A [I]] == Map [K]) goto V1; if (K != 0) goto No; MS = FP; ML = I; goto OK; case 'Q': /* Observe */ case 'F': /* + Find */ if (!Matched()) goto No; goto OK; case 'U': /* Uncover */ if (!Matched()) goto No; PP = PP1; Changed = 1; goto OK; case 'D': /* Delete */ if (!Matched()) goto No; FP = ML; Changed = 1; goto OK; case 'T': /* Traverse */ if (!Matched()) goto No; FP = ML; I = MS; for (;;) {if (I == FP) goto OK; A [PP] = A [I]; PP++; I++; } case 'S': /* Substitute */ if (FP != MS) goto No; FP = ML; Changed = 1; case 'I': /* + Insert */ MakeSpace(); if (PP - LBeg + LEnd - FP > 80) goto No; JP = Text; Changed = 1; for (;;) {if (*JP == 0) goto OK; A [PP] = *JP; PP++; JP--; } case 'G': /* Get (line from TT) */ MakeSpace(); I = getchar(); if (I == ':') {Sym = getchar(); ungetc(Sym, stdin); if (Sym == NL) getchar(); goto No; } LeftStar(); while (I != NL) {A [PP++] = I; I = getchar(); } case 'B': /* break (insert newline) */ Break(); Changed = 1; goto OK; case 'P': /* Print */ PrintLine(); if (Num == 0) goto Get; case 'M': /* Move */ RightStar(); if (FP == FEnd) goto No; Break(); M1: Refresh(); goto OK; case 'K': /* Kill line */ PP = LBeg; FP = LEnd; Changed = 1; K1: if (FP == FEnd) goto No; goto M1; case 'J': /* Join (delete newline) */ RightStar(); if (PP - LBeg > 80) goto No; Changed = 1; goto K1; case 'W': /* Move back */ if (In == SIn) goto No; MakeSpace(); if (LBeg == Top) goto No; LEnd = FP - PP + LBeg - 1; for (;;) {K = A [PP - 1]; if (K == NL && PP != LBeg) break; FP--; PP--; A [FP] = K; } LBeg = PP; MS = 0; goto OK; T1: /* %C, %S, %A */ if (Type != 1) goto Er2; if (Code == 'S') {SwitchInputs(); Changed = 1; goto Monitor; } /* Close file (+EOF on command stream) */ if (!Changed) {printf("No Changes\n"); fclose(TempFile); /* ACORN */ /*unlink(TempFName);*/ rcode = remove(TempFName); return; } if (In == SIn) SwitchInputs(); for (;;) {RightStar(); if (FP == FEnd) break; Break(); Refresh(); } fclose(InFile [1]); fclose(InFile [2]); if (Code == 'A') /* Write everything to "E.nnnnnn" */ {while(Top != PP) PrintSymbol(A [Top++], TempFile); fclose(TempFile); fprintf(stderr, "Contents left in \"%s\"\n", TempFName); } else /* Everything goes to OutFile */ {int C; fclose(TempFile); if ((TempFile = fopen(TempFName, "r")) == NULL) /* Re-read */ {fprintf(stderr, "Cannot re-open \"%s\"\n", TempFName); exit(1); } while ((OutFile = fopen(OutFName, "w")) == NULL) {fprintf(stderr, "Cannot create \"%s\"\nFile: ", OutFName); OutFName = ReadName(); } while ((C = getc(TempFile)) != EOF) /* Copy TempFile -> Out */ PrintSymbol(C, OutFile); while (Top != PP) /* Copy Buff -> Out */ PrintSymbol(A [Top++], OutFile); fclose(OutFile); /* ACORN */ /*unlink(TempFName);*/ /* Delete temporary file */ fclose(TempFile); rcode = remove(TempFName); printf("Edit \"%s\" completed.\n", OutFName); } } }