Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 May 2006 17:17:59 +0900
From:      Ganbold <ganbold@micom.mng.net>
To:        Gleb Smirnoff <glebius@FreeBSD.org>
Cc:        cvs-src@FreeBSD.org, src-committers@FreeBSD.org, cvs-all@FreeBSD.org
Subject:   Re: cvs commit: src/usr.sbin/ngctl config.c connect.c debug.c dot.c list.c main.c mkpeer.c msg.c name.c ngctl.h rmhook.c show.c	shutdown.c status.c types.c write.c
Message-ID:  <4476B9B7.7030602@micom.mng.net>
In-Reply-To: <20060525072311.GG27819@FreeBSD.org>
References:  <200605241446.k4OEkvo0011251@repoman.freebsd.org>	<44751F01.4030702@micom.mng.net> <20060525072311.GG27819@FreeBSD.org>

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

Gleb,

Gleb Smirnoff wrote:
> On Thu, May 25, 2006 at 12:05:37PM +0900, Ganbold wrote:
> G> Gleb,
> G> 
> G> I modified my previous patch accordingly. Hopefully it follows style(9) 
> G> more; removed typedef, changed function names to follow original 
> G> function naming styles in code, space after return statements according 
> G> to style(9).
>
> I am working on your patch now. I'm doing some minor changes to match
> style of surrounding code, and also moving to queue(3) instead of
> home-made linked list.
>
>
> G> -		return(CMDRTN_USAGE);
> G> +		return (CMDRTN_USAGE);
>
> Let's commit these style changes later.
>
> G> diff -u /usr/src/usr.sbin/ngctl/main.c /usr/home/tsgan/ngctl/main.c
> G> --- /usr/src/usr.sbin/ngctl/main.c	Wed May 24 23:46:55 2006
> G> +++ /usr/home/tsgan/ngctl/main.c	Thu May 25 11:47:59 2006
> G> @@ -50,6 +50,7 @@
> G>  #include <stdlib.h>
> G>  #include <string.h>
> G>  #include <sysexits.h>
> G> +#include <termios.h>
> G>  #include <unistd.h>
> G>  
> G>  #include <netgraph.h>
> G> @@ -61,6 +62,13 @@
> G>  #define WHITESPACE		" \t\r\n\v\f"
> G>  #define DUMP_BYTES_PER_LINE	16
> G>  
> G> +/* Previously issued commands list */
> G> +struct cmdlist {
> G> +	char		*cmd;	/* command */
> G> +	struct cmdlist	*prev;	/* previous command */
> G> +	struct cmdlist	*next;	/* next command */
> G> +};
> G> +
>
> Yes, yes. This is what I'm tending to do. Do not touch ngctl.h, since
> this type is private to main.c
>
> I lowercased CMDLIST, too. :)
>
> G> +static int	ScanCmd(char *cmd, struct cmdlist **curr);
>
> I uppercased "s" and "c" in this function too. Damn, haven't you rooted
> my notebook? :)
>   
Maybe :)
> Please wait for me to send you a patch converted to queue(3) macro, and
> then continue discussion.
>
>   
OK, Here is the patch for main.c which uses TAILQ. I couldn't get rid of 
tailcmd and yet I found one small bug in ScanCmd.
For loop (for (j=0 ; j < i; j++) {putchar(8); putchar(' '); 
putchar(8);}) in ScanCmd really should be inside the if statement 
otherwise it will delete the whole line if user presses up key first time.
The other thing is when user press down arrow after entering some 
commands it shows last command.
Should we let this behave in this way? Usually in shell after entering 
command when user presses down arrow it will not show anything.
Please let me know if I did something wrong. Maybe there are some wrong 
naming with tailq variables.

thanks,

Ganbold


--------------010007020708090901020203
Content-Type: text/plain;
 name="main.c.patch2"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="main.c.patch2"

--- /usr/src/usr.sbin/ngctl/main.c	Wed May 24 23:46:55 2006
+++ main.c	Fri May 26 16:53:58 2006
@@ -39,6 +39,7 @@
  */
 
 #include <sys/param.h>
