File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / aitcli.c
Revision 1.1.1.1.2.8: download - view: text, annotated - select for diffs - revision graph
Sat Apr 24 10:05:26 2010 UTC (14 years, 2 months ago) by misho
Branches: cli1_0
Diff to: branchpoint 1.1.1.1: preferred, unified
added comment

    1: /*************************************************************************
    2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
    3: *  by Michael Pounov <misho@openbsd-bg.org>
    4: *
    5: * $Author: misho $
    6: * $Id: aitcli.c,v 1.1.1.1.2.8 2010/04/24 10:05:26 misho Exp $
    7: *
    8: *************************************************************************/
    9: #include "global.h"
   10: 
   11: 
   12: #pragma GCC visibility push(hidden)
   13: 
   14: cliCommands_t cli_stdCmds[] = {
   15: 	{ "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename }, 
   16: 	{ "-------", NULL, "---------------------", NULL, NULL }, 
   17: 	{ "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL }, 
   18: 	{ "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL }, 
   19: 	{ NULL, NULL, NULL, NULL }
   20: };
   21: 
   22: // ------------------------------------------------
   23: 
   24: int cli_Errno;
   25: char cli_Error[STRSIZ];
   26: 
   27: #pragma GCC visibility pop
   28: 
   29: 
   30: static void cli_Null_Prep_Term(int meta)
   31: {
   32: }
   33: 
   34: static void cli_Null_Deprep_Term()
   35: {
   36: }
   37: 
   38: static int cli_Pre_Input_Change_Mode()
   39: {
   40: 	return 0;
   41: }
   42: 
   43: static int cli_GetC(FILE *dummy)
   44: {
   45: 	int ch = rl_getc(stdin);
   46: 
   47: 	/*
   48: 	if (is_special_char(ch)) {
   49: 		pending_special_char = ch;
   50: 		return '\r';
   51: 	}
   52: 	*/
   53: 
   54: 	return ch;
   55: }
   56: 
   57: 
   58: // cli_GetErrno() Get error code of last operation
   59: inline int cli_GetErrno()
   60: {
   61: 	return cli_Errno;
   62: }
   63: 
   64: // io_GetError() Get error text of last operation
   65: inline const char *cli_GetError()
   66: {
   67: 	return cli_Error;
   68: }
   69: 
   70: // cli_SetErr() Set error to variables for internal use!!!
   71: inline void cli_SetErr(int eno, char *estr, ...)
   72: {
   73: 	va_list lst;
   74: 
   75: 	cli_Errno = eno;
   76: 	memset(cli_Error, 0, STRSIZ);
   77: 	va_start(lst, estr);
   78: 	vsnprintf(cli_Error, STRSIZ, estr, lst);
   79: 	va_end(lst);
   80: }
   81: 
   82: // ------------------------------------------------------------
   83: 
   84: /*
   85:  * cli_Printf() Printf CLI features
   86:  * @out = Output stream
   87:  * @csFormat = Printf format string
   88:  * return: -1 error, != -1 printed chars
   89: */
   90: inline int cli_Printf(FILE *out, const char *csFormat, ...)
   91: {
   92: 	va_list lst;
   93: 	int ret;
   94: 
   95: 	va_start(lst, csFormat);
   96: 
   97: 	ret = vfprintf(out, csFormat, lst);
   98: 	if (-1 == ret)
   99: 		LOGERR;
  100: 
  101: 	va_end(lst);
  102: 	return ret;
  103: }
  104: 
  105: 
  106: /*
  107:  * cliComp() Initialize completion CLI features
  108:  * @cmdComplete = Completion function
  109:  * @cmdEntry = Compentry function
  110:  * return: none
  111: */
  112: inline void cliComp(cli_Completion_t *cmdComplete, cli_CompEntry_t *cmdEntry)
  113: {
  114: 	// command completon
  115: 	rl_attempted_completion_function = cmdComplete;
  116: 	rl_completion_entry_function = cmdEntry;
  117: }
  118: 
  119: /*
  120:  * cliTTY() Initialize I/O TTY CLI features
  121:  * @term = terminal name
  122:  * @inp = input handle
  123:  * @out = output handle
  124:  * @win = window size
  125:  * return: -1 error, != -1 ok
  126: */
  127: inline int cliTTY(const char *term, FILE *inp, FILE *out, struct winsize *win)
  128: {
  129: 	if (term)
  130: 		rl_terminal_name = term;
  131: 
  132: 	if (inp)
  133: 		rl_instream = inp;
  134: 	if (out)
  135: 		rl_outstream = out;
  136: 
  137: 	if (win)
  138: 	       if (ioctl(!rl_outstream ? STDOUT_FILENO : fileno(rl_outstream), TIOCSWINSZ, win) == -1) {
  139: 		       LOGERR;
  140: 		       return -1;
  141: 	       }
  142: 
  143: 	return 0;
  144: }
  145: 
  146: /*
  147:  * cli_ReadHistory() Read CLI History from file
  148:  * @csFile = history file name, if NULL default history name is ".aitcli.history"
  149:  * return: -1 error; != -1 readed ok
  150: */
  151: inline int cli_ReadHistory(const char *csFile)
  152: {
  153: 	return read_history(!csFile ? ".aitcli.history" : csFile);
  154: }
  155: 
  156: /*
  157:  * cli_WriteHistory() Write CLI History to file
  158:  * @csFile = history file name, if NULL default history name is ".aitcli.history"
  159:  * @lineNum = save number of history entry lines, if -1 all lines saved without limit
  160:  * return: -1 error; != -1 readed ok
  161: */
  162: inline int cli_WriteHistory(const char *csFile, int lineNum)
  163: {
  164: 	int ret;
  165: 	const char *psFile = !csFile ? ".aitcli.history" : csFile;
  166: 
  167: 	ret = write_history(psFile);
  168: 	if (-1 != ret && -1 != lineNum)
  169: 		history_truncate_file(psFile, lineNum);
  170: 
  171: 	return ret;
  172: }
  173: 
  174: /*
  175:  * cliInit() Initialize Readline
  176:  * @csProg = program name
  177:  * return: none
  178: */
  179: inline void cliInit(const char *csProg)
  180: {
  181: 	rl_readline_name = csProg;
  182: 
  183: 	rl_variable_bind("editing-mode", "emacs");
  184: }
  185: 
  186: /*
  187:  * cliNetInit() Initialize Readline if CLI bind to socket
  188:  * @csProg = program name
  189:  * @pty = Master pty
  190:  * @term = stdin termios
  191:  * return: none
  192: */
  193: void cliNetInit(const char *csProg, int pty, struct termios *term)
  194: {
  195: 	struct termios t;
  196: 
  197: 	if (term) {
  198: 		t = *term;
  199: 		// Don`t uncomment this because input chars on CLI socket console will be hidden!!!
  200: //		t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
  201: 		t.c_iflag &= ~ICRNL;
  202: 		t.c_iflag |= IGNBRK;
  203: 		t.c_cc[VMIN] = 1;
  204: 		t.c_cc[VTIME] = 0;
  205: 		tcsetattr(pty, TCSANOW, &t);
  206: 	}
  207: 
  208: 	cliInit(csProg);
  209: 
  210: 	rl_instream = fdopen(pty, "r");
  211: 
  212: 	rl_prep_term_function = cli_Null_Prep_Term;
  213: 	rl_deprep_term_function = cli_Null_Deprep_Term;
  214: 	rl_pre_input_hook = cli_Pre_Input_Change_Mode;
  215: 
  216: 	rl_getc_function = cli_GetC;
  217: 
  218: }
  219: 
  220: /*
  221:  * cliNetExec() Execute net CLI main loop
  222:  * @cmdList = Commands list
  223:  * @csPrompt = Prompt text
  224:  * @sock = client socket
  225:  * @term = stdin termios
  226:  * @win = window size of tty
  227:  * return: -1 error, 0 = exit w/^+D, 1 done.
  228: */
  229: int cliNetExec(cliCommands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)
  230: {
  231: 	int pty, ret = 0, r, s, alen;
  232: 	fd_set fds;
  233: 	struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
  234: 	u_char buf[BUFSIZ];
  235: 	struct telnetAttrs *a, Attr[3];
  236: 
  237: 	switch (forkpty(&pty, NULL, term, win)) {
  238: 		case -1:
  239: 			LOGERR;
  240: 			return -1;
  241: 		case 0:
  242: 			close(sock);
  243: 
  244: 			cliTTY(NULL, NULL, NULL, win);
  245: 			ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
  246: 			_exit(ret);
  247: 		default:
  248: 			cliNetInit(getprogname(), pty, term);
  249: 
  250: 			telnet_SetCmd(Attr, DO, TELOPT_TTYPE);
  251: 			telnet_SetCmd(Attr + 1, DO, TELOPT_LINEMODE);
  252: 			telnet_SetCmd(Attr + 2, GA, 0);
  253: 			if ((ret = telnetSend(sock, Attr, 3, NULL, 0, 0)) == -1) {
  254: 				cli_Errno = telnet_GetErrno();
  255: 				strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  256: 				return -1;
  257: 			}
  258: 
  259: 			while (42) {
  260: 				FD_ZERO(&fds);
  261: 				FD_SET(sock, &fds);
  262: 				FD_SET(pty, &fds);
  263: 				if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) < 1)
  264: 					break;
  265: 
  266: 				r = FD_ISSET(sock, &fds) ? sock : pty;
  267: 				s = FD_ISSET(sock, &fds) ? pty : sock;
  268: 
  269: 				if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
  270: 					if (-2 == ret)
  271: 						continue;
  272: 					// EOF
  273: 					if (-3 == ret)
  274: 						shutdown(r, SHUT_RD);
  275: 					else {
  276: 						cli_Errno = telnet_GetErrno();
  277: 						strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  278: 					}
  279: 					break;
  280: 				}
  281: 				if ((ret = telnetSend(s, NULL, 0, buf, ret, 0)) == -1) {
  282: 					cli_Errno = telnet_GetErrno();
  283: 					strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  284: 					break;
  285: 				}
  286: 
  287: 				/*
  288: 				if ((ret = read(r, &ch, 1)) < 1) {
  289: 					if (!ret)
  290: 						shutdown(r, SHUT_RD);
  291: 					break;
  292: 				}
  293: 				if (write(s, &ch, 1) == -1) 
  294: 					break;
  295: 				*/
  296: 			}
  297: 
  298: 			close(pty);
  299: 	}
  300: 
  301: 	return ret;
  302: }
  303: 
  304: /*
  305:  * cliExec() Execute CLI main loop
  306:  * @cmdList = Commands list
  307:  * @csPrompt = Prompt text
  308:  * return: -1 error, 0 = exit w/^+D, 1 done.
  309: */
  310: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
  311: {
  312: 	char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
  313: 	int ret = 0;
  314: 	register int i;
  315: 	cliCommands_t *cmd = NULL;
  316: 	FILE *out;
  317: 
  318: 	inline int inline_help()
  319: 	{
  320: 		cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
  321: 		rl_on_new_line();
  322: 		return 0;
  323: 	}
  324: 
  325: 	char **cli_stdCompletion(const char *text, int start, int end)
  326: 	{
  327: 		register int i;
  328: 		char **matches = NULL;
  329: 
  330: 		char *cmdCompGet(const char *text, int state)
  331: 		{
  332: 			int len = strlen(text);
  333: 
  334: 			for (i = state; cmdList[i].cmd_name; i++) {
  335: 				if (strncmp(cmdList[i].cmd_name, "---", 3) && 
  336: 						!strncmp(cmdList[i].cmd_name, text, len))
  337: 					return strdup(cmdList[i].cmd_name);
  338: 			}
  339: 
  340: 			return NULL;
  341: 		}
  342: 
  343: 		if (!start)
  344: 			matches = rl_completion_matches(text, cmdCompGet);
  345: 		else
  346: 			for (i = 0; cmdList[i].cmd_name; i++) {
  347: 				if (!cmdList[i].cmd_comp)
  348: 					continue;
  349: 				if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
  350: 					matches = rl_completion_matches(text, cmdList[i].cmd_comp);
  351: 			}
  352: 
  353: 		return matches;
  354: 	}
  355: 	char *cli_stdCompEntry(const char *ignore, int invoking_key)
  356: 	{
  357: 		return NULL;
  358: 	}
  359: 
  360: 	/* --- main body of CLI --- */
  361: 
  362: 	out = rl_outstream;
  363: 	if (!out)
  364: 		out = stdout;
  365: 
  366: 	rl_bind_key('?', inline_help);
  367: 	if (!rl_attempted_completion_function) 
  368: 		cliComp(cli_stdCompletion, cli_stdCompEntry);
  369: 
  370: 	do {
  371: 		line = readline(csPrompt);
  372: 		if (!line) {	// ^+d
  373: 			cli_Printf(out, "\n");
  374: 			break;
  375: 		}
  376: 		// clear whitespaces
  377: 		for (s = line; isspace(*s); s++);
  378: 		if (*s) {
  379: 			for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
  380: 			*++t = 0;
  381: 		}
  382: 
  383: 		if (*s) {
  384: 			add_history(s);
  385: 
  386: 			memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
  387: 			for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
  388: 					*app ? app++ : app);
  389: 
  390: 			/*
  391: 			for (i = 0; i < MAX_PROMPT_ITEMS; i++)
  392: 				cli_Printf(out, "i=%d %s\n", i, items[i]);
  393: 				*/
  394: 
  395: 			// exec_cmd ...
  396: 			for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
  397: 				if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
  398: 					cmd = &cmdList[i];
  399: 					break;
  400: 				}
  401: 			if (!cmd) {
  402: 				cli_Printf(out, "Command '%s' not found!\n", items[0]);
  403: 				ret = -1;
  404: 			} else
  405: 				ret = cmd->cmd_func(cmdList, i, out, items);
  406: 		}
  407: 
  408: 		free(line);
  409: 	} while (ret < 1);
  410: 
  411: 	return ret;
  412: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>