Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 19 May 2006 16:44:01 +0900
From:      Ganbold <ganbold@micom.mng.net>
To:        freebsd-current@freebsd.org
Cc:        Gleb Smirnoff <glebius@FreeBSD.org>
Subject:   small patch for ngctl
Message-ID:  <446D7741.10102@micom.mng.net>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------010005050004060700000402
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Hi,

I've made a small patch enhancement for ngctl to support commands 
history via up/down keys when ngctl works under interactive mode.
It was tested on 7.0-CURRENT machine (FreeBSD 7.0-CURRENT #3: Tue May 16 
19:17:48 ULAST 2006).

I thought this feature might be useful for all. I hope my patch follows 
style(9) and it is made against CURRENT, it should also cleanly apply on 
RELENG_6.

I saw a similar enhancement patch in PR 
http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/87352 which adds line 
edit and history support to ngctl(8) via libedit.

My patch uses TERMIOS(4) structure which I thought more portable. Maybe 
I'm wrong.

Please let me know if there is anything that I did wrong.

thanks,

Ganbold



--------------010005050004060700000402
Content-Type: text/x-patch;
 name="main.c.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="main.c.patch"

--- /usr/src/usr.sbin/ngctl/main.c	Sat Feb  5 04:09:11 2005
+++ main.c	Fri May 19 15:37:17 2006
@@ -56,6 +56,11 @@
 static int	ReadCmd(int ac, char **av);
 static int	HelpCmd(int ac, char **av);
 static int	QuitCmd(int ac, char **av);
+static int 	cmdscan(char *cmd, CMDLIST **curr);
+static CMDLIST 	*InstallCmd(char *cmd);
+static CMDLIST 	*GetNextCmd(CMDLIST *curr);
+static CMDLIST 	*GetPrevCmd(CMDLIST *curr);
+static void 	RemoveCmd(CMDLIST *p);
 
 /* List of commands */
 static const struct ngcmd *const cmds[] = {
@@ -105,6 +110,12 @@
 /* Our control and data sockets */
 int	csock, dsock;
 
+/* this variable must be set go to previous cmd in history list */
+int 	goprev;
+
+CMDLIST *headcmd = NULL;
+CMDLIST *tailcmd = NULL;
+
 /*
  * main()
  */
@@ -202,12 +213,25 @@
 DoInteractive(void)
 {
 	const int maxfd = MAX(csock, dsock) + 1;
+	CMDLIST *curr;
+	int scan_status = 0;
+	struct termios new_settings;
+	struct termios stored_settings;
 
 	(*help_cmd.func)(0, NULL);
 	while (1) {
 		struct timeval tv;
 		fd_set rfds;
 
+		/* record the old settings to restore the terminal when finished */
+		tcgetattr(0, &stored_settings);
+		new_settings = stored_settings;
+
+		/* set things up for character-at-a-time */
+		new_settings.c_lflag &= ~(ECHO | ECHOK | ICANON);
+		new_settings.c_cc[VTIME] = 1;
+		tcsetattr(0, TCSANOW, &new_settings);
+
 		/* See if any data or control messages are arriving */
 		FD_ZERO(&rfds);
 		FD_SET(csock, &rfds);
@@ -256,14 +280,26 @@
 		if (FD_ISSET(0, &rfds)) {
 			char buf[LINE_MAX];
 
-			if (fgets(buf, sizeof(buf), stdin) == NULL) {
-				printf("\n");
-				break;
+			/* alwasy begin from last command */
+			goprev = 0;
+			memset(buf, 0, LINE_MAX);
+
+			scan_status = cmdscan(buf, &curr);
+			if (scan_status == 0) {
+				rewind(stdin);
+				continue;
 			}
+		        snprintf(buf, LINE_MAX, "%s", curr->cmd);
 			if (DoParseCommand(buf) == CMDRTN_QUIT)
 				break;
 		}
+		/* restore the old settings */
+		tcsetattr(0, TCSANOW, &stored_settings);
 	}
+	/* destroy commands */
+        while (headcmd != NULL)
+                RemoveCmd(headcmd);
+
 	return(CMDRTN_QUIT);
 }
 
@@ -493,6 +529,178 @@
 		}
 		printf("%s\n", sbuf);
 	}
