page@swan.ulowell.edu (Bob Page) (12/02/88)
Submitted-by: grwalter@watcgl.waterloo.edu Posting-number: Volume 2, Issue 81 Archive-name: editors/stevie.1 This is a source release of the STEVIE editor, a public domain clone of the UNIX editor 'vi'. The program was originally developed for the Atari ST, but has been ported to UNIX, OS/2, BSD 4.3 and the Amiga. # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # Makefile # README # alloc.c # amiga.c # amiga.h # ascii.h # charset.c # cmdline.c # dec.c # edit.c # fileio.c # help.c # inc.c # This archive created: Thu Dec 1 20:37:03 1988 cat << \SHAR_EOF > Makefile # # Makefile for Lattice C on Amiga # .c.o: lc $(CFLAGS) $< LIBS = /regexp/regexp.lib LDFLAGS= CFLAGS = -cu -dAUTO_INDENT LINKFLAGS = NODEBUG MACH= amiga.o raw.o sendpacket.o OBJ= main.o edit.o linefunc.o normal.o cmdline.o charset.o \ updateRealscreen.o misccmds.o help.o dec.o inc.o search.o alloc.o \ updateNextscreen.o mark.o screen.o fileio.o param.o $(MACH) all : stevie stevie : $(OBJ) BLINK TO stevie FROM lib:c.o $(OBJ) \ LIBRARY $(LIBS) lib:lc.lib lib:amiga.lib \ $(LINKFLAGS) clean : delete $(OBJ) SHAR_EOF cat << \SHAR_EOF > README STEVIE Source Release This is a source release of the STEVIE editor, a public domain clone of the UNIX editor 'vi'. The program was originally developed for the Atari ST, but has been ported to UNIX, OS/2, BSD 4.3 and the Amiga as well. To compile STEVIE, you'll also need Henry Spencer's regular expression library. The files included in this release are: README This file. stevie.doc Reference manual for STEVIE. Assumes familiarity with vi. source.doc Quick overview of the major data structures used. porting.doc Tips for porting STEVIE to other systems. makefile.os2 makefile.usg makefile.tos makefile.bsd makefile.amiga.lattice Makefiles for OS/2, UNIX System V, Atari ST, BSD 4.3 UNIX and the Amiga respectively. amiga.c amiga.h bsd.c bsd.h os2.c os2.h unix.c unix.h tos.c tos.h System-dependent routines for the same. alloc.c ascii.h cmdline.c edit.c fileio.c help.c charset.c keymap.h linefunc.c main.c mark.c misccmds.c normal.c param.c param.h ptrfunc.c screen.c search.c stevie.h term.h macros.h C source and header files for STEVIE. To compile STEVIE for one of the provided systems: 1. Compile the regular expression library and install as appropriate for your system. 2. Edit the file 'stevie.h' to set the system defines as needed. 3. Check the makefile for your system, and modify as needed. 4. Compile. Good Luck... Tony Andrews March 12, 1988 G. R. (Fred) Walter August 14, 1988 SHAR_EOF cat << \SHAR_EOF > alloc.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * This file contains various routines dealing with allocation and * deallocation of data structures. */ 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... */ emsg("alloc() is unable to find memory!"); } return (p); } char * strsave(string) char *string; { return (strcpy(alloc((unsigned) (strlen(string) + 1)), string)); } void screenalloc() { /* * If we're changing the size of the screen, free the old arrays */ if (Realscreen != NULL) free(Realscreen); if (Nextscreen != NULL) free(Nextscreen); Realscreen = malloc((unsigned) (Rows * Columns)); Nextscreen = malloc((unsigned) (Rows * Columns)); } /* * Allocate and initialize a new line structure with room for 'nchars' * characters. */ LINE * newline(nchars) int nchars; { LINE *l; l = (LINE *) alloc((unsigned) sizeof(LINE)); if (l == NULL) return (LINE *) NULL; l->s = alloc((unsigned) nchars); /* the line is empty */ l->s[0] = NUL; l->size = nchars; l->prev = (LINE *) NULL; /* should be initialized by caller */ l->next = (LINE *) NULL; return l; } /* * filealloc() - construct an initial empty file buffer */ void filealloc() { if ((Filemem->linep = newline(1)) == NULL) { fprintf(stderr, "Unable to allocate file memory!\n"); getout(1); } if ((Fileend->linep = newline(1)) == NULL) { fprintf(stderr, "Unable to allocate file memory!\n"); getout(1); } Filemem->index = 0; Fileend->index = 0; Filemem->linep->next = Fileend->linep; Fileend->linep->prev = Filemem->linep; *Curschar = *Filemem; *Topchar = *Filemem; Filemem->linep->num = 0; Fileend->linep->num = 0xffff; clrall(); /* clear all marks */ } /* * freeall() - free the current buffer * * Free all lines in the current buffer. */ void freeall() { LINE *lp, *xlp; for (lp = Filemem->linep; lp != NULL; lp = xlp) { if (lp->s != NULL) free(lp->s); xlp = lp->next; free((char *) lp); } Curschar->linep = NULL; /* clear pointers */ Filemem->linep = NULL; Fileend->linep = NULL; } /* * canincrease(n) - returns TRUE if the current line can be increased 'n' * bytes * * This routine returns immediately if the requested space is available. If not, * it attempts to allocate the space and adjust the data structures * accordingly. If everything fails it returns FALSE. */ bool_t canincrease(n) int n; { int nsize; char *s; /* pointer to new space */ nsize = strlen(Curschar->linep->s) + 1 + n; /* size required */ if (nsize <= Curschar->linep->size) return TRUE; /* * Need to allocate more space for the string. Allow some extra space on * the assumption that we may need it soon. This avoids excessive numbers * of calls to malloc while entering new text. */ s = alloc((unsigned) (nsize + SLOP)); if (s == NULL) { emsg("Can't add anything, file is too big!"); State = NORMAL; return FALSE; } Curschar->linep->size = nsize + SLOP; strcpy(s, Curschar->linep->s); free(Curschar->linep->s); Curschar->linep->s = s; return TRUE; } SHAR_EOF cat << \SHAR_EOF > amiga.c /* * Amiga system-dependent routines. */ #include "stevie.h" long stdin_file_handle = 0; int GetCharacter() { char c; Read(stdin_file_handle, &c, sizeof(c)); return ((int) c); } /* * getCSIsequence - get a CSI sequence * - either cursor keys, help, functionkeys, or some * other sequence (if other, check window size) */ int getCSIsequence() { int c; int param1; int param2; c = GetCharacter(); if (isdigit(c)) { param1 = 0; while (isdigit(c)) { param1 = param1 * 10 + c - '0'; c = GetCharacter(); } if (c == '~') /* function key */ return ((char) (K_F1 + param1)); /* must be an event of some sort or a window bound report */ if (c == ';') { param2 = 0; c = GetCharacter(); while (isdigit(c)) { param2 = param2 * 10 + c - '0'; c = GetCharacter(); } if (c == ';') { param1 = 0; c = GetCharacter(); while (isdigit(c)) { param1 = param1 * 10 + c - '0'; c = GetCharacter(); } if (c == ';') { param2 = 0; c = GetCharacter(); while (isdigit(c)) { param2 = param2 * 10 + c - '0'; c = GetCharacter(); } if (c == ' ') { c = GetCharacter(); if (c == 'r') { if (param1 < 2) param1 = 2; if (param2 < 5) param2 = 5; if (Columns != param2 || Rows != param1) { Columns = param2; Rows = param1; P(P_LI) = Rows; return (-1); } else return 0; } } } } } while ((c != '|') && (c != '~')) c = GetCharacter(); outstr("\033[0 q"); fflush(stdout); /* flush out the window size request */ return 0; } switch (c) { case 'A': /* cursor up */ return K_UARROW; case 'B': /* cursor down */ return K_DARROW; case 'C': /* cursor right */ return K_RARROW; case 'D': /* cursor left */ return K_LARROW; case 'T': /* shift cursor up */ return K_SUARROW; case 'S': /* shift cursor down */ return K_SDARROW; case ' ': /* shift cursor left or right */ c = GetCharacter(); if (c == 'A') /* shift cursor left */ return K_SLARROW; if (c == '@') /* shift cursor right */ return K_SRARROW; break; case '?': /* help */ c = GetCharacter(); if (c == '~') return K_HELP; break; } return 0; /* some other control code */ } /* * inchar() - get a character from the keyboard */ char inchar() { int c; fflush(stdout); /* flush any pending output */ for (;;) { c = GetCharacter(); if (c != 0x9b) break; c = getCSIsequence(); if (c > 0) break; if (c == -1) { screenalloc(); screenclear(); updateNextscreen(); updateRealscreen(); msg(""); cursupdate(); windgoto(Cursrow, Curscol); fflush(stdout); } } return (char) c; } void outstr(s) char *s; { while (*s) outchar(*s++); } void beep() { if (RedrawingDisabled) return; outchar('\007'); } void sleep(n) int n; { void Delay(); if (n > 0) Delay(50L * n); } void delay() { void Delay(); Delay(25L); } void windinit() { stdin_file_handle = Input(); if (!IsInteractive(stdin_file_handle)) { fprintf(stderr, "stdin is not interactive ?!?!?!?"); exit(2); } Columns = 80; P(P_LI) = Rows = 24; if (raw(stdin) != 0) perror("raw"); outstr("\033[12{"); /* window resize events activated */ outstr("\033[0 q"); /* get window size */ fflush(stdout); for (;;) { if (GetCharacter() == 0x9b) if (getCSIsequence() == -1) break; } } void windexit(r) int r; { outstr("\033[12}"); /* window resize events de-activated */ fflush(stdout); /* flush any pending output */ if (cooked(stdin) != 0) perror("cooked"); exit(r); } void windgoto(r, c) int c; int r; { r++; c++; outstr("\033["); if (r >= 10) outchar((char) (r / 10 + '0')); outchar((char) (r % 10 + '0')); outchar(';'); if (c >= 10) outchar((char) (c / 10 + '0')); outchar((char) (c % 10 + '0')); outchar('H'); } FILE * fopenb(fname, mode) char *fname; char *mode; { FILE *fopen(); char modestr[16]; sprintf(modestr, "%sb", mode); return fopen(fname, modestr); } SHAR_EOF cat << \SHAR_EOF > amiga.h /* * Amiga Machine-dependent routines. */ char inchar(); #define outchar(c) putchar(c) void outstr(); void beep(); void windinit(), windexit(), windgoto(); void delay(); void sleep(); SHAR_EOF cat << \SHAR_EOF > ascii.h /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ /* * Definitions of various common control characters */ #define NUL '\000' #define BS '\010' #define TAB '\011' #define NL '\012' #define NL_STR "\012" #define CR '\015' #define ESC '\033' #define ESC_STR "\033" #define UNDO_SHIFTJ '\333' #define UNDO_SHIFTJ_STR "\333" #define CTRL(x) ((x) & 0x1f) SHAR_EOF cat << \SHAR_EOF > charset.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * This file shows how to display characters on the screen. This is approach * is something of an overkill. It's a remnant from the original code that * isn't worth messing with for now. TABS are special-cased depending on the * value of the "list" parameter. */ struct charinfo chars[] = { /* 0 */ 1, NUL, /* 1 */ 2, "^A", /* 2 */ 2, "^B", /* 3 */ 2, "^C", /* 4 */ 2, "^D", /* 5 */ 2, "^E", /* 6 */ 2, "^F", /* 7 */ 2, "^G", /* 8 */ 2, "^H", /* 9 */ 2, "^I", /* 10 */ 7, "[ERROR]", /* shouldn't happen */ /* 11 */ 2, "^K", /* 12 */ 2, "^L", /* 13 */ 2, "^M", /* 14 */ 2, "^N", /* 15 */ 2, "^O", /* 16 */ 2, "^P", /* 17 */ 2, "^Q", /* 18 */ 2, "^R", /* 19 */ 2, "^S", /* 20 */ 2, "^T", /* 21 */ 2, "^U", /* 22 */ 2, "^V", /* 23 */ 2, "^W", /* 24 */ 2, "^X", /* 25 */ 2, "^Y", /* 26 */ 2, "^Z", /* 27 */ 2, "^[", /* 28 */ 2, "^\\", /* 29 */ 2, "^]", /* 30 */ 2, "^^", /* 31 */ 2, "^_", /* 32 */ 1, " ", /* 33 */ 1, "!", /* 34 */ 1, "\"", /* 35 */ 1, "#", /* 36 */ 1, "$", /* 37 */ 1, "%", /* 38 */ 1, "&", /* 39 */ 1, "'", /* 40 */ 1, "(", /* 41 */ 1, ")", /* 42 */ 1, "*", /* 43 */ 1, "+", /* 44 */ 1, ",", /* 45 */ 1, "-", /* 46 */ 1, ".", /* 47 */ 1, "/", /* 48 */ 1, "0", /* 49 */ 1, "1", /* 50 */ 1, "2", /* 51 */ 1, "3", /* 52 */ 1, "4", /* 53 */ 1, "5", /* 54 */ 1, "6", /* 55 */ 1, "7", /* 56 */ 1, "8", /* 57 */ 1, "9", /* 58 */ 1, ":", /* 59 */ 1, ";", /* 60 */ 1, "<", /* 61 */ 1, "=", /* 62 */ 1, ">", /* 63 */ 1, "?", /* 64 */ 1, "@", /* 65 */ 1, "A", /* 66 */ 1, "B", /* 67 */ 1, "C", /* 68 */ 1, "D", /* 69 */ 1, "E", /* 70 */ 1, "F", /* 71 */ 1, "G", /* 72 */ 1, "H", /* 73 */ 1, "I", /* 74 */ 1, "J", /* 75 */ 1, "K", /* 76 */ 1, "L", /* 77 */ 1, "M", /* 78 */ 1, "N", /* 79 */ 1, "O", /* 80 */ 1, "P", /* 81 */ 1, "Q", /* 82 */ 1, "R", /* 83 */ 1, "S", /* 84 */ 1, "T", /* 85 */ 1, "U", /* 86 */ 1, "V", /* 87 */ 1, "W", /* 88 */ 1, "X", /* 89 */ 1, "Y", /* 90 */ 1, "Z", /* 91 */ 1, "[", /* 92 */ 1, "\\", /* 93 */ 1, "]", /* 94 */ 1, "^", /* 95 */ 1, "_", /* 96 */ 1, "`", /* 97 */ 1, "a", /* 98 */ 1, "b", /* 99 */ 1, "c", /* 100 */ 1, "d", /* 101 */ 1, "e", /* 102 */ 1, "f", /* 103 */ 1, "g", /* 104 */ 1, "h", /* 105 */ 1, "i", /* 106 */ 1, "j", /* 107 */ 1, "k", /* 108 */ 1, "l", /* 109 */ 1, "m", /* 110 */ 1, "n", /* 111 */ 1, "o", /* 112 */ 1, "p", /* 113 */ 1, "q", /* 114 */ 1, "r", /* 115 */ 1, "s", /* 116 */ 1, "t", /* 117 */ 1, "u", /* 118 */ 1, "v", /* 119 */ 1, "w", /* 120 */ 1, "x", /* 121 */ 1, "y", /* 122 */ 1, "z", /* 123 */ 1, "{", /* 124 */ 1, "|", /* 125 */ 1, "}", /* 126 */ 1, "~", /* 127 */ 2, "^?", /* 128 */ 5, "[128]", /* 129 */ 5, "[129]", /* 130 */ 5, "[130]", /* 131 */ 5, "[131]", /* 132 */ 5, "[132]", /* 133 */ 5, "[133]", /* 134 */ 5, "[134]", /* 135 */ 5, "[135]", /* 136 */ 5, "[136]", /* 137 */ 5, "[137]", /* 138 */ 5, "[138]", /* 139 */ 5, "[139]", /* 140 */ 5, "[140]", /* 141 */ 5, "[141]", /* 142 */ 5, "[142]", /* 143 */ 5, "[143]", /* 144 */ 5, "[144]", /* 145 */ 5, "[145]", /* 146 */ 5, "[146]", /* 147 */ 5, "[147]", /* 148 */ 5, "[148]", /* 149 */ 5, "[149]", /* 150 */ 5, "[150]", /* 151 */ 5, "[151]", /* 152 */ 5, "[152]", /* 153 */ 5, "[153]", /* 154 */ 5, "[154]", /* 155 */ 5, "[155]", /* 156 */ 5, "[156]", /* 157 */ 5, "[157]", /* 158 */ 5, "[158]", /* 159 */ 5, "[159]", #ifdef AMIGA /* 160 */ 1, "\240", /* 161 */ 1, "\241", /* 162 */ 1, "\242", /* 163 */ 1, "\243", /* 164 */ 1, "\244", /* 165 */ 1, "\245", /* 166 */ 1, "\246", /* 167 */ 1, "\247", /* 168 */ 1, "\250", /* 169 */ 1, "\251", /* 170 */ 1, "\252", /* 171 */ 1, "\253", /* 172 */ 1, "\254", /* 173 */ 1, "\255", /* 174 */ 1, "\256", /* 175 */ 1, "\257", /* 176 */ 1, "\260", /* 177 */ 1, "\261", /* 178 */ 1, "\262", /* 179 */ 1, "\263", /* 180 */ 1, "\264", /* 181 */ 1, "\265", /* 182 */ 1, "\266", /* 183 */ 1, "\267", /* 184 */ 1, "\270", /* 185 */ 1, "\271", /* 186 */ 1, "\272", /* 187 */ 1, "\273", /* 188 */ 1, "\274", /* 189 */ 1, "\275", /* 190 */ 1, "\276", /* 191 */ 1, "\277", /* 192 */ 1, "\300", /* 193 */ 1, "\301", /* 194 */ 1, "\302", /* 195 */ 1, "\303", /* 196 */ 1, "\304", /* 197 */ 1, "\305", /* 198 */ 1, "\306", /* 199 */ 1, "\307", /* 200 */ 1, "\310", /* 201 */ 1, "\311", /* 202 */ 1, "\312", /* 203 */ 1, "\313", /* 204 */ 1, "\314", /* 205 */ 1, "\315", /* 206 */ 1, "\316", /* 207 */ 1, "\317", /* 208 */ 1, "\320", /* 209 */ 1, "\321", /* 210 */ 1, "\322", /* 211 */ 1, "\323", /* 212 */ 1, "\324", /* 213 */ 1, "\325", /* 214 */ 1, "\326", /* 215 */ 1, "\327", /* 216 */ 1, "\330", /* 217 */ 1, "\331", /* 218 */ 1, "\332", /* 219 */ 1, "\333", /* 220 */ 1, "\334", /* 221 */ 1, "\335", /* 222 */ 1, "\336", /* 223 */ 1, "\337", /* 224 */ 1, "\340", /* 225 */ 1, "\341", /* 226 */ 1, "\342", /* 227 */ 1, "\343", /* 228 */ 1, "\344", /* 229 */ 1, "\345", /* 230 */ 1, "\346", /* 231 */ 1, "\347", /* 232 */ 1, "\350", /* 233 */ 1, "\351", /* 234 */ 1, "\352", /* 235 */ 1, "\353", /* 236 */ 1, "\354", /* 237 */ 1, "\355", /* 238 */ 1, "\356", /* 239 */ 1, "\357", /* 240 */ 1, "\360", /* 241 */ 1, "\361", /* 242 */ 1, "\362", /* 243 */ 1, "\363", /* 244 */ 1, "\364", /* 245 */ 1, "\365", /* 246 */ 1, "\366", /* 247 */ 1, "\367", /* 248 */ 1, "\370", /* 249 */ 1, "\371", /* 250 */ 1, "\372", /* 251 */ 1, "\373", /* 252 */ 1, "\374", /* 253 */ 1, "\375", /* 254 */ 1, "\376", /* 255 */ 1, "\377" #else /* 160 */ 5, "[160]", /* 161 */ 5, "[161]", /* 162 */ 5, "[162]", /* 163 */ 5, "[163]", /* 164 */ 5, "[164]", /* 165 */ 5, "[165]", /* 166 */ 5, "[166]", /* 167 */ 5, "[167]", /* 168 */ 5, "[168]", /* 169 */ 5, "[169]", /* 170 */ 5, "[170]", /* 171 */ 5, "[171]", /* 172 */ 5, "[172]", /* 173 */ 5, "[173]", /* 174 */ 5, "[174]", /* 175 */ 5, "[175]", /* 176 */ 5, "[176]", /* 177 */ 5, "[177]", /* 178 */ 5, "[178]", /* 179 */ 5, "[179]", /* 180 */ 5, "[180]", /* 181 */ 5, "[181]", /* 182 */ 5, "[182]", /* 183 */ 5, "[183]", /* 184 */ 5, "[184]", /* 185 */ 5, "[185]", /* 186 */ 5, "[186]", /* 187 */ 5, "[187]", /* 188 */ 5, "[188]", /* 189 */ 5, "[189]", /* 190 */ 5, "[190]", /* 191 */ 5, "[191]", /* 192 */ 5, "[192]", /* 193 */ 5, "[193]", /* 194 */ 5, "[194]", /* 195 */ 5, "[195]", /* 196 */ 5, "[196]", /* 197 */ 5, "[197]", /* 198 */ 5, "[198]", /* 199 */ 5, "[199]", /* 200 */ 5, "[200]", /* 201 */ 5, "[201]", /* 202 */ 5, "[202]", /* 203 */ 5, "[203]", /* 204 */ 5, "[204]", /* 205 */ 5, "[205]", /* 206 */ 5, "[206]", /* 207 */ 5, "[207]", /* 208 */ 5, "[208]", /* 209 */ 5, "[209]", /* 210 */ 5, "[210]", /* 211 */ 5, "[211]", /* 212 */ 5, "[212]", /* 213 */ 5, "[213]", /* 214 */ 5, "[214]", /* 215 */ 5, "[215]", /* 216 */ 5, "[216]", /* 217 */ 5, "[217]", /* 218 */ 5, "[218]", /* 219 */ 5, "[219]", /* 220 */ 5, "[220]", /* 221 */ 5, "[221]", /* 222 */ 5, "[222]", /* 223 */ 5, "[223]", /* 224 */ 5, "[224]", /* 225 */ 5, "[225]", /* 226 */ 5, "[226]", /* 227 */ 5, "[227]", /* 228 */ 5, "[228]", /* 229 */ 5, "[229]", /* 230 */ 5, "[230]", /* 231 */ 5, "[231]", /* 232 */ 5, "[232]", /* 233 */ 5, "[233]", /* 234 */ 5, "[234]", /* 235 */ 5, "[235]", /* 236 */ 5, "[236]", /* 237 */ 5, "[237]", /* 238 */ 5, "[238]", /* 239 */ 5, "[239]", /* 240 */ 5, "[240]", /* 241 */ 5, "[241]", /* 242 */ 5, "[242]", /* 243 */ 5, "[243]", /* 244 */ 5, "[244]", /* 245 */ 5, "[245]", /* 246 */ 5, "[246]", /* 247 */ 5, "[247]", /* 248 */ 5, "[248]", /* 249 */ 5, "[249]", /* 250 */ 5, "[250]", /* 251 */ 5, "[251]", /* 252 */ 5, "[252]", /* 253 */ 5, "[253]", /* 254 */ 5, "[254]", /* 255 */ 5, "[255]" #endif }; SHAR_EOF cat << \SHAR_EOF > cmdline.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" static char *altfile = NULL; /* alternate file */ static int altline; /* line # in alternate file */ static char *nowrtmsg = "No write since last change (use ! to override)"; extern char **files; /* used for "n" and "rew" */ extern int curfile; extern int numfiles; /* * The next two variables contain the bounds of any range given in a command. * If no range was given, both contain null line pointers. If only a single * line was given, u_pos will contain a null line pointer. */ static LPTR l_pos, u_pos; static bool_t interactive; /* TRUE if we're reading a real command line */ static bool_t doecmd(); static void badcmd(), doshell(), get_range(); static LPTR *get_line(); #ifdef MEGAMAX overlay "cmdline" #endif /* * readcmdline() - accept a command line starting with ':', '/', or '?' * * readcmdline() accepts and processes colon commands and searches. If 'cmdline' * is null, the command line is read here. Otherwise, cmdline points to a * complete command line that should be used. This is used in main() to * handle initialization commands in the environment variable "EXINIT". */ void readcmdline(firstc, cmdline) char firstc; /* either ':', '/', or '?' */ char *cmdline; /* optional command string */ { char c; char buff[CMDBUFFSIZE]; char *p, *q, *cmd, *arg; bool_t literal_next_flag = FALSE; /* * Clear the range variables. */ l_pos.linep = (LINE *) NULL; u_pos.linep = (LINE *) NULL; interactive = (cmdline == NULL); if (interactive) gotocmdline(YES, firstc); p = buff; if (firstc != ':') *p++ = firstc; if (interactive) { /* collect the command string, handling '\b' and @ */ for (;;) { c = vgetc(); if (c == CTRL('V') && !literal_next_flag) { literal_next_flag = TRUE; outchar('^'); continue; } if (c == '\n' || ((c == '\r' || c == ESC) && (!literal_next_flag))) break; if ((c == '\b') && (!literal_next_flag)) { if (p > buff) { p--; /* * this is gross, but it relies only on 'gotocmdline' */ gotocmdline(YES, firstc == ':' ? ':' : NUL); for (q = buff; q < p; q++) outstr(chars[*q].ch_str); } else { msg(""); return; /* back to cmd mode */ } continue; } if ((c == '@') && (!literal_next_flag)) { p = buff; gotocmdline(YES, firstc); continue; } if (literal_next_flag) { literal_next_flag = FALSE; outchar('\b'); } outstr(chars[c].ch_str); *p++ = c; } *p = '\0'; } else { if (strlen(cmdline) > CMDBUFFSIZE - 2) /* should really do something * better here... */ return; strcpy(p, cmdline); } /* skip any initial white space */ for (cmd = buff; *cmd != NUL && isspace(*cmd); cmd++); /* search commands */ c = *cmd; if (c == '/' || c == '?') { cmd++; /* was the command was '//' or '??' (I.E. repeat last search) */ if ((*cmd == c) || (*cmd == NUL)) { if (c == '/') searchagain(FORWARD); else searchagain(BACKWARD); return; } /* If there is a matching '/' or '?' at the end, toss it */ p = strchr(cmd, NUL); if (*(p - 1) == c && *(p - 2) != '\\') *(p - 1) = NUL; dosearch((c == '/') ? FORWARD : BACKWARD, cmd); return; } /* * Parse a range, if present (and update the cmd pointer). */ get_range(&cmd); /* isolate the command and find any argument */ for (p = cmd; *p != NUL && !isspace(*p); p++); if (*p == NUL) arg = NULL; else { *p = NUL; for (p++; *p != NUL && isspace(*p); p++); arg = p; if (*arg == NUL) arg = NULL; } if (strcmp(cmd, "q!") == 0) getout(0); if (strcmp(cmd, "q") == 0) { if (Changed) emsg(nowrtmsg); else getout(0); return; } if (strcmp(cmd, "w") == 0) { if (arg == NULL) { if (Filename != NULL) { writeit(Filename, &l_pos, &u_pos); UNCHANGED; } else emsg("No output file"); } else writeit(arg, &l_pos, &u_pos); return; } if (strcmp(cmd, "wq") == 0) { if (Filename != NULL) { if (writeit(Filename, (LPTR *) NULL, (LPTR *) NULL)) getout(0); } else emsg("No output file"); return; } if (strcmp(cmd, "x") == 0) { if (Changed) { if (Filename != NULL) { if (!writeit(Filename, (LPTR *) NULL, (LPTR *) NULL)) return; } else { emsg("No output file"); return; } } getout(0); } if (strcmp(cmd, "f") == 0 && arg == NULL) { fileinfo(); return; } if (*cmd == 'n') { if ((curfile + 1) < numfiles) { /* * stuff ":e[!] FILE\n" */ stuffReadbuff(":e"); if (cmd[1] == '!') stuffReadbuff("!"); stuffReadbuff(" "); stuffReadbuff(files[++curfile]); stuffReadbuff("\n"); } else emsg("No more files!"); return; } if (*cmd == 'p') { if (curfile > 0) { /* * stuff ":e[!] FILE\n" */ stuffReadbuff(":e"); if (cmd[1] == '!') stuffReadbuff("!"); stuffReadbuff(" "); stuffReadbuff(files[--curfile]); stuffReadbuff("\n"); } else emsg("No more files!"); return; } if (strncmp(cmd, "rew", 3) == 0) { if (numfiles <= 1) /* nothing to rewind */ return; curfile = 0; /* * stuff ":e[!] FILE\n" */ stuffReadbuff(":e"); if (cmd[3] == '!') stuffReadbuff("!"); stuffReadbuff(" "); stuffReadbuff(files[0]); stuffReadbuff("\n"); return; } if (strcmp(cmd, "e") == 0 || strcmp(cmd, "e!") == 0) { doecmd(arg, cmd[1] == '!'); return; } if (strcmp(cmd, "f") == 0) { Filename = strsave(arg); filemess(""); return; } if (strcmp(cmd, "r") == 0 || strcmp(cmd, ".r") == 0) { if (arg == NULL) { badcmd(); return; } if (readfile(arg, Curschar, 1)) { emsg("Can't open file"); return; } updateNextscreen(); CHANGED; return; } if (strcmp(cmd, ".=") == 0) { smsg("line %d", cntllines(Filemem, Curschar)); return; } if (strcmp(cmd, "$=") == 0) { smsg("%d", cntllines(Filemem, Fileend) - 1); return; } if (strncmp(cmd, "ta", 2) == 0) { dotag(arg, cmd[2] == '!'); return; } if (strcmp(cmd, "set") == 0) { doset(arg, interactive); return; } if (strcmp(cmd, "help") == 0) { if (help()) { screenclear(); updateNextscreen(); } return; } if (strcmp(cmd, "version") == 0) { extern char *Version; msg(Version); return; } if (strcmp(cmd, "sh") == 0) { doshell(); return; } /* * If we got a line, but no command, then go to the line. */ if (*cmd == NUL && l_pos.linep != NULL) { *Curschar = l_pos; cursupdate(); return; } badcmd(); } /* * get_range - parse a range specifier * * Ranges are of the form: * * addr[,addr] * * where 'addr' is: * * $ [+-NUM] 'x [+-NUM] (where x denotes a currently defined mark) * . [+-NUM] NUM * * The pointer *cp is updated to point to the first character following the * range spec. If an initial address is found, but no second, the upper bound * is equal to the lower. */ static void get_range(cp) char **cp; { LPTR *l; char *p; if ((l = get_line(cp)) == NULL) return; l_pos = *l; for (p = *cp; *p != NUL && isspace(*p); p++); *cp = p; if (*p != ',') { /* is there another line spec ? */ u_pos = l_pos; return; } *cp = ++p; if ((l = get_line(cp)) == NULL) { u_pos = l_pos; return; } u_pos = *l; } static LPTR * get_line(cp) char **cp; { static LPTR pos; LPTR *lp; char *p, c; int lnum; pos.index = 0; /* shouldn't matter... check back later */ p = *cp; /* * Determine the basic form, if present. */ switch (c = *p++) { case '$': pos.linep = Fileend->linep->prev; break; case '.': pos.linep = Curschar->linep; break; case '\'': if ((lp = getmark(*p++)) == NULL) { emsg("Unknown mark"); return (LPTR *) NULL; } pos = *lp; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (lnum = c - '0'; isdigit(*p); p++) lnum = (lnum * 10) + (*p - '0'); if (lnum == 0) lnum = 1; pos = *gotoline(lnum); break; default: return (LPTR *) NULL; } while (*p != NUL && isspace(*p)) p++; if (*p == '-' || *p == '+') { bool_t neg = (*p++ == '-'); for (lnum = 0; isdigit(*p); p++) lnum = (lnum * 10) + (*p - '0'); if (neg) lnum = -lnum; pos = *gotoline(cntllines(Filemem, &pos) + lnum); } *cp = p; return &pos; } static void badcmd() { if (interactive) emsg("Unrecognized command"); } /* * dotag(tag, force) - goto tag */ void dotag(tag, force) char *tag; bool_t force; { FILE *tp, *fopen(); char lbuf[LSIZE]; char *fname, *str; if ((tp = fopen("tags", "r")) == NULL) { emsg("Can't open tags file"); return; } while (fgets(lbuf, LSIZE, tp) != NULL) { if ((fname = strchr(lbuf, TAB)) == NULL) { emsg("Format error in tags file"); return; } *fname++ = '\0'; if ((str = strchr(fname, TAB)) == NULL) { emsg("Format error in tags file"); return; } *str++ = '\0'; if (strcmp(lbuf, tag) == 0) { if (doecmd(fname, force)) { stuffReadbuff(str); /* str has \n at end */ stuffReadbuff("\007"); /* CTRL('G') */ fclose(tp); return; } } } emsg("tag not found"); fclose(tp); } static bool_t doecmd(arg, force) char *arg; bool_t force; { int line = 1; /* line # to go to in new file */ if (!force && Changed) { emsg(nowrtmsg); return FALSE; } if (arg != NULL) { /* * First detect a ":e" on the current file. This is mainly for ":ta" * commands where the destination is within the current file. */ if (Filename != NULL && strcmp(arg, Filename) == 0) { if (!Changed || (Changed && !force)) return TRUE; } if (strcmp(arg, "#") == 0) { /* alternate */ char *s = Filename; if (altfile == NULL) { emsg("No alternate file"); return FALSE; } Filename = altfile; altfile = s; line = altline; altline = cntllines(Filemem, Curschar); } else { altfile = Filename; altline = cntllines(Filemem, Curschar); Filename = strsave(arg); } } if (Filename == NULL) { emsg("No filename"); return FALSE; } /* clear mem and read file */ freeall(); filealloc(); UNCHANGED; readfile(Filename, Filemem, 0); *Topchar = *Curschar; if (line != 1) { stuffnumReadbuff(line); stuffReadbuff("G"); } setpcmark(); updateNextscreen(); return TRUE; } static void doshell() { char *sh, *getenv(); sh = getenv("SHELL"); if (sh == NULL) { emsg("Shell variable not set"); return; } gotocmdline(YES, NUL); if (system(sh) < 0) { emsg("Exec failed"); return; } wait_return(); } void gotocmdline(clr, firstc) bool_t clr; char firstc; { windgoto(Rows - 1, 0); if (clr) outstr(T_EL); /* clear the bottom line */ if (firstc) outchar(firstc); } /* * msg(s) - displays the string 's' on the status line */ void msg(s) char *s; { gotocmdline(YES, NUL); outstr(s); #ifdef AMIGA fflush(stdout); #endif #ifdef BSD fflush(stdout); #endif } /* VARARGS */ void smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9) char *s; int a1, a2, a3, a4, a5, a6, a7, a8, a9; { char sbuf[MAX_COLUMNS + 1]; sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9); msg(sbuf); } /* * emsg() - display an error message * * Rings the bell, if appropriate, and calls message() to do the real work */ void emsg(s) char *s; { UndoInProgress = FALSE; RedrawingDisabled = FALSE; ResetBuffers(); if (P(P_EB)) beep(); outstr(T_TI); msg(s); outstr(T_TP); #ifdef AMIGA fflush(stdout); #endif #ifdef BSD fflush(stdout); #endif } void wait_return() { char c; outstr("Press RETURN to continue"); do { c = vgetc(); } while (c != '\r' && c != '\n'); screenclear(); updateNextscreen(); } SHAR_EOF cat << \SHAR_EOF > dec.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * dec(p) * * Decrement the line pointer 'p' crossing line boundaries as necessary. Return * 1 when crossing a line, -1 when at start of file, 0 otherwise. */ int dec(lp) LPTR *lp; { if (lp->index > 0) { /* still within line */ lp->index--; return 0; } if (lp->linep->prev != NULL) { /* there is a prior line */ lp->linep = lp->linep->prev; lp->index = strlen(lp->linep->s); return 1; } return -1; /* at start of file */ } SHAR_EOF cat << \SHAR_EOF > edit.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * This flag is used to make auto-indent work right on lines where only a * <RETURN> or <ESC> is typed. It is set when an auto-indent is done, and * reset when any other editting is done on the line. If an <ESC> or <RETURN> * is received, and did_ai is TRUE, the line is truncated. */ bool_t did_ai = FALSE; void edit() { char c; bool_t literal_next_flag = FALSE; Prenum = 0; /* position the display and the cursor at the top of the file. */ *Topchar = *Filemem; *Curschar = *Filemem; Cursrow = Curscol = 0; for (;;) { if (!RedrawingDisabled) { cursupdate(); /* Figure out where the cursor is based on * Curschar. */ if (MustRedrawLine) redrawline(); if (MustRedrawScreen) updateRealscreen(); windgoto(Cursrow, Curscol); } c = vgetc(); if (State == 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'); continue; } /* execute the command */ normal(c); Prenum = 0; } else { if (c == CTRL('V') && !literal_next_flag) { literal_next_flag = TRUE; outchar('^'); continue; } if (literal_next_flag) { literal_next_flag = FALSE; outchar('\b'); if (c != NL) { did_ai = FALSE; insertchar(c); continue; } } switch (c) { /* We're in insert mode */ case CR: case NL: *Insbuffptr++ = NL; *Insbuffptr = NUL; if (!opencmd(FORWARD, TRUE)) goto doESCkey; /* out of memory */ if (!RedrawingDisabled) windgoto(Cursrow, Curscol); break; case ESC: /* an escape ends input mode */ doESCkey: set_want_col = TRUE; /* Don't end up on a '\n' if you can help it. */ if (gchar(Curschar) == NUL && Curschar->index != 0) dec(Curschar); /* * The cursor should end up on the last inserted character. * This is an attempt to match the real 'vi', but it may not * be quite right yet. */ if (Curschar->index != 0 && !endofline(Curschar)) dec(Curschar); State = NORMAL; msg(""); if (!UndoInProgress) { int n; char *p; if (last_command == 'o') AppendToUndobuff(UNDO_SHIFTJ_STR); if (Insbuff != Insbuffptr) { if (last_command == 'O') AppendToUndobuff("0"); AppendToRedobuff(Insbuff); AppendToUndoUndobuff(Insbuff); n = 0; for (p = Insbuff; p < Insbuffptr; p++) { if (*p == NL) { if (n) { AppendNumberToUndobuff(n); AppendToUndobuff("dl"); n = 0; } AppendToUndobuff(UNDO_SHIFTJ_STR); } else n++; } if (n) { AppendNumberToUndobuff(n); AppendToUndobuff("dl"); } } if (last_command == 'c') { AppendToUndobuff(mkstr(last_command_char)); AppendToUndobuff(Yankbuff); AppendToUndobuff(ESC_STR); } AppendToRedobuff(ESC_STR); AppendToUndoUndobuff(ESC_STR); if (last_command == 'O') AppendToUndobuff(UNDO_SHIFTJ_STR); } break; case CTRL('D'): /* * Control-D is treated as a backspace in insert mode to make * auto-indent easier. This isn't completely compatible with * vi, but it's a lot easier than doing it exactly right, and * the difference isn't very noticeable. */ case BS: /* can't backup past starting point */ if (Curschar->linep == Insstart->linep && Curschar->index <= Insstart->index) { beep(); break; } /* can't backup to a previous line */ if (Curschar->linep != Insstart->linep && Curschar->index <= 0) { beep(); break; } did_ai = FALSE; dec(Curschar); delchar(TRUE, FALSE); Insbuffptr--; *Insbuffptr = NUL; cursupdate(); updateline(); break; default: did_ai = FALSE; insertchar(c); break; } } } } /* * Special characters in this context are those that need processing other * than the simple insertion that can be performed here. This includes ESC * which terminates the insert, and CR/NL which need special processing to * open up a new line. This routine tries to optimize insertions performed by * the "redo" command, so it needs to know when it should stop and defer * processing to the "normal" mechanism. */ #define ISSPECIAL(c) ((c) == NL || (c) == CR || (c) == ESC) void insertchar(c) char c; { if (anyinput()) { /* If there's any pending input, grab it all * at once. */ char *p; p = Insbuffptr; *Insbuffptr++ = c; for (c = vpeekc(); !ISSPECIAL(c) && anyinput(); c = vpeekc()) { c = vgetc(); *Insbuffptr++ = c; /* * The following kludge avoids overflowing the insert buffer. */ if (Insbuffptr + 10 >= &Insbuff[INSERT_SIZE]) { int n; *Insbuffptr = NUL; insstr(p); Insbuffptr = Insbuff; p = Insbuffptr; emsg("Insert buffer overflow - buffers flushed"); sleep(2); n = cntllines(Filemem, Curschar); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); if (endofline(Curschar)) { AppendToRedobuff("a"); AppendToUndoUndobuff("a"); } else { AppendToRedobuff("i"); AppendToUndoUndobuff("i"); } } } *Insbuffptr = NUL; insstr(p); } else { inschar(c); *Insbuffptr++ = c; /* * The following kludge avoids overflowing the insert buffer. */ if (Insbuffptr + 10 >= &Insbuff[INSERT_SIZE]) { int n; Insbuffptr = Insbuff; emsg("Insert buffer overflow - buffers flushed"); sleep(2); n = cntllines(Filemem, Curschar); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); if (endofline(Curschar)) { AppendToRedobuff("a"); AppendToUndoUndobuff("a"); } else { AppendToRedobuff("i"); AppendToUndoUndobuff("i"); } } *Insbuffptr = NUL; } updateline(); } void getout(r) int r; { windgoto(Rows - 1, 0); putchar('\r'); putchar('\n'); windexit(r); } void scrolldown(nlines) int nlines; { LPTR *p; int done = 0; /* total # of physical lines done */ /* Scroll up 'nlines' lines. */ while (nlines--) { if ((p = prevline(Topchar)) == NULL) break; done += plines(p); *Topchar = *p; if (Curschar->linep == Botchar->linep->prev) *Curschar = *prevline(Curschar); } s_ins(0, done); } void scrollup(nlines) int nlines; { LPTR *p; int done = 0; /* total # of physical lines done */ int pl; /* # of plines for the current line */ /* Scroll down 'nlines' lines. */ while (nlines--) { pl = plines(Topchar); if ((p = nextline(Topchar)) == NULL) break; done += pl; if (Curschar->linep == Topchar->linep) *Curschar = *p; *Topchar = *p; } s_del(0, done); } /* * oneright oneleft onedown oneup * * Move one char {right,left,down,up}. Return TRUE when sucessful, FALSE when * we hit a boundary (of a line, or the file). */ bool_t oneright() { set_want_col = TRUE; switch (inc(Curschar)) { case 0: return TRUE; case 1: dec(Curschar); /* crossed a line, so back up */ /* FALLTHROUGH */ case -1: return FALSE; } return FALSE; /* PARANOIA: should never reach here */ } bool_t oneleft() { set_want_col = TRUE; switch (dec(Curschar)) { case 0: return TRUE; case 1: inc(Curschar); /* crossed a line, so back up */ /* FALLTHROUGH */ case -1: return FALSE; } return FALSE; /* PARANOIA: should never reach here */ } void beginline(flag) bool_t flag; { while (oneleft()); if (flag) { while (isspace(gchar(Curschar)) && oneright()); } set_want_col = TRUE; } bool_t oneup(n) { LPTR p, *np; int k; 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 FALSE; } p = *np; } *Curschar = p; cursupdate(); /* make sure Topchar is valid */ /* try to advance to the column we want to be at */ *Curschar = *coladvance(&p, Curswant); return TRUE; } bool_t onedown(n) { LPTR 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 FALSE; } p = *np; } cursupdate(); /* make sure Topchar is valid */ /* try to advance to the column we want to be at */ *Curschar = *coladvance(&p, Curswant); return TRUE; } SHAR_EOF cat << \SHAR_EOF > fileio.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" void filemess(s) char *s; { sprintf(IObuff, "\"%s\" %s", ((Filename == NULL) ? "" : Filename), s); msg(IObuff); } void renum() { LPTR *p; unsigned int l = 0; for (p = Filemem; p != NULL; p = nextline(p), l += LINEINC) p->linep->num = l; Fileend->linep->num = 0xffff; } #ifdef MEGAMAX overlay "fileio" #endif bool_t readfile(fname, fromp, nochangename) char *fname; LPTR *fromp; bool_t nochangename; /* if TRUE, don't change the Filename */ { FILE *f, *fopen(); LINE *curr; char *p; int i, c; long nchars; int unprint = 0; int linecnt = 0; bool_t wasempty = bufempty(); curr = fromp->linep; if (!nochangename) Filename = strsave(fname); if ((f = fopen(fname, "r")) == NULL) return TRUE; filemess(""); i = 0; for (nchars = 0; (c = getc(f)) != EOF; nchars++) { if (c >= 0x80) { c -= 0x80; unprint++; } /* * Nulls are special, so they can't show up in the file. We should * count nulls seperate from other nasties, but this is okay for now. */ if (c == NUL) { unprint++; continue; } if (c == '\n') { /* process the completed line */ int len; LINE *lp; IObuff[i] = NUL; len = strlen(IObuff) + 1; if ((lp = newline(len)) == NULL) { fprintf(stderr, "not enough memory - should never happen"); getout(1); } strcpy(lp->s, IObuff); curr->next->prev = lp; /* new line to next one */ lp->next = curr->next; curr->next = lp; /* new line to prior one */ lp->prev = curr; curr = lp; /* new line becomes current */ i = 0; linecnt++; } else IObuff[i++] = (char) c; } fclose(f); /* * If the buffer was empty when we started, we have to go back and remove * the "dummy" line at Filemem and patch up the ptrs. */ if (wasempty) { LINE *dummy = Filemem->linep; /* dummy line ptr */ free(dummy->s); /* free string space */ Filemem->linep = Filemem->linep->next; free((char *) dummy); /* free LINE struct */ Filemem->linep->prev = NULL; Curschar->linep = Filemem->linep; Topchar->linep = Filemem->linep; } if (unprint > 0) p = "\"%s\" %d lines, %ld characters (%d un-printable))"; else p = "\"%s\" %d lines, %ld characters"; sprintf(IObuff, p, fname, linecnt, nchars, unprint); msg(IObuff); renum(); return FALSE; } /* * writeit - write to file 'fname' lines 'start' through 'end' * * If either 'start' or 'end' contain null line pointers, the default is to use * the start or end of the file respectively. */ bool_t writeit(fname, start, end) char *fname; LPTR *start, *end; { FILE *f, *fopen(); FILE *fopenb(); /* open in binary mode, where needed */ char backup[16], *s; long nchars; int lines; LPTR *p; sprintf(IObuff, "\"%s\"", fname); msg(IObuff); /* * Form the backup file name - change foo.* to foo.bak */ strcpy(backup, fname); for (s = backup; *s && *s != '.'; s++); *s = NUL; strcat(backup, ".bak"); /* * Delete any existing backup and move the current version to the backup. * For safety, we don't remove the backup until the write has finished * successfully. And if the 'backup' option is set, leave it around. */ rename(fname, backup); f = P(P_CR) ? fopen(fname, "w") : fopenb(fname, "w"); if (f == NULL) { emsg("Can't open file for writing!"); return FALSE; } /* * If we were given a bound, start there. Otherwise just start at the * beginning of the file. */ if (start == NULL || start->linep == NULL) p = Filemem; else p = start; lines = 0; nchars = 0; do { fprintf(f, "%s\n", p->linep->s); nchars += strlen(p->linep->s) + 1; lines++; /* * If we were given an upper bound, and we just did that line, then * bag it now. */ if (end != NULL && end->linep != NULL) { if (end->linep == p->linep) break; } } while ((p = nextline(p)) != NULL); fclose(f); sprintf(IObuff, "\"%s\" %d lines, %ld characters", fname, lines, nchars); msg(IObuff); UNCHANGED; /* * Remove the backup unless they want it left around */ if (!P(P_BK)) remove(backup); return TRUE; } SHAR_EOF cat << \SHAR_EOF > help.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" char *Version = "STEVIE - Version 3.10a"; static int helprow; #ifdef HELP #ifdef MEGAMAX overlay "help" #endif static void longline(); bool_t help() { /*********************************************************************** * First Screen: Positioning within file, Adjusting the Screen ***********************************************************************/ outstr(T_ED); windgoto(helprow = 0, 0); longline("\ Positioning within file\n\ =======================\n\ ^F Forward screenfull Worked on by:\n\ ^B Backward screenfull Tim Thompson\n"); longline("\ ^D scroll down half screen Tony Andrews\n\ ^U scroll up half screen G.R. (Fred) Walter\n"); longline("\ G Goto line (end default)\n\ ]] next function\n\ [[ previous function\n\ /re next occurence of regular expression 're'\n"); longline("\ ?re prior occurence of regular expression 're'\n\ n repeat last / or ?\n\ N reverse last / or ?\n\ % find matching (, ), {, }, [, or ]\n"); longline("\ \n\ Adjusting the screen\n\ ====================\n\ ^L Redraw the screen\n\ ^E scroll window down 1 line\n\ ^Y scroll window up 1 line\n"); longline("\ z<RETURN> redraw, current line at top\n\ z- ... at bottom\n\ z. ... at center\n"); windgoto(0, 52); longline(Version); windgoto(helprow = Rows - 2, 47); longline("<Press space bar to continue>\n"); windgoto(helprow = Rows - 1, 47); longline("<Any other key will quit>"); if (vgetc() != ' ') return TRUE; /*********************************************************************** * Second Screen: Character positioning ***********************************************************************/ outstr(T_ED); windgoto(helprow = 0, 0); longline("\ Character Positioning\n\ =====================\n\ ^ first non-white\n\ 0 beginning of line\n\ $ end of line\n\ h backward\n"); longline("\ l forward\n\ ^H same as h\n\ space same as l\n\ fx find 'x' forward\n"); longline("\ Fx find 'x' backward\n\ tx upto 'x' forward\n\ Tx upto 'x' backward\n\ ; Repeat last f, F, t, or T\n"); longline("\ , inverse of ;\n\ | to specified column\n\ % find matching (, ), {, }, [, or ]\n"); windgoto(helprow = Rows - 2, 47); longline("<Press space bar to continue>\n"); windgoto(helprow = Rows - 1, 47); longline("<Any other key will quit>"); if (vgetc() != ' ') return TRUE; /*********************************************************************** * Third Screen: Line Positioning, Marking and Returning ***********************************************************************/ outstr(T_ED); windgoto(helprow = 0, 0); longline("\ Line Positioning\n\ =====================\n\ H home window line\n\ L last window line\n\ M middle window line\n"); longline("\ + next line, at first non-white\n\ - previous line, at first non-white\n\ CR return, same as +\n\ j next line, same column\n\ k previous line, same column\n"); longline("\ \n\ Marking and Returning\n\ =====================\n\ `` previous context\n\ '' ... at first non-white in line\n"); longline("\ mx mark position with letter 'x'\n\ `x to mark 'x'\n\ 'x ... at first non-white in line\n"); windgoto(helprow = Rows - 2, 47); longline("<Press space bar to continue>\n"); windgoto(helprow = Rows - 1, 47); longline("<Any other key will quit>"); if (vgetc() != ' ') return TRUE; /*********************************************************************** * Fourth Screen: Insert & Replace, ***********************************************************************/ outstr(T_ED); windgoto(helprow = 0, 0); longline("\ Insert and Replace\n\ ==================\n\ a append after cursor\n\ i insert before cursor\n\ A append at end of line\n\ I insert before first non-blank\n"); longline("\ o open line below\n\ O open line above\n\ rx replace single char with 'x'\n\ R replace characters (not yet)\n\ ~ replace character under cursor with other case\n"); longline("\ \n\ Words, sentences, paragraphs\n\ ============================\n\ w word forward\n\ b back word\n\ e end of word\n\ ) to next sentence (not yet)\n\ } to next paragraph (not yet)\n"); longline("\ ( back sentence (not yet)\n\ { back paragraph (not yet)\n\ W blank delimited word\n\ B back W\n\ E to end of W"); windgoto(helprow = Rows - 2, 47); longline("<Press space bar to continue>\n"); windgoto(helprow = Rows - 1, 47); longline("<Any other key will quit>"); if (vgetc() != ' ') return TRUE; /*********************************************************************** * Fifth Screen: Misc. operations, ***********************************************************************/ outstr(T_ED); windgoto(helprow = 0, 0); longline("\ Undo & Redo\n\ =============\n\ u undo last change\n\ U restore current line (not yet)\n\ . repeat last change\n"); longline("\ \n\ File manipulation\n\ =================\n"); longline("\ :w write back changes\n\ :wq write and quit\n\ :x write if modified, and quit\n\ :q quit\n\ :q! quit, discard changes\n\ :e name edit file 'name'\n"); longline("\ :e! reedit, discard changes\n\ :e # edit alternate file\n\ :w name write file 'name'\n"); longline("\ :n edit next file in arglist\n\ :n args specify new arglist (not yet)\n\ :rew rewind arglist\n\ :f show current file and lines\n"); longline("\ :f file change current file name\n\ :ta tag to tag file entry 'tag'\n\ ^] :ta, current word is tag"); windgoto(helprow = Rows - 2, 47); longline("<Press space bar to continue>\n"); windgoto(helprow = Rows - 1, 47); longline("<Any other key will quit>"); if (vgetc() != ' ') return TRUE; /*********************************************************************** * Sixth Screen: Operators, Misc. operations, Yank & Put ***********************************************************************/ outstr(T_ED); windgoto(helprow = 0, 0); longline("\ Operators (double to affect lines)\n\ ==================================\n\ d delete\n\ c change\n"); longline("\ < left shift\n\ > right shift\n\ y yank to buffer\n"); longline("\n\ Miscellaneous operations\n\ ========================\n\ C change rest of line\n\ D delete rest of line\n\ s substitute chars\n"); longline("\ S substitute lines (not yet)\n\ J join lines\n\ x delete characters\n\ X ... before cursor\n"); longline("\n\ Yank and Put\n\ ============\n\ p put back text\n\ P put before\n\ Y yank lines"); windgoto(helprow = Rows - 1, 47); longline("<Press any key>"); vgetc(); return TRUE; } static void longline(p) char *p; { # ifdef AMIGA outstr(p); # else char *s; for (s = p; *s; s++) { if (*s == '\n') windgoto(++helprow, 0); else outchar(*s); } # endif } #else bool_t help() { msg("Sorry, help not configured"); return FALSE; } #endif SHAR_EOF cat << \SHAR_EOF > inc.c /* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * inc(p) * * Increment the line pointer 'p' crossing line boundaries as necessary. Return * 1 when crossing a line, -1 when at end of file, 0 otherwise. */ int inc(lp) LPTR *lp; { char *p = &(lp->linep->s[lp->index]); if (*p != NUL) { /* still within line */ lp->index++; return ((p[1] != NUL) ? 0 : 1); } if (lp->linep->next != Fileend->linep) { /* there is a next line */ lp->index = 0; lp->linep = lp->linep->next; return 1; } return -1; } SHAR_EOF # End of shell archive exit 0 -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.