page@swan.ulowell.edu (Bob Page) (12/02/88)
Submitted-by: grwalter@watcgl.waterloo.edu Posting-number: Volume 2, Issue 84 Archive-name: editors/stevie.4 # 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: # bsd.c # bsd.h # makefile.amiga.lattice # makefile.bsd # makefile.os2 # makefile.tos # makefile.usg # os2.c # os2.h # porting.doc # tos.c # tos.h # unix.c # unix.h # normal.c # sendpacket.c # This archive created: Thu Dec 1 20:32:15 1988 cat << \SHAR_EOF > bsd.c /* * System-dependent routines for BSD 4.3 UNIX */ #include "stevie.h" #include <sgtty.h> /* * inchar() - get a character from the keyboard */ char inchar() { char c; fflush(stdout); /* flush any pending output */ c = (char) getchar(); return c; } void outstr(s) char *s; { while (*s) outchar(*s++); } void beep() { if (RedrawingDisabled) return; outchar('\007'); } void delay() { sleep(1); } static struct sgttyb ostate; void windinit() { char *getenv(); char *term; struct sgttyb nstate; term = getenv("TERM"); if (!term) { fprintf(stderr, "Invalid terminal type '%s'\n", term); exit(1); } if ((strncmp(term, "vt", 2) != 0) && (strncmp(term, "xterm", 5) != 0)) { fprintf(stderr, "Invalid terminal type '%s'\n", term); exit(1); } Columns = 80; P(P_LI) = Rows = 24; /* * Go into cbreak mode */ ioctl(1, (long) TIOCGETP, (char *) &ostate); nstate = ostate; nstate.sg_flags = nstate.sg_flags & ~(ECHO | CRMOD) | CBREAK; ioctl(1, (long) TIOCSETP, (char *) &nstate); } void windexit(r) int r; { fflush(stdout); ioctl(0, (long) TIOCSETP, (char *) &ostate); 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; { return fopen(fname, mode); } SHAR_EOF cat << \SHAR_EOF > bsd.h /* * BSD 4.3 Machine-dependent routines. */ char inchar(); #define outchar(c) putchar(c) void outstr(), beep(); #define remove(path) unlink(path) int rename(); void windinit(), windexit(), windgoto(); void delay(); SHAR_EOF cat << \SHAR_EOF > makefile.amiga.lattice # # 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 > makefile.bsd # # Makefile for BSD 4.3 UNIX # LIBS = ../regexp/regexp.o ../regexp/regsub.o LDFLAGS= CFLAGS = -I../regexp -pg -g -DDEBUG -DAUTO_INDENT LINTFLAGS = -I../regexp -DDEBUG -DAUTO_INDENT MACHOBJ= bsd.o MACHSRC= bsd.c SRC= main.c edit.c linefunc.c normal.c cmdline.c charset.c \ updateRealscreen.c misccmds.c help.c dec.c inc.c search.c alloc.c \ updateNextscreen.c mark.c screen.c fileio.c param.c $(MACHSRC) 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 $(MACHOBJ) all : stevie stevie : $(OBJ) $(CC) $(OBJ) $(LIBS) $(CFLAGS) -o stevie lint: lint $(LINTFLAGS) $(SRC) clean : rm -f *.out *.o core stevie *.BAK SHAR_EOF cat << \SHAR_EOF > makefile.os2 # # Makefile for OS/2 # # The make command with OS/2 is really stupid. # LIBS = ..\regexp\regexp.obj ..\regexp\regsub.obj # # Compact model lets us edit large files, but keep small model code # MODEL= -AC CFLAGS = $(MODEL) -I..\regexp MACH= os2.obj OBJ= main.obj edit.obj linefunc.obj normal.obj cmdline.obj charset.obj \ updateRealscreen.obj \ misccmds.obj help.obj dec.obj inc.obj search.obj alloc.obj \ updateNextscreen.obj mark.obj screen.obj fileio.obj param.obj $(MACH) main.obj: main.c cl -c $(CFLAGS) main.c updateRealscreen.obj: nexttoscreen.c cl -c $(CFLAGS) updateRealscreen.c alloc.obj : alloc.c cl -c $(CFLAGS) alloc.c edit.obj : edit.c cl -c $(CFLAGS) edit.c updateNextscreen.obj : filetonext.c cl -c $(CFLAGS) updateNextscreen.c linefunc.obj : linefunc.c cl -c $(CFLAGS) linefunc.c normal.obj : normal.c cl -c $(CFLAGS) normal.c cmdline.obj : cmdline.c cl -c $(CFLAGS) cmdline.c charset.obj : charset.c cl -c $(CFLAGS) charset.c misccmds.obj : misccmds.c cl -c $(CFLAGS) misccmds.c help.obj : help.c cl -c $(CFLAGS) help.c dec.obj : dec.c cl -c $(CFLAGS) dec.c inc.obj : inc.c cl -c $(CFLAGS) inc.c search.obj : search.c cl -c $(CFLAGS) search.c mark.obj : mark.c cl -c $(CFLAGS) mark.c screen.obj : screen.c cl -c $(CFLAGS) screen.c fileio.obj : fileio.c cl -c $(CFLAGS) fileio.c param.obj : param.c cl -c $(CFLAGS) param.c os2.obj : os2.c cl -c $(CFLAGS) os2.c stevie.exe : $(OBJ) cl $(MODEL) *.obj $(LIBS) -o stevie.exe copy stevie.exe rstevie.exe bind rstevie.exe \lib\api.lib \lib\doscalls.lib SHAR_EOF cat << \SHAR_EOF > makefile.tos # # Makefile for the Atari ST - Megamax C compiler # LIBS = \megamax\regexp.lib CFLAGS = -DMEGAMAX # Megamax rule .c.o: mmcc $(CFLAGS) $< mmimp $*.o mmlib rv vi.lib $*.o MACH= tos.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.ttp stevie.ttp : $(OBJ) $(LINKER) vi.lib $(LIBS) -o stevie.ttp clean : $(RM) $(OBJ) vi.lib SHAR_EOF cat << \SHAR_EOF > makefile.usg # # Makefile for UNIX (System V) # LIBS = ../regexp/regexp.a LDFLAGS= CFLAGS = -I../regexp -O MACH= unix.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) $(CC) $(OBJ) $(LIBS) -o stevie clean : rm $(OBJ) SHAR_EOF cat << \SHAR_EOF > os2.c /* * OS/2 System-dependent routines. */ #include "stevie.h" /* * inchar() - get a character from the keyboard */ char inchar() { int c; flushbuf(); /* flush any pending output */ c = getch(); if (c == EOF) /* EOF used like \n, so just assign it */ c = '\n'; return ((char) c); } #define BSIZE 2048 static char outbuf[BSIZE]; static int bpos = 0; flushbuf() { if (bpos != 0) write(1, outbuf, bpos); bpos = 0; } /* * Macro to output a character. Used within this file for speed. */ #define outone(c) outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf() /* * Function version for use outside this file. */ void outchar(c) char c; { outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf(); } void outstr(s) char *s; { while (*s) { outone(*s++); } } void beep() { if (RedrawingDisabled) return; outone('\007'); } sleep(n) int n; { extern far pascal DOSSLEEP(); DOSSLEEP(1000L * n); } void delay() { DOSSLEEP(500L); } void windinit() { Columns = 80; P(P_LI) = Rows = 25; } void windexit(r) int r; { flushbuf(); exit(r); } void windgoto(r, c) int r, c; { r += 1; c += 1; /* * Check for overflow once, to save time. */ if (bpos + 8 >= BSIZE) flushbuf(); outbuf[bpos++] = '\033'; outbuf[bpos++] = '['; if (r >= 10) outbuf[bpos++] = r / 10 + '0'; outbuf[bpos++] = r % 10 + '0'; outbuf[bpos++] = ';'; if (c >= 10) outbuf[bpos++] = c / 10 + '0'; outbuf[bpos++] = c % 10 + '0'; outbuf[bpos++] = '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 > os2.h /* * OS2 Machine-dependent routines. */ char inchar(); void outchar(); void outstr(), beep(); void windinit(), windexit(), windgoto(); void delay(); void sleep(); SHAR_EOF cat << \SHAR_EOF > porting.doc Release Notes for STEVIE - Version 3.10a Porting Tony Andrews - March 6, 1988 Porting the editor is a relatively simple task. Most of the code is pretty machine-independent. For each environment, there is a file of routines that perform various low-level operations that tend to vary a lot from one machine to another. Another file contains the escape sequences to be used for each machine. The machine-dependent files currently used are: tos.c: Atari ST - ifdef for either Megamax or Alcyon tos.h unix.c: UNIX System V unix.h os2.c: Microsoft OS/2 os2.h amiga.c: Amiga amiga.h bsd.c: BSD 4.3 UNIX bsd.h Each of these files are around 150 lines long and deal with low-level issues like character I/O to the terminal, terminal initialization, cursor addressing, and so on. There are different tradeoffs to be made depending on the environment. For example, the UNIX version buffers terminal output because of the relatively high overhead of system calls. A quick look at the files will make it clear what needs to be done in a new environment. Terminal escape sequences are in the file "term.h". These are defined statically, for the time being. There is some discussion in term.h regarding which sequences are optional and which are not. The editor is somewhat flexible in dealing with a lack of terminal capabilities. The character set is in the file "charset.c". Because not all C compilers support command line macro definitions, the #define's for system-specific macros are placed at the beginning of the file 'stevie.h'. If you port to a new system, add another line there to define the macro you choose for your port. The basic process for doing a new port is: 1. Come up with a macro name to use when ifdef'ing your system- specific changes. Add a line at the top of 'stevie.h' to define the macro name you've chosen. 2. Look at amiga.c, bsd.c, unix.c, tos.c, and os2.c and copy the one that comes closest to working on your system. Then modify your new file as needed. 3. Look at term.h and edit the file appropriately adding a new set of escape sequence definitions for your system. 4. If you haven't already, get a copy of Henry Spencer's regular expression library and compile it. This has been very simple every time I've done it. 5. Compiling and debug the editor. SHAR_EOF cat << \SHAR_EOF > tos.c /* * System-dependent routines for the Atari ST. */ #include "stevie.h" #include <osbind.h> /* * The following buffer is used to work around a bug in TOS. It appears that * unread console input can cause a crash, but only if console output is * going on. The solution is to always grab any unread input before putting * out a character. The following buffer holds any characters read in this * fashion. The problem can be easily produced because STEVIE can't yet keep * up with the normal auto-repeat rate in insert mode. */ #define IBUFSZ 128 static long inbuf[IBUFSZ]; /* buffer for unread input */ static long *inptr = inbuf; /* where to put next character */ /* * inchar() - get a character from the keyboard * * Certain special keys are mapped to values above 0x80. These mappings are * defined in keymap.h. If the key has a non-zero ascii value, it is simply * returned. Otherwise it may be a special key we want to map. * * The ST has a bug involving keyboard input that seems to occur when typing * quickly, especially typing capital letters. Sometimes a value of * 0x02540000 is read. This doesn't correspond to anything on the keyboard, * according to my documentation. My solution is to loop when any unknown key * is seen. Normally, the bell is rung to indicate the error. If the "bug" * value is seen, we ignore it completely. */ char inchar() { for (;;) { long c, *p; /* * Get the next input character, either from the input buffer or * directly from TOS. */ if (inptr != inbuf) { /* input in the buffer, use it */ c = inbuf[0]; /* * Shift everything else in the buffer down. This would be * cleaner if we used a circular buffer, but it really isn't * worth it. */ inptr--; for (p = inbuf; p < inptr; p++) *p = *(p + 1); } else c = Crawcin(); if ((c & 0xff) != 0) return ((char) c); switch ((int) (c >> 16) & 0xff) { case 0x62: return K_HELP; case 0x61: return K_UNDO; case 0x52: return K_INSERT; case 0x47: return K_HOME; case 0x48: return K_UARROW; case 0x50: return K_DARROW; case 0x4b: return K_LARROW; case 0x4d: return K_RARROW; case 0x29: return K_CGRAVE; /* control grave accent */ /* * Occurs due to a bug in TOS. */ case 0x54: break; /* * Add the function keys here later if we put in support for * macros. */ default: beep(); break; } } } /* * get_inchars - snarf away any pending console input * * If the buffer overflows, we discard what's left and ring the bell. */ static void get_inchars() { while (Cconis()) { if (inptr >= &inbuf[IBUFSZ]) { /* no room in buffer? */ Crawcin(); /* discard the input */ beep(); /* and sound the alarm */ } else *inptr++ = Crawcin(); } } void outchar(c) char c; { get_inchars(); Cconout(c); } void outstr(s) char *s; { get_inchars(); Cconws(s); } #define BGND 0 #define TEXT 3 /* * vbeep() - visual bell */ static void vbeep() { int text, bgnd; /* text and background colors */ long l; text = Setcolor(TEXT, -1); bgnd = Setcolor(BGND, -1); Setcolor(TEXT, bgnd); /* swap colors */ Setcolor(BGND, text); for (l = 0; l < 5000; l++); /* short pause */ Setcolor(TEXT, text); /* restore colors */ Setcolor(BGND, bgnd); } void beep() { if (RedrawingDisabled) return; if (P(P_VB)) vbeep(); else outchar('\007'); } /* * remove(file) - remove a file */ void remove(file) char *file; { Fdelete(file); } /* * rename(of, nf) - rename existing file 'of' to 'nf' */ void rename(of, nf) char *of, *nf; { Fdelete(nf); /* if 'nf' exists, remove it */ Frename(0, of, nf); } void windinit() { if (Getrez() == 0) Columns = 40; /* low resolution */ else Columns = 80; /* medium or high */ P(P_LI) = Rows = 25; Cursconf(1, NULL); } void windexit(r) int r; { exit(r); } void windgoto(r, c) int r, c; { outstr("\033Y"); outchar(r + 040); outchar(c + 040); } /* * System calls or library routines missing in TOS. */ void sleep(n) int n; { int k; k = Tgettime(); while (Tgettime() <= k + n); } void delay() { long n; for (n = 0; n < 8000; n++); } int system(cmd) char *cmd; { char arg[1]; arg[0] = (char) 0; /* no arguments passed to the shell */ if (Pexec(0, cmd, arg, 0L) < 0) return -1; else return 0; } #ifdef MEGAMAX char * strchr(s, c) char *s; int c; { do { if (*s == c) return (s); } while (*s++); return (NULL); } #endif #ifdef MEGAMAX FILE * fopenb(fname, mode) char *fname; char *mode; { char modestr[10]; sprintf(modestr, "b%s", mode); return fopen(fname, modestr); } #endif /* * getenv() - get a string from the environment * * Both Alcyon and Megamax are missing getenv(). This routine works for both * compilers and with the Beckemeyer and Gulam shells. With gulam, the * env_style variable should be set to either "mw" or "gu". */ char * getenv(name) char *name; { extern long _base; char *envp, *p; envp = *((char **) (_base + 0x2c)); for (; *envp; envp += strlen(envp) + 1) { if (strncmp(envp, name, strlen(name)) == 0) { p = envp + strlen(name); if (*p++ == '=') return p; } } return (char *) 0; } SHAR_EOF cat << \SHAR_EOF > tos.h /* * Atari Machine-dependent routines. */ char inchar(); void outchar(); void outstr(), beep(); void remove(), rename(); void windinit(), windexit(), windgoto(); void delay(); void sleep(); SHAR_EOF cat << \SHAR_EOF > unix.c /* * System-dependent routines for UNIX System V Release 3. */ #include "stevie.h" /* #include <termio.h> /* System V */ #include <curses.h> /* BSD */ /* * inchar() - get a character from the keyboard */ char inchar() { char c; flushbuf(); /* flush any pending output */ while (read(0, &c, 1) != 1); return c; } #define BSIZE 2048 static char outbuf[BSIZE]; static int bpos = 0; flushbuf() { if (bpos != 0) write(1, outbuf, bpos); bpos = 0; } /* * Macro to output a character. Used within this file for speed. */ #define outone(c) outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf() /* * Function version for use outside this file. */ void outchar(c) char c; { outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf(); } void outstr(s) char *s; { while (*s) { outone(*s++); } } void beep() { if (RedrawingDisabled) return; outone('\007'); } /* * remove(file) - remove a file */ void remove(file) char *file; { unlink(file); } /* * rename(of, nf) - rename existing file 'of' to 'nf' */ void rename(of, nf) char *of, *nf; { unlink(nf); link(of, nf); unlink(of); } void delay() { /* not implemented */ } static struct termio ostate; void windinit() { char *getenv(); char *term; struct termio nstate; if ((term = getenv("TERM")) == NULL || strcmp(term, "vt100") != 0) { fprintf(stderr, "Invalid terminal type '%s'\n", term); exit(1); } Columns = 80; P(P_LI) = Rows = 24; /* * Go into cbreak mode */ ioctl(0, TCGETA, &ostate); nstate = ostate; nstate.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL); nstate.c_cc[VMIN] = 1; nstate.c_cc[VTIME] = 0; ioctl(0, TCSETAW, &nstate); } void windexit(r) int r; { /* * Restore terminal modes */ ioctl(0, TCSETAW, &ostate); exit(r); } #define outone(c) outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf() void windgoto(r, c) int r, c; { r += 1; c += 1; /* * Check for overflow once, to save time. */ if (bpos + 8 >= BSIZE) flushbuf(); outbuf[bpos++] = '\033'; outbuf[bpos++] = '['; if (r >= 10) outbuf[bpos++] = r / 10 + '0'; outbuf[bpos++] = r % 10 + '0'; outbuf[bpos++] = ';'; if (c >= 10) outbuf[bpos++] = c / 10 + '0'; outbuf[bpos++] = c % 10 + '0'; outbuf[bpos++] = 'H'; } FILE * fopenb(fname, mode) char *fname; char *mode; { return fopen(fname, mode); } SHAR_EOF cat << \SHAR_EOF > unix.h /* * Unix Machine-dependent routines. */ char inchar(); void outchar(); void outstr(), beep(); void remove(), rename(); void windinit(), windexit(), windgoto(); void delay(); SHAR_EOF cat << \SHAR_EOF > normal.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 */ /* * This file contains the main routine for processing characters in command * mode as well as routines for handling the operators. */ #include "stevie.h" static void doshift(), dodelete(), doput(), dochange(); static void startinsert(); static bool_t dojoin(); static bool_t doyank(); /* * Macro evaluates true if char 'c' is a valid identifier character */ #define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_') /* * Operators */ #define NOP 0 /* no pending operation */ #define DELETE 1 #define YANK 2 #define CHANGE 3 #define LSHIFT 4 #define RSHIFT 5 #define CLEAROP (operator = NOP)/* clear any pending operator */ static int operator = NOP; /* current pending operator */ /* * When a cursor motion command is made, it is marked as being a character or * line oriented motion. Then, if an operator is in effect, the operation * becomes character or line oriented accordingly. * * Character motions are marked as being inclusive or not. Most char. motions * are inclusive, but some (e.g. 'w') are not. * * Generally speaking, every command in normal() should either clear any pending * operator (with CLEAROP), or set the motion type variable. */ /* * Motion types */ #define MBAD (-1) /* 'bad' motion type marks unusable yank buf */ #define MCHAR 0 #define MLINE 1 static int mtype; /* type of the current cursor motion */ static bool_t mincl; /* true if char motion is inclusive */ static int ybtype = MBAD; static int ybcrossline = FALSE; static LPTR startop; /* cursor pos. at start of operator */ /* * Operators can have counts either before the operator, or between the * operator and the following cursor motion as in: * * d3w or 3dw * * If a count is given before the operator, it is saved in opnum. If normal() is * called with a pending operator, the count in opnum (if present) overrides * any count that came later. */ static int opnum = 0; #define DEFAULT1(x) (((x) == 0) ? 1 : (x)) /* * normal * * Execute a command in normal mode. */ void normal(c) char c; { char *p; int n; int nn; bool_t flag = FALSE; int type = 0; /* used in some operations to modify type */ int dir = FORWARD; /* search direction */ char nchar = NUL; bool_t finish_op; LPTR temp_Curschar; last_command = NUL; /* * If there is an operator pending, then the command we take this time * will terminate it. Finish_op tells us to finish the operation before * returning this time (unless the operation was cancelled). */ finish_op = (operator != NOP); /* * If we're in the middle of an operator AND we had a count before the * operator, then that count overrides the current value of Prenum. What * this means effectively, is that commands like "3dw" get turned into * "d3w" which makes things fall into place pretty neatly. */ if (finish_op) { if (opnum != 0) Prenum = opnum; } else opnum = 0; switch (c) { case K_HELP: CLEAROP; if (help()) { screenclear(); updateNextscreen(); } break; case CTRL('L'): CLEAROP; screenclear(); updateNextscreen(); break; case CTRL('D'): CLEAROP; if (Prenum) P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum; scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); updateNextscreen(); break; case CTRL('U'): CLEAROP; if (Prenum) P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum; scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); updateNextscreen(); break; case CTRL('F'): CLEAROP; if (nextline(Topchar) == NULL) { beep(); break; } screenclear(); Prenum = DEFAULT1(Prenum); while (Prenum > 0) { *Curschar = *prevline(Botchar); *Topchar = *Curschar; Topchar->index = 0; updateNextscreen(); Prenum--; } beginline(TRUE); break; case CTRL('B'): CLEAROP; if (prevline(Topchar) == NULL) { beep(); break; } screenclear(); Prenum = DEFAULT1(Prenum); while (Prenum > 0) { *Curschar = *Topchar; n = Rows - 1; { LPTR *lp = Curschar; int l = 0; while ((l < n) && (lp != NULL)) { l += plines(lp); *Topchar = *lp; lp = prevline(lp); } } Topchar->index = 0; Prenum--; } beginline(TRUE); updateNextscreen(); break; case CTRL('E'): CLEAROP; scrollup(DEFAULT1(Prenum)); updateNextscreen(); break; case CTRL('Y'): CLEAROP; scrolldown(DEFAULT1(Prenum)); updateNextscreen(); break; case 'z': CLEAROP; switch (vgetc()) { case NL: /* put Curschar at top of screen */ case CR: *Topchar = *Curschar; Topchar->index = 0; updateNextscreen(); break; case '.': /* put Curschar in middle of screen */ n = Rows / 2; goto dozcmd; case '-': /* put Curschar at bottom of screen */ n = Rows - 1; /* FALLTHROUGH */ dozcmd: { LPTR *lp = Curschar; int l = 0; while ((l < n) && (lp != NULL)) { l += plines(lp); *Topchar = *lp; lp = prevline(lp); } } Topchar->index = 0; updateNextscreen(); break; default: beep(); } break; case CTRL('G'): CLEAROP; fileinfo(); break; case 'G': mtype = MLINE; *Curschar = *gotoline(Prenum); beginline(TRUE); break; case 'H': mtype = MLINE; *Curschar = *Topchar; for (n = Prenum; n && onedown(1); n--); beginline(TRUE); break; case 'M': mtype = MLINE; *Curschar = *Topchar; for (n = 0; n < Rows / 2 && onedown(1); n++); beginline(TRUE); break; case 'L': mtype = MLINE; *Curschar = *prevline(Botchar); for (n = Prenum; n && oneup(1); n--); beginline(TRUE); break; case 'l': case K_RARROW: case ' ': mtype = MCHAR; mincl = FALSE; n = DEFAULT1(Prenum); while (n--) { if (!oneright()) { if (operator != DELETE && operator != CHANGE) { beep(); } else { if (lineempty(Curschar)) { CLEAROP; beep(); } else { mincl = TRUE; } } break; } } set_want_col = TRUE; break; case 'h': case K_LARROW: case CTRL('H'): mtype = MCHAR; mincl = FALSE; Prenum = DEFAULT1(Prenum); n = Prenum; while (n--) { if (!oneleft()) { if (operator != DELETE && operator != CHANGE) { beep(); } else if (Prenum == 1) { CLEAROP; beep(); } break; } } set_want_col = TRUE; break; case '-': flag = TRUE; /* FALLTHROUGH */ case 'k': case K_UARROW: case CTRL('P'): mtype = MLINE; if (!oneup(DEFAULT1(Prenum))) { CLEAROP; beep(); } else if (flag) beginline(TRUE); break; case '+': case CR: case NL: flag = TRUE; /* FALLTHROUGH */ case 'j': case K_DARROW: case CTRL('N'): mtype = MLINE; if (!onedown(DEFAULT1(Prenum))) { CLEAROP; beep(); } else if (flag) beginline(TRUE); break; /* * This is a strange motion command that helps make operators more * logical. It is actually implemented, but not documented in the * real 'vi'. This motion command actually refers to "the current * line". Commands like "dd" and "yy" are really an alternate form of * "d_" and "y_". It does accept a count, so "d3_" works to delete 3 * lines. */ case '_': lineop: mtype = MLINE; if (!onedown(DEFAULT1(Prenum) - 1)) { CLEAROP; beep(); } else beginline(TRUE); break; case '|': mtype = MCHAR; mincl = TRUE; beginline(FALSE); if (Prenum > 0) *Curschar = *coladvance(Curschar, Prenum - 1); Curswant = Prenum - 1; break; case CTRL(']'): /* :ta to current identifier */ CLEAROP; { char ch; LPTR save; save = *Curschar; /* * First back up to start of identifier. This doesn't match the * real vi but I like it a little better and it shouldn't bother * anyone. */ ch = gchar(Curschar); while (IDCHAR(ch)) { if (!oneleft()) break; ch = gchar(Curschar); } if (!IDCHAR(ch)) oneright(); stuffReadbuff(":ta "); /* * Now grab the chars in the identifier */ ch = gchar(Curschar); while (IDCHAR(ch)) { stuffReadbuff(mkstr(ch)); if (!oneright()) break; ch = gchar(Curschar); } stuffReadbuff("\n"); *Curschar = save; /* restore, in case of error */ } break; case '%': mtype = MCHAR; mincl = TRUE; { LPTR *pos; if ((pos = showmatch()) == NULL) { CLEAROP; beep(); } else { setpcmark(); *Curschar = *pos; set_want_col = TRUE; } } break; /* * Word Motions */ case 'B': type = 1; /* FALLTHROUGH */ case 'b': mtype = MCHAR; mincl = FALSE; set_want_col = TRUE; for (n = DEFAULT1(Prenum); n > 0; n--) { LPTR *pos; if ((Curschar->linep->prev == NULL) && (Curschar->index == 0)) { CLEAROP; beep(); break; } pos = bck_word(Curschar, type); if (pos == NULL) *Curschar = *gotoline(1); /* goto top of file */ else *Curschar = *pos; } break; case 'W': type = 1; /* FALLTHROUGH */ case 'w': /* * This is a little strange. To match what the real vi does, we * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems * impolite at first, but it's really more what we mean when we say * 'cw'. */ if (operator == CHANGE) goto doecmd; mtype = MCHAR; mincl = FALSE; set_want_col = TRUE; for (n = DEFAULT1(Prenum); n > 0; n--) { LPTR *pos; if ((pos = fwd_word(Curschar, type)) == NULL) { CLEAROP; beep(); break; } else *Curschar = *pos; } break; case 'E': type = 1; /* FALLTHROUGH */ case 'e': doecmd: mtype = MCHAR; mincl = TRUE; set_want_col = TRUE; for (n = DEFAULT1(Prenum); n > 0; n--) { LPTR *pos; if ((pos = end_word(Curschar, type)) == NULL) { CLEAROP; beep(); break; } else *Curschar = *pos; } break; case '$': mtype = MCHAR; mincl = TRUE; while (oneright()); Curswant = 999; /* so we stay at the end */ break; case '^': flag = TRUE; /* FALLTHROUGH */ case '0': mtype = MCHAR; mincl = TRUE; beginline(flag); break; case 'A': set_want_col = TRUE; while (oneright()); ResetBuffers(); AppendToRedobuff("A"); goto doAPPENDcmd; case 'a': ResetBuffers(); AppendToRedobuff("a"); doAPPENDcmd: CLEAROP; /* Works just like an 'i'nsert on the next character. */ n = RowNumber(Curschar); AppendPositionToUndoUndobuff(Curschar->index, n); AppendToUndoUndobuff("a"); if (!lineempty(Curschar)) inc(Curschar); n = RowNumber(Curschar); AppendPositionToUndobuff(Curschar->index, n); startinsert(FALSE); CHANGED; break; case 'I': beginline(TRUE); ResetBuffers(); AppendToRedobuff("I"); goto doINSERTcmd; /* FALLTHROUGH */ case 'i': case K_INSERT: ResetBuffers(); AppendToRedobuff("i"); doINSERTcmd: CLEAROP; n = RowNumber(Curschar); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); AppendToUndoUndobuff("i"); startinsert(FALSE); CHANGED; break; case 'o': CLEAROP; ResetBuffers(); n = RowNumber(Curschar); AppendToRedobuff("o"); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); AppendToUndoUndobuff("o"); if (opencmd(FORWARD, TRUE)) { startinsert(TRUE); CHANGED; } last_command = 'o'; break; case 'O': CLEAROP; ResetBuffers(); n = RowNumber(Curschar); AppendToRedobuff("O"); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); AppendToUndoUndobuff("O"); if (opencmd(BACKWARD, TRUE)) { startinsert(TRUE); CHANGED; } last_command = 'O'; break; case 'd': if (operator == DELETE) /* handle 'dd' */ goto lineop; if (Prenum != 0) opnum = Prenum; startop = *Curschar; operator = DELETE; break; /* * Some convenient abbreviations... */ case 'x': if (Prenum) stuffnumReadbuff(Prenum); stuffReadbuff("dl"); break; case 'X': if (Prenum) stuffnumReadbuff(Prenum); stuffReadbuff("dh"); break; case 'D': stuffReadbuff("d$"); break; case 'Y': if (Prenum) stuffnumReadbuff(Prenum); stuffReadbuff("yy"); break; case 'C': stuffReadbuff("c$"); break; case 'c': if (operator == CHANGE) { /* handle 'cc' */ CLEAROP; stuffReadbuff("0c$"); break; } if (Prenum != 0) opnum = Prenum; startop = *Curschar; operator = CHANGE; break; case 'y': if (operator == YANK) /* handle 'yy' */ goto lineop; if (Prenum != 0) opnum = Prenum; startop = *Curschar; operator = YANK; break; case 'p': if (RedrawingDisabled) { RedrawingDisabled = FALSE; updateNextscreen(); break; } else if (Yankbuffptr != NULL) { doput(FORWARD); if (ybcrossline) { n = RowNumber(Curschar); stuffnumReadbuff(n); stuffReadbuff("G"); if (ybtype == MCHAR) { stuffnumReadbuff(Curschar->index); if (Curschar->index) stuffReadbuff("l"); } else stuffnumReadbuff(0); } stuffReadbuff("p"); RedrawingDisabled = TRUE; } else beep(); break; case 'P': if (RedrawingDisabled) { RedrawingDisabled = FALSE; updateNextscreen(); break; } else if (Yankbuffptr != NULL) { doput(BACKWARD); n = RowNumber(Curschar); stuffnumReadbuff(n); stuffReadbuff("G"); if (ybtype == MCHAR) { stuffnumReadbuff(Curschar->index); if (Curschar->index) stuffReadbuff("l"); } else stuffnumReadbuff(0); stuffReadbuff("P"); RedrawingDisabled = TRUE; } else beep(); break; case '>': if (operator == RSHIFT) /* handle >> */ goto lineop; if (operator == LSHIFT) { CLEAROP; beep(); break; } if (Prenum != 0) opnum = Prenum; startop = *Curschar; /* save current position */ operator = RSHIFT; break; case '<': if (operator == LSHIFT) /* handle << */ goto lineop; if (operator == RSHIFT) { CLEAROP; beep(); break; } if (Prenum != 0) opnum = Prenum; startop = *Curschar; /* save current position */ operator = LSHIFT; break; case 's': /* substitute characters */ if (Prenum) stuffnumReadbuff(Prenum); stuffReadbuff("cl"); break; case '?': case '/': case ':': CLEAROP; readcmdline(c, (char *) NULL); break; case 'n': mtype = MCHAR; mincl = FALSE; set_want_col = TRUE; repsearch(0); break; case 'N': mtype = MCHAR; mincl = FALSE; set_want_col = TRUE; repsearch(1); break; /* * Character searches */ case 'T': dir = BACKWARD; /* FALLTHROUGH */ case 't': type = 1; goto docsearch; case 'F': dir = BACKWARD; /* FALLTHROUGH */ case 'f': docsearch: mtype = MCHAR; mincl = TRUE; set_want_col = TRUE; if ((nchar = vgetc()) == ESC) /* search char */ break; if (!searchc(nchar, dir, type)) { CLEAROP; beep(); } break; case ',': flag = 1; /* FALLTHROUGH */ case ';': mtype = MCHAR; mincl = TRUE; set_want_col = TRUE; if (!crepsearch(flag)) { CLEAROP; beep(); } break; /* * Function searches */ case '[': dir = BACKWARD; /* FALLTHROUGH */ case ']': mtype = MLINE; set_want_col = TRUE; if (vgetc() != c) beep(); if (!findfunc(dir)) { CLEAROP; beep(); } break; /* * Marks */ case 'm': CLEAROP; if (!setmark(vgetc())) beep(); break; case '\'': flag = TRUE; /* FALLTHROUGH */ case '`': { LPTR mtmp, *mark = getmark(vgetc()); if (mark == NULL) { CLEAROP; beep(); } else { mtmp = *mark; setpcmark(); *Curschar = mtmp; if (flag) beginline(TRUE); } mtype = flag ? MLINE : MCHAR; mincl = TRUE; /* ignored if not MCHAR */ set_want_col = TRUE; } break; case 'r': CLEAROP; if (lineempty(Curschar)) { /* Nothing to replace */ beep(); break; } if ((nchar = vgetc()) == ESC) break; Prenum = DEFAULT1(Prenum); n = strlen(Curschar->linep->s) - Curschar->index; if (n < Prenum) { beep(); break; } ResetBuffers(); nn = RowNumber(Curschar); AppendPositionToUndobuff(Curschar->index, nn); AppendPositionToUndoUndobuff(Curschar->index, nn); while (Prenum > 0) { AppendToRedobuff("r"); AppendToRedobuff(mkstr(nchar)); AppendToUndobuff("r"); AppendToUndobuff(mkstr(gchar(Curschar))); AppendToUndoUndobuff("r"); AppendToUndoUndobuff(mkstr(nchar)); pchar(Curschar, nchar); /* Change current character. */ if (Prenum > 1) { oneright(); AppendToRedobuff("l"); AppendToUndobuff("l"); AppendToUndoUndobuff("l"); } Prenum--; } CHANGED; updateline(); break; case '~': /* swap case */ CLEAROP; if (lineempty(Curschar)) { beep(); break; } ResetBuffers(); n = RowNumber(Curschar); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); Prenum = DEFAULT1(Prenum); if (Prenum > 0) { AppendNumberToRedobuff(Prenum); AppendNumberToUndobuff(Prenum); AppendNumberToUndoUndobuff(Prenum); } AppendToRedobuff("~"); AppendToUndobuff("~"); AppendToUndoUndobuff("~"); while (Prenum > 0) { c = gchar(Curschar); if (isalpha(c)) { if (islower(c)) pchar(Curschar, toupper(c)); else pchar(Curschar, tolower(c)); } if (!oneright()) break; Prenum--; } CHANGED; updateline(); break; case UNDO_SHIFTJ: CLEAROP; if (UndoInProgress) { if (dojoin(FALSE, FALSE)) { CHANGED; updateNextscreen(); } break; } goto doSHIFTJcommand; case 'J': CLEAROP; doSHIFTJcommand: if (nextline(Curschar) == NULL) { /* on last line */ beep(); break; } ResetBuffers(); temp_Curschar = *Curschar; nn = strlen(Curschar->linep->s); if (nn < 0) nn = 0; n = RowNumber(&temp_Curschar); AppendToRedobuff("J"); AppendPositionToUndobuff(nn, n); AppendPositionToUndoUndobuff(0, n); AppendToUndoUndobuff("J"); if (lineempty(nextline(Curschar))) { AppendToUndobuff("a\n"); if (!dojoin(FALSE, TRUE)) { beep(); break; } } else if (lineempty(Curschar)) { AppendToUndobuff("i\n"); if (!dojoin(FALSE, TRUE)) { beep(); break; } } else { AppendToUndobuff("dli\n"); if (!dojoin(TRUE, TRUE)) { beep(); break; } } AppendToUndobuff(ESC_STR); AppendPositionToUndobuff(nn, n); CHANGED; updateNextscreen(); break; case K_CGRAVE: /* shorthand command */ CLEAROP; stuffReadbuff(":e #\n"); break; case 'Z': /* write, if changed, and exit */ if (vgetc() != 'Z') { beep(); break; } if (Changed) { if (Filename != NULL) { if (!writeit(Filename, (LPTR *) NULL, (LPTR *) NULL)) return; } else { emsg("No output file"); return; } } getout(0); break; case '.': CLEAROP; if (RedrawingDisabled) { RedrawingDisabled = FALSE; updateNextscreen(); } else if (Redobuffptr != NULL) { stuffReadbuff(Redobuff); stuffReadbuff("."); RedrawingDisabled = TRUE; } else beep(); break; case 'u': case K_UNDO: CLEAROP; if (UndoInProgress) { p = UndoUndobuff; UndoUndobuff = Undobuff; Undobuff = p; p = UndoUndobuffptr; UndoUndobuffptr = Undobuffptr; Undobuffptr = p; UndoInProgress = FALSE; RedrawingDisabled = FALSE; updateNextscreen(); } else if (Undobuffptr != NULL) { stuffReadbuff(Undobuff); stuffReadbuff("u"); UndoInProgress = TRUE; RedrawingDisabled = TRUE; } else { beep(); } break; default: CLEAROP; beep(); break; } /* * If an operation is pending, handle it... */ if (finish_op) { /* we just finished an operator */ if (operator == NOP) /* ... but it was cancelled */ return; switch (operator) { case LSHIFT: case RSHIFT: ResetBuffers(); n = RowNumber(&startop); AppendPositionToUndobuff(startop.index, n); AppendPositionToUndoUndobuff(startop.index, n); if (Prenum != 0) { AppendNumberToRedobuff(Prenum); AppendNumberToUndobuff(Prenum); AppendNumberToUndoUndobuff(Prenum); } AppendToRedobuff((operator == LSHIFT) ? "<" : ">"); AppendToUndobuff((operator == LSHIFT) ? ">" : "<"); AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">"); AppendToRedobuff(mkstr(c)); if (c == '>') AppendToUndobuff("<"); else if (c == '<') AppendToUndobuff(">"); else AppendToUndobuff(mkstr(c)); AppendToUndoUndobuff(mkstr(c)); doshift(operator); CHANGED; break; case DELETE: ResetBuffers(); n = RowNumber(&startop); AppendPositionToUndoUndobuff(startop.index, n); temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar); n = RowNumber(&temp_Curschar); if (Prenum != 0) { AppendNumberToRedobuff(Prenum); AppendNumberToUndoUndobuff(Prenum); } AppendToRedobuff("d"); AppendToUndoUndobuff("d"); AppendToRedobuff(mkstr(c)); AppendToUndoUndobuff(mkstr(c)); if (nchar != NUL) { AppendToRedobuff(mkstr(nchar)); AppendToUndoUndobuff(mkstr(nchar)); } AppendPositionToUndobuff(temp_Curschar.index, n); dodelete(TRUE, TRUE, TRUE); AppendPositionToUndobuff(temp_Curschar.index, n); CHANGED; break; case YANK: ResetBuffers(); /* no redo/undo/(undo of undo) on yank... */ doyank(); if (!ybcrossline) *Curschar = startop; else if (lt(&startop, Curschar)) *Curschar = startop; break; case CHANGE: ResetBuffers(); n = RowNumber(&startop); AppendPositionToUndoUndobuff(startop.index, n); temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar); n = RowNumber(&temp_Curschar); if (mtype == MLINE) AppendPositionToUndobuff(0, n); else AppendPositionToUndobuff(temp_Curschar.index, n); if (Prenum != 0) { AppendNumberToRedobuff(Prenum); AppendNumberToUndoUndobuff(Prenum); } AppendToRedobuff("c"); AppendToUndoUndobuff("c"); AppendToRedobuff(mkstr(c)); AppendToUndoUndobuff(mkstr(c)); if (nchar != NUL) { AppendToRedobuff(mkstr(nchar)); AppendToUndoUndobuff(mkstr(nchar)); } dochange(); CHANGED; last_command = 'c'; break; default: beep(); } operator = NOP; } } /* * tabinout(shift_type, num) * * If shift_type == RSHIFT, add a tab to the begining of the next num lines; * otherwise delete a tab from the beginning of the next num lines. */ static void tabinout(shift_type, num) int shift_type; int num; { LPTR *p; beginline(FALSE); while (num-- > 0) { beginline(FALSE); if (shift_type == RSHIFT) inschar(TAB); else { if (gchar(Curschar) == TAB) delchar(TRUE, FALSE); } if (num > 0) { if ((p = nextline(Curschar)) != NULL) *Curschar = *p; else break; } } } /* * doshift - handle a shift operation */ static void doshift(op) int op; { LPTR top, bot; int nlines; top = startop; bot = *Curschar; if (lt(&bot, &top)) pswap(top, bot); nlines = cntllines(&top, &bot); *Curschar = top; tabinout(op, nlines); /* * The cursor position afterward is the prior of the two positions. */ *Curschar = top; /* * If we were on the last char of a line that got shifted left, then move * left one so we aren't beyond the end of the line */ if (gchar(Curschar) == NUL && Curschar->index > 0) Curschar->index--; if (op == RSHIFT) oneright(); else oneleft(); updateNextscreen(); if (nlines > P(P_RP)) smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<'); } /* * dodelete - handle a delete operation */ static void dodelete(redraw, setup_for_undo, try_to_yank) bool_t redraw; bool_t setup_for_undo; bool_t try_to_yank; { LPTR top, bot; int nlines; int n; /* * Do a yank of whatever we're about to delete. If there's too much stuff * to fit in the yank buffer, then get a confirmation before doing the * delete. This is crude, but simple. And it avoids doing a delete of * something we can't put back if we want. */ if (try_to_yank) { if (!doyank()) { msg("yank buffer exceeded: press <y> to confirm"); if (vgetc() != 'y') { msg("delete aborted"); *Curschar = startop; return; } } } top = startop; bot = *Curschar; if (lt(&bot, &top)) pswap(top, bot); *Curschar = top; nlines = cntllines(&top, &bot); cursupdate(); if (mtype == MLINE) { if (operator == CHANGE) { last_command_char = 'a'; delline(nlines - 1); Curschar->index = 0; while (delchar(TRUE, FALSE)); } else { if (bot.linep->next == Fileend->linep) last_command_char = 'o'; else last_command_char = 'O'; if (setup_for_undo) AppendToUndobuff(mkstr(last_command_char)); delline(nlines); } } else if (top.linep == bot.linep) { /* del. within line */ if (!mincl) dec(&bot); if (endofline(&bot)) last_command_char = 'a'; else last_command_char = 'i'; if (setup_for_undo) AppendToUndobuff(mkstr(last_command_char)); n = bot.index - top.index + 1; while (n--) if (!delchar(TRUE, FALSE)) break; } else { /* del. between lines */ if (endofline(&top)) { if (nextline(&top)) { if (lineempty(nextline(&top))) last_command_char = 'a'; else last_command_char = 'i'; } else { last_command_char = 'a'; } } else { last_command_char = 'i'; } if (setup_for_undo) AppendToUndobuff(mkstr(last_command_char)); n = Curschar->index; while (Curschar->index >= n) if (!delchar(TRUE, FALSE)) break; top = *Curschar; *Curschar = *nextline(Curschar); delline(nlines - 2); Curschar->index = 0; n = bot.index; if (!mincl) n--; while (n-- >= 0) if (!delchar(TRUE, FALSE)) break; *Curschar = top; dojoin(FALSE, FALSE); } if (mtype == MCHAR && nlines == 1 && redraw) updateline(); else updateNextscreen(); if (nlines > P(P_RP)) smsg("%d fewer lines", nlines); if (setup_for_undo) { AppendToUndobuff(Yankbuff); AppendToUndobuff(ESC_STR); } } /* * dochange - handle a change operation */ static void dochange() { bool_t doappend; /* true if we should do append, not insert */ doappend = lt(Curschar, &startop); /* needed for some compilers */ doappend = endofline(doappend ? &startop : Curschar); dodelete(FALSE, FALSE, TRUE); if (doappend && !lineempty(Curschar)) inc(Curschar); startinsert(FALSE); } static bool_t doyank() { LPTR top, bot; char *ybend = &Yankbuff[YANKSIZE - 1]; int nlines; Yankbuffptr = Yankbuff; top = startop; bot = *Curschar; if (lt(&bot, &top)) pswap(top, bot); nlines = cntllines(&top, &bot); ybtype = mtype; /* set the yank buffer type */ ybcrossline = FALSE; if (LINEOF(&top) != LINEOF(&bot)) ybcrossline = TRUE; if (mtype == MLINE) { ybcrossline = TRUE; top.index = 0; bot.index = strlen(bot.linep->s); /* * The following statement checks for the special case of yanking a * blank line at the beginning of the file. If not handled right, we * yank an extra char (a newline). */ if (dec(&bot) == -1) { *Yankbuff = NUL; Yankbuffptr = NULL; return TRUE; } } else { if (!mincl) if (!equal(&top, &bot)) dec(&bot); } for (; ltoreq(&top, &bot); inc(&top)) { *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL; Yankbuffptr++; if (Yankbuffptr >= ybend) { *Yankbuffptr = NUL; msg("yank too big for buffer"); ybtype = MBAD; return FALSE; } } *Yankbuffptr = NUL; if (operator == YANK) if (nlines > P(P_RP)) smsg("%d lines yanked", nlines); return TRUE; } static void doput(dir) int dir; { bool_t type; if (ybtype == MBAD) { beep(); return; } type = (ybtype == MCHAR); if (dir == FORWARD) stuffReadbuff(type ? "a" : "o"); else stuffReadbuff(type ? "i" : "O"); stuffReadbuff(Yankbuff); stuffReadbuff(ESC_STR); } static void startinsert(startln) int startln; /* if set, insert at start of line */ { *Insstart = *Curschar; if (startln) { Insstart->index = 0; } Insbuffptr = Insbuff; *Insbuffptr = NUL; State = INSERT; if (P(P_MO)) msg("Insert Mode"); } void ResetBuffers() { if (UndoInProgress) return; *Redobuff = NUL; Redobuffptr = NULL; *Undobuff = NUL; Undobuffptr = NULL; *UndoUndobuff = NUL; UndoUndobuffptr = NULL; } void AppendToRedobuff(s) char *s; { if (UndoInProgress) return; if (Redobuffptr == NULL) { if ((strlen(s) + 1) < REDO_UNDO_SIZE) { strcpy(Redobuff, s); Redobuffptr = Redobuff; } else emsg("Couldn't AppendToRedobuff() - should never happen"); return; } if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { strcat(Redobuff, s); return; } emsg("Couldn't AppendToRedobuff() - should never happen"); } void AppendNumberToRedobuff(n) int n; { char buf[32]; if (UndoInProgress) return; sprintf(buf, "%d", n); AppendToRedobuff(buf); } void AppendToUndobuff(s) char *s; { if (UndoInProgress) return; if (Undobuffptr == NULL) { if ((strlen(s) + 1) < REDO_UNDO_SIZE) { strcpy(Undobuff, s); Undobuffptr = Undobuff; } else emsg("Couldn't AppendToUndobuff() - should never happen"); return; } if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { strcat(Undobuff, s); return; } emsg("Couldn't AppendToUndobuff() - should never happen"); } void AppendNumberToUndobuff(n) int n; { char buf[32]; if (UndoInProgress) return; sprintf(buf, "%d", n); AppendToUndobuff(buf); } void AppendPositionToUndobuff(column, row) int column; int row; { if (UndoInProgress) return; AppendNumberToUndobuff(row); AppendToUndobuff("G"); AppendNumberToUndobuff(column); if (column) AppendToUndobuff("l"); } void AppendToUndoUndobuff(s) char *s; { if (UndoInProgress) return; if (UndoUndobuffptr == NULL) { if ((strlen(s) + 1) < REDO_UNDO_SIZE) { strcpy(UndoUndobuff, s); UndoUndobuffptr = Undobuff; } else emsg("Couldn't AppendToUndoUndobuff() - should never happen"); return; } if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { strcat(UndoUndobuff, s); return; } emsg("Couldn't AppendToUndoUndobuff() - should never happen"); } void AppendNumberToUndoUndobuff(n) int n; { char buf[32]; if (UndoInProgress) return; sprintf(buf, "%d", n); AppendToUndoUndobuff(buf); } void AppendPositionToUndoUndobuff(column, row) int column; int row; { if (UndoInProgress) return; AppendNumberToUndoUndobuff(row); AppendToUndoUndobuff("G"); AppendNumberToUndoUndobuff(column); if (column) AppendToUndoUndobuff("l"); } static bool_t dojoin(leading_space, strip_leading_spaces) bool_t leading_space; bool_t strip_leading_spaces; { int scol; /* save cursor column */ int currsize; /* size of the current line */ int nextsize; /* size of the next line */ if (nextline(Curschar) == NULL) /* on last line */ return FALSE; if (!canincrease(nextsize = strlen(Curschar->linep->next->s))) return FALSE; currsize = strlen(Curschar->linep->s); while (oneright()); /* to end of line */ strcat(Curschar->linep->s, Curschar->linep->next->s); /* * Delete the following line. To do this we move the cursor there * briefly, and then move it back. Don't back up if the delete made us * the last line. */ Curschar->linep = Curschar->linep->next; scol = Curschar->index; if (nextline(Curschar) != NULL) { delline(1); Curschar->linep = Curschar->linep->prev; } else delline(1); Curschar->index = scol; if (currsize) oneright(); /* go to first char. of joined line */ if (nextsize != 0 && strip_leading_spaces) { /* * Delete leading white space on the joined line and insert a single * space. */ while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) { delchar(TRUE, TRUE); } if (leading_space) inschar(' '); } return TRUE; } char * mkstr(c) char c; { static char s[2]; s[0] = c; s[1] = NUL; return s; } SHAR_EOF cat << \SHAR_EOF > sendpacket.c /* * Sendpacket.c * * An invaluable addition to your Amiga.lib file. This code sends a packet the * given message port. This makes working around DOS lots easier. * * Note, I didn't write this, those wonderful folks at CBM did. I do suggest * however that you may wish to add it to Amiga.Lib, to do so, compile it and * say 'oml lib:amiga.lib -r sendpacket.o' */ #include <exec/types.h> #include <exec/ports.h> #include <exec/memory.h> #include <libraries/dos.h> #include <libraries/dosextens.h> /* * Function - SendPacket written by Phil Lindsay, Carolyn Scheppner, and Andy * Finkel. This function will send a packet of the given type to the Message * Port supplied. */ long SendPacket(pid, action, args, nargs) struct MsgPort *pid; /* process indentifier ... (handlers message * port ) */ long action, /* packet type ... (what you want handler to * do ) */ args[], /* a pointer to a argument list */ nargs; /* number of arguments in list */ { struct MsgPort *replyport; struct StandardPacket *packet; long count, *pargs, res1; replyport = (struct MsgPort *) CreatePort(NULL, 0); if (!replyport) return (0); /* Allocate space for a packet, make it public and clear it */ packet = (struct StandardPacket *) AllocMem((long) sizeof(struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR); if (!packet) { DeletePort(replyport); return (0); } packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt); packet->sp_Pkt.dp_Link = &(packet->sp_Msg); packet->sp_Pkt.dp_Port = replyport; packet->sp_Pkt.dp_Type = action; /* copy the args into the packet */ pargs = &(packet->sp_Pkt.dp_Arg1); /* address of first argument */ for (count = 0; count < nargs; count++) pargs[count] = args[count]; PutMsg(pid, packet); /* send packet */ WaitPort(replyport); GetMsg(replyport); res1 = packet->sp_Pkt.dp_Res1; FreeMem(packet, (long) sizeof(struct StandardPacket)); DeletePort(replyport); return (res1); } 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.