Date: Thu, 13 Oct 2005 01:24:42 +0100 From: "Nuno Antunes" <nuno.antunes@gmail.com> To: "FreeBSD gnats submit" <FreeBSD-gnats-submit@FreeBSD.org> Subject: bin/87352: [PATCH] Add line edit and history support to ngctl via libedit Message-ID: <1129163082.0@pt1wgfdc.ip.lab> Resent-Message-ID: <200510130030.j9D0UK3o082458@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 87352 >Category: bin >Synopsis: [PATCH] Add line edit and history support to ngctl via libedit >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Thu Oct 13 00:30:19 GMT 2005 >Closed-Date: >Last-Modified: >Originator: Nuno Antunes >Release: FreeBSD 6.0-BETA4 i386 >Organization: >Environment: System: FreeBSD 6.0-BETA4 #2: Sat Sep 17 10:56:38 WEST 2005 nant@pt1wgfdc.ip.lab:/usr/obj/usr/src/sys/LIFEBOOK8010 >Description: This patch group adds line editing and history facilities to ngctl via libedit while on the interactive mode tool. It now uses two threads, one to handle user interactivity and the other to handle the data/control socket operation. I'm not sure if any special syncronization is required. Only minimal syncronization was implemented (via a signal). >How-To-Repeat: >Fix: --- Makefile.diff begins here --- --- usr.sbin/ngctl/Makefile.orig Wed Oct 12 23:18:02 2005 +++ usr.sbin/ngctl/Makefile Tue Oct 11 23:13:21 2005 @@ -7,6 +7,6 @@ msg.c debug.c shutdown.c rmhook.c status.c types.c write.c WARNS?= 3 DPADD= ${LIBNETGRAPH} -LDADD= -lnetgraph +LDADD= -lnetgraph -ledit -ltermcap -lpthread .include <bsd.prog.mk> --- Makefile.diff ends here --- --- ngctl.h.diff begins here --- --- usr.sbin/ngctl/ngctl.h.orig Mon Oct 10 22:51:59 2005 +++ usr.sbin/ngctl/ngctl.h Wed Oct 12 23:26:24 2005 @@ -52,6 +52,8 @@ #include <ctype.h> #include <errno.h> #include <err.h> +#include <histedit.h> +#include <pthread.h> #include <netgraph.h> #include <netgraph/ng_socket.h> @@ -66,6 +68,14 @@ const char *desc; /* description */ const char *help; /* help text */ const char *aliases[MAX_CMD_ALIAS]; /* command aliases */ +}; + +/* Data to be passed to the threads. */ +struct thread_data { + EditLine *el; /* EditLine cookie */ + History *hist; /* History cookie */ + HistEvent he; /* History event */ + pthread_t interact; }; /* Command return values */ --- ngctl.h.diff ends here --- --- main.c.diff begins here --- --- usr.sbin/ngctl/main.c.orig Fri Feb 4 20:09:11 2005 +++ usr.sbin/ngctl/main.c Wed Oct 12 23:26:09 2005 @@ -49,13 +49,15 @@ static int ReadFile(FILE *fp); static int DoParseCommand(char *line); static int DoCommand(int ac, char **av); -static int DoInteractive(void); +static void * DoInteractive(void *); +static void * DoMonitor(void *); static const struct ngcmd *FindCommand(const char *string); static int MatchCommand(const struct ngcmd *cmd, const char *s); static void Usage(const char *msg); static int ReadCmd(int ac, char **av); static int HelpCmd(int ac, char **av); static int QuitCmd(int ac, char **av); +static const char *GetPrompt(EditLine *); /* List of commands */ static const struct ngcmd *const cmds[] = { @@ -152,7 +154,45 @@ if (fp != NULL) { rtn = ReadFile(fp); } else if (interactive) { - rtn = DoInteractive(); + pthread_t monitor; + struct thread_data td; + void *thread_rtn; + + /* Initialize libedit stuff */ + td.el = el_init(getprogname(), stdin, stdout, stderr); + if (td.el == NULL) + return CMDRTN_ERROR; + td.hist = history_init(); + if (td.hist != NULL) { + history(td.hist, &td.he, H_SETSIZE, 100); + el_set(td.el, EL_HIST, history, td.hist); + } + else printf("no history!!!\n"); + + el_set(td.el, EL_PROMPT, GetPrompt); + el_set(td.el, EL_SIGNAL, 1); + el_set(td.el, EL_EDITOR, "emacs"); + el_source(td.el, NULL); + + /* + * Now we create two threads. The Interactive thread to + * handle the interactive stuff and the Monitor thread + * to print data as it arrives on the socket. + */ + pthread_create(&td.interact, NULL, DoInteractive, &td); + pthread_create(&monitor, NULL, DoMonitor, &td); + + + /* Wait for the Interactive thread to finish */ + pthread_join(td.interact, &thread_rtn); + rtn = *(int *)thread_rtn; + + pthread_cancel(monitor); + + /* Destroy libedit stuff */ + el_end(td.el); + if (td.hist != NULL) + history_end(td.hist); } else Usage("no command specified"); } else { @@ -198,38 +238,66 @@ /* * Interactive mode */ -static int -DoInteractive(void) +static void * +DoInteractive(void *vp) { - const int maxfd = MAX(csock, dsock) + 1; + struct thread_data *td = (struct thread_data *)vp; + static int rtn; (*help_cmd.func)(0, NULL); + while (1) { - struct timeval tv; - fd_set rfds; + char buf[LINE_MAX]; + const char *line = NULL; + int count = 0; + + if ((line = el_gets(td->el, &count)) != NULL) { + int len; + + strncpy(buf, line, LINE_MAX - 1); + buf[LINE_MAX] = '\0';/* In case len(line) >= LINE_MAX */ + len = strlen(buf); + if (buf[--len] == '\n') + buf[len] = '\0'; + if ((rtn = DoParseCommand(buf)) == CMDRTN_QUIT) + break; + history(td->hist, &td->he, H_ENTER, buf); + } + } + + return(&rtn); +} + +/* + * Monitor thread + */ +static void * +DoMonitor(void *vp) +{ + const int maxfd = MAX(csock, dsock) + 1; + struct thread_data *td = (struct thread_data *)vp; + fd_set rfds; + while (1) { /* See if any data or control messages are arriving */ FD_ZERO(&rfds); FD_SET(csock, &rfds); FD_SET(dsock, &rfds); - memset(&tv, 0, sizeof(tv)); - if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) { - - /* Issue prompt and wait for anything to happen */ - printf("%s", PROMPT); - fflush(stdout); - FD_ZERO(&rfds); - FD_SET(0, &rfds); - FD_SET(csock, &rfds); - FD_SET(dsock, &rfds); - if (select(maxfd, &rfds, NULL, NULL, NULL) < 0) - err(EX_OSERR, "select"); - - /* If not user input, print a newline first */ - if (!FD_ISSET(0, &rfds)) - printf("\n"); +again: + if (select(maxfd, &rfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) /* Allow signals */ + goto again; + err(EX_OSERR, "select"); } + /* Save current line */ + + + + /* Print a newline before echoing anything */ + printf("\n"); + fflush(stdout); + /* Display any incoming control message */ if (FD_ISSET(csock, &rfds)) MsgRead(); @@ -252,19 +320,14 @@ free(buf); } - /* Get any user input */ - if (FD_ISSET(0, &rfds)) { - char buf[LINE_MAX]; + /* Restore the prompt to a known state */ + el_reset(td->el); + printf("%s", GetPrompt(td->el)); + fflush(stdout); + pthread_kill(td->interact, SIGCONT); + } /* while */ - if (fgets(buf, sizeof(buf), stdin) == NULL) { - printf("\n"); - break; - } - if (DoParseCommand(buf) == CMDRTN_QUIT) - break; - } - } - return(CMDRTN_QUIT); + return NULL; } /* @@ -493,6 +556,13 @@ } printf("%s\n", sbuf); } +} + +static const char * +GetPrompt(EditLine *el) +{ + (void) el; + return PROMPT; } /* --- main.c.diff ends here --- >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1129163082.0>