+#include <sys/queue.h>
 #include <sys/socket.h>
 #include <sys/select.h>
 
@@ -50,6 +51,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sysexits.h>
+#include <termios.h>
 #include <unistd.h>
 
 #include <netgraph.h>
@@ -61,6 +63,11 @@
 #define WHITESPACE		" \t\r\n\v\f"
 #define DUMP_BYTES_PER_LINE	16
 
+struct cmdlist {
+	char			*cmd;	/* command */
+	TAILQ_ENTRY(cmdlist) 	cmd_link;
+};
+
 /* Internal functions */
 static int	ReadFile(FILE *fp);
 static int	DoParseCommand(char *line);
@@ -72,6 +79,10 @@
 static int	ReadCmd(int ac, char **av);
 static int	HelpCmd(int ac, char **av);
 static int	QuitCmd(int ac, char **av);
+static int	ScanCmd(char *cmd, struct cmdlist **curr);
+static struct	cmdlist *AddCmd(char *cmd);
+static struct	cmdlist *GetNextCmd(struct cmdlist *curr);
+static struct	cmdlist *GetPrevCmd(struct cmdlist *curr);
 
 /* List of commands */
 static const struct ngcmd *const cmds[] = {
@@ -118,9 +129,18 @@
 	{ "exit" }
 };
 
+TAILQ_HEAD(tqhead, cmdlist) tq;
+struct tqhead *tqp;
+
+/* tail command in commands list */
+struct cmdlist *tailcmd = NULL;
+
 /* Our control and data sockets */
 int	csock, dsock;
 
+/* this variable must be set go to previous command in history list */
+int	goprev;
+
 /*
  * main()
  */
@@ -188,7 +208,7 @@
 		rtn = EX_OSERR;
 		break;
 	}
-	return(rtn);
+	return (rtn);
 }
 
 /*
@@ -205,10 +225,10 @@
 			continue;
 		if ((rtn = DoParseCommand(line)) != 0) {
 			warnx("line %d: error in file", num);
-			return(rtn);
+			return (rtn);
 		}
 	}
-	return(CMDRTN_OK);
+	return (CMDRTN_OK);
 }
 
 /*
@@ -218,12 +238,29 @@
 DoInteractive(void)
 {
 	const int maxfd = MAX(csock, dsock) + 1;
+	struct cmdlist *curr;
+	int scan_status = 0;
+	struct termios new_settings;
+	struct termios stored_settings;
+
+	/* init tailq */
+	TAILQ_INIT(&tq);
+	tqp = &tq;
 
 	(*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);
@@ -272,15 +309,28 @@
 		if (FD_ISSET(0, &rfds)) {
 			char buf[LINE_MAX];
 
-			if (fgets(buf, sizeof(buf), stdin) == NULL) {
-				printf("\n");
-				break;
+			/* always begin from last command */
+			goprev = 0;
+			memset(buf, 0, LINE_MAX);
+
+			scan_status = ScanCmd(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 ((curr = TAILQ_FIRST(tqp)) != NULL) {
+		TAILQ_REMOVE(tqp, curr, cmd_link);
+		free(curr);
 	}
-	return(CMDRTN_QUIT);
+	return (CMDRTN_QUIT);
 }
 
 /*
@@ -298,7 +348,7 @@
 	    av[++ac] = strtok(NULL, WHITESPACE));
 
 	/* Do command */
-	return(DoCommand(ac, av));
+	return (DoCommand(ac, av));
 }
 
 /*
@@ -311,12 +361,12 @@
 	int rtn;
 
 	if (ac == 0 || *av[0] == 0)
-		return(CMDRTN_OK);
+		return (CMDRTN_OK);
 	if ((cmd = FindCommand(av[0])) == NULL)
-		return(CMDRTN_ERROR);
+		return (CMDRTN_ERROR);
 	if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE)
 		warnx("usage: %s", cmd->cmd);
-	return(rtn);
+	return (rtn);
 }
 
 /*
@@ -331,16 +381,16 @@
 		if (MatchCommand(cmds[k], string)) {
 			if (found != -1) {
 				warnx("\"%s\": ambiguous command", string);
-				return(NULL);
+				return (NULL);
 			}
 			found = k;
 		}
 	}
 	if (found == -1) {
 		warnx("\"%s\": unknown command", string);
-		return(NULL);
+		return (NULL);
 	}
-	return(cmds[found]);
+	return (cmds[found]);
 }
 
 /*
@@ -383,17 +433,17 @@
 	case 2:
 		if ((fp = fopen(av[1], "r")) == NULL) {
 			warn("%s", av[1]);
-			return(CMDRTN_ERROR);
+			return (CMDRTN_ERROR);
 		}
 		break;
 	default:
-		return(CMDRTN_USAGE);
+		return (CMDRTN_USAGE);
 	}
 
 	/* Process it */
 	rtn = ReadFile(fp);
 	fclose(fp);
-	return(rtn);
+	return (rtn);
 }
 
 /*
@@ -419,7 +469,7 @@
 			*s = '\0';
 			printf("  %-10s %s\n", buf, cmd->desc);
 		}
-		return(CMDRTN_OK);
+		return (CMDRTN_OK);
 	default:
 		/* Show help on a specific command */
 		if ((cmd = FindCommand(av[1])) != NULL) {
@@ -462,7 +512,7 @@
 			}
 		}
 	}
