File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / aitcli.c
Revision 1.11: download - view: text, annotated - select for diffs - revision graph
Wed Nov 20 16:40:02 2013 UTC (10 years, 8 months ago) by misho
Branches: MAIN
CVS tags: cli3_6, HEAD, CLI3_5
version 3.5

    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.11 2013/11/20 16:40:02 misho Exp $
    7: *
    8: **************************************************************************
    9: The ELWIX and AITNET software is distributed under the following
   10: terms:
   11: 
   12: All of the documentation and software included in the ELWIX and AITNET
   13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
   14: 
   15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
   16: 	by Michael Pounov <misho@elwix.org>.  All rights reserved.
   17: 
   18: Redistribution and use in source and binary forms, with or without
   19: modification, are permitted provided that the following conditions
   20: are met:
   21: 1. Redistributions of source code must retain the above copyright
   22:    notice, this list of conditions and the following disclaimer.
   23: 2. Redistributions in binary form must reproduce the above copyright
   24:    notice, this list of conditions and the following disclaimer in the
   25:    documentation and/or other materials provided with the distribution.
   26: 3. All advertising materials mentioning features or use of this software
   27:    must display the following acknowledgement:
   28: This product includes software developed by Michael Pounov <misho@elwix.org>
   29: ELWIX - Embedded LightWeight unIX and its contributors.
   30: 4. Neither the name of AITNET nor the names of its contributors
   31:    may be used to endorse or promote products derived from this software
   32:    without specific prior written permission.
   33: 
   34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
   35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   37: ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   44: SUCH DAMAGE.
   45: */
   46: #include "global.h"
   47: #include "cli.h"
   48: 
   49: 
   50: #pragma GCC visibility push(hidden)
   51: 
   52: int cli_Errno;
   53: char cli_Error[STRSIZ];
   54: 
   55: #pragma GCC visibility pop
   56: 
   57: // cli_GetErrno() Get error code of last operation
   58: int
   59: cli_GetErrno()
   60: {
   61: 	return cli_Errno;
   62: }
   63: 
   64: // cli_GetError() Get error text of last operation
   65: const char *
   66: cli_GetError()
   67: {
   68: 	return cli_Error;
   69: }
   70: 
   71: // cli_SetErr() Set error to variables for internal use!!!
   72: void
   73: cli_SetErr(int eno, char *estr, ...)
   74: {
   75: 	va_list lst;
   76: 
   77: 	cli_Errno = eno;
   78: 	memset(cli_Error, 0, sizeof cli_Error);
   79: 	va_start(lst, estr);
   80: 	vsnprintf(cli_Error, sizeof cli_Error, estr, lst);
   81: 	va_end(lst);
   82: }
   83: 
   84: // ------------------------------------------------------------
   85: 
   86: static inline void
   87: clrscrEOL(linebuffer_t * __restrict buf)
   88: {
   89: 	register int i;
   90: 
   91: 	if (buf && buf->line_prompt) {
   92: 		write(buf->line_out, K_CR, 1);
   93: 
   94: 		for (i = 0; i < buf->line_len; i++)
   95: 			write(buf->line_out, K_SPACE, 1);
   96: 	}
   97: }
   98: 
   99: static inline void
  100: printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
  101: {
  102: 	if (buf) {
  103: 		if (prompt && buf->line_prompt) {
  104: 			write(buf->line_out, K_CR, 1);
  105: 			write(buf->line_out, buf->line_prompt, buf->line_bol);
  106: 		}
  107: 
  108: 		write(buf->line_out, buf->line_buf, len == -1 ? 
  109: 				buf->line_eol - buf->line_bol: len);
  110: 	}
  111: }
  112: 
  113: static inline void
  114: printfCR(linebuffer_t * __restrict buf, int prompt)
  115: {
  116: 	if (buf && prompt && buf->line_prompt) {
  117: 		write(buf->line_out, K_CR, 1);
  118: 		write(buf->line_out, buf->line_prompt, buf->line_bol);
  119: 	}
  120: }
  121: 
  122: static inline void
  123: printfNL(linebuffer_t * __restrict buf, int prompt)
  124: {
  125: 	if (buf) {
  126: 		write(buf->line_out, K_ENTER, 1);
  127: 
  128: 		if (prompt)
  129: 			if (prompt && buf->line_prompt)
  130: 				write(buf->line_out, buf->line_prompt, buf->line_bol);
  131: 	}
  132: }
  133: 
  134: // ------------------------------------------------------------
  135: 
  136: static int
  137: bufCHAR(int idx, void * __restrict cli_buffer)
  138: {
  139: 	linebuffer_t *buf = cli_buffer;
  140: 	int pos;
  141: 
  142: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  143: 		return RETCODE_ERR;
  144: 
  145: 	pos = buf->line_eol - buf->line_bol;
  146: 
  147: 	if (buf->line_mode == LINEMODE_INS)
  148: 		memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos, 
  149: 				buf->line_len - buf->line_eol);
  150: 	if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1)
  151: 		buf->line_len += buf->line_keys[idx].key_len;
  152: 	buf->line_eol += buf->line_keys[idx].key_len;
  153: 
  154: 	memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
  155: 	buf->line_buf[buf->line_len - 1] = 0;
  156: 
  157: 	write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
  158: 
  159: 	if (buf->line_mode == LINEMODE_INS) {
  160: 		write(buf->line_out, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len, 
  161: 				buf->line_len - buf->line_eol);
  162: 		printfEOL(buf, -1, 1);
  163: 	}
  164: 	return RETCODE_OK;
  165: }
  166: 
  167: static int
  168: bufEOL(int idx, void * __restrict cli_buffer)
  169: {
  170: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  171: 		return RETCODE_ERR;
  172: 
  173: 	printfCR(cli_buffer, 1);
  174: 	return RETCODE_EOL;
  175: }
  176: 
  177: static int
  178: bufEOF(int idx, void * __restrict cli_buffer)
  179: {
  180: 	/*
  181: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  182: 		return RETCODE_ERR;
  183: 	*/
  184: 
  185: 	return RETCODE_EOF;
  186: }
  187: 
  188: static int
  189: bufUP(int idx, void * __restrict cli_buffer)
  190: {
  191: 	linebuffer_t *buf = cli_buffer;
  192: 	int pos;
  193: 
  194: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  195: 		return RETCODE_ERR;
  196: 
  197: 	if (!buf->line_h)
  198: 		buf->line_h = TAILQ_FIRST(&buf->line_history);
  199: 	else
  200: 		buf->line_h = TAILQ_NEXT(buf->line_h, hist_next);
  201: 	if (!buf->line_h)
  202: 		return RETCODE_OK;
  203: 
  204: 	clrscrEOL(buf);
  205: 	cli_freeLine(buf);
  206: 
  207: 	pos = buf->line_eol - buf->line_bol;
  208: 
  209: 	buf->line_len += buf->line_h->hist_len;
  210: 	buf->line_eol += buf->line_h->hist_len;
  211: 
  212: 	memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
  213: 	buf->line_buf[buf->line_len - 1] = 0;
  214: 
  215: 	printfEOL(buf, -1, 1);
  216: 	return RETCODE_OK;
  217: }
  218: 
  219: static int
  220: bufDOWN(int idx, void * __restrict cli_buffer)
  221: {
  222: 	linebuffer_t *buf = cli_buffer;
  223: 	int pos;
  224: 
  225: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  226: 		return RETCODE_ERR;
  227: 
  228: 	if (!buf->line_h)
  229: 		buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead);
  230: 	else
  231: 		buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next);
  232: 	if (!buf->line_h)
  233: 		return RETCODE_OK;
  234: 
  235: 	clrscrEOL(buf);
  236: 	cli_freeLine(buf);
  237: 
  238: 	pos = buf->line_eol - buf->line_bol;
  239: 
  240: 	buf->line_len += buf->line_h->hist_len;
  241: 	buf->line_eol += buf->line_h->hist_len;
  242: 
  243: 	memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
  244: 	buf->line_buf[buf->line_len - 1] = 0;
  245: 
  246: 	printfEOL(buf, -1, 1);
  247: 	return RETCODE_OK;
  248: }
  249: 
  250: static int
  251: bufCLR(int idx, void * __restrict cli_buffer)
  252: {
  253: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  254: 		return RETCODE_ERR;
  255: 
  256: 	clrscrEOL(cli_buffer);
  257: 	cli_freeLine(cli_buffer);
  258: 
  259: 	printfCR(cli_buffer, 1);
  260: 	return RETCODE_OK;
  261: }
  262: 
  263: static int
  264: bufBS(int idx, void * __restrict cli_buffer)
  265: {
  266: 	linebuffer_t *buf = cli_buffer;
  267: 
  268: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  269: 		return RETCODE_ERR;
  270: 
  271: 	if (buf->line_bol < buf->line_eol) {
  272: 		clrscrEOL(buf);
  273: 
  274: 		buf->line_eol--;
  275: 		buf->line_len--;
  276: 		memmove(buf->line_buf + buf->line_eol - buf->line_bol, 
  277: 				buf->line_buf + buf->line_eol - buf->line_bol + 1, 
  278: 				buf->line_len - buf->line_eol);
  279: 		buf->line_buf[buf->line_len - 1] = 0;
  280: 
  281: 		printfEOL(buf, buf->line_len - 1, 1);
  282: 		printfEOL(buf, -1, 1);
  283: 	}
  284: 
  285: 	return RETCODE_OK;
  286: }
  287: 
  288: static int
  289: bufBTAB(int idx, void * __restrict cli_buffer)
  290: {
  291: 	linebuffer_t *buf = cli_buffer;
  292: 
  293: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  294: 		return RETCODE_ERR;
  295: 
  296: 	if (buf->line_bol < buf->line_eol) {
  297: 		clrscrEOL(buf);
  298: 
  299: 		buf->line_len = buf->line_eol - buf->line_bol + 1;
  300: 		buf->line_buf[buf->line_len - 1] = 0;
  301: 
  302: 		printfEOL(buf, -1, 1);
  303: 	}
  304: 
  305: 	return RETCODE_OK;
  306: }
  307: 
  308: static int
  309: bufMODE(int idx, void * __restrict cli_buffer)
  310: {
  311: 	linebuffer_t *buf = cli_buffer;
  312: 
  313: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  314: 		return RETCODE_ERR;
  315: 
  316: 	buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS;
  317: 	return RETCODE_OK;
  318: }
  319: 
  320: static int
  321: bufBEGIN(int idx, void * __restrict cli_buffer)
  322: {
  323: 	linebuffer_t *buf = cli_buffer;
  324: 
  325: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  326: 		return RETCODE_ERR;
  327: 
  328: 	buf->line_eol = buf->line_bol;
  329: 
  330: 	printfCR(buf, 1);
  331: 	return RETCODE_OK;
  332: }
  333: 
  334: static int
  335: bufEND(int idx, void * __restrict cli_buffer)
  336: {
  337: 	linebuffer_t *buf = cli_buffer;
  338: 
  339: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  340: 		return RETCODE_ERR;
  341: 
  342: 	buf->line_eol = buf->line_len - 1;
  343: 
  344: 	printfEOL(buf, -1, 1);
  345: 	return RETCODE_OK;
  346: }
  347: 
  348: static int
  349: bufLEFT(int idx, void * __restrict cli_buffer)
  350: {
  351: 	linebuffer_t *buf = cli_buffer;
  352: 
  353: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  354: 		return RETCODE_ERR;
  355: 
  356: 	if (buf->line_bol < buf->line_eol)
  357: 		printfEOL(buf, --buf->line_eol - buf->line_bol, 1);
  358: 
  359: 	return RETCODE_OK;
  360: }
  361: 
  362: static int
  363: bufRIGHT(int idx, void * __restrict cli_buffer)
  364: {
  365: 	linebuffer_t *buf = cli_buffer;
  366: 
  367: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  368: 		return RETCODE_ERR;
  369: 
  370: 	if (buf->line_eol < buf->line_len - 1)
  371: 		printfEOL(buf, ++buf->line_eol - buf->line_bol, 1);
  372: 
  373: 	return RETCODE_OK;
  374: }
  375: 
  376: static int
  377: bufDEL(int idx, void * __restrict cli_buffer)
  378: {
  379: 	linebuffer_t *buf = cli_buffer;
  380: 
  381: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  382: 		return RETCODE_ERR;
  383: 
  384: 	clrscrEOL(buf);
  385: 
  386: 	buf->line_len--;
  387: 	memmove(buf->line_buf + buf->line_eol - buf->line_bol, 
  388: 			buf->line_buf + buf->line_eol - buf->line_bol + 1, 
  389: 			buf->line_len - buf->line_eol);
  390: 	buf->line_buf[buf->line_len - 1] = 0;
  391: 
  392: 	printfEOL(buf, buf->line_len - 1, 1);
  393: 	printfEOL(buf, -1, 1);
  394: 
  395: 	return RETCODE_OK;
  396: }
  397: 
  398: static int
  399: bufComp(int idx, void * __restrict cli_buffer)
  400: {
  401: 	linebuffer_t *buf = cli_buffer;
  402: 	char *str, *s, **app, *items[MAX_PROMPT_ITEMS], szLine[STRSIZ];
  403: 	register int i, j;
  404: 	struct tagCommand *cmd, *c;
  405: 	int pos, ret = RETCODE_OK;
  406: 
  407: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  408: 		return RETCODE_ERR;
  409: 
  410: 	str = e_strdup(buf->line_buf);
  411: 	if (!str)
  412: 		return RETCODE_ERR;
  413: 	else {
  414: 		s = str;
  415: 		str_Trim(s);
  416: 	}
  417: 
  418: 	i = j = 0;
  419: 	c = NULL;
  420: 	memset(szLine, 0, STRSIZ);
  421: 	if (*s) {
  422: 		memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
  423: 		for (app = items, i = 0; app < items + MAX_PROMPT_ITEMS - 1 && 
  424: 				(*app = strsep(&s, " \t")); 
  425: 			 	*app ? i++ : i, *app ? app++ : app);
  426: 
  427: 		if (i) {
  428: 			SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
  429: 				if (cmd->cmd_level & (1 << buf->line_level) && 
  430: 						!strncmp(cmd->cmd_name, items[0], 
  431: 							strlen(items[0]))) {
  432: 					if (strncmp(cmd->cmd_name, CLI_CMD_SEP, 
  433: 								strlen(CLI_CMD_SEP))) {
  434: 						j++;
  435: 						c = cmd;
  436: 						strlcat(szLine, " ", STRSIZ);
  437: 						strlcat(szLine, cmd->cmd_name, STRSIZ);
  438: 					}
  439: 				}
  440: 			}
  441: 
  442: 			if (i > 1 && c) {
  443: 				/* we are on argument of command and has complition info */
  444: 				j++;	// always must be j > 1 ;) for arguments
  445: 				strlcpy(szLine, c->cmd_info, STRSIZ);
  446: 			}
  447: 		} else {
  448: 			/* we have valid char but i == 0, this case is illegal */
  449: 			ret = RETCODE_ERR;
  450: 			goto endcomp;
  451: 		}
  452: 	} else {
  453: 		/* we on 0 position of prompt, show commands for this level */
  454: 		SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
  455: 			if (cmd->cmd_level & (1 << buf->line_level))
  456: 				if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) {
  457: 					j++;
  458: 					c = cmd;
  459: 					strlcat(szLine, " ", STRSIZ);
  460: 					strlcat(szLine, cmd->cmd_name, STRSIZ);
  461: 				}
  462: 		}
  463: 	}
  464: 
  465: 	/* completion show actions ... */
  466: 	if (j > 1 && c) {
  467: 		printfNL(buf, 0);
  468: 		write(buf->line_out, szLine, strlen(szLine));
  469: 		printfNL(buf, 1);
  470: 		printfEOL(buf, buf->line_len - 1, 1);
  471: 		printfEOL(buf, -1, 1);
  472: 	}
  473: 	if (j == 1 && c) {
  474: 		clrscrEOL(buf);
  475: 		cli_freeLine(buf);
  476: 
  477: 		pos = buf->line_eol - buf->line_bol;
  478: 
  479: 		buf->line_len += c->cmd_len + 1;
  480: 		buf->line_eol += c->cmd_len + 1;
  481: 
  482: 		memcpy(buf->line_buf + pos, c->cmd_name, c->cmd_len);
  483: 		buf->line_buf[pos + c->cmd_len] = (u_char) *K_SPACE;
  484: 		buf->line_buf[buf->line_len - 1] = 0;
  485: 
  486: 		printfEOL(buf, -1, 1);
  487: 	}
  488: 
  489: endcomp:
  490: 	e_free(str);
  491: 	return ret;
  492: }
  493: 
  494: static int
  495: bufHelp(int idx, void * __restrict cli_buffer)
  496: {
  497: 	linebuffer_t *buf = cli_buffer;
  498: 
  499: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  500: 		return RETCODE_ERR;
  501: 
  502: 	cli_Cmd_Help(buf, buf->line_level, NULL);
  503: 
  504: 	printfEOL(buf, buf->line_len - 1, 1);
  505: 	printfEOL(buf, -1, 1);
  506: 	return RETCODE_OK;
  507: }
  508: 
  509: static int
  510: bufEndNode(int idx, void * __restrict cli_buffer)
  511: {
  512: 	linebuffer_t *buf = cli_buffer;
  513: 
  514: 	if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
  515: 		return RETCODE_ERR;
  516: 
  517: 	if (buf->line_level > 0) {
  518: 		printfNL(cli_buffer, 0);
  519: 		buf->line_level--;
  520: 		cli_Printf(buf, "Enter to config level %d\n", buf->line_level);
  521: 	}
  522: 
  523: 	return bufCLR(idx, cli_buffer);
  524: }
  525: 
  526: 
  527: /*
  528:  * cli_Printf() - Send message to CLI session
  529:  *
  530:  * @cli_buffer = CLI buffer
  531:  * @fmt = printf format string
  532:  * @... = arguments defined in fmt
  533:  * return: none
  534: */
  535: void
  536: cli_Printf(linebuffer_t * __restrict cli_buffer, char *fmt, ...)
  537: {
  538: 	va_list lst;
  539: 	FILE *f;
  540: 
  541: 	if (fmt) {
  542: 		f = fdopen(cli_buffer->line_out, "a");
  543: 		if (!f) {
  544: 			LOGERR;
  545: 			return;
  546: 		}
  547: 
  548: 		va_start(lst, fmt);
  549: 		vfprintf(f, fmt, lst);
  550: 		va_end(lst);
  551: 	} else
  552: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  553: }
  554: 
  555: /*
  556:  * cli_PrintHelp() - Print help screen
  557:  *
  558:  * @cli_buffer = CLI buffer
  559:  * return: none
  560: */
  561: void
  562: cli_PrintHelp(linebuffer_t * __restrict cli_buffer)
  563: {
  564: 	if (cli_buffer) {
  565: 		bufHelp(0, cli_buffer);
  566: 		clrscrEOL(cli_buffer);
  567: 	} else
  568: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  569: }
  570: 
  571: 
  572: /*
  573:  * cli_BindKey() - Bind function to key
  574:  *
  575:  * @key = key structure
  576:  * @cli_buffer = CLI buffer
  577:  * return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position
  578: */
  579: int
  580: cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict cli_buffer)
  581: {
  582: 	register int i;
  583: 
  584: 	if (!key || !cli_buffer) {
  585: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  586: 		return RETCODE_ERR;
  587: 	}
  588: 
  589: 	for (i = 0; i < MAX_BINDKEY; i++)
  590: 		if (key->key_len == cli_buffer->line_keys[i].key_len && 
  591: 				!memcmp(key->key_ch, cli_buffer->line_keys[i].key_ch, 
  592: 					key->key_len)) {
  593: 			cli_buffer->line_keys[i].key_func = key->key_func;
  594: 			return i;
  595: 		}
  596: 
  597: 	return RETCODE_OK;
  598: }
  599: 
  600: 
  601: /*
  602:  * cli_addCommand() - Add command to CLI session
  603:  *
  604:  * @cli_buffer = CLI buffer
  605:  * @csCmd = Command name
  606:  * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
  607:  * @funcCmd = Callback function when user call command
  608:  * @csInfo = Inline information for command
  609:  * @csHelp = Help line when call help
  610:  * return: RETCODE_ERR error, RETCODE_OK ok
  611: */
  612: int
  613: cli_addCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd, 
  614: 		int cliLevel, cmd_func_t funcCmd, 
  615: 		const char *csInfo, const char *csHelp)
  616: {
  617: 	struct tagCommand *cmd;
  618: 
  619: 	if (!cli_buffer || !csCmd) {
  620: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  621: 		return RETCODE_ERR;
  622: 	}
  623: 
  624: 	cmd = e_malloc(sizeof(struct tagCommand));
  625: 	if (!cmd) {
  626: 		LOGERR;
  627: 		return RETCODE_ERR;
  628: 	} else
  629: 		memset(cmd, 0, sizeof(struct tagCommand));
  630: 
  631: 	cmd->cmd_level = cliLevel;
  632: 	cmd->cmd_func = funcCmd;
  633: 	cmd->cmd_len = strlcpy(cmd->cmd_name, csCmd, STRSIZ);
  634: 	if (csInfo)
  635: 		strlcpy(cmd->cmd_info, csInfo, STRSIZ);
  636: 	if (csHelp)
  637: 		strlcpy(cmd->cmd_help, csHelp, STRSIZ);
  638: 	SLIST_INSERT_HEAD(&cli_buffer->line_cmds, cmd, cmd_next);
  639: 	return RETCODE_OK;
  640: }
  641: 
  642: /*
  643:  * cli_delCommand() - Delete command from CLI session
  644:  *
  645:  * @cli_buffer = CLI buffer
  646:  * @csCmd = Command name
  647:  * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
  648:  * return: RETCODE_ERR error, RETCODE_OK ok
  649: */
  650: int
  651: cli_delCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd, int cliLevel)
  652: {
  653: 	struct tagCommand *cmd;
  654: 	int ret = RETCODE_OK;
  655: 
  656: 	if (!cli_buffer || !csCmd) {
  657: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  658: 		return RETCODE_ERR;
  659: 	}
  660: 
  661: 	SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next) 
  662: 		if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
  663: 			ret = 1;
  664: 			SLIST_REMOVE(&cli_buffer->line_cmds, cmd, tagCommand, cmd_next);
  665: 			e_free(cmd);
  666: 			break;
  667: 		}
  668: 
  669: 	return ret;
  670: }
  671: 
  672: /*
  673:  * cli_updCommand() - Update command in CLI session
  674:  *
  675:  * @cli_buffer = CLI buffer
  676:  * @csCmd = Command name
  677:  * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
  678:  * @funcCmd = Callback function when user call command
  679:  * @csInfo = Inline information for command
  680:  * @csHelp = Help line when call help
  681:  * return: RETCODE_ERR error, RETCODE_OK ok
  682: */
  683: int
  684: cli_updCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd, 
  685: 		int cliLevel, cmd_func_t funcCmd, 
  686: 		const char *csInfo, const char *csHelp)
  687: {
  688: 	struct tagCommand *cmd;
  689: 	int ret = RETCODE_OK;
  690: 
  691: 	if (!cli_buffer || !csCmd) {
  692: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  693: 		return RETCODE_ERR;
  694: 	}
  695: 
  696: 	SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next)
  697: 		if ((!cmd->cmd_level || cmd->cmd_level == cliLevel) && 
  698: 				!strcmp(cmd->cmd_name, csCmd)) {
  699: 			if (!cmd->cmd_level)
  700: 				cmd->cmd_level = cliLevel;
  701: 			if (funcCmd)
  702: 				cmd->cmd_func = funcCmd;
  703: 			if (csInfo)
  704: 				strlcpy(cmd->cmd_info, csInfo, STRSIZ);
  705: 			if (csHelp)
  706: 				strlcpy(cmd->cmd_help, csHelp, STRSIZ);
  707: 
  708: 			break;
  709: 		}
  710: 
  711: 	return ret;
  712: }
  713: 
  714: 
  715: /*
  716:  * cli_addHistory() - Add line to history
  717:  *
  718:  * @cli_buffer = CLI buffer
  719:  * @str = Add custom text or if NULL use readed line from CLI buffer
  720:  * return: RETCODE_ERR error, RETCODE_OK ok
  721: */
  722: int
  723: cli_addHistory(linebuffer_t * __restrict cli_buffer, const char * __restrict str)
  724: {
  725: 	struct tagHistory *h;
  726: 
  727: 	if (!cli_buffer) {
  728: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  729: 		return RETCODE_ERR;
  730: 	}
  731: 
  732: 	if (!(h = e_malloc(sizeof(struct tagHistory)))) {
  733: 		LOGERR;
  734: 		return RETCODE_ERR;
  735: 	} else
  736: 		memset(h, 0, sizeof(struct tagHistory));
  737: 
  738: 	if (str) {
  739: 		if (!*str) {
  740: 			e_free(h);
  741: 			return RETCODE_OK;
  742: 		}
  743: 
  744: 		h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
  745: 	} else {
  746: 		if (!*cli_buffer->line_buf || cli_buffer->line_len < 2) {
  747: 			e_free(h);
  748: 			return RETCODE_OK;
  749: 		}
  750: 
  751: 		memcpy(h->hist_line, cli_buffer->line_buf, (h->hist_len = cli_buffer->line_len));
  752: 		str_Trim(h->hist_line);
  753: 		h->hist_len = strlen(h->hist_line);
  754: 	}
  755: 
  756: 	TAILQ_INSERT_HEAD(&cli_buffer->line_history, h, hist_next);
  757: 	return h->hist_len;
  758: }
  759: 
  760: /*
  761:  * cli_saveHistory() - Save history to file
  762:  *
  763:  * @cli_buffer = CLI buffer
  764:  * @histfile = History filename, if NULL will be use default name
  765:  * @lines = Maximum history lines to save
  766:  * return: RETCODE_ERR error, RETCODE_OK ok
  767: */
  768: int
  769: cli_saveHistory(linebuffer_t * __restrict cli_buffer, const char *histfile, int lines)
  770: {
  771: 	FILE *f;
  772: 	mode_t mode;
  773: 	char szFName[MAXPATHLEN];
  774: 	struct tagHistory *h;
  775: 
  776: 	if (!cli_buffer) {
  777: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  778: 		return RETCODE_ERR;
  779: 	}
  780: 	if (!histfile)
  781: 		strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
  782: 	else
  783: 		strlcpy(szFName, histfile, MAXPATHLEN);
  784: 
  785: 	mode = umask(0177);
  786: 	f = fopen(szFName, "w");
  787: 	if (!f) {
  788: 		LOGERR;
  789: 		return RETCODE_ERR;
  790: 	}
  791: 
  792: 	TAILQ_FOREACH(h, &cli_buffer->line_history, hist_next) {
  793: 		fprintf(f, "%s\n", h->hist_line);
  794: 
  795: 		if (lines)
  796: 			lines--;
  797: 		else
  798: 			break;
  799: 	}
  800: 
  801: 	fclose(f);
  802: 	umask(mode);
  803: 
  804: 	return RETCODE_OK;
  805: }
  806: 
  807: /*
  808:  * cli_loadHistory() - Load history from file
  809:  *
  810:  * @cli_buffer = CLI buffer
  811:  * @histfile = History filename, if NULL will be use default name
  812:  * return: RETCODE_ERR error, RETCODE_OK ok
  813: */
  814: int
  815: cli_loadHistory(linebuffer_t * __restrict cli_buffer, const char *histfile)
  816: {
  817: 	FILE *f;
  818: 	char szFName[MAXPATHLEN], buf[BUFSIZ];
  819: 	struct tagHistory *h;
  820: 
  821: 	if (!cli_buffer) {
  822: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  823: 		return RETCODE_ERR;
  824: 	}
  825: 	if (!histfile)
  826: 		strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
  827: 	else
  828: 		strlcpy(szFName, histfile, MAXPATHLEN);
  829: 
  830: 	f = fopen(szFName, "r");
  831: 	if (!f)
  832: 		return RETCODE_OK;
  833: 
  834: 	while (fgets(buf, BUFSIZ, f)) {
  835: 		if (!*buf || *buf == '#')
  836: 			continue;
  837: 		else
  838: 			str_Trim(buf);
  839: 
  840: 		if (!(h = e_malloc(sizeof(struct tagHistory)))) {
  841: 			LOGERR;
  842: 			fclose(f);
  843: 			return RETCODE_ERR;
  844: 		} else
  845: 			memset(h, 0, sizeof(struct tagHistory));
  846: 
  847: 		h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ);
  848: 		TAILQ_INSERT_TAIL(&cli_buffer->line_history, h, hist_next);
  849: 	}
  850: 
  851: 	fclose(f);
  852: 
  853: 	return RETCODE_OK;
  854: }
  855: 
  856: /*
  857:  * cli_resetHistory() - Reset history search in CLI session
  858:  *
  859:  * @cli_buffer = CLI buffer
  860:  * return: none
  861: */
  862: void
  863: cli_resetHistory(linebuffer_t * __restrict cli_buffer)
  864: {
  865: 	cli_buffer->line_h = NULL;
  866: }
  867: 
  868: 
  869: /*
  870:  * cli_freeLine() - Clear entire line
  871:  *
  872:  * @cli_buffer = CLI buffer
  873:  * return: RETCODE_ERR error, RETCODE_OK ok
  874: */
  875: int
  876: cli_freeLine(linebuffer_t * __restrict cli_buffer)
  877: {
  878: 	int code = RETCODE_ERR;
  879: 
  880: 	if (cli_buffer) {
  881: 		if (cli_buffer->line_buf)
  882: 			e_free(cli_buffer->line_buf);
  883: 
  884: 		cli_buffer->line_buf = e_malloc(BUFSIZ);
  885: 		if (cli_buffer->line_buf) {
  886: 			memset(cli_buffer->line_buf, 0, BUFSIZ);
  887: 			cli_buffer->line_eol = cli_buffer->line_bol;
  888: 			cli_buffer->line_len = 1 + cli_buffer->line_eol;
  889: 
  890: 			code = RETCODE_OK;
  891: 		} else
  892: 			LOGERR;
  893: 	} else
  894: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  895: 
  896: 	return code;
  897: }
  898: 
  899: /*
  900:  * cli_setPrompt() - Set new prompt for CLI session
  901:  *
  902:  * @cli_buffer = CLI buffer
  903:  * @prompt = new text for prompt or if NULL disable prompt
  904:  * return: none
  905: */
  906: void
  907: cli_setPrompt(linebuffer_t * __restrict cli_buffer, const char *prompt)
  908: {
  909: 	if (cli_buffer) {
  910: 		if (cli_buffer->line_prompt) {
  911: 			e_free(cli_buffer->line_prompt);
  912: 			cli_buffer->line_prompt = NULL;
  913: 			cli_buffer->line_bol = 0;
  914: 		}
  915: 
  916: 		if (prompt) {
  917: 			cli_buffer->line_prompt = e_strdup(prompt);
  918: 			if (cli_buffer->line_prompt) {
  919: 				cli_buffer->line_bol = strlen(cli_buffer->line_prompt);
  920: 				cli_buffer->line_eol = cli_buffer->line_bol;
  921: 				cli_buffer->line_len = 1 + cli_buffer->line_eol;
  922: 			} else
  923: 				LOGERR;
  924: 		}
  925: 	} else
  926: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  927: }
  928: 
  929: 
  930: /*
  931:  * cliEnd() - Clear data, Free resources and close CLI session
  932:  *
  933:  * @cli_buffer = CLI buffer
  934:  * return: RETCODE_ERR error, RETCODE_OK ok
  935: */
  936: void
  937: cliEnd(linebuffer_t * __restrict cli_buffer)
  938: {
  939: 	struct tagHistory *h;
  940: 	struct tagCommand *c;
  941: 
  942: 	if (cli_buffer) {
  943: 		while ((c = SLIST_FIRST(&cli_buffer->line_cmds))) {
  944: 			SLIST_REMOVE_HEAD(&cli_buffer->line_cmds, cmd_next);
  945: 			e_free(c);
  946: 		}
  947: 		while ((h = TAILQ_FIRST(&cli_buffer->line_history))) {
  948: 			TAILQ_REMOVE(&cli_buffer->line_history, h, hist_next);
  949: 			e_free(h);
  950: 		}
  951: 
  952: 		if (cli_buffer->line_prompt)
  953: 			e_free(cli_buffer->line_prompt);
  954: 
  955: 		if (cli_buffer->line_keys)
  956: 			e_free(cli_buffer->line_keys);
  957: 		if (cli_buffer->line_buf)
  958: 			e_free(cli_buffer->line_buf);
  959: 
  960: 		e_free(cli_buffer);
  961: 		cli_buffer = NULL;
  962: 	} else
  963: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
  964: }
  965: 
  966: /*
  967:  * cliInit() - Start CLI session, allocate memory for resources and bind keys
  968:  *
  969:  * @fin = Input device handle
  970:  * @fout = Output device handle
  971:  * @prompt = text for prompt, if NULL disable prompt
  972:  * return: NULL if error or !=NULL CLI buffer
  973: */
  974: linebuffer_t *
  975: cliInit(int fin, int fout, const char *prompt)
  976: {
  977: 	linebuffer_t *cli_buffer;
  978: 	bindkey_t *keys;
  979: 	register int i;
  980: 
  981: 	/* init buffer */
  982: 	cli_buffer = e_malloc(sizeof(linebuffer_t));
  983: 	if (!cli_buffer) {
  984: 		LOGERR;
  985: 		return NULL;
  986: 	} else {
  987: 		memset(cli_buffer, 0, sizeof(linebuffer_t));
  988: 
  989: 		cli_buffer->line_in = fin;
  990: 		cli_buffer->line_out = fout;
  991: 
  992: 		TAILQ_INIT(&cli_buffer->line_history);
  993: 		SLIST_INIT(&cli_buffer->line_cmds);
  994: 
  995: 		if (prompt) {
  996: 			cli_buffer->line_prompt = e_strdup(prompt);
  997: 			if (!cli_buffer->line_prompt) {
  998: 				LOGERR;
  999: 				e_free(cli_buffer);
 1000: 				return NULL;
 1001: 			} else
 1002: 				cli_buffer->line_eol = cli_buffer->line_bol = 
 1003: 					strlen(cli_buffer->line_prompt);
 1004: 		} else
 1005: 			cli_buffer->line_mode = LINEMODE_OVER;
 1006: 	}
 1007: 	cli_buffer->line_buf = e_malloc(BUFSIZ);
 1008: 	if (!cli_buffer->line_buf) {
 1009: 		LOGERR;
 1010: 		if (cli_buffer->line_prompt)
 1011: 			e_free(cli_buffer->line_prompt);
 1012: 		e_free(cli_buffer);
 1013: 		return NULL;
 1014: 	} else {
 1015: 		memset(cli_buffer->line_buf, 0, BUFSIZ);
 1016: 		cli_buffer->line_len = 1 + cli_buffer->line_eol;
 1017: 	}
 1018: 	keys = e_calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
 1019: 	if (!keys) {
 1020: 		LOGERR;
 1021: 		if (cli_buffer->line_prompt)
 1022: 			e_free(cli_buffer->line_prompt);
 1023: 		e_free(cli_buffer->line_buf);
 1024: 		e_free(cli_buffer);
 1025: 		return NULL;
 1026: 	} else
 1027: 		memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
 1028: 
 1029: 	/* add helper functions */
 1030: 	cli_addCommand(cli_buffer, "exit", 1, cli_Cmd_Exit, "exit <cr>", "Exit from console");
 1031: 	cli_addCommand(cli_buffer, "help", -1, cli_Cmd_Help, "help [command] <cr>", "Help screen");
 1032: 	cli_addCommand(cli_buffer, "-------", -1, NULL, "-------------------------", NULL);
 1033: 	cli_addCommand(cli_buffer, "where", -1, cli_Cmd_WhereAmI, "where <cr>", 
 1034: 			"Query current level of console");
 1035: 	cli_addCommand(cli_buffer, "top", -1, cli_Cmd_Top, "top <cr>", "Top level of console");
 1036: 	cli_addCommand(cli_buffer, "end", -1, cli_Cmd_End, "end <cr>", "End level of console");
 1037: 	cli_addCommand(cli_buffer, "config", -1, cli_Cmd_Config, "config <cr>", 
 1038: 			"Config next level of console");
 1039: 	cli_addCommand(cli_buffer, "-------", -1, NULL, "-------------------------", NULL);
 1040: 
 1041: 	/* fill key bindings */
 1042: 	/* ascii chars & ctrl+chars */
 1043: 	for (i = 0; i < 256; i++) {
 1044: 		*keys[i].key_ch = (u_char) i;
 1045: 		keys[i].key_len = 1;
 1046: 
 1047: 		if (!i || i == *K_CTRL_D)
 1048: 			keys[i].key_func = bufEOF;
 1049: 		if (i == *K_CTRL_M || i == *K_CTRL_J)
 1050: 			keys[i].key_func = bufEOL;
 1051: 		if (cli_buffer->line_prompt && (i == *K_CTRL_H || i == *K_BACKSPACE))
 1052: 			keys[i].key_func = bufBS;
 1053: 		if (cli_buffer->line_prompt && i == *K_CTRL_C)
 1054: 			keys[i].key_func = bufCLR;
 1055: 		if (cli_buffer->line_prompt && i == *K_CTRL_A)
 1056: 			keys[i].key_func = bufBEGIN;
 1057: 		if (cli_buffer->line_prompt && i == *K_CTRL_E)
 1058: 			keys[i].key_func = bufEND;
 1059: 		if (cli_buffer->line_prompt && i == *K_TAB)
 1060: 			keys[i].key_func = bufComp;
 1061: 		if (cli_buffer->line_prompt && i == *K_CTRL_Z)
 1062: 			keys[i].key_func = bufEndNode;
 1063: 		if (i >= *K_SPACE && i < *K_BACKSPACE)
 1064: 			keys[i].key_func = bufCHAR;
 1065: 		if (i > *K_BACKSPACE && i < 0xff)
 1066: 			keys[i].key_func = bufCHAR;
 1067: 		if (cli_buffer->line_prompt && i == '?')
 1068: 			keys[i].key_func = bufHelp;
 1069: 	}
 1070: 	/* alt+chars */
 1071: 	for (i = 256; i < 512; i++) {
 1072: 		keys[i].key_ch[0] = 0x1b;
 1073: 		keys[i].key_ch[1] = (u_char) i - 256;
 1074: 		keys[i].key_len = 2;
 1075: 	}
 1076: 
 1077: 	/* 3 bytes */
 1078: 	keys[i].key_len = sizeof K_F1 - 1;
 1079: 	memcpy(keys[i].key_ch, K_F1, keys[i].key_len);
 1080: 	i++;
 1081: 	keys[i].key_len = sizeof K_F2 - 1;
 1082: 	memcpy(keys[i].key_ch, K_F2, keys[i].key_len);
 1083: 	i++;
 1084: 	keys[i].key_len = sizeof K_F3 - 1;
 1085: 	memcpy(keys[i].key_ch, K_F3, keys[i].key_len);
 1086: 	i++;
 1087: 	keys[i].key_len = sizeof K_F4 - 1;
 1088: 	memcpy(keys[i].key_ch, K_F4, keys[i].key_len);
 1089: 	i++;
 1090: 	keys[i].key_len = sizeof K_CTRL_SH_F1 - 1;
 1091: 	memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i].key_len);
 1092: 	i++;
 1093: 	keys[i].key_len = sizeof K_CTRL_SH_F2 - 1;
 1094: 	memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i].key_len);
 1095: 	i++;
 1096: 	keys[i].key_len = sizeof K_CTRL_SH_F3 - 1;
 1097: 	memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i].key_len);
 1098: 	i++;
 1099: 	keys[i].key_len = sizeof K_CTRL_SH_F4 - 1;
 1100: 	memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i].key_len);
 1101: 	i++;
 1102: 	keys[i].key_len = sizeof K_CTRL_SH_F5 - 1;
 1103: 	memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i].key_len);
 1104: 	i++;
 1105: 	keys[i].key_len = sizeof K_CTRL_SH_F6 - 1;
 1106: 	memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i].key_len);
 1107: 	i++;
 1108: 	keys[i].key_len = sizeof K_CTRL_SH_F7 - 1;
 1109: 	memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i].key_len);
 1110: 	i++;
 1111: 	keys[i].key_len = sizeof K_CTRL_SH_F8 - 1;
 1112: 	memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i].key_len);
 1113: 	i++;
 1114: 	keys[i].key_len = sizeof K_CTRL_SH_F9 - 1;
 1115: 	memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i].key_len);
 1116: 	i++;
 1117: 	keys[i].key_len = sizeof K_CTRL_SH_F10 - 1;
 1118: 	memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i].key_len);
 1119: 	i++;
 1120: 	keys[i].key_len = sizeof K_CTRL_SH_F11 - 1;
 1121: 	memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i].key_len);
 1122: 	i++;
 1123: 	keys[i].key_len = sizeof K_CTRL_SH_F12 - 1;
 1124: 	memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i].key_len);
 1125: 	i++;
 1126: 	keys[i].key_len = sizeof K_CTRL_F1 - 1;
 1127: 	memcpy(keys[i].key_ch, K_CTRL_F1, keys[i].key_len);
 1128: 	i++;
 1129: 	keys[i].key_len = sizeof K_CTRL_F2 - 1;
 1130: 	memcpy(keys[i].key_ch, K_CTRL_F2, keys[i].key_len);
 1131: 	i++;
 1132: 	keys[i].key_len = sizeof K_CTRL_F3 - 1;
 1133: 	memcpy(keys[i].key_ch, K_CTRL_F3, keys[i].key_len);
 1134: 	i++;
 1135: 	keys[i].key_len = sizeof K_CTRL_F4 - 1;
 1136: 	memcpy(keys[i].key_ch, K_CTRL_F4, keys[i].key_len);
 1137: 	i++;
 1138: 	keys[i].key_len = sizeof K_CTRL_F5 - 1;
 1139: 	memcpy(keys[i].key_ch, K_CTRL_F5, keys[i].key_len);
 1140: 	i++;
 1141: 	keys[i].key_len = sizeof K_CTRL_F6 - 1;
 1142: 	memcpy(keys[i].key_ch, K_CTRL_F6, keys[i].key_len);
 1143: 	i++;
 1144: 	keys[i].key_len = sizeof K_CTRL_F7 - 1;
 1145: 	memcpy(keys[i].key_ch, K_CTRL_F7, keys[i].key_len);
 1146: 	i++;
 1147: 	keys[i].key_len = sizeof K_CTRL_F8 - 1;
 1148: 	memcpy(keys[i].key_ch, K_CTRL_F8, keys[i].key_len);
 1149: 	i++;
 1150: 	keys[i].key_len = sizeof K_CTRL_F9 - 1;
 1151: 	memcpy(keys[i].key_ch, K_CTRL_F9, keys[i].key_len);
 1152: 	i++;
 1153: 	keys[i].key_len = sizeof K_CTRL_F10 - 1;
 1154: 	memcpy(keys[i].key_ch, K_CTRL_F10, keys[i].key_len);
 1155: 	i++;
 1156: 	keys[i].key_len = sizeof K_CTRL_F11 - 1;
 1157: 	memcpy(keys[i].key_ch, K_CTRL_F11, keys[i].key_len);
 1158: 	i++;
 1159: 	keys[i].key_len = sizeof K_CTRL_F12 - 1;
 1160: 	memcpy(keys[i].key_ch, K_CTRL_F12, keys[i].key_len);
 1161: 	i++;
 1162: 	keys[i].key_len = sizeof K_HOME - 1;
 1163: 	if (cli_buffer->line_prompt)
 1164: 		keys[i].key_func = bufBEGIN;
 1165: 	memcpy(keys[i].key_ch, K_HOME, keys[i].key_len);
 1166: 	i++;
 1167: 	keys[i].key_len = sizeof K_END - 1;
 1168: 	if (cli_buffer->line_prompt)
 1169: 		keys[i].key_func = bufEND;
 1170: 	memcpy(keys[i].key_ch, K_END, keys[i].key_len);
 1171: 	i++;
 1172: 	keys[i].key_len = sizeof K_UP - 1;
 1173: 	if (cli_buffer->line_prompt)
 1174: 		keys[i].key_func = bufUP;
 1175: 	memcpy(keys[i].key_ch, K_UP, keys[i].key_len);
 1176: 	i++;
 1177: 	keys[i].key_len = sizeof K_DOWN - 1;
 1178: 	if (cli_buffer->line_prompt)
 1179: 		keys[i].key_func = bufDOWN;
 1180: 	memcpy(keys[i].key_ch, K_DOWN, keys[i].key_len);
 1181: 	i++;
 1182: 	keys[i].key_len = sizeof K_RIGHT - 1;
 1183: 	if (cli_buffer->line_prompt)
 1184: 		keys[i].key_func = bufRIGHT;
 1185: 	memcpy(keys[i].key_ch, K_RIGHT, keys[i].key_len);
 1186: 	i++;
 1187: 	keys[i].key_len = sizeof K_LEFT - 1;
 1188: 	if (cli_buffer->line_prompt)
 1189: 		keys[i].key_func = bufLEFT;
 1190: 	memcpy(keys[i].key_ch, K_LEFT, keys[i].key_len);
 1191: 	i++;
 1192: 	keys[i].key_len = sizeof K_BTAB - 1;
 1193: 	if (cli_buffer->line_prompt)
 1194: 		keys[i].key_func = bufBTAB;
 1195: 	memcpy(keys[i].key_ch, K_BTAB, keys[i].key_len);
 1196: 	i++;
 1197: 	/* 4 bytes */
 1198: 	keys[i].key_len = sizeof K_INS - 1;
 1199: 	if (cli_buffer->line_prompt)
 1200: 		keys[i].key_func = bufMODE;
 1201: 	memcpy(keys[i].key_ch, K_INS, keys[i].key_len);
 1202: 	i++;
 1203: 	keys[i].key_len = sizeof K_DEL - 1;
 1204: 	if (cli_buffer->line_prompt)
 1205: 		keys[i].key_func = bufDEL;
 1206: 	memcpy(keys[i].key_ch, K_DEL, keys[i].key_len);
 1207: 	i++;
 1208: 	keys[i].key_len = sizeof K_PGUP - 1;
 1209: 	memcpy(keys[i].key_ch, K_PGUP, keys[i].key_len);
 1210: 	i++;
 1211: 	keys[i].key_len = sizeof K_PGDN - 1;
 1212: 	memcpy(keys[i].key_ch, K_PGDN, keys[i].key_len);
 1213: 	i++;
 1214: 	/* 5 bytes */
 1215: 	keys[i].key_len = sizeof K_F5 - 1;
 1216: 	memcpy(keys[i].key_ch, K_F5, keys[i].key_len);
 1217: 	i++;
 1218: 	keys[i].key_len = sizeof K_F6 - 1;
 1219: 	memcpy(keys[i].key_ch, K_F6, keys[i].key_len);
 1220: 	i++;
 1221: 	keys[i].key_len = sizeof K_F7 - 1;
 1222: 	memcpy(keys[i].key_ch, K_F7, keys[i].key_len);
 1223: 	i++;
 1224: 	keys[i].key_len = sizeof K_F8 - 1;
 1225: 	memcpy(keys[i].key_ch, K_F8, keys[i].key_len);
 1226: 	i++;
 1227: 	keys[i].key_len = sizeof K_F9 - 1;
 1228: 	memcpy(keys[i].key_ch, K_F9, keys[i].key_len);
 1229: 	i++;
 1230: 	keys[i].key_len = sizeof K_F10 - 1;
 1231: 	memcpy(keys[i].key_ch, K_F10, keys[i].key_len);
 1232: 	i++;
 1233: 	keys[i].key_len = sizeof K_F11 - 1;
 1234: 	memcpy(keys[i].key_ch, K_F11, keys[i].key_len);
 1235: 	i++;
 1236: 	keys[i].key_len = sizeof K_F12 - 1;
 1237: 	memcpy(keys[i].key_ch, K_F12, keys[i].key_len);
 1238: 	i++;
 1239: 
 1240: 	cli_buffer->line_keys = keys;
 1241: 	return cli_buffer;
 1242: }
 1243: 
 1244: /*
 1245:  * cliInitLine() - Init CLI input line terminal
 1246:  *
 1247:  * @cli_buffer = CLI buffer
 1248:  * return: none
 1249: */
 1250: int
 1251: cliInitLine(linebuffer_t * __restrict cli_buffer)
 1252: {
 1253: 	struct termios t;
 1254: 
 1255: 	memset(&t, 0, sizeof t);
 1256: 	tcgetattr(cli_buffer->line_in, &t);
 1257: 	t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | 
 1258: 			ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
 1259: 	t.c_iflag |= IGNBRK;
 1260: 	t.c_cc[VMIN] = 1;
 1261: 	t.c_cc[VTIME] = 0;
 1262: 	return tcsetattr(cli_buffer->line_in, TCSANOW, &t);
 1263: }
 1264: 
 1265: /*
 1266:  * cliReadLine() - Read line from opened CLI session
 1267:  *
 1268:  * @cli_buffer = CLI buffer
 1269:  * @timeout = Session timeout (-1 infinit)
 1270:  * @cmd_name = If timeout reached, we should call with this cmd_name (default name is "exit")
 1271:  * return: NULL if error or !=NULL readed line, must be e_free after use!
 1272: */
 1273: char *
 1274: cliReadLine(linebuffer_t * __restrict cli_buffer, int timeout, const char *cmd_name)
 1275: {
 1276: 	int code, readLen, ret;
 1277: 	register int i;
 1278: 	struct pollfd fds;
 1279: 	char buf[BUFSIZ], *str = NULL;
 1280: 
 1281: 	if (!cli_buffer) {
 1282: 		cli_SetErr(EINVAL, "Invalid input parameters ...");
 1283: 		return NULL;
 1284: 	} else if (timeout > 0)
 1285: 		timeout *= 1000;	/* convert from sec to ms */
 1286: 
 1287: 	memset(&fds, 0, sizeof fds);
 1288: 	fds.fd = cli_buffer->line_in;
 1289: 	fds.events = POLLIN;
 1290: 
 1291: 	printfCR(cli_buffer, 1);
 1292: 	while (42) {
 1293: 		if ((ret = poll(&fds, 1, timeout)) < 1) {
 1294: 			if (!ret) {
 1295: 				if (str)
 1296: 					e_free(str);
 1297: 				str = e_strdup(cmd_name ? cmd_name : "exit");
 1298: 			} else
 1299: 				LOGERR;
 1300: 			return str;
 1301: 		}
 1302: 
 1303: 		memset(buf, 0, sizeof buf);
 1304: 		readLen = read(cli_buffer->line_in, buf, BUFSIZ);
 1305: 		if (readLen == -1) {
 1306: 			LOGERR;
 1307: 			return str;
 1308: 		}
 1309: 		if (!readLen) {
 1310: 			if (cli_buffer->line_buf)
 1311: 				str = e_strdup(cli_buffer->line_buf);
 1312: 			else
 1313: 				cli_SetErr(EPIPE, "Unknown state ...");
 1314: 			return str;
 1315: 		}
 1316: 
 1317: recheck:
 1318: 		for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
 1319: 			if (readLen >= cli_buffer->line_keys[i].key_len && 
 1320: 					!memcmp(cli_buffer->line_keys[i].key_ch, buf, 
 1321: 						cli_buffer->line_keys[i].key_len)) {
 1322: 				readLen -= cli_buffer->line_keys[i].key_len;
 1323: 				if (readLen)
 1324: 					memmove(buf, buf + cli_buffer->line_keys[i].key_len, readLen);
 1325: 				else
 1326: 					memset(buf, 0, cli_buffer->line_keys[i].key_len);
 1327: 
 1328: 				if (cli_buffer->line_keys[i].key_func)
 1329: 					if ((code = cli_buffer->line_keys[i].key_func(i, cli_buffer)))
 1330: 						readLen = 0;
 1331: 
 1332: 				if (readLen)
 1333: 					goto recheck;
 1334: 				else
 1335: 					break;
 1336: 			}
 1337: 
 1338: 		if (code)
 1339: 			break;
 1340: 	}
 1341: 
 1342: 	if (code != RETCODE_ERR && code != RETCODE_EOF && cli_buffer->line_buf)
 1343: 		str = e_strdup(cli_buffer->line_buf);
 1344: 	return str;
 1345: }
 1346: 
 1347: 
 1348: /*
 1349:  * cliNetLoop() - CLI network main loop binded to socket
 1350:  *
 1351:  * @cli_buffer = CLI buffer
 1352:  * @csHistFile = History file name
 1353:  * @sock = client socket
 1354:  * @timeout = Session timeout (-1 infinit)
 1355:  * @cmd_name = If timeout reached, we should call with this cmd_name (default name is "exit")
 1356:  * return: RETCODE_ERR error, RETCODE_OK ok
 1357: */
 1358: int
 1359: cliNetLoop(linebuffer_t * __restrict cli_buffer, const char *csHistFile, 
 1360: 		int sock, int timeout, const char *cmd_name)
 1361: {
 1362: 	u_char buf[BUFSIZ];
 1363: 	int pid, stat, pty, r, s, alen, flg, attrlen = 0, ret = 0;
 1364: 	fd_set fds;
 1365: 	struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
 1366: 	struct telnetAttrs *a, Attr[10];
 1367: 
 1368: 	switch ((pid = forkpty(&pty, NULL, NULL, NULL))) {
 1369: 		case -1:
 1370: 			LOGERR;
 1371: 			return -1;
 1372: 		case 0:
 1373: 			if (!cli_buffer) {
 1374: 				cli_SetErr(EINVAL, "Invalid input parameters ...");
 1375: 				return -1;
 1376: 			} else
 1377: 				close(sock);
 1378: 
 1379: 			ret = cliLoop(cli_buffer, csHistFile, timeout, cmd_name) < 0 ? 1 : 0;
 1380: 			cliEnd(cli_buffer);
 1381: 
 1382: 			_exit(ret);
 1383: 		default:
 1384: 			cli_telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
 1385: 			cli_telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
 1386: 			cli_telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
 1387: 			cli_telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
 1388: 			cli_telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
 1389: 			if ((ret = cli_telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1)
 1390: 				return -1;
 1391: 			else
 1392: 				flg = 0;
 1393: 
 1394: 			while (42) {
 1395: 				if (waitpid(pid, &stat, WNOHANG))
 1396: 					break;
 1397: 
 1398: 				FD_ZERO(&fds);
 1399: 				FD_SET(sock, &fds);
 1400: 				FD_SET(pty, &fds);
 1401: 				if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
 1402: 					if (!ret)
 1403: 						cli_SetErr(ETIMEDOUT, "Client session timeout ...");
 1404: 
 1405: 					break;
 1406: 				}
 1407: 
 1408: 				r = FD_ISSET(sock, &fds) ? sock : pty;
 1409: 				s = FD_ISSET(sock, &fds) ? pty : sock;
 1410: 
 1411: 				if (FD_ISSET(sock, &fds)) {
 1412: 					memset(buf, 0, BUFSIZ);
 1413: 					if ((ret = cli_telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
 1414: 						if (a)
 1415: 							e_free(a);
 1416: 
 1417: 						if (-2 == ret)
 1418: 							continue;
 1419: 						// EOF
 1420: 						if (-3 == ret)
 1421: 							shutdown(sock, SHUT_RD);
 1422: 						break;
 1423: 					}
 1424: 					attrlen = 0;
 1425: 					if (1 == flg && alen) {
 1426: 						cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
 1427: 						cli_telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
 1428: 					}
 1429: 					if (2 == flg && alen) {
 1430: 						cli_telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
 1431: 						cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
 1432: 								LFLOW_OFF, NULL, 0);
 1433: 						cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
 1434: 								LFLOW_RESTART_XON, NULL, 0);
 1435: 						cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
 1436: 					}
 1437: 					if (a)
 1438: 						e_free(a);
 1439: 
 1440: 					if ((ret = write(pty, buf, ret)) == -1) {
 1441: 						LOGERR;
 1442: 						break;
 1443: 					}
 1444: 				}
 1445: 
 1446: 				if (FD_ISSET(pty, &fds)) {
 1447: 					memset(buf, 0, BUFSIZ);
 1448: 					if ((ret = read(pty, buf, BUFSIZ)) == -1) {
 1449: 						LOGERR;
 1450: 						break;
 1451: 					}
 1452: 
 1453: 					if ((ret = cli_telnetSend(sock, Attr, pty == s ? 0 : attrlen, 
 1454: 									buf, ret, 0)) == -1)
 1455: 						break;
 1456: 					else
 1457: 						flg++;
 1458: 				}
 1459: 			}
 1460: 
 1461: 			close(pty);
 1462: 	}
 1463: 
 1464: 	return ret;
 1465: }
 1466: 
 1467: /*
 1468:  * cliLoop() - CLI main loop
 1469:  *
 1470:  * @cli_buffer = CLI buffer
 1471:  * @csHistFile = History file name
 1472:  * @timeout = Session timeout (-1 infinit)
 1473:  * @cmd_name = If timeout reached, we should call with this cmd_name (default name is "exit")
 1474:  * return: RETCODE_ERR error, RETCODE_OK ok
 1475: */
 1476: int
 1477: cliLoop(linebuffer_t * __restrict cli_buffer, const char *csHistFile, 
 1478: 		int timeout, const char *cmd_name)
 1479: {
 1480: 	char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
 1481: 	register int i;
 1482: 	int ret = RETCODE_OK;
 1483: 	struct tagCommand *cmd;
 1484: 
 1485: 	/* --- main body of CLI --- */
 1486: 	cliInitLine(cli_buffer);
 1487: 
 1488: 	if (cli_loadHistory(cli_buffer, csHistFile) == RETCODE_ERR)
 1489: 		return RETCODE_ERR;
 1490: 
 1491: 	do {
 1492: 		line = cliReadLine(cli_buffer, timeout, cmd_name);
 1493: 		if (!line) {
 1494: 			printfNL(cli_buffer, 0);
 1495: 			break;
 1496: 		} else
 1497: 			cli_addHistory(cli_buffer, NULL);
 1498: 		// clear whitespaces
 1499: 		for (s = line; isspace((int) *s); s++);
 1500: 		if (*s) {
 1501: 			for (t = s + strlen(s) - 1; t > s && isspace((int) *t); t--);
 1502: 			*++t = 0;
 1503: 		}
 1504: 
 1505: 		if (*s) {
 1506: 			memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
 1507: 			for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && 
 1508: 					(*app = strsep(&s, " \t")); *app ? app++ : app);
 1509: 
 1510: 			// exec_cmd ...
 1511: 			i = 0;
 1512: 			SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next) {
 1513: 				if (!(cmd->cmd_level & (1 << cli_buffer->line_level)))
 1514: 					continue;
 1515: 				if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0])))
 1516: 					break;
 1517: 				else
 1518: 					i++;
 1519: 			}
 1520: 
 1521: 			if (!cmd) {
 1522: 				cli_Printf(cli_buffer, "\nCommand '%s' not found!\n", items[0]);
 1523: 				ret = -1;
 1524: 			} else
 1525: 				if (cmd->cmd_func) {
 1526: 					cli_Printf(cli_buffer, "\n");
 1527: 					ret = cmd->cmd_func(cli_buffer, 
 1528: 							cli_buffer->line_level, items);
 1529: 				} else {
 1530: 					clrscrEOL(cli_buffer);
 1531: 					printfCR(cli_buffer, 1);
 1532: 				}
 1533: 		}
 1534: 
 1535: 		cli_freeLine(cli_buffer);
 1536: 		cli_resetHistory(cli_buffer);
 1537: 		e_free(line);
 1538: 	} while (ret < 1);
 1539: 
 1540: 	cli_saveHistory(cli_buffer, csHistFile, HISTORY_LINES);
 1541: 	return ret;
 1542: }

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