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