From afe1b00e16c7efc5c93c958fa4f357325977a2a1 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 21 May 2002 11:10:13 +0000 Subject: 17141 plus mods: add `zle -F fd handler' feature. --- Src/Zle/zle_main.c | 195 ++++++++++++++++++++++++++++++++++++++------------- Src/Zle/zle_thingy.c | 103 +++++++++++++++++++++++++++ 2 files changed, 250 insertions(+), 48 deletions(-) (limited to 'Src/Zle') diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 5af18fbac..56797a9a9 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -141,6 +141,17 @@ mod_export char *zlenoargs[1] = { NULL }; static int delayzsetterm; #endif +/* + * File descriptors we are watching as well as the terminal fd. + * These are all for reading; we don't watch for writes or exceptions. + */ +/**/ +int nwatch; /* Number of fd's we are watching */ +/**/ +int *watch_fds; /* The list of fds, not terminated! */ +/**/ +char **watch_funcs; /* The corresponding functions to call, normal array */ + /* set up terminal */ /**/ @@ -324,86 +335,174 @@ breakread(int fd, char *buf, int n) # define read breakread #endif -/**/ -mod_export int -getkey(int keytmout) +static int +raw_getkey(int keytmout, char *cptr) { - char cc; - unsigned int ret; long exp100ths; - int die = 0, r, icnt = 0; - int old_errno = errno, obreaks = breaks; - + int ret; #ifdef HAVE_SELECT fd_set foofd; - #else # ifdef HAS_TIO struct ttyinfo ti; - # endif #endif - if (kungetct) - ret = STOUC(kungetbuf[--kungetct]); - else { + /* + * Handle timeouts and watched fd's. We only do one at once; + * key timeouts take precedence. This saves tricky timing + * problems with the key timeout. + */ + if ((nwatch || keytmout) #ifdef FIONREAD - if (delayzsetterm) { - int val; - ioctl(SHTTY, FIONREAD, (char *)&val); - if (!val) - zsetterm(); - } + && ! delayzsetterm #endif - if (keytmout -#ifdef FIONREAD - && ! delayzsetterm -#endif - ) { - if (keytimeout > 500) - exp100ths = 500; - else if (keytimeout > 0) - exp100ths = keytimeout; - else - exp100ths = 0; + ) { + if (!keytmout || keytimeout <= 0) + exp100ths = 0; + else if (keytimeout > 500) + exp100ths = 500; + else + exp100ths = keytimeout; #ifdef HAVE_SELECT + if (!keytmout || exp100ths) { + struct timeval *tvptr = NULL; + struct timeval expire_tv; + int i, fdmax = SHTTY, errtry = 0; if (exp100ths) { - struct timeval expire_tv; - expire_tv.tv_sec = exp100ths / 100; expire_tv.tv_usec = (exp100ths % 100) * 10000L; + tvptr = &expire_tv; + } + do { + int selret; FD_ZERO(&foofd); FD_SET(SHTTY, &foofd); - if (select(SHTTY+1, (SELECT_ARG_2_T) & foofd, - NULL, NULL, &expire_tv) <= 0) - return EOF; - } + if (!keytmout && !errtry) { + for (i = 0; i < nwatch; i++) { + int fd = watch_fds[i]; + FD_SET(fd, &foofd); + if (fd > fdmax) + fdmax = fd; + } + } + selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd, + NULL, NULL, tvptr); + /* + * Make sure a user interrupt gets passed on straight away. + */ + if (selret < 0 && errflag) + return selret; + /* + * Try to avoid errors on our special fd's from + * messing up reads from the terminal. Try first + * with all fds, then try unsetting the special ones. + */ + if (selret < 0 && !keytmout && !errtry) { + errtry = 1; + continue; + } + if (selret == 0) { + /* Special value -2 signals nothing ready */ + return -2; + } else if (selret < 0) + return selret; + if (!keytmout && nwatch) { + /* + * Copy the details of the watch fds in case the + * user decides to delete one from inside the + * handler function. + */ + int lnwatch = nwatch; + int *lwatch_fds = zalloc(lnwatch*sizeof(int)); + char **lwatch_funcs = zarrdup(watch_funcs); + memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(int)); + for (i = 0; i < lnwatch; i++) { + if (FD_ISSET(lwatch_fds[i], &foofd)) { + /* Handle the fd. */ + LinkList funcargs = znewlinklist(); + zaddlinknode(funcargs, ztrdup(lwatch_funcs[i])); + { + char buf[BDIGBUFSIZE]; + convbase(buf, lwatch_fds[i], 10); + zaddlinknode(funcargs, ztrdup(buf)); + } + + callhookfunc(lwatch_funcs[i], funcargs); + if (errflag) { + /* No sensible way of handling errors here */ + errflag = 0; + /* + * Paranoia: don't run the hooks again this + * time. + */ + errtry = 1; + } + freelinklist(funcargs, freestr); + } + } + /* Function may have invalidated the display. */ + if (resetneeded) + zrefresh(); + zfree(lwatch_fds, lnwatch*sizeof(int)); + freearray(lwatch_funcs); + } + } while (!FD_ISSET(SHTTY, &foofd)); + } #else # ifdef HAS_TIO - ti = shttyinfo; - ti.tio.c_lflag &= ~ICANON; - ti.tio.c_cc[VMIN] = 0; - ti.tio.c_cc[VTIME] = exp100ths / 10; + ti = shttyinfo; + ti.tio.c_lflag &= ~ICANON; + ti.tio.c_cc[VMIN] = 0; + ti.tio.c_cc[VTIME] = exp100ths / 10; # ifdef HAVE_TERMIOS_H - tcsetattr(SHTTY, TCSANOW, &ti.tio); + tcsetattr(SHTTY, TCSANOW, &ti.tio); # else - ioctl(SHTTY, TCSETA, &ti.tio); + ioctl(SHTTY, TCSETA, &ti.tio); # endif - r = read(SHTTY, &cc, 1); + ret = read(SHTTY, cptr, 1); # ifdef HAVE_TERMIOS_H - tcsetattr(SHTTY, TCSANOW, &shttyinfo.tio); + tcsetattr(SHTTY, TCSANOW, &shttyinfo.tio); # else - ioctl(SHTTY, TCSETA, &shttyinfo.tio); + ioctl(SHTTY, TCSETA, &shttyinfo.tio); # endif - return (r <= 0) ? EOF : cc; + return (ret <= 0) ? ret : *cptr; # endif #endif + } + + ret = read(SHTTY, cptr, 1); + + return ret; +} + +/**/ +mod_export int +getkey(int keytmout) +{ + char cc; + unsigned int ret; + int die = 0, r, icnt = 0; + int old_errno = errno, obreaks = breaks; + + if (kungetct) + ret = STOUC(kungetbuf[--kungetct]); + else { +#ifdef FIONREAD + if (delayzsetterm) { + int val; + ioctl(SHTTY, FIONREAD, (char *)&val); + if (!val) + zsetterm(); } +#endif for (;;) { int q = queue_signal_level(); dont_queue_signals(); - r = read(SHTTY, &cc, 1); + r = raw_getkey(keytmout, &cc); restore_queue_signals(q); + if (r == -2) /* timeout */ + return EOF; if (r == 1) break; if (r == 0) { @@ -1101,7 +1200,7 @@ zleaftertrap(Hookdef dummy, void *dat) static struct builtin bintab[] = { BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLRp", NULL), BUILTIN("vared", 0, bin_vared, 1, 7, 0, NULL, NULL), - BUILTIN("zle", 0, bin_zle, 0, -1, 0, "lDANCLmMgGcRaUKI", NULL), + BUILTIN("zle", 0, bin_zle, 0, -1, 0, "aAcCDFgGIKlLmMNRU", NULL), }; /* The order of the entries in this table has to match the *HOOK diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index dd2aea40d..7cf558f97 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -341,6 +341,7 @@ bin_zle(char *name, char **args, char *ops, int func) { 'U', bin_zle_unget, 1, 1 }, { 'K', bin_zle_keymap, 1, 1 }, { 'I', bin_zle_invalidate, 0, 0 }, + { 'F', bin_zle_fd, 0, 2 }, { 0, bin_zle_call, 0, -1 }, }; struct opn const *op, *opp; @@ -678,6 +679,108 @@ bin_zle_invalidate(char *name, char **args, char *ops, char func) return 1; } +/**/ +static int +bin_zle_fd(char *name, char **args, char *ops, char func) +{ + int fd = 0, i, found = 0; + char *endptr; + + if (*args) { + fd = (int)zstrtol(*args, &endptr, 10); + + if (*endptr || fd < 0) { + zwarnnam(name, "Bad file descriptor number for -F: %s", *args, 0); + return 1; + } + } + + if (ops['L'] || !*args) { + /* Listing handlers. */ + if (args[1]) { + zwarnnam(name, "too many arguments for -FL", NULL, 0); + return 1; + } + for (i = 0; i < nwatch; i++) { + if (*args && watch_fds[i] != fd) + continue; + found = 1; + printf("%s -F %d %s\n", name, watch_fds[i], watch_funcs[i]); + } + /* only return status 1 if fd given and not found */ + return *args && !found; + } + + if (args[1]) { + /* Adding or replacing a handler */ + char *funcnam = ztrdup(args[1]); + if (nwatch) { + for (i = 0; i < nwatch; i++) { + if (watch_fds[i] == fd) { + zsfree(watch_funcs[i]); + watch_funcs[i] = funcnam; + found = 1; + break; + } + } + } + if (!found) { + /* zrealloc handles NULL pointers, so OK for first time through */ + int newnwatch = nwatch+1; + watch_fds = (int *)zrealloc(watch_fds, + newnwatch * sizeof(int)); + watch_funcs = (char **)zrealloc(watch_funcs, + (newnwatch+1) * sizeof(char *)); + watch_fds[nwatch] = fd; + watch_funcs[nwatch] = funcnam; + watch_funcs[newnwatch] = NULL; + nwatch = newnwatch; + } + } else { + /* Deleting a handler */ + for (i = 0; i < nwatch; i++) { + if (watch_fds[i] == fd) { + int newnwatch = nwatch-1; + int *new_fds; + char **new_funcs; + + zsfree(watch_funcs[i]); + if (newnwatch) { + new_fds = zalloc(newnwatch*sizeof(int)); + new_funcs = zalloc((newnwatch+1)*sizeof(char*)); + if (i) { + memcpy(new_fds, watch_fds, i*sizeof(int)); + memcpy(new_funcs, watch_funcs, i*sizeof(char *)); + } + if (i < newnwatch) { + memcpy(new_fds+i, watch_fds+i+1, + (newnwatch-i)*sizeof(int)); + memcpy(new_funcs+i, watch_funcs+i+1, + (newnwatch-i)*sizeof(char *)); + } + new_funcs[newnwatch] = NULL; + } else { + new_fds = NULL; + new_funcs = NULL; + } + zfree(watch_fds, nwatch*sizeof(int)); + zfree(watch_funcs, (nwatch+1)*sizeof(char *)); + watch_fds = new_fds; + watch_funcs = new_funcs; + nwatch = newnwatch; + found = 1; + break; + } + } + if (!found) { + zwarnnam(name, "No handler installed for fd %d", NULL, fd); + return 1; + } + } + + return 0; +} + /*******************/ /* initialiasation */ /*******************/ -- cgit 1.4.1