Path: utzoo!attcan!uunet!mcvax!unido!fauern!faui44!immd3.informatik.uni-erlangen.de!rtregn From: rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) Newsgroups: comp.os.minix Subject: Sources of new version of Stevie (vi clone) 3 of 5 Message-ID: <769@faui10.informatik.uni-erlangen.de> Date: 14 Dec 88 16:11:19 GMT Organization: IMMD I, University of Erlangen, W-Erlangen Lines: 3616 Found 1 control char in "'makefile'" makefile.min was the original makefile for ST Minix #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'main.c' <<'END_OF_FILE' Xstatic char RCSid[] = X"$Header: main.c,v 1.5 88/11/10 13:28:38 tony Exp $"; X X/* X * The main routine and routines to deal with the input buffer. X * X * $Log: main.c,v $ X * Revision 1.5 88/11/10 13:28:38 tony X * Moved the call to do_mlines() to edit() so that it gets called after X * the various position pointers have been initialized. X * X * Revision 1.4 88/11/10 08:58:40 tony X * Added code to scan for modelines in the file currently being edited. X * X * Revision 1.3 88/11/01 21:32:29 tony X * Changed the RBSIZE macro (and comment) since it is no longer tied to X * the yank buffer size. X * X * Revision 1.2 88/08/26 08:45:14 tony X * Misc. changes to make lint happy. X * X * Revision 1.1 88/03/20 21:08:18 tony X * Initial revision X * X * X */ X X#include "stevie.h" X Xint Rows; /* Number of Rows and Columns */ Xint Columns; /* in the current window. */ X Xchar *Realscreen = NULL; /* What's currently on the screen, a single */ X /* array of size Rows*Columns. */ Xchar *Nextscreen = NULL; /* What's to be put on the screen. */ X Xchar *Filename = NULL; /* Current file name */ X XLPTR *Filemem; /* Pointer to the first line of the file */ X XLPTR *Filetop; /* Line 'above' the start of the file */ X XLPTR *Fileend; /* Pointer to the end of the file in Filemem. */ X /* (It points to the byte AFTER the last byte.) */ X XLPTR *Topchar; /* Pointer to the byte in Filemem which is */ X /* in the upper left corner of the screen. */ X XLPTR *Botchar; /* Pointer to the byte in Filemem which is */ X /* just off the bottom of the screen. */ X XLPTR *Curschar; /* Pointer to byte in Filemem at which the */ X /* cursor is currently placed. */ X Xint Cursrow, Curscol; /* Current position of cursor */ X Xint Cursvcol; /* Current virtual column, the column number of */ X /* the file's actual line, as opposed to the */ X /* column number we're at on the screen. This */ X /* makes a difference on lines that span more */ X /* than one screen line. */ X Xint Curswant = 0; /* The column we'd like to be at. This is used */ X /* try to stay in the same column through up/down */ X /* cursor motions. */ X Xbool_t set_want_col; /* If set, then update Curswant the next time */ X /* through cursupdate() to the current virtual */ X /* column. */ X Xint State = NORMAL; /* This is the current state of the command */ X /* interpreter. */ X Xint Prenum = 0; /* The (optional) number before a command. */ X XLPTR *Insstart; /* This is where the latest insert/append */ X /* mode started. */ X Xbool_t Changed = 0; /* Set to 1 if something in the file has been */ X /* changed and not written out. */ X Xchar Redobuff[1024]; /* Each command should stuff characters into this */ X /* buffer that will re-execute itself. */ X Xchar Insbuff[1024]; /* Each insertion gets stuffed into this buffer. */ X Xint Ninsert = 0; /* Number of characters in the current insertion. */ Xchar *Insptr = NULL; X Xchar **files; /* list of input files */ Xint numfiles; /* number of input files */ Xint curfile; /* number of the current file */ X Xstatic void Xusage() X{ X fprintf(stderr, "usage: stevie [file ...]\n"); X fprintf(stderr, " stevie -t tag\n"); X fprintf(stderr, " stevie +[num] file\n"); X fprintf(stderr, " stevie +/pat file\n"); X exit(1); X} X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X char *initstr, *getenv(); /* init string from the environment */ X char *tag = NULL; /* tag from command line */ X char *pat = NULL; /* pattern from command line */ X int line = -1; /* line number from command line */ X X /* X * Process the command line arguments. X */ X if (argc > 1) { X switch (argv[1][0]) { X X case '-': /* -t tag */ X if (argv[1][1] != 't') X usage(); X X if (argv[2] == NULL) X usage(); X X Filename = NULL; X tag = argv[2]; X numfiles = 1; X break; X X case '+': /* +n or +/pat */ X if (argv[1][1] == '/') { X if (argv[2] == NULL) X usage(); X Filename = strsave(argv[2]); X pat = &(argv[1][1]); X numfiles = 1; X X } else if (isdigit(argv[1][1]) || argv[1][1] == NUL) { X if (argv[2] == NULL) X usage(); X Filename = strsave(argv[2]); X numfiles = 1; X X line = (isdigit(argv[1][1])) ? X atoi(&(argv[1][1])) : 0; X } else X usage(); X X break; X X default: /* must be a file name */ X Filename = strsave(argv[1]); X files = &(argv[1]); X numfiles = argc - 1; X break; X } X } else { X Filename = NULL; X numfiles = 1; X } X curfile = 0; X X if (numfiles > 1) X fprintf(stderr, "%d files to edit\n", numfiles); X X windinit(); X X /* X * Allocate LPTR structures for all the various position pointers X */ X if ((Filemem = (LPTR *) malloc(sizeof(LPTR))) == NULL || X (Filetop = (LPTR *) malloc(sizeof(LPTR))) == NULL || X (Fileend = (LPTR *) malloc(sizeof(LPTR))) == NULL || X (Topchar = (LPTR *) malloc(sizeof(LPTR))) == NULL || X (Botchar = (LPTR *) malloc(sizeof(LPTR))) == NULL || X (Curschar = (LPTR *) malloc(sizeof(LPTR))) == NULL || X (Insstart = (LPTR *) malloc(sizeof(LPTR))) == NULL ) { X fprintf(stderr, "Can't allocate data structures\n"); X windexit(0); X } X X screenalloc(); X filealloc(); /* Initialize Filemem, Filetop, and Fileend */ X X screenclear(); X X if ((initstr = getenv("EXINIT")) != NULL) { X char *lp, buf[128]; X X if ((lp = getenv("LINES")) != NULL) { X sprintf(buf, "%s lines=%s", initstr, lp); X readcmdline(':', buf); X } else X readcmdline(':', initstr); X } X X if (Filename != NULL) { X if (readfile(Filename, Filemem, FALSE)) X filemess("[New File]"); X } else X msg("Empty Buffer"); X X setpcmark(); X X updatescreen(); X X if (tag) { X stuffin(":ta "); X stuffin(tag); X stuffin("\n"); X X } else if (pat) { X stuffin(pat); X stuffin("\n"); X X } else if (line >= 0) { X if (line > 0) X stuffnum(line); X stuffin("G"); X } X X edit(); X X windexit(0); X X return 1; /* shouldn't be reached */ X} X X#define RBSIZE 1024 Xstatic char getcbuff[RBSIZE]; Xstatic char *getcnext = NULL; X Xvoid Xstuffin(s) Xchar *s; X{ X if ( getcnext == NULL ) { X strcpy(getcbuff,s); X getcnext = getcbuff; X } else X strcat(getcbuff,s); X} X Xvoid Xstuffnum(n) Xint n; X{ X char buf[32]; X X sprintf(buf, "%d", n); X stuffin(buf); X} X X/*VARARGS1*/ Xvoid Xaddtobuff(s,c1,c2,c3,c4,c5,c6) Xchar *s; Xchar c1, c2, c3, c4, c5, c6; X{ X char *p = s; X if ( (*p++ = c1) == NUL ) X return; X if ( (*p++ = c2) == NUL ) X return; X if ( (*p++ = c3) == NUL ) X return; X if ( (*p++ = c4) == NUL ) X return; X if ( (*p++ = c5) == NUL ) X return; X if ( (*p++ = c6) == NUL ) X return; X} X Xint Xvgetc() X{ X int c; X X /* X * inchar() may map special keys by using stuffin(). If it does X * so, it returns -1 so we know to loop here to get a real char. X */ X do { X if ( getcnext != NULL ) { X int nextc = *getcnext++; X if ( *getcnext == NUL ) { X *getcbuff = NUL; X getcnext = NULL; X } X return(nextc); X } X c = inchar(); X } while (c == -1); X X return c; X} X X#if 0 Xint Xvpeekc() X{ X if ( getcnext != NULL ) X return(*getcnext); X return(-1); X} X#endif X X/* X * anyinput X * X * Return non-zero if input is pending. X */ X Xbool_t Xanyinput() X{ X return (getcnext != NULL); X} X X/* X * do_mlines() - process mode lines for the current file X * X * Returns immediately if the "ml" parameter isn't set. X */ X#define NMLINES 5 /* no. of lines at start/end to check for modelines */ X Xvoid Xdo_mlines() X{ X void chk_mline(); X int i; X LPTR *p; X X if (!P(P_ML)) X return; X X p = Filemem; X for (i=0; i < NMLINES ;i++) { X chk_mline(p->linep->s); X if ((p = nextline(p)) == NULL) X break; X } X X if ((p = prevline(Fileend)) == NULL) X return; X X for (i=0; i < NMLINES ;i++) { X chk_mline(p->linep->s); X if ((p = prevline(p)) == NULL) X break; X } X} X X/* X * chk_mline() - check a single line for a mode string X */ Xstatic void Xchk_mline(s) Xregister char *s; X{ X register char *cs; /* local copy of any modeline found */ X register char *e; X X for (; *s != NUL ;s++) { X if (strncmp(s, "vi:", 3) == 0 || strncmp(s, "ex:", 3) == 0) { X cs = strsave(s+3); X if ((e = strchr(cs, ':')) != NULL) { X *e = NUL; X readcmdline(':', cs); X } X free(cs); X } X } X} END_OF_FILE if test 7937 -ne `wc -c <'main.c'`; then echo shar: \"'main.c'\" unpacked with wrong size! fi # end of 'main.c' fi if test -f 'makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile'\" else echo shar: Extracting \"'makefile'\" \(491 characters\) sed "s/^X//" >'makefile' <<'END_OF_FILE' X# X# Makefile for PC Minix X# X XLDFLAGS= -i -T/usr/tmp XCFLAGS= -DMINIX -O X X XMACH= minix.s X XOBJ= main.s edit.s linefunc.s normal.s cmdline.s hexchars.s \ X misccmds.s help.s ptrfunc.s search.s alloc.s mark.s \ X regexp.s regsub.s \ X screen.s fileio.s param.s undo.s version.s X X X Xall : stevie X Xstevie : $(OBJ) $(MACH) X $(CC) $(LDFLAGS) $(OBJ) $(MACH) -o stevie X @echo stevie fertig X Xctags: ctags.c X $(CC) -o ctags ctags.c X chmem =4096 ctags Xclean : X rm $(OBJ) $(MACH) END_OF_FILE echo shar: 1 control character may be missing from \"'makefile'\" if test 491 -ne `wc -c <'makefile'`; then echo shar: \"'makefile'\" unpacked with wrong size! fi # end of 'makefile' fi if test -f 'makefile.dos' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile.dos'\" else echo shar: Extracting \"'makefile.dos'\" \(1487 characters\) sed "s/^X//" >'makefile.dos' <<'END_OF_FILE' X# X# Makefile for DOS X# X# This makefile is set up for Microsoft C 5.1 X# X X# X# Compact model lets us edit large files, but keep small model code X# XMODEL= /AC XCFLAGS = $(MODEL) X XMACH= dos.obj X XOBJ= main.obj edit.obj linefunc.obj normal.obj cmdline.obj hexchars.obj \ X misccmds.obj help.obj ptrfunc.obj search.obj alloc.obj mark.obj \ X regexp.obj regsub.obj \ X screen.obj fileio.obj param.obj undo.obj version.obj $(MACH) X Xall: stevie.exe X Xmain.obj: main.c X cl -c $(CFLAGS) main.c X Xalloc.obj : alloc.c X cl -c $(CFLAGS) alloc.c X Xedit.obj : edit.c X cl -c $(CFLAGS) edit.c X Xlinefunc.obj : linefunc.c X cl -c $(CFLAGS) linefunc.c X Xnormal.obj : normal.c X cl -c $(CFLAGS) normal.c X Xcmdline.obj : cmdline.c X cl -c $(CFLAGS) cmdline.c X Xhexchars.obj : hexchars.c X cl -c $(CFLAGS) hexchars.c X Xmisccmds.obj : misccmds.c X cl -c $(CFLAGS) misccmds.c X Xhelp.obj : help.c X cl -c $(CFLAGS) help.c X Xptrfunc.obj : ptrfunc.c X cl -c $(CFLAGS) ptrfunc.c X Xsearch.obj : search.c X cl -c $(CFLAGS) search.c X Xmark.obj : mark.c X cl -c $(CFLAGS) mark.c X Xscreen.obj : screen.c X cl -c $(CFLAGS) screen.c X Xfileio.obj : fileio.c X cl -c $(CFLAGS) fileio.c X Xparam.obj : param.c X cl -c $(CFLAGS) param.c X Xdos.obj : dos.c X cl -c $(CFLAGS) dos.c X Xregexp.obj : regexp.c X cl -c $(CFLAGS) regexp.c X Xregsub.obj : regsub.c X cl -c $(CFLAGS) regsub.c X Xundo.obj : undo.c X cl -c $(CFLAGS) undo.c X Xversion.obj : version.c X cl -c $(CFLAGS) version.c X Xstevie.exe : $(OBJ) X cl $(MODEL) *.obj c:\lib\setargv.obj -o stevie.exe /F 6000 -link /NOE END_OF_FILE if test 1487 -ne `wc -c <'makefile.dos'`; then echo shar: \"'makefile.dos'\" unpacked with wrong size! fi # end of 'makefile.dos' fi if test -f 'makefile.min' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile.min'\" else echo shar: Extracting \"'makefile.min'\" \(393 characters\) sed "s/^X//" >'makefile.min' <<'END_OF_FILE' X# X# Makefile for Atari ST Minix X# X XLDFLAGS= -T. XCFLAGS= -O -T. X XMACH= minix.o X XOBJ= main.o edit.o linefunc.o normal.o cmdline.o hexchars.o \ X misccmds.o help.o ptrfunc.o search.o alloc.o mark.o \ X regexp.o regsub.o \ X screen.o fileio.o param.o undo.o version.o X Xall : stevie X Xstevie : $(OBJ) $(MACH) X $(CC) $(LDFLAGS) $(OBJ) $(MACH) -o stevie X chmem =150000 stevie X Xclean : X rm $(OBJ) $(MACH) END_OF_FILE if test 393 -ne `wc -c <'makefile.min'`; then echo shar: \"'makefile.min'\" unpacked with wrong size! fi # end of 'makefile.min' fi if test -f 'makefile.os2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile.os2'\" else echo shar: Extracting \"'makefile.os2'\" \(1451 characters\) sed "s/^X//" >'makefile.os2' <<'END_OF_FILE' X# X# Makefile for OS/2 X# X# The make command with OS/2 is really stupid. X# X X# X# Compact model lets us edit large files, but keep small model code X# XMODEL= /AC XCFLAGS = $(MODEL) X XMACH= os2.obj X XOBJ= main.obj edit.obj linefunc.obj normal.obj cmdline.obj hexchars.obj \ X misccmds.obj help.obj ptrfunc.obj search.obj alloc.obj mark.obj \ X screen.obj fileio.obj param.obj undo.obj version.obj $(MACH) X Xmain.obj: main.c X cl -c $(CFLAGS) main.c X Xalloc.obj : alloc.c X cl -c $(CFLAGS) alloc.c X Xedit.obj : edit.c X cl -c $(CFLAGS) edit.c X Xlinefunc.obj : linefunc.c X cl -c $(CFLAGS) linefunc.c X Xnormal.obj : normal.c X cl -c $(CFLAGS) normal.c X Xcmdline.obj : cmdline.c X cl -c $(CFLAGS) cmdline.c X Xhexchars.obj : hexchars.c X cl -c $(CFLAGS) hexchars.c X Xmisccmds.obj : misccmds.c X cl -c $(CFLAGS) misccmds.c X Xhelp.obj : help.c X cl -c $(CFLAGS) help.c X Xptrfunc.obj : ptrfunc.c X cl -c $(CFLAGS) ptrfunc.c X Xsearch.obj : search.c X cl -c $(CFLAGS) search.c X Xmark.obj : mark.c X cl -c $(CFLAGS) mark.c X Xscreen.obj : screen.c X cl -c $(CFLAGS) screen.c X Xfileio.obj : fileio.c X cl -c $(CFLAGS) fileio.c X Xparam.obj : param.c X cl -c $(CFLAGS) param.c X Xregexp.obj : regexp.c X cl -c $(CFLAGS) regexp.c X Xregsub.obj : regsub.c X cl -c $(CFLAGS) regsub.c X Xos2.obj : os2.c X cl -c $(CFLAGS) os2.c X Xundo.obj : undo.c X cl -c $(CFLAGS) undo.c X Xversion.obj : version.c X cl -c $(CFLAGS) version.c X Xstevie.exe : $(OBJ) X cl $(MODEL) *.obj \pmsdk\lib\setargv.obj -o stevie.exe /F 6000 -link /NOE END_OF_FILE if test 1451 -ne `wc -c <'makefile.os2'`; then echo shar: \"'makefile.os2'\" unpacked with wrong size! fi # end of 'makefile.os2' fi if test -f 'makefile.tos' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile.tos'\" else echo shar: Extracting \"'makefile.tos'\" \(418 characters\) sed "s/^X//" >'makefile.tos' <<'END_OF_FILE' X# X# Makefile for the Atari ST - Sozobon C Compiler X# X XCFLAGS = -O X X.c.o: X $(CC) -c $(CFLAGS) $< X ar rv vi.lib $*.o X XMACH = tos.o X XOBJ = main.o edit.o linefunc.o normal.o cmdline.o hexchars.o \ X misccmds.o help.o ptrfunc.o search.o alloc.o mark.o \ X regexp.o regsub.o \ X screen.o fileio.o param.o undo.o version.o $(MACH) X Xall : stevie.ttp X Xstevie.ttp : $(OBJ) X $(CC) vi.lib -o stevie.ttp X Xclean : X $(RM) $(OBJ) vi.lib END_OF_FILE if test 418 -ne `wc -c <'makefile.tos'`; then echo shar: \"'makefile.tos'\" unpacked with wrong size! fi # end of 'makefile.tos' fi if test -f 'makefile.usg' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile.usg'\" else echo shar: Extracting \"'makefile.usg'\" \(499 characters\) sed "s/^X//" >'makefile.usg' <<'END_OF_FILE' X# X# Makefile for UNIX (System V) X# X XLDFLAGS= XCFLAGS= -g X XMACH= unix.o X XOBJ= main.o edit.o linefunc.o normal.o cmdline.o hexchars.o \ X misccmds.o help.o ptrfunc.o search.o alloc.o mark.o \ X regexp.o regsub.o \ X screen.o fileio.o param.o undo.o version.o term.o X Xall : stevie stevie.doc X Xstevie : $(OBJ) $(MACH) X $(CC) $(LDFLAGS) $(OBJ) $(MACH) -o stevie -lcurses X Xlint : X lint $(OBJ:.o=.c) $(MACH:.o=.c) X Xstevie.doc : stevie.mm X nroff -rB1 -Tlp -mm stevie.mm > stevie.doc X Xclean : X rm $(OBJ) $(MACH) END_OF_FILE if test 499 -ne `wc -c <'makefile.usg'`; then echo shar: \"'makefile.usg'\" unpacked with wrong size! fi # end of 'makefile.usg' fi if test -f 'mark.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mark.c'\" else echo shar: Extracting \"'mark.c'\" \(2315 characters\) sed "s/^X//" >'mark.c' <<'END_OF_FILE' Xstatic char RCSid[] = X"$Header: mark.c,v 1.2 88/10/27 07:54:45 tony Exp $"; X X/* X * Routines to save and retrieve marks. X * X * $Log: mark.c,v $ X * Revision 1.2 88/10/27 07:54:45 tony X * Removed support for Megamax. X * X * Revision 1.1 88/03/20 21:08:32 tony X * Initial revision X * X * X */ X X#include "stevie.h" X X#define NMARKS 10 /* max. # of marks that can be saved */ X Xstruct mark { X char name; X LPTR pos; X}; X Xstatic struct mark mlist[NMARKS]; Xstatic struct mark pcmark; /* previous context mark */ Xstatic bool_t pcvalid = FALSE; /* true if pcmark is valid */ X X/* X * setmark(c) - set mark 'c' at current cursor position X * X * Returns TRUE on success, FALSE if no room for mark or bad name given. X */ Xbool_t Xsetmark(c) Xchar c; X{ X int i; X X if (!isalpha(c)) X return FALSE; X X /* X * If there is already a mark of this name, then just use the X * existing mark entry. X */ X for (i=0; i < NMARKS ;i++) { X if (mlist[i].name == c) { X mlist[i].pos = *Curschar; X return TRUE; X } X } X X /* X * There wasn't a mark of the given name, so find a free slot X */ X for (i=0; i < NMARKS ;i++) { X if (mlist[i].name == NUL) { /* got a free one */ X mlist[i].name = c; X mlist[i].pos = *Curschar; X return TRUE; X } X } X return FALSE; X} X X/* X * setpcmark() - set the previous context mark to the current position X */ Xvoid Xsetpcmark() X{ X pcmark.pos = *Curschar; X pcvalid = TRUE; X} X X/* X * getmark(c) - find mark for char 'c' X * X * Return pointer to LPTR or NULL if no such mark. X */ XLPTR * Xgetmark(c) Xchar c; X{ X register int i; X X if (c == '\'' || c == '`') /* previous context mark */ X return pcvalid ? &(pcmark.pos) : (LPTR *) NULL; X X for (i=0; i < NMARKS ;i++) { X if (mlist[i].name == c) X return &(mlist[i].pos); X } X return (LPTR *) NULL; X} X X/* X * clrall() - clear all marks X * X * Used mainly when trashing the entire buffer during ":e" type commands X */ Xvoid Xclrall() X{ X register int i; X X for (i=0; i < NMARKS ;i++) X mlist[i].name = NUL; X pcvalid = FALSE; X} X X/* X * clrmark(line) - clear any marks for 'line' X * X * Used any time a line is deleted so we don't have marks pointing to X * non-existent lines. X */ Xvoid Xclrmark(line) XLINE *line; X{ X register int i; X X for (i=0; i < NMARKS ;i++) { X if (mlist[i].pos.linep == line) X mlist[i].name = NUL; X } X if (pcvalid && (pcmark.pos.linep == line)) X pcvalid = FALSE; X} END_OF_FILE if test 2315 -ne `wc -c <'mark.c'`; then echo shar: \"'mark.c'\" unpacked with wrong size! fi # end of 'mark.c' fi if test -f 'minix.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'minix.c'\" else echo shar: Extracting \"'minix.c'\" \(3974 characters\) sed "s/^X//" >'minix.c' <<'END_OF_FILE' Xstatic char RCSid[] = X"$Header: minix.c,v 1.3 88/10/29 14:07:36 tony Exp $"; X X/* X * System-dependent routines for Minix-ST X * X * modifications by: Robert Regn rtregn@faui32.uucp X * X * $Log: minix.c,v $ X * Revision 1.3 88/10/29 14:07:36 tony X * Added optional support for termcap. X * X * Revision 1.2 88/10/27 08:20:12 tony X * Added doshell() (same as the unix version), and a no-op version of fixname(). X * X * Revision 1.1 88/10/25 20:14:00 tony X * Initial revision X * X * X */ X X#include "stevie.h" X#include X X/* X * inchar() - get a character from the keyboard X */ Xint Xinchar() X{ X char c[4]; X short n; X X flushbuf(); /* flush any pending output */ X X while ( (n=read(0, c, 3)) < 1) X ; X if (n > 1) { X if (n == 3 && c[0] == 033 && c[1] == '[') /* cursor button */ X switch (c[2]) { X case 'A': return K_UARROW; X case 'B': return K_DARROW; X case 'C': return K_RARROW; X case 'D': return K_LARROW; X default: /* .. stuffin */ ; X } X c[n] = NUL; X stuffin ( c+1); X } X X return *c; X} X X#define BSIZE 2048 Xstatic char outbuf[BSIZE]; Xstatic int bpos = 0; X Xvoid Xflushbuf() X{ X if (bpos != 0) X write(1, outbuf, bpos); X bpos = 0; X} X X/* X * Macro to output a character. Used within this file for speed. X */ X#define outone(c) outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf() X X/* X * Function version for use outside this file. X */ Xvoid Xoutchar(c) Xregister char c; X{ X outbuf[bpos++] = c; X if (bpos >= BSIZE) X flushbuf(); X} X Xvoid Xoutstr(s) Xregister char *s; X{ X while (*s) { X outone(*s++); X } X} X Xvoid Xbeep() X{ X outone('\007'); X} X X/* X * remove(file) - remove a file X */ Xvoid Xremove(file) Xchar *file; X{ X unlink(file); X} X X/* X * rename(of, nf) - rename existing file 'of' to 'nf' X */ Xvoid Xrename(of, nf) Xchar *of, *nf; X{ X unlink(nf); X link(of, nf); X unlink(of); X} X Xvoid Xdelay() X{ X /* not implemented */ X} X Xstatic struct sgttyb ostate; X X/* X * Go into cbreak mode X */ Xvoid Xset_tty() X{ X struct sgttyb nstate; X X ioctl(0, TIOCGETP, &ostate); X nstate = ostate; X nstate.sg_flags &= ~(XTABS|ECHO); X nstate.sg_flags |= CBREAK; X ioctl(0, TIOCSETP, &nstate); X} X X/* X * Restore original terminal modes X */ Xvoid Xreset_tty() X{ X ioctl(0, TIOCSETP, &ostate); X} X Xvoid Xwindinit() X{ X#ifdef TERMCAP X if (t_init() != 1) { X fprintf(stderr, "unknown terminal type\n"); X exit(1); X } X#else X X char *term, *getenv(); X if ((term = getenv("TERM")) == NULL || strcmp(term, "minix") != 0) { X fprintf(stderr, "Invalid terminal type '%s'\n", term); X exit(1); X } X Columns = 80; X P(P_LI) = Rows = 25; X#endif X X set_tty(); X} X Xvoid Xwindexit(r) Xint r; X{ X flushbuf(); X reset_tty(); X exit(r); X} X X#define outone(c) outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf() X Xvoid Xwindgoto(r, c) Xregister int r, c; X{ X#ifdef TERMCAP X char *tgoto(); X#else X r += 1; X c += 1; X#endif X X /* X * Check for overflow once, to save time. X */ X if (bpos + 8 >= BSIZE) X flushbuf(); X X#ifdef TERMCAP X outstr(tgoto(T_CM, c, r)); X#else X outbuf[bpos++] = '\033'; X outbuf[bpos++] = '['; X if (r >= 10) X outbuf[bpos++] = r/10 + '0'; X outbuf[bpos++] = r%10 + '0'; X outbuf[bpos++] = ';'; X if (c >= 10) X outbuf[bpos++] = c/10 + '0'; X outbuf[bpos++] = c%10 + '0'; X outbuf[bpos++] = 'H'; X#endif X} X XFILE * Xfopenb(fname, mode) Xchar *fname; Xchar *mode; X{ X return fopen(fname, mode); X} X Xchar * Xstrchr(s, c) Xchar *s; Xchar c; X{ X char *index(); X X return index(s, c); X} X Xchar * Xfixname(s) Xchar *s; X{ X return s; X} X X/* X * doshell() - run a command or an interactive shell X */ Xvoid Xdoshell(cmd) Xchar *cmd; X{ X char *cp, *getenv(); X char cline[128]; X X outstr("\r\n"); X flushbuf(); X reset_tty(); X X if (cmd == NULL /* don't say sh sh */ X || *cmd == NUL ) { /* handle :! */ X if ((cmd = getenv("SHELL")) == NULL) X cmd = "/bin/sh"; X switch (fork() ) { X case 0: X execl(cmd, "sh", "-i", 0); X emsg("exec failed - "); X exit(1); X X case -1: emsg("fork failed - "); X break; X X default: wait(0); X } X X } X X else if (system(cmd) == -1) X outstr("execution of command failed - "); X set_tty(); X X wait_return(); X} END_OF_FILE if test 3974 -ne `wc -c <'minix.c'`; then echo shar: \"'minix.c'\" unpacked with wrong size! fi # end of 'minix.c' fi if test -f 'misccmds.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'misccmds.c'\" else echo shar: Extracting \"'misccmds.c'\" \(11025 characters\) sed "s/^X//" >'misccmds.c' <<'END_OF_FILE' Xstatic char RCSid[] = X"$Header: misccmds.c,v 1.9 88/09/06 06:50:42 tony Exp $"; X X/* X * Various routines to perform specific editing operations or return X * useful information about the file. X * X * $Log: misccmds.c,v $ X * Revision 1.9 88/09/06 06:50:42 tony X * Fixed a bug with shifts that was introduced when replace mode was added. X * X * Revision 1.8 88/08/30 20:36:51 tony X * After much prodding from Mark, I finally added support for replace mode. X * X * Revision 1.7 88/08/26 08:45:18 tony X * Misc. changes to make lint happy. X * X * Revision 1.6 88/06/26 14:50:19 tony X * Added a new parameter to delline() to control whether screen updates X * may be performed while deleting the line(s). X * X * Revision 1.5 88/05/02 07:34:46 tony X * Fixed a bug in the last change to plines(). The column counter wasn't X * being initialized to zero, so plines() returned garbage. X * X * Revision 1.4 88/05/01 20:09:13 tony X * Modified plines() to support the new "number" parameter. X * X * Revision 1.3 88/04/30 20:00:04 tony X * Added code to openfwd() and openbwd() to support auto-indent. X * X * Revision 1.2 88/04/23 20:39:21 tony X * Reworked openfwd() to avoid a problem when opening a line at the end of X * the file when on the last line of the screen. Opening a line now inserts X * or scrolls appropriately and deals with opens correctly when the cursor X * is on a long line. X * X * Revision 1.1 88/03/20 21:08:41 tony X * Initial revision X * X * X */ X X#include "stevie.h" X Xstatic void openfwd(), openbwd(); X Xextern bool_t did_ai; X X/* X * opencmd X * X * Add a blank line above or below the current line. X */ X Xvoid Xopencmd(dir, can_ai) Xint dir; Xint can_ai; /* if true, consider auto-indent */ X{ X if (dir == FORWARD) X openfwd(can_ai); X else X openbwd(can_ai); X} X Xstatic void Xopenfwd(can_ai) Xint can_ai; X{ X LINE *l; X LPTR *next; X char *s; /* string to be moved to new line, if any */ X int newindex = 0; /* index of the cursor on the new line */ X X /* X * If we're in insert mode, we need to move the remainder of the X * current line onto the new line. Otherwise the new line is left X * blank. X */ X if (State == INSERT || State == REPLACE) X s = &Curschar->linep->s[Curschar->index]; X else X s = ""; X X if ((next = nextline(Curschar)) == NULL) /* open on last line */ X next = Fileend; X X /* X * By asking for as much space as the prior line had we make sure X * that we'll have enough space for any auto-indenting. X */ X if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL) X return; X X if (*s != NUL) X strcpy(l->s, s); /* copy string to new line */ X X else if (can_ai && P(P_AI) && !anyinput()) { X char *p; X X /* X * Copy prior line, and truncate after white space X */ X strcpy(l->s, Curschar->linep->s); X X for (p = l->s; *p == ' ' || *p == TAB ;p++) X ; X *p = NUL; X newindex = p - l->s; X X /* X * If we just did an auto-indent, then we didn't type X * anything on the prior line, and it should be truncated. X */ X if (did_ai) X Curschar->linep->s[0] = NUL; X X did_ai = TRUE; X } X X /* truncate current line at cursor */ X if (State == INSERT || State == REPLACE) X *s = NUL; X X X Curschar->linep->next = l; /* link neighbors to new line */ X next->linep->prev = l; X X l->prev = Curschar->linep; /* link new line to neighbors */ X l->next = next->linep; X X if (next == Fileend) /* new line at end */ X l->num = Curschar->linep->num + LINEINC; X X else if ((l->prev->num) + 1 == l->next->num) /* no gap, renumber */ X renum(); X X else { /* stick it in the middle */ X unsigned long lnum; X lnum = ((long)l->prev->num + (long)l->next->num) / 2; X l->num = lnum; X } X X /* X * Get the cursor to the start of the line, so that 'Cursrow' X * gets set to the right physical line number for the stuff X * that follows... X */ X Curschar->index = 0; X cursupdate(); X X /* X * If we're doing an open on the last logical line, then X * go ahead and scroll the screen up. Otherwise, just insert X * a blank line at the right place. We use calls to plines() X * in case the cursor is resting on a long line. X */ X if (Cursrow + plines(Curschar) == (Rows - 1)) X scrollup(1); X else X s_ins(Cursrow+plines(Curschar), 1); X X *Curschar = *nextline(Curschar); /* cursor moves down */ X Curschar->index = newindex; X X updatescreen(); /* because Botchar is now invalid... */ X X cursupdate(); /* update Cursrow before insert */ X} X Xstatic void Xopenbwd(can_ai) Xint can_ai; X{ X LINE *l; X LINE *prev; X int newindex = 0; X X prev = Curschar->linep->prev; X X if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL) X return; X X Curschar->linep->prev = l; /* link neighbors to new line */ X prev->next = l; X X l->next = Curschar->linep; /* link new line to neighbors */ X l->prev = prev; X X if (can_ai && P(P_AI) && !anyinput()) { X char *p; X X /* X * Copy current line, and truncate after white space X */ X strcpy(l->s, Curschar->linep->s); X X for (p = l->s; *p == ' ' || *p == TAB ;p++) X ; X *p = NUL; X newindex = p - l->s; X X did_ai = TRUE; X } X X Curschar->linep = Curschar->linep->prev; X Curschar->index = newindex; X X if (prev == Filetop->linep) /* new start of file */ X Filemem->linep = l; X X renum(); /* keep it simple - we don't do this often */ X X cursupdate(); /* update Cursrow before insert */ X if (Cursrow != 0) X s_ins(Cursrow, 1); /* insert a physical line */ X X updatescreen(); X} X Xint Xcntllines(pbegin,pend) XLPTR *pbegin, *pend; X{ X LINE *lp; X int lnum = 1; X X for (lp = pbegin->linep; lp != pend->linep ;lp = lp->next) X lnum++; X X return(lnum); X} X X/* X * plines(p) - return the number of physical screen lines taken by line 'p' X */ Xint Xplines(p) XLPTR *p; X{ X register int col = 0; X register char *s; X X s = p->linep->s; X X if (*s == NUL) /* empty line */ X return 1; X X for (; *s != NUL ;s++) { X if ( *s == TAB && !P(P_LS)) X col += P(P_TS) - (col % P(P_TS)); X else X col += chars[(unsigned)(*s & 0xff)].ch_size; X } X X /* X * If list mode is on, then the '$' at the end of X * the line takes up one extra column. X */ X if (P(P_LS)) X col += 1; X /* X * If 'number' mode is on, add another 8. X */ X if (P(P_NU)) X col += 8; X X return ((col + (Columns-1)) / Columns); X} X Xvoid Xfileinfo() X{ X long l1, l2; X char buf[80]; X X if (bufempty()) { X l1 = 0; X l2 = 1; /* sonst div by zero */ X } X else { X l1 = cntllines(Filemem, Curschar); X l2 = cntllines(Filemem, Fileend) - 1; X } X X l1 = cntllines(Filemem, Curschar); X l2 = cntllines(Filemem, Fileend) - 1; X sprintf(buf, "\"%s\"%s line %ld of %ld -- %ld %% --", X (Filename != NULL) ? Filename : "No File", X Changed ? " [Modified]" : "", X l1, l2, (l1 * 100)/l2); X msg(buf); X} X X/* X * gotoline(n) - return a pointer to line 'n' X * X * Returns a pointer to the last line of the file if n is zero, or X * beyond the end of the file. X */ XLPTR * Xgotoline(n) Xint n; X{ X static LPTR l; X X l.index = 0; X X if ( n == 0 ) X l = *prevline(Fileend); X else { X LPTR *p; X X for (l = *Filemem; --n > 0 ;l = *p) X if ((p = nextline(&l)) == NULL) X break; X } X return &l; X} X Xvoid Xinschar(c) Xint c; X{ X register char *p, *pend; X X /* make room for the new char. */ X if ( ! canincrease(1) ) X return; X X if (State != REPLACE) { X p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1]; X pend = &Curschar->linep->s[Curschar->index]; X X for (; p > pend ;p--) X *p = *(p-1); X X *p = c; X X } else { /* replace mode */ X /* X * Once we reach the end of the line, we are effectively X * inserting new text, so make sure the string terminator X * stays out there. X */ X if (gchar(Curschar) == NUL) X Curschar->linep->s[Curschar->index+1] = NUL; X pchar(Curschar, c); X } X X /* X * If we're in insert mode and showmatch mode is set, then X * check for right parens and braces. If there isn't a match, X * then beep. If there is a match AND it's on the screen, then X * flash to it briefly. If it isn't on the screen, don't do anything. X */ X if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) { X LPTR *lpos, csave; X X if ((lpos = showmatch()) == NULL) /* no match, so beep */ X beep(); X else if (LINEOF(lpos) >= LINEOF(Topchar)) { X updatescreen(); /* show the new char first */ X csave = *Curschar; X *Curschar = *lpos; /* move to matching char */ X cursupdate(); X windgoto(Cursrow, Curscol); X delay(); /* brief pause */ X *Curschar = csave; /* restore cursor position */ X cursupdate(); X } X } X X inc(Curschar); X CHANGED; X} X X#if 0 Xvoid Xinsstr(s) Xregister char *s; X{ X register char *p, *endp; X register int k, n = strlen(s); X X /* Move everything in the file over to make */ X /* room for the new string. */ X if (!canincrease(n)) X return; X X endp = &Curschar->linep->s[Curschar->index]; X p = Curschar->linep->s + strlen(Curschar->linep->s) + 1 + n; X X for (; p>endp ;p--) X *p = *(p-n); X X p = &Curschar->linep->s[Curschar->index]; X for ( k=0; kindex+1; i < Curschar->linep->size ;i++) X Curschar->linep->s[i-1] = Curschar->linep->s[i]; X X /* If we just took off the last character of a non-blank line, */ X /* we don't want to end up positioned at the newline. */ X if (fixpos) { X if (gchar(Curschar)==NUL && Curschar->index>0 && State!=INSERT) X Curschar->index--; X } X CHANGED; X X return TRUE; X} X X Xvoid Xdelline(nlines, can_update) Xint nlines; Xbool_t can_update; X{ X register LINE *p, *q; X int doscreen; /* if true, update the screen */ X X doscreen = can_update; X /* X * There's no point in keeping the screen updated if we're X * deleting more than a screen's worth of lines. X */ X if (nlines > (Rows - 1) && can_update) { X doscreen = FALSE; X s_del(Cursrow, Rows-1); /* flaky way to clear rest of screen */ X } X X while ( nlines-- > 0 ) { X X if (bufempty()) /* nothing to delete */ X break; X X if (buf1line()) { /* just clear the line */ X Curschar->linep->s[0] = NUL; X Curschar->index = 0; X break; X } X X p = Curschar->linep->prev; X q = Curschar->linep->next; X X if (p == Filetop->linep) { /* first line of file so... */ X Filemem->linep = q; /* adjust start of file */ X Topchar->linep = q; /* and screen */ X } X p->next = q; X q->prev = p; X X clrmark(Curschar->linep); /* clear marks for the line */ X X /* X * Delete the correct number of physical lines on the screen X */ X if (doscreen) X s_del(Cursrow, plines(Curschar)); X X /* X * If deleting the top line on the screen, adjust Topchar X */ X if (Topchar->linep == Curschar->linep) X Topchar->linep = q; X X free(Curschar->linep->s); X free(Curschar->linep); X X Curschar->linep = q; X Curschar->index = 0; /* is this right? */ X CHANGED; X X /* If we delete the last line in the file, back up */ X if ( Curschar->linep == Fileend->linep) { X Curschar->linep = Curschar->linep->prev; X /* and don't try to delete any more lines */ X break; X } X } X} END_OF_FILE if test 11025 -ne `wc -c <'misccmds.c'`; then echo shar: \"'misccmds.c'\" unpacked with wrong size! fi # end of 'misccmds.c' fi if test -f 'normal.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'normal.c'\" else echo shar: Extracting \"'normal.c'\" \(28721 characters\) sed "s/^X//" >'normal.c' <<'END_OF_FILE' Xstatic char RCSid[] = X"$Header: normal.c,v 1.12 88/11/01 21:32:55 tony Exp $"; X X/* X * Contains the main routine for processing characters in X * command mode as well as routines for handling the operators. X * X * $Log: normal.c,v $ X * Revision 1.12 88/11/01 21:32:55 tony X * Improved the 'put' code. It now modifies the buffer directly instead X * of stuffing things into the input buffer. This is MUCH faster. The X * yank buffer is still statically allocated, but this can be easily X * changed now. X * X * Revision 1.11 88/10/27 08:13:39 tony X * Made the replace command more robust. X * X * Revision 1.10 88/09/16 08:37:05 tony X * No longer beeps when repeated searches fail. X * X * Revision 1.9 88/08/30 20:36:57 tony X * After much prodding from Mark, I finally added support for replace mode. X * X * Revision 1.8 88/08/26 13:46:05 tony X * Added support for the '!' (filter) operator. X * X * Revision 1.7 88/08/26 08:45:24 tony X * Misc. changes to make lint happy. X * X * Revision 1.6 88/07/09 20:38:20 tony X * Added support code for the 'U' command. X * X * Revision 1.5 88/06/28 07:51:09 tony X * Fixed a bug involving redo's of the '~' command. The redo would just X * repeat the replacement last performed instead of switching the case of X * the current character. X * X * Revision 1.4 88/06/26 14:49:45 tony X * Modified calls to delline() for the addition of a new parameter. X * X * Revision 1.3 88/05/04 08:28:09 tony X * Fixed a minor bug with the 'G' command. It now goes to the first X * non-white character on the destination line. X * X * Revision 1.2 88/04/29 14:47:21 tony X * Fixed up several motion commands to clear any pending operator if the X * motion command failed. This fixes several bugs where commands like X * "dtx" would fail, but still perform the indicated operation. X * X * Revision 1.1 88/03/20 21:09:09 tony X * Initial revision X * X * X */ X X#include "stevie.h" X Xstatic void doshift(), dodelete(), doput(), dochange(), dofilter(); Xstatic void tabinout(), startinsert(); Xstatic bool_t dojoin(), doyank(); X X/* X * Macro evaluates true if char 'c' is a valid identifier character X */ X#define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_') X X/* X * Operators X */ X#define NOP 0 /* no pending operation */ X#define DELETE 1 X#define YANK 2 X#define CHANGE 3 X#define LSHIFT 4 X#define RSHIFT 5 X#define FILTER 6 X X#define CLEAROP (operator = NOP) /* clear any pending operator */ X Xstatic int operator = NOP; /* current pending operator */ X X/* X * When a cursor motion command is made, it is marked as being a character X * or line oriented motion. Then, if an operator is in effect, the operation X * becomes character or line oriented accordingly. X * X * Character motions are marked as being inclusive or not. Most char. X * motions are inclusive, but some (e.g. 'w') are not. X * X * Generally speaking, every command in normal() should either clear any X * pending operator (with CLEAROP), or set the motion type variable. X */ X X/* X * Motion types X */ X#define MBAD (-1) /* 'bad' motion type marks unusable yank buf */ X#define MCHAR 0 X#define MLINE 1 X Xstatic int mtype; /* type of the current cursor motion */ Xstatic bool_t mincl; /* true if char motion is inclusive */ X Xstatic LPTR startop; /* cursor pos. at start of operator */ X X/* X * Operators can have counts either before the operator, or between the X * operator and the following cursor motion as in: X * X * d3w or 3dw X * X * If a count is given before the operator, it is saved in opnum. If X * normal() is called with a pending operator, the count in opnum (if X * present) overrides any count that came later. X */ Xstatic int opnum = 0; X X X#define DEFAULT1(x) (((x) == 0) ? 1 : (x)) X X/* X * normal X * X * Execute a command in normal mode. X */ X Xvoid Xnormal(c) Xint c; X{ X int n; X bool_t flag = FALSE; X int type = 0; /* used in some operations to modify type */ X int dir = FORWARD; /* search direction */ X int nchar = NUL; X bool_t finish_op; X X /* X * If there is an operator pending, then the command we take X * this time will terminate it. Finish_op tells us to finish X * the operation before returning this time (unless the operation X * was cancelled. X */ X finish_op = (operator != NOP); X X /* X * If we're in the middle of an operator AND we had a count before X * the operator, then that count overrides the current value of X * Prenum. What this means effectively, is that commands like X * "3dw" get turned into "d3w" which makes things fall into place X * pretty neatly. X */ X if (finish_op) { X if (opnum != 0) X Prenum = opnum; X } else X opnum = 0; X X u_lcheck(); /* clear the "line undo" buffer if we've moved */ X X switch(c & 0xff){ X X case K_HELP: X CLEAROP; X if (help()) { X screenclear(); X updatescreen(); X } X break; X X case CTRL('L'): X CLEAROP; X screenclear(); X updatescreen(); X break; X X case CTRL('D'): X CLEAROP; X if (Prenum) X P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum; X scrollup(P(P_SS)); X onedown(P(P_SS)); X updatescreen(); X break; X X case CTRL('U'): X CLEAROP; X if (Prenum) X P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum; X scrolldown(P(P_SS)); X oneup(P(P_SS)); X updatescreen(); X break; X X /* X * ^F and ^B are neat hacks, but don't take counts. This is very X * code-efficient, and does the right thing. I'll fix it later X * to take a count. The old code took a count, but didn't do the X * right thing in other respects (e.g. leaving some context). X */ X case CTRL('F'): X#if 1 X screenclear(); X stuffin("Lz\nM"); X#else X /* X * Old code X */ X CLEAROP; X n = DEFAULT1(Prenum); X if ( ! onedown(Rows * n) ) X beep(); X cursupdate(); X#endif X break; X X case CTRL('B'): X#if 1 X screenclear(); X stuffin("Hz-M"); X#else X /* X * Old code X */ X CLEAROP; X n = DEFAULT1(Prenum); X if ( ! oneup(Rows * n) ) X beep(); X cursupdate(); X#endif X break; X X case CTRL('E'): X CLEAROP; X scrollup(DEFAULT1(Prenum)); X updatescreen(); X break; X X case CTRL('Y'): X CLEAROP; X scrolldown(DEFAULT1(Prenum)); X updatescreen(); X break; X X case 'z': X CLEAROP; X switch (vgetc()) { X case NL: /* put Curschar at top of screen */ X case CR: X *Topchar = *Curschar; X Topchar->index = 0; X updatescreen(); X break; X X case '.': /* put Curschar in middle of screen */ X n = Rows/2; X goto dozcmd; X X case '-': /* put Curschar at bottom of screen */ X n = Rows-1; X /* fall through */ X X dozcmd: X { X register LPTR *lp = Curschar; X register int l = 0; X X while ((l < n) && (lp != NULL)) { X l += plines(lp); X *Topchar = *lp; X lp = prevline(lp); X } X } X Topchar->index = 0; X updatescreen(); X break; X X default: X beep(); X } X break; X X case CTRL('G'): X CLEAROP; X fileinfo(); X break; X X case 'G': X mtype = MLINE; X *Curschar = *gotoline(Prenum); X beginline(TRUE); X break; X X case 'H': X mtype = MLINE; X *Curschar = *Topchar; X for (n = Prenum; n && onedown(1) ;n--) X ; X beginline(TRUE); X break; X X case 'M': X mtype = MLINE; X *Curschar = *Topchar; X for (n = 0; n < Rows/2 && onedown(1) ;n++) X ; X beginline(TRUE); X break; X X case 'L': X mtype = MLINE; X *Curschar = *prevline(Botchar); X for (n = Prenum; n && oneup(1) ;n--) X ; X beginline(TRUE); X break; X X case 'l': X case K_RARROW: X case ' ': X mtype = MCHAR; X mincl = FALSE; X n = DEFAULT1(Prenum); X while (n--) { X if ( ! oneright() ) X beep(); X } X set_want_col = TRUE; X break; X X case 'h': X case K_LARROW: X case CTRL('H'): X mtype = MCHAR; X mincl = FALSE; X n = DEFAULT1(Prenum); X while (n--) { X if ( ! oneleft() ) X beep(); X } X set_want_col = TRUE; X break; X X case '-': X flag = TRUE; X /* fall through */ X X case 'k': X case K_UARROW: X case CTRL('P'): X mtype = MLINE; X if ( ! oneup(DEFAULT1(Prenum)) ) X beep(); X if (flag) X beginline(TRUE); X break; X X case '+': X case CR: X case NL: X flag = TRUE; X /* fall through */ X X case 'j': X case K_DARROW: X case CTRL('N'): X mtype = MLINE; X if ( ! onedown(DEFAULT1(Prenum)) ) X beep(); X if (flag) X beginline(TRUE); X break; X X /* X * This is a strange motion command that helps make operators X * more logical. It is actually implemented, but not documented X * in the real 'vi'. This motion command actually refers to "the X * current line". Commands like "dd" and "yy" are really an alternate X * form of "d_" and "y_". It does accept a count, so "d3_" works to X * delete 3 lines. X */ X case '_': X lineop: X mtype = MLINE; X onedown(DEFAULT1(Prenum)-1); X break; X X case '|': X mtype = MCHAR; X mincl = TRUE; X beginline(FALSE); X if (Prenum > 0) X *Curschar = *coladvance(Curschar, Prenum-1); X Curswant = Prenum - 1; X break; X X case CTRL(']'): /* :ta to current identifier */ X CLEAROP; X { X char ch; X LPTR save; X X save = *Curschar; X /* X * First back up to start of identifier. This X * doesn't match the real vi but I like it a X * little better and it shouldn't bother anyone. X */ X ch = gchar(Curschar); X while (IDCHAR(ch)) { X if (!oneleft()) X break; X ch = gchar(Curschar); X } X if (!IDCHAR(c)) X oneright(); X X stuffin(":ta "); X /* X * Now grab the chars in the identifier X */ X ch = gchar(Curschar); X while (IDCHAR(ch)) { X stuffin(mkstr(ch)); X if (!oneright()) X break; X ch = gchar(Curschar); X } X stuffin("\n"); X X *Curschar = save; /* restore, in case of error */ X } X break; X X case '%': X mtype = MCHAR; X mincl = TRUE; X { X LPTR *pos; X X if ((pos = showmatch()) == NULL) { X beep(); X CLEAROP; X } else { X setpcmark(); X *Curschar = *pos; X set_want_col = TRUE; X } X } X break; X X /* X * Word Motions X */ X X case 'B': X type = 1; X /* fall through */ X X case 'b': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = bck_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case 'W': X type = 1; X /* fall through */ X X case 'w': X /* X * This is a little strange. To match what the real vi X * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'. X * This seems impolite at first, but it's really more X * what we mean when we say 'cw'. X */ X if (operator == CHANGE) X goto doecmd; X X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = fwd_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case 'E': X type = 1; X /* fall through */ X X case 'e': X doecmd: X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = end_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case '$': X mtype = MCHAR; X mincl = TRUE; X while ( oneright() ) X ; X Curswant = 999; /* so we stay at the end */ X break; X X case '^': X flag = TRUE; X /* fall through */ X X case '0': X mtype = MCHAR; X mincl = TRUE; X beginline(flag); X break; X X case 'x': X CLEAROP; X if (lineempty()) /* can't do it on a blank line */ X beep(); X if (Prenum) X stuffnum(Prenum); X stuffin("d."); X break; X X case 'X': X CLEAROP; X if (!oneleft()) X beep(); X else { X addtobuff(Redobuff, 'X', NUL); X u_saveline(); X delchar(TRUE); X updateline(); X } X break; X X case 'A': X set_want_col = TRUE; X while (oneright()) X ; X /* fall through */ X X case 'a': X CLEAROP; X /* Works just like an 'i'nsert on the next character. */ X if (!lineempty()) X inc(Curschar); X u_saveline(); X startinsert(mkstr(c), FALSE); X break; X X case 'I': X beginline(TRUE); X /* fall through */ X X case 'i': X case K_INSERT: X CLEAROP; X u_saveline(); X startinsert(mkstr(c), FALSE); X break; X X case 'o': X CLEAROP; X u_save(Curschar->linep, Curschar->linep->next); X opencmd(FORWARD, TRUE); X startinsert("o", TRUE); X break; X X case 'O': X CLEAROP; X u_save(Curschar->linep->prev, Curschar->linep); X opencmd(BACKWARD, TRUE); X startinsert("O", TRUE); X break; X X case 'R': X CLEAROP; X u_saveline(); X startinsert("R", FALSE); X break; X X case 'd': X if (operator == DELETE) /* handle 'dd' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = DELETE; X break; X X case '!': X if (operator == FILTER) /* handle '!!' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = FILTER; X break; X X /* X * Some convenient abbreviations... X */ X X case 'D': X stuffin("d$"); X break; X X case 'Y': X if (Prenum) X stuffnum(Prenum); X stuffin("yy"); X break; X X case 'C': X stuffin("c$"); X break; X X case 'c': X if (operator == CHANGE) { /* handle 'cc' */ X CLEAROP; X stuffin("0c$"); X break; X } X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = CHANGE; X break; X X case 'y': X if (operator == YANK) /* handle 'yy' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = YANK; X break; X X case 'p': X doput(FORWARD); X break; X X case 'P': X doput(BACKWARD); X break; X X case '>': X if (operator == RSHIFT) /* handle >> */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = RSHIFT; X break; X X case '<': X if (operator == LSHIFT) /* handle << */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; /* save current position */ X operator = LSHIFT; X break; X X case 's': /* substitute characters */ X if (Prenum) X stuffnum(Prenum); X stuffin("c."); X break; X X case '?': X case '/': X case ':': X CLEAROP; X readcmdline(c, NULL); X break; X X case 'n': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!repsearch(0)) X CLEAROP; X break; X X case 'N': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!repsearch(1)) X CLEAROP; X break; X X /* X * Character searches X */ X case 'T': X dir = BACKWARD; X /* fall through */ X X case 't': X type = 1; X goto docsearch; X X case 'F': X dir = BACKWARD; X /* fall through */ X X case 'f': X docsearch: X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X if ((nchar = vgetc()) == ESC) /* search char */ X break; X if (!searchc(nchar, dir, type)) { X CLEAROP; X beep(); X } X break; X X case ',': X flag = 1; X /* fall through */ X X case ';': X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X if (!crepsearch(flag)) { X CLEAROP; X beep(); X } X break; X X /* X * Function searches X */ X X case '[': X dir = BACKWARD; X /* fall through */ X X case ']': X mtype = MLINE; X set_want_col = TRUE; X if (vgetc() != c) { X beep(); X CLEAROP; X break; X } X X if (!findfunc(dir)) { X beep(); X CLEAROP; X } X break; X X /* X * Marks X */ X X case 'm': X CLEAROP; X if (!setmark(vgetc())) X beep(); X break; X X case '\'': X flag = TRUE; X /* fall through */ X X case '`': X { X LPTR mtmp, *mark = getmark(vgetc()); X X if (mark == NULL) { X beep(); X CLEAROP; X } else { X mtmp = *mark; X setpcmark(); X *Curschar = mtmp; X if (flag) X beginline(TRUE); X } X mtype = flag ? MLINE : MCHAR; X mincl = TRUE; /* ignored if not MCHAR */ X set_want_col = TRUE; X } X break; X X case 'r': X CLEAROP; X if (lineempty()) { /* Nothing to replace */ X beep(); X break; X } X if ((nchar = vgetc()) == ESC) X break; X X if ((nchar & 0x80) || nchar == CR || nchar == NL) { X beep(); X break; X } X u_saveline(); X X /* Change current character. */ X pchar(Curschar, nchar); X X /* Save stuff necessary to redo it */ X addtobuff(Redobuff, 'r', nchar, NULL); X X CHANGED; X updateline(); X break; X X case '~': /* swap case */ X CLEAROP; X if (lineempty()) { X beep(); X break; X } X c = gchar(Curschar); X X if (isalpha(c)) { X if (islower(c)) X c = toupper(c); X else X c = tolower(c); X } X u_saveline(); X X pchar(Curschar, c); /* Change current character. */ X oneright(); X X addtobuff(Redobuff, '~', NULL); X X CHANGED; X updateline(); X X break; X X case 'J': X CLEAROP; X X u_save(Curschar->linep->prev, Curschar->linep->next->next); X X if (!dojoin()) X beep(); X X addtobuff(Redobuff,'J',NULL); X updatescreen(); X break; X X case K_CGRAVE: /* shorthand command */ X CLEAROP; X stuffin(":e #\n"); X break; X X case 'Z': /* write, if changed, and exit */ X if (vgetc() != 'Z') { X beep(); X break; X } X X doxit(); X break; X X case '.': X /* X * If a delete is in effect, we let '.' help out the same X * way that '_' helps for some line operations. It's like X * an 'l', but subtracts one from the count and is inclusive. X */ X if (operator == DELETE || operator == CHANGE) { X if (Prenum != 0) { X n = DEFAULT1(Prenum) - 1; X while (n--) X if (! oneright()) X break; X } X mtype = MCHAR; X mincl = TRUE; X } else { /* a normal 'redo' */ X CLEAROP; X stuffin(Redobuff); X } X break; X X case 'u': X case K_UNDO: X CLEAROP; X u_undo(); X break; X X case 'U': X CLEAROP; X u_lundo(); X break; X X default: X CLEAROP; X beep(); X break; X } X X /* X * If an operation is pending, handle it... X */ X if (finish_op) { /* we just finished an operator */ X if (operator == NOP) /* ... but it was cancelled */ X return; X X switch (operator) { X X case LSHIFT: X case RSHIFT: X doshift(operator, c, nchar, Prenum); X break; X X case DELETE: X dodelete(c, nchar, Prenum); X break; X X case YANK: X doyank(); /* no redo on yank... */ X break; X X case CHANGE: X dochange(c, nchar, Prenum); X break; X X case FILTER: X dofilter(c, nchar, Prenum); X break; X X default: X beep(); X } X operator = NOP; X } X} X X/* X * doshift - handle a shift operation X */ Xstatic void Xdoshift(op, c1, c2, num) Xint op; Xchar c1, c2; Xint num; X{ X LPTR top, bot; X int nlines; X char opchar; X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X nlines = cntllines(&top, &bot); X *Curschar = top; X tabinout((op == LSHIFT), nlines); X X /* construct Redo buff */ X opchar = (op == LSHIFT) ? '<' : '>'; X if (num != 0) X sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2); X else X sprintf(Redobuff, "%c%c%c", opchar, c1, c2); X X /* X * The cursor position afterward is the prior of the two positions. X */ X *Curschar = top; X X /* X * If we were on the last char of a line that got shifted left, X * then move left one so we aren't beyond the end of the line X */ X if (gchar(Curschar) == NUL && Curschar->index > 0) X Curschar->index--; X X updatescreen(); X X if (nlines > P(P_RP)) X smsg("%d lines %ced", nlines, opchar); X} X X/* X * dodelete - handle a delete operation X */ Xstatic void Xdodelete(c1, c2, num) Xchar c1, c2; Xint num; X{ X LPTR top, bot; X int nlines; X int n; X X /* X * Do a yank of whatever we're about to delete. If there's too much X * stuff to fit in the yank buffer, then get a confirmation before X * doing the delete. This is crude, but simple. And it avoids doing X * a delete of something we can't put back if we want. X */ X if (!doyank()) { X msg("yank buffer exceeded: press to confirm"); X if (vgetc() != 'y') { X msg("delete aborted"); X *Curschar = startop; X return; X } X } X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X nlines = cntllines(&top, &bot); X *Curschar = top; X cursupdate(); X X if (mtype == MLINE) { X delline(nlines, TRUE); X } else { X if (!mincl && bot.index != 0) X dec(&bot); X X if (top.linep == bot.linep) { /* del. within line */ X n = bot.index - top.index + 1; X while (n--) X if (!delchar(TRUE)) X break; X } else { /* del. between lines */ X n = Curschar->index; X while (Curschar->index >= n) X if (!delchar(TRUE)) X break; X X top = *Curschar; X *Curschar = *nextline(Curschar); X delline(nlines-2, TRUE); X Curschar->index = 0; X n = bot.index + 1; X while (n--) X if (!delchar(TRUE)) X break; X *Curschar = top; X dojoin(); X } X } X X /* construct Redo buff */ X if (num != 0) X sprintf(Redobuff, "d%d%c%c", num, c1, c2); X else X sprintf(Redobuff, "d%c%c", c1, c2); X X if (mtype == MCHAR && nlines == 1) X updateline(); X else X updatescreen(); X X if (nlines > P(P_RP)) X smsg("%d fewer lines", nlines); X} X X/* X * dofilter - handle a filter operation X */ X X#define ITMP "/tmp/viXXXXXX" X#define OTMP "/tmp/voXXXXXX" X Xstatic char itmp[15]; Xstatic char otmp[15]; X X X/* X * dofilter - filter lines through a command given by the user X * X * We use temp files and the system() routine here. This would normally X * be done using pipes on a UNIX machine, but this is more portable to X * the machines we usually run on. The system() routine needs to be able X * to deal with redirection somehow, and should handle things like looking X * at the PATH env. variable, and adding reasonable extensions to the X * command name given by the user. All reasonable versions of system() X * do this. X */ Xstatic void Xdofilter(c1, c2, num) Xchar c1, c2; Xint num; X{ X char *mktemp(); X static char *lastcmd = NULL; /* the last thing we did */ X char buff[80]; /* buffer for prompting */ X char *p, *q; X char c; X char cmdln[128]; /* filtering command line */ X LPTR top, bot; X int nlines; X int n; X X top = startop; X bot = *Curschar; X X /* X * Finish prompting for the filtering command... X */ X gotocmd(TRUE, '!'); X p = buff; X X /* collect the command string, handling '\b' and @ */ X for (;;) { X c = vgetc(); X if (c==NL || c==CR || c==EOF) X break; X if (c == BS) { X if (p > buff) { X p--; X /* this is gross, but it relies X * only on 'gotocmd' X */ X gotocmd(TRUE, '!'); X for (q=buff; q < p ; q++) X outchar(*q); X } else { X msg(""); X return; /* operator aborted */ X } X continue; X } X if (c == '@') { X p = buff; X gotocmd(TRUE, '!'); X continue; X } X outchar(c); X *p++ = c; X } X *p = '\0'; X X if (buff[0] == '!') { /* use the 'last' command */ X if (lastcmd == NULL) { X emsg("No previous command"); X return; X } X strcpy(buff, lastcmd); X } X X /* X * Remember the current command X */ X if (lastcmd != NULL) X free(lastcmd); X lastcmd = strsave(buff); X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X nlines = cntllines(&top, &bot); X *Curschar = top; X cursupdate(); X X /* X * 1. Form temp file names X * 2. Write the lines to a temp file X * 3. Run the filter command on the temp file X * 4. Read the output of the command into the buffer X * 5. Delete the original lines to be filtered X * 6. Remove the temp files X */ X X strcpy(itmp, ITMP); X strcpy(otmp, OTMP); X X if (mktemp(itmp) == NULL || mktemp(otmp) == NULL) { X emsg("Can't get temp file names"); X return; X } X X if (!writeit(itmp, &top, &bot)) { X emsg("Can't create input temp file"); X return; X } X X sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp); X X if (system(cmdln) != 0) { X emsg("Filter command failed"); X remove(ITMP); X return; X } X X if (readfile(otmp, &bot, TRUE)) { X emsg("Can't read filter output"); X return; X } X X delline(nlines, TRUE); X X remove(itmp); X remove(otmp); X X /* construct Redo buff */ X if (num != 0) X sprintf(Redobuff, "d%d%c%c", num, c1, c2); X else X sprintf(Redobuff, "d%c%c", c1, c2); X X updatescreen(); X X if (nlines > P(P_RP)) X smsg("%d lines filtered", nlines); X} X X/* X * dochange - handle a change operation X */ Xstatic void Xdochange(c1, c2, num) Xchar c1, c2; Xint num; X{ X char sbuf[16]; X bool_t doappend; /* true if we should do append, not insert */ X X doappend = endofline( (lt(Curschar, &startop)) ? &startop: Curschar); X X if (mtype == MLINE) { X msg("multi-line changes not yet supported"); X return; X } X X dodelete(c1, c2, num); X X if (num) X sprintf(sbuf, "c%d%c%c", num, c1, c2); X else X sprintf(sbuf, "c%c%c", c1, c2); X X if (doappend && !lineempty()) X inc(Curschar); X X startinsert(sbuf, FALSE); X} X X#ifndef YBSIZE X#define YBSIZE 4096 X#endif X Xstatic char ybuf[YBSIZE]; Xstatic int ybtype = MBAD; X Xstatic bool_t Xdoyank() X{ X LPTR top, bot; X char *yptr = ybuf; X char *ybend = &ybuf[YBSIZE-1]; X int nlines; X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X nlines = cntllines(&top, &bot); X X ybtype = mtype; /* set the yank buffer type */ X X if (mtype == MLINE) { X top.index = 0; X bot.index = strlen(bot.linep->s); X /* X * The following statement checks for the special case of X * yanking a blank line at the beginning of the file. If X * not handled right, we yank an extra char (a newline). X */ X if (dec(&bot) == -1) { X ybuf[0] = NUL; X if (operator == YANK) X *Curschar = startop; X return TRUE; X } X } else { X if (!mincl) { X if (bot.index) X bot.index--; X } X } X X for (; ltoreq(&top, &bot) ;inc(&top)) { X *yptr = (gchar(&top) != NUL) ? gchar(&top) : NL; X if (++yptr >= ybend) { X msg("yank too big for buffer"); X ybtype = MBAD; X return FALSE; X } X } X X *yptr = NUL; X X if (operator == YANK) { /* restore Curschar if really doing yank */ X *Curschar = startop; X X if (nlines > P(P_RP)) X smsg("%d lines yanked", nlines); X } X X return TRUE; X} X X/* X * inslines(lp, dir, buf) X * X * Inserts lines in the file from the given buffer. Lines are inserted X * before or after "lp" according to the given direction flag. Newlines X * in the buffer result in multiple lines being inserted. The cursor X * is left on the first of the inserted lines. X */ Xstatic void Xinslines(lp, dir, buf) XLINE *lp; Xint dir; Xchar *buf; X{ X register char *cp = buf; X register int len; X char *ep; X LINE *l, *nc = NULL; X LPTR sc; X X if (dir == BACKWARD) X lp = lp->prev; X X do { X if ((ep = strchr(cp, NL)) == NULL) X len = strlen(cp); X else X len = ep - cp; X X l = newline(len+1); X strncpy(l->s, cp, len); X l->s[len] = NUL; X X l->next = lp->next; X l->prev = lp; X lp->next->prev = l; X lp->next = l; X X if (nc == NULL) X nc = l; X X lp = lp->next; X X cp = ep + 1; X } while (ep != NULL); X X if (dir == BACKWARD) /* fix the top line in case we were there */ X Filemem->linep = Filetop->linep->next; X X renum(); X X updatescreen(); X Curschar->linep = nc; X Curschar->index = 0; X} X X/* X * doput(dir) X * X * Put the yank buffer at the current location, using the direction given X * by 'dir'. X */ Xstatic void Xdoput(dir) Xint dir; X{ X if (ybtype == MBAD) { X beep(); X return; X } X X u_saveline(); X X if (ybtype == MLINE) X inslines(Curschar->linep, dir, ybuf); X else { X /* X * If we did a character-oriented yank, and the buffer X * contains multiple lines, the situation is more complex. X * For the moment, we punt, and pretend the user did a X * line-oriented yank. This doesn't actually happen that X * often. X */ X if (strchr(ybuf, NL) != NULL) X inslines(Curschar->linep, dir, ybuf); X else { X char *s; X int len; X X len = strlen(Curschar->linep->s) + strlen(ybuf) + 1; X s = alloc(len); X strcpy(s, Curschar->linep->s); X if (dir == FORWARD) X Curschar->index++; X strcpy(s + Curschar->index, ybuf); X strcat(s, &Curschar->linep->s[Curschar->index]); X free(Curschar->linep->s); X Curschar->linep->s = s; X Curschar->linep->size = len; X updateline(); X } X } X X CHANGED; X} X X/* X * tabinout(inout,num) X * X * If inout==0, add a tab to the begining of the next num lines. X * If inout==1, delete a tab from the beginning of the next num lines. X */ Xstatic void Xtabinout(inout, num) Xint inout; Xint num; X{ X int ntodo = num; X LPTR *p; X X beginline(FALSE); X while ( ntodo-- > 0 ) { X beginline(FALSE); X if ( inout == 0 ) X inschar(TAB); X else { X if ( gchar(Curschar) == TAB ) X delchar(TRUE); X } X if ( ntodo > 0 ) { X if ( (p=nextline(Curschar)) != NULL ) X *Curschar = *p; X else X break; X } X } X} X Xstatic void Xstartinsert(initstr, startln) Xchar *initstr; Xint startln; /* if set, insert point really at start of line */ X{ X char *p, c; X X *Insstart = *Curschar; X if (startln) X Insstart->index = 0; X Ninsert = 0; X Insptr = Insbuff; X for (p=initstr; (c=(*p++))!='\0'; ) X *Insptr++ = c; X X if (*initstr == 'R') X State = REPLACE; X else X State = INSERT; X X if (P(P_MO)) X msg((State == INSERT) ? "Insert Mode" : "Replace Mode"); X} X Xstatic bool_t Xdojoin() X{ X int scol; /* save cursor column */ X int size; /* size of the joined line */ X X if (nextline(Curschar) == NULL) /* on last line */ X return FALSE; X X if (!canincrease(size = strlen(Curschar->linep->next->s))) X return FALSE; X X while (oneright()) /* to end of line */ X ; X X strcat(Curschar->linep->s, Curschar->linep->next->s); X X /* X * Delete the following line. To do this we move the cursor X * there briefly, and then move it back. Don't back up if the X * delete made us the last line. X */ X Curschar->linep = Curschar->linep->next; X scol = Curschar->index; X X if (nextline(Curschar) != NULL) { X delline(1, TRUE); X Curschar->linep = Curschar->linep->prev; X } else X delline(1, TRUE); X X Curschar->index = scol; X X oneright(); /* go to first char. of joined line */ X X if (size != 0) { X /* X * Delete leading white space on the joined line X * and insert a single space. X */ X while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) X delchar(TRUE); X inschar(' '); X } X X return TRUE; X} X Xchar * Xmkstr(c) Xchar c; X{ X static char s[2]; X X s[0] = c; X s[1] = NUL; X X return s; X} END_OF_FILE if test 28721 -ne `wc -c <'normal.c'`; then echo shar: \"'normal.c'\" unpacked with wrong size! fi # end of 'normal.c' fi if test -f 'os2_c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'os2_c'\" else echo shar: Extracting \"'os2_c'\" \(5296 characters\) sed "s/^X//" >'os2_c' <<'END_OF_FILE' Xstatic char RCSid[] = X"$Header: os2.c,v 1.4 88/10/28 14:40:33 tony Exp $"; X X/* X * OS/2 System-dependent routines. X * X * $Log: os2.c,v $ X * Revision 1.4 88/10/28 14:40:33 tony X * Added doshell() to support ":sh" and ":!". X * X * Revision 1.3 88/10/06 10:14:22 tony X * Added fixname() routine which trims the base and extension parts of X * a file name to 8 and 3 characters, and returns a pointer to the X * resulting string. This makes it easier to deal with UNIX-style names X * on stupid systems. X * X * Revision 1.2 88/04/25 16:50:19 tony X * Minor changes for OS/2 version 1.1; also fixed up the RCS header. X * X * Revision 1.1 88/03/21 12:04:23 tony X * Initial revision X * X * X */ X X#define INCL_BASE X#include X#include "stevie.h" X X/* X * inchar() - get a character from the keyboard X */ Xint Xinchar() X{ X int c; X X for (;;beep()) { /* loop until we get a valid character */ X X flushbuf(); /* flush any pending output */ X X switch (c = getch()) { X case 0x1e: X return K_CGRAVE; X case 0: /* special key */ X if (State != NORMAL) { X c = getch(); /* throw away next char */ X continue; /* and loop for another char */ X } X switch (c = getch()) { X case 0x50: X return K_DARROW; X case 0x48: X return K_UARROW; X case 0x4b: X return K_LARROW; X case 0x4d: X return K_RARROW; X case 0x52: X return K_INSERT; X case 0x47: X stuffin("1G"); X return -1; X case 0x4f: X stuffin("G"); X return -1; X case 0x51: X stuffin(mkstr(CTRL('F'))); X return -1; X case 0x49: X stuffin(mkstr(CTRL('B'))); X return -1; X /* X * Hard-code some useful function key macros. X */ X case 0x3b: /* F1 */ X stuffin(":p\n"); X return -1; X case 0x54: /* SF1 */ X stuffin(":p!\n"); X return -1; X case 0x3c: /* F2 */ X stuffin(":n\n"); X return -1; X case 0x55: /* SF2 */ X stuffin(":n!\n"); X return -1; X case 0x3d: /* F3 */ X stuffin(":e #\n"); X return -1; X case 0x3e: /* F4 */ X stuffin(":rew\n"); X return -1; X case 0x57: /* SF4 */ X stuffin(":rew!\n"); X return -1; X case 0x3f: /* F5 */ X stuffin("[["); X return -1; X case 0x40: /* F6 */ X stuffin("]]"); X return -1; X case 0x41: /* F7 */ X stuffin("<<"); X return -1; X case 0x42: /* F8 */ X stuffin(">>"); X return -1; X case 0x43: /* F9 */ X stuffin(":x\n"); X return -1; X case 0x44: /* F10 */ X stuffin(":help\n"); X return -1; X default: X break; X } X break; X X default: X return c; X } X } X} X X#define BSIZE 2048 Xstatic char outbuf[BSIZE]; Xstatic int bpos = 0; X Xvoid Xflushbuf() X{ X if (bpos != 0) X write(1, outbuf, bpos); X bpos = 0; X} X X/* X * Macro to output a character. Used within this file for speed. X */ X#define outone(c) outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf() X X/* X * Function version for use outside this file. X */ Xvoid Xoutchar(c) Xregister char c; X{ X outbuf[bpos++] = c; X if (bpos >= BSIZE) X flushbuf(); X} X Xstatic char cell[2] = { 0, 7 }; X X/* X * outstr(s) - write a string to the console X * X * We implement insert/delete line escape sequences here. This is kind X * of a kludge, but at least it's localized to a single point. X */ Xvoid Xoutstr(s) Xregister char *s; X{ X if (strcmp(s, T_DL) == 0) { /* delete line */ X int r, c; X X flushbuf(); X VioGetCurPos(&r, &c, 0); X VioScrollUp(r, 0, 100, 100, 1, cell, 0); X return; X } X if (strcmp(s, T_IL) == 0) { /* insert line */ X int r, c; X X flushbuf(); X VioGetCurPos(&r, &c, 0); X VioScrollDn(r, 0, 100, 100, 1, cell, 0); X return; X } X X while (*s) { X outone(*s++); X } X} X Xvoid Xbeep() X{ X outone('\007'); X} X Xsleep(n) Xint n; X{ X DosSleep(1000L * n); X} X Xvoid Xdelay() X{ X flushbuf(); X DosSleep(300L); X} X Xvoid Xwindinit() X{ X Columns = 80; X P(P_LI) = Rows = 25; X} X Xvoid Xwindexit(r) Xint r; X{ X flushbuf(); X exit(r); X} X Xvoid Xwindgoto(r, c) Xregister int r, c; X{ X r += 1; X c += 1; X X /* X * Check for overflow once, to save time. X */ X if (bpos + 8 >= BSIZE) X flushbuf(); X X outbuf[bpos++] = '\033'; X outbuf[bpos++] = '['; X if (r >= 10) X outbuf[bpos++] = r/10 + '0'; X outbuf[bpos++] = r%10 + '0'; X outbuf[bpos++] = ';'; X if (c >= 10) X outbuf[bpos++] = c/10 + '0'; X outbuf[bpos++] = c%10 + '0'; X outbuf[bpos++] = 'H'; X} X XFILE * Xfopenb(fname, mode) Xchar *fname; Xchar *mode; X{ X FILE *fopen(); X char modestr[16]; X X sprintf(modestr, "%sb", mode); X return fopen(fname, modestr); X} X X#define PSIZE 128 X X/* X * fixname(s) - fix up a dos name X * X * Takes a name like: X * X * \x\y\z\base.ext X * X * and trims 'base' to 8 characters, and 'ext' to 3. X */ Xchar * Xfixname(s) Xchar *s; X{ X char *strchr(), *strrchr(); X static char f[PSIZE]; X char base[32]; X char ext[32]; X char *p; X int i; X X strcpy(f, s); X X for (i=0; i < PSIZE ;i++) X if (f[i] == '/') X f[i] = '\\'; X X /* X * Split the name into directory, base, extension. X */ X if ((p = strrchr(f, '\\')) != NULL) { X strcpy(base, p+1); X p[1] = '\0'; X } else { X strcpy(base, f); X f[0] = '\0'; X } X X if ((p = strchr(base, '.')) != NULL) { X strcpy(ext, p+1); X *p = '\0'; X } else X ext[0] = '\0'; X X /* X * Trim the base name if necessary. X */ X if (strlen(base) > 8) X base[8] = '\0'; X X if (strlen(ext) > 3) X ext[3] = '\0'; X X /* X * Paste it all back together X */ X strcat(f, base); X strcat(f, "."); X strcat(f, ext); X X return f; X} X Xvoid Xdoshell(cmd) Xchar *cmd; X{ X if (cmd == NULL) X cmd = "cmd.exe"; X X system(cmd); X wait_return(); X} END_OF_FILE if test 5296 -ne `wc -c <'os2_c'`; then echo shar: \"'os2_c'\" unpacked with wrong size! fi # end of 'os2_c' fi echo shar: End of shell archive. exit 0