-	return(CMDRTN_OK);
+	return (CMDRTN_OK);
 }
 
 /*
@@ -471,7 +521,159 @@
 static int
 QuitCmd(int ac __unused, char **av __unused)
 {
-	return(CMDRTN_QUIT);
+	return (CMDRTN_QUIT);
+}
+
+/*
+ * Get commands from stdin, up/down arrow keys handling
+ */
+static int
+ScanCmd(char *cmd, struct cmdlist **curr)
+{
+	struct 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 = AddCmd(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 */
+						p = GetPrevCmd(p);
+						if (p != NULL) {
+							for (j=0 ; j < i; j++) {
+								putchar(8); putchar(' '); putchar(8);
+							}
+							printf("%s", p->cmd);
+							cmd = strdup(p->cmd);
+							i = strlen(cmd);
+						}
+					} else if (c == 66) {
+						/* down arrow key */
+						p = GetNextCmd(p);
+						if(p != NULL) {
+							for (j=0 ; j < i; j++) {
+								putchar(8); putchar(' '); putchar(8);
+							}
+							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));
+}
+
+/*
+ * add new command to the command list
+ */
+static struct cmdlist
+*AddCmd(char *cmd)
+{
+	struct cmdlist *p;
+
+	p = (struct cmdlist *) malloc(sizeof(struct cmdlist));
+
+	if (p == NULL || (p->cmd = strdup(cmd)) == NULL) {
+		warn("malloc");
+		exit (CMDRTN_ERROR);
+	}
+	if(TAILQ_FIRST(tqp) == NULL)
+		TAILQ_INSERT_HEAD(tqp, p, cmd_link);
+	else
+		TAILQ_INSERT_TAIL(tqp, p, cmd_link);
+
+	/* store tail */
+	tailcmd = p;
+
+	return (p);
+ }
+
+/*
+ * Get next command from the command list
+ */
+static struct cmdlist
+*GetNextCmd(struct cmdlist *curr)
+{
+	struct cmdlist *p;
+
+	p = curr;
+
+	/* if current command is not latest in a command list, get next command */
+	if (p != tailcmd && p != NULL)
+		p = TAILQ_NEXT(p, cmd_link);
+
+	return (p);
+}
+
+/*
+ * Get previous command from the command list
+ */
+static struct cmdlist
+*GetPrevCmd(struct cmdlist *curr)
+{
+	struct cmdlist *p;
+
+	p = curr;
+
+	if (p == tailcmd && goprev != 1) {
+		goprev = 1;
+		return (p);
+	}
+
+	/* if current command is not first in a command list, get previous command */
+	if (goprev && p != TAILQ_FIRST(tqp) && p != NULL)
+		p = TAILQ_PREV(p, tqhead, cmd_link);
+
+	return (p);
 }
 
 /*

--------------010007020708090901020203--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4476B9B7.7030602>