From owner-freebsd-current@FreeBSD.ORG Fri May 19 07:44:28 2006 Return-Path: X-Original-To: freebsd-current@freebsd.org Delivered-To: freebsd-current@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 81A5816A41F; Fri, 19 May 2006 07:44:28 +0000 (UTC) (envelope-from ganbold@micom.mng.net) Received: from publicd.ub.mng.net (publicd.ub.mng.net [202.179.0.88]) by mx1.FreeBSD.org (Postfix) with ESMTP id 41B0D43D4C; Fri, 19 May 2006 07:44:24 +0000 (GMT) (envelope-from ganbold@micom.mng.net) Received: from [202.179.0.164] (helo=[192.168.0.18]) by publicd.ub.mng.net with esmtpa (Exim 4.61 (FreeBSD)) (envelope-from ) id 1FgzeU-000LBm-Ss; Fri, 19 May 2006 16:44:03 +0900 Message-ID: <446D7741.10102@micom.mng.net> Date: Fri, 19 May 2006 16:44:01 +0900 From: Ganbold User-Agent: Thunderbird 1.5.0.2 (X11/20060425) MIME-Version: 1.0 To: freebsd-current@freebsd.org Content-Type: multipart/mixed; boundary="------------010005050004060700000402" Cc: Gleb Smirnoff Subject: small patch for ngctl X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 May 2006 07:44:28 -0000 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 #include #include +#include #include #include #include @@ -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--