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