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