Added emg editor from github.com/ibara/emg.

This commit is contained in:
Sergey
2014-05-24 19:37:02 -07:00
parent 66ffc30ccd
commit b3abe580bd
27 changed files with 5994 additions and 2 deletions

View File

@@ -11,8 +11,8 @@ CFLAGS += -Werror
# Programs that live in subdirectories, and have makefiles of their own.
# /bin
SUBDIR = adb adc-demo ar as awk basic cc chflags chpass \
cpp dc diff env fdisk find forth fstat glcdtest hostname \
id la lcc lcpp ld ls login make man med \
cpp dc diff emg env fdisk find forth fstat glcdtest \
hostname id la lcc lcpp ld ls login make man med \
more nm passwd picoc portio printf pwm \
rdprof ranlib re renice retroforth scm setty sl \
sed sh smallc smlrc stty sysctl test uname wiznet xargs \

1
src/cmd/emg/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
emg

41
src/cmd/emg/ChangeLog Normal file
View File

@@ -0,0 +1,41 @@
ChangeLog
=========
March 16, 2014 : emg 1.5
------------------------
Add line number to the mode line.
Implement prompted go to line function.
Remove lesser used expensive movement functions.
Documentation tweaks to reflect above changes.
March 9, 2014 : emg 1.4
-----------------------
Huge whitespace cleanup.
Make the window creation code mode consistent.
Small documentation fix.
March 8, 2014 : emg 1.3
-----------------------
Remove all OpenBSD support. emg is now for RetroBSD only.
Remove tmux alternative keybindings.
Revert Listbuffer command back to CTRL-x CTRL-b.
December 2, 2013 : emg 1.2
--------------------------
Alternate keybindings for RetroBSD users using flow control terminal emulators.
Alternate keybindings for tmux users who are using the default control command.
October 24, 2013 : emg 1.1
--------------------------
Listbuffer command is now CTRL-x l (originally CTRL-x CTRL-b).
This is because the default command keybinding of tmux is CTRL-b.
Search is now executed with <ENTER> instead of <ESC>.
<ESC> felt awkward, plus I don't search for newlines.
Lots of code cleanups (ttyio.c).
Removal of unused #if blocks.
Use panic() everywhere.
Fix all warnings from gcc -Wall.
October 19, 2013 : emg 1.0
--------------------------
Initial version of emg. Current targets are OpenBSD and RetroBSD.

49
src/cmd/emg/Makefile Normal file
View File

@@ -0,0 +1,49 @@
# emg Makefile
# for RetroBSD
TOPSRC = $(shell cd ../../..; pwd)
include $(TOPSRC)/target.mk
# Some basic CFLAGS.
CFLAGS = -Os -Wall -Werror
# With the extra LDFLAGS, save some bytes.
CFLAGS += -ffunction-sections -fdata-sections
# This reduces code size to 46K.
CFLAGS += -mips16
# Set the screen size.
# Will default to FORCE_COLS=80 and FORCE_ROWS=24
# if not set here.
CFLAGS += -DFORCE_COLS=80 -DFORCE_ROWS=24
# with CFLAGS+= -ffunction-sections -fdatasections
LDFLAGS += -Wl,--gc-sections
LIBS = -ltermcap -lc
MAN = emg.0
MANSRC = emg.1
OBJS = basic.o buffer.o display.o file.o fileio.o line.o main.o \
random.o region.o search.o tcap.o ttyio.o window.o word.o
all: emg ${MAN}
emg: ${OBJS}
${CC} ${LDFLAGS} -o emg.elf ${OBJS} ${LIBS}
${OBJDUMP} -S emg.elf > emg.dis
${SIZE} emg.elf
${ELF2AOUT} emg.elf $@ && rm emg.elf
${MAN}: ${MANSRC}
${MANROFF} ${MANSRC} > ${MAN}
install: all
install emg ${DESTDIR}/bin/emg
cp ${MAN} ${DESTDIR}/share/man/cat1/
cp -p emg.keys ${DESTDIR}/share/emg.keys
clean:
rm -f *.o *~ *.core *.bak *.dis emg ${MAN}

44
src/cmd/emg/README Normal file
View File

@@ -0,0 +1,44 @@
emg
===
emg, or Ersatz Mg, is a very tiny Emacs-like text editor created by
combining elements of Ersatz Emacs and Mg (both the mg1a release and the
current OpenBSD-maintained version).
The goal of this editor is to have something Emacs like for RetroBSD
(a release of 2.11BSD for PIC32 microcontrollers). After noticing that the
vi clone RetroBSD is using, VIrus, is GPL-licensed, I decided to provide
a better-licensed editor. I also decided that, as a vi user myself, it would
be easier to create an Emacs clone. Like you, I'm also unsure as to how that
conclusion was reached.
I had initially tried to port Mg to RetroBSD but it was simply too large.
Ersatz Emacs does not build on RetroBSD, as RetroBSD is missing some functions
that Ersatz Emacs requires. It made sense to try to take from each and create
an editor that would work.
In a way, emg has a double meaning: not only is it a combination of
the two programs that comprise it, it is also a substitute Mg after my initial
port failed.
I have cleaned up some code where necessary; emg builds without errors on
RetroBSD.
Patches are also very welcome. I ask that you keep in mind the resource
constraints of RetroBSD: everything must fit in 96K RAM. But of course,
smaller is better.
I've left Chris Baird's Ersatz Emacs README here so others can better
appreciate the history of this software.
As both Ersatz Emacs and Mg are Public Domain, emg is also Public Domain.
Versions of emg up to and including 1.2 also supported OpenBSD; OpenBSD
has since dropped the older headers, such as sgtty.h and it is not worth
reimplementing these for OpenBSD, since OpenBSD maintains Mg.
Tarballs can be found here:
http://devio.us/~bcallah/emg/
Github repo, for patches:
https://github.com/ibara/emg

39
src/cmd/emg/README-ERSATZ Normal file
View File

@@ -0,0 +1,39 @@
This shar file contains the source to a microemacs-derived text editor
that I have been personally hacking on for over a decade.
Originally this was MicroEMACS 3.6 as released to mod.sources and the
Public Domain by Daniel Lawrence in 1986, and was itself based on the
work of Steve Wilhite and George Jones to MicroEMACS 2.0 (then also
public domain) by Dave Conroy. I would like to reiterate Lawrence's
thanks to them for writing such nice, well structured and documented
code.
"Ersatz-Emacs", as I call it today, is the above text editor throughly
cleansed of routines and features that I personally never use. It is
also an editor MINIX-creator Andy Tanenbaum could describe as "fitting
inside a student's brain" (namely, mine).
This source code should compile cleanly on any "modern" UN*X system
with a termcap/curses library. This release has been tested with
NetBSD and various Linux systems, although in the past when it was
still mostly MicroEMACS, proto-Ersatz-Emacs was an editor of choice on
SunOS, Solaris, Xenix, Minix/i386, and AIX. Supporting these and
similar systems should not be difficult.
I encourage people to personalise this very simple editor to their own
requirements. Please send any useful bug reports and fixes back to me,
but I'm not really interested in incorporating new features unless it
simplifies the program further. Feel free to do a code-fork and
distribute your own perfect text editor.
The title "Ersatz" comes from the category Richard Stallman uses in
MIT AI Memo 519a to describe those editors that are a surface-deep
imitation (key bindings) of "real" ITS Emacs. If you are familiar with
any Emacs-variant editor, you should have few problems with Ersatz.
All source code of this program is in the Public Domain. I am a rabid
Stallmanite weenie, but it would be improper to publish this under a
different licence than it was given to me with.
--
Chris Baird,, <cjb@brushtail.apana.org.au>

266
src/cmd/emg/basic.c Normal file
View File

