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