2017-03-23 18:41:14 -04:00
|
|
|
/* zep.c, Zep Emacs, Public Domain, Hugh Barney, 2017, Derived from: Anthony's Editor January 93 */
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <curses.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <termios.h>
|
|
|
|
|
|
|
|
#define B_MODIFIED 0x01 /* modified buffer */
|
|
|
|
#define MSGLINE (LINES-1)
|
|
|
|
#define CHUNK 8096L
|
|
|
|
#define K_BUFFER_LENGTH 256
|
|
|
|
#define MAX_FNAME 256
|
|
|
|
#define TEMPBUF 512
|
|
|
|
#define MIN_GAP_EXPAND 512
|
|
|
|
#define NOMARK -1
|
|
|
|
#define STRBUF_M 64
|
|
|
|
|
|
|
|
typedef unsigned char char_t;
|
|
|
|
typedef long point_t;
|
|
|
|
|
|
|
|
typedef struct keymap_t {
|
|
|
|
char *key_desc; /* name of bound function */
|
|
|
|
char *key_bytes; /* the string of bytes when this key is pressed */
|
|
|
|
void (*func)(void);
|
|
|
|
} keymap_t;
|
|
|
|
|
|
|
|
typedef struct buffer_t
|
|
|
|
{
|
|
|
|
point_t b_mark; /* the mark */
|
|
|
|
point_t b_point; /* the point */
|
|
|
|
point_t b_page; /* start of page */
|
|
|
|
point_t b_epage; /* end of page */
|
|
|
|
char_t *b_buf; /* start of buffer */
|
|
|
|
char_t *b_ebuf; /* end of buffer */
|
|
|
|
char_t *b_gap; /* start of gap */
|
|
|
|
char_t *b_egap; /* end of gap */
|
|
|
|
char w_top; /* Origin 0 top row of window */
|
|
|
|
char w_rows; /* no. of rows of text in window */
|
|
|
|
int b_row; /* cursor row */
|
|
|
|
int b_col; /* cursor col */
|
|
|
|
char b_fname[MAX_FNAME + 1]; /* filename */
|
|
|
|
char b_flags; /* buffer flags */
|
|
|
|
} buffer_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some compilers define size_t as a unsigned 16 bit number while
|
|
|
|
* point_t and off_t might be defined as a signed 32 bit number.
|
|
|
|
* malloc(), realloc(), fread(), and fwrite() take size_t parameters,
|
|
|
|
* which means there will be some size limits because size_t is too
|
|
|
|
* small of a type.
|
|
|
|
*/
|
|
|
|
#define MAX_SIZE_T ((unsigned long) (size_t) ~0)
|
|
|
|
|
|
|
|
int done;
|
|
|
|
char_t *input;
|
|
|
|
int msgflag;
|
|
|
|
char msgline[TEMPBUF];
|
|
|
|
char temp[TEMPBUF];
|
|
|
|
keymap_t *key_return;
|
|
|
|
keymap_t *key_map;
|
|
|
|
buffer_t *curbp;
|
|
|
|
point_t nscrap = 0;
|
|
|
|
char_t *scrap = NULL;
|
|
|
|
char searchtext[STRBUF_M];
|
|
|
|
|
|
|
|
buffer_t* new_buffer()
|
|
|
|
{
|
|
|
|
buffer_t *bp = (buffer_t *)malloc(sizeof(buffer_t));
|
|
|
|
assert(bp != NULL);
|
|
|
|
|
|
|
|
bp->b_point = 0;
|
|
|
|
bp->b_page = 0;
|
|
|
|
bp->b_epage = 0;
|
|
|
|
bp->b_flags = 0;
|
|
|
|
bp->b_buf = NULL;
|
|
|
|
bp->b_ebuf = NULL;
|
|
|
|
bp->b_gap = NULL;
|
|
|
|
bp->b_egap = NULL;
|
|
|
|
bp->b_fname[0] = '\0';
|
|
|
|
bp->w_top = 0;
|
|
|
|
bp->w_rows = LINES - 2;
|
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fatal(char *msg)
|
|
|
|
{
|
|
|
|
move(LINES-1, 0);
|
|
|
|
refresh();
|
|
|
|
endwin();
|
|
|
|
noraw();
|
2017-03-25 17:47:17 -04:00
|
|
|
printf("\nzep v1.1: %s\n", msg);
|
2017-03-23 18:41:14 -04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void msg(char *msg, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, msg);
|
|
|
|
(void)vsprintf(msgline, msg, args);
|
|
|
|
va_end(args);
|
|
|
|
msgflag = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given a buffer offset, convert it to a pointer into the buffer */
|
|
|
|
char_t * ptr(buffer_t *bp, register point_t offset)
|
|
|
|
{
|
|
|
|
if (offset < 0) return (bp->b_buf);
|
|
|
|
return (bp->b_buf+offset + (bp->b_buf + offset < bp->b_gap ? 0 : bp->b_egap-bp->b_gap));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given a pointer into the buffer, convert it to a buffer offset */
|
|
|
|
point_t pos(buffer_t *bp, register char_t *cp)
|
|
|
|
{
|
|
|
|
assert(bp->b_buf <= cp && cp <= bp->b_ebuf);
|
|
|
|
return (cp - bp->b_buf - (cp < bp->b_egap ? 0 : bp->b_egap - bp->b_gap));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enlarge gap by n chars, position of gap cannot change */
|
|
|
|
int growgap(buffer_t *bp, point_t n)
|
|
|
|
{
|
|
|
|
char_t *new;
|
|
|
|
point_t buflen, newlen, xgap, xegap;
|
|
|
|
|
|
|
|
assert(bp->b_buf <= bp->b_gap);
|
|
|
|
assert(bp->b_gap <= bp->b_egap);
|
|
|
|
assert(bp->b_egap <= bp->b_ebuf);
|
|
|
|
|
|
|
|
xgap = bp->b_gap - bp->b_buf;
|
|
|
|
xegap = bp->b_egap - bp->b_buf;
|
|
|
|
buflen = bp->b_ebuf - bp->b_buf;
|
|
|
|
|
|
|
|
/* reduce number of reallocs by growing by a minimum amount */
|
|
|
|
n = (n < MIN_GAP_EXPAND ? MIN_GAP_EXPAND : n);
|
|
|
|
newlen = buflen + n * sizeof (char_t);
|
|
|
|
|
|
|
|
if (buflen == 0) {
|
|
|
|
if (newlen < 0 || MAX_SIZE_T < newlen) fatal("Failed to allocate required memory.\n");
|
|
|
|
new = (char_t*) malloc((size_t) newlen);
|
|
|
|
if (new == NULL) fatal("Failed to allocate required memory.\n");
|
|
|
|
} else {
|
|
|
|
if (newlen < 0 || MAX_SIZE_T < newlen) {
|
|
|
|
msg("Failed to allocate required memory");
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
new = (char_t*) realloc(bp->b_buf, (size_t) newlen);
|
|
|
|
if (new == NULL) {
|
|
|
|
msg("Failed to allocate required memory"); /* Report non-fatal error. */
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Relocate pointers in new buffer and append the new
|
|
|
|
* extension to the end of the gap.
|
|
|
|
*/
|
|
|
|
bp->b_buf = new;
|
|
|
|
bp->b_gap = bp->b_buf + xgap;
|
|
|
|
bp->b_ebuf = bp->b_buf + buflen;
|
|
|
|
bp->b_egap = bp->b_buf + newlen;
|
|
|
|
while (xegap < buflen--)
|
|
|
|
*--bp->b_egap = *--bp->b_ebuf;
|
|
|
|
bp->b_ebuf = bp->b_buf + newlen;
|
|
|
|
|
|
|
|
assert(bp->b_buf < bp->b_ebuf); /* Buffer must exist. */
|
|
|
|
assert(bp->b_buf <= bp->b_gap);
|
|
|
|
assert(bp->b_gap < bp->b_egap); /* Gap must grow only. */
|
|
|
|
assert(bp->b_egap <= bp->b_ebuf);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
point_t movegap(buffer_t *bp, point_t offset)
|
|
|
|
{
|
|
|
|
char_t *p = ptr(bp, offset);
|
|
|
|
while (p < bp->b_gap)
|
|
|
|
*--bp->b_egap = *--bp->b_gap;
|
|
|
|
while (bp->b_egap < p)
|
|
|
|
*bp->b_gap++ = *bp->b_egap++;
|
|
|
|
assert(bp->b_gap <= bp->b_egap);
|
|
|
|
assert(bp->b_buf <= bp->b_gap);
|
|
|
|
assert(bp->b_egap <= bp->b_ebuf);
|
|
|
|
return (pos(bp, bp->b_egap));
|
|
|
|
}
|
|
|
|
|
|
|
|
void save()
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
point_t length;
|
|
|
|
|
|
|
|
fp = fopen(curbp->b_fname, "w");
|
|
|
|
if (fp == NULL) msg("Failed to open file \"%s\".", curbp->b_fname);
|
|
|
|
(void) movegap(curbp, (point_t) 0);
|
|
|
|
length = (point_t) (curbp->b_ebuf - curbp->b_egap);
|
|
|
|
if (fwrite(curbp->b_egap, sizeof (char), (size_t) length, fp) != length)
|
|
|
|
msg("Failed to write file \"%s\".", curbp->b_fname);
|
|
|
|
fclose(fp);
|
|
|
|
curbp->b_flags &= ~B_MODIFIED;
|
|
|
|
msg("File \"%s\" %ld bytes saved.", curbp->b_fname, pos(curbp, curbp->b_ebuf));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reads file into buffer at point */
|
|
|
|
int insert_file(char *fn, int modflag)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
size_t len;
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
if (stat(fn, &sb) < 0) {
|
|
|
|
msg("Failed to find file \"%s\".", fn);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
if (MAX_SIZE_T < sb.st_size) {
|
|
|
|
msg("File \"%s\" is too big to load.", fn);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
if (curbp->b_egap - curbp->b_gap < sb.st_size * sizeof (char_t) && !growgap(curbp, sb.st_size))
|
|
|
|
return (FALSE);
|
|
|
|
if ((fp = fopen(fn, "r")) == NULL) {
|
|
|
|
msg("Failed to open file \"%s\".", fn);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
curbp->b_point = movegap(curbp, curbp->b_point);
|
|
|
|
curbp->b_gap += len = fread(curbp->b_gap, sizeof (char), (size_t) sb.st_size, fp);
|
|
|
|
|
|
|
|
if (fclose(fp) != 0) {
|
|
|
|
msg("Failed to close file \"%s\".", fn);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
curbp->b_flags &= (modflag ? B_MODIFIED : ~B_MODIFIED);
|
|
|
|
msg("File \"%s\" %ld bytes read.", fn, len);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
char_t *get_key(keymap_t *keys, keymap_t **key_return)
|
|
|
|
{
|
|
|
|
keymap_t *k;
|
|
|
|
int submatch;
|
|
|
|
static char_t buffer[K_BUFFER_LENGTH];
|
|
|
|
static char_t *record = buffer;
|
|
|
|
|
|
|
|
*key_return = NULL;
|
|
|
|
|
|
|
|
/* if recorded bytes remain, return next recorded byte. */
|
|
|
|
if (*record != '\0') {
|
|
|
|
*key_return = NULL;
|
|
|
|
return record++;
|
|
|
|
}
|
|
|
|
/* reset record buffer. */
|
|
|
|
record = buffer;
|
|
|
|
|
|
|
|
do {
|
|
|
|
assert(K_BUFFER_LENGTH > record - buffer);
|
|
|
|
/* read and record one byte. */
|
|
|
|
*record++ = (unsigned)getch();
|
|
|
|
*record = '\0';
|
|
|
|
|
|
|
|
/* if recorded bytes match any multi-byte sequence... */
|
|
|
|
for (k = keys, submatch = 0; k->key_bytes != NULL; ++k) {
|
|
|
|
char_t *p, *q;
|
|
|
|
|
|
|
|
for (p = buffer, q = (char_t *)k->key_bytes; *p == *q; ++p, ++q) {
|
|
|
|
/* an exact match */
|
|
|
|
if (*q == '\0' && *p == '\0') {
|
|
|
|
record = buffer;
|
|
|
|
*record = '\0';
|
|
|
|
*key_return = k;
|
|
|
|
return record; /* empty string */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* record bytes match part of a command sequence */
|
|
|
|
if (*p == '\0' && *q != '\0') {
|
|
|
|
submatch = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (submatch);
|
|
|
|
/* nothing matched, return recorded bytes. */
|
|
|
|
record = buffer;
|
|
|
|
return (record++);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reverse scan for start of logical line containing offset */
|
|
|
|
point_t lnstart(buffer_t *bp, register point_t off)
|
|
|
|
{
|
|
|
|
register char_t *p;
|
|
|
|
do
|
|
|
|
p = ptr(bp, --off);
|
|
|
|
while (bp->b_buf < p && *p != '\n');
|
|
|
|
return (bp->b_buf < p ? ++off : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward scan for start of logical line segment containing 'finish' */
|
|
|
|
point_t segstart(buffer_t *bp, point_t start, point_t finish)
|
|
|
|
{
|
|
|
|
char_t *p;
|
|
|
|
int c = 0;
|
|
|
|
point_t scan = start;
|
|
|
|
|
|
|
|
while (scan < finish) {
|
|
|
|
p = ptr(bp, scan);
|
|
|
|
if (*p == '\n') {
|
|
|
|
c = 0;
|
|
|
|
start = scan+1;
|
|
|
|
} else if (COLS <= c) {
|
|
|
|
c = 0;
|
|
|
|
start = scan;
|
|
|
|
}
|
|
|
|
++scan;
|
|
|
|
c += *p == '\t' ? 8 - (c & 7) : 1;
|
|
|
|
}
|
|
|
|
return (c < COLS ? start : finish);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward scan for start of logical line segment following 'finish' */
|
|
|
|
point_t segnext(buffer_t *bp, point_t start, point_t finish)
|
|
|
|
{
|
|
|
|
char_t *p;
|
|
|
|
int c = 0;
|
|
|
|
|
|
|
|
point_t scan = segstart(bp, start, finish);
|
|
|
|
for (;;) {
|
|
|
|
p = ptr(bp, scan);
|
|
|
|
if (bp->b_ebuf <= p || COLS <= c)
|
|
|
|
break;
|
|
|
|
++scan;
|
|
|
|
if (*p == '\n')
|
|
|
|
break;
|
|
|
|
c += *p == '\t' ? 8 - (c & 7) : 1;
|
|
|
|
}
|
|
|
|
return (p < bp->b_ebuf ? scan : pos(bp, bp->b_ebuf));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move up one screen line */
|
|
|
|
point_t upup(buffer_t *bp, point_t off)
|
|
|
|
{
|
|
|
|
point_t curr = lnstart(bp, off);
|
|
|
|
point_t seg = segstart(bp, curr, off);
|
|
|
|
if (curr < seg)
|
|
|
|
off = segstart(bp, curr, seg-1);
|
|
|
|
else
|
|
|
|
off = segstart(bp, lnstart(bp,curr-1), curr-1);
|
|
|
|
return (off);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move down one screen line */
|
|
|
|
point_t dndn(buffer_t *bp, point_t off)
|
|
|
|
{
|
|
|
|
return (segnext(bp, lnstart(bp,off), off));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the offset of a column on the specified line */
|
|
|
|
point_t lncolumn(buffer_t *bp, point_t offset, int column)
|
|
|
|
{
|
|
|
|
int c = 0;
|
|
|
|
char_t *p;
|
|
|
|
while ((p = ptr(bp, offset)) < bp->b_ebuf && *p != '\n' && c < column) {
|
|
|
|
c += *p == '\t' ? 8 - (c & 7) : 1;
|
|
|
|
++offset;
|
|
|
|
}
|
|
|
|
return (offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void modeline(buffer_t *bp)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char mch;
|
|
|
|
|
|
|
|
standout();
|
|
|
|
move(bp->w_top + bp->w_rows, 0);
|
|
|
|
mch = ((bp->b_flags & B_MODIFIED) ? '*' : '=');
|
|
|
|
sprintf(temp, "=%c Zep: == %s ", mch, bp->b_fname);
|
|
|
|
addstr(temp);
|
|
|
|
|
|
|
|
for (i = strlen(temp) + 1; i <= COLS; i++)
|
|
|
|
addch('=');
|
|
|
|
standend();
|
|
|
|
}
|
|
|
|
|
|
|
|
void dispmsg()
|
|
|
|
{
|
|
|
|
move(MSGLINE, 0);
|
|
|
|
if (msgflag) {
|
|
|
|
addstr(msgline);
|
|
|
|
msgflag = FALSE;
|
|
|
|
}
|
|
|
|
clrtoeol();
|
|
|
|
}
|
|
|
|
|
|
|
|
void display()
|
|
|
|
{
|
|
|
|
char_t *p;
|
|
|
|
int i, j, k;
|
|
|
|
buffer_t *bp = curbp;
|
|
|
|
|
|
|
|
/* find start of screen, handle scroll up off page or top of file */
|
|
|
|
/* point is always within b_page and b_epage */
|
|
|
|
if (bp->b_point < bp->b_page)
|
|
|
|
bp->b_page = segstart(bp, lnstart(bp,bp->b_point), bp->b_point);
|
|
|
|
|
|
|
|
/* reframe when scrolled off bottom */
|
|
|
|
if (bp->b_epage <= bp->b_point) {
|
|
|
|
/* Find end of screen plus one. */
|
|
|
|
bp->b_page = dndn(bp, bp->b_point);
|
|
|
|
/* if we scoll to EOF we show 1 blank line at bottom of screen */
|
|
|
|
if (pos(bp, bp->b_ebuf) <= bp->b_page) {
|
|
|
|
bp->b_page = pos(bp, bp->b_ebuf);
|
|
|
|
i = bp->w_rows - 1;
|
|
|
|
} else {
|
|
|
|
i = bp->w_rows - 0;
|
|
|
|
}
|
|
|
|
/* Scan backwards the required number of lines. */
|
|
|
|
while (0 < i--)
|
|
|
|
bp->b_page = upup(bp, bp->b_page);
|
|
|
|
}
|
|
|
|
|
|
|
|
move(bp->w_top, 0); /* start from top of window */
|
|
|
|
i = bp->w_top;
|
|
|
|
j = 0;
|
|
|
|
bp->b_epage = bp->b_page;
|
|
|
|
|
|
|
|
/* paint screen from top of page until we hit maxline */
|
|
|
|
while (1) {
|
|
|
|
/* reached point - store the cursor position */
|
|
|
|
if (bp->b_point == bp->b_epage) {
|
|
|
|
bp->b_row = i;
|
|
|
|
bp->b_col = j;
|
|
|
|
}
|
|
|
|
p = ptr(bp, bp->b_epage);
|
|
|
|
if (bp->w_top + bp->w_rows <= i || bp->b_ebuf <= p) /* maxline */
|
|
|
|
break;
|
|
|
|
if (*p != '\r') {
|
|
|
|
if (isprint(*p) || *p == '\t' || *p == '\n') {
|
|
|
|
j += *p == '\t' ? 8-(j&7) : 1;
|
|
|
|
addch(*p);
|
|
|
|
} else {
|
|
|
|
const char *ctrl = unctrl(*p);
|
|
|
|
j += (int) strlen(ctrl);
|
|
|
|
addstr(ctrl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*p == '\n' || COLS <= j) {
|
|
|
|
j -= COLS;
|
|
|
|
if (j < 0)
|
|
|
|
j = 0;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
++bp->b_epage;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* replacement for clrtobot() to bottom of window */
|
|
|
|
for (k=i; k < bp->w_top + bp->w_rows; k++) {
|
|
|
|
move(k, j); /* clear from very last char not start of line */
|
|
|
|
clrtoeol();
|
|
|
|
j = 0; /* thereafter start of line */
|
|
|
|
}
|
|
|
|
|
|
|
|
modeline(bp);
|
|
|
|
dispmsg();
|
|
|
|
move(bp->b_row, bp->b_col); /* set cursor */
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void top() { curbp->b_point = 0; }
|
|
|
|
void bottom() { curbp->b_epage = curbp->b_point = pos(curbp, curbp->b_ebuf); }
|
|
|
|
void left() { if (0 < curbp->b_point) --curbp->b_point; }
|
|
|
|
void right() { if (curbp->b_point < pos(curbp, curbp->b_ebuf)) ++curbp->b_point; }
|
|
|
|
void up() { curbp->b_point = lncolumn(curbp, upup(curbp, curbp->b_point),curbp->b_col); }
|
|
|
|
void down() { curbp->b_point = lncolumn(curbp, dndn(curbp, curbp->b_point),curbp->b_col); }
|
|
|
|
void lnbegin() { curbp->b_point = segstart(curbp, lnstart(curbp,curbp->b_point), curbp->b_point); }
|
|
|
|
void quit() { done = 1; }
|
|
|
|
|
|
|
|
void lnend()
|
|
|
|
{
|
|
|
|
curbp->b_point = dndn(curbp, curbp->b_point);
|
|
|
|
left();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pgdown()
|
|
|
|
{
|
|
|
|
curbp->b_page = curbp->b_point = upup(curbp, curbp->b_epage);
|
|
|
|
while (0 < curbp->b_row--)
|
|
|
|
down();
|
|
|
|
curbp->b_epage = pos(curbp, curbp->b_ebuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pgup()
|
|
|
|
{
|
|
|
|
int i = curbp->w_rows;
|
|
|
|
while (0 < --i) {
|
|
|
|
curbp->b_page = upup(curbp, curbp->b_page);
|
|
|
|
up();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void insert()
|
|
|
|
{
|
|
|
|
assert(curbp->b_gap <= curbp->b_egap);
|
|
|
|
if (curbp->b_gap == curbp->b_egap && !growgap(curbp, CHUNK)) return;
|
|
|
|
curbp->b_point = movegap(curbp, curbp->b_point);
|
|
|
|
*curbp->b_gap++ = *input == '\r' ? '\n' : *input;
|
|
|
|
curbp->b_point = pos(curbp, curbp->b_egap);
|
|
|
|
curbp->b_flags |= B_MODIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void backsp()
|
|
|
|
{
|
|
|
|
curbp->b_point = movegap(curbp, curbp->b_point);
|
|
|
|
if (curbp->b_buf < curbp->b_gap) {
|
|
|
|
--curbp->b_gap;
|
|
|
|
curbp->b_flags |= B_MODIFIED;
|
|
|
|
}
|
|
|
|
curbp->b_point = pos(curbp, curbp->b_egap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void delete()
|
|
|
|
{
|
|
|
|
curbp->b_point = movegap(curbp, curbp->b_point);
|
|
|
|
if (curbp->b_egap < curbp->b_ebuf) {
|
|
|
|
curbp->b_point = pos(curbp, ++curbp->b_egap);
|
|
|
|
curbp->b_flags |= B_MODIFIED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_mark()
|
|
|
|
{
|
|
|
|
curbp->b_mark = (curbp->b_mark == curbp->b_point ? NOMARK : curbp->b_point);
|
|
|
|
msg("Mark set");
|
|
|
|
}
|
|
|
|
|
|
|
|
void copy_cut(int cut)
|
|
|
|
{
|
|
|
|
char_t *p;
|
|
|
|
/* if no mark or point == marker, nothing doing */
|
|
|
|
if (curbp->b_mark == NOMARK || curbp->b_point == curbp->b_mark) return;
|
|
|
|
if (scrap != NULL) {
|
|
|
|
free(scrap);
|
|
|
|
scrap = NULL;
|
|
|
|
}
|
|
|
|
if (curbp->b_point < curbp->b_mark) {
|
|
|
|
/* point above marker: move gap under point, region = marker - point */
|
|
|
|
(void)movegap(curbp, curbp->b_point);
|
|
|
|
p = ptr(curbp, curbp->b_point);
|
|
|
|
nscrap = curbp->b_mark - curbp->b_point;
|
|
|
|
} else {
|
|
|
|
/* if point below marker: move gap under marker, region = point - marker */
|
|
|
|
(void)movegap(curbp, curbp->b_mark);
|
|
|
|
p = ptr(curbp, curbp->b_mark);
|
|
|
|
nscrap = curbp->b_point - curbp->b_mark;
|
|
|
|
}
|
|
|
|
if ((scrap = (char_t*) malloc(nscrap)) == NULL) {
|
|
|
|
msg("No more memory available.");
|
|
|
|
} else {
|
|
|
|
(void)memcpy(scrap, p, nscrap * sizeof (char_t));
|
|
|
|
if (cut) {
|
|
|
|
curbp->b_egap += nscrap; /* if cut expand gap down */
|
|
|
|
curbp->b_point = pos(curbp, curbp->b_egap); /* set point to after region */
|
|
|
|
curbp->b_flags |= B_MODIFIED;
|
|
|
|
msg("%ld bytes cut.", nscrap);
|
|
|
|
} else {
|
|
|
|
msg("%ld bytes copied.", nscrap);
|
|
|
|
}
|
|
|
|
curbp->b_mark = NOMARK; /* unmark */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void paste()
|
|
|
|
{
|
|
|
|
if (nscrap <= 0) {
|
|
|
|
msg("Nothing to paste.");
|
|
|
|
} else if (nscrap < curbp->b_egap - curbp->b_gap || growgap(curbp, nscrap)) {
|
|
|
|
curbp->b_point = movegap(curbp, curbp->b_point);
|
|
|
|
memcpy(curbp->b_gap, scrap, nscrap * sizeof (char_t));
|
|
|
|
curbp->b_gap += nscrap;
|
|
|
|
curbp->b_point = pos(curbp, curbp->b_egap);
|
|
|
|
curbp->b_flags |= B_MODIFIED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void copy() { copy_cut(FALSE); }
|
|
|
|
void cut() { copy_cut(TRUE); }
|
|
|
|
|
|
|
|
void killtoeol()
|
|
|
|
{
|
|
|
|
/* point = start of empty line or last char in file */
|
|
|
|
if (*(ptr(curbp, curbp->b_point)) == 0xa || (curbp->b_point + 1 == ((curbp->b_ebuf - curbp->b_buf) - (curbp->b_egap - curbp->b_gap))) ) {
|
|
|
|
delete();
|
|
|
|
} else {
|
|
|
|
curbp->b_mark = curbp->b_point;
|
|
|
|
lnend();
|
|
|
|
copy_cut(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
point_t search_forward(buffer_t *bp, point_t start_p, char *stext)
|
|
|
|
{
|
|
|
|
point_t end_p = pos(bp, bp->b_ebuf);
|
|
|
|
point_t p,pp;
|
|
|
|
char* s;
|
|
|
|
|
|
|
|
if (0 == strlen(stext))
|
|
|
|
return start_p;
|
|
|
|
|
|
|
|
for (p=start_p; p < end_p; p++) {
|
|
|
|
for (s=stext, pp=p; *s == *(ptr(bp, pp)) && *s !='\0' && pp < end_p; s++, pp++)
|
|
|
|
;
|
|
|
|
|
|
|
|
if (*s == '\0')
|
|
|
|
return pp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void search()
|
|
|
|
{
|
|
|
|
int cpos = 0;
|
|
|
|
int c;
|
|
|
|
point_t o_point = curbp->b_point;
|
|
|
|
point_t found;
|
|
|
|
|
|
|
|
searchtext[0] = '\0';
|
|
|
|
msg("Search: %s", searchtext);
|
|
|
|
dispmsg();
|
|
|
|
cpos = strlen(searchtext);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
refresh();
|
|
|
|
c = getch();
|
|
|
|
/* ignore control keys other than C-g, backspace, CR, C-s, C-R, ESC */
|
|
|
|
if (c < 32 && c != 07 && c != 0x08 && c != 0x13 && c != 0x12 && c != 0x1b)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch(c) {
|
|
|
|
case 0x1b: /* esc */
|
|
|
|
searchtext[cpos] = '\0';
|
|
|
|
flushinp(); /* discard any escape sequence without writing in buffer */
|
|
|
|
return;
|
|
|
|
case 0x07: /* ctrl-g */
|
|
|
|
curbp->b_point = o_point;
|
|
|
|
return;
|
|
|
|
case 0x13: /* ctrl-s, do the search */
|
|
|
|
found = search_forward(curbp, curbp->b_point, searchtext);
|
|
|
|
if (found != -1 ) {
|
|
|
|
curbp->b_point = found;
|
|
|
|
msg("Search: %s", searchtext);
|
|
|
|
display();
|
|
|
|
} else {
|
|
|
|
msg("Failing Search: %s", searchtext);
|
|
|
|
dispmsg();
|
|
|
|
curbp->b_point = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x7f: /* del, erase */
|
|
|
|
case 0x08: /* backspace */
|
|
|
|
if (cpos == 0)
|
|
|
|
continue;
|
|
|
|
searchtext[--cpos] = '\0';
|
|
|
|
msg("Search: %s", searchtext);
|
|
|
|
dispmsg();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (cpos < STRBUF_M - 1) {
|
|
|
|
searchtext[cpos++] = c;
|
|
|
|
searchtext[cpos] = '\0';
|
|
|
|
msg("Search: %s", searchtext);
|
|
|
|
dispmsg();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the key bindings: desc, keys, func */
|
|
|
|
keymap_t keymap[] = {
|
|
|
|
{"C-a beginning-of-line ", "\x01", lnbegin },
|
|
|
|
{"C-b ", "\x02", left },
|
|
|
|
{"C-d forward-delete-char ", "\x04", delete },
|
|
|
|
{"C-e end-of-line ", "\x05", lnend },
|
|
|
|
{"C-f ", "\x06", right },
|
|
|
|
{"C-n ", "\x0E", down },
|
|
|
|
{"C-p ", "\x10", up },
|
|
|
|
{"C-h backspace ", "\x08", backsp },
|
|
|
|
{"C-k kill-to-eol ", "\x0B", killtoeol },
|
|
|
|
{"C-s search ", "\x13", search },
|
|
|
|
{"C-v ", "\x16", pgdown },
|
|
|
|
{"C-w kill-region ", "\x17", cut},
|
|
|
|
{"C-y yank ", "\x19", paste},
|
|
|
|
{"C-space set-mark ", "\x00", set_mark },
|
|
|
|
{"esc @ set-mark ", "\x1B\x40", set_mark },
|
|
|
|
{"esc k kill-region ", "\x1B\x6B", cut },
|
|
|
|
{"esc v ", "\x1B\x76", pgup },
|
|
|
|
{"esc w copy-region ", "\x1B\x77", copy},
|
|
|
|
{"esc < beg-of-buf ", "\x1B\x3C", top },
|
|
|
|
{"esc > end-of-buf ", "\x1B\x3E", bottom },
|
|
|
|
{"up previous-line ", "\x1B\x5B\x41", up },
|
|
|
|
{"down next-line ", "\x1B\x5B\x42", down },
|
|
|
|
{"left backward-character ", "\x1B\x5B\x44", left },
|
|
|
|
{"right forward-character ", "\x1B\x5B\x43", right },
|
|
|
|
{"home beginning-of-line ", "\x1B\x4F\x48", lnbegin },
|
|
|
|
{"end end-of-line ", "\x1B\x4F\x46", lnend },
|
|
|
|
{"DEL forward-delete-char ", "\x1B\x5B\x33\x7E", delete },
|
|
|
|
{"backspace delete-left ", "\x7f", backsp },
|
|
|
|
{"PgUp ", "\x1B\x5B\x35\x7E",pgup },
|
|
|
|
{"PgDn ", "\x1B\x5B\x36\x7E", pgdown },
|
|
|
|
{"C-x C-s save-buffer ", "\x18\x13", save },
|
|
|
|
{"C-x C-c exit ", "\x18\x03", quit },
|
|
|
|
{"K_ERROR ", NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (argc != 2) fatal("usage: zep filename\n");
|
|
|
|
|
|
|
|
initscr();
|
|
|
|
raw();
|
|
|
|
noecho();
|
|
|
|
|
|
|
|
curbp = new_buffer();
|
|
|
|
(void)insert_file(argv[1], FALSE);
|
|
|
|
/* Save filename irregardless of load() success. */
|
|
|
|
strncpy(curbp->b_fname, argv[1], MAX_FNAME);
|
|
|
|
curbp->b_fname[MAX_FNAME] = '\0'; /* force truncation */
|
|
|
|
|
|
|
|
if (!growgap(curbp, CHUNK)) fatal("Failed to allocate required memory.\n");
|
|
|
|
|
|
|
|
key_map = keymap;
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
display();
|
|
|
|
input = get_key(key_map, &key_return);
|
|
|
|
|
|
|
|
if (key_return != NULL) {
|
|
|
|
(key_return->func)();
|
|
|
|
} else {
|
|
|
|
/* allow TAB and NEWLINE, any other control char is 'Not Bound' */
|
2017-03-25 17:47:17 -04:00
|
|
|
if (*input > 31 || *input == 10 || *input == 9)
|
2017-03-23 18:41:14 -04:00
|
|
|
insert();
|
|
|
|
else {
|
|
|
|
fflush(stdin);
|
|
|
|
msg("Not bound");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
move(MSGLINE, 0);
|
|
|
|
refresh();
|
|
|
|
noraw();
|
|
|
|
endwin();
|
|
|
|
return 0;
|
|
|
|
}
|