bin/87352: [PATCH] Add line edit and history support to ngctl via libedit

Nuno Antunes nuno.antunes at gmail.com
Wed Oct 12 17:30:21 PDT 2005


>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 at 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:


More information about the freebsd-bugs mailing list