Added emg editor from github.com/ibara/emg.
This commit is contained in:
@@ -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
1
src/cmd/emg/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
emg
|
||||
41
src/cmd/emg/ChangeLog
Normal file
41
src/cmd/emg/ChangeLog
Normal 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
49
src/cmd/emg/Makefile
Normal 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
44
src/cmd/emg/README
Normal 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
39
src/cmd/emg/README-ERSATZ
Normal 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
266
src/cmd/emg/basic.c
Normal 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
494
src/cmd/emg/buffer.c
Normal 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
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
75
src/cmd/emg/ebind.h
Normal 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
78
src/cmd/emg/edef.h
Normal 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
70
src/cmd/emg/efunc.h
Normal 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
58
src/cmd/emg/emg.1
Normal 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
147
src/cmd/emg/emg.keys
Normal 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
162
src/cmd/emg/estruct.h
Normal 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
465
src/cmd/emg/file.c
Normal 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
121
src/cmd/emg/fileio.c
Normal 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
507
src/cmd/emg/line.c
Normal 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
465
src/cmd/emg/main.c
Normal 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
290
src/cmd/emg/random.c
Normal 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
142
src/cmd/emg/region.c
Normal 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(®ion)) != TRUE)
|
||||
return (s);
|
||||
if ((lastflag & CFKILL) == 0) /* This is a kill type */
|
||||
kdelete(); /* command, so do magic */
|
||||
thisflag |= CFKILL; /* kill buffer stuff */
|
||||
curwp->w_dotp = region.r_linep;
|
||||
curwp->w_doto = region.r_offset;
|
||||
return (ldelete(region.r_size, TRUE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy all of the characters in the region to the kill buffer. Don't move dot
|
||||
* at all. This is a bit like a kill region followed by a yank. Bound to "M-W"
|
||||
*/
|
||||
int copyregion(int f, int n)
|
||||
{
|
||||
LINE *linep;
|
||||
REGION region;
|
||||
int loffs, s;
|
||||
|
||||
if ((s = getregion(®ion)) != TRUE)
|
||||
return (s);
|
||||
if ((lastflag & CFKILL) == 0) /* Kill type command */
|
||||
kdelete();
|
||||
thisflag |= CFKILL;
|
||||
linep = region.r_linep; /* Current line */
|
||||
loffs = region.r_offset; /* Current offset */
|
||||
while (region.r_size--)
|
||||
{
|
||||
if (loffs == llength(linep))
|
||||
{ /* End of line */
|
||||
if ((s = kinsert('\n')) != TRUE)
|
||||
return (s);
|
||||
linep = lforw(linep);
|
||||
loffs = 0;
|
||||
}
|
||||
else
|
||||
{ /* Middle of line */
|
||||
if ((s = kinsert(lgetc(linep, loffs))) != TRUE)
|
||||
return (s);
|
||||
++loffs;
|
||||
}
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
535
src/cmd/emg/search.c
Normal 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
144
src/cmd/emg/tcap.c
Normal 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
163
src/cmd/emg/ttyio.c
Normal 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
336
src/cmd/emg/window.c
Normal 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
284
src/cmd/emg/word.c
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user