@@ -0,0 +1,266 @@
/* This file is in the public domain. */
/*
* 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 <stdlib.h> /* atoi(3), ugh */
#include "estruct.h"
#include "edef.h"
extern int getccol(int bflg);
extern int inword();
extern void mlwrite();
extern int mlreplyt();
int forwchar(int f, int n);
int backchar(int f, int n);
int forwline(int f, int n);
int backline(int f, int n);
/*
* 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".
*/
long getgoal(LINE *dlp)
{
int col = 0;
int dbo = 0;
int newcol, c;
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);
}
/*
* Move the cursor to the
* beginning of the current line.
* Trivial.
*/
/* ARGSUSED0 */
int gotobol(int f, int n)
{
curwp->w_doto = 0;
return (TRUE);
}
/*
* Move the cursor to the end of the current line. Trivial. No errors.
*/
/* ARGSUSED0 */
int gotoeol(int f, int n)
{
curwp->w_doto = llength(curwp->w_dotp);
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.
*/
int backchar(int f, int n)
{
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;
curwp->w_dotline--;
}
else
curwp->w_doto--;
}
return (TRUE);
}
/*
* Move the cursor forwards 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.
*/
int forwchar(int f, 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;
curwp->w_dotline++;
}
else
curwp->w_doto++;
}
return (TRUE);
}
/*
* move to a particular line. argument (n) must be a positive integer for this
* to actually do anything
*/
int gotoline(int f, int n)
{
if ((n < 1) || (n > curwp->w_bufp->b_lines)) /* if a bogus argument...then leave */
return (FALSE); /* but we should never get here */
/* first, we go to the start of the buffer */
curwp->w_dotp = lforw(curbp->b_linep);
curwp->w_doto = 0;
curwp->w_dotline = 0; /* and reset the line number */
return (forwline(f, n - 1));
}
/*
* Prompt for which line number we want to go to, then execute gotoline()
* with that number as its argument.
*
* Make sure the bounds are within the file.
*
* Bound to M-G
*/
int setline(int f, int n)
{
char setl[6];
int l;
(void)mlreplyt("Go to line: ", setl, 6, 10);
l = atoi(setl); /* XXX: This sucks! */
if (l < 1)
l = 1;
else if (l > curwp->w_bufp->b_lines)
l = curwp->w_bufp->b_lines;
gotoline(f, l);
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-<".
*/
/* ARGSUSED0 */
int gotobob(int f, int n)
{
curwp->w_dotp = lforw(curbp->b_linep);
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
curwp->w_dotline = 0;
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->".
*/
/* ARGSUSED0 */
int gotoeob(int f, int n)
{
curwp->w_dotp = curbp->b_linep;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
curwp->w_dotline = curwp->w_bufp->b_lines;
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.
*/
int forwline(int f, int n)
{
LINE *dlp;
if (n < 0)
return (backline(f, -n));
if ((lastflag & CFCPCN) == 0)/* Reset goal if last */
curgoal = getccol(FALSE); /* not C-P or C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && dlp != curbp->b_linep)
{
dlp = lforw(dlp);
curwp->w_dotline++;
}
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
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".
*/
int backline(int f, int n)
{
LINE *dlp;
if (n < 0)
return (forwline(f, -n));
if ((lastflag & CFCPCN) == 0)/* Reset goal if the */
curgoal = getccol(FALSE); /* 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_dotline--;
}
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* Set the mark in the current window to the value of "." in the window. No
* errors are possible. Bound to "M-.".
*/
/* ARGSUSED0 */
int setmark(int f, int n)
{
curwp->w_markp = curwp->w_dotp;
curwp->w_marko = curwp->w_doto;
mlwrite("[Mark set]");
return (TRUE);
}

494
src/cmd/emg/buffer.c Normal file
View File

@@ -0,0 +1,494 @@
/* This file is in the public domain. */
/*
* 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 <stdlib.h> /* free(3), malloc(3) */
#include <string.h> /* strncpy(3) */
#include "estruct.h"
#include "edef.h"
extern int mlreply(char *prompt, char *buf, int nbuf);
extern int readin(char fname[]);
extern void mlwrite();
extern void mlerase();
extern int mlyesno(char *prompt);
extern void lfree(LINE *lp);
extern WINDOW *wpopup();
extern LINE *lalloc();
int swbuffer(BUFFER *bp);
int usebuffer(int f, int n);
int nextbuffer(int f, int n);
int killbuffer(int f, int n);
int zotbuf(BUFFER *bp);
int namebuffer(int f, int n);
int listbuffers(int f, int n);
int makelist();
void itoa(char buf[], int width, int num);
int addline(char *text);
int anycb();
BUFFER* bfind(char *bname, int cflag, int bflag);
int bclear(BUFFER *bp);
/*
* make buffer BP current
*/
int swbuffer(BUFFER *bp)
{
WINDOW *wp;
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. */
if (curbp->b_active != TRUE)
{ /* buffer not active yet */
/* read it in and activate it */
readin(curbp->b_fname);
curbp->b_dotp = lforw(curbp->b_linep);
curbp->b_doto = 0;
curbp->b_active = TRUE;
}
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 != 0)
{
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);
}
/*
* 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.
*/
/* ARGSUSED0 */
int usebuffer(int f, int n)
{
BUFFER *bp;
char bufn[NBUFN];
int s;
if ((s = mlreply("Use buffer: ", bufn, NBUFN)) != TRUE)
return (s);
if ((bp = bfind(bufn, TRUE, 0)) == NULL)
return (FALSE);
return (swbuffer(bp));
}
/* switch to the next buffer in the buffer list
*/
/* ARGSUSED0 */
int nextbuffer(int f, int n)
{
BUFFER *bp;
bp = curbp->b_bufp;
/* cycle through the buffers to find an eligable one */
while ((bp == NULL) || (bp->b_flag & BFTEMP))
{
if (bp == NULL)
bp = bheadp;
else
bp = bp->b_bufp;
}
return (swbuffer(bp));
}
/*
* 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".
*/
/* ARGSUSED0 */
int killbuffer(int f, int n)
{
BUFFER *bp;
char bufn[NBUFN];
int s;
if ((s = mlreply("Kill buffer: ", bufn, NBUFN)) != TRUE)
return (s);
if ((bp = bfind(bufn, FALSE, 0)) == NULL) /* Easy if unknown */
return (TRUE);
return (zotbuf(bp));
}
/* kill the buffer pointed to by bp */
int zotbuf(BUFFER *bp)
{
BUFFER *bp1, *bp2;
int s;
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 (bp->b_linep); /* Release header line */
bp1 = 0; /* 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(bp); /* Release buffer block */
return (TRUE);
}
/* Rename the current buffer */
/* ARGSUSED0 */
int namebuffer(int f, int n)
{
BUFFER *bp; /* pointer to scan through all buffers */
char bufn[NBUFN]; /* buffer to hold buffer name */
/* prompt for and get the new buffer name */
ask:
if (mlreply("Change buffer name to: ", bufn, NBUFN) != TRUE)
return (FALSE);
/* and check for duplicates */
bp = bheadp;
while (bp != 0)
{
if (bp != curbp)
{
/* if the names the same */
if (strcmp(bufn, bp->b_bname) == 0)
goto ask; /* try again */
}
bp = bp->b_bufp; /* onward */
}
strncpy(curbp->b_bname, bufn, NBUFN); /* copy buffer name to structure */
curwp->w_flag |= WFMODE; /* make mode line replot */
mlerase();
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".
*/
/* ARGSUSED0 */
int listbuffers(int f, int n)
{
WINDOW *wp;
BUFFER *bp;
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 != 0)
{
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 = 0;
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).
*/
int makelist()
{
BUFFER *bp;
LINE *lp;
char *cp1, *cp2;
char b[7], line[128];
int nbytes, s, c;
blistp->b_flag &= ~BFCHG; /* Don't complain! */
if ((s = bclear(blistp)) != TRUE) /* Blow old text away */
return (s);
strncpy (blistp->b_fname, "", 1);
if (addline("AC Size Buffer File") == FALSE ||
addline("-- ------- ------ ----") == FALSE)
return (FALSE);
bp = bheadp;
/* build line to report global mode settings */
cp1 = &line[0];
*cp1++ = ' ';
*cp1++ = ' ';
*cp1++ = ' ';
/* output the list of buffers */
while (bp != 0)
{
if ((bp->b_flag & BFTEMP) != 0)
{ /* Skip magic ones */
bp = bp->b_bufp;
continue;
}
cp1 = &line[0]; /* Start at left edge */
/* output status of ACTIVE flag (has the file been read in? */
if (bp->b_active == TRUE) /* "@" if activated */
*cp1++ = '@';
else
*cp1++ = ' ';
/* output status of changed flag */
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++ = (char)c;
*cp1++ = ' '; /* Gap */
cp2 = &bp->b_bname[0]; /* Buffer name */
while ((c = *cp2++) != 0)
*cp1++ = (char)c;
cp2 = &bp->b_fname[0]; /* File name */
if (*cp2 != 0)
{
while (cp1 < &line[2 + 1 + 5 + 1 + 6 + 1 + NBUFN]) /* XXX ??? */
*cp1++ = ' ';
while ((c = *cp2++) != 0)
{
if (cp1 < &line[128 - 1])
*cp1++ = (char)c;
}
}
*cp1 = 0; /* Add to the buffer */
if (addline(line) == FALSE)
return (FALSE);
bp = bp->b_bufp;
}
return (TRUE); /* All done */
}
void itoa(char buf[], int width, int num)
{
buf[width] = 0; /* End of string */
while (num >= 10)
{ /* Conditional digits */
buf[--width] = (char)((num % 10) + '0');
num /= 10;
}
buf[--width] = (char)(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.
*/
int addline(char *text)
{
LINE *lp;
int ntext, i;
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.
*/
int anycb()
{
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(char *bname, int cflag, int bflag)
{
BUFFER *bp, *sb;
LINE *lp;
bp = bheadp;
while (bp != 0)
{
if (strcmp(bname, bp->b_bname) == 0)
{
if ((bp->b_flag & BFTEMP) != 0)
{
mlwrite ("Cannot select builtin buffer");
return (0);
}
return (bp);
}
bp = bp->b_bufp;
}
if (cflag != FALSE)
{
if ((bp = (BUFFER *) malloc(sizeof(BUFFER))) == NULL)
return (0);
if ((lp = lalloc(0)) == NULL)
{
free(bp);
return (BUFFER*)0;
}
/* find the place in the list to insert this buffer */
if (bheadp == NULL || strcmp(bheadp->b_bname, bname) > 0)
{
/* insert at the begining */
bp->b_bufp = bheadp;
bheadp = bp;
}
else
{
sb = bheadp;
while (sb->b_bufp != 0)
{
if (strcmp(sb->b_bufp->b_bname, bname) > 0)
break;
sb = sb->b_bufp;
}
/* and insert it */
bp->b_bufp = sb->b_bufp;
sb->b_bufp = bp;
}
/* and set up the other buffer fields */
bp->b_active = TRUE;
bp->b_dotp = lp;
bp->b_doto = 0;
bp->b_markp = 0;
bp->b_marko = 0;
bp->b_flag = (char)bflag;
bp->b_nwnd = 0;
bp->b_linep = lp;
bp->b_lines = 1;
strncpy(bp->b_fname, "", 1);
strncpy(bp->b_bname, bname, NBUFN);
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.
*/
int bclear(BUFFER *bp)
{
LINE *lp;
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 = 0; /* Invalidate "mark" */
bp->b_marko = 0;
bp->b_lines = 1;
return (TRUE);
}

1015
src/cmd/emg/display.c Normal file

File diff suppressed because it is too large Load Diff

75
src/cmd/emg/ebind.h Normal file
View File

@@ -0,0 +1,75 @@
/* This file is in the public domain. */
/*
* 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 | 'D', forwdel},
{CTRL | 'E', gotoeol},
{CTRL | 'F', forwchar},
{CTRL | 'G', ctrlg},
{CTRL | 'H', backdel},
{CTRL | 'I', tab},
{CTRL | 'K', killtext},
{CTRL | 'L', refresh},
{CTRL | 'M', newline},
{CTRL | 'N', forwline},
{CTRL | 'O', openline},
{CTRL | 'P', backline},
{CTRL | 'Q', quote},
{CTRL | 'R', backsearch},
{CTRL | 'S', forwsearch},
{CTRL | 'T', twiddle},
{CTRL | 'W', killregion},
{CTRL | 'Y', yank},
{CTLX | '(', ctlxlp},
{CTLX | ')', ctlxrp},
{CTLX | '1', onlywind},
{CTLX | '2', splitwind},
{CTLX | 'B', usebuffer},
{CTLX | 'E', ctlxe},
{CTLX | 'F', setfillcol},
{CTLX | 'K', killbuffer},
{CTLX | 'N', filename},
{CTLX | 'O', nextwind},
{CTLX | 'S', filesave}, /* non-standard */
{CTLX | 'Q', quote}, /* non-standard */
{CTLX | 'X', nextbuffer},
{CTLX | '^', enlargewind},
{CTLX | CTRL | 'B', listbuffers},
{CTLX | CTRL | 'C', quit},
{CTLX | CTRL | 'F', filefind},
{CTLX | CTRL | 'I', insfile},
{CTLX | CTRL | 'R', fileread},
{CTLX | CTRL | 'S', filesave},
{CTLX | CTRL | 'W', filewrite},
{META | ' ', setmark},
{META | '%', qreplace},
{META | '.', setmark},
{META | '<', gotobob},
{META | '>', gotoeob},
{META | 'B', backword},
{META | 'C', capword},
{META | 'D', delfword},
{META | 'F', forwword},
{META | 'G', setline}, /* non-standard */
{META | 'L', lowerword},
{META | 'R', sreplace},
{META | 'S', forwsearch}, /* non-standard */
{META | 'U', upperword},
{META | 'W', copyregion},
{META | 'Z', quickexit},
{META | 0x7F, delbword},
{META | CTRL | 'H', delbword},
{META | CTRL | 'N', namebuffer},
{0x7F, backdel},
{META | '[', extendedcmd},
{META | 'O', extendedcmd},
{0, 0}
};

78
src/cmd/emg/edef.h Normal file
View File

@@ -0,0 +1,78 @@
/* This file is in the public domain. */
#ifndef NULL
#define NULL ((void*)0)
#endif
#ifdef maindef
/*
* for MAIN.C
* initialized global definitions
*/
short kbdm[NKBDM] = {CTLX | ')'}; /* Macro */
int fillcol = 72; /* Current fill column */
char pat[NPAT]; /* Search pattern */
char rpat[NPAT]; /* replacement pattern */
int revexist = FALSE;
int eolexist = TRUE; /* does clear to EOL exist */
int sgarbf = TRUE; /* TRUE if screen is garbage */
int mpresf = FALSE; /* TRUE if message in last line */
/* uninitialized global definitions */
int currow; /* Cursor row */
int curcol; /* Cursor column */
int thisflag; /* Flags, this command */
int lastflag; /* Flags, last command */
int curgoal; /* Goal for C-P, C-N */
WINDOW *curwp; /* Current window */
BUFFER *curbp; /* Current buffer */
WINDOW *wheadp; /* Head of list of windows */
BUFFER *bheadp; /* Head of list of buffers */
BUFFER *blistp; /* Buffer for C-X C-B */
short *kbdmip; /* Input pointer for above */
short *kbdmop; /* Output pointer for above */
#else
/*
* for all the other .C files
* initialized global external declarations
*/
extern int fillcol; /* Fill column */
extern short kbdm[]; /* Holds kayboard macro data */
extern char pat[]; /* Search pattern */
extern char rpat[]; /* Replacement pattern */
extern int eolexist; /* does clear to EOL exist? */
extern int revexist; /* does reverse video exist? */
extern char *modename[]; /* text names of modes */
extern char modecode[]; /* letters to represent modes */
extern KEYTAB keytab[]; /* key bind to functions table */
extern int gmode; /* global editor mode */
extern int sgarbf; /* State of screen unknown */
extern int mpresf; /* Stuff in message line */
extern int clexec; /* command line execution flag */
/* initialized global external declarations */
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 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 *kbdmip; /* Input pointer for above */
extern short *kbdmop; /* Output pointer for above */
#endif
/* terminal table defined only in TERM.C */
#ifndef termdef
extern TERM term; /* Terminal information */
#endif

70
src/cmd/emg/efunc.h Normal file
View File

@@ -0,0 +1,70 @@
/* This file is in the public domain. */
/* EFUNC.H: function declarations and names
*
* This file list all the C code functions used. To add functions, declare it
* here in both the extern function list and the name binding table
*/
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 filefind(); /* 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 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 forwsearch(); /* Search forward */
extern int backsearch(); /* Search backwards */
extern int sreplace(); /* search and replace */
extern int qreplace(); /* search and replace w/query */
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 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 refresh(); /* Refresh the screen */
extern int twiddle(); /* Twiddle characters */
extern int tab(); /* Insert tab */
extern int newline(); /* Insert CR-LF */
extern int openline(); /* Open up a blank line */
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 killtext(); /* Kill forward */
extern int yank(); /* Yank back from killbuffer */
extern int upperword(); /* Upper case word */
extern int lowerword(); /* Lower case word */
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 quickexit(); /* low keystroke style exit */
extern int setline(); /* go to a numbered line */
extern int namebuffer(); /* rename the current buffer */
extern int deskey(); /* describe a key's binding */
extern int insfile(); /* insert a file */
extern int nextbuffer(); /* switch to the next buffer */
extern int forwhunt(); /* hunt forward for next match */
extern int backhunt(); /* hunt backwards for next match */
extern int extendedcmd(); /* parse ANSI/VT100 extended keys */

58
src/cmd/emg/emg.1 Normal file
View File

@@ -0,0 +1,58 @@
.\" This file is in the public domain.
.\"
.\" Basic emg man page.
.\" As both Ersatz Emacs and Mg are Public Domain, emg is also Public Domain.
.\"
.Dd March 23, 2014
.Os
.Dt EMG 1
.Sh NAME
.Nm emg
.Nd very small Emacs-like text editor
.Sh SYNOPSIS
.Nm emg
.Op Ar
.Sh DESCRIPTION
.Nm ,
or Ersatz Mg, is an Emacs-like text editor designed for memory-constrained
environments.
.Nm
was originally created to fit into an operating environment of 96K of RAM, and
one in which Mg did not fit and Ersatz Emacs did not build.
By combining parts of each, a working editor was created.
.Pp
When invoked without file arguments,
.Nm
creates a
.Qq main
buffer.
This buffer must be renamed
.Ic ( C-x n )
in order to be saved
.Ic ( C-x C-s ) .
.Pp
As both Ersatz Emacs and Mg are Public Domain,
.Nm
is also Public Domain.
.Sh FILES
There is a chart of key bindings in
.Pa /usr/local/share/doc/emg/emg.keys .
Consulting this file is a must, as
.Nm
does not guarantee keybindings to be identical to other Emacs implementations.
.Sh AUTHORS
.Nm
is a combination of Ersatz Emacs and Mg and therefore all authors for both
deserve credit.
Ersatz Emacs and Mg were combined by
.An Brian Callahan Aq Mt bcallah@openbsd.org
to create
.Nm .
.Sh BUGS
None known.
However, patches are appreciated if any are found.
.Sh TODO
It would be nice to have automatic window resizing.
It would also be nice if
.Nm
could clear the screen when quit, like Mg does.

147
src/cmd/emg/emg.keys Normal file
View File

@@ -0,0 +1,147 @@
emg keybindings (March 16, 2014)
Based on Ersatz Emacs (2000/09/14)
M- means to use the <ESC> key prior to using another key
^A means to use the control key at the same time as the 'A' key
------------------------------------------------------------------------------
MOVING THE CURSOR
^F Forward character M-F Forward word
^B Backward character M-B Backward word
^N Next line M-P Front of paragraph
^P Previous line M-N End of paragraph
^A Front of line M-< or [HOME] Start of file
^E End of line M-> or [END] End of file
M-G Go to line Arrow keys are active
------------------------------------------------------------------------------
DELETING & INSERTING
<- Delete previous character M-<- Delete previous word
^D Delete next character M-D Delete next word
^K Delete to end of line ^O Insert line
------------------------------------------------------------------------------
FORMATTING & TRANSPOSING
M-U UPPERCASE word M-C Capitalize word
M-L lowercase word ^T Transpose characters
^Q Quote next key, so that control codes may be entered into text. (or ^X Q)
M-Q Format paragraph so that text is left-justified between margins.
^X F Set the right margin for paragraph formatting to the current position of
the cursor.
------------------------------------------------------------------------------
SEARCHING
^S Search forward from cursor position. Type in a string and end it with
ENTER. Either case matches. (or M-S)
^R As above, but reverse search from cursor position.
------------------------------------------------------------------------------
REPLACING
M-R Replace all instances of first typed-in string with second typed-in
string.
M-% Replace with query. Answer with:
Y replace & continue N no replacement & continue
! replace the rest ? Get a list of options
. exit and return to entry point
^G,'q' or <return> exit and remain at current location
------------------------------------------------------------------------------
COPYING AND MOVING
^@ or M-<spacebar> Set mark at current position.
^W Delete region.
M-W Copy region to kill buffer.
^Y Yank back kill buffer at cursor.
A region is defined as the area between this mark and the current cursor
position. The kill buffer is the text which has been most recently deleted or
copied.
Generally, the procedure for copying or moving text is:
1) Mark out region using M-<spacebar> at the beginning and move the cursor to
the end.
2) Delete it (with ^W) or copy it (with M-W) into the kill buffer.
3) Move the cursor to the desired location and yank it back (with ^Y).
------------------------------------------------------------------------------
MULTIPLE BUFFERS
A buffer contains a COPY of a document being edited, and must be saved for
changes to be kept. Many buffers may be activated at once.
^X B Switch to another buffer.
^X ^B Show buffer directory in a window (^X 1 to remove).
^X K Delete a non-displayed buffer.
^X X Switch to next buffer in buffer list.
^X N Change the filename associated with the buffer.
M-^N Change the name of the buffer.
------------------------------------------------------------------------------
READING FROM DISK
^X^F Find file; read into a new buffer created from filename.
(This is the usual way to edit a new file.)
^X^R Read file into current buffer, erasing its previous contents.
No new buffer will be created.
^X^I Insert file into current buffer at cursor's location.
------------------------------------------------------------------------------
SAVING TO DISK
^X^S Save current buffer to disk, using the buffer's filename as the name of
the disk file. Any disk file of that name will be overwritten. (or ^X S)
^X^W Write current buffer to disk. Type in a new filename at the prompt to
write to; it will also become the current buffer's filename.
------------------------------------------------------------------------------
MULTIPLE WINDOWS
Many windows may be visible at once on the screen. Windows may show different
parts of the same buffer, or each may display a different one.
^X 2 Split the current window in two ^X 1 Show only current window
^X O Move cursor to next window ^X ^ Enlarge current window
M-^V Scroll other window down M-^Z Scroll other window up
------------------------------------------------------------------------------
EXITING
^X^C Exit. Any unsaved files will require confirmation.
M-Z Write out all changed buffers automatically and exit.
------------------------------------------------------------------------------
MACROS
^X ( Start recording a keyboard macro. Typing ^G or an error aborts.
^X ) Stop recording macro.
^X E Execute macro.
------------------------------------------------------------------------------
REPEAT & NUMBER PREFIX
^U<number> or M-<number>
Number prefix and universal repeat. May be followed by an integer
(default = 4) and repeats the next command that many times.
Exceptions follow.
^U<number>^L
Reposition the cursor to a particular screen row; i.e., ^U0^L moves the
cursor and the line it is on to the top of the screen. Negative numbers
are from the bottom of the screen.
^U<number>^X F
Set the right margin to column <number> for paragraph formatting.
^U<number>^X^
Enlarge a split window by <number> rows. A negative number shrinks the
window.
------------------------------------------------------------------------------
SPECIAL KEYS
^G Cancel current command.
^L Redraws the screen completely.
------------------------------------------------------------------------------

162
src/cmd/emg/estruct.h Normal file
View File

@@ -0,0 +1,162 @@
/* This file is in the public domain. */
/* ESTRUCT: Structure and preprocessor */
/* internal constants */
#define NFILEN 80 /* maximum # of bytes, file name */
#define NBUFN 16 /* maximum # of bytes, buffer name */
#define NLINE 512 /* maximum # of bytes, line */
#define NKBDM 256 /* maximum # of strokes, keyboard macro */
#define NPAT 80 /* maximum # of bytes, pattern */
#define HUGE 32700 /* Huge number for "impossible" row&col */
#define METACH 0x1B /* M- prefix, Control-[, ESC */
#define BELL 0x07 /* a bell character */
#define TAB 0x09 /* a tab character */
#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 FIOLNG 4 /* line longer than allowed len */
#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 "." */
long w_doto; /* Byte offset for "." */
struct LINE *w_markp; /* Line containing "mark" */
long 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 */
int w_dotline; /* current line number of dot */
} 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". Buffers may be "Inactive" which means the
* files accosiated with them have not been read in yet. These get read in at
* "use buffer" time
*/
typedef struct BUFFER
{
struct BUFFER *b_bufp; /* Link to next BUFFER */
struct LINE *b_dotp; /* Link to "." LINE structure */
long b_doto; /* Offset of "." in above LINE */
struct LINE *b_markp; /* The same as the above two, */
long b_marko; /* but for the "mark" */
struct LINE *b_linep; /* Link to the header LINE */
char b_active; /* window activated flag */
char b_nwnd; /* Count of windows on buffer */
char b_flag; /* Flags */
char b_fname[NFILEN]; /* File name */
char b_bname[NBUFN]; /* Buffer name */
int b_lines; /* Number of lines in file */
} 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 */
long r_offset; /* Origin LINE offset */
long 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 */
int l_size; /* Allocated size */
int 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
{
int t_nrow; /* Number of rows */
int t_ncol; /* Number of columns */
int t_margin; /* min margin for extended lines */
int t_scrsiz; /* size of scroll region " */
void (*t_open) (); /* Open terminal at the start */
void (*t_close) (); /* Close terminal at end */
int (*t_getchar) (); /* Get character from keyboard */
void (*t_putchar) (); /* Put character to display */
void (*t_flush) (); /* Flush output buffers */
void (*t_move) (); /* Move the cursor, origin 0 */
void (*t_eeol) (); /* Erase to end of line */
void (*t_eeop) (); /* Erase to end of page */
void (*t_beep) (); /* Beep */
void (*t_rev) (); /* set reverse video state */
} TERM;
/* structure for the table of initial key bindings */
typedef struct
{
short k_code; /* Key code */
int (*k_fp) (); /* Routine to handle it */
} KEYTAB;

465
src/cmd/emg/file.c Normal file
View File

@@ -0,0 +1,465 @@
/* This file is in the public domain. */
/*
* The routines in this file handle the reading and writing of disk files.
* All details about the reading and writing of the disk are in "fileio.c"
*/
#include <string.h> /* strncpy(3) */
#include "estruct.h"
#include "edef.h"
extern int mlreply(char *prompt, char *buf, int nbuf);
extern int swbuffer(BUFFER *bp);
extern void mlwrite();
extern int bclear(BUFFER *bp);
extern int ffropen(char *fn);
extern int ffgetline(char buf[], int nbuf);
extern int ffwopen(char *fn);
extern int ffclose();
extern int ffputline(char buf[], int nbuf);
extern BUFFER *bfind();
extern LINE *lalloc();
int fileread(int f, int n);
int insfile(int f, int n);
int filefind(int f, int n);
int getfile(char fname[]);
int readin(char fname[]);
void makename(char bname[], char fname[]);
int filewrite(int f, int n);
int filesave(int f, int n);
int writeout(char *fn);
int filename(int f, int n);
int ifile(char fname[]);
/*
* 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"
*/
int fileread(int f, int n)
{
int s;
char fname[NFILEN];
if ((s = mlreply("Read file: ", fname, NFILEN)) != TRUE)
return (s);
return (readin(fname));
}
/*
* Insert a file into the current buffer. This is really easy; all you do it
* find the name of the file, and call the standard "insert a file into the
* current buffer" code. Bound to "C-X C-I".
*/
int insfile(int f, int n)
{
int s;
char fname[NFILEN];
if ((s = mlreply("Insert file: ", fname, NFILEN)) != TRUE)
return (s);
return (ifile(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-F.
*/
int filefind(int f, int n)
{
char fname[NFILEN]; /* file user wishes to find */
int s; /* status return */
if ((s = mlreply("Find file: ", fname, NFILEN)) != TRUE)
return (s);
return (getfile(fname));
}
int getfile(char fname[])
{
BUFFER *bp;
LINE *lp;
char bname[NBUFN]; /* buffer name to put file */
int i, s;
for (bp = bheadp; bp != (BUFFER*)0; 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;
}
swbuffer(bp);
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)) != (BUFFER*)0)
{
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 == (BUFFER*)0 && (bp = bfind(bname, TRUE, 0)) == (BUFFER*)0)
{
mlwrite("Cannot create buffer");
return (FALSE);
}
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 find 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.
*/
int readin(char fname[])
{
LINE *lp1, *lp2;
WINDOW *wp;
BUFFER *bp;
char line[NLINE];
int nbytes, s, i;
int nline = 0; /* initialize here to silence a gcc warning */
int lflag; /* any lines longer than allowed? */
bp = curbp; /* Cheap */
if ((s = bclear(bp)) != TRUE) /* Might be old */
return (s);
bp->b_flag &= ~(BFTEMP | BFCHG);
strncpy(bp->b_fname, fname, NFILEN);
if ((s = ffropen(fname)) == FIOERR) /* Hard file open */
goto out;
if (s == FIOFNF)
{ /* File not found */
mlwrite("[New file]");
goto out;
}
mlwrite("[Reading file]");
lflag = FALSE;
while ((s = ffgetline(line, NLINE)) == FIOSUC || s == FIOLNG)
{
if (s == FIOLNG)
lflag = TRUE;
nbytes = strlen(line);
if ((lp1 = lalloc(nbytes)) == NULL)
{
s = FIOERR; /* Keep message on the display */
break;
}
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 %d lines]", nline);
else
mlwrite("[Read 1 line]");
}
if (lflag)
{
if (nline != 1)
mlwrite("[Read %d lines: Long lines wrapped]", nline);
else
mlwrite("[Read 1 line: Long lines wrapped]");
}
curwp->w_bufp->b_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 || s == FIOFNF) /* 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.
*/
void makename(char bname[], char fname[])
{
char *cp1, *cp2;
cp1 = &fname[0];
while (*cp1 != 0)
++cp1;
while (cp1 != &fname[0] && cp1[-1] != '/')
--cp1;
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".
*/
int filewrite(int f, int n)
{
WINDOW *wp;
char fname[NFILEN];
int s;
if ((s = mlreply("Write file: ", fname, NFILEN)) != TRUE)
return (s);
if ((s = writeout(fname)) == TRUE)
{
strncpy(curbp->b_fname, fname, NFILEN);
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"
*/
int filesave(int f, int n)
{
WINDOW *wp;
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.
*/
int writeout(char *fn)
{
LINE *lp;
int nline, s;
if ((s = ffwopen(fn)) != FIOSUC) /* Open writes message */
return (FALSE);
mlwrite("[Writing]"); /* tell us were writing */
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 %d lines]", nline);
else
mlwrite("[Wrote 1 line]");
}
}
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.
*/
int filename(int f, int n)
{
WINDOW *wp;
char fname[NFILEN];
int s;
if ((s = mlreply("Name: ", fname, NFILEN)) == ABORT)
return (s);
if (s == FALSE)
strncpy(curbp->b_fname, "", 1);
else
strncpy(curbp->b_fname, fname, NFILEN);
wp = wheadp; /* update mode lines */
while (wp != NULL)
{
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* Insert file "fname" into the current buffer, Called by insert file command.
* Return the final status of the read.
*/
int ifile(char fname[])
{
LINE *lp0, *lp1, *lp2;
BUFFER *bp;
char line[NLINE];
int i, s, nbytes;
int nline = 0;
int lflag; /* any lines longer than allowed? */
bp = curbp; /* Cheap */
bp->b_flag |= BFCHG; /* we have changed */
bp->b_flag &= ~BFTEMP; /* and are not temporary */
if ((s = ffropen(fname)) == FIOERR) /* Hard file open */
goto out;
if (s == FIOFNF)
{ /* File not found */
mlwrite("[No such file]");
return (FALSE);
}
mlwrite("[Inserting file]");
/* back up a line and save the mark here */
curwp->w_dotp = lback(curwp->w_dotp);
curwp->w_doto = 0;
curwp->w_markp = curwp->w_dotp;
curwp->w_marko = 0;
lflag = FALSE;
while ((s = ffgetline(line, NLINE)) == FIOSUC || s == FIOLNG)
{
if (s == FIOLNG)
lflag = TRUE;
nbytes = strlen(line);
if ((lp1 = lalloc(nbytes)) == NULL)
{
s = FIOERR; /* keep message on the */
break; /* display */
}
lp0 = curwp->w_dotp; /* line previous to insert */
lp2 = lp0->l_fp; /* line after insert */
/* re-link new line between lp0 and lp2 */
lp2->l_bp = lp1;
lp0->l_fp = lp1;
lp1->l_bp = lp0;
lp1->l_fp = lp2;
/* and advance and write out the current line */
curwp->w_dotp = lp1;
for (i = 0; i < nbytes; ++i)
lputc(lp1, i, line[i]);
++nline;
}
ffclose(); /* Ignore errors */
curwp->w_markp = lforw(curwp->w_markp);
if (s == FIOEOF)
{ /* Don't zap message! */
if (nline != 1)
mlwrite("[Inserted %d lines]", nline);
else
mlwrite("[Inserted 1 line]");
}
if (lflag)
{
if (nline != 1)
mlwrite("[Inserted %d lines: Long lines wrapped]", nline);
else
mlwrite("[Inserted 1 line: Long lines wrapped]");
}
out:
/* advance to the next line and mark the window for changes */
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_flag |= WFHARD;
/* copy window parameters back to the buffer structure */
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
/* we need to update number of lines in the buffer */
curwp->w_bufp->b_lines += nline;
if (s == FIOERR) /* False if error */
return (FALSE);
return (TRUE);
}

121
src/cmd/emg/fileio.c Normal file
View File

@@ -0,0 +1,121 @@
/* This file is in the public domain. */
/*
* The routines in this file read and write ASCII files from the disk. All of
* the knowledge about files is here. A better message writing scheme should
* be used
*/
#include <stdio.h> /* fopen(3), et.al. */
#include "estruct.h"
extern void mlwrite();
int ffropen(char *fn);
int ffwopen(char *fn);
int ffclose();
int ffputline(char buf[], int nbuf);
int ffgetline(char buf[], int nbuf);
FILE *ffp; /* File pointer, all functions */
/*
* Open a file for reading.
*/
int ffropen(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).
*/
int ffwopen(char *fn)
{
if ((ffp = fopen(fn, "w")) == NULL)
{
mlwrite("Cannot open file for writing");
return (FIOERR);
}
return (FIOSUC);
}
/*
* Close a file. Should look at the status in all systems.
*/
int ffclose()
{
if (fclose(ffp) != FALSE)
{
mlwrite("Error closing file");
return (FIOERR);
}
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.
*/
int ffputline(char buf[], int nbuf)
{
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.
*/
int ffgetline(char buf[], int nbuf)
{
int c, i;
i = 0;
while ((c = fgetc(ffp)) != EOF && c != '\n')
{
if (i >= nbuf - 2)
{
buf[nbuf - 2] = c; /* store last char read */
buf[nbuf - 1] = 0; /* and terminate it */
mlwrite("File has long lines");
return (FIOLNG);
}
buf[i++] = c;
}
if (c == EOF)
{
if (ferror(ffp))
{
mlwrite("File read error");
return (FIOERR);
}
if (i != 0)
{
mlwrite("No newline at EOF");
return (FIOERR);
}
return (FIOEOF);
}
buf[i] = 0;
return (FIOSUC);
}

507
src/cmd/emg/line.c Normal file
View File

@@ -0,0 +1,507 @@
/* This file is in the public domain. */
/*
* 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 <stdlib.h> /* malloc(3) */
#include "estruct.h"
#include "edef.h"
extern void mlwrite();
extern int backchar(int f, int n);
LINE* lalloc(int used);
void lfree(LINE *lp);
void lchange(int flag);
int linsert(int n, int c);
int lnewline();
int ldelete(int n, int kflag);
int ldelnewline();
void kdelete();
int kinsert(int c);
int kremove(int n);
#define NBLOCK 16 /* Line block chunk size */
#define KBLOCK 1024 /* Kill buffer block size */
char *kbufp = NULL; /* Kill buffer data */
unsigned long kused = 0; /* # of bytes used in KB */
unsigned long 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(int used)
{
LINE *lp;
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
*/
void lfree(LINE *lp)
{
BUFFER *bp;
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).
*/
void lchange(int flag)
{
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
*/
int linsert(int n, int c)
{
WINDOW *wp;
LINE *lp1, *lp2, *lp3;
char *cp1, *cp2;
int i, doto;
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.
*/
int lnewline()
{
WINDOW *wp;
char *cp1, *cp2;
LINE *lp1, *lp2;
int doto;
lchange(WFHARD);
curwp->w_bufp->b_lines++;
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;
}
curwp->w_dotline++;
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.
*/
int ldelete(int n, int kflag)
{
LINE *dotp;
WINDOW *wp;
char *cp1, *cp2;
int doto, chunk;
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.
*/
int ldelnewline()
{
LINE *lp1, *lp2, *lp3;
WINDOW *wp;
char *cp1, *cp2;
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);
}
/* Keep line counts in sync */
curwp->w_bufp->b_lines--;
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.
*/
void 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.
*/
int kinsert(int c)
{
char *nbufp;
if (kused == ksize)
{
if (ksize == 0) /* first time through? */
nbufp = malloc(KBLOCK); /* alloc the first block */
else /* or re allocate a bigger block */
nbufp = realloc(kbufp, ksize + KBLOCK);
if (nbufp == NULL) /* abort if it fails */
return (FALSE);
kbufp = nbufp; /* point our global at it */
ksize += KBLOCK; /* and adjust the size */
}
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.
*/
int kremove(int n)
{
if (n >= kused)
return (-1);
else
return (kbufp[n] & 0xFF);
}

465
src/cmd/emg/main.c Normal file
View File

@@ -0,0 +1,465 @@
/* This file is in the public domain. */
/*
* This program is in public domain; originally written by Dave G. Conroy.
* This file contains the main driving routine, and some keyboard processing
* code
*/
#define maindef /* make global definitions not external */
#include <string.h> /* strncpy(3) */
#include <stdlib.h> /* malloc(3) */
#include "estruct.h" /* global structures and defines */
#include "efunc.h" /* function declarations and name table */
#include "edef.h" /* global definitions */
#include "ebind.h"
extern void getwinsize();
extern void vtinit();
extern void vttidy();
extern void update();
extern void mlerase();
extern void mlwrite();
extern int mlyesno(char *prompt);
extern void makename(char bname[], char fname[]);
extern int readin(char fname[]);
extern int linsert(int f, int n);
extern int anycb();
extern BUFFER *bfind();
void edinit(char bname[]);
int execute(int c, int f, int n);
int getkey();
int getctl();
int quickexit(int f, int n);
int quit(int f, int n);
int ctlxlp(int f, int n);
int ctlxrp(int f, int n);
int ctlxe(int f, int n);
int ctrlg(int f, int n);
int extendedcmd(int f, int n);
int main(int argc, char *argv[])
{
BUFFER *bp;
char bname[NBUFN]; /* buffer name of file to read */
int c, f, n, mflag;
int ffile; /* first file flag */
int carg; /* current arg to scan */
int basec; /* c stripped of meta character */
/* initialize the editor and process the startup file */
getwinsize(); /* find out the "real" screen size */
strncpy(bname, "main", 5); /* default buffer name */
edinit(bname); /* Buffers, windows */
vtinit(); /* Displays */
ffile = TRUE; /* no file to edit yet */
update(); /* let the user know we are here */
/* scan through the command line and get the files to edit */
for (carg = 1; carg < argc; ++carg)
{
/* set up a buffer for this file */
makename(bname, argv[carg]);
/* if this is the first file, read it in */
if (ffile)
{
bp = curbp;
makename(bname, argv[carg]);
strncpy(bp->b_bname, bname, NBUFN);
strncpy(bp->b_fname, argv[carg], NFILEN);
if (readin(argv[carg]) == ABORT)
{
strncpy(bp->b_bname, "main", 5);
strncpy(bp->b_fname, "", 1);
}
bp->b_dotp = bp->b_linep;
bp->b_doto = 0;
ffile = FALSE;
}
else
{
/* set this to inactive */
bp = bfind(bname, TRUE, 0);
strncpy(bp->b_fname, argv[carg], NFILEN);
bp->b_active = FALSE;
}
}
/* setup to process commands */
lastflag = 0; /* Fake last flags */
curwp->w_flag |= WFMODE; /* and force an update */
loop:
update(); /* Fix up the screen */
c = getkey();
if (mpresf != FALSE)
{
mlerase();
update();
}
f = FALSE;
n = 1;
/* do META-# processing if needed */
basec = c & ~META; /* strip meta char off if there */
if ((c & META) && ((basec >= '0' && basec <= '9') || basec == '-'))
{
f = TRUE; /* there is a # arg */
n = 0; /* start with a zero default */
mflag = 1; /* current minus flag */
c = basec; /* strip the META */
while ((c >= '0' && c <= '9') || (c == '-'))
{
if (c == '-')
{
/* already hit a minus or digit? */
if ((mflag == -1) || (n != 0))
break;
mflag = -1;
}
else
n = n * 10 + (c - '0');
if ((n == 0) && (mflag == -1)) /* lonely - */
mlwrite("Arg:");
else
mlwrite("Arg: %d", n * mflag);
c = getkey(); /* get the next key */
}
n = n * mflag; /* figure in the sign */
}
/* do ^U repeat argument processing */
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.
*/
void edinit(char bname[])
{
BUFFER *bp;
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.
*/
int execute(int c, int f, int n)
{
KEYTAB *ktp;
int status;
ktp = &keytab[0]; /* Look in key table */
while (ktp->k_fp != NULL)
{
if (ktp->k_code == c)
{
thisflag = 0;
status = (*ktp->k_fp) (f, n);
lastflag = thisflag;
return (status);
}
++ktp;
}
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);
}
mlwrite("\007[Key not bound]"); /* complain */
lastflag = 0; /* Fake last flags */
return (FALSE);
}
/*
* Read in a key. Do the standard keyboard preprocessing. Convert the keys to
* the internal character set.
*/
int getkey()
{
int c;
c = (*term.t_getchar) ();
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.
*/
int getctl()
{
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 any buffer has changed
* do a write on that buffer and exit emacs, otherwise simply exit.
*/
int quickexit(int f, int n)
{
BUFFER *bp; /* scanning pointer to buffers */
bp = bheadp;
while (bp != NULL)
{
if ((bp->b_flag & BFCHG) != 0 /* Changed */
&& (bp->b_flag & BFTEMP) == 0)
{ /* Real */
curbp = bp; /* make that buffer current */
mlwrite("[Saving %s]", (int*)bp->b_fname);
filesave(f, n);
}
bp = bp->b_bufp; /* on to the next buffer */
}
return 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".
*/
int quit(int f, int n)
{
int s;
if (f != FALSE /* Argument forces it */
|| anycb () == FALSE /* All buffers clean */
|| (s = mlyesno("Modified buffers exist. Leave anyway")) == TRUE)
{
vttidy();
exit (0);
}
mlwrite("");
return (s);
}
/*
* Begin a keyboard macro. Error if not at the top level in keyboard
* processing. Set up variables and return.
*/
int ctlxlp(int f, int 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.
*/
int ctlxrp(int f, int 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.
*/
int ctlxe(int f, int n)
{
int c, af, an, s;
if (kbdmip != NULL || kbdmop != NULL)
{
mlwrite("No macro defined");
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.
*/
int ctrlg(int f, int n)
{
(*term.t_beep) ();
if (kbdmip != NULL)
{
kbdm[0] = (CTLX | ')');
kbdmip = NULL;
}
mlwrite("[Aborted]");
return (ABORT);
}
/*
* Handle ANSI escape-extended commands (with "ESC [" or "ESC O" prefix)
*/
int extendedcmd(int f, int n)
{
int (*cmd)();
int c;
c = getctl();
switch (c)
{
case 'A': cmd = backline; break;
case 'B': cmd = forwline; break;
case 'C': cmd = forwchar; break;
case 'D': cmd = backchar; break;
case 'H': cmd = gotobob; break;
case 'W': cmd = gotoeob; break;
default: mlwrite("\007[Key not bound]");
return (FALSE);
}
return cmd(f, n);
}

290
src/cmd/emg/random.c Normal file
View File

@@ -0,0 +1,290 @@
/* This file is in the public domain. */
/*
* This file contains the command processing functions for a number of random
* commands. There is no functional grouping here, for sure.
*/
#include "estruct.h"
#include "edef.h"
extern void mlwrite();
extern void lchange(int flag);
extern int lnewline();
extern int linsert(int n, int c);
extern int backchar(int f, int n);
extern void kdelete();
extern int ldelete(int f, int n);
extern int kremove(int k);
int setfillcol(int f, int n);
int getccol(int bflg);
int twiddle(int f, int n);
int quote(int f, int n);
int tab(int f, int n);
int openline(int f, int n);
int newline(int f, int n);
int forwdel(int f, int n);
int backdel(int f, int n);
int killtext(int f, int n);
int yank(int f, int n);
/*
* Set fill column to n.
*/
int setfillcol(int f, int n)
{
fillcol = n;
mlwrite("[Fill column is %d]", n);
return (TRUE);
}
/*
* Return current column. Stop at first non-blank given TRUE argument.
*/
int getccol(int bflg)
{
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
*/
int twiddle(int f, int n)
{
LINE *dotp;
int doto, cl, 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 "C-Q"
*/
int quote(int f, int n)
{
int s, 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));
}
/*
* Insert a tab into file.
* Bound to "C-I"
*/
int tab(int f, int n)
{
if (n < 0)
return (FALSE);
return (linsert(n, 9));
}
/*
* 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
* processors. They even handle the looping. Normally this is bound to "C-O"
*/
int openline(int f, int n)
{
int i, 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".
*/
int newline(int f, int n)
{
int s;
if (n < 0)
return (FALSE);
/* insert some lines */
while (n--)
{
if ((s = lnewline()) != TRUE)
return (s);
}
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"
*/
int forwdel(int f, int 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"
*/
int backdel(int f, int n)
{
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"
*/
int killtext(int f, int n)
{
LINE *nextp;
int chunk;
if ((lastflag & CFKILL) == 0)/* Clear kill buffer if last wasn't a kill */
kdelete();
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"
*/
int yank(int f, int n)
{
int c, i;
if (n < 0)
return (FALSE);
while (n--)
{
i = 0;
while ((c = kremove(i)) >= 0)
{
if (c == '\n')
{
if (lnewline(FALSE, 1) == FALSE)
return (FALSE);
}
else
{
if (linsert(1, c) == FALSE)
return (FALSE);
}
++i;
}
}
return (TRUE);
}

142
src/cmd/emg/region.c Normal file
View File

@@ -0,0 +1,142 @@
/* This file is in the public domain. */
/*
* 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 "estruct.h"
#include "edef.h"
extern void kdelete();
extern int ldelete(int f, int n);
extern int kinsert(int c);
extern void mlwrite();
int killregion(int f, int n);
int copyregion(int f, int n);
int getregion(REGION *rp);
/*
* 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"
*/
int killregion(int f, int n)
{
REGION region;
int s;
if ((s = getregion(&region)) != 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"
*/
int copyregion(int f, int n)
{
LINE *linep;
REGION region;
int loffs, s;
if ((s = getregion(&region)) != 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);
}
/*
* 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
*/
int getregion(REGION *rp)
{
LINE *flp, *blp;
int fsize, bsize;
if (curwp->w_markp == (struct LINE*)0)
{
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);
}

535
src/cmd/emg/search.c Normal file
View File

@@ -0,0 +1,535 @@
/* This file is in the public domain. */
/*
* The functions in this file implement commands that search in the forward
* and backward directions. There are no special characters in the search
* strings
*/
#include <string.h> /* strncpy(3), strncat(3) */
#include "estruct.h"
#include "edef.h"
extern void mlwrite();
extern int mlreplyt(char *prompt, char *buf, int nbuf, char eolchar);
extern void update();
extern int forwchar(int f, int n);
extern int ldelete(int n, int kflag);
extern int lnewline();
extern int linsert(int n, int c);
int forwsearch(int f, int n);
int forwhunt(int f, int n);
int backsearch(int f, int n);
int backhunt(int f, int n);
int bsearch(int f, int n);
int eq(int bc, int pc);
int readpattern(char *prompt);
int sreplace(int f, int n);
int qreplace(int f, int n);
int replaces(int kind, int f, int n);
int forscan(char *patrn, int leavep);
void expandp(char *srcstr, char *deststr, int maxlength);
#define PTBEG 1 /* leave the point at the begining on search */
#define PTEND 2 /* leave the point at the end on search */
/*
* 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"
*/
int forwsearch(int f, int n)
{
int status;
if (n == 0) /* resolve the repeat count */
n = 1;
if (n < 1) /* search backwards */
return (backsearch(f, -n));
/* ask the user for the text of a pattern */
if ((status = readpattern("Search")) != TRUE)
return (status);
/* search for the pattern */
while (n-- > 0)
{
if ((status = forscan(&pat[0], PTEND)) == FALSE)
break;
}
/* and complain if not there */
if (status == FALSE)
mlwrite("Not found");
return (status);
}
int forwhunt(int f, int n)
{
int status = 0;
/* resolve the repeat count */
if (n == 0)
n = 1;
if (n < 1) /* search backwards */
return (backhunt(f, -n));
/* Make sure a pattern exists */
if (pat[0] == 0)
{
mlwrite("No pattern set");
return (FALSE);
}
/* search for the pattern */
while (n-- > 0)
{
if ((status = forscan(&pat[0], PTEND)) == FALSE)
break;
}
/* and complain if not there */
if (status == FALSE)
mlwrite("Not found");
return (status);
}
/*
* 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
* matched]. Bound to "C-R"
*/
int backsearch(int f, int n)
{
int s;
if (n == 0) /* resolve null and negative arguments */
n = 1;
if (n < 1)
return (forwsearch(f, -n));
if ((s = readpattern("Reverse search")) != TRUE) /* get a pattern to search */
return (s);
return bsearch(f, n); /* and go search for it */
}
/* hunt backward for the last search string entered
*/
int backhunt(int f, int n)
{
if (n == 0) /* resolve null and negative arguments */
n = 1;
if (n < 1)
return (forwhunt(f, -n));
if (pat[0] == 0) /* Make sure a pattern exists */
{
mlwrite("No pattern set");
return (FALSE);
}
return bsearch(f, n); /* go search */
}
int bsearch(int f, int n)
{
LINE *clp, *tlp;
char *epp, *pp;
int cbo, tbo, c;
/* find a pointer to the end of the pattern */
for (epp = &pat[0]; epp[1] != 0; ++epp)
;
/* make local copies of the starting location */
clp = curwp->w_dotp;
cbo = curwp->w_doto;
while (n-- > 0)
{
for (;;)
{
/* if we are at the begining of the line, wrap back around */
if (cbo == 0)
{
clp = lback(clp);
if (clp == curbp->b_linep)
{
mlwrite("Not found");
return (FALSE);
}
cbo = llength(clp) + 1;
}
/* fake the <NL> at the end of a line */
if (--cbo == llength(clp))
c = '\n';
else
c = lgetc(clp, cbo);
/* check for a match against the end of the pattern */
if (eq(c, *epp) != FALSE)
{
tlp = clp;
tbo = cbo;
pp = epp;
/* scanning backwards through the rest of the pattern
* looking for a match */
while (pp != &pat[0])
{
/* wrap across a line break */
if (tbo == 0)
{
tlp = lback(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = llength(tlp) + 1;
}
/* fake the <NL> */
if (--tbo == llength(tlp))
c = '\n';
else
c = lgetc(tlp, tbo);
if (eq(c, *--pp) == FALSE)
goto fail;
}
/* A Match! reset the current cursor */
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
goto next;
}
fail:;
}
next:;
}
return (TRUE);
}
/*
* Compare two characters. The "bc" comes from the buffer. It has it's case
* folded out. The "pc" is from the pattern
*/
int eq(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.
*/
int readpattern(char *prompt)
{
char tpat[NPAT + 20];
int s;
strncpy(tpat, prompt, NPAT-12); /* copy prompt to output string */
strncat(tpat, " [", 3); /* build new prompt string */
expandp(&pat[0], &tpat[strlen (tpat)], NPAT / 2); /* add old pattern */
strncat(tpat, "]: ", 4);
s = mlreplyt(tpat, tpat, NPAT, 10); /* Read pattern */
if (s == TRUE) /* Specified */
strncpy(pat, tpat, NPAT);
else if (s == FALSE && pat[0] != 0) /* CR, but old one */
s = TRUE;
return (s);
}
/*
* Search and replace (ESC-R)
*/
int sreplace(int f, int n)
{
return (replaces(FALSE, f, n));
}
/*
* search and replace with query (ESC-CTRL-R)
*/
int qreplace(int f, int n)
{
return (replaces(TRUE, f, n));
}
/*
* replaces: search for a string and replace it with another string. query
* might be enabled (according to kind)
*/
int replaces(int kind, int f, int n)
{
LINE *origline; /* original "." position */
char tmpc; /* temporary character */
char c; /* input char for query */
char tpat[NPAT]; /* temporary to hold search pattern */
int i; /* loop index */
int s; /* success flag on pattern inputs */
int slength, rlength; /* length of search and replace strings */
int numsub; /* number of substitutions */
int nummatch; /* number of found matches */
int nlflag; /* last char of search string a <NL>? */
int nlrepl; /* was a replace done on the last line? */
int origoff; /* and offset (for . query option) */
/* check for negative repititions */
if (f && n < 0)
return (FALSE);
/* ask the user for the text of a pattern */
if ((s = readpattern((kind == FALSE ? "Replace" : "Query replace"))) != TRUE)
return (s);
strncpy(&tpat[0], &pat[0], NPAT); /* salt it away */
/* ask for the replacement string */
strncpy(&pat[0], &rpat[0], NPAT); /* set up default string */
if ((s = readpattern("with")) == ABORT)
return (s);
/* move everything to the right place and length them */
strncpy(&rpat[0], &pat[0], NPAT);
strncpy(&pat[0], &tpat[0], NPAT);
slength = strlen(&pat[0]);
rlength = strlen(&rpat[0]);
/* set up flags so we can make sure not to do a recursive replace on the
* last line */
nlflag = (pat[slength - 1] == '\n');
nlrepl = FALSE;
/* build query replace question string */
strncpy(tpat, "Replace '", 10);
expandp(&pat[0], &tpat[strlen (tpat)], NPAT / 3);
strncat(tpat, "' with '", 9);
expandp(&rpat[0], &tpat[strlen (tpat)], NPAT / 3);
strncat(tpat, "'? ", 4);
/* save original . position */
origline = curwp->w_dotp;
origoff = curwp->w_doto;
/* scan through the file */
numsub = 0;
nummatch = 0;
while ((f == FALSE || n > nummatch) &&
(nlflag == FALSE || nlrepl == FALSE))
{
/* search for the pattern */
if (forscan(&pat[0], PTBEG) != TRUE)
break; /* all done */
++nummatch; /* increment # of matches */
/* check if we are on the last line */
nlrepl = (lforw (curwp->w_dotp) == curwp->w_bufp->b_linep);
/* check for query */
if (kind)
{
/* get the query */
mlwrite(&tpat[0], &pat[0], &rpat[0]);
qprompt:
update(); /* show the proposed place to change */
c = (*term.t_getchar) (); /* and input */
mlwrite(""); /* and clear it */
/* and respond appropriately */
switch (c)
{
case 'y': /* yes, substitute */
case ' ':
break;
case 'n': /* no, onword */
forwchar(FALSE, 1);
continue;
case '!': /* yes/stop asking */
kind = FALSE;
break;
case '.': /* abort! and return */
/* restore old position */
curwp->w_dotp = origline;
curwp->w_doto = origoff;
curwp->w_flag |= WFMOVE;
case BELL: /* abort! and stay */
mlwrite("Aborted!");
return (FALSE);
case 0x0d: /* controlled exit */
case 'q':
return (TRUE);
default: /* bitch and beep */
(*term.t_beep) ();
case '?': /* help me */
mlwrite("(Y)es, (N)o, (!)Do the rest, (^G,RET,q)Abort, (.)Abort back, (?)Help: ");
goto qprompt;
}
}
/* delete the sucker */
if (ldelete(slength, FALSE) != TRUE)
{
/* error while deleting */
mlwrite("ERROR while deleting");
return (FALSE);
}
/* and insert its replacement */
for (i = 0; i < rlength; i++)
{
tmpc = rpat[i];
s = (tmpc == '\n' ? lnewline() : linsert(1, tmpc));
if (s != TRUE)
{
/* error while inserting */
mlwrite("Out of memory while inserting");
return (FALSE);
}
}
numsub++; /* increment # of substitutions */
}
/* and report the results */
mlwrite("%d substitutions", numsub);
return (TRUE);
}
/* search forward for a <patrn>
*/
int forscan(char *patrn, int leavep)
{
LINE *curline; /* current line during scan */
LINE *lastline; /* last line position during scan */
LINE *matchline; /* current line during matching */
char *patptr; /* pointer into pattern */
int curoff; /* position within current line */
int lastoff; /* position within last line */
int c; /* character at current position */
int matchoff; /* position in matching line */
/* setup local scan pointers to global "." */
curline = curwp->w_dotp;
curoff = curwp->w_doto;
/* scan each character until we hit the head link record */
while (curline != curbp->b_linep)
{
/* save the current position in case we need to restore it on a match */
lastline = curline;
lastoff = curoff;
/* get the current character resolving EOLs */
if (curoff == llength(curline))
{ /* if at EOL */
curline = lforw(curline); /* skip to next line */
curoff = 0;
c = '\n'; /* and return a <NL> */
}
else
c = lgetc(curline, curoff++); /* get the char */
/* test it against first char in pattern */
if (eq(c, patrn[0]) != FALSE) /* if we find it. */
{
/* setup match pointers */
matchline = curline;
matchoff = curoff;
patptr = &patrn[0];
/* scan through patrn for a match */
while (*++patptr != 0)
{
/* advance all the pointers */
if (matchoff == llength(matchline))
{
/* advance past EOL */
matchline = lforw(matchline);
matchoff = 0;
c = '\n';
}
else
c = lgetc(matchline, matchoff++);
/* and test it against the pattern */
if (eq(*patptr, c) == FALSE)
goto fail;
}
/* A SUCCESSFULL MATCH!!! */
/* reset the global "." pointers */
if (leavep == PTEND)
{ /* at end of string */
curwp->w_dotp = matchline;
curwp->w_doto = matchoff;
}
else
{ /* at begining of string */
curwp->w_dotp = lastline;
curwp->w_doto = lastoff;
}
curwp->w_flag |= WFMOVE; /* flag that we have moved */
return (TRUE);
}
fail:; /* continue to search */
}
/* we could not find a match */
return (FALSE);
}
/* expandp: expand control key sequences for output
*/
void expandp(char *srcstr, char *deststr, int maxlength)
{
char c; /* current char to translate */
/* scan through the string */
while ((c = *srcstr++) != 0)
{
if (c < 0x20 || c == 0x7f)
{ /* control character */
*deststr++ = '^';
*deststr++ = c ^ 0x40;
maxlength -= 2;
}
else if (c == '%')
{
*deststr++ = '%';
*deststr++ = '%';
maxlength -= 2;
}
else
{ /* any other character */
*deststr++ = c;
maxlength--;
}
/* check for maxlength */
if (maxlength < 4)
{
*deststr++ = '$';
*deststr = '\0';
return;
}
}
*deststr = '\0';
return;
}

144
src/cmd/emg/tcap.c Normal file
View File

@@ -0,0 +1,144 @@
/* This file is in the public domain. */
/* termios video driver */
#define termdef 1 /* don't define "term" externally */
/* Did you remember to set FORCE_COLS? */
#ifndef FORCE_COLS
#define FORCE_COLS 80
#endif
/* Did you remember to set FORCE_ROWS? */
#ifndef FORCE_ROWS
#define FORCE_ROWS 24
#endif
/*
* XXX:
* Default/sane(?) maximum column and row sizes.
* Taken from mg1a.
*
* Let the user override this with a
* CFLAGS += -DMAXCOL=XXX -DMAXROW=XXX
* line in the Makefile.
*/
#ifndef MAXCOL
#define MAXCOL 132
#endif
#ifndef MAXROW
#define MAXROW 66
#endif
#include <stdio.h> /* puts(3), snprintf(3) */
#include "estruct.h"
#include "edef.h"
#undef CTRL /* Needs to be done here. */
#include <sys/ioctl.h>
extern int tgetent();
extern char *tgetstr();
extern char *tgoto();
extern void tputs();
extern char *getenv();
extern void ttopen();
extern int ttgetc();
extern void ttputc();
extern void ttflush();
extern void ttclose();
extern void panic();
void getwinsize();
void tcapopen();
void tcapmove(int row, int col);
void tcapeeol();
void tcapeeop();
void tcaprev();
void tcapbeep();
#define MARGIN 8
#define SCRSIZ 64
#define BEL 0x07
#define TCAPSLEN 64
char tcapbuf[TCAPSLEN]; /* capabilities actually used */
char *CM, *CE, *CL, *SO, *SE;
TERM term = {
0, 0, MARGIN, SCRSIZ, tcapopen, ttclose, ttgetc, ttputc,
ttflush, tcapmove, tcapeeol, tcapeeop, tcapbeep, tcaprev
};
void getwinsize()
{
int cols = FORCE_COLS;
int rows = FORCE_ROWS;
/* Too small and we're out */
if ((cols < 10) || (rows < 3))
panic("Too few columns or rows");
if (FORCE_COLS > MAXCOL)
cols = MAXCOL;
if (FORCE_ROWS > MAXROW)
rows = MAXROW;
term.t_ncol = cols;
term.t_nrow = rows - 1;
}
void tcapopen()
{
char tcbuf[1024];
char *p, *tv_stype;
if ((tv_stype = getenv("TERM")) == NULL)
panic("TERM not defined");
if ((tgetent(tcbuf, tv_stype)) != 1)
panic("Unknown terminal type");
p = tcapbuf;
CL = tgetstr("cl", &p);
CM = tgetstr("cm", &p);
CE = tgetstr("ce", &p);
SE = tgetstr("se", &p);
SO = tgetstr("so", &p);
if (CE == NULL)
eolexist = FALSE;
if (SO != NULL && SE != NULL)
revexist = TRUE;
if (CL == NULL || CM == NULL)
panic("Need cl & cm abilities");
if (p >= &tcapbuf[TCAPSLEN]) /* XXX */
panic("Description too big");
ttopen ();
}
void tcaprev(int state)
{
if (revexist)
tputs((state ? SO : SE), 1, ttputc);
}
void tcapmove (int row, int col)
{
tputs(tgoto(CM, col, row), 1, ttputc);
}
void tcapeeol()
{
tputs(CE, 1, ttputc);
}
void tcapeeop()
{
tputs(CL, 1, ttputc);
}
void tcapbeep()
{
ttputc(BEL);
}

163
src/cmd/emg/ttyio.c Normal file
View File

@@ -0,0 +1,163 @@
/* This file is in the public domain. */
/*
* This file comes from mg1a.
* Uses the panic function from OpenBSD's mg.
*/
/*
* Ultrix-32 and Unix terminal I/O.
* The functions in this file
* negotiate with the operating system for
* keyboard characters, and write characters to
* the display in a barely buffered fashion.
*/
#include <stdio.h>
#include <sgtty.h>
#include <stdlib.h>
#include <term.h>
#include <unistd.h>
#undef CTRL
#include "estruct.h"
void ttflush(void);
void panic(char *);
extern void getwinsize();
#define NROW 66 /* Rows. */
#define NCOL 132 /* Columns. */
#define NOBUF 512 /* Output buffer size. */
char obuf[NOBUF]; /* Output buffer. */
int nobuf;
struct sgttyb oldtty; /* V6/V7 stty data. */
struct sgttyb newtty;
struct tchars oldtchars; /* V7 editing. */
struct tchars newtchars;
struct ltchars oldltchars; /* 4.2 BSD editing. */
struct ltchars newltchars;
/*
* This function gets called once, to set up
* the terminal channel.
*/
void ttopen(void) {
register char *tv_stype;
char *getenv(), *tgetstr(), tcbuf[1024];
if (ioctl(0, TIOCGETP, (char *) &oldtty) < 0)
panic("ttopen can't get sgtty");
newtty.sg_ospeed = oldtty.sg_ospeed;
newtty.sg_ispeed = oldtty.sg_ispeed;
newtty.sg_erase = oldtty.sg_erase;
newtty.sg_kill = oldtty.sg_kill;
newtty.sg_flags = oldtty.sg_flags;
newtty.sg_flags &= ~(ECHO|CRMOD); /* Kill echo, CR=>NL. */
newtty.sg_flags |= RAW|ANYP; /* raw mode for 8 bit path.*/
if (ioctl(0, TIOCSETP, (char *) &newtty) < 0)
panic("ttopen can't set sgtty");
if (ioctl(0, TIOCGETC, (char *) &oldtchars) < 0)
panic("ttopen can't get chars");
newtchars.t_intrc = 0xFF; /* Interrupt. */
newtchars.t_quitc = 0xFF; /* Quit. */
newtchars.t_startc = 0xFF; /* ^Q, for terminal. */
newtchars.t_stopc = 0xFF; /* ^S, for terminal. */
newtchars.t_eofc = 0xFF;
newtchars.t_brkc = 0xFF;
if (ioctl(0, TIOCSETC, (char *) &newtchars) < 0)
panic("ttopen can't set chars");
if (ioctl(0, TIOCGLTC, (char *) &oldltchars) < 0)
panic("ttopen can't get ltchars");
newltchars.t_suspc = 0xFF; /* Suspend #1. */
newltchars.t_dsuspc = 0xFF; /* Suspend #2. */
newltchars.t_rprntc = 0xFF;
newltchars.t_flushc = 0xFF; /* Output flush. */
newltchars.t_werasc = 0xFF;
newltchars.t_lnextc = 0xFF; /* Literal next. */
if (ioctl(0, TIOCSLTC, (char *) &newltchars) < 0)
panic("ttopen can't set ltchars");
/* do this the REAL way */
if ((tv_stype = getenv("TERM")) == NULL)
panic("TERM not defined");
if((tgetent(tcbuf, tv_stype)) != 1)
panic("Unknown terminal type");
getwinsize();
}
/*
* This function gets called just
* before we go back home to the shell. Put all of
* the terminal parameters back.
*/
void ttclose(void) {
ttflush();
if (ioctl(0, TIOCSLTC, (char *) &oldltchars) < 0)
panic("ttclose can't set ltchars");
if (ioctl(0, TIOCSETC, (char *) &oldtchars) < 0)
panic("ttclose can't set chars");
if (ioctl(0, TIOCSETP, (char *) &oldtty) < 0)
panic("ttclose can't set sgtty");
}
/*
* Write character to the display.
* Characters are buffered up, to make things
* a little bit more efficient.
*/
int ttputc(int c) {
if (nobuf >= NOBUF)
ttflush();
obuf[nobuf++] = c;
return (c);
}
/*
* Flush output.
*/
void ttflush(void) {
if (nobuf != 0) {
if (write(1, obuf, nobuf) != nobuf)
panic("ttflush write failed");
nobuf = 0;
}
}
/*
* Read character from terminal.
* All 8 bits are returned, so that you can use
* a multi-national terminal.
*/
int ttgetc(void) {
char buf[1];
while (read(0, &buf[0], 1) != 1);
return (buf[0] & 0xFF);
}
/*
* typeahead returns TRUE if there are characters available to be read
* in.
*/
int typeahead(void) {
int x;
return((ioctl(0, FIONREAD, (char *) &x) < 0) ? 0 : x);
}
/*
* panic - just exit, as quickly as we can.
* From OpenBSD's mg.
*/
void panic(char *s)
{
ttclose();
(void) fputs("panic: ", stderr);
(void) fputs(s, stderr);
(void) fputc('\n', stderr);
exit(1);
}

336
src/cmd/emg/window.c Normal file
View File

@@ -0,0 +1,336 @@
/* This file is in the public domain. */
/*
* Window management. Some of the functions are internal, and some are
* attached to keys that the user actually types
*/
#include <stdlib.h> /* free(3), malloc(3) */
#include "estruct.h"
#include "edef.h"
extern void upmode();
extern void mlwrite();
int refresh(int f, int n);
int nextwind(int f, int n);
int prevwind(int f, int n);
int onlywind(int f, int n);
int splitwind(int f, int n);
int enlargewind(int f, int n);
int shrinkwind(int f, int n);
WINDOW* wpopup();
/*
* Refresh the screen. With no argument, it does the refresh and centers
* the cursor on the screen. With an argument it does a reposition instead.
* Bound to "C-L"
*/
int refresh(int f, int n)
{
if (n >= 0)
n++; /* adjust to screen row */
if (f == FALSE)
{
sgarbf = TRUE;
n = 0; /* Center dot */
}
curwp->w_force = n;
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"
*/
int nextwind(int f, int n)
{
WINDOW *wp;
if ((wp = curwp->w_wndp) == NULL)
wp = wheadp;
curwp = wp;
curbp = wp->w_bufp;
upmode();
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
*/
int prevwind(int f, int n)
{
WINDOW *wp1, *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;
upmode();
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
*/
int onlywind(int f, int n)
{
WINDOW *wp;
LINE *lp;
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"
*/
int splitwind(int f, int n)
{
LINE *lp;
WINDOW *wp, *wp1, *wp2;
int ntru, ntrl, ntrd;
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"
*/
int enlargewind(int f, int n)
{
WINDOW *adjwp;
LINE *lp;
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
*/
int shrinkwind(int f, int n)
{
WINDOW *adjwp;
LINE *lp;
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()
{
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);
}

284
src/cmd/emg/word.c Normal file
View File

@@ -0,0 +1,284 @@
/* This file is in the public domain. */
/*
* 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 "estruct.h"
#include "edef.h"
extern int backchar(int f, int n);
extern int forwchar(int f, int n);
extern void lchange(int flag);
extern int ldelete(int n, int kflag);
extern void mlwrite();
extern int linsert(int n, int c);
extern int lnewline();
int backword(int f, int n);
int forwword(int f, int n);
int upperword(int f, int n);
int lowerword(int f, int n);
int capword(int f, int n);
int delfword(int f, int n);
int delbword(int f, int n);
int inword();
/*
* 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
*/
int backword(int f, int 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
*/
int forwword(int f, int 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"
*/
int upperword(int f, int n)
{
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"
*/
int lowerword(int f, int n)
{
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"
*/
int capword(int f, int n)
{
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"
*/
int delfword(int f, int n)
{
LINE *dotp;
int size, 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"
*/
int delbword(int f, int n)
{
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
*/
int inword()
{
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);
}