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