File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / aitcli.c
Revision 1.19: download - view: text, annotated - select for diffs - revision graph
Mon Dec 5 22:23:38 2022 UTC (17 months, 3 weeks ago) by misho
Branches: MAIN
CVS tags: cli4_6, cli4_5, HEAD, CLI4_5, CLI4_4
version 4.4

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

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