MICRO_EMACS for PC-AT (Re Posting... This one works..i hope)
Curt Jutzi
jutz at pogo.UUCP
Sun Mar 16 07:46:00 AEST 1986
Well People.... This one works I hope. I spent some time
making sure that KERMIT didn't screw up the files on the
transfere and THIS IS THE SOURCE THAT COMPILES CORRECTLY.
There are a couple of warnings with the microsoft C compiler.
that are relative to an assignment or a compare and it barks
about differant levels of indirection. Don't worry about it.
If this one does not work just post that fact to the net.
I DO NOT WANT TO GET ANY MAIL SAYING THIS DOESN'T WORK.. OK...
<<<<<<<<< I WILL NOT SUPPORT THIS CODE ..... OK >>>>>>>>>>>>>>
COMPILE AT OWN RISK
<____________________ CUT HERE ________________________________________>
/*
*
* ED.H MODULE
*
*
*/
/*
* This file is the general header file for all parts of the MicroEMACS
* display editor. It contains definitions used by everyone, and it contains
* the stuff you have to edit to create a version of the editor for a specific
* operating system and terminal.
*/
#define AMIGA 0 /* AmigaDOS, Lattice */
#define ST520 0 /* ST520, TOS */
#define MWC86 1
#define V7 0 /* V7 UN*X or Coherent */
#define VMS 0 /* VAX/VMS */
#define CPM 0 /* CP/M-86 */
#ifndef MSDOS /* Already defined for Lattice */
#define MSDOS 1 /* MS-DOS */
#endif
#ifdef MSDOS
#define CONRAW 7
#define CONDIO 6
#endif
#define ANSI 1
#define VT52 0 /* VT52 terminal (Zenith). */
#define VT100 0 /* Handle VT100 style keypad. */
#define LK201 0 /* Handle LK201 style keypad. */
#define RAINBOW 0 /* Use Rainbow fast video. */
#define TERMCAP 0 /* Use TERMCAP */
#define CVMVAS 1 /* C-V, M-V arg. in screens. */
#define NFILEN 80 /* # of bytes, file name */
#define NBUFN 16 /* # of bytes, buffer name */
#define NLINE 256 /* # of bytes, line */
#define NKBDM 256 /* # of strokes, keyboard macro */
#define NPAT 80 /* # of bytes, pattern */
#define HUGE 1000 /* Huge number */
#define AGRAVE 0x60 /* M- prefix, Grave (LK201) */
#define METACH 0x1B /* M- prefix, Control-[, ESC */
#define CTMECH 0x1C /* C-M- prefix, Control-\ */
#define EXITCH 0x1D /* Exit level, Control-] */
#define CTRLCH 0x1E /* C- prefix, Control-^ */
#define HELPCH 0x1F /* Help key, Control-_ */
#define CTRL 0x0100 /* Control flag, or'ed in */
#define META 0x0200 /* Meta flag, or'ed in */
#define CTLX 0x0400 /* ^X flag, or'ed in */
#define FALSE 0 /* False, no, bad, etc. */
#define TRUE 1 /* True, yes, good, etc. */
#define ABORT 2 /* Death, ^G, abort, etc. */
#define FIOSUC 0 /* File I/O, success. */
#define FIOFNF 1 /* File I/O, file not found. */
#define FIOEOF 2 /* File I/O, end of file. */
#define FIOERR 3 /* File I/O, error. */
#define CFCPCN 0x0001 /* Last command was C-P, C-N */
#define CFKILL 0x0002 /* Last command was a kill */
/*
* There is a window structure allocated for every active display window. The
* windows are kept in a big list, in top to bottom screen order, with the
* listhead at "wheadp". Each window contains its own values of dot and mark.
* The flag field contains some bits that are set by commands to guide
* redisplay; although this is a bit of a compromise in terms of decoupling,
* the full blown redisplay is just too expensive to run for every input
* character.
*/
typedef struct WINDOW {
struct WINDOW *w_wndp; /* Next window */
struct BUFFER *w_bufp; /* Buffer displayed in window */
struct LINE *w_linep; /* Top line in the window */
struct LINE *w_dotp; /* Line containing "." */
short w_doto; /* Byte offset for "." */
struct LINE *w_markp; /* Line containing "mark" */
short w_marko; /* Byte offset for "mark" */
char w_toprow; /* Origin 0 top row of window */
char w_ntrows; /* # of rows of text in window */
char w_force; /* If NZ, forcing row. */
char w_flag; /* Flags. */
} WINDOW;
#define WFFORCE 0x01 /* Window needs forced reframe */
#define WFMOVE 0x02 /* Movement from line to line */
#define WFEDIT 0x04 /* Editing within a line */
#define WFHARD 0x08 /* Better to a full display */
#define WFMODE 0x10 /* Update mode line. */
/*
* Text is kept in buffers. A buffer header, described below, exists for every
* buffer in the system. The buffers are kept in a big list, so that commands
* that search for a buffer by name can find the buffer header. There is a
* safe store for the dot and mark in the header, but this is only valid if
* the buffer is not being displayed (that is, if "b_nwnd" is 0). The text for
* the buffer is kept in a circularly linked list of lines, with a pointer to
* the header line in "b_linep".
*/
typedef struct BUFFER {
struct BUFFER *b_bufp; /* Link to next BUFFER */
struct LINE *b_dotp; /* Link to "." LINE structure */
short b_doto; /* Offset of "." in above LINE */
struct LINE *b_markp; /* The same as the above two, */
short b_marko; /* but for the "mark" */
struct LINE *b_linep; /* Link to the header LINE */
char b_nwnd; /* Count of windows on buffer */
char b_flag; /* Flags */
char b_fname[NFILEN]; /* File name */
char b_bname[NBUFN]; /* Buffer name */
} BUFFER;
#define BFTEMP 0x01 /* Internal temporary buffer */
#define BFCHG 0x02 /* Changed since last write */
/*
* The starting position of a region, and the size of the region in
* characters, is kept in a region structure. Used by the region commands.
*/
typedef struct {
struct LINE *r_linep; /* Origin LINE address. */
short r_offset; /* Origin LINE offset. */
short r_size; /* Length in characters. */
} REGION;
/*
* All text is kept in circularly linked lists of "LINE" structures. These
* begin at the header line (which is the blank line beyond the end of the
* buffer). This line is pointed to by the "BUFFER". Each line contains a the
* number of bytes in the line (the "used" size), the size of the text array,
* and the text. The end of line is not stored as a byte; it's implied. Future
* additions will include update hints, and a list of marks into the line.
*/
typedef struct LINE {
struct LINE *l_fp; /* Link to the next line */
struct LINE *l_bp; /* Link to the previous line */
short l_size; /* Allocated size */
short l_used; /* Used size */
char l_text[1]; /* A bunch of characters. */
} LINE;
#define lforw(lp) ((lp)->l_fp)
#define lback(lp) ((lp)->l_bp)
#define lgetc(lp, n) ((lp)->l_text[(n)]&0xFF)
#define lputc(lp, n, c) ((lp)->l_text[(n)]=(c))
#define llength(lp) ((lp)->l_used)
/*
* The editor communicates with the display using a high level interface. A
* "TERM" structure holds useful variables, and indirect pointers to routines
* that do useful operations. The low level get and put routines are here too.
* This lets a terminal, in addition to having non standard commands, have
* funny get and put character code too. The calls might get changed to
* "termp->t_field" style in the future, to make it possible to run more than
* one terminal type.
*/
typedef struct {
short t_nrow; /* Number of rows. */
short t_ncol; /* Number of columns. */
int (*t_open)(); /* Open terminal at the start. */
int (*t_close)(); /* Close terminal at end. */
int (*t_getchar)(); /* Get character from keyboard. */
int (*t_putchar)(); /* Put character to display. */
int (*t_flush)(); /* Flush output buffers. */
int (*t_move)(); /* Move the cursor, origin 0. */
int (*t_eeol)(); /* Erase to end of line. */
int (*t_eeop)(); /* Erase to end of page. */
int (*t_beep)(); /* Beep. */
} TERM;
extern int fillcol; /* Fill column */
extern int currow; /* Cursor row */
extern int curcol; /* Cursor column */
extern int thisflag; /* Flags, this command */
extern int lastflag; /* Flags, last command */
extern int curgoal; /* Goal for C-P, C-N */
extern int mpresf; /* Stuff in message line */
extern int sgarbf; /* State of screen unknown */
extern WINDOW *curwp; /* Current window */
extern BUFFER *curbp; /* Current buffer */
extern WINDOW *wheadp; /* Head of list of windows */
extern BUFFER *bheadp; /* Head of list of buffers */
extern BUFFER *blistp; /* Buffer for C-X C-B */
extern short kbdm[]; /* Holds kayboard macro data */
extern short *kbdmip; /* Input pointer for above */
extern short *kbdmop; /* Output pointer for above */
extern char pat[]; /* Search pattern */
extern TERM term; /*(Terminal information. */
extern BUFFER *bfind(); /* Lookup a buffer by name */
extern WINDOW *wpopup(); /* Pop up window creation */
extern LINE *lalloc(); /* Allocate a line */
/*
* ANSI.C MODULE
*
*/
/*
*
* The routines in this file provide support for ANSI style terminals
* over a serial line. The serial I/O services are provided by routines in
* "termio.c". It compiles into nothing if not an ANSI device.
*/
#include <stdio.h>
#include "ed.h"
#if ANSI
#define NROW 25 /* Screen size. */
#define NCOL 80 /* Edit if you want to. */
#define BEL 0x07 /* BEL character. */
#define ESC 0x1B /* ESC character. */
extern int ttopen(); /* Forward references. */
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int ansimove();
extern int ansieeol();
extern int ansieeop();
extern int ansibeep();
extern int ansiopen();
/*
* Standard terminal interface dispatch table. Most of the fields point into
* "termio" code.
*/
TERM term = {
NROW-1,
NCOL,
ansiopen,
ttclose,
ttgetc,
ttputc,
ttflush,
ansimove,
ansieeol,
ansieeop,
ansibeep
};
ansimove(row, col)
{
ttputc(ESC);
ttputc('[');
ansiparm(row+1);
ttputc(';');
ansiparm(col+1);
ttputc('H');
}
ansieeol()
{
ttputc(ESC);
ttputc('[');
ttputc('K');
}
ansieeop()
{
ttputc(ESC);
ttputc('[');
ttputc('J');
}
ansibeep()
{
ttputc(BEL);
ttflush();
}
ansiparm(n)
register int n;
{
register int q;
q = n/10;
if (q != 0)
ansiparm(q);
ttputc((n%10) + '0');
}
#endif
ansiopen()
{
#if V7
register char *cp;
char *getenv();
if ((cp = getenv("TERM")) == NULL) {
puts("Shell variable TERM not defined!");
exit(1);
}
if (strcmp(cp, "vt100") != 0) {
puts("Terminal type not 'vt100'!");
exit(1);
}
#endif
ttopen();
}
/*
*
* BASIC.C MODULE
*
*/
/*
* The routines in this file move the cursor around on the screen. They
* compute a new value for the cursor, then adjust ".". The display code
* always updates the cursor location, so only moves between lines, or
* functions that adjust the top line in the window and invalidate the
* framing, are hard.
*/
#include <stdio.h>
#include "ed.h"
/*
* Move the cursor to the
* beginning of the current line.
* Trivial.
*/
gotobol(f, n)
{
curwp->w_doto = 0;
return (TRUE);
}
/*
* Move the cursor backwards by "n" characters. If "n" is less than zero call
* "forwchar" to actually do the move. Otherwise compute the new cursor
* location. Error if you try and move out of the buffer. Set the flag if the
* line pointer for dot changes.
*/
backchar(f, n)
register int n;
{
register LINE *lp;
if (n < 0)
return (forwchar(f, -n));
while (n--) {
if (curwp->w_doto == 0) {
if ((lp=lback(curwp->w_dotp)) == curbp->b_linep)
return (FALSE);
curwp->w_dotp = lp;
curwp->w_doto = llength(lp);
curwp->w_flag |= WFMOVE;
} else
curwp->w_doto--;
}
return (TRUE);
}
/*
* Move the cursor to the end of the current line. Trivial. No errors.
*/
gotoeol(f, n)
{
curwp->w_doto = llength(curwp->w_dotp);
return (TRUE);
}
/*
* Move the cursor forwwards by "n" characters. If "n" is less than zero call
* "backchar" to actually do the move. Otherwise compute the new cursor
* location, and move ".". Error if you try and move off the end of the
* buffer. Set the flag if the line pointer for dot changes.
*/
forwchar(f, n)
register int n;
{
if (n < 0)
return (backchar(f, -n));
while (n--) {
if (curwp->w_doto == llength(curwp->w_dotp)) {
if (curwp->w_dotp == curbp->b_linep)
return (FALSE);
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_doto = 0;
curwp->w_flag |= WFMOVE;
} else
curwp->w_doto++;
}
return (TRUE);
}
/*
* Goto the beginning of the buffer. Massive adjustment of dot. This is
* considered to be hard motion; it really isn't if the original value of dot
* is the same as the new value of dot. Normally bound to "M-<".
*/
gotobob(f, n)
{
curwp->w_dotp = lforw(curbp->b_linep);
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Move to the end of the buffer. Dot is always put at the end of the file
* (ZJ). The standard screen code does most of the hard parts of update.
* Bound to "M->".
*/
gotoeob(f, n)
{
curwp->w_dotp = curbp->b_linep;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Move forward by full lines. If the number of lines to move is less than
* zero, call the backward line function to actually do it. The last command
* controls how the goal column is set. Bound to "C-N". No errors are
* possible.
*/
forwline(f, n)
{
register LINE *dlp;
if (n < 0)
return (backline(f, -n));
if ((lastflag&CFCPCN) == 0) /* Reset goal if last */
curgoal = curcol; /* not C-P or C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && dlp!=curbp->b_linep)
dlp = lforw(dlp);
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp); /* modified cej was 'elp' */
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* This function is like "forwline", but goes backwards. The scheme is exactly
* the same. Check for arguments that are less than zero and call your
* alternate. Figure out the new line and call "movedot" to perform the
* motion. No errors are possible. Bound to "C-P".
*/
backline(f, n)
{
register LINE *dlp;
if (n < 0)
return (forwline(f, -n));
if ((lastflag&CFCPCN) == 0) /* Reset goal if the */
curgoal = curcol; /* last isn't C-P, C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && lback(dlp)!=curbp->b_linep)
dlp = lback(dlp);
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* This routine, given a pointer to a LINE, and the current cursor goal
* column, return the best choice for the offset. The offset is returned.
* Used by "C-N" and "C-P".
*/
getgoal(dlp)
register LINE *dlp;
{
register int c;
register int col;
register int newcol;
register int dbo;
col = 0;
dbo = 0;
while (dbo != llength(dlp)) {
c = lgetc(dlp, dbo);
newcol = col;
if (c == '\t')
newcol |= 0x07;
else if (c<0x20 || c==0x7F)
++newcol;
++newcol;
if (newcol > curgoal)
break;
col = newcol;
++dbo;
}
return (dbo);
}
/*
* Scroll forward by a specified number of lines, or by a full page if no
* argument. Bound to "C-V". The "2" in the arithmetic on the window size is
* the overlap; this value is the default overlap value in ITS EMACS. Because
* this zaps the top line in the display window, we have to do a hard update.
*/
forwpage(f, n)
register int n;
{
register LINE *lp;
if (f == FALSE) {
n = curwp->w_ntrows - 2; /* Default scroll. */
if (n <= 0) /* Forget the overlap */
n = 1; /* if tiny window. */
} else if (n < 0)
return (backpage(f, -n));
#if CVMVAS
else /* Convert from pages */
n *= curwp->w_ntrows; /* to lines. */
#endif
lp = curwp->w_linep;
while (n-- && lp!=curbp->b_linep)
lp = lforw(lp);
curwp->w_linep = lp;
curwp->w_dotp = lp;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* This command is like "forwpage", but it goes backwards. The "2", like
* above, is the overlap between the two windows. The value is from the ITS
* EMACS manual. Bound to "M-V". We do a hard update for exactly the same
* reason.
*/
backpage(f, n)
register int n;
{
register LINE *lp;
if (f == FALSE) {
n = curwp->w_ntrows - 2; /* Default scroll. */
if (n <= 0) /* Don't blow up if the */
n = 1; /* window is tiny. */
} else if (n < 0)
return (forwpage(f, -n));
#if CVMVAS
else /* Convert from pages */
n *= curwp->w_ntrows; /* to lines. */
#endif
lp = curwp->w_linep;
while (n-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_dotp = lp;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Set the mark in the current window to the value of "." in the window. No
* errors are possible. Bound to "M-.".
*/
setmark(f, n)
{
curwp->w_markp = curwp->w_dotp;
curwp->w_marko = curwp->w_doto;
mlwrite("[Mark set]");
return (TRUE);
}
/*
* Swap the values of "." and "mark" in the current window. This is pretty
* easy, bacause all of the hard work gets done by the standard routine
* that moves the mark about. The only possible error is "no mark". Bound to
* "C-X C-X".
*/
swapmark(f, n)
{
register LINE *odotp;
register int odoto;
if (curwp->w_markp == NULL) {
mlwrite("No mark in this window");
return (FALSE);
}
odotp = curwp->w_dotp;
odoto = curwp->w_doto;
curwp->w_dotp = curwp->w_markp;
curwp->w_doto = curwp->w_marko;
curwp->w_markp = odotp;
curwp->w_marko = odoto;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
*
* FILEIO.C MODULE
*
*/
/*
* The routines in this file read and write ASCII files from the disk. All of
* the knowledge about files are here. A better message writing scheme should
* be used.
*/
#include <stdio.h>
#include "ed.h"
FILE *ffp; /* File pointer, all functions. */
/*
* Open a file for reading.
*/
ffropen(fn)
char *fn;
{
if ((ffp=fopen(fn, "r")) == NULL)
return (FIOFNF);
return (FIOSUC);
}
/*
* Open a file for writing. Return TRUE if all is well, and FALSE on error
* (cannot create).
*/
ffwopen(fn)
char *fn;
{
#if VMS
register int fd;
if ((fd=creat(fn, 0666, "rfm=var", "rat=cr")) < 0
|| (ffp=fdopen(fd, "w")) == NULL) {
#else
if ((ffp=fopen(fn, "w")) == NULL) {
#endif
mlwrite("Cannot open file for writing");
return (FIOERR);
}
return (FIOSUC);
}
/*
* Close a file. Should look at the status in all systems.
*/
ffclose()
{
#if V7
if (fclose(ffp) != FALSE) {
mlwrite("Error closing file");
return(FIOERR);
}
return(FIOSUC);
#endif
fclose(ffp);
return (FIOSUC);
}
/*
* Write a line to the already opened file. The "buf" points to the buffer,
* and the "nbuf" is its length, less the free newline. Return the status.
* Check only at the newline.
*/
ffputline(buf, nbuf)
char buf[];
{
register int i;
for (i = 0; i < nbuf; ++i)
fputc(buf[i]&0xFF, ffp);
fputc('\n', ffp);
if (ferror(ffp)) {
mlwrite("Write I/O error");
return (FIOERR);
}
return (FIOSUC);
}
/*
* Read a line from a file, and store the bytes in the supplied buffer. The
* "nbuf" is the length of the buffer. Complain about long lines and lines
* at the end of the file that don't have a newline present. Check for I/O
* errors too. Return status.
*/
ffgetline(buf, nbuf)
register char buf[];
{
register int c;
register int i;
i = 0;
while ((c = fgetc(ffp)) != EOF && c != '\n') {
if (i >= nbuf-1) {
mlwrite("File has long line");
return (FIOERR);
}
buf[i++] = c;
}
if (c == EOF) {
if (ferror(ffp)) {
mlwrite("File read error");
return (FIOERR);
}
if (i != 0) {
mlwrite("File has funny line at EOF");
return (FIOERR);
}
return (FIOEOF);
}
buf[i] = 0;
return (FIOSUC);
}
/*
*
* LINE.C MODULE
*
*/
/*
* The functions in this file are a general set of line management utilities.
* They are the only routines that touch the text. They also touch the buffer
* and window structures, to make sure that the necessary updating gets done.
* There are routines in this file that handle the kill buffer too. It isn't
* here for any good reason.
*
* Note that this code only updates the dot and mark values in the window list.
* Since all the code acts on the current window, the buffer that we are
* editing must be being displayed, which means that "b_nwnd" is non zero,
* which means that the dot and mark values in the buffer headers are nonsense.
*/
#include <stdio.h>
#include "ed.h"
#define NBLOCK 16 /* Line block chunk size */
#define KBLOCK 256 /* Kill buffer block size */
char *kbufp = NULL; /* Kill buffer data */
int kused = 0; /* # of bytes used in KB */
int ksize = 0; /* # of bytes allocated in KB */
/*
* This routine allocates a block of memory large enough to hold a LINE
* containing "used" characters. The block is always rounded up a bit. Return
* a pointer to the new block, or NULL if there isn't any memory left. Print a
* message in the message line if no space.
*/
LINE *
lalloc(used)
register int used;
{
register LINE *lp;
register int size;
size = (used+NBLOCK-1) & ~(NBLOCK-1);
if (size == 0) /* Assume that an empty */
size = NBLOCK; /* line is for type-in. */
if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
mlwrite("Cannot allocate %d bytes", size);
return (NULL);
}
lp->l_size = size;
lp->l_used = used;
return (lp);
}
/*
* Delete line "lp". Fix all of the links that might point at it (they are
* moved to offset 0 of the next line. Unlink the line from whatever buffer it
* might be in. Release the memory. The buffers are updated too; the magic
* conditions described in the above comments don't hold here.
*/
lfree(lp)
register LINE *lp;
{
register BUFFER *bp;
register WINDOW *wp;
wp = wheadp;
while (wp != NULL) {
if (wp->w_linep == lp)
wp->w_linep = lp->l_fp;
if (wp->w_dotp == lp) {
wp->w_dotp = lp->l_fp;
wp->w_doto = 0;
}
if (wp->w_markp == lp) {
wp->w_markp = lp->l_fp;
wp->w_marko = 0;
}
wp = wp->w_wndp;
}
bp = bheadp;
while (bp != NULL) {
if (bp->b_nwnd == 0) {
if (bp->b_dotp == lp) {
bp->b_dotp = lp->l_fp;
bp->b_doto = 0;
}
if (bp->b_markp == lp) {
bp->b_markp = lp->l_fp;
bp->b_marko = 0;
}
}
bp = bp->b_bufp;
}
lp->l_bp->l_fp = lp->l_fp;
lp->l_fp->l_bp = lp->l_bp;
free((char *) lp);
}
/*
* This routine gets called when a character is changed in place in the current
* buffer. It updates all of the required flags in the buffer and window
* system. The flag used is passed as an argument; if the buffer is being
* displayed in more than 1 window we change EDIT t HARD. Set MODE if the
* mode line needs to be updated (the "*" has to be set).
*/
lchange(flag)
register int flag;
{
register WINDOW *wp;
if (curbp->b_nwnd != 1) /* Ensure hard. */
flag = WFHARD;
if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
flag |= WFMODE; /* update mode lines. */
curbp->b_flag |= BFCHG;
}
wp = wheadp;
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= flag;
wp = wp->w_wndp;
}
}
/*
* Insert "n" copies of the character "c" at the current location of dot. In
* the easy case all that happens is the text is stored in the line. In the
* hard case, the line has to be reallocated. When the window list is updated,
* take special care; I screwed it up once. You always update dot in the
* current window. You update mark, and a dot in another window, if it is
* greater than the place where you did the insert. Return TRUE if all is
* well, and FALSE on errors.
*/
linsert(n, c)
{
register char *cp1;
register char *cp2;
register LINE *lp1;
register LINE *lp2;
register LINE *lp3;
register int doto;
register int i;
register WINDOW *wp;
lchange(WFEDIT);
lp1 = curwp->w_dotp; /* Current line */
if (lp1 == curbp->b_linep) { /* At the end: special */
if (curwp->w_doto != 0) {
mlwrite("bug: linsert");
return (FALSE);
}
if ((lp2=lalloc(n)) == NULL) /* Allocate new line */
return (FALSE);
lp3 = lp1->l_bp; /* Previous line */
lp3->l_fp = lp2; /* Link in */
lp2->l_fp = lp1;
lp1->l_bp = lp2;
lp2->l_bp = lp3;
for (i=0; i<n; ++i)
lp2->l_text[i] = c;
curwp->w_dotp = lp2;
curwp->w_doto = n;
return (TRUE);
}
doto = curwp->w_doto; /* Save for later. */
if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
if ((lp2=lalloc(lp1->l_used+n)) == NULL)
return (FALSE);
cp1 = &lp1->l_text[0];
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
cp2 += n;
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp2;
lp2->l_fp = lp1->l_fp;
lp1->l_fp->l_bp = lp2;
lp2->l_bp = lp1->l_bp;
free((char *) lp1);
} else { /* Easy: in place */
lp2 = lp1; /* Pretend new line */
lp2->l_used += n;
cp2 = &lp1->l_text[lp1->l_used];
cp1 = cp2-n;
while (cp1 != &lp1->l_text[doto])
*--cp2 = *--cp1;
}
for (i=0; i<n; ++i) /* Add the characters */
lp2->l_text[doto+i] = c;
wp = wheadp; /* Update windows */
while (wp != NULL) {
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1) {
wp->w_dotp = lp2;
if (wp==curwp || wp->w_doto>doto)
wp->w_doto += n;
}
if (wp->w_markp == lp1) {
wp->w_markp = lp2;
if (wp->w_marko > doto)
wp->w_marko += n;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* Insert a newline into the buffer at the current location of dot in the
* current window. The funny ass-backwards way it does things is not a botch;
* it just makes the last line in the file not a special case. Return TRUE if
* everything works out and FALSE on error (memory allocation failure). The
* update of dot and mark is a bit easier then in the above case, because the
* split forces more updating.
*/
lnewline()
{
register char *cp1;
register char *cp2;
register LINE *lp1;
register LINE *lp2;
register int doto;
register WINDOW *wp;
lchange(WFHARD);
lp1 = curwp->w_dotp; /* Get the address and */
doto = curwp->w_doto; /* offset of "." */
if ((lp2=lalloc(doto)) == NULL) /* New first half line */
return (FALSE);
cp1 = &lp1->l_text[0]; /* Shuffle text around */
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
cp2 = &lp1->l_text[0];
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_used -= doto;
lp2->l_bp = lp1->l_bp;
lp1->l_bp = lp2;
lp2->l_bp->l_fp = lp2;
lp2->l_fp = lp1;
wp = wheadp; /* Windows */
while (wp != NULL) {
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1) {
if (wp->w_doto < doto)
wp->w_dotp = lp2;
else
wp->w_doto -= doto;
}
if (wp->w_markp == lp1) {
if (wp->w_marko < doto)
wp->w_markp = lp2;
else
wp->w_marko -= doto;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* This function deletes "n" bytes, starting at dot. It understands how do deal
* with end of lines, etc. It returns TRUE if all of the characters were
* deleted, and FALSE if they were not (because dot ran into the end of the
* buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
*/
ldelete(n, kflag)
{
register char *cp1;
register char *cp2;
register LINE *dotp;
register int doto;
register int chunk;
register WINDOW *wp;
while (n != 0) {
dotp = curwp->w_dotp;
doto = curwp->w_doto;
if (dotp == curbp->b_linep) /* Hit end of buffer. */
return (FALSE);
chunk = dotp->l_used-doto; /* Size of chunk. */
if (chunk > n)
chunk = n;
if (chunk == 0) { /* End of line, merge. */
lchange(WFHARD);
if (ldelnewline() == FALSE
|| (kflag!=FALSE && kinsert('\n')==FALSE))
return (FALSE);
--n;
continue;
}
lchange(WFEDIT);
cp1 = &dotp->l_text[doto]; /* Scrunch text. */
cp2 = cp1 + chunk;
if (kflag != FALSE) { /* Kill? */
while (cp1 != cp2) {
if (kinsert(*cp1) == FALSE)
return (FALSE);
++cp1;
}
cp1 = &dotp->l_text[doto];
}
while (cp2 != &dotp->l_text[dotp->l_used])
*cp1++ = *cp2++;
dotp->l_used -= chunk;
wp = wheadp; /* Fix windows */
while (wp != NULL) {
if (wp->w_dotp==dotp && wp->w_doto>=doto) {
wp->w_doto -= chunk;
if (wp->w_doto < doto)
wp->w_doto = doto;
}
if (wp->w_markp==dotp && wp->w_marko>=doto) {
wp->w_marko -= chunk;
if (wp->w_marko < doto)
wp->w_marko = doto;
}
wp = wp->w_wndp;
}
n -= chunk;
}
return (TRUE);
}
/*
* Delete a newline. Join the current line with the next line. If the next line
* is the magic header line always return TRUE; merging the last line with the
* header line can be thought of as always being a successful operation, even
* if nothing is done, and this makes the kill buffer work "right". Easy cases
* can be done by shuffling data around. Hard cases require that lines be moved
* about in memory. Return FALSE on error and TRUE if all looks ok. Called by
* "ldelete" only.
*/
ldelnewline()
{
register char *cp1;
register char *cp2;
register LINE *lp1;
register LINE *lp2;
register LINE *lp3;
register WINDOW *wp;
lp1 = curwp->w_dotp;
lp2 = lp1->l_fp;
if (lp2 == curbp->b_linep) { /* At the buffer end. */
if (lp1->l_used == 0) /* Blank line. */
lfree(lp1);
return (TRUE);
}
if (lp2->l_used <= lp1->l_size-lp1->l_used) {
cp1 = &lp1->l_text[lp1->l_used];
cp2 = &lp2->l_text[0];
while (cp2 != &lp2->l_text[lp2->l_used])
*cp1++ = *cp2++;
wp = wheadp;
while (wp != NULL) {
if (wp->w_linep == lp2)
wp->w_linep = lp1;
if (wp->w_dotp == lp2) {
wp->w_dotp = lp1;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp2) {
wp->w_markp = lp1;
wp->w_marko += lp1->l_used;
}
wp = wp->w_wndp;
}
lp1->l_used += lp2->l_used;
lp1->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp1;
free((char *) lp2);
return (TRUE);
}
if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
return (FALSE);
cp1 = &lp1->l_text[0];
cp2 = &lp3->l_text[0];
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
cp1 = &lp2->l_text[0];
while (cp1 != &lp2->l_text[lp2->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp3;
lp3->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp3;
lp3->l_bp = lp1->l_bp;
wp = wheadp;
while (wp != NULL) {
if (wp->w_linep==lp1 || wp->w_linep==lp2)
wp->w_linep = lp3;
if (wp->w_dotp == lp1)
wp->w_dotp = lp3;
else if (wp->w_dotp == lp2) {
wp->w_dotp = lp3;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp1)
wp->w_markp = lp3;
else if (wp->w_markp == lp2) {
wp->w_markp = lp3;
wp->w_marko += lp1->l_used;
}
wp = wp->w_wndp;
}
free((char *) lp1);
free((char *) lp2);
return (TRUE);
}
/*
* Delete all of the text saved in the kill buffer. Called by commands when a
* new kill context is being created. The kill buffer array is released, just
* in case the buffer has grown to immense size. No errors.
*/
kdelete()
{
if (kbufp != NULL) {
free((char *) kbufp);
kbufp = NULL;
kused = 0;
ksize = 0;
}
}
/*
* Insert a character to the kill buffer, enlarging the buffer if there isn't
* any room. Always grow the buffer in chunks, on the assumption that if you
* put something in the kill buffer you are going to put more stuff there too
* later. Return TRUE if all is well, and FALSE on errors.
*/
kinsert(c)
{
register char *nbufp;
register int i;
if (kused == ksize) {
if ((nbufp=malloc(ksize+KBLOCK)) == NULL)
return (FALSE);
for (i=0; i<ksize; ++i)
nbufp[i] = kbufp[i];
if (kbufp != NULL)
free((char *) kbufp);
kbufp = nbufp;
ksize += KBLOCK;
}
kbufp[kused++] = c;
return (TRUE);
}
/*
* This function gets characters from the kill buffer. If the character index
* "n" is off the end, it returns "-1". This lets the caller just scan along
* until it gets a "-1" back.
*/
kremove(n)
{
if (n >= kused)
return (-1);
else
return (kbufp[n] & 0xFF);
}
/*
*
* REGION.C MODULE
*
*
*/
/*
* The routines in this file
* deal with the region, that magic space
* between "." and mark. Some functions are
* commands. Some functions are just for
* internal use.
*/
#include <stdio.h>
#include "ed.h"
/*
* Kill the region. Ask "getregion"
* to figure out the bounds of the region.
* Move "." to the start, and kill the characters.
* Bound to "C-W".
*/
killregion(f, n)
{
register int s;
REGION region;
if ((s=getregion(®ion)) != TRUE)
return (s);
if ((lastflag&CFKILL) == 0) /* This is a kill type */
kdelete(); /* command, so do magic */
thisflag |= CFKILL; /* kill buffer stuff. */
curwp->w_dotp = region.r_linep;
curwp->w_doto = region.r_offset;
return (ldelete(region.r_size, TRUE));
}
/*
* Copy all of the characters in the
* region to the kill buffer. Don't move dot
* at all. This is a bit like a kill region followed
* by a yank. Bound to "M-W".
*/
copyregion(f, n)
{
register LINE *linep;
register int loffs;
register int s;
REGION region;
if ((s=getregion(®ion)) != TRUE)
return (s);
if ((lastflag&CFKILL) == 0) /* Kill type command. */
kdelete();
thisflag |= CFKILL;
linep = region.r_linep; /* Current line. */
loffs = region.r_offset; /* Current offset. */
while (region.r_size--) {
if (loffs == llength(linep)) { /* End of line. */
if ((s=kinsert('\n')) != TRUE)
return (s);
linep = lforw(linep);
loffs = 0;
} else { /* Middle of line. */
if ((s=kinsert(lgetc(linep, loffs))) != TRUE)
return (s);
++loffs;
}
}
return (TRUE);
}
/*
* Lower case region. Zap all of the upper
* case characters in the region to lower case. Use
* the region code to set the limits. Scan the buffer,
* doing the changes. Call "lchange" to ensure that
* redisplay is done in all buffers. Bound to
* "C-X C-L".
*/
lowerregion(f, n)
{
register LINE *linep;
register int loffs;
register int c;
register int s;
REGION region;
if ((s=getregion(®ion)) != TRUE)
return (s);
lchange(WFHARD);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (c>='A' && c<='Z')
lputc(linep, loffs, c+'a'-'A');
++loffs;
}
}
return (TRUE);
}
/*
* Upper case region. Zap all of the lower
* case characters in the region to upper case. Use
* the region code to set the limits. Scan the buffer,
* doing the changes. Call "lchange" to ensure that
* redisplay is done in all buffers. Bound to
* "C-X C-L".
*/
upperregion(f, n)
{
register LINE *linep;
register int loffs;
register int c;
register int s;
REGION region;
if ((s=getregion(®ion)) != TRUE)
return (s);
lchange(WFHARD);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (c>='a' && c<='z')
lputc(linep, loffs, c-'a'+'A');
++loffs;
}
}
return (TRUE);
}
/*
* This routine figures out the
* bounds of the region in the current window, and
* fills in the fields of the "REGION" structure pointed
* to by "rp". Because the dot and mark are usually very
* close together, we scan outward from dot looking for
* mark. This should save time. Return a standard code.
* Callers of this routine should be prepared to get
* an "ABORT" status; we might make this have the
* conform thing later.
*/
getregion(rp)
register REGION *rp;
{
register LINE *flp;
register LINE *blp;
register int fsize;
register int bsize;
if (curwp->w_markp == NULL) {
mlwrite("No mark set in this window");
return (FALSE);
}
if (curwp->w_dotp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
if (curwp->w_doto < curwp->w_marko) {
rp->r_offset = curwp->w_doto;
rp->r_size = curwp->w_marko-curwp->w_doto;
} else {
rp->r_offset = curwp->w_marko;
rp->r_size = curwp->w_doto-curwp->w_marko;
}
return (TRUE);
}
blp = curwp->w_dotp;
bsize = curwp->w_doto;
flp = curwp->w_dotp;
fsize = llength(flp)-curwp->w_doto+1;
while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
if (flp != curbp->b_linep) {
flp = lforw(flp);
if (flp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
rp->r_offset = curwp->w_doto;
rp->r_size = fsize+curwp->w_marko;
return (TRUE);
}
fsize += llength(flp)+1;
}
if (lback(blp) != curbp->b_linep) {
blp = lback(blp);
bsize += llength(blp)+1;
if (blp == curwp->w_markp) {
rp->r_linep = blp;
rp->r_offset = curwp->w_marko;
rp->r_size = bsize - curwp->w_marko;
return (TRUE);
}
}
}
mlwrite("Bug: lost mark");
return (FALSE);
}
/*
*
* SPAWN.C MODULE
*
*/
/*
* The routines in this file are called to create a subjob running a command
* interpreter. This code is a big fat nothing on CP/M-86. You lose.
*/
#include <stdio.h>
#include "ed.h"
#if AMIGA
#define NEW 1006
#endif
#if VMS
#define EFN 0 /* Event flag. */
#include <ssdef.h> /* Random headers. */
#include <stsdef.h>
#include <descrip.h>
#include <iodef.h>
extern int oldmode[]; /* In "termio.c" */
extern int newmode[]; /* In "termio.c" */
extern short iochan; /* In "termio.c" */
#endif
#if MSDOS
#include <dos.h>
#endif
#if V7
#include <signal.h>
#endif
/*
* Create a subjob with a copy of the command intrepreter in it. When the
* command interpreter exits, mark the screen as garbage so that you do a full
* repaint. Bound to "C-C". The message at the start in VMS puts out a newline.
* Under some (unknown) condition, you don't get one free when DCL starts up.
*/
spawncli(f, n)
{
#if AMIGA
long newcli;
newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", NEW);
mlwrite("[Starting new CLI]");
sgarbf = TRUE;
Execute("", newcli, 0);
Close(newcli);
return(TRUE);
#endif
#if V7
register char *cp;
char *getenv();
#endif
#if VMS
movecursor(term.t_nrow, 0); /* In last line. */
mlputs("[Starting DCL]\r\n");
(*term.t_flush)(); /* Ignore "ttcol". */
sgarbf = TRUE;
return (sys(NULL)); /* NULL => DCL. */
#endif
#if CPM
mlwrite("Not in CP/M-86");
#endif
#if MSDOS
movecursor(term.t_nrow, 0); /* Seek to last line. */
(*term.t_flush)();
sys("\\command.com", ""); /* Run CLI. */
sgarbf = TRUE;
return(TRUE);
#endif
#if V7
movecursor(term.t_nrow, 0); /* Seek to last line. */
(*term.t_flush)();
ttclose(); /* stty to old settings */
if ((cp = getenv("SHELL")) != NULL && *cp != '\0')
system(cp);
else
system("exec /bin/sh");
sgarbf = TRUE;
sleep(2);
ttopen();
return(TRUE);
#endif
}
/*
* Run a one-liner in a subjob. When the command returns, wait for a single
* character to be typed, then mark the screen as garbage so a full repaint is
* done. Bound to "C-X !".
*/
spawn(f, n)
{
register int s;
char line[NLINE];
#if AMIGA
long newcli;
newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", NEW);
if ((s=mlreply("CLI command: ", line, NLINE)) != TRUE)
return (s);
Execute(line,0,newcli);
Close(newcli);
while ((*term.t_getchar)() != '\r') /* Pause. */
;
sgarbf = TRUE;
return(TRUE);
#endif
#if VMS
if ((s=mlreply("DCL command: ", line, NLINE)) != TRUE)
return (s);
(*term.t_putchar)('\n'); /* Already have '\r' */
(*term.t_flush)();
s = sys(line); /* Run the command. */
mlputs("\r\n\n[End]"); /* Pause. */
(*term.t_flush)();
while ((*term.t_getchar)() != '\r')
;
sgarbf = TRUE;
return (s);
#endif
#if CPM
mlwrite("Not in CP/M-86");
return (FALSE);
#endif
#if MSDOS
if ((s=mlreply("MS-DOS command: ", line, NLINE)) != TRUE)
return (s);
system(line);
while ((*term.t_getchar)() != '\r') /* Pause. */
;
sgarbf = TRUE;
return (TRUE);
#endif
#if V7
if ((s=mlreply("! ", line, NLINE)) != TRUE)
return (s);
(*term.t_putchar)('\n'); /* Already have '\r' */
(*term.t_flush)();
ttclose(); /* stty to old modes */
system(line);
sleep(2);
ttopen();
mlputs("[End]"); /* Pause. */
(*term.t_flush)();
while ((s = (*term.t_getchar)()) != '\r' && s != ' ')
;
sgarbf = TRUE;
return (TRUE);
#endif
}
#if VMS
/*
* Run a command. The "cmd" is a pointer to a command string, or NULL if you
* want to run a copy of DCL in the subjob (this is how the standard routine
* LIB$SPAWN works. You have to do wierd stuff with the terminal on the way in
* and the way out, because DCL does not want the channel to be in raw mode.
*/
sys(cmd)
register char *cmd;
{
struct dsc$descriptor cdsc;
struct dsc$descriptor *cdscp;
long status;
long substatus;
long iosb[2];
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
oldmode, sizeof(oldmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
return (FALSE);
cdscp = NULL; /* Assume DCL. */
if (cmd != NULL) { /* Build descriptor. */
cdsc.dsc$a_pointer = cmd;
cdsc.dsc$w_length = strlen(cmd);
cdsc.dsc$b_dtype = DSC$K_DTYPE_T;
cdsc.dsc$b_class = DSC$K_CLASS_S;
cdscp = &cdsc;
}
status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0);
if (status != SS$_NORMAL)
substatus = status;
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
newmode, sizeof(newmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
return (FALSE);
if ((substatus&STS$M_SUCCESS) == 0) /* Command failed. */
return (FALSE);
return (TRUE);
}
#endif
#if MSDOS
/*
* This routine, once again by Bob McNamara, is a C translation of the "system"
* routine in the MWC-86 run time library. It differs from the "system" routine
* in that it does not unconditionally append the string ".exe" to the end of
* the command name. We needed to do this because we want to be able to spawn
* off "command.com". We really do not understand what it does, but if you don't
* do it exactly "malloc" starts doing very very strange things.
*/
sys(cmd, tail)
char *cmd;
char *tail;
{
#if MWC_86
register unsigned n;
extern char *__end;
n = __end + 15;
n >>= 4;
n = ((n + dsreg() + 16) & 0xFFF0) + 16;
return(execall(cmd, tail, n));
#endif
#if LATTICE
return forklp(cmd, tail, NULL);
#endif
}
#endif
/*
*
* TCAP.C MODULE
*
*/
#include <stdio.h>
#include "ed.h"
#if TERMCAP
#define NROW 24
#define NCOL 80
#define BEL 0x07
#define ESC 0x1B
extern int ttopen();
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int tcapmove();
extern int tcapeeol();
extern int tcapeeop();
extern int tcapbeep();
extern int tcapopen();
extern int tput();
extern char *tgoto();
#define TCAPSLEN 315
char tcapbuf[TCAPSLEN];
char PC,
*CM,
*CL,
*CE,
*UP,
*CD;
TERM term = {
NROW-1,
NCOL,
tcapopen,
ttclose,
ttgetc,
ttputc,
ttflush,
tcapmove,
tcapeeol,
tcapeeop,
tcapbeep
};
tcapopen()
{
char *getenv();
char *t, *p, *tgetstr();
char tcbuf[1024];
char *tv_stype;
char err_str[72];
if ((tv_stype = getenv("TERM")) == NULL)
{
puts("Environment variable TERM not defined!");
exit(1);
}
if((tgetent(tcbuf, tv_stype)) != 1)
{
sprintf(err_str, "Unknown terminal type %s!", tv_stype);
puts(err_str);
exit(1);
}
p = tcapbuf;
t = tgetstr("pc", &p);
if(t)
PC = *t;
CD = tgetstr("cd", &p);
CM = tgetstr("cm", &p);
CE = tgetstr("ce", &p);
UP = tgetstr("up", &p);
if(CD == NULL || CM == NULL || CE == NULL || UP == NULL)
{
puts("Incomplete termcap entry\n");
exit(1);
}
if (p >= &tcapbuf[TCAPSLEN])
{
puts("Terminal description too big!\n");
exit(1);
}
ttopen();
}
tcapmove(row, col)
register int row, col;
{
putpad(tgoto(CM, col, row));
}
tcapeeol()
{
putpad(CE);
}
tcapeeop()
{
putpad(CD);
}
tcapbeep()
{
ttputc(BEL);
}
putpad(str)
char *str;
{
tputs(str, 1, ttputc);
}
putnpad(str, n)
char *str;
{
tputs(str, n, ttputc);
}
#endif TERMCAP
/*
*
* BUFFER.C MODULE
*
*/
/*
* Buffer management.
* Some of the functions are internal,
* and some are actually attached to user
* keys. Like everyone else, they set hints
* for the display system.
*/
#include <stdio.h>
#include "ed.h"
/*
* Attach a buffer to a window. The
* values of dot and mark come from the buffer
* if the use count is 0. Otherwise, they come
* from some other window.
*/
usebuffer(f, n)
{
register int s;
char bufn[NBUFN];
if ((s=mlreply("Use buffer: ", bufn, NBUFN)) != TRUE)
return (s);
return(useb(bufn));
}
/*
* Attach a buffer to a window. The
* values of dot and mark come from the buffer
* if the use count is 0. Otherwise, they come
* from some other window.
*
*/
useb(bufn)
char bufn[];
{
register BUFFER *bp;
register WINDOW *wp;
if ((bp=bfind(bufn, TRUE, 0)) == NULL)
return (FALSE);
if (--curbp->b_nwnd == 0) { /* Last use. */
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
}
curbp = bp; /* Switch. */
curwp->w_bufp = bp;
curwp->w_linep = bp->b_linep; /* For macros, ignored. */
curwp->w_flag |= WFMODE|WFFORCE|WFHARD; /* Quite nasty. */
if (bp->b_nwnd++ == 0) { /* First use. */
curwp->w_dotp = bp->b_dotp;
curwp->w_doto = bp->b_doto;
curwp->w_markp = bp->b_markp;
curwp->w_marko = bp->b_marko;
return (TRUE);
}
wp = wheadp; /* Look for old. */
while (wp != NULL) {
if (wp!=curwp && wp->w_bufp==bp) {
curwp->w_dotp = wp->w_dotp;
curwp->w_doto = wp->w_doto;
curwp->w_markp = wp->w_markp;
curwp->w_marko = wp->w_marko;
break;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* Dispose of a buffer, by name.
* Ask for the name. Look it up (don't get too
* upset if it isn't there at all!). Get quite upset
* if the buffer is being displayed. Clear the buffer (ask
* if the buffer has been changed). Then free the header
* line and the buffer header. Bound to "C-X K".
*/
killbuffer(f, n)
{
register BUFFER *bp;
register BUFFER *bp1;
register BUFFER *bp2;
register int s;
char bufn[NBUFN];
if ((s=mlreply("Kill buffer: ", bufn, NBUFN)) != TRUE)
return (s);
if ((bp=bfind(bufn, FALSE, 0)) == NULL) /* Easy if unknown. */
return (TRUE);
if (bp->b_nwnd != 0) { /* Error if on screen. */
mlwrite("Buffer is being displayed");
return (FALSE);
}
if ((s=bclear(bp)) != TRUE) /* Blow text away. */
return (s);
free((char *) bp->b_linep); /* Release header line. */
bp1 = NULL; /* Find the header. */
bp2 = bheadp;
while (bp2 != bp) {
bp1 = bp2;
bp2 = bp2->b_bufp;
}
bp2 = bp2->b_bufp; /* Next one in chain. */
if (bp1 == NULL) /* Unlink it. */
bheadp = bp2;
else
bp1->b_bufp = bp2;
free((char *) bp); /* Release buffer block */
return (TRUE);
}
/*
* List all of the active
* buffers. First update the special
* buffer that holds the list. Next make
* sure at least 1 window is displaying the
* buffer list, splitting the screen if this
* is what it takes. Lastly, repaint all of
* the windows that are displaying the
* list. Bound to "C-X C-B".
*/
listbuffers(f, n)
{
register WINDOW *wp;
register BUFFER *bp;
register int s;
if ((s=makelist()) != TRUE)
return (s);
if (blistp->b_nwnd == 0) { /* Not on screen yet. */
if ((wp=wpopup()) == NULL)
return (FALSE);
bp = wp->w_bufp;
if (--bp->b_nwnd == 0) {
bp->b_dotp = wp->w_dotp;
bp->b_doto = wp->w_doto;
bp->b_markp = wp->w_markp;
bp->b_marko = wp->w_marko;
}
wp->w_bufp = blistp;
++blistp->b_nwnd;
}
wp = wheadp;
while (wp != NULL) {
if (wp->w_bufp == blistp) {
wp->w_linep = lforw(blistp->b_linep);
wp->w_dotp = lforw(blistp->b_linep);
wp->w_doto = 0;
wp->w_markp = NULL;
wp->w_marko = 0;
wp->w_flag |= WFMODE|WFHARD;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* This routine rebuilds the
* text in the special secret buffer
* that holds the buffer list. It is called
* by the list buffers command. Return TRUE
* if everything works. Return FALSE if there
* is an error (if there is no memory).
*/
makelist()
{
register char *cp1;
register char *cp2;
register int c;
register BUFFER *bp;
register LINE *lp;
register int nbytes;
register int s;
register int type;
char b[6+1];
char line[128];
blistp->b_flag &= ~BFCHG; /* Don't complain! */
if ((s=bclear(blistp)) != TRUE) /* Blow old text away */
return (s);
strcpy(blistp->b_fname, "");
if (addline("C Size Buffer File") == FALSE
|| addline("- ---- ------ ----") == FALSE)
return (FALSE);
bp = bheadp; /* For all buffers */
while (bp != NULL) {
if ((bp->b_flag&BFTEMP) != 0) { /* Skip magic ones. */
bp = bp->b_bufp;
continue;
}
cp1 = &line[0]; /* Start at left edge */
if ((bp->b_flag&BFCHG) != 0) /* "*" if changed */
*cp1++ = '*';
else
*cp1++ = ' ';
*cp1++ = ' '; /* Gap. */
nbytes = 0; /* Count bytes in buf. */
lp = lforw(bp->b_linep);
while (lp != bp->b_linep) {
nbytes += llength(lp)+1;
lp = lforw(lp);
}
itoa(b, 6, nbytes); /* 6 digit buffer size. */
cp2 = &b[0];
while ((c = *cp2++) != 0)
*cp1++ = c;
*cp1++ = ' '; /* Gap. */
cp2 = &bp->b_bname[0]; /* Buffer name */
while ((c = *cp2++) != 0)
*cp1++ = c;
cp2 = &bp->b_fname[0]; /* File name */
if (*cp2 != 0) {
while (cp1 < &line[1+1+6+1+NBUFN+1])
*cp1++ = ' ';
while ((c = *cp2++) != 0) {
if (cp1 < &line[128-1])
*cp1++ = c;
}
}
*cp1 = 0; /* Add to the buffer. */
if (addline(line) == FALSE)
return (FALSE);
bp = bp->b_bufp;
}
return (TRUE); /* All done */
}
itoa(buf, width, num)
register char buf[];
register int width;
register int num;
{
buf[width] = 0; /* End of string. */
while (num >= 10) { /* Conditional digits. */
buf[--width] = (num%10) + '0';
num /= 10;
}
buf[--width] = num + '0'; /* Always 1 digit. */
while (width != 0) /* Pad with blanks. */
buf[--width] = ' ';
}
/*
* The argument "text" points to
* a string. Append this line to the
* buffer list buffer. Handcraft the EOL
* on the end. Return TRUE if it worked and
* FALSE if you ran out of room.
*/
addline(text)
char *text;
{
register LINE *lp;
register int i;
register int ntext;
ntext = strlen(text);
if ((lp=lalloc(ntext)) == NULL)
return (FALSE);
for (i=0; i<ntext; ++i)
lputc(lp, i, text[i]);
blistp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */
lp->l_bp = blistp->b_linep->l_bp;
blistp->b_linep->l_bp = lp;
lp->l_fp = blistp->b_linep;
if (blistp->b_dotp == blistp->b_linep) /* If "." is at the end */
blistp->b_dotp = lp; /* move it to new line */
return (TRUE);
}
/*
* Look through the list of
* buffers. Return TRUE if there
* are any changed buffers. Buffers
* that hold magic internal stuff are
* not considered; who cares if the
* list of buffer names is hacked.
* Return FALSE if no buffers
* have been changed.
*/
anycb()
{
register BUFFER *bp;
bp = bheadp;
while (bp != NULL) {
if ((bp->b_flag&BFTEMP)==0 && (bp->b_flag&BFCHG)!=0)
return (TRUE);
bp = bp->b_bufp;
}
return (FALSE);
}
/*
* Find a buffer, by name. Return a pointer
* to the BUFFER structure associated with it. If
* the named buffer is found, but is a TEMP buffer (like
* the buffer list) conplain. If the buffer is not found
* and the "cflag" is TRUE, create it. The "bflag" is
* the settings for the flags in in buffer.
*/
BUFFER *
bfind(bname, cflag, bflag)
register char *bname;
{
register BUFFER *bp;
register LINE *lp;
bp = bheadp;
while (bp != NULL) {
if (strcmp(bname, bp->b_bname) == 0) {
if ((bp->b_flag&BFTEMP) != 0) {
mlwrite("Cannot select builtin buffer");
return (NULL);
}
return (bp);
}
bp = bp->b_bufp;
}
if (cflag != FALSE) {
if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
return (NULL);
if ((lp=lalloc(0)) == NULL) {
free((char *) bp);
return (NULL);
}
bp->b_bufp = bheadp;
bheadp = bp;
bp->b_dotp = lp;
bp->b_doto = 0;
bp->b_markp = NULL;
bp->b_marko = 0;
bp->b_flag = bflag;
bp->b_nwnd = 0;
bp->b_linep = lp;
strcpy(bp->b_fname, "");
strcpy(bp->b_bname, bname);
lp->l_fp = lp;
lp->l_bp = lp;
}
return (bp);
}
/*
* This routine blows away all of the text
* in a buffer. If the buffer is marked as changed
* then we ask if it is ok to blow it away; this is
* to save the user the grief of losing text. The
* window chain is nearly always wrong if this gets
* called; the caller must arrange for the updates
* that are required. Return TRUE if everything
* looks good.
*/
bclear(bp)
register BUFFER *bp;
{
register LINE *lp;
register int s;
if ((bp->b_flag&BFTEMP) == 0 /* Not scratch buffer. */
&& (bp->b_flag&BFCHG) != 0 /* Something changed */
&& (s=mlyesno("Discard changes")) != TRUE)
return (s);
bp->b_flag &= ~BFCHG; /* Not changed */
while ((lp=lforw(bp->b_linep)) != bp->b_linep)
lfree(lp);
bp->b_dotp = bp->b_linep; /* Fix "." */
bp->b_doto = 0;
bp->b_markp = NULL; /* Invalidate "mark" */
bp->b_marko = 0;
return (TRUE);
}
/* This routine takes a line of text and a buffer pointer
* and places the line of text into the buffer and appends
* a CR-LF to the end of the text string
*
*/
addbufftext(text,bp)
char *text;
BUFFER *bp;
{
register int ntext;
register LINE *lp;
register int i;
ntext = strlen(text);
if ((lp=lalloc(ntext)) == NULL)
return (FALSE);
for (i=0; i<ntext; ++i)
if (text[i] == '\n')
newline();
else
lputc(lp, i, text[i]);
bp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */
lp->l_bp = bp->b_linep->l_bp;
bp->b_linep->l_bp = lp;
lp->l_fp = bp->b_linep;
if (bp->b_dotp == bp->b_linep) /* If "." is at the end */
bp->b_dotp = lp; /* move it to new line */
return (TRUE);
}
/*
*
* VT52 MODULE
*
*/
/*
* The routines in this file
* provide support for VT52 style terminals
* over a serial line. The serial I/O services are
* provided by routines in "termio.c". It compiles
* into nothing if not a VT52 style device. The
* bell on the VT52 is terrible, so the "beep"
* routine is conditionalized on defining BEL.
*/
#include <stdio.h>
#include "ed.h"
#if VT52
#define NROW 24 /* Screen size. */
#define NCOL 80 /* Edit if you want to. */
#define BIAS 0x20 /* Origin 0 coordinate bias. */
#define ESC 0x1B /* ESC character. */
#define BEL 0x07 /* ascii bell character */
extern int ttopen(); /* Forward references. */
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int vt52move();
extern int vt52eeol();
extern int vt52eeop();
extern int vt52beep();
extern int vt52open();
/*
* Dispatch table. All the
* hard fields just point into the
* terminal I/O code.
*/
TERM term = {
NROW-1,
NCOL,
vt52open,
ttclose,
ttgetc,
ttputc,
ttflush,
vt52move,
vt52eeol,
vt52eeop,
vt52beep
};
vt52move(row, col)
{
ttputc(ESC);
ttputc('Y');
ttputc(row+BIAS);
ttputc(col+BIAS);
}
vt52eeol()
{
ttputc(ESC);
ttputc('K');
}
vt52eeop()
{
ttputc(ESC);
ttputc('J');
}
vt52beep()
{
#ifdef BEL
ttputc(BEL);
ttflush();
#endif
}
#endif
vt52open()
{
#if V7
register char *cp;
char *getenv();
if ((cp = getenv("TERM")) == NULL) {
puts("Shell variable TERM not defined!");
exit(1);
}
if (strcmp(cp, "vt52") != 0 && strcmp(cp, "z19") != 0) {
puts("Terminal type not 'vt52'or 'z19' !");
exit(1);
}
#endif
ttopen();
}
/*
*
* WORD.C MODULE
*
*/
/*
* The routines in this file implement commands that work word at a time.
* There are all sorts of word mode commands. If I do any sentence and/or
* paragraph mode commands, they are likely to be put in this file.
*/
#include <stdio.h>
#include "ed.h"
/* Word wrap on n-spaces. Back-over whatever precedes the point on the current
* line and stop on the first word-break or the beginning of the line. If we
* reach the beginning of the line, jump back to the end of the word and start
* a new line. Otherwise, break the line at the word-break, eat it, and jump
* back to the end of the word.
* NOTE: This function may leaving trailing blanks.
* Returns TRUE on success, FALSE on errors.
*/
wrapword(n)
int n;
{
register int cnt, oldp;
oldp = curwp->w_dotp;
cnt = -1;
do {
cnt++;
if (! backchar(NULL, 1))
return(FALSE);
}
while (! inword());
if (! backword(NULL, 1))
return(FALSE);
if (oldp == (int) (curwp->w_dotp && curwp->w_doto)) {
if (! backdel(NULL, 1))
return(FALSE);
if (! newline(NULL, 1))
return(FALSE);
}
return(forwword(NULL, 1) && forwchar(NULL, cnt));
}
/*
* Move the cursor backward by "n" words. All of the details of motion are
* performed by the "backchar" and "forwchar" routines. Error if you try to
* move beyond the buffers.
*/
backword(f, n)
{
if (n < 0)
return (forwword(f, -n));
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (forwchar(FALSE, 1));
}
/*
* Move the cursor forward by the specified number of words. All of the motion
* is done by "forwchar". Error if you try and move beyond the buffer's end.
*/
forwword(f, n)
{
if (n < 0)
return (backword(f, -n));
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (TRUE);
}
/*
* Move the cursor forward by the specified number of words. As you move,
* convert any characters to upper case. Error if you try and move beyond the
* end of the buffer. Bound to "M-U".
*/
upperword(f, n)
{
register int c;
if (n < 0)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='a' && c<='z') {
c -= 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (TRUE);
}
/*
* Move the cursor forward by the specified number of words. As you move
* convert characters to lower case. Error if you try and move over the end of
* the buffer. Bound to "M-L".
*/
lowerword(f, n)
{
register int c;
if (n < 0)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='A' && c<='Z') {
c += 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (TRUE);
}
/*
* Move the cursor forward by the specified number of words. As you move
* convert the first character of the word to upper case, and subsequent
* characters to lower case. Error if you try and move past the end of the
* buffer. Bound to "M-C".
*/
capword(f, n)
{
register int c;
if (n < 0)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
if (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='a' && c<='z') {
c -= 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
while (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='A' && c<='Z') {
c += 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
}
return (TRUE);
}
/*
* Kill forward by "n" words. Remember the location of dot. Move forward by
* the right number of words. Put dot back where it was and issue the kill
* command for the right number of characters. Bound to "M-D".
*/
delfword(f, n)
{
register int size;
register LINE *dotp;
register int doto;
if (n < 0)
return (FALSE);
dotp = curwp->w_dotp;
doto = curwp->w_doto;
size = 0;
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
while (inword() != FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
}
curwp->w_dotp = dotp;
curwp->w_doto = doto;
return (ldelete(size, TRUE));
}
/*
* Kill backwards by "n" words. Move backwards by the desired number of words,
* counting the characters. When dot is finally moved to its resting place,
* fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
*/
delbword(f, n)
{
register int size;
if (n < 0)
return (FALSE);
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
size = 0;
while (n--) {
while (inword() == FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
while (inword() != FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
return (ldelete(size, TRUE));
}
/*
* Return TRUE if the character at dot is a character that is considered to be
* part of a word. The word character list is hard coded. Should be setable.
*/
inword()
{
register int c;
if (curwp->w_doto == llength(curwp->w_dotp))
return (FALSE);
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='a' && c<='z')
return (TRUE);
if (c>='A' && c<='Z')
return (TRUE);
if (c>='0' && c<='9')
return (TRUE);
if (c=='$' || c=='_') /* For identifiers */
return (TRUE);
return (FALSE);
}
/*
*
* DISPLAY.C MODULE
*
*
*/
/*
* The functions in this file handle redisplay. There are two halves, the
* ones that update the virtual display screen, and the ones that make the
* physical display screen the same as the virtual display screen. These
* functions use hints that are left in the windows by the commands.
*
* REVISION HISTORY:
*
* ? Steve Wilhite, 1-Dec-85
* - massive cleanup on code.
*/
#include <stdio.h>
#include "ed.h"
#define WFDEBUG 0 /* Window flag debug. */
typedef struct VIDEO {
short v_flag; /* Flags */
char v_text[1]; /* Screen data. */
} VIDEO;
#define VFCHG 0x0001 /* Changed. */
int sgarbf = TRUE; /* TRUE if screen is garbage */
int mpresf = FALSE; /* TRUE if message in last line */
int vtrow = 0; /* Row location of SW cursor */
int vtcol = 0; /* Column location of SW cursor */
int ttrow = HUGE; /* Row location of HW cursor */
int ttcol = HUGE; /* Column location of HW cursor */
VIDEO **vscreen; /* Virtual screen. */
VIDEO **pscreen; /* Physical screen. */
/*
* Initialize the data structures used by the display code. The edge vectors
* used to access the screens are set up. The operating system's terminal I/O
* channel is set up. All the other things get initialized at compile time.
* The original window has "WFCHG" set, so that it will get completely
* redrawn on the first call to "update".
*/
vtinit()
{
register int i;
register VIDEO *vp;
(*term.t_open)();
vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (vscreen == NULL)
exit(1);
pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (pscreen == NULL)
exit(1);
for (i = 0; i < term.t_nrow; ++i)
{
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
exit(1);
vscreen[i] = vp;
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
exit(1);
pscreen[i] = vp;
}
}
/*
* Clean up the virtual terminal system, in anticipation for a return to the
* operating system. Move down to the last line and clear it out (the next
* system prompt will be written in the line). Shut down the channel to the
* terminal.
*/
vttidy()
{
movecursor(term.t_nrow, 0);
(*term.t_eeol)();
(*term.t_close)();
}
/*
* Set the virtual cursor to the specified row and column on the virtual
* screen. There is no checking for nonsense values; this might be a good
* idea during the early stages.
*/
vtmove(row, col)
{
vtrow = row;
vtcol = col;
}
/*
* Write a character to the virtual screen. The virtual row and column are
* updated. If the line is too long put a "$" in the last column. This routine
* only puts printing characters into the virtual terminal buffers. Only
* column overflow is checked.
*/
vtputc(c)
int c;
{
register VIDEO *vp;
vp = vscreen[vtrow];
if (vtcol >= term.t_ncol)
vp->v_text[term.t_ncol - 1] = '$';
else if (c == '\t')
{
do
{
vtputc(' ');
}
while ((vtcol&0x07) != 0);
}
else if (c < 0x20 || c == 0x7F)
{
vtputc('^');
vtputc(c ^ 0x40);
}
else
vp->v_text[vtcol++] = c;
}
/*
* Write a character to the virtual screen. The virtual row and column are
* updated. If the line is too long put a "$" in the last column. This routine
* only puts printing characters into the virtual terminal buffers. Only
* column overflow is checked. If 'c' is a ctl char it is put directly into
* the buffer as is. Tabs are sent as tabs also.
*/
vtputcl(c)
int c;
{
register VIDEO *vp;
vp = vscreen[vtrow];
if (vtcol >= term.t_ncol)
vp->v_text[term.t_ncol - 1] = '$';
else if (c == '\t')
{
do
{
vtputc(' ');
}
while ((vtcol&0x07) != 0);
}
else
vp->v_text[vtcol++] = c;
}
/*
* Erase from the end of the software cursor to the end of the line on which
* the software cursor is located.
*/
vteeol()
{
register VIDEO *vp;
vp = vscreen[vtrow];
while (vtcol < term.t_ncol)
vp->v_text[vtcol++] = ' ';
}
/*
* Make sure that the display is right. This is a three part process. First,
* scan through all of the windows looking for dirty ones. Check the framing,
* and refresh the screen. Second, make sure that "currow" and "curcol" are
* correct for the current window. Third, make the virtual and physical
* screens the same.
*/
update()
{
register LINE *lp;
register WINDOW *wp;
register VIDEO *vp1;
register VIDEO *vp2;
register int i;
register int j;
register int c;
wp = wheadp;
while (wp != NULL)
{
/* Look at any window with update flags set on. */
if (wp->w_flag != 0)
{
/* If not force reframe, check the framing. */
if ((wp->w_flag & WFFORCE) == 0)
{
lp = wp->w_linep;
for (i = 0; i < wp->w_ntrows; ++i)
{
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_linep)
break;
lp = lforw(lp);
}
}
/* Not acceptable, better compute a new value for the line at the
* top of the window. Then set the "WFHARD" flag to force full
* redraw.
*/
i = wp->w_force;
if (i > 0)
{
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows-1;
}
else if (i < 0)
{
i += wp->w_ntrows;
if (i < 0)
i = 0;
}
else
i = wp->w_ntrows/2;
lp = wp->w_dotp;
while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
{
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_flag |= WFHARD; /* Force full. */
out:
/* Try to use reduced update. Mode line update has its own special
* flag. The fast update is used if the only thing to do is within
* the line editing.
*/
lp = wp->w_linep;
i = wp->w_toprow;
if ((wp->w_flag & ~WFMODE) == WFEDIT)
{
while (lp != wp->w_dotp)
{
++i;
lp = lforw(lp);
}
vscreen[i]->v_flag |= VFCHG;
vtmove(i, 0);
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
}
else
if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
{
while (i < wp->w_toprow+wp->w_ntrows)
{
vscreen[i]->v_flag |= VFCHG;
vtmove(i, 0);
if (lp != wp->w_bufp->b_linep)
{
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j));
lp = lforw(lp);
}
vteeol();
++i;
}
}
#if ~WFDEBUG
if ((wp->w_flag&WFMODE) != 0)
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
}
#if WFDEBUG
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
wp = wp->w_wndp;
}
/* Always recompute the row and column number of the hardware cursor. This
* is the only update for simple moves.
*/
lp = curwp->w_linep;
currow = curwp->w_toprow;
while (lp != curwp->w_dotp)
{
++currow;
lp = lforw(lp);
}
curcol = 0;
i = 0;
while (i < curwp->w_doto)
{
c = lgetc(lp, i++);
if (c == '\t')
curcol |= 0x07;
else if (c < 0x20 || c == 0x7F)
++curcol;
++curcol;
}
if (curcol >= term.t_ncol) /* Long line. */
curcol = term.t_ncol-1;
/* Special hacking if the screen is garbage. Clear the hardware screen,
* and update your copy to agree with it. Set all the virtual screen
* change bits, to force a full update.
*/
if (sgarbf != FALSE)
{
for (i = 0; i < term.t_nrow; ++i)
{
vscreen[i]->v_flag |= VFCHG;
vp1 = pscreen[i];
for (j = 0; j < term.t_ncol; ++j)
vp1->v_text[j] = ' ';
}
movecursor(0, 0); /* Erase the screen. */
(*term.t_eeop)();
sgarbf = FALSE; /* Erase-page clears */
mpresf = FALSE; /* the message area. */
}
/* Make sure that the physical and virtual displays agree. Unlike before,
* the "updateline" code is only called with a line that has been updated
* for sure.
*/
for (i = 0; i < term.t_nrow; ++i)
{
vp1 = vscreen[i];
if ((vp1->v_flag&VFCHG) != 0)
{
vp1->v_flag &= ~VFCHG;
vp2 = pscreen[i];
updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
}
}
/* Finally, update the hardware cursor and flush out buffers. */
movecursor(currow, curcol);
(*term.t_flush)();
}
/*
* Update a single line. This does not know how to use insert or delete
* character sequences; we are using VT52 functionality. Update the physical
* row and column variables. It does try an exploit erase to end of line. The
* RAINBOW version of this routine uses fast video.
*/
updateline(row, vline, pline)
char vline[];
char pline[];
{
#if RAINBOW
register char *cp1;
register char *cp2;
register int nch;
cp1 = &vline[0]; /* Use fast video. */
cp2 = &pline[0];
putline(row+1, 1, cp1);
nch = term.t_ncol;
do
{
*cp2 = *cp1;
++cp2;
++cp1;
}
while (--nch);
#else
register char *cp1;
register char *cp2;
register char *cp3;
register char *cp4;
register char *cp5;
register int nbflag;
cp1 = &vline[0]; /* Compute left match. */
cp2 = &pline[0];
while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0])
{
++cp1;
++cp2;
}
/* This can still happen, even though we only call this routine on changed
* lines. A hard update is always done when a line splits, a massive
* change is done, or a buffer is displayed twice. This optimizes out most
* of the excess updating. A lot of computes are used, but these tend to
* be hard operations that do a lot of update, so I don't really care.
*/
if (cp1 == &vline[term.t_ncol]) /* All equal. */
return;
nbflag = FALSE;
cp3 = &vline[term.t_ncol]; /* Compute right match. */
cp4 = &pline[term.t_ncol];
while (cp3[-1] == cp4[-1])
{
--cp3;
--cp4;
if (cp3[0] != ' ') /* Note if any nonblank */
nbflag = TRUE; /* in right match. */
}
cp5 = cp3;
if (nbflag == FALSE) /* Erase to EOL ? */
{
while (cp5!=cp1 && cp5[-1]==' ')
--cp5;
if (cp3-cp5 <= 3) /* Use only if erase is */
cp5 = cp3; /* fewer characters. */
}
movecursor(row, cp1-&vline[0]); /* Go to start of line. */
while (cp1 != cp5) /* Ordinary. */
{
(*term.t_putchar)(*cp1);
++ttcol;
*cp2++ = *cp1++;
}
if (cp5 != cp3) /* Erase. */
{
(*term.t_eeol)();
while (cp1 != cp3)
*cp2++ = *cp1++;
}
#endif
}
/*
* Redisplay the mode line for the window pointed to by the "wp". This is the
* only routine that has any idea of how the modeline is formatted. You can
* change the modeline format by hacking at this routine. Called by "update"
* any time there is a dirty window.
*/
modeline(wp)
WINDOW *wp;
{
register char *cp;
register int c;
register int n;
register BUFFER *bp;
n = wp->w_toprow+wp->w_ntrows; /* Location. */
vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
vtmove(n, 0); /* Seek to right line. */
#ifdef ANSI
vtputcl('');
vtputcl('[');
vtputcl('1');
vtputcl(';');
vtputcl('4');
vtputcl('1');
vtputcl('m');
#endif
vtputc('-');
bp = wp->w_bufp;
if ((bp->b_flag&BFCHG) != 0) /* "*" if changed. */
vtputc('*');
else
vtputc('-');
n = 2;
cp = " MicroEMACS -- "; /* Buffer name. */
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
cp = &bp->b_bname[0];
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
vtputc(' ');
++n;
if (bp->b_fname[0] != 0) /* File name. */
{
cp = "-- File: ";
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
cp = &bp->b_fname[0];
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
vtputc(' ');
++n;
}
#if WFDEBUG
vtputc('-');
vtputc((wp->w_flag&WFMODE)!=0 ? 'M' : '-');
vtputc((wp->w_flag&WFHARD)!=0 ? 'H' : '-');
vtputc((wp->w_flag&WFEDIT)!=0 ? 'E' : '-');
vtputc((wp->w_flag&WFMOVE)!=0 ? 'V' : '-');
vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
n += 6;
#endif
#ifndef ANSI
while (n < term.t_ncol) /* Pad to full width. */
{
vtputc('-');
++n;
}
#endif
#ifdef ANSI
while (n < term.t_ncol-11) /* Pad to full width. */
{
vtputc('-');
++n;
}
vtputcl('');
vtputcl('[');
vtputcl('0');
vtputcl('m');
#endif
}
/*
* Send a command to the terminal to move the hardware cursor to row "row"
* and column "col". The row and column arguments are origin 0. Optimize out
* random calls. Update "ttrow" and "ttcol".
*/
movecursor(row, col)
{
if (row!=ttrow || col!=ttcol)
{
ttrow = row;
ttcol = col;
(*term.t_move)(row, col);
}
}
/*
* Erase the message line. This is a special routine because the message line
* is not considered to be part of the virtual screen. It always works
* immediately; the terminal buffer is flushed via a call to the flusher.
*/
mlerase()
{
movecursor(term.t_nrow, 0);
(*term.t_eeol)();
(*term.t_flush)();
mpresf = FALSE;
}
/*
* Ask a yes or no question in the message line. Return either TRUE, FALSE, or
* ABORT. The ABORT status is returned if the user bumps out of the question
* with a ^G. Used any time a confirmation is required.
*/
mlyesno(prompt)
char *prompt;
{
register int s;
char buf[64];
for (;;)
{
strcpy(buf, prompt);
strcat(buf, " [y/n]? ");
s = mlreply(buf, buf, sizeof(buf));
if (s == ABORT)
return (ABORT);
if (s != FALSE)
{
if (buf[0]=='y' || buf[0]=='Y')
return (TRUE);
if (buf[0]=='n' || buf[0]=='N')
return (FALSE);
}
}
}
/*
* Write a prompt into the message line, then read back a response. Keep
* track of the physical position of the cursor. If we are in a keyboard
* macro throw the prompt away, and return the remembered response. This
* lets macros run at full speed. The reply is always terminated by a carriage
* return. Handle erase, kill, and abort keys.
*/
mlreply(prompt, buf, nbuf)
char *prompt;
char *buf;
{
register int cpos;
register int i;
register int c;
cpos = 0;
if (kbdmop != NULL)
{
while ((c = *kbdmop++) != '\0')
buf[cpos++] = c;
buf[cpos] = 0;
if (buf[0] == 0)
return (FALSE);
return (TRUE);
}
mlwrite(prompt);
for (;;)
{
c = (*term.t_getchar)();
switch (c)
{
case 0x0D: /* Return, end of line */
buf[cpos++] = 0;
if (kbdmip != NULL)
{
if (kbdmip+cpos > &kbdm[NKBDM-3])
{
ctrlg(FALSE, 0);
(*term.t_flush)();
return (ABORT);
}
for (i=0; i<cpos; ++i)
*kbdmip++ = buf[i];
}
(*term.t_putchar)('\r');
ttcol = 0;
(*term.t_flush)();
if (buf[0] == 0)
return (FALSE);
return (TRUE);
case 0x07: /* Bell, abort */
(*term.t_putchar)('^');
(*term.t_putchar)('G');
ttcol += 2;
ctrlg(FALSE, 0);
(*term.t_flush)();
return (ABORT);
case 0x7F: /* Rubout, erase */
case 0x08: /* Backspace, erase */
if (cpos != 0)
{
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
if (buf[--cpos] < 0x20)
{
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
}
(*term.t_flush)();
}
break;
case 0x15: /* C-U, kill */
while (cpos != 0)
{
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
if (buf[--cpos] < 0x20)
{
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
}
}
(*term.t_flush)();
break;
default:
if (cpos < nbuf-1)
{
buf[cpos++] = c;
if (c < ' ')
{
(*term.t_putchar)('^');
++ttcol;
c ^= 0x40;
}
(*term.t_putchar)(c);
++ttcol;
(*term.t_flush)();
}
}
}
}
/*
* Write a message into the message line. Keep track of the physical cursor
* position. A small class of printf like format items is handled. Assumes the
* stack grows down; this assumption is made by the "++" in the argument scan
* loop. Set the "message line" flag TRUE.
*/
mlwrite(fmt, arg)
char *fmt;
{
register int c;
register char *ap;
movecursor(term.t_nrow, 0);
ap = (char *) &arg;
while ((c = *fmt++) != 0) {
if (c != '%') {
(*term.t_putchar)(c);
++ttcol;
}
else
{
c = *fmt++;
switch (c) {
case 'd':
mlputi(*(int *)ap, 10);
ap += sizeof(int);
break;
case 'o':
mlputi(*(int *)ap, 8);
ap += sizeof(int);
break;
case 'x':
mlputi(*(int *)ap, 16);
ap += sizeof(int);
break;
case 'D':
mlputli(*(long *)ap, 10);
ap += sizeof(long);
break;
case 's':
mlputs(*(char **)ap);
ap += sizeof(char *);
break;
default:
(*term.t_putchar)(c);
++ttcol;
}
}
}
(*term.t_eeol)();
(*term.t_flush)();
mpresf = TRUE;
}
/*
* Write out a string. Update the physical cursor position. This assumes that
* the characters in the string all have width "1"; if this is not the case
* things will get screwed up a little.
*/
mlputs(s)
char *s;
{
register int c;
while ((c = *s++) != 0)
{
(*term.t_putchar)(c);
++ttcol;
}
}
/*
* Write out an integer, in the specified radix. Update the physical cursor
* position. This will not handle any negative numbers; maybe it should.
*/
mlputi(i, r)
{
register int q;
static char hexdigits[] = "0123456789ABCDEF";
if (i < 0)
{
i = -i;
(*term.t_putchar)('-');
}
q = i/r;
if (q != 0)
mlputi(q, r);
(*term.t_putchar)(hexdigits[i%r]);
++ttcol;
}
/*
* do the same except as a long integer.
*/
mlputli(l, r)
long l;
{
register long q;
if (l < 0)
{
l = -l;
(*term.t_putchar)('-');
}
q = l/r;
if (q != 0)
mlputli(q, r);
(*term.t_putchar)((int)(l%r)+'0');
++ttcol;
}
#if RAINBOW
putline(row, col, buf)
int row, col;
char buf[];
{
int n;
n = strlen(buf);
if (col + n - 1 > term.t_ncol)
n = term.t_ncol - col + 1;
Put_Data(row, col, n, buf);
}
#endif
/*
*
* FILE.C MODULE
*
*/
/*
* The routines in this file
* handle the reading and writing of
* disk files. All of details about the
* reading and writing of the disk are
* in "fileio.c".
*/
#include <stdio.h>
#include "ed.h"
/*
* Read a file into the current
* buffer. This is really easy; all you do it
* find the name of the file, and call the standard
* "read a file into the current buffer" code.
* Bound to "C-X C-R".
*/
fileread(f, n)
{
register int s;
char fname[NFILEN];
if ((s=mlreply("Read file: ", fname, NFILEN)) != TRUE)
return (s);
return (readin(fname));
}
/*
* Select a file for editing.
* Look around to see if you can find the
* fine in another buffer; if you can find it
* just switch to the buffer. If you cannot find
* the file, create a new buffer, read in the
* text, and switch to the new buffer.
* Bound to C-X C-V.
*/
filevisit(f, n)
{
register BUFFER *bp;
register WINDOW *wp;
register LINE *lp;
register int i;
register int s;
char bname[NBUFN];
char fname[NFILEN];
if ((s=mlreply("Visit file: ", fname, NFILEN)) != TRUE)
return (s);
for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
if ((bp->b_flag&BFTEMP)==0 && strcmp(bp->b_fname, fname)==0) {
if (--curbp->b_nwnd == 0) {
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
}
curbp = bp;
curwp->w_bufp = bp;
if (bp->b_nwnd++ == 0) {
curwp->w_dotp = bp->b_dotp;
curwp->w_doto = bp->b_doto;
curwp->w_markp = bp->b_markp;
curwp->w_marko = bp->b_marko;
} else {
wp = wheadp;
while (wp != NULL) {
if (wp!=curwp && wp->w_bufp==bp) {
curwp->w_dotp = wp->w_dotp;
curwp->w_doto = wp->w_doto;
curwp->w_markp = wp->w_markp;
curwp->w_marko = wp->w_marko;
break;
}
wp = wp->w_wndp;
}
}
lp = curwp->w_dotp;
i = curwp->w_ntrows/2;
while (i-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_flag |= WFMODE|WFHARD;
mlwrite("[Old buffer]");
return (TRUE);
}
}
makename(bname, fname); /* New buffer name. */
while ((bp=bfind(bname, FALSE, 0)) != NULL) {
s = mlreply("Buffer name: ", bname, NBUFN);
if (s == ABORT) /* ^G to just quit */
return (s);
if (s == FALSE) { /* CR to clobber it */
makename(bname, fname);
break;
}
}
if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
mlwrite("Cannot create buffer");
return (FALSE);
}
/* added by Curt Jutzi */
if (nextwind(NULL,NULL)==FALSE) /* if two windows open go to other */
splitwind(NULL,NULL); /* and display new buffer their else */
/* split window and display in other */
if (--curbp->b_nwnd == 0) { /* Undisplay. */
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
}
curbp = bp; /* Switch to it. */
curwp->w_bufp = bp;
curbp->b_nwnd++;
return (readin(fname)); /* Read it in. */
}
/*
* Read file "fname" into the current
* buffer, blowing away any text found there. Called
* by both the read and visit commands. Return the final
* status of the read. Also called by the mainline,
* to read in a file specified on the command line as
* an argument.
*/
readin(fname)
char fname[];
{
register LINE *lp1;
register LINE *lp2;
register int i;
register WINDOW *wp;
register BUFFER *bp;
register int s;
register int nbytes;
register int nline;
char line[NLINE];
bp = curbp; /* Cheap. */
if ((s=bclear(bp)) != TRUE) /* Might be old. */
return (s);
bp->b_flag &= ~(BFTEMP|BFCHG);
strcpy(bp->b_fname, fname);
if ((s=ffropen(fname)) == FIOERR) /* Hard file open. */
goto out;
if (s == FIOFNF) { /* File not found. */
mlwrite("[New file]");
goto out;
}
mlwrite("[Reading file]");
nline = 0;
while ((s=ffgetline(line, NLINE)) == FIOSUC) {
nbytes = strlen(line);
if ((lp1=lalloc(nbytes)) == NULL) {
s = FIOERR; /* Keep message on the */
break; /* display. */
}
lp2 = lback(curbp->b_linep);
lp2->l_fp = lp1;
lp1->l_fp = curbp->b_linep;
lp1->l_bp = lp2;
curbp->b_linep->l_bp = lp1;
for (i=0; i<nbytes; ++i)
lputc(lp1, i, line[i]);
++nline;
}
ffclose(); /* Ignore errors. */
if (s == FIOEOF) { /* Don't zap message! */
if (nline == 1)
mlwrite("[Read 1 line]");
else
mlwrite("[Read %d lines]", nline);
}
out:
for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
if (wp->w_bufp == curbp) {
wp->w_linep = lforw(curbp->b_linep);
wp->w_dotp = lforw(curbp->b_linep);
wp->w_doto = 0;
wp->w_markp = NULL;
wp->w_marko = 0;
wp->w_flag |= WFMODE|WFHARD;
}
}
if (s == FIOERR) /* False if error. */
return (FALSE);
return (TRUE);
}
/*
* Take a file name, and from it
* fabricate a buffer name. This routine knows
* about the syntax of file names on the target system.
* I suppose that this information could be put in
* a better place than a line of code.
*/
makename(bname, fname)
char bname[];
char fname[];
{
register char *cp1;
register char *cp2;
cp1 = &fname[0];
while (*cp1 != 0)
++cp1;
#if AMIGA
while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='/')
--cp1;
#endif
#if VMS
while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!=']')
--cp1;
#endif
#if CPM
while (cp1!=&fname[0] && cp1[-1]!=':')
--cp1;
#endif
#if MSDOS
while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\')
--cp1;
#endif
#if V7
while (cp1!=&fname[0] && cp1[-1]!='/')
--cp1;
#endif
cp2 = &bname[0];
while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
*cp2++ = *cp1++;
*cp2 = 0;
}
/*
* Ask for a file name, and write the
* contents of the current buffer to that file.
* Update the remembered file name and clear the
* buffer changed flag. This handling of file names
* is different from the earlier versions, and
* is more compatable with Gosling EMACS than
* with ITS EMACS. Bound to "C-X C-W".
*/
filewrite(f, n)
{
register WINDOW *wp;
register int s;
char fname[NFILEN];
if ((s=mlreply("Write file: ", fname, NFILEN)) != TRUE)
return (s);
if ((s=writeout(fname)) == TRUE) {
strcpy(curbp->b_fname, fname);
curbp->b_flag &= ~BFCHG;
wp = wheadp; /* Update mode lines. */
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
}
return (s);
}
/*
* Save the contents of the current
* buffer in its associatd file. No nothing
* if nothing has changed (this may be a bug, not a
* feature). Error if there is no remembered file
* name for the buffer. Bound to "C-X C-S". May
* get called by "C-Z".
*/
filesave(f, n)
{
register WINDOW *wp;
register int s;
if ((curbp->b_flag&BFCHG) == 0) /* Return, no changes. */
return (TRUE);
if (curbp->b_fname[0] == 0) { /* Must have a name. */
mlwrite("No file name");
return (FALSE);
}
if ((s=writeout(curbp->b_fname)) == TRUE) {
curbp->b_flag &= ~BFCHG;
wp = wheadp; /* Update mode lines. */
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
}
return (s);
}
/*
* This function performs the details of file
* writing. Uses the file management routines in the
* "fileio.c" package. The number of lines written is
* displayed. Sadly, it looks inside a LINE; provide
* a macro for this. Most of the grief is error
* checking of some sort.
*/
writeout(fn)
char *fn;
{
register int s;
register LINE *lp;
register int nline;
if ((s=ffwopen(fn)) != FIOSUC) /* Open writes message. */
return (FALSE);
lp = lforw(curbp->b_linep); /* First line. */
nline = 0; /* Number of lines. */
while (lp != curbp->b_linep) {
if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
break;
++nline;
lp = lforw(lp);
}
if (s == FIOSUC) { /* No write error. */
s = ffclose();
if (s == FIOSUC) { /* No close error. */
if (nline == 1)
mlwrite("[Wrote 1 line]");
else
mlwrite("[Wrote %d lines]", nline);
}
} else /* Ignore close error */
ffclose(); /* if a write error. */
if (s != FIOSUC) /* Some sort of error. */
return (FALSE);
return (TRUE);
}
/*
* The command allows the user
* to modify the file name associated with
* the current buffer. It is like the "f" command
* in UNIX "ed". The operation is simple; just zap
* the name in the BUFFER structure, and mark the windows
* as needing an update. You can type a blank line at the
* prompt if you wish.
*/
filename(f, n)
{
register WINDOW *wp;
register int s;
char fname[NFILEN];
if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
return (s);
if (s == FALSE)
strcpy(curbp->b_fname, "");
else
strcpy(curbp->b_fname, fname);
wp = wheadp; /* Update mode lines. */
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
return (TRUE);
}
/*
*
* RANDOM.C MODULE
*
*/
/*
* This file contains the command processing functions for a number of random
* commands. There is no functional grouping here, for sure.
*/
#include <stdio.h>
#include "ed.h"
int tabsize; /* Tab size (0: use real tabs) */
/*
* Set fill column to n.
*/
setfillcol(f, n)
{
fillcol = n;
return(TRUE);
}
/*
* Display the current position of the cursor, in origin 1 X-Y coordinates,
* the character that is under the cursor (in octal), and the fraction of the
* text that is before the cursor. The displayed column is not the current
* column, but the column that would be used on an infinite width display.
* Normally this is bound to "C-X =".
*/
showcpos(f, n)
{
register LINE *clp;
register long nch;
register int cbo;
register long nbc;
register int cac;
register int ratio;
register int col;
register int i;
register int c;
clp = lforw(curbp->b_linep); /* Grovel the data. */
cbo = 0;
nch = 0;
for (;;) {
if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
nbc = nch;
if (cbo == llength(clp))
cac = '\n';
else
cac = lgetc(clp, cbo);
}
if (cbo == llength(clp)) {
if (clp == curbp->b_linep)
break;
clp = lforw(clp);
cbo = 0;
} else
++cbo;
++nch;
}
col = getccol(FALSE); /* Get real column. */
ratio = 0; /* Ratio before dot. */
if (nch != 0)
ratio = (100L*nbc) / nch;
mlwrite("X=%d Y=%d CH=0x%x .=%D (%d%% of %D)",
col+1, currow+1, cac, nbc, ratio, nch);
return (TRUE);
}
/*
* Return current column. Stop at first non-blank given TRUE argument.
*/
getccol(bflg)
int bflg;
{
register int c, i, col;
col = 0;
for (i=0; i<curwp->w_doto; ++i) {
c = lgetc(curwp->w_dotp, i);
if (c!=' ' && c!='\t' && bflg)
break;
if (c == '\t')
col |= 0x07;
else if (c<0x20 || c==0x7F)
++col;
++col;
}
return(col);
}
/*
* Twiddle the two characters on either side of dot. If dot is at the end of
* the line twiddle the two characters before it. Return with an error if dot
* is at the beginning of line; it seems to be a bit pointless to make this
* work. This fixes up a very common typo with a single stroke. Normally bound
* to "C-T". This always works within a line, so "WFEDIT" is good enough.
*/
twiddle(f, n)
{
register LINE *dotp;
register int doto;
register int cl;
register int cr;
dotp = curwp->w_dotp;
doto = curwp->w_doto;
if (doto==llength(dotp) && --doto<0)
return (FALSE);
cr = lgetc(dotp, doto);
if (--doto < 0)
return (FALSE);
cl = lgetc(dotp, doto);
lputc(dotp, doto+0, cr);
lputc(dotp, doto+1, cl);
lchange(WFEDIT);
return (TRUE);
}
/*
* Quote the next character, and insert it into the buffer. All the characters
* are taken literally, with the exception of the newline, which always has
* its line splitting meaning. The character is always read, even if it is
* inserted 0 times, for regularity. Bound to "M-Q" (for me) and "C-Q" (for
* Rich, and only on terminals that don't need XON-XOFF).
*/
quote(f, n)
{
register int s;
register int c;
c = (*term.t_getchar)();
if (n < 0)
return (FALSE);
if (n == 0)
return (TRUE);
if (c == '\n') {
do {
s = lnewline();
} while (s==TRUE && --n);
return (s);
}
return (linsert(n, c));
}
/*
* Set tab size if given non-default argument (n <> 1). Otherwise, insert a
* tab into file. If given argument, n, of zero, change to true tabs.
* If n > 1, simulate tab stop every n-characters using spaces. This has to be
* done in this slightly funny way because the tab (in ASCII) has been turned
* into "C-I" (in 10 bit code) already. Bound to "C-I".
*/
tab(f, n)
{
if (n < 0)
return (FALSE);
if (n == 0 || n > 1) {
tabsize = n;
return(TRUE);
}
if (! tabsize)
return(linsert(1, '\t'));
return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
}
/*
* Open up some blank space. The basic plan is to insert a bunch of newlines,
* and then back up over them. Everything is done by the subcommand
* procerssors. They even handle the looping. Normally this is bound to "C-O".
*/
openline(f, n)
{
register int i;
register int s;
if (n < 0)
return (FALSE);
if (n == 0)
return (TRUE);
i = n; /* Insert newlines. */
do {
s = lnewline();
} while (s==TRUE && --i);
if (s == TRUE) /* Then back up overtop */
s = backchar(f, n); /* of them all. */
return (s);
}
/*
* Insert a newline. Bound to "C-M". If you are at the end of the line and the
* next line is a blank line, just move into the blank line. This makes "C-O"
* and "C-X C-O" work nicely, and reduces the ammount of screen update that
* has to be done. This would not be as critical if screen update were a lot
* more efficient.
*/
newline(f, n)
{
int nicol;
register LINE *lp;
register int s;
if (n < 0)
return (FALSE);
while (n--) {
lp = curwp->w_dotp;
if (llength(lp) == curwp->w_doto
&& lp != curbp->b_linep
&& llength(lforw(lp)) == 0) {
if ((s=forwchar(FALSE, 1)) != TRUE)
return (s);
} else if ((s=lnewline()) != TRUE)
return (s);
}
return (TRUE);
}
/*
* Delete blank lines around dot. What this command does depends if dot is
* sitting on a blank line. If dot is sitting on a blank line, this command
* deletes all the blank lines above and below the current line. If it is
* sitting on a non blank line then it deletes all of the blank lines after
* the line. Normally this command is bound to "C-X C-O". Any argument is
* ignored.
*/
deblank(f, n)
{
register LINE *lp1;
register LINE *lp2;
register int nld;
lp1 = curwp->w_dotp;
while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp->b_linep)
lp1 = lp2;
lp2 = lp1;
nld = 0;
while ((lp2=lforw(lp2))!=curbp->b_linep && llength(lp2)==0)
++nld;
if (nld == 0)
return (TRUE);
curwp->w_dotp = lforw(lp1);
curwp->w_doto = 0;
return (ldelete(nld));
}
/*
* Insert a newline, then enough tabs and spaces to duplicate the indentation
* of the previous line. Assumes tabs are every eight characters. Quite simple.
* Figure out the indentation of the current line. Insert a newline by calling
* the standard routine. Insert the indentation by inserting the right number
* of tabs and spaces. Return TRUE if all ok. Return FALSE if one of the
* subcomands failed. Normally bound to "C-J".
*/
indent(f, n)
{
register int nicol;
register int c;
register int i;
if (n < 0)
return (FALSE);
while (n--) {
nicol = 0;
for (i=0; i<llength(curwp->w_dotp); ++i) {
c = lgetc(curwp->w_dotp, i);
if (c!=' ' && c!='\t')
break;
if (c == '\t')
nicol |= 0x07;
++nicol;
}
if (lnewline() == FALSE
|| ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE)
|| ((i=nicol%8)!=0 && linsert(i, ' ')==FALSE))
return (FALSE);
}
return (TRUE);
}
/*
* Delete forward. This is real easy, because the basic delete routine does
* all of the work. Watches for negative arguments, and does the right thing.
* If any argument is present, it kills rather than deletes, to prevent loss
* of text if typed with a big argument. Normally bound to "C-D".
*/
forwdel(f, n)
{
if (n < 0)
return (backdel(f, -n));
if (f != FALSE) { /* Really a kill. */
if ((lastflag&CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
}
return (ldelete(n, f));
}
/*
* Delete backwards. This is quite easy too, because it's all done with other
* functions. Just move the cursor back, and delete forwards. Like delete
* forward, this actually does a kill if presented with an argument. Bound to
* both "RUBOUT" and "C-H".
*/
backdel(f, n)
{
register int s;
if (n < 0)
return (forwdel(f, -n));
if (f != FALSE) { /* Really a kill. */
if ((lastflag&CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
}
if ((s=backchar(f, n)) == TRUE)
s = ldelete(n, f);
return (s);
}
/*
* Kill text. If called without an argument, it kills from dot to the end of
* the line, unless it is at the end of the line, when it kills the newline.
* If called with an argument of 0, it kills from the start of the line to dot.
* If called with a positive argument, it kills from dot forward over that
* number of newlines. If called with a negative argument it kills backwards
* that number of newlines. Normally bound to "C-K".
*/
kill(f, n)
{
register int chunk;
register LINE *nextp;
if ((lastflag&CFKILL) == 0) /* Clear kill buffer if */
kdelete(); /* last wasn't a kill. */
thisflag |= CFKILL;
if (f == FALSE) {
chunk = llength(curwp->w_dotp)-curwp->w_doto;
if (chunk == 0)
chunk = 1;
} else if (n == 0) {
chunk = curwp->w_doto;
curwp->w_doto = 0;
} else if (n > 0) {
chunk = llength(curwp->w_dotp)-curwp->w_doto+1;
nextp = lforw(curwp->w_dotp);
while (--n) {
if (nextp == curbp->b_linep)
return (FALSE);
chunk += llength(nextp)+1;
nextp = lforw(nextp);
}
} else {
mlwrite("neg kill");
return (FALSE);
}
return (ldelete(chunk, TRUE));
}
/*
* Yank text back from the kill buffer. This is really easy. All of the work
* is done by the standard insert routines. All you do is run the loop, and
* check for errors. Bound to "C-Y". The blank lines are inserted with a call
* to "newline" instead of a call to "lnewline" so that the magic stuff that
* happens when you type a carriage return also happens when a carriage return
* is yanked back from the kill buffer.
*/
yank(f, n)
{
register int c;
register int i;
extern int kused;
if (n < 0)
return (FALSE);
while (n--) {
i = 0;
while ((c=kremove(i)) >= 0) {
if (c == '\n') {
if (newline(FALSE, 1) == FALSE)
return (FALSE);
} else {
if (linsert(1, c) == FALSE)
return (FALSE);
}
++i;
}
}
return (TRUE);
}
/*
*
* SEARCH.C MODULE
*
*/
/*
* The functions in this file implement commands that search in the forward
* and backward directions. There are no special characters in the search
* strings. Probably should have a regular expression search, or something
* like that.
*
* REVISION HISTORY:
*
* ? Steve Wilhite, 1-Dec-85
* - massive cleanup on code.
*/
#include <stdio.h>
#include "ed.h"
/*
* Search forward. Get a search string from the user, and search, beginning at
* ".", for the string. If found, reset the "." to be just after the match
* string, and [perhaps] repaint the display. Bound to "C-S".
*/
forwsearch(f, n)
{
register LINE *clp;
register int cbo;
register LINE*tlp;
register int tbo;
register int c;
register char *pp;
register int s;
if ((s = readpattern("Search")) != TRUE)
return (s);
clp = curwp->w_dotp;
cbo = curwp->w_doto;
while (clp != curbp->b_linep)
{
if (cbo == llength(clp))
{
clp = lforw(clp);
cbo = 0;
c = '\n';
}
else
c = lgetc(clp, cbo++);
if (eq(c, pat[0]) != FALSE)
{
tlp = clp;
tbo = cbo;
pp = &pat[1];
while (*pp != 0)
{
if (tlp == curbp->b_linep)
goto fail;
if (tbo == llength(tlp))
{
tlp = lforw(tlp);
tbo = 0;
c = '\n';
}
else
c = lgetc(tlp, tbo++);
if (eq(c, *pp++) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
fail:;
}
mlwrite("Not found");
return (FALSE);
}
/*
* Reverse search. Get a search string from the user, and search, starting at
* "." and proceeding toward the front of the buffer. If found "." is left
* pointing at the first character of the pattern [the last character that was
j matched]. Bound to "C-R".
*/
backsearch(f, n)
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register char *epp;
register char *pp;
register int s;
if ((s = readpattern("Reverse search")) != TRUE)
return (s);
for (epp = &pat[0]; epp[1] != 0; ++epp)
;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
for (;;)
{
if (cbo == 0)
{
clp = lback(clp);
if (clp == curbp->b_linep)
{
mlwrite("Not found");
return (FALSE);
}
cbo = llength(clp)+1;
}
if (--cbo == llength(clp))
c = '\n';
else
c = lgetc(clp, cbo);
if (eq(c, *epp) != FALSE)
{
tlp = clp;
tbo = cbo;
pp = epp;
while (pp != &pat[0])
{
if (tbo == 0)
{
tlp = lback(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = llength(tlp)+1;
}
if (--tbo == llength(tlp))
c = '\n';
else
c = lgetc(tlp, tbo);
if (eq(c, *--pp) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
fail:;
}
}
/*
* Compare two characters. The "bc" comes from the buffer. It has it's case
* folded out. The "pc" is from the pattern.
*/
eq(bc, pc)
int bc;
int pc;
{
/*
if (bc>='a' && bc<='z')
bc -= 0x20;
if (pc>='a' && pc<='z')
pc -= 0x20;
*/
if (bc == pc)
return (TRUE);
return (FALSE);
}
/*
* Read a pattern. Stash it in the external variable "pat". The "pat" is not
* updated if the user types in an empty line. If the user typed an empty line,
* and there is no old pattern, it is an error. Display the old pattern, in the
* style of Jeff Lomicka. There is some do-it-yourself control expansion.
*/
readpattern(prompt)
char *prompt;
{
register char *cp1;
register char *cp2;
register int c;
register int s;
char tpat[NPAT+20];
cp1 = &tpat[0]; /* Copy prompt */
cp2 = prompt;
while ((c = *cp2++) != '\0')
*cp1++ = c;
if (pat[0] != '\0') /* Old pattern */
{
*cp1++ = ' ';
*cp1++ = '[';
cp2 = &pat[0];
while ((c = *cp2++) != 0)
{
if (cp1 < &tpat[NPAT+20-6]) /* "??]: \0" */
{
if (c<0x20 || c==0x7F) {
*cp1++ = '^';
c ^= 0x40;
}
else if (c == '%') /* Map "%" to */
*cp1++ = c; /* "%%". */
*cp1++ = c;
}
}
*cp1++ = ']';
}
*cp1++ = ':'; /* Finish prompt */
*cp1++ = ' ';
*cp1++ = '\0';
s = mlreply(tpat, tpat, NPAT); /* Read pattern */
if (s == TRUE) /* Specified */
strcpy(pat, tpat);
else if (s == FALSE && pat[0] != 0) /* CR, but old one */
s = TRUE;
return (s);
}
/*
*
* WINDOW.C MODULE
*
*/
/*
* Window management. Some of the functions are internal, and some are
* attached to keys that the user actually types.
*/
#include <stdio.h>
#include "ed.h"
/*
* Reposition dot in the current window to line "n". If the argument is
* positive, it is that line. If it is negative it is that line from the
* bottom. If it is 0 the window is centered (this is what the standard
* redisplay code does). With no argument it defaults to 1. Bound to M-!.
* Because of the default, it works like in Gosling.
*/
reposition(f, n)
{
curwp->w_force = n;
curwp->w_flag |= WFFORCE;
return (TRUE);
}
/*
* Refresh the screen. With no argument, it just does the refresh. With an
* argument it recenters "." in the current window. Bound to "C-L".
*/
refresh(f, n)
{
if (f == FALSE)
sgarbf = TRUE;
else
{
curwp->w_force = 0; /* Center dot. */
curwp->w_flag |= WFFORCE;
}
return (TRUE);
}
/*
* The command make the next window (next => down the screen) the current
* window. There are no real errors, although the command does nothing if
* there is only 1 window on the screen. Bound to "C-X C-N".
*/
nextwind(f, n)
{
register WINDOW *wp;
if ((wp = curwp->w_wndp) == NULL) {
wp = wheadp;
}
curwp = wp;
curbp = wp->w_bufp;
if (wheadp->w_wndp == NULL) /* cej if no other windows */
return (FALSE); /* return false */
else
return (TRUE);
}
/*
* This command makes the previous window (previous => up the screen) the
* current window. There arn't any errors, although the command does not do a
* lot if there is 1 window.
*/
prevwind(f, n)
{
register WINDOW *wp1;
register WINDOW *wp2;
wp1 = wheadp;
wp2 = curwp;
if (wp1 == wp2)
wp2 = NULL;
while (wp1->w_wndp != wp2)
wp1 = wp1->w_wndp;
curwp = wp1;
curbp = wp1->w_bufp;
return (TRUE);
}
/*
* This command moves the current window down by "arg" lines. Recompute the
* top line in the window. The move up and move down code is almost completely
* the same; most of the work has to do with reframing the window, and picking
* a new dot. We share the code by having "move down" just be an interface to
* "move up". Magic. Bound to "C-X C-N".
*/
mvdnwind(f, n)
int n;
{
return (mvupwind(f, -n));
}
/*
* Move the current window up by "arg" lines. Recompute the new top line of
* the window. Look to see if "." is still on the screen. If it is, you win.
* If it isn't, then move "." to center it in the new framing of the window
* (this command does not really move "."; it moves the frame). Bound to
* "C-X C-P".
*/
mvupwind(f, n)
int n;
{
register LINE *lp;
register int i;
lp = curwp->w_linep;
if (n < 0)
{
while (n++ && lp!=curbp->b_linep)
lp = lforw(lp);
}
else
{
while (n-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
}
curwp->w_linep = lp;
curwp->w_flag |= WFHARD; /* Mode line is OK. */
for (i = 0; i < curwp->w_ntrows; ++i)
{
if (lp == curwp->w_dotp)
return (TRUE);
if (lp == curbp->b_linep)
break;
lp = lforw(lp);
}
lp = curwp->w_linep;
i = curwp->w_ntrows/2;
while (i-- && lp != curbp->b_linep)
lp = lforw(lp);
curwp->w_dotp = lp;
curwp->w_doto = 0;
return (TRUE);
}
/*
* This command makes the current window the only window on the screen. Bound
* to "C-X 1". Try to set the framing so that "." does not have to move on the
* display. Some care has to be taken to keep the values of dot and mark in
* the buffer structures right if the distruction of a window makes a buffer
* become undisplayed.
*/
onlywind(f, n)
{
register WINDOW *wp;
register LINE *lp;
register int i;
while (wheadp != curwp) {
wp = wheadp;
wheadp = wp->w_wndp;
if (--wp->w_bufp->b_nwnd == 0) {
wp->w_bufp->b_dotp = wp->w_dotp;
wp->w_bufp->b_doto = wp->w_doto;
wp->w_bufp->b_markp = wp->w_markp;
wp->w_bufp->b_marko = wp->w_marko;
}
free((char *) wp);
}
while (curwp->w_wndp != NULL) {
wp = curwp->w_wndp;
curwp->w_wndp = wp->w_wndp;
if (--wp->w_bufp->b_nwnd == 0) {
wp->w_bufp->b_dotp = wp->w_dotp;
wp->w_bufp->b_doto = wp->w_doto;
wp->w_bufp->b_markp = wp->w_markp;
wp->w_bufp->b_marko = wp->w_marko;
}
free((char *) wp);
}
lp = curwp->w_linep;
i = curwp->w_toprow;
while (i!=0 && lback(lp)!=curbp->b_linep) {
--i;
lp = lback(lp);
}
curwp->w_toprow = 0;
curwp->w_ntrows = term.t_nrow-1;
curwp->w_linep = lp;
curwp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Split the current window. A window smaller than 3 lines cannot be split.
* The only other error that is possible is a "malloc" failure allocating the
* structure for the new window. Bound to "C-X 2".
*/
splitwind(f, n)
{
register WINDOW *wp;
register LINE *lp;
register int ntru;
register int ntrl;
register int ntrd;
register WINDOW *wp1;
register WINDOW *wp2;
if (curwp->w_ntrows < 3) {
mlwrite("Cannot split a %d line window", curwp->w_ntrows);
return (FALSE);
}
if ((wp = (WINDOW *) malloc(sizeof(WINDOW))) == NULL) {
mlwrite("Cannot allocate WINDOW block");
return (FALSE);
}
++curbp->b_nwnd; /* Displayed twice. */
wp->w_bufp = curbp;
wp->w_dotp = curwp->w_dotp;
wp->w_doto = curwp->w_doto;
wp->w_markp = curwp->w_markp;
wp->w_marko = curwp->w_marko;
wp->w_flag = 0;
wp->w_force = 0;
ntru = (curwp->w_ntrows-1) / 2; /* Upper size */
ntrl = (curwp->w_ntrows-1) - ntru; /* Lower size */
lp = curwp->w_linep;
ntrd = 0;
while (lp != curwp->w_dotp) {
++ntrd;
lp = lforw(lp);
}
lp = curwp->w_linep;
if (ntrd <= ntru) { /* Old is upper window. */
if (ntrd == ntru) /* Hit mode line. */
lp = lforw(lp);
curwp->w_ntrows = ntru;
wp->w_wndp = curwp->w_wndp;
curwp->w_wndp = wp;
wp->w_toprow = curwp->w_toprow+ntru+1;
wp->w_ntrows = ntrl;
} else { /* Old is lower window */
wp1 = NULL;
wp2 = wheadp;
while (wp2 != curwp) {
wp1 = wp2;
wp2 = wp2->w_wndp;
}
if (wp1 == NULL)
wheadp = wp;
else
wp1->w_wndp = wp;
wp->w_wndp = curwp;
wp->w_toprow = curwp->w_toprow;
wp->w_ntrows = ntru;
++ntru; /* Mode line. */
curwp->w_toprow += ntru;
curwp->w_ntrows = ntrl;
while (ntru--)
lp = lforw(lp);
}
curwp->w_linep = lp; /* Adjust the top lines */
wp->w_linep = lp; /* if necessary. */
curwp->w_flag |= WFMODE|WFHARD;
wp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Enlarge the current window. Find the window that loses space. Make sure it
* is big enough. If so, hack the window descriptions, and ask redisplay to do
* all the hard work. You don't just set "force reframe" because dot would
* move. Bound to "C-X Z".
*/
enlargewind(f, n)
{
register WINDOW *adjwp;
register LINE *lp;
register int i;
if (n < 0)
return (shrinkwind(f, -n));
if (wheadp->w_wndp == NULL) {
mlwrite("Only one window");
return (FALSE);
}
if ((adjwp=curwp->w_wndp) == NULL) {
adjwp = wheadp;
while (adjwp->w_wndp != curwp)
adjwp = adjwp->w_wndp;
}
if (adjwp->w_ntrows <= n) {
mlwrite("Impossible change");
return (FALSE);
}
if (curwp->w_wndp == adjwp) { /* Shrink below. */
lp = adjwp->w_linep;
for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
lp = lforw(lp);
adjwp->w_linep = lp;
adjwp->w_toprow += n;
} else { /* Shrink above. */
lp = curwp->w_linep;
for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_toprow -= n;
}
curwp->w_ntrows += n;
adjwp->w_ntrows -= n;
curwp->w_flag |= WFMODE|WFHARD;
adjwp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Shrink the current window. Find the window that gains space. Hack at the
* window descriptions. Ask the redisplay to do all the hard work. Bound to
* "C-X C-Z".
*/
shrinkwind(f, n)
{
register WINDOW *adjwp;
register LINE *lp;
register int i;
if (n < 0)
return (enlargewind(f, -n));
if (wheadp->w_wndp == NULL) {
mlwrite("Only one window");
return (FALSE);
}
if ((adjwp=curwp->w_wndp) == NULL) {
adjwp = wheadp;
while (adjwp->w_wndp != curwp)
adjwp = adjwp->w_wndp;
}
if (curwp->w_ntrows <= n) {
mlwrite("Impossible change");
return (FALSE);
}
if (curwp->w_wndp == adjwp) { /* Grow below. */
lp = adjwp->w_linep;
for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
lp = lback(lp);
adjwp->w_linep = lp;
adjwp->w_toprow -= n;
} else { /* Grow above. */
lp = curwp->w_linep;
for (i=0; i<n && lp!=curbp->b_linep; ++i)
lp = lforw(lp);
curwp->w_linep = lp;
curwp->w_toprow += n;
}
curwp->w_ntrows -= n;
adjwp->w_ntrows += n;
curwp->w_flag |= WFMODE|WFHARD;
adjwp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Pick a window for a pop-up. Split the screen if there is only one window.
* Pick the uppermost window that isn't the current window. An LRU algorithm
* might be better. Return a pointer, or NULL on error.
*/
WINDOW *
wpopup()
{
register WINDOW *wp;
if (wheadp->w_wndp == NULL /* Only 1 window */
&& splitwind(FALSE, 0) == FALSE) /* and it won't split */
return (NULL);
wp = wheadp; /* Find window to use */
while (wp!=NULL && wp==curwp)
wp = wp->w_wndp;
return (wp);
}
/*
*
* MAIN.C MODULE
*
*/
/*
* This program is in public domain; written by Dave G. Conroy.
* This file contains the main driving routine, and some keyboard processing
* code, for the MicroEMACS screen editor.
*
* REVISION HISTORY:
*
* 1.0 Steve Wilhite, 30-Nov-85
* - Removed the old LK201 and VT100 logic. Added code to support the
* DEC Rainbow keyboard (which is a LK201 layout) using the the Level
* 1 Console In ROM INT. See "rainbow.h" for the function key definitions.
*
* 2.0 George Jones, 12-Dec-85
* - Ported to Amiga.
*/
#include <stdio.h>
#include "ed.h"
#if VMS
#include <ssdef.h>
#define GOOD (SS$_NORMAL)
#endif
#ifndef GOOD
#define GOOD 0
#endif
int currow; /* Working cursor row */
int curcol; /* Working cursor column */
int fillcol; /* Current fill column */
int thisflag; /* Flags, this command */
int lastflag; /* Flags, last command */
int curgoal; /* Goal column */
BUFFER *curbp; /* Current buffer */
WINDOW *curwp; /* Current window */
BUFFER *bheadp; /* BUFFER listhead */
WINDOW *wheadp; /* WINDOW listhead */
BUFFER *blistp; /* Buffer list BUFFER */
short kbdm[NKBDM] = {CTLX|')'}; /* Macro */
short *kbdmip; /* Input for above */
short *kbdmop; /* Output for above */
char pat[NPAT]; /* Pattern */
typedef struct {
short k_code; /* Key code */
int (*k_fp)(); /* Routine to handle it */
} KEYTAB;
extern int ctrlg(); /* Abort out of things */
extern int quit(); /* Quit */
extern int ctlxlp(); /* Begin macro */
extern int ctlxrp(); /* End macro */
extern int ctlxe(); /* Execute macro */
extern int fileread(); /* Get a file, read only */
extern int filevisit(); /* Get a file, read write */
extern int filewrite(); /* Write a file */
extern int filesave(); /* Save current file */
extern int filename(); /* Adjust file name */
extern int getccol(); /* Get current column */
extern int gotobol(); /* Move to start of line */
extern int forwchar(); /* Move forward by characters */
extern int gotoeol(); /* Move to end of line */
extern int backchar(); /* Move backward by characters */
extern int forwline(); /* Move forward by lines */
extern int backline(); /* Move backward by lines */
extern int forwpage(); /* Move forward by pages */
extern int backpage(); /* Move backward by pages */
extern int gotobob(); /* Move to start of buffer */
extern int gotoeob(); /* Move to end of buffer */
extern int setfillcol(); /* Set fill column. */
extern int setmark(); /* Set mark */
extern int swapmark(); /* Swap "." and mark */
extern int forwsearch(); /* Search forward */
extern int backsearch(); /* Search backwards */
extern int showcpos(); /* Show the cursor position */
extern int nextwind(); /* Move to the next window */
extern int prevwind(); /* Move to the previous window */
extern int onlywind(); /* Make current window only one */
extern int splitwind(); /* Split current window */
extern int mvdnwind(); /* Move window down */
extern int mvupwind(); /* Move window up */
extern int enlargewind(); /* Enlarge display window. */
extern int shrinkwind(); /* Shrink window. */
extern int listbuffers(); /* Display list of buffers */
extern int usebuffer(); /* Switch a window to a buffer */
extern int killbuffer(); /* Make a buffer go away. */
extern int reposition(); /* Reposition window */
extern int refresh(); /* Refresh the screen */
extern int twiddle(); /* Twiddle characters */
extern int tab(); /* Insert tab */
extern int newline(); /* Insert CR-LF */
extern int indent(); /* Insert CR-LF, then indent */
extern int openline(); /* Open up a blank line */
extern int deblank(); /* Delete blank lines */
extern int quote(); /* Insert literal */
extern int backword(); /* Backup by words */
extern int forwword(); /* Advance by words */
extern int forwdel(); /* Forward delete */
extern int backdel(); /* Backward delete */
extern int kill(); /* Kill forward */
extern int yank(); /* Yank back from killbuffer. */
extern int upperword(); /* Upper case word. */
extern int lowerword(); /* Lower case word. */
extern int upperregion(); /* Upper case region. */
extern int lowerregion(); /* Lower case region. */
extern int capword(); /* Initial capitalize word. */
extern int delfword(); /* Delete forward word. */
extern int delbword(); /* Delete backward word. */
extern int killregion(); /* Kill region. */
extern int copyregion(); /* Copy region to kill buffer. */
extern int spawncli(); /* Run CLI in a subjob. */
extern int spawn(); /* Run a command in a subjob. */
extern int quickexit(); /* low keystroke style exit. */
extern int metacmds(); /* meta commands addec c.jutzi */
/*
* Command table.
* This table is *roughly* in ASCII order, left to right across the
* characters of the command. This expains the funny location of the
* control-X commands.
*/
KEYTAB keytab[] = {
CTRL|'@', setmark,
CTRL|'A', gotobol,
CTRL|'B', backchar,
CTRL|'C', spawncli, /* Run CLI in subjob. */
CTRL|'D', forwdel,
CTRL|'E', gotoeol,
CTRL|'F', forwchar,
CTRL|'G', ctrlg,
CTRL|'H', backdel,
CTRL|'I', tab,
CTRL|'J', indent,
CTRL|'K', kill,
CTRL|'L', refresh,
CTRL|'M', newline,
CTRL|'N', forwline,
CTRL|'O', openline,
CTRL|'P', backline,
CTRL|'Q', quote, /* Often unreachable */
CTRL|'R', backsearch,
CTRL|'S', forwsearch, /* Often unreachable */
CTRL|'T', twiddle,
CTRL|'V', forwpage,
CTRL|'W', killregion,
CTRL|'Y', yank,
CTRL|'Z', quickexit, /* quick save and exit */
CTLX|CTRL|'B', listbuffers,
CTLX|CTRL|'C', quit, /* Hard quit. */
CTLX|CTRL|'F', filename,
CTLX|CTRL|'L', lowerregion,
CTLX|CTRL|'O', deblank,
CTLX|CTRL|'N', mvdnwind,
CTLX|CTRL|'P', mvupwind,
CTLX|CTRL|'R', fileread,
CTLX|CTRL|'S', filesave, /* Often unreachable */
CTLX|CTRL|'U', upperregion,
CTLX|CTRL|'V', filevisit,
CTLX|CTRL|'W', filewrite,
CTLX|CTRL|'X', swapmark,
CTLX|CTRL|'Z', shrinkwind,
CTLX|'!', spawn, /* Run 1 command. */
CTLX|'=', showcpos,
CTLX|'(', ctlxlp,
CTLX|')', ctlxrp,
CTLX|'1', onlywind,
CTLX|'2', splitwind,
CTLX|'B', usebuffer,
CTLX|'E', ctlxe,
CTLX|'F', setfillcol,
CTLX|'K', killbuffer,
CTLX|'N', nextwind,
CTLX|'P', prevwind,
CTLX|'Z', enlargewind,
META|CTRL|'H', delbword,
META|'!', reposition,
META|'.', setmark,
META|'>', gotoeob,
META|'<', gotobob,
META|'B', backword,
META|'C', capword,
META|'D', delfword,
META|'F', forwword,
META|'L', lowerword,
META|'U', upperword,
META|'V', backpage,
META|'W', copyregion,
META|'X', metacmds,
META|0x7F, delbword,
0x7F, backdel
};
#define NKEYTAB (sizeof(keytab)/sizeof(keytab[0]))
#if RAINBOW
#include "rainbow.h"
/*
* Mapping table from the LK201 function keys to the internal EMACS character.
*/
short lk_map[][2] = {
Up_Key, CTRL+'P',
Down_Key, CTRL+'N',
Left_Key, CTRL+'B',
Right_Key, CTRL+'F',
Shift+Left_Key, META+'B',
Shift+Right_Key, META+'F',
Control+Left_Key, CTRL+'A',
Control+Right_Key, CTRL+'E',
Prev_Scr_Key, META+'V',
Next_Scr_Key, CTRL+'V',
Shift+Up_Key, META+'<',
Shift+Down_Key, META+'>',
Cancel_Key, CTRL+'G',
Find_Key, CTRL+'S',
Shift+Find_Key, CTRL+'R',
Insert_Key, CTRL+'Y',
Options_Key, CTRL+'D',
Shift+Options_Key, META+'D',
Remove_Key, CTRL+'W',
Shift+Remove_Key, META+'W',
Select_Key, CTRL+'@',
Shift+Select_Key, CTLX+CTRL+'X',
Interrupt_Key, CTRL+'U',
Keypad_PF2, META+'L',
Keypad_PF3, META+'C',
Keypad_PF4, META+'U',
Shift+Keypad_PF2, CTLX+CTRL+'L',
Shift+Keypad_PF4, CTLX+CTRL+'U',
Keypad_1, CTLX+'1',
Keypad_2, CTLX+'2',
Do_Key, CTLX+'E',
Keypad_4, CTLX+CTRL+'B',
Keypad_5, CTLX+'B',
Keypad_6, CTLX+'K',
Resume_Key, META+'!',
Control+Next_Scr_Key, CTLX+'N',
Control+Prev_Scr_Key, CTLX+'P',
Control+Up_Key, CTLX+CTRL+'P',
Control+Down_Key, CTLX+CTRL+'N',
Help_Key, CTLX+'=',
Shift+Do_Key, CTLX+'(',
Control+Do_Key, CTLX+')',
Keypad_0, CTLX+'Z',
Shift+Keypad_0, CTLX+CTRL+'Z',
Main_Scr_Key, CTRL+'C',
Keypad_Enter, CTLX+'!',
Exit_Key, CTLX+CTRL+'C',
Shift+Exit_Key, CTRL+'Z'
};
#define lk_map_size (sizeof(lk_map)/2)
#endif
main(argc, argv)
char *argv[];
{
register int c;
register int f;
register int n;
register int mflag;
char bname[NBUFN];
strcpy(bname, "main"); /* Work out the name of */
if (argc > 1) /* the default buffer. */
makename(bname, argv[1]);
edinit(bname); /* Buffers, windows. */
vtinit(); /* Displays. */
if (argc > 1) {
update(); /* You have to update */
readin(argv[1]); /* in case "[New file]" */
}
lastflag = 0; /* Fake last flags. */
loop:
update(); /* Fix up the screen */
c = getkey();
if (mpresf != FALSE) {
mlerase();
update();
if (c == ' ') /* ITS EMACS does this */
goto loop;
}
f = FALSE;
n = 1;
if (c == (CTRL|'U')) { /* ^U, start argument */
f = TRUE;
n = 4; /* with argument of 4 */
mflag = 0; /* that can be discarded. */
mlwrite("Arg: 4");
while ((c=getkey()) >='0' && c<='9' || c==(CTRL|'U') || c=='-'){
if (c == (CTRL|'U'))
n = n*4;
/*
* If dash, and start of argument string, set arg.
* to -1. Otherwise, insert it.
*/
else if (c == '-') {
if (mflag)
break;
n = 0;
mflag = -1;
}
/*
* If first digit entered, replace previous argument
* with digit and set sign. Otherwise, append to arg.
*/
else {
if (!mflag) {
n = 0;
mflag = 1;
}
n = 10*n + c - '0';
}
mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1));
}
/*
* Make arguments preceded by a minus sign negative and change
* the special argument "^U -" to an effective "^U -1".
*/
if (mflag == -1) {
if (n == 0)
n++;
n = -n;
}
}
if (c == (CTRL|'X')){ /* ^X is a prefix */
c = CTLX | getctl();
}
if (kbdmip != NULL) { /* Save macro strokes. */
if (c!=(CTLX|')') && kbdmip>&kbdm[NKBDM-6]) {
ctrlg(FALSE, 0);
goto loop;
}
if (f != FALSE) {
*kbdmip++ = (CTRL|'U');
*kbdmip++ = n;
}
*kbdmip++ = c;
}
execute(c, f, n); /* Do it. */
goto loop;
}
/*
* Initialize all of the buffers and windows. The buffer name is passed down
* as an argument, because the main routine may have been told to read in a
* file by default, and we want the buffer name to be right.
*/
edinit(bname)
char bname[];
{
register BUFFER *bp;
register WINDOW *wp;
bp = bfind(bname, TRUE, 0); /* First buffer */
blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer */
wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
if (bp==NULL || wp==NULL || blistp==NULL)
exit(1);
curbp = bp; /* Make this current */
wheadp = wp;
curwp = wp;
wp->w_wndp = NULL; /* Initialize window */
wp->w_bufp = bp;
bp->b_nwnd = 1; /* Displayed. */
wp->w_linep = bp->b_linep;
wp->w_dotp = bp->b_linep;
wp->w_doto = 0;
wp->w_markp = NULL;
wp->w_marko = 0;
wp->w_toprow = 0;
wp->w_ntrows = term.t_nrow-1; /* "-1" for mode line. */
wp->w_force = 0;
wp->w_flag = WFMODE|WFHARD; /* Full. */
}
/*
* This is the general command execution routine. It handles the fake binding
* of all the keys to "self-insert". It also clears out the "thisflag" word,
* and arranges to move it to the "lastflag", so that the next command can
* look at it. Return the status of command.
*/
execute(c, f, n)
{
register KEYTAB *ktp;
register int status;
ktp = &keytab[0]; /* Look in key table. */
while (ktp < &keytab[NKEYTAB]) {
if (ktp->k_code == c) {
thisflag = 0;
status = (*ktp->k_fp)(f, n);
lastflag = thisflag;
return (status);
}
++ktp;
}
/*
* If a space was typed, fill column is defined, the argument is non-
* negative, and we are now past fill column, perform word wrap.
*/
if (c == ' ' && fillcol > 0 && n>=0 && getccol(FALSE) > fillcol)
wrapword();
if ((c>=0x20 && c<=0x7E) /* Self inserting. */
|| (c>=0xA0 && c<=0xFE)) {
if (n <= 0) { /* Fenceposts. */
lastflag = 0;
return (n<0 ? FALSE : TRUE);
}
thisflag = 0; /* For the future. */
status = linsert(n, c);
lastflag = thisflag;
return (status);
}
lastflag = 0; /* Fake last flags. */
return (FALSE);
}
/*
* Read in a key.
* Do the standard keyboard preprocessing. Convert the keys to the internal
* character set.
*/
getkey()
{
register int c;
c = (*term.t_getchar)();
#if RAINBOW
if (c & Function_Key)
{
int i;
for (i = 0; i < lk_map_size; i++)
if (c == lk_map[i][0])
return lk_map[i][1];
}
else if (c == Shift + 015) return CTRL | 'J';
else if (c == Shift + 0x7F) return META | 0x7F;
#endif
if (c == METACH) { /* Apply M- prefix */
c = getctl();
return (META | c);
}
if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
c = CTRL | (c+'@');
return (c);
}
/*
* Get a key.
* Apply control modifications to the read key.
*/
getctl()
{
register int c;
c = (*term.t_getchar)();
if (c>='a' && c<='z') /* Force to upper */
c -= 0x20;
if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
c = CTRL | (c+'@');
return (c);
}
/*
* Fancy quit command, as implemented by Norm. If the current buffer has
* changed do a write current buffer and exit emacs, otherwise simply exit.
*/
quickexit(f, n)
{
if ((curbp->b_flag&BFCHG) != 0 /* Changed. */
&& (curbp->b_flag&BFTEMP) == 0) /* Real. */
filesave(f, n);
quit(f, n); /* conditionally quit */
}
/*
* Quit command. If an argument, always quit. Otherwise confirm if a buffer
* has been changed and not written out. Normally bound to "C-X C-C".
*/
quit(f, n)
{
register int s;
if (f != FALSE /* Argument forces it. */
|| anycb() == FALSE /* All buffers clean. */
|| (s=mlyesno("Quit")) == TRUE) { /* User says it's OK. */
vttidy();
exit(GOOD);
}
return (s);
}
/*
* Begin a keyboard macro.
* Error if not at the top level in keyboard processing. Set up variables and
* return.
*/
ctlxlp(f, n)
{
if (kbdmip!=NULL || kbdmop!=NULL) {
mlwrite("Not now");
return (FALSE);
}
mlwrite("[Start macro]");
kbdmip = &kbdm[0];
return (TRUE);
}
/*
* End keyboard macro. Check for the same limit conditions as the above
* routine. Set up the variables and return to the caller.
*/
ctlxrp(f, n)
{
if (kbdmip == NULL) {
mlwrite("Not now");
return (FALSE);
}
mlwrite("[End macro]");
kbdmip = NULL;
return (TRUE);
}
/*
* Execute a macro.
* The command argument is the number of times to loop. Quit as soon as a
* command gets an error. Return TRUE if all ok, else FALSE.
*/
ctlxe(f, n)
{
register int c;
register int af;
register int an;
register int s;
if (kbdmip!=NULL || kbdmop!=NULL) {
mlwrite("Not now");
return (FALSE);
}
if (n <= 0)
return (TRUE);
do {
kbdmop = &kbdm[0];
do {
af = FALSE;
an = 1;
if ((c = *kbdmop++) == (CTRL|'U')) {
af = TRUE;
an = *kbdmop++;
c = *kbdmop++;
}
s = TRUE;
} while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE);
kbdmop = NULL;
} while (s==TRUE && --n);
return (s);
}
/*
* Abort.
* Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
* Sometimes called as a routine, to do general aborting of stuff.
*/
ctrlg(f, n)
{
(*term.t_beep)();
if (kbdmip != NULL) {
kbdm[0] = (CTLX|')');
kbdmip = NULL;
}
return (ABORT);
}
/*
*
* META.C MODULE
*
*/
#include <stdio.h>
#include "ed.h"
#define NMETA_CMDS 52
int replace_string(); /* replace string global */
int query_replace(); /* query replace */
extern BUFFER *bfind(); /* buffer find */
extern int forwsearch(); /* forward search */
extern int backsearch(); /* backward search */
extern int ctlxlp(); /* Begin macro */
extern int ctlxrp(); /* End macro */
extern int ctlxe(); /* Execute macro */
extern int fileread(); /* Get a file, read only */
extern int filevisit(); /* Get a file, read write */
extern int filewrite(); /* Write a file */
extern int filesave(); /* Save current file */
extern int quit(); /* Quit */
extern int gotobol(); /* Move to start of line */
extern int forwchar(); /* Move forward by characters */
extern int gotoeol(); /* Move to end of line */
extern int backchar(); /* Move backward by characters */
extern int forwline(); /* Move forward by lines */
extern int backline(); /* Move backward by lines */
extern int forwpage(); /* Move forward by pages */
extern int backpage(); /* Move backward by pages */
extern int gotobob(); /* Move to start of buffer */
extern int gotoeob(); /* Move to end of buffer */
extern int setmark(); /* Set mark */
extern int swapmark(); /* Swap "." and mark */
extern int nextwind(); /* Move to the next window */
extern int prevwind(); /* Move to the previous window */
extern int onlywind(); /* Make current window only one */
extern int splitwind(); /* Split current window */
extern int mvdnwind(); /* Move window down */
extern int mvupwind(); /* Move window up */
extern int enlargewind(); /* Enlarge display window. */
extern int shrinkwind(); /* Shrink window. */
extern int listbuffers(); /* Display list of buffers */
extern int usebuffer(); /* Switch a window to a buffer */
extern int killbuffer(); /* Make a buffer go away. */
extern int reposition(); /* Reposition window */
extern int refresh(); /* Refresh the screen */
extern int deblank(); /* Delete blank lines */
extern int quote(); /* Insert literal */
extern int backword(); /* Backup by words */
extern int forwword(); /* Advance by words */
extern int forwdel(); /* Forward delete */
extern int backdel(); /* Backward delete */
extern int kill(); /* Kill forward */
extern int yank(); /* Yank back from killbuffer. */
extern int upperword(); /* Upper case word. */
extern int lowerword(); /* Lower case word. */
extern int upperregion(); /* Upper case region. */
extern int lowerregion(); /* Lower case region. */
extern int capword(); /* Initial capitalize word. */
extern int delfword(); /* Delete forward word. */
extern int delbword(); /* Delete backward word. */
extern int killregion(); /* Kill region. */
extern int copyregion(); /* Copy region to kill buffer. */
struct Meta_Disc {
char *meta_str;
int (*meta_fctn)();
} Meta_Commands[NMETA_CMDS] = {
{ "replace-string",replace_string },
{ "query-replace",query_replace },
{ "forward-search",forwsearch },
{ "backward-search",backsearch },
{ "begin-macro",ctlxlp },
{ "end-macro",ctlxrp },
{ "execute-macro",ctlxe },
{ "read-file",fileread },
{ "visit-file",filevisit },
{ "write-file",filewrite },
{ "save-file",filesave },
{ "begin-of-line",gotobol },
{ "end-of-line",gotoeol },
{ "top-of-buffer",gotobob },
{ "bottom-of-buffer",gotoeob },
{ "set-mark",setmark },
{ "swap-mark",swapmark },
{ "next-window",nextwind },
{ "previous-window",prevwind },
{ "only-window",onlywind },
{ "split-window",splitwind },
{ "move-window-down",mvdnwind },
{ "move-window-up",mvupwind },
{ "enlarge-window",enlargewind },
{ "shrink-window",shrinkwind },
{ "list-buffers",listbuffers },
{ "use-buffer",usebuffer },
{ "kill-buffer",killbuffer },
{ "reposition-window",reposition },
{ "refresh-screen",refresh },
{ "delete-blank-lines",deblank },
{ "insert-literial",quote },
{ "backward-word",backword },
{ "forward-word",forwword },
{ "kill-to-end-of-line",kill },
{ "yank-from-killbuffer",yank },
{ "case-word-upper",upperword },
{ "case-word-lower",lowerword },
{ "case-region-upper",upperregion },
{ "case-region-lower",lowerregion },
{ "case-word-capitalize",capword },
{ "delete-next-word",delfword },
{ "delete-previous-word",delbword },
{ "kill-region",killregion },
{ "copy-region",copyregion },
{ "quit",quit },
{ "backward-character",backchar },
{ "forward-character",forwchar },
{ "forward-line",forwline },
{ "backward-line",backline },
{ "forward-page",forwpage },
{ "backward-page",backpage }
};
/* test routine */
metacmds() {
register int s;
char buff[64];
s = mlreply("Meta-X: ",buff,sizeof(buff));
if (s == ABORT)
return(ABORT);
if ((s = findpos(buff)) < 0) {
mlwrite("Meta-X command does not exist");
createhelpbuffer();
return(ABORT);
}
else {
(*(Meta_Commands[s].meta_fctn))(NULL,1);
}
}
/* Find Position()
*
* This procedure matches the string typed in at the command line
* with one of the functions in the META-X list. If none is found
* then it returns with a -1, else it returns with the index into
* the Meta_Commands (list).
*/
findpos(buff)
char *buff;
{
register int i;
for (i=0;i<NMETA_CMDS;i++){
if (strcmp(Meta_Commands[i].meta_str,buff)==0)
return(i);
}
return(-1);
}
/*
* Replace String ()
*
* This routine reads in an old and new string and does a global
* replacement of all those occurances. Most of this routine is
* a copy of the Search command ('forwardsearch');
*/
replace_string() {
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register char *pp;
register int s;
register int repl_cnt;
register char old_str[64];
register char new_str[64];
repl_cnt = 0;
s = mlreply("Old String: ",old_str,sizeof(old_str));
if ((s == ABORT) || (s != TRUE))
return (s);
if ((s = mlreply("New String: ",new_str,sizeof(new_str))) == ABORT)
return (s);
clp = curwp->w_dotp;
cbo = curwp->w_doto;
while (clp != curbp->b_linep)
{
if (cbo == llength(clp))
{
clp = lforw(clp);
cbo = 0;
c = '\n';
}
else
c = lgetc(clp, cbo++);
if (eq(c, old_str[0]) != FALSE)
{
tlp = clp;
tbo = cbo;
pp = &old_str[1];
while (*pp != 0)
{
if (tlp == curbp->b_linep)
goto fail;
if (tbo == llength(tlp))
{
tlp = lforw(tlp);
tbo = 0;
c = '\n';
}
else
c = lgetc(tlp, tbo++);
if (eq(c, *pp++) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
repl_cnt++;
replace_occ(strlen(old_str),new_str);
}
fail:;
}
mlwrite(" %d Strings Replaced ",repl_cnt);
return (FALSE);
}
/* Query Replace()
*
* This routine reads in an old and new string and does a query
* replacement of all those occurances. Most of this routine is
* a copy of the Search command ('forwardsearch');
*/
query_replace() {
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register int ch;
register char *pp;
register int s;
register int repl_cnt;
register char old_str[64];
register char new_str[64];
repl_cnt = 0;
s = mlreply("Old String: ",old_str,sizeof(old_str));
if ((s == ABORT) || (s != TRUE))
return (s);
if ((s = mlreply("New String: ",new_str,sizeof(new_str))) == ABORT)
return (s);
mlwrite("<ESC>,n,N : <SP>,y,Y : ^G");
clp = curwp->w_dotp;
cbo = curwp->w_doto;
while (clp != curbp->b_linep) {
if (cbo == llength(clp)) {
clp = lforw(clp);
cbo = 0;
c = '\n';
} /* if */
else
c = lgetc(clp, cbo++);
if (eq(c, old_str[0]) != FALSE) {
tlp = clp;
tbo = cbo;
pp = &old_str[1];
while (*pp != 0) {
if (tlp == curbp->b_linep)
goto fail;
if (tbo == llength(tlp)) {
tlp = lforw(tlp);
tbo = 0;
c = '\n';
} /* if */
else
c = lgetc(tlp, tbo++);
if (eq(c, *pp++) == FALSE)
goto fail;
} /* while */
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
update();
tryagain:;
ch = (*term.t_getchar)();
switch (ch) {
case 0x07 : {
ctrlg(FALSE, 0);
mlwrite("Aborted");
(*term.t_flush)();
return(0);
goto fail;
}
break;
case 0x020 :
case 0x079 :
case 0x059 : {
repl_cnt++;
replace_occ(strlen(old_str),new_str);
}
break;
case 0x01b :
case 0x06e :
case 0x04E : {
}
break;
default : {
ctrlg(FALSE, 0);
(*term.t_flush)();
mlwrite(" TRY AGAIN! <ESC>,n,N : <SP>,y,Y : ^G");
update();
goto tryagain;
}
} /* switch */
} /* if */
fail:;
} /* while */
mlwrite(" %d Strings Replaced ",repl_cnt);
return (FALSE);
}
/*
* REPLACE_OCCURANCE
*
* This routine is called by the Replace String and the Query Replace
* routines.
* This routine takes the current cursor location, deletes the old
* string and replaces it with the new string
*/
replace_occ(old_length,new_str)
int old_length;
char *new_str;
{
register int i;
register int len;
backdel(TRUE,old_length);
len = strlen(new_str);
for (i=0;i<len;i++) {
linsert(1,*new_str);
new_str++;
}
}
/*
* CREATE HELP BUFFER
*
* This routine displays the help buffer. If one does not exist
* then the one is created. The buffer is flushed and the strings
* from the Meta-x data structure are written into the buffer.
*
*/
createhelpbuffer() {
register BUFFER *bp;
register int i;
bp = bfind("HELP",TRUE,0);
if (nextwind(NULL,NULL)==FALSE)
splitwind();
useb("HELP");
if (bclear(bp) != TRUE)
return(FALSE);
for (i=0;i<NMETA_CMDS;i++)
if (addbufftext(Meta_Commands[i].meta_str,bp) != TRUE)
return(FALSE);
gotobob(NULL,NULL);
prevwind(NULL,NULL);
}
/*
* TERMIO.C MODULE
*
*/
/*
* The functions in this file negotiate with the operating system for
* characters, and write characters in a barely buffered fashion on the display.
* All operating systems.
*/
#include <stdio.h>
#include "ed.h"
#if AMIGA
#define NEW 1006
#define LEN 1
static long terminal;
#endif
#if VMS
#include <stsdef.h>
#include <ssdef.h>
#include <descrip.h>
#include <iodef.h>
#include <ttdef.h>
#define NIBUF 128 /* Input buffer size */
#define NOBUF 1024 /* MM says bug buffers win! */
#define EFN 0 /* Event flag */
char obuf[NOBUF]; /* Output buffer */
int nobuf; /* # of bytes in above */
char ibuf[NIBUF]; /* Input buffer */
int nibuf; /* # of bytes in above */
int ibufi; /* Read index */
int oldmode[2]; /* Old TTY mode bits */
int newmode[2]; /* New TTY mode bits */
short iochan; /* TTY I/O channel */
#endif
#if CPM
#include <bdos.h>
#endif
#if MSDOS
#undef LATTICE
#include <dos.h>
#endif
#if RAINBOW
#include "rainbow.h"
#endif
#if V7
#include <sgtty.h> /* for stty/gtty functions */
struct sgttyb ostate; /* saved tty state */
struct sgttyb nstate; /* values for editor mode */
#endif
/*
* This function is called once to set up the terminal device streams.
* On VMS, it translates SYS$INPUT until it finds the terminal, then assigns
* a channel to it and sets it raw. On CPM it is a no-op.
*/
ttopen()
{
#if AMIGA
terminal = Open("RAW:1/1/639/199/MicroEmacs", NEW);
#endif
#if VMS
struct dsc$descriptor idsc;
struct dsc$descriptor odsc;
char oname[40];
int iosb[2];
int status;
odsc.dsc$a_pointer = "SYS$INPUT";
odsc.dsc$w_length = strlen(odsc.dsc$a_pointer);
odsc.dsc$b_dtype = DSC$K_DTYPE_T;
odsc.dsc$b_class = DSC$K_CLASS_S;
idsc.dsc$b_dtype = DSC$K_DTYPE_T;
idsc.dsc$b_class = DSC$K_CLASS_S;
do {
idsc.dsc$a_pointer = odsc.dsc$a_pointer;
idsc.dsc$w_length = odsc.dsc$w_length;
odsc.dsc$a_pointer = &oname[0];
odsc.dsc$w_length = sizeof(oname);
status = LIB$SYS_TRNLOG(&idsc, &odsc.dsc$w_length, &odsc);
if (status!=SS$_NORMAL && status!=SS$_NOTRAN)
exit(status);
if (oname[0] == 0x1B) {
odsc.dsc$a_pointer += 4;
odsc.dsc$w_length -= 4;
}
} while (status == SS$_NORMAL);
status = SYS$ASSIGN(&odsc, &iochan, 0, 0);
if (status != SS$_NORMAL)
exit(status);
status = SYS$QIOW(EFN, iochan, IO$_SENSEMODE, iosb, 0, 0,
oldmode, sizeof(oldmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
exit(status);
newmode[0] = oldmode[0];
newmode[1] = oldmode[1] | TT$M_PASSALL | TT$M_NOECHO;
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
newmode, sizeof(newmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
exit(status);
#endif
#if CPM
#endif
#if MSDOS
#endif
#if V7
gtty(1, &ostate); /* save old state */
gtty(1, &nstate); /* get base of new state */
nstate.sg_flags |= RAW;
nstate.sg_flags &= ~(ECHO|CRMOD); /* no echo for now... */
stty(1, &nstate); /* set mode */
#endif
}
/*
* This function gets called just before we go back home to the command
* interpreter. On VMS it puts the terminal back in a reasonable state.
* Another no-operation on CPM.
*/
ttclose()
{
#if AMIGA
Close(terminal);
#endif
#if VMS
int status;
int iosb[1];
ttflush();
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
oldmode, sizeof(oldmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
exit(status);
status = SYS$DASSGN(iochan);
if (status != SS$_NORMAL)
exit(status);
#endif
#if CPM
#endif
#if MSDOS
#endif
#if V7
stty(1, &ostate);
#endif
}
/*
* Write a character to the display. On VMS, terminal output is buffered, and
* we just put the characters in the big array, after checking for overflow.
* On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
* MS-DOS (use the very very raw console output routine).
*/
ttputc(c)
#if AMIGA
char c;
#endif
{
#if AMIGA
Write(terminal, &c, LEN);
#endif
#if VMS
if (nobuf >= NOBUF)
ttflush();
obuf[nobuf++] = c;
#endif
#if CPM
bios(BCONOUT, c, 0);
#endif
#if MSDOS & MWC86
bdos(CONDIO, c, 0);
#endif
#if RAINBOW
Put_Char(c); /* fast video */
#endif
#if V7
fputc(c, stdout);
#endif
}
/*
* Flush terminal buffer. Does real work where the terminal output is buffered
* up. A no-operation on systems where byte at a time terminal I/O is done.
*/
ttflush()
{
#if AMIGA
#endif
#if VMS
int status;
int iosb[2];
status = SS$_NORMAL;
if (nobuf != 0) {
status = SYS$QIOW(EFN, iochan, IO$_WRITELBLK|IO$M_NOFORMAT,
iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
if (status == SS$_NORMAL)
status = iosb[0] & 0xFFFF;
nobuf = 0;
}
return (status);
#endif
#if CPM
#endif
#if MSDOS
#endif
#if V7
fflush(stdout);
#endif
}
/*
* Read a character from the terminal, performing no editing and doing no echo
* at all. More complex in VMS that almost anyplace else, which figures. Very
* simple on CPM, because the system can do exactly what you want.
*/
ttgetc()
{
#if AMIGA
char ch;
Read(terminal, &ch, LEN);
return (int) ch;
#endif
#if VMS
int status;
int iosb[2];
int term[2];
while (ibufi >= nibuf) {
ibufi = 0;
term[0] = 0;
term[1] = 0;
status = SYS$QIOW(EFN, iochan, IO$_READLBLK|IO$M_TIMED,
iosb, 0, 0, ibuf, NIBUF, 0, term, 0, 0);
if (status != SS$_NORMAL)
exit(status);
status = iosb[0] & 0xFFFF;
if (status!=SS$_NORMAL && status!=SS$_TIMEOUT)
exit(status);
nibuf = (iosb[0]>>16) + (iosb[1]>>16);
if (nibuf == 0) {
status = SYS$QIOW(EFN, iochan, IO$_READLBLK,
iosb, 0, 0, ibuf, 1, 0, term, 0, 0);
if (status != SS$_NORMAL
|| (status = (iosb[0]&0xFFFF)) != SS$_NORMAL)
exit(status);
nibuf = (iosb[0]>>16) + (iosb[1]>>16);
}
}
return (ibuf[ibufi++] & 0xFF); /* Allow multinational */
#endif
#if CPM
return (biosb(BCONIN, 0, 0));
#endif
#if RAINBOW
int Ch;
while ((Ch = Read_Keyboard()) < 0);
if ((Ch & Function_Key) == 0)
if (!((Ch & 0xFF) == 015 || (Ch & 0xFF) == 0177))
Ch &= 0xFF;
return Ch;
#endif
#if MSDOS & MWC86
return (bdos(CONRAW, 0, 0)&0x07F);
#endif
#if V7
return(fgetc(stdin));
#endif
}
More information about the Comp.sources.unix
mailing list