tjt@glimmer.UUCP (Tim Thompson) (06/28/87)
# Enclosed is (part 1 of) the source for a mini-VI which I wrote when I # first got the ST, to make things easier for my own development. Although # it is somewhat minimal in capabilities, it does have a fairly good # implementation of the 'u' (undo) and '.' (repeat) commands. I have # much more interesting things to work on these days, so I am unlikely # to provide any support (hence my posting of the source). It has worked # well for 6 months, and since no alternatives have appeared on the net # (other than VIX, which is too different for me), I figure it might be # useful. See the README below for more info. # ...Tim Thompson...ihnp4!twitch!tjt... # #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README # Makefile # cmdline.c # edit.c # help.c # hexchars.c # linefunc.c # This archive created: Sun Jun 28 13:18:36 1987 export PATH; PATH=/bin:$PATH if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \SHAR_EOF > 'README' STEVIE - ST Editor for VI Enthusiasts ...Tim Thompson...twitch!tjt... ===================================== The STEVIE editor is a fairly accurate (in the small subset of commands it chooses to implement) version of the VI editor. It was written at a time when alternatives (ie. VIX) were not available. To VI users, STEVIE should provide a much more familiar environment than VIX, but has nowhere near the capabilities (or speed), in general. Features/Limitations ==================== - The "." (repeat last operation) and "u" (undo last insertion or deletion) commands are available, and will repeat/undo almost anything. - The commands implemented in STEVIE happen to be the subset of VI commands used by its author (suprise!). A fair number of additional (positioning and substitution) commands should be easy to add. Regular expressions and global substitutions are not implemented. - STEVIE is limited to editing files < 64K. The source code has no built-in limitations, but the file is kept in memory as a single contiguous stream of bytes. This implies various limitations, among them the fact that operations such as insert and delete will slow down (by a LOT, in the current implementation) as the file being edited grows larger. The slowness could be eliminated by some reworking, which I'm unmotiviated to do, since I rarely edit files larger than 16K. Scrolling in general is also unnecessarily slow. Reading/writing files is also slow. - Known glitches/annoyances: <control-d> scrolling works strangely. Long (>80 chars) lines are usually handled okay, but strange (positional) things sometime happen at the bottom of the screen. Backspacing at the beginning of a line, in insert mode, isn't handled correctly. Undoing insertions at the end of a line are sometimes off by a character. The :r command does not work very well. A final line that does not end in '\n' is troublesome. - There is some support in the editor for displaying and editing binary files. Giving a '-b' option tells the editor that you are intentionally editing a binary file. Unprintable characters are displayed in a [xxx] format, where xxx is either octal or hex (if you use the -x option). The binary editing support is mostly an untested hack (although it was one of the original goals of the editor, so the support for it was not unplanned). If it works, fine; I'm not claiming that it does. - Although STEVIE has been used with no problems for several months, it no doubt contains bugs that will surface when used by others. Cautious initial use is recommended. Commands ======== Some commands can make use of a numeric prefix, indicated in the summary below as [#]. Cursor movement commands ======================== control-l Redraw screen control-d Cursor down 1/2 screen control-u Cursor up 1/2 screen control-f Cursor forward 1 screen control-b Cursor back 1 screen control-g Give info on file h Cursor left 1 char j Cursor down 1 char k Cursor up 1 char l Cursor right 1 char $ Cursor to end of line ^ -or- 0 Cursor to beginning of line b Cursor back 1 word w Cursor forward 1 word\n\ [#]G Goto line # (or last line if no #) Modification commands ===================== x Delete 1 char dw Delete 1 word D Delete rest of line [#]dd Delete 1 (or #) lines C Change rest of line cw Change word cc Change line r Replace single character [#]yy Yank 1 (or #) lines p Insert last yanked or deleted line(s) P below (p) or above (P) current line J Join current and next line [#]<< Shift line left 1 (or #) tabs [#]>> Shift line right 1 (or #) tabs i Enter Insert mode (<ESC> to exit) a Append (<ESC> to exit) \n\ o Open line (<ESC> to exit)\n\ Miscellaneous ============= . Repeat last operation. The works for most (but not all) commands, including insertions. u Undo last insert or delete. This works for most operations, although there are undoubtedly some for which it will not. /str/ Search for 'str' (NO REGULAR EXPRESSIONS) ?str? Search backward for 'str' n Repeat previous search :.= Print current line number :$= Print number of lines in file H Help - a quick summary of commands File manipulation ================= :w Write file :wq Write and quit :e {file} Edit a new file :e! Re-read current file :f Print file into (current line and total # of lines) :f {file} Change current file name :q Quit :q! Quit (no save) SHAR_EOF fi # end of overwriting check if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \SHAR_EOF > 'Makefile' CFLAGS = -DTCAP # or UNIXPC or ATARI OBJ = main.o edit.o linefunc.o normal.o \ cmdline.o hexchars.o misccmds.o help.o default : stevie stevie : $(OBJ) tcapwind.o cc $(OBJ) tcapwind.o -lcurses -ltermcap -o stevie s4vi : $(OBJ) unixpcwind.o cc $(OBJ) unixpcwind.o -ltam -ltermcap -o s4vi tcapwind.o : window.c cc -c $(CFLAGS) window.c mv window.o tcapwind.o unixpcwind.o : window.c cc -c $(CFLAGS) window.c mv window.o unixpcwind.o clean : rm -f *.o stevie s4vi SHAR_EOF fi # end of overwriting check if test -f 'cmdline.c' then echo shar: will not over-write existing file "'cmdline.c'" else cat << \SHAR_EOF > 'cmdline.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <stdio.h> #include <ctype.h> #include "stevie.h" readcmdline(firstc) int firstc; /* either ':', '/', or '?' */ { int c; char buff[100]; char *p, *q, *cmd, *arg; gotocmd(1,1,firstc); p = buff; if ( firstc != ':' ) *p++ = firstc; /* collect the command string, handling '\b' and @ */ for ( ; ; ) { c = vgetc(); if ( c=='\n'||c=='\r'||c==EOF ) break; if ( c=='\b' ) { if ( p > buff ) { p--; /* I know this is gross, but it has the */ /* advantage of relying only on 'gotocmd' */ gotocmd(1,0,firstc==':'?':':0); for ( q=buff; q<p; q++ ) windputc(*q); windrefresh(); } continue; } if ( c=='@' ) { p = buff; gotocmd(1,1,firstc); continue; } windputc(c); windrefresh(); *p++ = c; } *p = '\0'; /* skip any initial white space */ for ( cmd = buff; isspace(*cmd); cmd++ ) ; /* search commands */ c = *cmd; if ( c == '/' || c == '?' ) { cmd++; if ( *cmd == c ) { /* the command was '//' or '??' */ repsearch(); return; } /* If there is a matching '/' or '?' at the end, toss it */ p = strchr(cmd,'\0'); if ( *(--p) == c ) *p = '\0'; dosearch(c=='/'?FORWARD:BACKWARD,cmd); return; } /* isolate the command and find any argument */ for ( p=cmd; *p!='\0' && ! isspace(*p); p++ ) ; if ( *p == '\0' ) arg = NULL; else { *p = '\0'; while ( *(++p) != '\0' && isspace(*p) ) ; arg = p; if ( *arg == '\0' ) arg = NULL; } if ( strcmp(cmd,"q!")==0 ) getout(); if ( strcmp(cmd,"q")==0 ) { if ( Changed ) message("File not written out. Use 'q!' to override."); else getout(); return; } if ( strcmp(cmd,"w")==0 ) { if ( arg == NULL ) { writeit(Filename); UNCHANGED; } else writeit(arg); return; } if ( strcmp(cmd,"wq")==0 ) { if ( writeit(Filename) ) getout(); return; } if ( strcmp(cmd,"f")==0 && arg==NULL ) { fileinfo(); return; } if ( strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0 ) { if ( cmd[1]!='!' && Changed ) { message("File not written out. Use 'e!' to override."); } else { if ( arg != NULL ) Filename = strsave(arg); /* clear mem and read file */ Fileend = Topchar = Curschar = Filemem; UNCHANGED; p = nextline(Curschar); readfile(Filename,Fileend,0); updatescreen(); } return; } if ( strcmp(cmd,"f") == 0 ) { Filename = strsave(arg); filemess(""); return; } if ( strcmp(cmd,"r") == 0 || strcmp(cmd,".r") == 0 ) { char *pp; if ( arg == NULL ) { badcmd(); return; } /* find the beginning of the next line and */ /* read file in there */ pp = nextline(Curschar); readfile(arg,pp,1); updatescreen(); CHANGED; return; } if ( strcmp(cmd,".=")==0 ) { char messbuff[80]; sprintf(messbuff,"line %d character %d", cntlines(Filemem,Curschar), 1+(int)(Curschar-Filemem)); message(messbuff); return; } if ( strcmp(cmd,"$=")==0 ) { char messbuff[8]; sprintf(messbuff,"%d", cntlines(Filemem,Fileend)-1); message(messbuff); return; } if ( strcmp(cmd,"set")==0 ) { if ( arg == NULL ) badcmd(); else if ( strcmp(arg,"oct")==0 ) { octchars(); updatescreen(); } else if ( strcmp(arg,"hex")==0 ) { hexchars(); updatescreen(); } else if ( strcmp(arg,"dec")==0 ) { decchars(); updatescreen(); } else badcmd(); return; } badcmd(); } badcmd() { message("Unrecognized command"); } gotocmd(clr,fresh,firstc) { int n; windgoto(Rows-1,0); if ( clr ) { /* clear the line */ for ( n=0; n<(Columns-1); n++ ) windputc(' '); windgoto(Rows-1,0); } if ( firstc ) windputc(firstc); if ( fresh ) windrefresh(); } message(s) char *s; { static char *lastmess = NULL; char *p; if ( lastmess!=NULL ) { if ( strcmp(lastmess,s)==0 ) return; free(lastmess); } gotocmd(1,1,0); /* take off any trailing newline */ if ( (p=strchr(s,'\0'))!=NULL && *p=='\n' ) *p = '\0'; windstr(s); lastmess = strsave(s); } writeit(fname) char *fname; { FILE *f; char buff[128]; char *p; int n; #ifdef ATARI if ( (f=fopen(fname,Binary?"bw":"w")) == NULL ) { #else if ( (f=fopen(fname,"w")) == NULL ) { #endif message("Unable to open file!"); return(0); } for ( n=0,p=Filemem; p<Fileend; p++,n++ ) putc(*p,f); fclose(f); sprintf(buff,"\"%s\" %d characters",fname,n); message(buff); UNCHANGED; return(1); } filemess(s) char *s; { char buff[128]; sprintf(buff,"\"%s\" %s",Filename,s); message(buff); } SHAR_EOF fi # end of overwriting check if test -f 'edit.c' then echo shar: will not over-write existing file "'edit.c'" else cat << \SHAR_EOF > 'edit.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <stdio.h> #include <ctype.h> #include "stevie.h" edit() { int c, c1, c2; char *p, *q; Prenum = 0; /* position the display and the cursor at the top of the file. */ Topchar = Filemem; Curschar = Filemem; Cursrow = Curscol = 0; for ( ;; ) { /* Figure out where the cursor is based on Curschar. */ cursupdate(); if ( State == INSERT ) message("Insert Mode"); /* printf("Curschar=(%d,%d) row/col=(%d,%d)", Curschar,*Curschar,Cursrow,Curscol); */ windgoto(Cursrow,Curscol); windrefresh(); c = vgetc(); if ( State != INSERT ) message(""); switch(State) { case NORMAL: /* We're in the normal (non-insert) mode. */ /* Pick up any leading digits and compute 'Prenum' */ if ( (Prenum>0 && isdigit(c)) || (isdigit(c) && c!='0') ){ Prenum = Prenum*10 + (c-'0'); break; } /* execute the command */ normal(c); Prenum = 0; break; case INSERT: /* We're in insert mode. */ switch(c){ case '\033': /* an ESCape ends input mode */ /* If we're past the end of the file, (which should */ /* only happen when we're editing a new file or a */ /* file that doesn't have a newline at the end of */ /* the line), add a newline automatically. */ if ( Curschar >= Fileend ) { insertchar('\n'); Curschar--; } /* Don't end up on a '\n' if you can help it. */ if ( Curschar>Filemem && *Curschar=='\n' && *(Curschar-1)!='\n' ) { Curschar--; } State = NORMAL; message(""); Uncurschar = Insstart; Undelchars = Ninsert; /* Undobuff[0] = '\0'; */ /* construct the Redo buffer */ p=Redobuff; q=Insbuff; while ( q<Insptr ) *p++ = *q++; *p++ = '\033'; *p = '\0'; updatescreen(); break; case '\b': if ( Curschar <= Insstart ) beep(); else { int wasnewline = 0; if ( *Curschar == '\n' ) wasnewline=1; Curschar--; delchar(); Insptr--; Ninsert--; if ( wasnewline ) Curschar++; cursupdate(); updatescreen(); } break; case '\030': /* control-x */ { int wasnewline = 0; char *p1; p1 = Curschar; if ( *Curschar == '\n' ) wasnewline = 1; inschar('['); inschar('x'); cursupdate(); updatescreen(); c1 = gethexchar(); inschar(c1); cursupdate(); updatescreen(); c2 = gethexchar(); Curschar = p1; delchar(); delchar(); delchar(); c = 16*hextoint(c1)+hextoint(c2); if(Debug)printf("(c=%d)",c); if ( wasnewline ) Curschar++; inschar(c); Ninsert++; *Insptr++ = c; updatescreen(); break; } case '\017': break; case '\r': c = '\n'; /* This is SUPPOSED to fall down into 'default' */ default: insertchar(c); break; } break; } } } insertchar(c) int c; { char *p; if ( ! anyinput() ) { inschar(c); *Insptr++ = c; Ninsert++; } else { /* If there's any pending input, grab */ /* it all at once. */ p = Insptr; *Insptr++ = c; Ninsert++; while ( (c=vpeekc()) != '\033' ) { c = vgetc(); *Insptr++ = c; Ninsert++; } *Insptr = '\0'; insstr(p); } updatescreen(); } gethexchar() { int c; for ( ;; ) { windgoto(Cursrow,Curscol); windrefresh(); c = vgetc(); if ( hextoint(c) >= 0 ) break; message("Expecting a hexidecimal character (0-9 or a-f)"); beep(); sleep(1); } return(c); } getout() { windgoto(Rows-1,0); windrefresh(); putchar('\r'); putchar('\n'); windexit(0); } cursupdate() { char *p; int inc, c, nlines; /* special case: file is completely empty */ if ( Fileend == Filemem ) { Topchar = Curschar = Filemem; } else if ( Curschar < Topchar ) { nlines = cntlines(Curschar,Topchar); /* if the cursor is above the top of */ /* the screen, put it at the top of the screen.. */ Topchar = Curschar; /* ... and, if we weren't very close to begin with, */ /* we scroll so that the line is close to the middle. */ if ( nlines > Rows/3 ) scrolldown(Rows/3); else { /* make sure we have the current line completely */ /* on the screen, by setting Topchar to the */ /* beginning of the current line (in a strange way). */ if ( (p=prevline(Topchar))!=NULL && (p=nextline(p))!=NULL ) { Topchar = p; } } updatescreen(); } else if ( Curschar >= Botchar && Curschar < Fileend ) { nlines = cntlines(Botchar,Curschar); /* If the cursor is off the bottom of the screen, */ /* put it at the top of the screen.. */ Topchar = Curschar; /* ... and back up */ if ( nlines > Rows/3 ) scrolldown((2*Rows)/3); else scrolldown(Rows-2); updatescreen(); } Cursrow = Curscol = Cursvcol = 0; for ( p=Topchar; p<Curschar; p++ ) { c = *p; if ( c == '\n' ) { Cursrow++; Curscol = Cursvcol = 0; continue; } /* A tab gets expanded, depending on the current column */ if ( c == '\t' ) inc = (8 - (Curscol)%8); else inc = chars[(unsigned)(c & 0xff)].ch_size; Curscol += inc; Cursvcol += inc; if ( Curscol >= Columns ) { Curscol -= Columns; Cursrow++; } } } scrolldown(nlines) int nlines; { int n; char *p; /* Scroll up 'nlines' lines. */ for ( n=nlines; n>0; n-- ) { if ( (p=prevline(Topchar)) == NULL ) break; Topchar = p; } } /* * oneright * oneleft * onedown * oneup * * Move one char {right,left,down,up}. Return 1 when * sucessful, 0 when we hit a boundary (of a line, or the file). */ oneright() { char *p; p = Curschar; if ( (*p++)=='\n' || p>=Fileend || *p == '\n' ) return(0); Curschar++; return(1); } oneleft() { char *p; p = Curschar; if ( *p=='\n' || p==Filemem || *(p-1) == '\n' ) return(0); Curschar--; return(1); } beginline() { while ( oneleft() ) ; } oneup(n) { char *p, *np; int savevcol, k; savevcol = Cursvcol; p = Curschar; for ( k=0; k<n; k++ ) { /* Look for the previous line */ if ( (np=prevline(p)) == NULL ) { /* If we've at least backed up a little .. */ if ( k > 0 ) break; /* to update the cursor, etc. */ else return(0); } p = np; } Curschar = p; /* This makes sure Topchar gets updated so the complete line */ /* is one the screen. */ cursupdate(); /* try to advance to the same (virtual) column */ /* that we were at before. */ Curschar = coladvance(p,savevcol); return(1); } onedown(n) { char *p, *np; int k; p = Curschar; for ( k=0; k<n; k++ ) { /* Look for the next line */ if ( (np=nextline(p)) == NULL ) { if ( k > 0 ) break; else return(0); } p = np; } /* try to advance to the same (virtual) column */ /* that we were at before. */ Curschar = coladvance(p,Cursvcol); return(1); } SHAR_EOF fi # end of overwriting check if test -f 'help.c' then echo shar: will not over-write existing file "'help.c'" else cat << \SHAR_EOF > 'help.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <ctype.h> #include "stevie.h" static int helprow; help() { windclear(); windgoto(helprow=0,0); longline("\ \n\ Cursor movement commands\n\ ========================\n\ control-l Redraw screen\n\ control-d Cursor down 1/2 screen\n\ control-u Cursor up 1/2 screen\n\ control-f Cursor forward 1 screen\n"); longline("\ control-b Cursor back 1 screen\n\ control-g Give info on file\n\ \n\ h Cursor left 1 char\n\ j Cursor down 1 char\n\ k Cursor up 1 char\n"); longline("\ l Cursor right 1 char\n\ $ Cursor to end of line\n\ ^ -or- 0 Cursor to beginning of line\n\ b Cursor back 1 word\n"); longline("\ w Cursor forward 1 word\n\ [#]G Goto line # (or last line if no #)\n\ \n\ <Press space bar to continue>\n\ <Any other key will quit>"); windrefresh(); if ( vgetc() != ' ' ) return; windclear(); windgoto(helprow=0,0); longline("\ \n\ Modification commands\n\ =====================\n\ x Delete 1 char\n\ dw Delete 1 word\n\ D Delete rest of line\n\ [#]dd Delete 1 (or #) lines\n\ C Change rest of line\n"); longline("\ cw Change word\n\ cc Change line\n\ r Replace single character\n\ [#]yy Yank 1 (or #) lines\n\ p Insert last yanked or deleted line(s)\n"); longline("\ P below (p) or above (P) current line\n\ J Join current and next line\n\ [#]<< Shift line left 1 (or #) tabs\n\ [#]>> Shift line right 1 (or #) tabs\n\ i Enter Insert mode (<ESC> to exit)\n"); longline("\ a Append (<ESC> to exit) \n\ o Open line (<ESC> to exit)\n\ \n\ <Press space bar to continue>\n\ <Any other key will quit>"); windrefresh(); if ( vgetc() != ' ' ) return; windclear(); windgoto(helprow=0,0); longline("\ \n\ Miscellaneous\n\ =============\n\ . Repeat last insert or delete\n\ u Undo last insert or delete\n\ /str/ Search for 'str'\n\ ?str? Search backward for 'str'\n"); longline(" n Repeat previous search\n\ :.= Print current line number\n\ :$= Print number of lines in file\n\ H Help\n\ \n\ File manipulation\n\ =================\n"); longline("\ :w Write file\n\ :wq Write and quit\n\ :e {file} Edit a new file\n\ :e! Re-read current file\n\ :f Print file into (current line and total # of lines)\n"); longline("\ :f {file} Change current file name\n\ :q Quit\n\ :q! Quit (no save)\n\ \n\ <Press any key>"); windrefresh(); vgetc(); } longline(p) char *p; { char *s; for ( s=p; *s; s++ ) { if ( *s == '\n' ) windgoto(++helprow,0); else windputc(*s); } } SHAR_EOF fi # end of overwriting check if test -f 'hexchars.c' then echo shar: will not over-write existing file "'hexchars.c'" else cat << \SHAR_EOF > 'hexchars.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <stdio.h> #include "stevie.h" /* * This stuff is used to provide readable interpretations of unprintable * characters. The 'xxx' in each is replaced with either hex, octal, or * decimal numbers, depending on what command-line argument is given. */ struct charinfo chars[] = { /* 000 */ 5, "[xxx]", /* 001 */ 5, "[xxx]", /* 002 */ 5, "[xxx]", /* 003 */ 5, "[xxx]", /* 004 */ 5, "[xxx]", /* 005 */ 5, "[xxx]", /* 006 */ 5, "[xxx]", /* 007 */ 5, "[xxx]", /* 010 */ 5, "[xxx]", /* 011 */ 5, "[xxx]", /* 012 */ 1, NULL, /* 013 */ 5, "[xxx]", /* 014 */ 5, "[xxx]", /* 015 */ 5, "[xxx]", /* 016 */ 5, "[xxx]", /* 017 */ 5, "[xxx]", /* 020 */ 5, "[xxx]", /* 021 */ 5, "[xxx]", /* 022 */ 5, "[xxx]", /* 023 */ 5, "[xxx]", /* 024 */ 5, "[xxx]", /* 025 */ 5, "[xxx]", /* 026 */ 5, "[xxx]", /* 027 */ 5, "[xxx]", /* 030 */ 5, "[xxx]", /* 031 */ 5, "[xxx]", /* 032 */ 5, "[xxx]", /* 033 */ 5, "[xxx]", /* 034 */ 5, "[xxx]", /* 035 */ 5, "[xxx]", /* 036 */ 5, "[xxx]", /* 037 */ 5, "[xxx]", /* 040 */ 1, NULL, /* 041 */ 1, NULL, /* 042 */ 1, NULL, /* 043 */ 1, NULL, /* 044 */ 1, NULL, /* 045 */ 1, NULL, /* 046 */ 1, NULL, /* 047 */ 1, NULL, /* 050 */ 1, NULL, /* 051 */ 1, NULL, /* 052 */ 1, NULL, /* 053 */ 1, NULL, /* 054 */ 1, NULL, /* 055 */ 1, NULL, /* 056 */ 1, NULL, /* 057 */ 1, NULL, /* 060 */ 1, NULL, /* 061 */ 1, NULL, /* 062 */ 1, NULL, /* 063 */ 1, NULL, /* 064 */ 1, NULL, /* 065 */ 1, NULL, /* 066 */ 1, NULL, /* 067 */ 1, NULL, /* 070 */ 1, NULL, /* 071 */ 1, NULL, /* 072 */ 1, NULL, /* 073 */ 1, NULL, /* 074 */ 1, NULL, /* 075 */ 1, NULL, /* 076 */ 1, NULL, /* 077 */ 1, NULL, /* 100 */ 1, NULL, /* 101 */ 1, NULL, /* 102 */ 1, NULL, /* 103 */ 1, NULL, /* 104 */ 1, NULL, /* 105 */ 1, NULL, /* 106 */ 1, NULL, /* 107 */ 1, NULL, /* 110 */ 1, NULL, /* 111 */ 1, NULL, /* 112 */ 1, NULL, /* 113 */ 1, NULL, /* 114 */ 1, NULL, /* 115 */ 1, NULL, /* 116 */ 1, NULL, /* 117 */ 1, NULL, /* 120 */ 1, NULL, /* 121 */ 1, NULL, /* 122 */ 1, NULL, /* 123 */ 1, NULL, /* 124 */ 1, NULL, /* 125 */ 1, NULL, /* 126 */ 1, NULL, /* 127 */ 1, NULL, /* 130 */ 1, NULL, /* 131 */ 1, NULL, /* 132 */ 1, NULL, /* 133 */ 1, NULL, /* 134 */ 1, NULL, /* 135 */ 1, NULL, /* 136 */ 1, NULL, /* 137 */ 1, NULL, /* 140 */ 1, NULL, /* 141 */ 1, NULL, /* 142 */ 1, NULL, /* 143 */ 1, NULL, /* 144 */ 1, NULL, /* 145 */ 1, NULL, /* 146 */ 1, NULL, /* 147 */ 1, NULL, /* 150 */ 1, NULL, /* 151 */ 1, NULL, /* 152 */ 1, NULL, /* 153 */ 1, NULL, /* 154 */ 1, NULL, /* 155 */ 1, NULL, /* 156 */ 1, NULL, /* 157 */ 1, NULL, /* 160 */ 1, NULL, /* 161 */ 1, NULL, /* 162 */ 1, NULL, /* 163 */ 1, NULL, /* 164 */ 1, NULL, /* 165 */ 1, NULL, /* 166 */ 1, NULL, /* 167 */ 1, NULL, /* 170 */ 1, NULL, /* 171 */ 1, NULL, /* 172 */ 1, NULL, /* 173 */ 1, NULL, /* 174 */ 1, NULL, /* 175 */ 1, NULL, /* 176 */ 1, NULL, /* 177 */ 5, "[xxx]", /* 200 */ 5, "[xxx]", /* 201 */ 5, "[xxx]", /* 202 */ 5, "[xxx]", /* 203 */ 5, "[xxx]", /* 204 */ 5, "[xxx]", /* 205 */ 5, "[xxx]", /* 206 */ 5, "[xxx]", /* 207 */ 5, "[xxx]", /* 210 */ 5, "[xxx]", /* 211 */ 5, "[xxx]", /* 212 */ 5, "[xxx]", /* 213 */ 5, "[xxx]", /* 214 */ 5, "[xxx]", /* 215 */ 5, "[xxx]", /* 216 */ 5, "[xxx]", /* 217 */ 5, "[xxx]", /* 220 */ 5, "[xxx]", /* 221 */ 5, "[xxx]", /* 222 */ 5, "[xxx]", /* 223 */ 5, "[xxx]", /* 224 */ 5, "[xxx]", /* 225 */ 5, "[xxx]", /* 226 */ 5, "[xxx]", /* 227 */ 5, "[xxx]", /* 230 */ 5, "[xxx]", /* 231 */ 5, "[xxx]", /* 232 */ 5, "[xxx]", /* 233 */ 5, "[xxx]", /* 234 */ 5, "[xxx]", /* 235 */ 5, "[xxx]", /* 236 */ 5, "[xxx]", /* 237 */ 5, "[xxx]", /* 240 */ 5, "[xxx]", /* 241 */ 5, "[xxx]", /* 242 */ 5, "[xxx]", /* 243 */ 5, "[xxx]", /* 244 */ 5, "[xxx]", /* 245 */ 5, "[xxx]", /* 246 */ 5, "[xxx]", /* 247 */ 5, "[xxx]", /* 250 */ 5, "[xxx]", /* 251 */ 5, "[xxx]", /* 252 */ 5, "[xxx]", /* 253 */ 5, "[xxx]", /* 254 */ 5, "[xxx]", /* 255 */ 5, "[xxx]", /* 256 */ 5, "[xxx]", /* 257 */ 5, "[xxx]", /* 260 */ 5, "[xxx]", /* 261 */ 5, "[xxx]", /* 262 */ 5, "[xxx]", /* 263 */ 5, "[xxx]", /* 264 */ 5, "[xxx]", /* 265 */ 5, "[xxx]", /* 266 */ 5, "[xxx]", /* 267 */ 5, "[xxx]", /* 270 */ 5, "[xxx]", /* 271 */ 5, "[xxx]", /* 272 */ 5, "[xxx]", /* 273 */ 5, "[xxx]", /* 274 */ 5, "[xxx]", /* 275 */ 5, "[xxx]", /* 276 */ 5, "[xxx]", /* 277 */ 5, "[xxx]", /* 300 */ 5, "[xxx]", /* 301 */ 5, "[xxx]", /* 302 */ 5, "[xxx]", /* 303 */ 5, "[xxx]", /* 304 */ 5, "[xxx]", /* 305 */ 5, "[xxx]", /* 306 */ 5, "[xxx]", /* 307 */ 5, "[xxx]", /* 310 */ 5, "[xxx]", /* 311 */ 5, "[xxx]", /* 312 */ 5, "[xxx]", /* 313 */ 5, "[xxx]", /* 314 */ 5, "[xxx]", /* 315 */ 5, "[xxx]", /* 316 */ 5, "[xxx]", /* 317 */ 5, "[xxx]", /* 320 */ 5, "[xxx]", /* 321 */ 5, "[xxx]", /* 322 */ 5, "[xxx]", /* 323 */ 5, "[xxx]", /* 324 */ 5, "[xxx]", /* 325 */ 5, "[xxx]", /* 326 */ 5, "[xxx]", /* 327 */ 5, "[xxx]", /* 330 */ 5, "[xxx]", /* 331 */ 5, "[xxx]", /* 332 */ 5, "[xxx]", /* 333 */ 5, "[xxx]", /* 334 */ 5, "[xxx]", /* 335 */ 5, "[xxx]", /* 336 */ 5, "[xxx]", /* 337 */ 5, "[xxx]", /* 340 */ 5, "[xxx]", /* 341 */ 5, "[xxx]", /* 342 */ 5, "[xxx]", /* 343 */ 5, "[xxx]", /* 344 */ 5, "[xxx]", /* 345 */ 5, "[xxx]", /* 346 */ 5, "[xxx]", /* 347 */ 5, "[xxx]", /* 350 */ 5, "[xxx]", /* 351 */ 5, "[xxx]", /* 352 */ 5, "[xxx]", /* 353 */ 5, "[xxx]", /* 354 */ 5, "[xxx]", /* 355 */ 5, "[xxx]", /* 356 */ 5, "[xxx]", /* 357 */ 5, "[xxx]", /* 360 */ 5, "[xxx]", /* 361 */ 5, "[xxx]", /* 362 */ 5, "[xxx]", /* 363 */ 5, "[xxx]", /* 364 */ 5, "[xxx]", /* 365 */ 5, "[xxx]", /* 366 */ 5, "[xxx]", /* 367 */ 5, "[xxx]", /* 370 */ 5, "[xxx]", /* 371 */ 5, "[xxx]", /* 372 */ 5, "[xxx]", /* 373 */ 5, "[xxx]", /* 374 */ 5, "[xxx]", /* 375 */ 5, "[xxx]", /* 376 */ 5, "[xxx]", /* 377 */ 5, "[xxx]", }; /* * octchars() * * Convert the charinfo strings to octal. */ octchars() { char *p; int n; for ( n=0; n<256; n++ ) { p = chars[n].ch_str; if ( p==NULL || *p != '[' ) continue; sprintf(++p,"%03o]",n); } } /* * hexchars() * * Convert the charinfo strings to hex. */ hexchars() { char *p; int n; for ( n=0; n<256; n++ ) { p = chars[n].ch_str; if ( p==NULL || *p != '[' ) continue; sprintf(++p,"x%02X]",n); } } /* * decchars() * * Convert the charinfo strings to decimal. */ decchars() { char *p; int n; for ( n=0; n<256; n++ ) { p = chars[n].ch_str; if ( p==NULL || *p != '[' ) continue; sprintf(++p,"%3d]",n); } } hextoint(c) int c; { if ( c>='0' && c<='9' ) return(c-'0'); if ( c>='a' && c<='f' ) return(10+c-'a'); if ( c>='A' && c<='F' ) return(10+c-'A'); return(-1); } SHAR_EOF fi # end of overwriting check if test -f 'linefunc.c' then echo shar: will not over-write existing file "'linefunc.c'" else cat << \SHAR_EOF > 'linefunc.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <stdio.h> #include "stevie.h" /* * nextline(curr) * * Return a pointer to the beginning of the next line after the * 'curr' char. Return NULL if there is no next line. */ char * nextline(curr) char *curr; { while ( curr<Fileend ) { if ( *curr++ == '\n' ) break; } if ( curr >= Fileend ) return(NULL); return(curr); } /* * prevline(curr) * * Return a pointer to the beginning of the previous line before the * 'curr' char. Return NULL if there is no previous line. */ char * prevline(curr) char *curr; { int nnl = 0; /* If we are currently positioned on a '\n', */ /* we are on a blank line. Adjust accordingly. */ if ( *curr == '\n' ) nnl = -1; while ( curr>Filemem ) { /* look for the 2nd previous newline */ if ( *curr == '\n' ) { nnl++; if ( nnl == 2) break; } curr--; } if ( curr <= Filemem ) { /* If we found 1 newline, we found the first line */ if ( nnl == 1 ) return(Filemem); else return(NULL); } /* return the first char of the previous line */ return(++curr); } /* * coladvance(p,col) * * Try to advance to the specified column, starting at p. */ char * coladvance(p,col) char *p; int col; { int c, inc; /* If we're on a blank ('\n' only) line, we can't do anything */ if ( *p == '\n' ) return(p); /* try to advance to the specified column */ for ( c=0; col-- > 0; c++ ) { /* Count a tab for what it's worth */ if ( *p == '\t' ) { inc = (7 - c%8); col -= inc; c += inc; } p++; /* Don't go past the end of */ /* the file or the line. */ if ( p==Fileend || *p=='\n' ) { p--; break; } } return(p); } char *strcpy(), *malloc(); #define NULL 0 char * alloc(size) unsigned size; { char *p; /* pointer to new storage space */ p = malloc(size); if ( p == (char *)NULL ) { /* if there is no more room... */ message("alloc() is unable to find memory!"); } return(p); } char * strsave(string) char *string; { return(strcpy(alloc((unsigned)(strlen(string)+1)),string)); } #define NULL 0 static char *laststr = NULL; static int lastdir; char * ssearch(dir,str) int dir; /* FORWARD or BACKWARD */ char *str; { if ( laststr != NULL ) free(laststr); laststr = strsave(str); lastdir = dir; if ( dir == BACKWARD ) return(bcksearch(str)); else return(fwdsearch(str)); } dosearch(dir,str) int dir; char *str; { char *p; if ( (p=ssearch(dir,str)) == NULL ) message("Pattern not found"); else { char *savep; cursupdate(); /* if we're backing up, we make sure the line we're on */ /* is on the screen. */ Curschar = savep = p; /* get to the beginning of the line */ beginline(); if ( Curschar < Topchar ) Topchar = Curschar; Curschar = savep; cursupdate(); updatescreen(); } } repsearch() { if ( laststr == NULL ) beep(); else { dosearch(lastdir,laststr); } } char * fwdsearch(str) char *str; { register char *sofar = str; register char *infile = Curschar+1; int leng = strlen(str); char *stopit; /* search forward to the end of the file */ for ( ; infile < Fileend && *sofar != '\0' ; infile++ ) { if ( *infile == *sofar ) sofar++; else sofar = str; } if ( *sofar == '\0' ) return(infile-strlen(str)); /* search from the beginning of the file to Curschar */ infile = Filemem; sofar = str; stopit = Curschar + leng; for ( ; infile <= stopit && *sofar != '\0' ; infile++ ) { if ( *infile == *sofar ) sofar++; else sofar = str; } if ( *sofar == '\0' ) return(infile-leng); else return(NULL); } char * bcksearch(str) char *str; { int leng = strlen(str); char *infile = Curschar+1; char *endofstr, *sofar, *stopit; /* make sure str isn't empty before getting pointer to */ /* its last character. */ if ( leng == 0 ) return(NULL); endofstr = &str[leng-1]; sofar = endofstr; /* search backward to the beginning of the file */ for ( ; infile >= Filemem && sofar >= str ; infile-- ) { if ( *infile == *sofar ) sofar--; else sofar = endofstr; } if ( sofar < str ) return(++infile); /* search backward from the end of the file */ sofar = endofstr; infile = Fileend-1; stopit = Curschar - leng; for ( ; infile >= stopit && sofar >= str ; infile-- ) { if ( *infile == *sofar ) sofar--; else sofar = endofstr; } if ( sofar < str ) return(++infile); else return(NULL); } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0
tjt@glimmer.UUCP (Tim Thompson) (06/28/87)
# Enclosed is (part 2 of) the source for a mini-VI which I wrote when I # first got the ST, to make things easier for my own development. Although # it is somewhat minimal in capabilities, it does have a fairly good # implementation of the 'u' (undo) and '.' (repeat) commands. I have # much more interesting things to work on these days, so I am unlikely # to provide any support. It has worked well for 6 months, and since # no alternatives have appeared on the net (other than VIX, which is # too different for me), I figure it might be useful. # ...Tim Thompson...ihnp4!twitch!tjt... #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # main.c # misccmds.c # normal.c # stevie.h # window.c # This archive created: Sun Jun 28 13:18:51 1987 export PATH; PATH=/bin:$PATH if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else cat << \SHAR_EOF > 'main.c' /* * STEVIE - ST Editor for VI Enthusiasts ...Tim Thompson...twitch!tjt... */ #include <stdio.h> #include <ctype.h> #include "stevie.h" #ifdef ATARI #include <osbind.h> #endif #define NULL 0 int Rows; /* Number of Rows and Columns */ int Columns; /* in the current window. */ char *Realscreen; /* What's currently on the screen, a single */ /* array of size Rows*Columns. */ char *Nextscreen; /* What's to be put on the screen. */ char *Filename = NULL; /* Current file name */ char *Filemem; /* The contents of the file, as a single array. */ char *Filemax; /* Pointer to the end of allocated space for */ /* Filemem. (It points to the first byte AFTER */ /* the allocated space.) */ char *Fileend; /* Pointer to the end of the file in Filemem. */ /* (It points to the byte AFTER the last byte.) */ char *Topchar; /* Pointer to the byte in Filemem which is */ /* in the upper left corner of the screen. */ char *Botchar; /* Pointer to the byte in Filemem which is */ /* just off the bottom of the screen. */ char *Curschar; /* Pointer to byte in Filemem at which the */ /* cursor is currently placed. */ int Cursrow, Curscol; /* Current position of cursor */ int Cursvcol; /* Current virtual column, the column number of */ /* the file's actual line, as opposed to the */ /* column number we're at on the screen. This */ /* makes a difference on lines that span more */ /* than one screen line. */ int State = NORMAL; /* This is the current state of the command */ /* interpreter. */ int Prenum = 0; /* The (optional) number before a command. */ char *Insstart; /* This is where the latest insert/append */ /* mode started. */ int Changed = 0; /* Set to 1 if something in the file has been */ /* changed and not written out. */ int Debug = 0; int Binary = 0; /* Set to 1 if the file should be read and written */ /* in binary mode (no cr-lf translation). */ char Redobuff[1024]; /* Each command should stuff characters into this */ /* buffer that will re-execute itself. */ char Undobuff[1024]; /* Each command should stuff characters into this */ /* buffer that will undo its effects. */ char Insbuff[1024]; /* Each insertion gets stuffed into this buffer. */ char *Uncurschar = NULL;/* Curschar is restored to this before undoing. */ int Ninsert = 0; /* Number of characters in the current insertion. */ int Undelchars = 0; /* Number of characters to delete, when undoing. */ char *Insptr = NULL; main(argc,argv) int argc; char **argv; { int mode = 8; while ( argc>1 && argv[1][0] == '-' ) { switch (argv[1][1]) { case 'x': mode = 16; break; case 'o': mode = 8; break; case 'd': Debug = 1; break; case 'b': Binary = 1; break; } argc--; argv++; } if ( argc <= 1 ) { fprintf(stderr,"usage: stevie {file}\n"); exit(1); } Filename = strsave(argv[1]); windinit(); /* Make sure Rows/Columns are big enough */ if ( Rows < 3 || Columns < 16 ) { fprintf(stderr,"Rows=%d Columns=%d not big enough!\n", Rows,Columns); windexit(0); } switch ( mode ) { case 8: octchars(); break; case 16: hexchars(); break; } screenalloc(); filealloc(); screenclear(); Fileend = Filemem; if ( readfile(Filename,Fileend,0) ) filemess("[New File]"); Topchar = Curschar = Filemem; updatescreen(); edit(); windexit(0); } /* * filetonext() * * Based on the current value of Topchar, transfer a screenfull of * stuff from Filemem to Nextscreen, and update Botchar. */ filetonext() { int row, col; char *screenp = Nextscreen; char *memp = Topchar; char *endscreen; char *nextrow; char extra[16]; int nextra = 0; int c; int n; /* The number of rows shown is Rows-1. */ /* The last line is the status/command line. */ endscreen = &screenp[(Rows-1)*Columns]; row = col = 0; while ( screenp < endscreen && memp < Fileend ) { /* Get the next character to put on the screen. */ /* The 'extra' array contains the extra stuff that is */ /* inserted to represent special characters (tabs, and */ /* other non-printable stuff. The order in the 'extra' */ /* array is reversed. */ if ( nextra > 0 ) c = extra[--nextra]; else { c = (unsigned)(0xff & (*memp++)); /* when getting a character from the file, we */ /* may have to turn it into something else on */ /* the way to putting it into 'Nextscreen'. */ if ( c == '\t' ) { strcpy(extra," "); /* tab amount depends on current column */ nextra = (7 - col%8); c = ' '; } else if ( (n=chars[c].ch_size) > 1 ) { char *p; nextra = 0; p = chars[c].ch_str; /* copy 'ch-str'ing into 'extra' in reverse */ while ( n > 1 ) extra[nextra++] = p[--n]; c = p[0]; } } if ( c == '\n' ) { row++; /* get pointer to start of next row */ nextrow = &Nextscreen[row*Columns]; /* blank out the rest of this row */ while ( screenp != nextrow ) *screenp++ = ' '; col = 0; continue; } /* store the character in Nextscreen */ if ( col >= Columns ) { row++; col = 0; } *screenp++ = c; col++; } /* make sure the rest of the screen is blank */ while ( screenp < endscreen ) *screenp++ = ' '; /* put '~'s on rows that aren't part of the file. */ if ( col != 0 ) row++; while ( row < Rows ) { Nextscreen[row*Columns] = '~'; row++; } Botchar = memp; } /* * nexttoscreen * * Transfer the contents of Nextscreen to the screen, using Realscreen * to avoid unnecessary output. */ nexttoscreen() { char *np = Nextscreen; char *rp = Realscreen; char *endscreen; char nc; int row = 0, col = 0; int gorow = -1, gocol = -1; endscreen = &np[(Rows-1)*Columns]; for ( ; np < endscreen ; np++,rp++ ) { /* If desired screen (contents of Nextscreen) does not */ /* match what's really there, put it there. */ if ( (nc=(*np)) != (*rp) ) { *rp = nc; /* if we are positioned at the right place, */ /* we don't have to use windgoto(). */ if ( ! (gorow == row && gocol == col) ) windgoto(gorow=row,gocol=col); windputc(nc); gocol++; } if ( ++col >= Columns ) { col = 0; row++; } } windrefresh(); } updatescreen() { filetonext(); nexttoscreen(); } screenclear() { int n; windclear(); /* blank out the stored screens */ for ( n=Rows*Columns-1; n>=0; n-- ) { Realscreen[n] = ' '; Nextscreen[n] = ' '; } } filealloc() { if ( (Filemem=malloc((unsigned)FILELENG)) == NULL ) { fprintf(stderr,"Unable to allocate %d bytes for file memory!\n", FILELENG); exit(1); } Filemax = Filemem + FILELENG; } screenalloc() { Realscreen = malloc((unsigned)(Rows*Columns)); Nextscreen = malloc((unsigned)(Rows*Columns)); } readfile(fname,fromp,nochangename) char *fname; char *fromp; int nochangename; /* if 1, don't change the Filename */ { #ifdef ATARI static char currdisk = 0; char fbuff[128]; int c1, c2; #endif FILE *f; char buff[128]; char *p; int c, n; int unprint = 0; #ifdef ATARI if ( currdisk == 0 ) currdisk = 'a' + Dgetdrv(); /* If a drive is specified, it is used from then */ /* on as the default drive. */ c1 = tolower(*fname); c2 = *(fname+1); if ( c2 == ':' && c1>='a' && c1<='z' ) currdisk = c1; else { /* if no drive is specified, use the default one. */ sprintf(fbuff,"%c:\\%s",toupper(currdisk),fname); fname = fbuff; } #endif if ( ! nochangename ) Filename = strsave(fname); #ifdef ATARI if ( (f=fopen(fname,Binary?"br":"r")) == NULL ) { #else if ( (f=fopen(fname,"r")) == NULL ) { #endif Fileend = Filemem; return(1); } for ( n=0; (c=getc(f)) != EOF; n++ ) { if ( ! (isprint(c)||isspace(c)) ) unprint++; if ( fromp >= Filemax ) { fprintf(stderr,"File too long (limit is %d)!\n",FILELENG); exit(1); } /* Insert the char at the current point by shifting /* everything down. */ for ( p=Fileend; p>fromp; p-- ) *p = *(p-1); *fromp++ = c; if ( Fileend < fromp ) Fileend = fromp; } if ( ! Binary && unprint > 0 ) { sprintf(buff,"%d unprintable chars! Perhaps binary mode (-b) should be used?",unprint); message(buff); sleep(2); } if ( unprint > 0 ) p = "\"%s\" %d characters (%d un-printable) (Press 'H' for help)"; else p = "\"%s\" %d characters (Press 'H' for help)"; sprintf(buff,p,fname,n,unprint); message(buff); fclose(f); return(0); } static char getcbuff[1024]; static char *getcnext = NULL; stuffin(s) char *s; { if ( getcnext == NULL ) { strcpy(getcbuff,s); getcnext = getcbuff; } else strcat(getcbuff,s); } addtobuff(s,c1,c2,c3,c4,c5,c6) char *s; char c1, c2, c3, c4, c5, c6; { char *p = s; if ( (*p++ = c1) == '\0' ) return; if ( (*p++ = c2) == '\0' ) return; if ( (*p++ = c3) == '\0' ) return; if ( (*p++ = c4) == '\0' ) return; if ( (*p++ = c5) == '\0' ) return; if ( (*p++ = c6) == '\0' ) return; } vgetc() { if ( getcnext != NULL ) { int nextc = *getcnext++; if ( *getcnext == '\0' ) { *getcbuff = '\0'; getcnext = NULL; } return(nextc); } return(windgetc()); } vpeekc() { if ( getcnext != NULL ) return(*getcnext); return(-1); } /* * anyinput * * Return non-zero if input is pending. */ anyinput() { if ( getcnext != NULL ) return(1); return(0); } #ifdef ATARI sleep(n) int n; { int k; k = Tgettime(); while ( Tgettime() <= k+n ) ; } #endif SHAR_EOF fi # end of overwriting check if test -f 'misccmds.c' then echo shar: will not over-write existing file "'misccmds.c'" else cat << \SHAR_EOF > 'misccmds.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <stdio.h> #include <ctype.h> #include "stevie.h" /* * opencmd * * Add a blank line below the current line. */ opencmd() { /* get to the end of the current line */ while ( Curschar<Fileend && (*Curschar) != '\n' ) Curschar++; /* Try to handle a file that doesn't end with a newline */ if ( Curschar >= Fileend ) Curschar = Fileend-1; /* Add the blank line */ appchar('\n'); } issepchar(c) char c; { if ( strchr(WORDSEP,c) != NULL ) return(1); return(0); } cntlines(pbegin,pend) char *pbegin, *pend; { int lnum = 1; char *p; for ( p=pbegin; p<pend; ) { if ( *p++ == '\n' ) lnum++; } return(lnum); } fileinfo() { char buff[128]; sprintf(buff,"\"%s\"%s line %d of %d", Filename, Changed?" [Modified]":"", cntlines(Filemem,Curschar), cntlines(Filemem,Fileend)-1); message(buff); } gotoline(n) int n; { char *p; if ( n == 0 ) { if ( (p=prevline(Fileend)) != NULL ) Curschar = p; } else { /* Start at the top of the file and go down 'n'-1 lines */ Curschar = Filemem; while ( --n > 0 ) { if ( (p=nextline(Curschar)) == NULL ) break; Curschar = p; } } Topchar = Curschar; for ( n=0; n<Rows/2; n++ ) { if ( (p=prevline(Topchar)) == NULL ) break; Topchar = p; } updatescreen(); } char *Savedline = NULL; int Savednum = 0; /* * yankline * * Save a copy of the current line(s) for later 'p'lacing. */ yankline(n) { char *savep, *p, *q; int leng, k; if ( Savedline != NULL ) free(Savedline); savep = Curschar; /* go to the beginning of the current line. */ beginline(); /* compute length of line */ for ( p=Curschar,leng=0; ; p++ ) { if ( *p == '\n' ) { /* keep going until we've seen 'n' lines */ if ( --n <= 0 ) break; } leng++; } /* save a copy of it */ Savedline = malloc((unsigned)(leng+2)); for ( p=Curschar,q=Savedline,k=0; k<leng; k++ ) *q++ = *p++; /* get the final newline */ *q++ = *p; *q = '\0'; Curschar = savep; Savednum = leng+1; } /* * putline * * If there is a currently saved line(s), 'p'ut it. * If k==1, 'P'ut the line (i.e. above instead of below. */ putline(k) int k; { char *p; int n; if ( Savedline == NULL ) return; message("Inserting saved stuff..."); if ( k == 0 ) { /* get to the end of the current line */ while ( Curschar<Fileend && *Curschar != '\n' ) Curschar++; } else beginline(); /* append or insert the characters of the saved line */ for ( p=Savedline,n=0; n<Savednum; p++,n++ ) { if ( k == 0 ) appchar(*p); else inschar(*p); } /* We want to end up at the beginning of the line. */ while ( n-->1 ) Curschar--; if ( k == 1 ) Curschar--; beginline(); message(""); updatescreen(); } inschar(c) int c; { register char *p; /* Move everything in the file over to make */ /* room for the new char. */ if ( ! canincrease(1) ) return; for ( p=Fileend; p>Curschar; p-- ) { *p = *(p-1); } *Curschar++ = c; Fileend++; CHANGED; } insstr(s) char *s; { register char *p; int k, n = strlen(s); /* Move everything in the file over to make */ /* room for the new string. */ if ( ! canincrease(n) ) return; for ( p=Fileend-1+n; p>Curschar; p-- ) { *p = *(p-n); } for ( k=0; k<n; k++ ) *Curschar++ = *s++; Fileend += n; CHANGED; } appchar(c) int c; { char *p, *endp; /* Move everything in the file over to make */ /* room for the new char. */ if ( ! canincrease(1) ) return; endp = Curschar+1; for ( p=Fileend; p>endp; p-- ) { *p = *(p-1); } *(++Curschar) = c; Fileend++; CHANGED; } canincrease(n) int n; { if ( (Fileend+n) >= Filemax ) { message("Can't add anything, file is too big!"); State = NORMAL; return(0); } return(1); } #define NULL 0 delchar() { char *p; /* Check for degenerate case; there's nothing in the file. */ if ( Filemem == Fileend ) return; /* Delete the character at Curschar by shifting everything */ /* in the file down. */ for ( p=Curschar+1; p<Fileend; p++ ) *(p-1) = *p; /* If we just took off the last character of a non-blank line, */ /* we don't want to end up positioned at the newline. */ if ( *Curschar=='\n' && Curschar>Filemem && *(Curschar-1)!='\n' ) Curschar--; Fileend--; CHANGED; } delword(deltrailing) int deltrailing; /* 1 if trailing white space should be removed. */ { int c = *Curschar; char *p = Undobuff; /* The Undo string is an 'i'nsert of the word we're deleting. */ *p++ = 'i'; /* If we're positioned on a word separator... */ if ( issepchar(c) && ! isspace(c) ) { /* If we're on a non-space separator, remove */ /* the separators and any following space. */ while ( issepchar(c) && ! isspace(c) ) { /* Add the deleted character to the Undobuff */ *p++ = *Curschar; delchar(); c = *Curschar; } } else { /* we're positioned in the middle of a word */ int endofline = 0; while ( ! issepchar(*Curschar) && *Curschar != '\n' ) { /* If the next char is a newline, we note */ /* that fact here, because delchar() won't */ /* position us there afterword. */ if ( *(Curschar+1) == '\n' ) endofline = 1; /* Add the deleted character to the Undobuff */ *p++ = *Curschar; delchar(); if ( endofline ) break; } } if ( deltrailing ) { /* remove any trailing white space */ while ( isspace(*Curschar) && *Curschar != '\n' ) { /* Add the deleted character to the Undobuff */ *p++ = *Curschar; delchar(); } } *p++ = '\033'; *p = '\0'; } delline(nlines) { int nchars; char *p, *q; /* If we're not at the beginning of the line, get there. */ if ( *Curschar != '\n' ) { /* back up to the previous newline (or the beginning */ /* of the file. */ while ( Curschar > Filemem ) { if ( *Curschar == '\n' ) { Curschar++; break; } Curschar--; } } message("Deleting..."); while ( nlines-- > 0 ) { /* Count the characters in the line */ for ( nchars=1,p=Curschar; p<Fileend&&*p!='\n'; p++,nchars++ ) ; /* Delete the characters of the line */ /* by moving everything else in the file down. */ q = Curschar; p = Curschar+nchars; while ( p<Fileend ) *q++ = *p++; Fileend -= nchars; CHANGED; /* If we delete the last line in the file, back up */ if ( Curschar >= Fileend ) { if ( (Curschar=prevline(Curschar)) == NULL ) Curschar = Filemem; /* and don't try to delete any more lines */ break; } } message(""); } char *strchr(s,c) char *s; int c; { do { if ( *s == c ) return(s); } while (*s++); return(NULL); } SHAR_EOF fi # end of overwriting check if test -f 'normal.c' then echo shar: will not over-write existing file "'normal.c'" else cat << \SHAR_EOF > 'normal.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include <ctype.h> #include "stevie.h" /* * normal * * Execute a command in normal mode. */ normal(c) int c; { char *p, *q; int nchar, n; switch(c){ case 'H': help(); /* fall through purposely */ case '\014': screenclear(); updatescreen(); break; case 04: /* control-d */ if ( ! onedown(10) ) beep(); break; case 025: /* control-u */ if ( ! oneup(10) ) beep(); break; case 06: /* control-f */ if ( ! onedown(Rows) ) beep(); break; case 02: /* control-b */ if ( ! oneup(Rows) ) beep(); break; case '\007': fileinfo(); break; case 'G': gotoline(Prenum); break; case 'l': if ( ! oneright() ) beep(); break; case 'h': if ( ! oneleft() ) beep(); break; case 'k': if ( ! oneup(1) ) beep(); break; case 'j': if ( ! onedown(1) ) beep(); break; case 'b': /* If we're on the first character of a word, force */ /* an initial backup. */ if ( ! issepchar(*Curschar) && Curschar>Filemem && issepchar(*(Curschar-1)) ) Curschar--; if ( ! issepchar(*Curschar) ) { /* If we start in the middle of a word, back */ /* up until we hit a separator. */ while ( Curschar>Filemem && !issepchar(*Curschar)) Curschar--; if ( issepchar(*Curschar) ) Curschar++; } else { /* back up past all separators. */ while ( Curschar>Filemem && issepchar(*Curschar)) Curschar--; /* back up past all non-separators. */ while (Curschar>Filemem && !issepchar(*Curschar)){ Curschar--; } if ( issepchar(*Curschar) ) Curschar++; } break; case 'w': if ( issepchar(*Curschar) ) { /* If we're on a separator, we advance to */ /* the next non-separator char. */ while ( (p=Curschar+1) < Fileend ) { Curschar = p; if ( ! issepchar(*Curschar) ) break; } } else { /* If we're in the middle of a word, we */ /* advance to the next word-separator. */ while ( (p=Curschar+1) < Fileend ) { Curschar = p; if ( issepchar(*Curschar) ) break; } /* Now go past any trailing white space */ while (isspace(*Curschar) && (Curschar+1)<Fileend) Curschar++; } break; case '$': while ( oneright() ) ; break; case '0': case '^': beginline(); break; case 'x': /* Can't do it if we're on a blank line. (Actually it */ /* does work, but we want to match the real 'vi'...) */ if ( *Curschar == '\n' ) beep(); else { addtobuff(Redobuff,'x',NULL); /* To undo it, we insert the same character back. */ resetundo(); addtobuff(Undobuff,'i',*Curschar,'\033',NULL); Uncurschar = Curschar; delchar(); updatescreen(); } break; case 'a': /* Works just like an 'i'nsert on the next character. */ if ( Curschar < (Fileend-1) ) Curschar++; resetundo(); startinsert("a"); break; case 'i': resetundo(); startinsert("i"); break; case 'o': opencmd(); updatescreen(); resetundo(); startinsert("o"); break; case 'd': nchar = vgetc(); n = (Prenum==0?1:Prenum); switch(nchar){ case 'd': sprintf(Redobuff,"%ddd",n); /* addtobuff(Redobuff,'d','d',NULL); */ beginline(); resetundo(); Uncurschar = Curschar; yankline(n); delline(n); beginline(); updatescreen(); /* If we have backed xyzzy, then we deleted the */ /* last line(s) in the file. */ if ( Curschar < Uncurschar ) { Uncurschar = Curschar; nchar = 'p'; } else nchar = 'P'; addtobuff(Undobuff,nchar,NULL); break; case 'w': addtobuff(Redobuff,'d','w',NULL); resetundo(); delword(1); Uncurschar = Curschar; updatescreen(); break; } break; case 'c': nchar = vgetc(); switch(nchar){ case 'c': resetundo(); /* Go to the beginning of the line */ beginline(); yankline(1); /* delete everything but the newline */ while ( *Curschar != '\n' ) delchar(); startinsert("cc"); updatescreen(); break; case 'w': resetundo(); delword(0); startinsert("cw"); updatescreen(); break; } break; case 'y': nchar = vgetc(); switch(nchar){ case 'y': yankline(Prenum==0?1:Prenum); break; default: beep(); } break; case '>': nchar = vgetc(); n = (Prenum==0?1:Prenum); switch(nchar){ case '>': tabinout(0,n); updatescreen(); break; default: beep(); } break; case '<': nchar = vgetc(); n = (Prenum==0?1:Prenum); switch(nchar){ case '<': tabinout(1,n); updatescreen(); break; default: beep(); } break; case '?': case '/': case ':': readcmdline(c); break; case 'n': repsearch(); break; case 'C': case 'D': p = Curschar; while ( Curschar >= p ) delchar(); updatescreen(); resetundo(); /* This should really go above the */ /* delchars above, and the undobuff should */ /* be constructed by them. */ if ( c == 'C' ) { Curschar++; startinsert("C"); } break; case 'r': nchar = vgetc(); resetundo(); if ( nchar=='\n' || (!Binary && nchar=='\r') ) { /* Replacing a char with a newline breaks the */ /* line in two, and is special. */ nchar = '\n'; /* convert \r to \n */ /* Save stuff necessary to undo it, by joining */ Uncurschar = Curschar-1; addtobuff(Undobuff,'J','i',*Curschar,'\033',NULL); /* Change current character. */ *Curschar = nchar; /* We don't want to end up on the '\n' */ if ( Curschar > Filemem ) Curschar--; else if (Curschar < Fileend ) Curschar++; } else { /* Replacing with a normal character */ addtobuff(Undobuff,'r',*Curschar,NULL); Uncurschar = Curschar; /* Change current character. */ *Curschar = nchar; } /* Save stuff necessary to redo it */ addtobuff(Redobuff,'r',nchar,NULL); updatescreen(); break; case 'p': putline(0); break; case 'P': putline(1); break; case 'J': for ( p=Curschar; *p!= '\n' && p<(Fileend-1) ; p++ ) ; if ( p >= (Fileend-1) ) { beep(); break; } Curschar = p; delchar(); resetundo(); Uncurschar = Curschar; addtobuff(Undobuff,'i','\n','\033',NULL); addtobuff(Redobuff,'J',NULL); updatescreen(); break; case '.': stuffin(Redobuff); break; case 'u': if ( Uncurschar != NULL && *Undobuff != '\0' ) { Curschar = Uncurschar; stuffin(Undobuff); *Undobuff = '\0'; } if ( Undelchars > 0 ) { Curschar = Uncurschar; /* construct the next Undobuff and Redobuff, which */ /* will re-insert the characters we're deleting. */ p = Undobuff; q = Redobuff; *p++ = *q++ = 'i'; while ( Undelchars-- > 0 ) { *p++ = *q++ = *Curschar; delchar(); } /* Finish constructing Uncursbuff, and Uncurschar */ /* is left unchanged. */ *p++ = *q++ = '\033'; *p = *q = '\0'; /* Undelchars has been reset to 0 */ updatescreen(); } break; default: beep(); break; } } /* * tabinout(inout,num) * * If inout==0, add a tab to the begining of the next num lines. * If inout==1, delete a tab from the begining of the next num lines. */ tabinout(inout,num) { int ntodo = num; char *savecurs, *p; beginline(); savecurs = Curschar; while ( ntodo-- > 0 ) { beginline(); if ( inout == 0 ) inschar('\t'); else { if ( *Curschar == '\t' ) delchar(); } if ( ntodo > 0 ) { if ( (p=nextline(Curschar)) != NULL ) Curschar = p; else break; } } /* We want to end up where we started */ Curschar = savecurs; updatescreen(); /* Construct re-do and un-do stuff */ sprintf(Redobuff,"%d%s",num,inout==0?">>":"<<"); resetundo(); Uncurschar = savecurs; sprintf(Undobuff,"%d%s",num,inout==0?"<<":">>"); } startinsert(initstr) char *initstr; { char *p, c; Insstart = Curschar; Ninsert = 0; Insptr = Insbuff; for (p=initstr; (c=(*p++))!='\0'; ) *Insptr++ = c; State = INSERT; windrefresh(); } resetundo() { Undelchars = 0; *Undobuff = '\0'; Uncurschar = NULL; } SHAR_EOF fi # end of overwriting check if test -f 'stevie.h' then echo shar: will not over-write existing file "'stevie.h'" else cat << \SHAR_EOF > 'stevie.h' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ /* One (and only 1) of the following 3 defines should be uncommented. */ /* Most of the code is machine-independent. Most of the machine- */ /* dependent stuff is in window.c */ #define ATARI 1 /* For the Atari 520 ST */ /*#define UNIXPC 1 /* The AT&T UNIX PC (console) */ /*#define TCAP 1 /* For termcap-based terminals */ #define FILELENG 64000 #define NORMAL 0 #define CMDLINE 1 #define INSERT 2 #define APPEND 3 #define FORWARD 4 #define BACKWARD 5 #define WORDSEP " \t\n()[]{},;:'\"-=" #define CHANGED Changed=1 #define UNCHANGED Changed=0 #ifndef NULL #define NULL 0 #endif struct charinfo { char ch_size; char *ch_str; }; extern struct charinfo chars[]; extern int State; extern int Rows; extern int Columns; extern char *Realscreen; extern char *Nextscreen; extern char *Filename; extern char *Filemem; extern char *Filemax; extern char *Fileend; extern char *Topchar; extern char *Botchar; extern char *Curschar; extern char *Insstart; extern int Cursrow, Curscol, Cursvcol; extern int Prenum; extern int Debug; extern int Changed; extern int Binary; extern char Redobuff[], Undobuff[], Insbuff[]; extern char *Uncurschar, *Insptr; extern int Ninsert, Undelchars; char *malloc(), *strchr(), *strsave(), *alloc(), *strcpy(); char *nextline(), *prevline(), *coladvance(), *ssearch(); char *fwdsearch(), *bcksearch(); SHAR_EOF fi # end of overwriting check if test -f 'window.c' then echo shar: will not over-write existing file "'window.c'" else cat << \SHAR_EOF > 'window.c' /* * STevie - ST editor for VI enthusiasts. ...Tim Thompson...twitch!tjt... */ #include "stevie.h" #include <stdio.h> #ifdef ATARI #include <osbind.h> #define EscSeq(x) Cconout('\033');Cconout(x); #endif #ifdef UNIXPC #include <sys/window.h> #endif #ifdef TCAP #include <curses.h> #endif windinit() { #ifdef ATARI Columns=80; Rows=25; Cursconf(1,NULL); #endif #ifdef UNIXPC struct uwdata uw; winit(); if ( ioctl(0,WIOCGETD,&uw) == -1 && ioctl(1,WIOCGETD,&uw) == -1 && ioctl(2,WIOCGETD,&uw) == -1 ) { fprintf(stderr,"*** ERROR *** Not a window!\n"); windexit(1); } Columns = uw.uw_width / uw.uw_hs; Rows = uw.uw_height / uw.uw_vs; cbreak(); nonl(); noecho(); #endif #ifdef TCAP char *getenv(); char *p = getenv("TERM"); initscr(); Columns = 80; if ( strncmp(p,"vt52",4)==0 ) Rows = 25; else Rows = 24; cbreak(); nonl(); noecho(); #endif } windgoto(r,c) int r,c; { #ifdef UNIXPC printf("\033[%d;%dH",r+1,c+1); #endif #ifdef ATARI EscSeq('Y'); Cconout(r+040); Cconout(c+040); #endif #ifdef TCAP move(r,c); #endif } windexit(r) int r; { #ifdef UNIXPC nocbreak(); nl(); echo(); wexit(); #endif #ifdef TCAP nocbreak(); nl(); echo(); endwin(); #endif exit(r); } windclear() { #ifdef UNIXPC printf("\033[H\033[J"); #endif #ifdef ATARI Cconws("\033H\033J"); #endif #ifdef TCAP clear(); refresh(); #endif } windgetc() { #ifdef ATARI return(Cnecin()); #else return(getchar()); #endif } windstr(s) char *s; { #ifdef ATARI Cconws(s); #endif #ifdef UNIXPC printf("%s",s); #endif #ifdef TCAP addstr(s); refresh(); #endif } windputc(c) int c; { #ifdef ATARI Cconout(c); #endif #ifdef UNIXPC putchar(c); #endif #ifdef TCAP addch(c); #endif } windrefresh() { #ifdef TCAP refresh(); #endif } beep() { #ifdef ATARI Cconout('\007'); #else putchar('\007'); #endif } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0