You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
5.9 KiB
278 lines
5.9 KiB
#include <sys/wait.h> |
|
#include <ctype.h> |
|
#include <dirent.h> |
|
#include <err.h> |
|
#include <limits.h> |
|
#include <locale.h> |
|
#include <signal.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
|
|
#include <histedit.h> |
|
|
|
|
|
static int continuation; |
|
volatile sig_atomic_t gotsig; |
|
static const char hfile[] = ".whistory"; |
|
|
|
static wchar_t * |
|
prompt(EditLine *el) |
|
{ |
|
static wchar_t a[] = L"\1\033[7m\1Edit$\1\033[0m\1 "; |
|
static wchar_t b[] = L"Edit> "; |
|
|
|
return continuation ? b : a; |
|
} |
|
|
|
|
|
static void |
|
sig(int i) |
|
{ |
|
gotsig = i; |
|
} |
|
|
|
const char * |
|
my_wcstombs(const wchar_t *wstr) |
|
{ |
|
static struct { |
|
char *str; |
|
int len; |
|
} buf; |
|
|
|
int needed = wcstombs(0, wstr, 0) + 1; |
|
if (needed > buf.len) { |
|
buf.str = malloc(needed); |
|
buf.len = needed; |
|
} |
|
wcstombs(buf.str, wstr, needed); |
|
buf.str[needed - 1] = 0; |
|
|
|
return buf.str; |
|
} |
|
|
|
|
|
static unsigned char |
|
complete(EditLine *el, int ch) |
|
{ |
|
DIR *dd = opendir("."); |
|
struct dirent *dp; |
|
const wchar_t *ptr; |
|
char *buf, *bptr; |
|
const LineInfoW *lf = el_wline(el); |
|
int len, mblen, i; |
|
unsigned char res = 0; |
|
wchar_t dir[1024]; |
|
|
|
/* Find the last word */ |
|
for (ptr = lf->cursor -1; !iswspace(*ptr) && ptr > lf->buffer; --ptr) |
|
continue; |
|
len = lf->cursor - ++ptr; |
|
|
|
/* Convert last word to multibyte encoding, so we can compare to it */ |
|
wctomb(NULL, 0); /* Reset shift state */ |
|
mblen = MB_LEN_MAX * len + 1; |
|
buf = bptr = malloc(mblen); |
|
if (buf == NULL) |
|
err(1, "malloc"); |
|
for (i = 0; i < len; ++i) { |
|
/* Note: really should test for -1 return from wctomb */ |
|
bptr += wctomb(bptr, ptr[i]); |
|
} |
|
*bptr = 0; /* Terminate multibyte string */ |
|
mblen = bptr - buf; |
|
|
|
/* Scan directory for matching name */ |
|
for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { |
|
if (mblen > strlen(dp->d_name)) |
|
continue; |
|
if (strncmp(dp->d_name, buf, mblen) == 0) { |
|
mbstowcs(dir, &dp->d_name[mblen], |
|
sizeof(dir) / sizeof(*dir)); |
|
if (el_winsertstr(el, dir) == -1) |
|
res = CC_ERROR; |
|
else |
|
res = CC_REFRESH; |
|
break; |
|
} |
|
} |
|
|
|
closedir(dd); |
|
free(buf); |
|
return res; |
|
} |
|
|
|
|
|
int |
|
main(int argc, char *argv[]) |
|
{ |
|
EditLine *el = NULL; |
|
int numc, ncontinuation; |
|
const wchar_t *line; |
|
TokenizerW *tok; |
|
HistoryW *hist; |
|
HistEventW ev; |
|
#ifdef DEBUG |
|
int i; |
|
#endif |
|
|
|
setlocale(LC_ALL, ""); |
|
|
|
(void)signal(SIGINT, sig); |
|
(void)signal(SIGQUIT, sig); |
|
(void)signal(SIGHUP, sig); |
|
(void)signal(SIGTERM, sig); |
|
|
|
hist = history_winit(); /* Init built-in history */ |
|
history_w(hist, &ev, H_SETSIZE, 100); /* Remember 100 events */ |
|
history_w(hist, &ev, H_LOAD, hfile); |
|
|
|
tok = tok_winit(NULL); /* Init the tokenizer */ |
|
|
|
el = el_init(argv[0], stdin, stdout, stderr); |
|
|
|
el_wset(el, EL_EDITOR, L"vi"); /* Default editor is vi */ |
|
el_wset(el, EL_SIGNAL, 1); /* Handle signals gracefully */ |
|
el_wset(el, EL_PROMPT_ESC, prompt, '\1'); /* Set the prompt function */ |
|
|
|
el_wset(el, EL_HIST, history_w, hist); /* FIXME - history_w? */ |
|
|
|
/* Add a user-defined function */ |
|
el_wset(el, EL_ADDFN, L"ed-complete", L"Complete argument", complete); |
|
|
|
/* Bind <tab> to it */ |
|
el_wset(el, EL_BIND, L"^I", L"ed-complete", NULL); |
|
|
|
/* |
|
* Bind j, k in vi command mode to previous and next line, instead |
|
* of previous and next history. |
|
*/ |
|
el_wset(el, EL_BIND, L"-a", L"k", L"ed-prev-line", NULL); |
|
el_wset(el, EL_BIND, L"-a", L"j", L"ed-next-line", NULL); |
|
|
|
/* Source the user's defaults file. */ |
|
el_source(el, NULL); |
|
|
|
while((line = el_wgets(el, &numc)) != NULL && numc != 0) { |
|
int ac, cc, co, rc; |
|
const wchar_t **av; |
|
|
|
const LineInfoW *li; |
|
li = el_wline(el); |
|
|
|
#ifdef DEBUG |
|
(void)fwprintf(stderr, L"==> got %d %ls", numc, line); |
|
(void)fwprintf(stderr, L" > li `%.*ls_%.*ls'\n", |
|
(li->cursor - li->buffer), li->buffer, |
|
(li->lastchar - 1 - li->cursor), |
|
(li->cursor >= li->lastchar) ? L"" : li->cursor); |
|
#endif |
|
|
|
if (gotsig) { |
|
(void)fprintf(stderr, "Got signal %d.\n", (int)gotsig); |
|
gotsig = 0; |
|
el_reset(el); |
|
} |
|
|
|
if(!continuation && numc == 1) |
|
continue; /* Only got a linefeed */ |
|
|
|
ac = cc = co = 0; |
|
ncontinuation = tok_wline(tok, li, &ac, &av, &cc, &co); |
|
if (ncontinuation < 0) { |
|
(void) fprintf(stderr, "Internal error\n"); |
|
continuation = 0; |
|
continue; |
|
} |
|
|
|
#ifdef DEBUG |
|
(void)fprintf(stderr, " > nc %d ac %d cc %d co %d\n", |
|
ncontinuation, ac, cc, co); |
|
#endif |
|
history_w(hist, &ev, continuation ? H_APPEND : H_ENTER, line); |
|
|
|
continuation = ncontinuation; |
|
ncontinuation = 0; |
|
if(continuation) |
|
continue; |
|
|
|
#ifdef DEBUG |
|
for (i = 0; i < ac; ++i) { |
|
(void)fwprintf(stderr, L" > arg# %2d ", i); |
|
if (i != cc) |
|
(void)fwprintf(stderr, L"`%ls'\n", av[i]); |
|
else |
|
(void)fwprintf(stderr, L"`%.*ls_%ls'\n", |
|
co, av[i], av[i] + co); |
|
} |
|
#endif |
|
|
|
if (wcscmp (av[0], L"history") == 0) { |
|
switch(ac) { |
|
case 1: |
|
for(rc = history_w(hist, &ev, H_LAST); |
|
rc != -1; |
|
rc = history_w(hist, &ev, H_PREV)) |
|
(void)fwprintf(stdout, L"%4d %ls", |
|
ev.num, ev.str); |
|
break; |
|
case 2: |
|
if (wcscmp(av[1], L"clear") == 0) |
|
history_w(hist, &ev, H_CLEAR); |
|
else |
|
goto badhist; |
|
break; |
|
case 3: |
|
if (wcscmp(av[1], L"load") == 0) |
|
history_w(hist, &ev, H_LOAD, |
|
my_wcstombs(av[2])); |
|
else if (wcscmp(av[1], L"save") == 0) |
|
history_w(hist, &ev, H_SAVE, |
|
my_wcstombs(av[2])); |
|
else |
|
goto badhist; |
|
break; |
|
badhist: |
|
default: |
|
(void)fprintf(stderr, |
|
"Bad history arguments\n"); |
|
break; |
|
} |
|
} else if (el_wparse(el, ac, av) == -1) { |
|
switch (fork()) { |
|
case 0: { |
|
Tokenizer *ntok = tok_init(NULL); |
|
int nargc; |
|
const char **nav; |
|
tok_str(ntok, my_wcstombs(line), &nargc, &nav); |
|
execvp(nav[0],(char **)nav); |
|
perror(nav[0]); |
|
_exit(1); |
|
/* NOTREACHED */ |
|
break; |
|
} |
|
case -1: |
|
perror("fork"); |
|
break; |
|
default: |
|
if (wait(&rc) == -1) |
|
perror("wait"); |
|
(void)fprintf(stderr, "Exit %x\n", rc); |
|
break; |
|
} |
|
} |
|
|
|
tok_wreset(tok); |
|
} |
|
|
|
el_end(el); |
|
tok_wend(tok); |
|
history_w(hist, &ev, H_SAVE, hfile); |
|
history_wend(hist); |
|
|
|
fprintf(stdout, "\n"); |
|
return 0; |
|
} |
|
|
|
|
|
|