--- /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); } /*