+}
+
+/* 
+ * Get commands from stdin, up/down arrow keys handling
+ */
+static int 
+cmdscan(char *cmd, CMDLIST **curr)
+{
+	CMDLIST *p;
+	int c, i, j, finished;
+	
+	p = *curr;
+	c = 1; i = 0; finished = 0;
+
+	while (c && !finished && i < LINE_MAX) {
+		c = getchar();
+		switch (c) {
+			case '\t':
+				printf("tab\n");
+				break;
+			case '\n':
+				finished = 1;
+				putchar('\n');
+				if (i > 0) {
+					cmd[i] = '\0';
+					p = InstallCmd(cmd);
+				}
+				break;
+			case 8:   
+				/* backspace */
+			case 127: 
+				/* delete */
+				if (i > 0) {
+					cmd[--i] = '\0';
+					putchar(8); putchar(' '); putchar(8);
+				}
+				break;
+			case 27:   
+				/* ESC
+				 * Check up/down arrow keys 
+				 */
+				c = getchar();
+				if (c == 91) {   
+					/* it looks like we have an arrow key here */
+					c = getchar();
+					if (c > 68 || c < 65) {   	
+						/* ignore right/left arrow keys, put the characters back in the queue
+						 *(except for the ESC)
+						 */
+						ungetc(c, stdin);
+						ungetc(91, stdin);
+					} else if (c == 65) { 
+						/* up arrow key */
+						for (j=0 ; j < i; j++) { 
+							putchar(8); putchar(' '); putchar(8); 
+						}
+						p = GetPrevCmd(p);
+						if (p != NULL) {
+							printf("%s", p->cmd);
+							cmd = strdup(p->cmd);
+							i = strlen(cmd);
+						}
+					} else if (c == 66) {
+						/* down arrow key */
+						for (j=0 ; j < i; j++) { 
+							putchar(8); putchar(' '); putchar(8); 
+						}
+						p = GetNextCmd(p);
+						if(p != NULL) {
+							printf("%s", p->cmd);
+							cmd = strdup(p->cmd);
+							i = strlen(cmd);
+						}
+					}
+				} else  
+					/* not an arrow key, put the char back */
+					ungetc(c, stdin);
+				break;
+
+			default:
+				cmd[i++] = c;
+				putchar(c);
+				break;
+		}
+	}
+	*curr = p;
+
+	return (strlen(cmd));
+}
+
+/* 
+ * Get next command from the list
+ */
+static CMDLIST 
+*GetNextCmd(CMDLIST *curr)
+{
+	CMDLIST *p;
+
+	p = curr;
+
+	/* if currend command is not latest in a cmd list, get next cmd */
+	if (p != tailcmd && p != NULL)
+		p = p->next;
+
+	return (p);
+}
+
+/* 
+ * Get previous command from the list
+ */
+static CMDLIST 
+*GetPrevCmd(CMDLIST *curr)
+{
+	CMDLIST *p;
+	
+	p = curr;
+
+	if (p == tailcmd && goprev != 1) {
+		goprev = 1;
+		return (p);
+	}
+
+	/* if currend command is not first in a cmd list, get previous cmd */
+	if (goprev && p != headcmd && p != NULL)
+		p = p->prev;
+
+	return (p);
+}
+
+/* 
+ * remove commands from the list
+ */
+static void 
+RemoveCmd(CMDLIST *p)
+{
+
+	if (p->prev == NULL)
+		headcmd = p->next;
+	else
+		p->prev->next = p->next;
+
+	if (p->next == NULL)
+		tailcmd = p->prev;
+	else
+		p->next->prev = p->prev;
+}
+
+/* 
+ * add new command to the list
+ */
+static CMDLIST 
+*InstallCmd(char *cmd)
+{
+	CMDLIST *p;
+
+	p = (CMDLIST *) malloc(sizeof(CMDLIST));
+
+	if (p == NULL || (p->cmd = strdup(cmd)) == NULL) {
+		warn("malloc");
+		exit (CMDRTN_ERROR);
+	}
+
+	if (headcmd == NULL) {
+		headcmd = p;
+		p->prev = NULL;
+	} else {
+		tailcmd->next = p;
+		p->prev = tailcmd;
+	}
+	tailcmd = p;
+	p->next = NULL;
+	return (p);
 }
 
 /*

--------------010005050004060700000402
Content-Type: text/x-patch;
 name="ngctl.h.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="ngctl.h.patch"

--- /usr/src/usr.sbin/ngctl/ngctl.h	Fri Jan  9 11:19:40 2004
+++ ngctl.h	Fri May 19 14:52:08 2006
@@ -46,6 +46,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <unistd.h>
 #include <sysexits.h>
 #include <limits.h>
@@ -67,6 +68,13 @@
 	  const char	*help;				/* help text */
 	  const char	*aliases[MAX_CMD_ALIAS];	/* command aliases */
 };
+
+/* cmd list */
+typedef struct cmd {
+        char *cmd;
+        struct cmd *prev;
+        struct cmd *next;
+} CMDLIST;
 
 /* Command return values */
 #define CMDRTN_OK		0

--------------010005050004060700000402--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?446D7741.10102>