home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Professional
/
OS2PRO194.ISO
/
os2
/
sysutils
/
kornshel
/
emacs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-01
|
39KB
|
1,943 lines
/*
* Emacs-like command line editing and history
*
* created by Ron Natalie at BRL
* modified by Doug Kingston, Doug Gwyn, and Lou Salkind
* adapted to PD ksh by Eric Gisin
* Modified Sep 1991 by Kai Uwe Rommel for OS/2:
* 8 bit support, 3rd meta key (extended keys)
*/
#include "config.h"
#ifdef EMACS
#ifndef lint
static char *RCSid = "$Id: emacs.c,v 1.3 1992/08/10 12:02:31 sjg Exp $";
#endif
#include "stdh.h"
#include <signal.h>
#include <sys/stat.h>
#ifdef OS2
#include <memory.h>
extern char *index_sep(char *);
extern char *rindex_sep(char *);
#endif
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <setjmp.h>
#include "sh.h"
#include "expand.h"
#include "edit.h"
#define PUSH_DELETE 1 /* push all deletes of >1 char */
static Area aedit;
#define AEDIT &aedit /* area for kill ring and macro defns */
#undef CTRL /* _BSD brain damage */
#define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */
#define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII */
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#endif
/* values returned by keyboard functions */
#define KSTD 0
#define KPREF 1 /* ^[, ^X */
#define KEOL 2 /* ^M, ^J */
#define KINTR 3 /* ^G, ^C */
#define KNULL 4
struct x_ftab {
int (*xf_func)();
char *xf_name;
char xf_db_tab;
char xf_db_char;
short xf_flags;
};
#define XF_ALLOC 2
#define XF_NOBIND 4
#define iscfs(c) (c == ' ' || c == '\t') /* Separator for completion */
#define ismfs(c) (!(isalnum(c)|| c == '$')) /* Separator for motion */
#define BEL 0x07
#define CMASK 0xFF /* 7-bit ASCII character mask */
#define X_TABS 4 /* number of keydef tables etc */
#define X_TABSZ 256 /* size of keydef tables etc */
static int x_prefix1 = CTRL('['), x_prefix2 = CTRL('X');
static int x_prefix3 = 0xE0;
static char **x_histp; /* history position */
static char **x_nextcmdp; /* for newline-and-next */
static char *xmp; /* mark pointer */
static int (*x_last_command)();
static struct x_ftab *(*x_tab)[X_TABSZ] = NULL; /* key definition */
static char *(*x_atab)[X_TABSZ] = NULL; /* macro definitions */
#define KILLSIZE 20
static char *killstack[KILLSIZE];
static int killsp, killtp;
static int x_curprefix;
static char *macroptr;
static int x_maxlen; /* to determine column width */
static int x_insert ARGS((int c));
static int x_ins_string ARGS((int c));
static void x_ins ARGS((char *cp));
static int x_del_back ARGS((int c));
static int x_del_char ARGS((int c));
static void x_delete ARGS((int nc));
static int x_del_bword ARGS((int c));
static int x_mv_bword ARGS((int c));
static int x_mv_fword ARGS((int c));
static int x_del_fword ARGS((int c));
static int x_bword ARGS((void));
static int x_fword ARGS((void));
static void x_goto ARGS((char *cp));
static void x_bs ARGS((int c));
static int x_size_str ARGS((char *cp));
static int x_size ARGS((int c));
static void x_zots ARGS((char *str));
static void x_zotc ARGS((int c));
static int x_mv_back ARGS((int c));
static int x_mv_forw ARGS((int c));
static int x_search_char ARGS((int c));
static int x_newline ARGS((int c));
static int x_end_of_text ARGS((int c));
static int x_beg_hist ARGS((int c));
static int x_end_hist ARGS((int c));
static int x_prev_com ARGS((int c));
static int x_next_com ARGS((int c));
static void x_load_hist ARGS((char **hp));
static int x_nl_next_com ARGS((int c));
static int x_eot_del ARGS((int c));
static int x_search_hist ARGS((int c));
static int x_search ARGS((char *pat, int offset));
static int x_match ARGS((char *str, char *pat));
static int x_del_line ARGS((int c));
static int x_mv_end ARGS((int c));
static int x_mv_begin ARGS((int c));
static int x_draw_line ARGS((int c));
static int x_transpose ARGS((int c));
static int x_literal ARGS((int c));
static int x_meta1 ARGS((int c));
static int x_meta2 ARGS((int c));
static int x_meta3 ARGS((int c));
static int x_kill ARGS((int c));
static void x_push ARGS((int nchars));
static int x_yank ARGS((int c));
static int x_meta_yank ARGS((int c));
static int x_abort ARGS((int c));
static int x_error ARGS((int c));
static int x_stuffreset ARGS((int c));
static int x_stuff ARGS((int c));
static void x_mapin ARGS((char *cp));
static char * x_mapout ARGS((int c));
static void x_print ARGS((int prefix, int key));
static int x_set_mark ARGS((int c));
static int x_kill_region ARGS((int c));
static int x_xchg_point_mark ARGS((int c));
static int x_copy_arg ARGS((int c));
static int x_noop ARGS((int c));
#ifdef SILLY
static int x_game_of_life ARGS((int c));
#endif
static void add_stash ARGS((char *dirnam, char *name));
static void list_stash ARGS((void));
static int x_comp_comm ARGS((int c));
static int x_list_comm ARGS((int c));
static int x_complete ARGS((int c));
static int x_enumerate ARGS((int c));
static int x_comp_file ARGS((int c));
static int x_list_file ARGS((int c));
static int x_comp_list ARGS((int c));
static void compl_dec ARGS((int type));
static void compl_file ARGS((int type));
static void compl_command ARGS((int type));
static int strmatch ARGS((char *s1, char *s2));
static int x_set_arg ARGS((int c));
static int x_prev_histword ARGS((void));
static int x_fold_case ARGS((int c));
static int x_clear_scr ARGS((int c));
static int x_list_jobs ARGS((int c));
static struct x_ftab x_ftab[] = {
{x_insert, "auto-insert", 0, 0, 0 },
{x_error, "error", 0, 0, 0 },
{x_ins_string, "macro-string", 0, 0, XF_NOBIND|XF_ALLOC},
{x_del_back, "delete-char-backward", 0, CTRL('H'), 0 },
{x_eot_del, "eot-or-delete", 0, CTRL('D'), 0 },
{x_del_bword, "delete-word-backward", 1, CTRL('H'), 0 },
{x_mv_bword, "backward-word", 1, 'b', 0 },
{x_del_line, "kill-line", 0, CTRL('U'), 0 },
{x_abort, "abort", 0, 0, 0 },
{x_noop, "no-op", 0, 0, 0 },
/* Do not move the above! */
{x_mv_fword, "forward-word", 1, 'f', 0 },
{x_del_char, "delete-char-forward", 0, 0, 0 },
{x_del_fword, "delete-word-forward", 1, 'd', 0 },
{x_mv_back, "backward-char", 0, CTRL('B'), 0 },
{x_mv_forw, "forward-char", 0, CTRL('F'), 0 },
{x_search_char, "search-character", 0, CTRL(']'), 0 },
{x_newline, "newline", 0, CTRL('M'), 0 },
{x_newline, "newline", 0, CTRL('J'), 0 },
{x_end_of_text, "eot", 0, CTRL('_'), 0 },
{x_abort, "abort", 0, CTRL('G'), 0 },
{x_prev_com, "up-history", 0, CTRL('P'), 0},
{x_next_com, "down-history", 0, CTRL('N'), 0},
{x_search_hist, "search-history", 0, CTRL('R'), 0},
{x_beg_hist, "beginning-of-history", 1, '<', 0},
{x_end_hist, "end-of-history", 1, '>', 0},
{x_mv_end, "end-of-line", 0, CTRL('E'), 0 },
{x_mv_begin, "beginning-of-line", 0, CTRL('A'), 0 },
{x_draw_line, "redraw", 0, 0, 0 },
{x_clear_scr, "clear-screen", 0, CTRL('L'), 0 },
{x_meta1, "prefix-1", 0, CTRL('['), 0 },
{x_meta2, "prefix-2", 0, CTRL('X'), 0 },
{x_meta3, "prefix-3", 0, 0xE0, 0 },
{x_kill, "kill-to-eol", 0, CTRL('K'), 0 },
{x_yank, "yank", 0, CTRL('Y'), 0 },
{x_meta_yank, "yank-pop", 1, 'y', 0 },
{x_literal, "quote", 0, CTRL('^'), 0 },
{x_stuffreset, "stuff-reset", 0, 0, 0 },
#if defined(BRL) && defined(TIOCSTI)
{x_stuff, "stuff", 0, CTRL('T'), 0 },
{x_transpose, "transpose-chars", 0, 0, 0 },
#else
{x_stuff, "stuff", 0, 0, 0 },
{x_transpose, "transpose-chars", 0, CTRL('T'), 0 },
#endif
#ifdef COMPLETE_LIST
{x_complete, "complete", 1, 0, 0 },
{x_comp_list, "complete-list", 1, CTRL('['), 0 },
#else
{x_complete, "complete", 1, CTRL('['), 0 },
{x_comp_list, "complete-list", 1, 0, 0 },
#endif
{x_enumerate, "list", 1, '?', 0 },
{x_comp_file, "complete-file", 1, CTRL('X'), 0 },
{x_comp_comm, "complete-command", 2, CTRL('['), 0 },
{x_list_file, "list-file", 2, CTRL('Y'), 0 },
{x_list_comm, "list-command", 2, '?', 0 },
{x_list_jobs, "list-jobs", 2, 'j', 0 },
{x_nl_next_com, "newline-and-next", 0, CTRL('O'), 0 },
{x_set_mark, "set-mark-command", 1, ' ', 0 },
{x_kill_region, "kill-region", 0, CTRL('W'), 0 },
{x_xchg_point_mark, "exchange-point-and-mark", 2, CTRL('X'), 0 },
#if 0
{x_copy_arg, "copy-last-arg", 1, '_', 0},
#endif
#ifdef SILLY
{x_game_of_life, "play-game-of-life", 0, 0, 0 },
#endif
#ifdef DEBUG
{x_debug_info, "debug-info", 1, CTRL('H'), 0 },
#endif
{x_prev_histword, "prev-hist-word", 1, '.', 0 },
{x_prev_histword, "prev-hist-word", 1, '_', 0 },
{x_set_arg, "", 1, '0', 0 },
{x_set_arg, "", 1, '1', 0 },
{x_set_arg, "", 1, '2', 0 },
{x_set_arg, "", 1, '3', 0 },
{x_set_arg, "", 1, '4', 0 },
{x_set_arg, "", 1, '5', 0 },
{x_set_arg, "", 1, '6', 0 },
{x_set_arg, "", 1, '7', 0 },
{x_set_arg, "", 1, '8', 0 },
{x_set_arg, "", 1, '9', 0 },
{x_fold_case, "upcase-word", 1, 'U', 0 },
{x_fold_case, "downcase-word", 1, 'L', 0 },
{x_fold_case, "capitalize-word", 1, 'C', 0 },
{x_fold_case, "upcase-word", 1, 'u', 0 },
{x_fold_case, "downcase-word", 1, 'l', 0 },
{x_fold_case, "capitalize-word", 1, 'c', 0 },
{ 0 }
};
#define xft_insert &x_ftab[0]
#define xft_error &x_ftab[1]
#define xft_ins_string &x_ftab[2]
#define xft_erase &x_ftab[3]
#define xft_kill &x_ftab[7]
#define xft_werase &x_ftab[5]
#define xft_intr &x_ftab[8]
#define xft_quit &x_ftab[9]
int
x_emacs(buf, len)
char *buf;
size_t len;
{
int c;
int i;
int (*func)();
extern x_insert();
xbp = xbuf = buf; xend = buf + len;
xlp = xcp = xep = buf;
*xcp = 0;
xlp_valid = TRUE;
xmp = NULL;
x_curprefix = 0;
macroptr = null;
x_histp = histptr + 1;
if (x_nextcmdp != NULL) {
x_load_hist(x_nextcmdp);
x_nextcmdp = NULL;
}
x_col = promptlen(prompt);
x_adj_ok = 1;
x_displen = x_cols - 2 - x_col;
x_adj_done = 0;
while (1) {
x_flush();
if (*macroptr) {
c = *macroptr++;
if (*macroptr == 0)
macroptr = null;
}
else {
if ((c = x_getc()) < 0)
return i;
}
if (x_curprefix == -1)
func = x_insert;
else
func = x_tab[x_curprefix][c&CMASK]->xf_func;
if (func == NULL)
func = x_error;
i = c | (x_curprefix << 8);
x_curprefix = 0;
switch (i = (*func)(i)) {
case KSTD:
x_last_command = func;
case KPREF:
case KNULL:
break;
case KEOL:
i = xep - xbuf;
x_last_command = 0;
return i;
case KINTR: /* special case for interrupt */
errno = EINTR;
return -1;
}
}
}
static int
x_insert(c) {
char str[2];
/*
* Should allow tab and control chars.
*/
if (c == 0) {
x_putc(BEL);
return KSTD;
}
str[0] = c;
str[1] = 0;
x_ins(str);
return KSTD;
}
static int
x_ins_string(c)
{
if (*macroptr) {
x_putc(BEL);
return KSTD;
}
macroptr = x_atab[c>>8][c & CMASK];
return KSTD;
}
static void
x_ins(cp)
char *cp;
{
int count;
register int adj = x_adj_done;
count = strlen(cp);
if (xep+count >= xend) {
x_putc(BEL);
return;
}
if (xcp != xep)
memmove(xcp+count, xcp, xep - xcp + 1);
else
xcp[count] = 0;
memmove(xcp, cp, count);
/*
* x_zots() may result in a call to x_adjust()
* we want xcp to reflect the new position.
*/
cp = xcp;
xcp += count;
xep += count;
xlp_valid = FALSE;
x_lastcp();
x_adj_ok = (xcp >= xlp);
x_zots(cp);
if (adj == x_adj_done) /* has x_adjust() been called? */
{
/* no */
for (cp = xlp; cp > xcp; )
x_bs(*--cp);
}
x_adj_ok = 1;
return;
}
static int
x_del_back(c) {
if (xcp == xbuf) {
x_putc(BEL);
return KSTD;
}
x_goto(xcp - 1);
x_delete(1);
return KSTD;
}
static int
x_del_char(c) {
if (xcp == xep) {
x_putc(BEL);
return KSTD;
}
x_delete(1);
return KSTD;
}
static void
x_delete(nc)
int nc;
{
int i,j;
char *cp;
if (nc == 0)
return;
if (xmp != NULL) {
if (xcp + nc > xmp)
xmp = xcp;
else if (xmp > xcp)
xmp -= nc;
}
#ifdef PUSH_DELETE
/*
* This lets us yank a word we have deleted.
*/
if (nc > 1)
x_push(nc);
#endif
xep -= nc;
cp = xcp;
j = 0;
i = nc;
while (i--) {
j += x_size(*cp++);
}
memmove(xcp, xcp+nc, xep - xcp + 1); /* Copies the null */
x_adj_ok = 0; /* don't redraw */
x_zots(xcp);
/*
* if we are already filling the line,
* there is no need to ' ','\b'.
* But if we must, make sure we do the minimum.
*/
if ((i = x_cols - 2 - x_col) > 0)
{
j = (j < i) ? j : i;
i = j;
while (i--)
x_putc(' ');
i = j;
while (i--)
x_putc('\b');
}
/*x_goto(xcp);*/
x_adj_ok = 1;
xlp_valid = FALSE;
for (cp = x_lastcp(); cp > xcp; )
x_bs(*--cp);
return;
}
static int
x_del_bword(c) {
x_delete(x_bword());
return KSTD;
}
static int
x_mv_bword(c) {
(void)x_bword();
return KSTD;
}
static int
x_mv_fword(c) {
x_goto(xcp + x_fword());
return KSTD;
}
static int
x_del_fword(c) {
x_delete(x_fword());
return KSTD;
}
static int
x_bword() {
int nc = 0;
register char *cp = xcp;
if (cp == xbuf) {
x_putc(BEL);
return 0;
}
if (x_last_command != x_set_arg)
x_arg = 1;
while (x_arg--)
{
while (cp != xbuf && ismfs(cp[-1]))
{
cp--;
nc++;
}
while (cp != xbuf && !ismfs(cp[-1]))
{
cp--;
nc++;
}
}
x_goto(cp);
return nc;
}
static int
x_fword() {
int nc = 0;
register char *cp = xcp;
if (cp == xep) {
x_putc(BEL);
return 0;
}
if (x_last_command != x_set_arg)
x_arg = 1;
while (x_arg--)
{
while (cp != xep && !ismfs(*cp))
{
cp++;
nc++;
}
while (cp != xep && ismfs(*cp))
{
cp++;
nc++;
}
}
return nc;
}
static void
x_goto(cp)
register char *cp;
{
if (cp < xbp || cp >= (xbp + x_displen))
{
/* we are heading off screen */
xcp = cp;
x_adjust();
}
else
{
if (cp < xcp) /* move back */
{
while (cp < xcp)
x_bs(*--xcp);
}
else
{
if (cp > xcp) /* move forward */
{
while (cp > xcp)
x_zotc(*xcp++);
}
}
}
}
static void
x_bs(c) {
register i;
i = x_size(c);
while (i--)
x_putc('\b');
}
static int
x_size_str(cp)
register char *cp;
{
register size = 0;
while (*cp)
size += x_size(*cp++);
return size;
}
static int
x_size(c) {
if (c=='\t')
return 4; /* Kludge, tabs are always four spaces. */
if (c < ' ' || c == 0x7F) /* ASCII control char */
return 2;
return 1;
}
static void
x_zots(str)
register char *str;
{
register int adj = x_adj_done;
x_lastcp();
while (*str && str < xlp && adj == x_adj_done)
x_zotc(*str++);
}
static void
x_zotc(c)
int c;
{
if (c == '\t') {
/* Kludge, tabs are always four spaces. */
x_puts(" ");
} else if (c < ' ' || c == 0x7F) { /* ASCII */
x_putc('^');
x_putc(UNCTRL(c));
} else
x_putc(c);
}
static int
x_mv_back(c) {
if (xcp == xbuf) {
x_putc(BEL);
return KSTD;
}
x_goto(xcp-1);
return KSTD;
}
static int
x_mv_forw(c) {
if (xcp == xep) {
x_putc(BEL);
return KSTD;
}
x_goto(xcp+1);
return KSTD;
}
static int
x_search_char(c)
int c;
{
char *cp;
*xep = '\0';
if ((c = x_getc()) < 0 ||
/* we search forward, I don't know what Korn does */
((cp = (xcp == xep) ? NULL : strchr(xcp+1, c)) == NULL &&
(cp = strchr(xbuf, c)) == NULL)) {
x_putc(BEL);
return KSTD;
}
x_goto(cp);
return KSTD;
}
static int
x_newline(c) {
x_putc('\n');
x_flush();
*xep++ = '\n';
return KEOL;
}
static int
x_end_of_text(c) {
#if 0
x_store_hist();
#endif
return KEOL;
}
static int x_beg_hist(c) {x_load_hist(history); return KSTD;}
static int x_end_hist(c) {x_load_hist(histptr); return KSTD;}
static int x_prev_com(c) {x_load_hist(x_histp-1); return KSTD;}
static int x_next_com(c) {x_load_hist(x_histp+1); return KSTD;}
static void
x_load_hist(hp)
register char **hp;
{
int oldsize;
if (hp < history || hp > histptr) {
x_putc(BEL);
return;
}
x_histp = hp;
oldsize = x_size_str(xbuf);
(void)strcpy(xbuf, *hp);
xbp = xbuf;
xep = xcp = xbuf + strlen(*hp);
xlp_valid = FALSE;
if (xep > x_lastcp())
x_goto(xep);
else
x_redraw(oldsize);
}
static int
x_nl_next_com(c)
int c;
{
x_nextcmdp = x_histp + 1;
return (x_newline(c));
}
static int
x_eot_del(c)
int c;
{
if (xep == xbuf)
return (x_end_of_text(c));
else
return (x_del_char(c));
}
static int x_search(), x_match();
/* reverse incremental history search */
static int
x_search_hist(c)
int c;
{
int offset = -1; /* offset of match in xbuf, else -1 */
char pat [256+1]; /* pattern buffer */
register char *p = pat;
int (*func)();
*p = 0;
while (1) {
if (offset < 0) {
x_puts("\nI-search: ");
x_zots(pat);
}
x_flush();
if ((c = x_getc()) < 0)
return KSTD;
func = x_tab[0][c&CMASK]->xf_func;
if (c == CTRL('['))
break;
else if (func == x_search_hist)
offset = x_search(pat, offset);
else if (func == x_del_back)
continue; /* todo */
else if (func == x_insert) {
/* add char to pattern */
*p++ = c, *p = 0;
if (offset >= 0) {
/* already have partial match */
offset = x_match(xbuf, pat);
if (offset >= 0) {
x_goto(xbuf + offset + (p - pat) - (*pat == '^'));
continue;
}
}
offset = x_search(pat, offset);
} else { /* other command */
static char push[2];
push[0] = c;
macroptr = push; /* push command */
break;
}
}
if (offset < 0)
x_redraw(-1);
return KSTD;
}
/* search backward from current line */
static int
x_search(pat, offset)
char *pat;
int offset;
{
register char **hp;
int i;
for (hp = x_histp; --hp >= history; ) {
i = x_match(*hp, pat);
if (i >= 0) {
if (offset < 0)
x_putc('\n');
x_load_hist(hp);
x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
return i;
}
}
x_putc(BEL);
x_histp = histptr;
return -1;
}
/* return position of first match of pattern in string, else -1 */
static int
x_match(str, pat)
char *str, *pat;
{
if (*pat == '^') {
return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
} else {
char *q = strstr(str, pat);
return (q == NULL) ? -1 : q - str;
}
}
static int
x_del_line(c) {
int i, j;
*xep = 0;
i = xep- xbuf;
j = x_size_str(xbuf);
xcp = xbuf;
x_push(i);
xlp = xbp = xep = xbuf;
xlp_valid = TRUE;
*xcp = 0;
xmp = NULL;
if ( c != -1 )
x_redraw(j);
return KSTD;
}
static int
x_mv_end(c) {
x_goto(xep);
return KSTD;
}
static int
x_mv_begin(c) {
x_goto(xbuf);
return KSTD;
}
static int
x_draw_line(c)
{
x_redraw(-1);
return KSTD;
}
void
x_redraw(limit)
int limit;
{
int i, j;
char *cp;
x_adj_ok = 0;
if (limit == -1)
x_putc('\n');
else
x_putc('\r');
x_flush();
if (xbp == xbuf)
{
pprompt(prompt);
x_col = promptlen(prompt);
}
x_displen = x_cols - 2 - x_col;
xlp_valid = FALSE;
cp = x_lastcp();
x_zots(xbp);
if (xbp != xbuf || xep > xlp)
limit = x_cols;
if (limit >= 0)
{
if (xep > xlp)
i = 0; /* we fill the line */
else
i = limit - (xlp - xbp);
for (j = 0; j < i && x_col < (x_cols - 2); j++)
x_putc(' ');
i = ' ';
if (xep > xlp) /* more off screen */
{
if (xbp > xbuf)
i = '*';
else
i = '>';
}
else
if (xbp > xbuf)
i = '<';
x_putc(i);
j++;
while (j--)
x_putc('\b');
}
for (cp = xlp; cp > xcp; )
x_bs(*--cp);
x_adj_ok = 1;
_D_(x_flush();)
return;
}
static int
x_transpose(c) {
char tmp;
if (xcp == xbuf) {
x_putc(BEL);
return KSTD;
} else if (xcp == xep) {
if (xcp - xbuf == 1) {
x_putc(BEL);
return KSTD;
}
x_bs(xcp[-1]);
x_bs(xcp[-2]);
x_zotc(xcp[-1]);
x_zotc(xcp[-2]);
tmp = xcp[-1];
xcp[-1] = xcp[-2];
xcp[-2] = tmp;
} else {
x_bs(xcp[-1]);
x_zotc(xcp[0]);
x_zotc(xcp[-1]);
tmp = xcp[-1];
xcp[-1] = xcp[0];
xcp[0] = tmp;
x_bs(xcp[0]);
}
return KSTD;
}
static int
x_literal(c) {
x_curprefix = -1;
return KSTD;
}
static int
x_meta1(c) {
x_curprefix = 1;
return KPREF;
}
static int
x_meta2(c) {
x_curprefix = 2;
return KPREF;
}
static int
x_meta3(c) {
x_curprefix = 3;
return KPREF;
}
static int
x_kill(c) {
int i;
i = xep - xcp;
xlp = xcp;
xlp_valid = TRUE;
x_push(i);
x_delete(i);
return KSTD;
}
static void
x_push(nchars) {
char *cp;
cp = alloc((size_t)(nchars+1), AEDIT);
memmove(cp, xcp, nchars);
cp[nchars] = 0;
if (killstack[killsp])
afree((void *)killstack[killsp], AEDIT);
killstack[killsp] = cp;
killsp = (killsp + 1) % KILLSIZE;
}
static int
x_yank(c) {
if (killsp == 0)
killtp = KILLSIZE;
else
killtp = killsp;
killtp --;
if (killstack[killtp] == 0) {
x_puts("\nnothing to yank");
x_redraw(-1);
return KSTD;
}
xmp = xcp;
x_ins(killstack[killtp]);
return KSTD;
}
static int
x_meta_yank(c) {
int len;
if (x_last_command != x_yank && x_last_command != x_meta_yank) {
x_puts("\nyank something first");
x_redraw(-1);
return KSTD;
}
len = strlen(killstack[killtp]);
x_goto(xcp - len);
x_delete(len);
do {
if (killtp == 0)
killtp = KILLSIZE - 1;
else
killtp--;
} while (killstack[killtp] == 0);
x_ins(killstack[killtp]);
return KSTD;
}
static int
x_abort(c) {
/* x_zotc(c); */
xlp = xep = xcp = xbp = xbuf;
xlp_valid = TRUE;
*xcp = 0;
x_del_line(-1);
return KINTR;
}
static int
x_error(c) {
x_putc(BEL);
return KSTD;
}
static int
x_stuffreset(c)
{
#ifdef TIOCSTI
(void)x_stuff(c);
return KINTR;
#else
x_zotc(c);
xlp = xcp = xep = xbp = xbuf;
xlp_valid = TRUE;
*xcp = 0;
x_redraw(-1);
return KSTD;
#endif
}
static int
x_stuff(c)
{
#if 0 || defined TIOCSTI
char ch = c;
bool_t savmode = x_mode(FALSE);
(void)ioctl(ttyfd, TIOCSTI, &ch);
(void)x_mode(savmode);
x_redraw(-1);
#endif
return KSTD;
}
static void
x_mapin(cp)
char *cp;
{
char *op;
op = cp;
while (*cp) {
/* XXX -- should handle \^ escape? */
if (*cp == '^') {
cp++;
if (*cp == '0')
*op++ = 0xE0;
else if (*cp >= '?') /* includes '?'; ASCII */
*op++ = CTRL(*cp);
else {
*op++ = '^';
cp--;
}
} else
*op++ = *cp;
cp++;
}
*op = 0;
}
static char *
x_mapout(c)
int c;
{
static char buf[8];
register char *p = buf;
if (c < ' ' || c == 0x7F) { /* ASCII */
*p++ = '^';
*p++ = (c == 0x7F) ? '?' : (c | 0x40);
}
else if (c == 0xE0) {
*p++ = '^';
*p++ = '0';
} else
*p++ = c;
*p = 0;
return buf;
}
static void
x_print(prefix, key)
int prefix, key;
{
if (prefix == 1)
shellf("%s", x_mapout(x_prefix1));
if (prefix == 2)
shellf("%s", x_mapout(x_prefix2));
if (prefix == 3)
shellf("%s", x_mapout(x_prefix3));
shellf("%s = ", x_mapout(key));
if (x_tab[prefix][key]->xf_func != x_ins_string)
shellf("%s\n", x_tab[prefix][key]->xf_name);
else
shellf("'%s'\n", x_atab[prefix][key]);
}
void
x_bind(a1, a2, macro)
char *a1, *a2;
int macro; /* bind -m */
{
struct x_ftab *fp;
int prefix, key;
char *sp = NULL;
if (x_tab == NULL)
return;
/* errorf("cannot bind, not a tty\n"); */
if (a1 == NULL) {
for (prefix = 0; prefix < X_TABS; prefix++)
for (key = 0; key < X_TABSZ; key++) {
fp = x_tab[prefix][key];
if (fp == NULL ||
fp->xf_func == x_insert || fp->xf_func == x_error)
continue;
x_print(prefix, key);
}
return;
}
x_mapin(a1);
prefix = key = 0;
for (;; a1++) {
key = *a1;
if (x_tab[prefix][key]->xf_func == x_meta1)
prefix = 1;
else
if (x_tab[prefix][key]->xf_func == x_meta2)
prefix = 2;
else
if (x_tab[prefix][key]->xf_func == x_meta3)
prefix = 3;
else
break;
}
if (a2 == NULL) {
x_print(prefix, key);
return;
}
if (*a2 == 0)
fp = xft_insert;
else if (!macro) {
for (fp = x_ftab; fp->xf_func; fp++)
if (strcmp(fp->xf_name, a2) == 0)
break;
if (fp->xf_func == NULL || (fp->xf_flags & XF_NOBIND))
errorf("%s: no such function\n", a2);
if (fp->xf_func == x_meta1)
x_prefix1 = key;
if (fp->xf_func == x_meta2)
x_prefix2 = key;
if (fp->xf_func == x_meta3)
x_prefix3 = key;
} else {
fp = xft_ins_string;
x_mapin(a2);
sp = strsave(a2, AEDIT);
}
if ((x_tab[prefix][key]->xf_flags & XF_ALLOC) && x_atab[prefix][key])
afree((void *)x_atab[prefix][key], AEDIT);
x_tab[prefix][key] = fp;
x_atab[prefix][key] = sp;
}
void
x_init_emacs()
{
register int i, j;
struct x_ftab *fp;
ainit(AEDIT);
x_tab = (struct x_ftab *(*)[X_TABSZ]) alloc(sizeofN(*x_tab, X_TABS), AEDIT);
for (j = 0; j < X_TABSZ; j++)
x_tab[0][j] = xft_insert;
for (i = 1; i < X_TABS; i++)
for (j = 0; j < X_TABSZ; j++)
x_tab[i][j] = xft_error;
for (fp = x_ftab; fp->xf_func; fp++)
if (fp->xf_db_char || fp->xf_db_tab)
x_tab[fp->xf_db_tab][fp->xf_db_char] = fp;
x_atab = (char *(*)[X_TABSZ]) alloc(sizeofN(*x_atab, X_TABS), AEDIT);
for (i = 1; i < X_TABS; i++)
for (j = 0; j < X_TABSZ; j++)
x_atab[i][j] = NULL;
}
static int
x_clear_scr(c)
{
write(1, "\033[H\033[J", 6);
x_redraw(0);
return KSTD;
}
void
x_emacs_keys(erase, kill, werase, intr, quit)
int erase, kill, werase, intr, quit;
{
x_tab[0][erase] = xft_erase;
x_tab[0][kill] = xft_kill;
x_tab[0][werase] = xft_werase;
x_tab[0][intr] = xft_intr;
x_tab[0][quit] = xft_quit;
x_tab[1][erase] = xft_werase;
}
static int
x_set_mark(c) {
xmp = xcp;
return KSTD;
}
static int
x_kill_region(c) {
int rsize;
char *xr;
if (xmp == NULL) {
x_putc(BEL);
return KSTD;
}
if (xmp > xcp) {
rsize = xmp - xcp;
xr = xcp;
} else {
rsize = xcp - xmp;
xr = xmp;
}
x_goto(xr);
x_push(rsize);
x_delete(rsize);
xmp = xr;
return KSTD;
}
static int
x_xchg_point_mark(c) {
char *tmp;
if (xmp == NULL) {
x_putc(BEL);
return KSTD;
}
tmp = xmp;
xmp = xcp;
x_goto( tmp );
return KSTD;
}
#if 0
static int
x_copy_arg(c) {
char *last;
if ((last = strval(local("_"))) && *last)
x_ins(last);
return KSTD;
}
#endif
static int
x_noop(c) {
return KNULL;
}
#ifdef SILLY
static int
x_game_of_life(c) {
char newbuf [256+1];
register char *ip, *op;
int i, len;
i = xep - xbuf;
*xep = 0;
len = x_size_str(xbuf);
xcp = xbp = xbuf;
memmove(newbuf+1, xbuf, i);
newbuf[0] = 'A';
newbuf[i] = 'A';
for (ip = newbuf+1, op = xbuf; --i >= 0; ip++, op++) {
/* Empty space */
if (*ip < '@' || *ip == '_' || *ip == 0x7F) {
/* Two adults, make whoopee */
if (ip[-1] < '_' && ip[1] < '_') {
/* Make kid look like parents. */
*op = '`' + ((ip[-1] + ip[1])/2)%32;
if (*op == 0x7F) /* Birth defect */
*op = '`';
}
else
*op = ' '; /* nothing happens */
continue;
}
/* Child */
if (*ip > '`') {
/* All alone, dies */
if (ip[-1] == ' ' && ip[1] == ' ')
*op = ' ';
else /* Gets older */
*op = *ip-'`'+'@';
continue;
}
/* Adult */
/* Overcrowded, dies */
if (ip[-1] >= '@' && ip[1] >= '@') {
*op = ' ';
continue;
}
*op = *ip;
}
*op = 0;
x_redraw(len);
return KSTD;
}
#endif
/*
* File/command name completion routines
*/
/* type: 0 for list, 1 for completion */
static XPtrV words;
static void
add_stash(dirnam, name)
char *dirnam; /* directory name, if file */
char *name;
{
char *cp;
register int type = 0; /* '*' if executable, '/' if directory, else 0 */
register int len = strlen(name);
/* determine file type */
if (dirnam) {
struct stat statb;
char *buf = alloc((size_t)(strlen(dirnam)+len+2), ATEMP);
if (strcmp(dirnam, ".") == 0)
*buf = '\0';
else if (strcmp(dirnam, DIRSEPSTR) == 0)
(void)strcpy(buf, DIRSEPSTR);
else
(void)strcat(strcpy(buf, dirnam), DIRSEPSTR);
(void)strcat(buf, name);
if (stat(buf, &statb)==0)
if (S_ISDIR(statb.st_mode))
type = '/';
else if (S_ISREG(statb.st_mode) &&
(statb.st_mode & S_IEXEC) != 0)
type = '*';
if (type)
++len;
afree((void *)buf, ATEMP);
}
if (len > x_maxlen)
x_maxlen = len;
/* stash name for later sorting */
cp = alloc((size_t)(len+1), ATEMP);
(void)strcpy(cp = alloc((size_t)(len+1), ATEMP), name);
if (dirnam && type) { /* append file type indicator */
cp[len-1] = type;
cp[len] = '\0';
}
XPput(words, cp);
}
static void
list_stash()
{
register char **array, **record;
int items = 0, tabstop, loc, nrows, jump, offset;
items = XPsize(words);
array = (char**) XPptrv(words);
if (items == 0)
return;
qsortp(XPptrv(words), (size_t)XPsize(words), xstrcmp);
/* print names */
x_maxlen = (x_maxlen/8 + 1) * 8; /* column width */
nrows = (items-1) / (x_cols/x_maxlen) + 1;
for (offset = 0; offset < nrows; ++offset) {
tabstop = loc = 0;
x_putc('\n');
for (jump = 0; offset+jump < items; jump += nrows) {
if (jump)
while (loc < tabstop) {
x_putc('\t');
loc = (loc/8 + 1) * 8;
}
record = array + (offset + jump);
x_puts(*record);
loc += strlen(*record);
tabstop += x_maxlen; /* next tab stop */
afree((void *)*record, ATEMP);
}
}
afree((void*)array, ATEMP);
x_redraw(-1);
}
static int
x_comp_comm(c) {
compl_command(1);
return KSTD;
}
static int
x_list_comm(c) {
compl_command(0);
return KSTD;
}
static int
x_complete(c) {
compl_dec(1);
return KSTD;
}
static int
x_enumerate(c) {
compl_dec(0);
return KSTD;
}
static int
x_comp_file(c) {
compl_file(1);
return KSTD;
}
static int
x_list_file(c) {
compl_file(0);
return KSTD;
}
static int
x_list_jobs(c)
int c;
{
j_jobs();
x_redraw(-1);
return KSTD;
}
static int
x_comp_list(c) {
compl_dec(2);
return KSTD;
}
static void compl_dec(type) { char *cp; cp = xcp;
while (cp != xbuf && !iscfs(*cp))
cp--;
if (cp == xbuf && index_sep(cp) == NULL)
compl_command(type);
else
compl_file(type);
}
static void
compl_file(type)
{
char *str;
register char *cp, *xp;
char *lastp;
char *dirnam;
char buf [256+1];
char bug [256+1];
DIR *dirp;
struct dirent *dp;
long loc = -1;
int len;
int multi = 0;
/* type == 0 for list, 1 for complete and 2 for complete-list */
str = xcp;
cp = buf;
xp = str;
while (xp != xbuf) {
--xp;
if (iscfs(*xp)) {
xp++;
break;
}
}
if (digit(*xp) && (xp[1] == '<' || xp[1] == '>'))
xp++;
while (*xp == '<' || *xp == '>')
xp++;
if (type) { /* for complete */
while (*xcp && !iscfs(*xcp))
x_zotc(*xcp++);
}
if (type != 1) { /* for list */
x_maxlen = 0;
XPinit(words, 16);
}
while (*xp && !iscfs(*xp))
*cp++ = *xp++;
*cp = 0;
strcpy(buf, cp = substitute(buf, DOTILDE));
afree((void*)cp, ATEMP);
lastp = rindex_sep(buf);
if (lastp)
*lastp = 0;
dirnam = (lastp == NULL) ? "." : (lastp == buf) ? DIRSEPSTR : buf;
dirp = opendir(dirnam);
if (dirp == NULL) {
x_putc(BEL);
return;
}
if (lastp == NULL)
lastp = buf;
else
lastp++;
len = strlen(lastp);
while ((dp = readdir(dirp)) != NULL) {
cp = dp->d_name;
if (cp[0] == '.' &&
(cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
continue; /* always ignore . and .. */
if (strncmp(lastp, cp, len) == 0) {
if (type /* for complete */) {
if (loc == -1) {
(void)strcpy(bug, cp);
loc = strlen(cp);
} else {
multi = 1;
loc = strmatch(bug, cp);
bug[loc] = 0;
}
}
if (type != 1) { /* for list */
add_stash(dirnam, cp);
}
}
}
(void)closedir(dirp);
if (type) { /* for complete */
if (loc < 0 ||
(loc == 0 && type != 2)) {
x_putc(BEL);
return;
}
cp = bug + len;
x_ins(cp);
if (!multi) {
struct stat statb;
if (lastp == buf)
buf[0] = 0;
else if (lastp == buf + 1) {
buf[1] = 0;
buf[0] = DIRSEP;
} else
(void)strcat(buf, DIRSEPSTR);
(void)strcat(buf, bug);
if (stat(buf, &statb) == 0 && S_ISDIR(statb.st_mode))
x_ins("/" /* DIRSEPSTR */);
/* conflict with \ at end of line in OS/2 mode ! */
else
x_ins(" ");
}
}
if (type == 0 || /* if list */
(type == 2 && multi)) { /* or complete-list and ambiguous */
list_stash();
}
}
static void
compl_command(type)
{
register struct tbl *tp;
char *str;
char buf [256+1];
char bug [256+1];
char *xp;
char *cp;
int len;
int multi;
int loc;
/* type == 0 for list, 1 for complete and 2 for complete-list */
str = xcp;
cp = buf;
xp = str;
while (xp != xbuf) {
--xp;
if (iscfs(*xp)) {
xp++;
break;
}
}
if (type) /* for complete */
while (*xcp && !iscfs(*xcp))
x_zotc(*xcp++);
if (type != 1) { /* for list */
x_maxlen = 0;
XPinit(words, 16);
}
while (*xp && !iscfs(*xp))
*cp++ = *xp++;
*cp = 0;
len = strlen(buf);
loc = -1;
multi = 0;
for (twalk(&commands); (tp = tnext()) != NULL; ) {
int klen;
if (!(tp->flag&ISSET))
continue;
klen = strlen(tp->name);
if (klen < len)
continue;
if (strncmp(buf, tp->name, len) ==0) {
if (type) { /* for complete */
if (loc == -1) {
(void)strcpy(bug, tp->name);
loc = klen;
} else {
multi = 1;
loc = strmatch(bug, tp->name);
bug[loc] = 0;
}
}
if (type != 1) { /* for list */
add_stash((char *)0, tp->name);
}
}
}
if (type) { /* for complete */
if (loc < 0 ||
(loc == 0 && type != 2)) {
x_putc(BEL);
return;
}
cp = bug + len;
x_ins(cp);
if (!multi)
x_ins(" ");
else if (type == 2) /* complete and list rest */
list_stash();
}
if (type == 0 || /* if list */
(type == 2 && multi)) { /* or complete-list and ambiguous */
list_stash();
}
}
static int
strmatch(s1, s2)
register char *s1, *s2;
{
register char *p;
for (p = s1; *p == *s2++ && *p != 0; p++)
;
return p - s1;
}
/* NAME:
* x_set_arg - set an arg value for next function
*
* DESCRIPTION:
* This is a simple implementation of M-[0-9].
*
* RETURN VALUE:
* KSTD
*/
static int
x_set_arg(c)
int c;
{
if ((x_arg = (c &= CMASK) - '0') < 0 || x_arg > 9)
{
x_arg = 1;
x_putc(BEL);
}
return KSTD;
}
/* NAME:
* x_prev_histword - recover word from prev command
*
* DESCRIPTION:
* This function recovers the last word from the previous
* command and inserts it into the current edit line. If a
* numeric arg is supplied then the n'th word from the
* start of the previous command is used.
*
* Bound to M-.
*
* RETURN VALUE:
* KSTD
*/
static int
x_prev_histword()
{
register char *rcp;
char *cp;
char **hp;
hp = x_histp-1;
if (hp < history || hp > histptr)
{
x_putc(BEL);
return KSTD;
}
cp = *hp;
if (x_last_command != x_set_arg)
{
rcp = &cp[strlen(cp) - 1];
/*
* ignore white-space after the last word
*/
while (rcp > cp && iscfs(*rcp))
rcp--;
while (rcp > cp && !iscfs(*rcp))
rcp--;
if (iscfs(*rcp))
rcp++;
x_ins(rcp);
}
else
{
int c;
rcp = cp;
/*
* ignore white-space at start of line
*/
while (*rcp && iscfs(*rcp))
rcp++;
while (x_arg-- > 1)
{
while (*rcp && !iscfs(*rcp))
rcp++;
while (*rcp && iscfs(*rcp))
rcp++;
}
cp = rcp;
while (*rcp && !iscfs(*rcp))
rcp++;
c = *rcp;
*rcp = '\0';
x_ins(cp);
*rcp = c;
}
return KSTD;
}
/* NAME:
* x_fold_case - convert word to UPPER/lower case
*
* DESCRIPTION:
* This function is used to implement M-u,M-l and M-c
* to upper case, lower case or Capitalize words.
*
* RETURN VALUE:
* None
*/
static int
x_fold_case(c)
int c;
{
register char *cp = xcp;
if (cp == xep)
{
x_putc(BEL);
return 0;
}
c &= 0137; /* strip prefixes and case */
if (x_last_command != x_set_arg)
x_arg = 1;
while (x_arg--)
{
/*
* fisrt skip over any white-space
*/
while (cp != xep && ismfs(*cp))
{
cp++;
}
/*
* do the first char on its own since it may be
* a different action than for the rest.
*/
if (cp != xep)
{
if (c == 'L') /* M-l */
{
if (isupper(*cp))
*cp = tolower(*cp);
}
else /* M-u or M-c */
{
if (islower(*cp))
*cp = toupper(*cp);
}
cp++;
}
/*
* now for the rest of the word
*/
while (cp != xep && !ismfs(*cp))
{
if (c == 'U') /* M-u */
{
if (islower(*cp))
*cp = toupper(*cp);
}
else /* M-l or M-c */
{
if (isupper(*cp))
*cp = tolower(*cp);
}
cp++;
}
}
x_goto(cp);
return 0;
}
/* NAME:
* x_lastcp - last visible char
*
* SYNOPSIS:
* x_lastcp()
*
* DESCRIPTION:
* This function returns a pointer to that char in the
* edit buffer that will be the last displayed on the
* screen. The sequence:
*
* for (cp = x_lastcp(); cp > xcp; cp)
* x_bs(*--cp);
*
* Will position the cursor correctly on the screen.
*
* RETURN VALUE:
* cp or NULL
*/
char *
x_lastcp()
{
register char *rcp;
register int i;
if (!xlp_valid)
{
for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
i += x_size(*rcp);
xlp = rcp;
}
xlp_valid = TRUE;
return (xlp);
}
#endif /* EDIT */