bin/87352: [PATCH] Add line edit and history support to ngctl(8)
via libedit
Nuno Antunes
nuno.antunes at gmail.com
Sat Nov 26 13:30:31 GMT 2005
Gleb Smirnoff wrote:
> Synopsis: [PATCH] Add line edit and history support to ngctl(8) via libedit
>
> State-Changed-From-To: open->suspended
> State-Changed-By: glebius
> State-Changed-When: Tue Nov 15 14:58:26 GMT 2005
> State-Changed-Why:
> Submitter promises to resubmit an updated version of patch.
>
>
> Responsible-Changed-From-To: freebsd-bugs->glebius
> Responsible-Changed-By: glebius
> Responsible-Changed-When: Tue Nov 15 14:58:26 GMT 2005
> Responsible-Changed-Why:
> I like this plan, so I will handle the PR.
>
> http://www.freebsd.org/cgi/query-pr.cgi?pr=87352
>
Hi Gleb,
This is what I have. Please find the patches attached.
I would prefer that the command line could be redisplayed automatically
after data arrived on the data socket, but I did not find a way to do
that. Instead, I am relying on the predefined libedit ^R emacs binding
that redisplays the complete command line. I also include a patch for
the manpage documenting this.
Please let me know what you think.
Thanks,
Nuno Antunes
-------------- next part --------------
--- usr.sbin/ngctl/Makefile.orig Mon Jan 26 10:27:18 2004
+++ usr.sbin/ngctl/Makefile Sun Nov 20 14:58:39 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>
-------------- next part --------------
--- usr.sbin/ngctl/main.c.orig Fri Feb 4 20:09:11 2005
+++ usr.sbin/ngctl/main.c Tue Nov 22 22:49:10 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,46 @@
if (fp != NULL) {
rtn = ReadFile(fp);
} else if (interactive) {
- rtn = DoInteractive();
+ pthread_t interact;
+ 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);
+ history(td.hist, &td.he, H_SETUNIQUE, 1);
+ el_set(td.el, EL_HIST, history, td.hist);
+ }
+
+ 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(&interact, NULL, DoInteractive, &td);
+ pthread_create(&monitor, NULL, DoMonitor, &td);
+
+ /* Wait for the Interactive thread to finish */
+ pthread_join(interact, &thread_rtn);
+ rtn = *(int *)thread_rtn;
+
+ /* Terminate monitor thread */
+ pthread_cancel(monitor);
+
+ /* Destroy libedit stuff */
+ el_end(td.el);
+ if (td.hist != NULL)
+ history_end(td.hist);
} else
Usage("no command specified");
} else {
@@ -196,40 +237,65 @@
}
/*
- * Interactive mode
+ * Interactive mode thread
*/
-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);
+ /* Truncate buf in case len(line) >= LINE_MAX */
+ buf[LINE_MAX - 1] = '\0';
+ len = strlen(buf);
+ if (buf[--len] == '\n')
+ buf[len] = '\0';
+ if (len > 0) {
+ history(td->hist, &td->he, H_ENTER, buf);
+ if ((rtn = DoParseCommand(buf)) == CMDRTN_QUIT)
+ break;
+ }
+ }
+ }
+
+ return(&rtn);
+}
+
+/*
+ * Monitor thread
+ */
+static void *
+DoMonitor(void *vp)
+{
+ const int maxfd = MAX(csock, dsock) + 1;
+ 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");
}
+ /* Print a newline before echoing anything */
+ printf("\n");
+ fflush(stdout);
+
/* Display any incoming control message */
if (FD_ISSET(csock, &rfds))
MsgRead();
@@ -251,20 +317,9 @@
DumpAscii(buf, rl);
free(buf);
}
+ } /* while */
- /* Get any user input */
- if (FD_ISSET(0, &rfds)) {
- char buf[LINE_MAX];
-
- if (fgets(buf, sizeof(buf), stdin) == NULL) {
- printf("\n");
- break;
- }
- if (DoParseCommand(buf) == CMDRTN_QUIT)
- break;
- }
- }
- return(CMDRTN_QUIT);
+ return NULL;
}
/*
@@ -493,6 +548,13 @@
}
printf("%s\n", sbuf);
}
+}
+
+static const char *
+GetPrompt(EditLine *el)
+{
+ (void) el;
+ return PROMPT;
}
/*
-------------- next part --------------
--- usr.sbin/ngctl/ngctl.8.orig Tue Nov 22 22:20:56 2005
+++ usr.sbin/ngctl/ngctl.8 Sat Nov 26 13:09:58 2005
@@ -124,6 +124,10 @@
.Dq help
command displays the available
commands, their usage and aliases, and a brief description.
+.Pp
+When in interactive mode, if a control message or new data is received and
+displayed while you were typing a command, you can press ^R to redisplay your
+command line.
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
-------------- next part --------------
--- usr.sbin/ngctl/ngctl.h.orig Tue Nov 22 22:37:21 2005
+++ usr.sbin/ngctl/ngctl.h Tue Nov 22 22:38:31 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,13 @@
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 */
};
/* Command return values */
More information about the freebsd-bugs
mailing list