1: /*************************************************************************
2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
6: * $Id: aitcli.c,v 1.19 2022/12/05 22:23:38 misho Exp $
7: *
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:
15: Copyright 2004 - 2022
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: */
46: #include "global.h"
47: #include "cli.h"
48:
49:
50: #pragma GCC visibility push(hidden)
51:
52: int cli_Errno;
53: char cli_Error[STRSIZ];
54:
55: #pragma GCC visibility pop
56:
57: // cli_GetErrno() Get error code of last operation
58: int
59: cli_GetErrno()
60: {
61: return cli_Errno;
62: }
63:
64: // cli_GetError() Get error text of last operation
65: const char *
66: cli_GetError()
67: {
68: return cli_Error;
69: }
70:
71: // cli_SetErr() Set error to variables for internal use!!!
72: void
73: cli_SetErr(int eno, char *estr, ...)
74: {
75: va_list lst;
76:
77: cli_Errno = eno;
78: memset(cli_Error, 0, sizeof cli_Error);
79: va_start(lst, estr);
80: vsnprintf(cli_Error, sizeof cli_Error, estr, lst);
81: va_end(lst);
82: }
83:
84: // ------------------------------------------------------------
85:
86: static inline void
87: clrscrEOL(linebuffer_t * __restrict buf)
88: {
89: register int i;
90: int ign __attribute__((unused));
91:
92: if (buf && buf->line_prompt) {
93: ign = write(buf->line_out, K_CR, 1);
94:
95: for (i = 0; i < buf->line_len; i++)
96: ign = write(buf->line_out, K_SPACE, 1);
97: }
98: }
99:
100: static inline void
101: printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
102: {
103: int ign __attribute__((unused));
104:
105: if (buf) {
106: if (prompt && buf->line_prompt) {
107: ign = write(buf->line_out, K_CR, 1);
108: ign = write(buf->line_out, buf->line_prompt, buf->line_bol);
109: }
110:
111: ign = write(buf->line_out, buf->line_buf, len == -1 ?
112: buf->line_eol - buf->line_bol : len);
113: }
114: }
115:
116: static inline void
117: printfCR(linebuffer_t * __restrict buf, int prompt)
118: {
119: int ign __attribute__((unused));
120:
121: if (buf && prompt && buf->line_prompt) {
122: ign = write(buf->line_out, K_CR, 1);
123: ign = write(buf->line_out, buf->line_prompt, buf->line_bol);
124: }
125: }
126:
127: static inline void
128: printfNL(linebuffer_t * __restrict buf, int prompt)
129: {
130: int ign __attribute__((unused));
131:
132: if (buf) {
133: ign = write(buf->line_out, K_ENTER, 1);
134:
135: if (prompt)
136: if (prompt && buf->line_prompt)
137: ign = write(buf->line_out, buf->line_prompt, buf->line_bol);
138: }
139: }
140:
141: // ------------------------------------------------------------
142:
143: static int
144: bufCHAR(int idx, void * __restrict cli_buffer)
145: {
146: linebuffer_t *buf = cli_buffer;
147: int pos;
148: int ign __attribute__((unused));
149:
150: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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:
165: if (buf->line_prompt)
166: ign = write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
167:
168: if (buf->line_mode == LINEMODE_INS) {
169: ign = write(buf->line_out, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len,
170: buf->line_len - buf->line_eol);
171: printfEOL(buf, -1, 1);
172: }
173: return RETCODE_OK;
174: }
175:
176: static int
177: bufEOL(int idx, void * __restrict cli_buffer)
178: {
179: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
180: return RETCODE_ERR;
181:
182: printfCR(cli_buffer, 1);
183: return RETCODE_EOL;
184: }
185:
186: static int
187: bufEOF(int idx, void * __restrict cli_buffer)
188: {
189: /*
190: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
191: return RETCODE_ERR;
192: */
193:
194: return RETCODE_EOF;
195: }
196:
197: static int
198: bufUP(int idx, void * __restrict cli_buffer)
199: {
200: linebuffer_t *buf = cli_buffer;
201: int pos;
202:
203: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
229: bufDOWN(int idx, void * __restrict cli_buffer)
230: {
231: linebuffer_t *buf = cli_buffer;
232: int pos;
233:
234: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
260: bufCLR(int idx, void * __restrict cli_buffer)
261: {
262: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
263: return RETCODE_ERR;
264:
265: clrscrEOL(cli_buffer);
266: cli_freeLine(cli_buffer);
267:
268: printfCR(cli_buffer, 1);
269: return RETCODE_OK;
270: }
271:
272: static int
273: bufBS(int idx, void * __restrict cli_buffer)
274: {
275: linebuffer_t *buf = cli_buffer;
276:
277: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
298: bufBTAB(int idx, void * __restrict cli_buffer)
299: {
300: linebuffer_t *buf = cli_buffer;
301:
302: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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);
312: }
313:
314: return RETCODE_OK;
315: }
316:
317: static int
318: bufMODE(int idx, void * __restrict cli_buffer)
319: {
320: linebuffer_t *buf = cli_buffer;
321:
322: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
330: bufBEGIN(int idx, void * __restrict cli_buffer)
331: {
332: linebuffer_t *buf = cli_buffer;
333:
334: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
344: bufEND(int idx, void * __restrict cli_buffer)
345: {
346: linebuffer_t *buf = cli_buffer;
347:
348: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
349: return RETCODE_ERR;
350:
351: buf->line_eol = buf->line_len - 1;
352:
353: printfEOL(buf, -1, 1);
354: return RETCODE_OK;
355: }
356:
357: static int
358: bufLEFT(int idx, void * __restrict cli_buffer)
359: {
360: linebuffer_t *buf = cli_buffer;
361:
362: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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: }
370:
371: static int
372: bufRIGHT(int idx, void * __restrict cli_buffer)
373: {
374: linebuffer_t *buf = cli_buffer;
375:
376: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
386: bufDEL(int idx, void * __restrict cli_buffer)
387: {
388: linebuffer_t *buf = cli_buffer;
389:
390: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
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
408: bufComp(int idx, void * __restrict cli_buffer)
409: {
410: linebuffer_t *buf = cli_buffer;
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;
415: int ign __attribute__((unused));
416:
417: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
418: return RETCODE_ERR;
419:
420: str = e_strdup(buf->line_buf);
421: if (!str)
422: return RETCODE_ERR;
423: else {
424: s = str;
425: str_Trim(s);
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);
433: for (app = items, i = 0; app < items + MAX_PROMPT_ITEMS - 1 &&
434: (*app = strsep(&s, " \t"));
435: *app ? i++ : i, *app ? app++ : app);
436:
437: if (i) {
438: SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
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))) {
444: j++;
445: c = cmd;
446: strlcat(szLine, " ", STRSIZ);
447: strlcat(szLine, cmd->cmd_name, STRSIZ);
448: }
449: }
450: }
451:
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;
461: }
462: } else {
463: /* we on 0 position of prompt, show commands for this level */
464: SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
465: if (cmd->cmd_level & (1 << buf->line_level))
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: }
474:
475: /* completion show actions ... */
476: if (j > 1 && c) {
477: printfNL(buf, 0);
478: ign = write(buf->line_out, szLine, strlen(szLine));
479: printfNL(buf, 1);
480: printfEOL(buf, buf->line_len - 1, 1);
481: printfEOL(buf, -1, 1);
482: }
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;
495:
496: printfEOL(buf, -1, 1);
497: }
498:
499: endcomp:
500: e_free(str);
501: return ret;
502: }
503:
504: static int
505: bufHelp(int idx, void * __restrict cli_buffer)
506: {
507: linebuffer_t *buf = cli_buffer;
508:
509: if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
510: return RETCODE_ERR;
511:
512: cli_Cmd_Help(buf, buf->line_level, NULL);
513:
514: printfEOL(buf, buf->line_len - 1, 1);
515: printfEOL(buf, -1, 1);
516: return RETCODE_OK;
517: }
518:
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:
536:
537: /*
538: * cli_Printf() - Send message to CLI session
539: *
540: * @cli_buffer = CLI buffer
541: * @fmt = printf format string
542: * @... = arguments defined in fmt
543: * return: none
544: */
545: void
546: cli_Printf(linebuffer_t * __restrict cli_buffer, char *fmt, ...)
547: {
548: va_list lst;
549: FILE *f;
550:
551: if (fmt) {
552: f = fdopen(cli_buffer->line_out, "a");
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
562: cli_SetErr(EINVAL, "Invalid input parameters ...");
563: }
564:
565: /*
566: * cli_PrintHelp() - Print help screen
567: *
568: * @cli_buffer = CLI buffer
569: * return: none
570: */
571: void
572: cli_PrintHelp(linebuffer_t * __restrict cli_buffer)
573: {
574: if (cli_buffer) {
575: bufHelp(0, cli_buffer);
576: clrscrEOL(cli_buffer);
577: } else
578: cli_SetErr(EINVAL, "Invalid input parameters ...");
579: }
580:
581:
582: /*
583: * cli_BindKey() - Bind function to key
584: *
585: * @key = key structure
586: * @cli_buffer = CLI buffer
587: * return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position
588: */
589: int
590: cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict cli_buffer)
591: {
592: register int i;
593:
594: if (!key || !cli_buffer) {
595: cli_SetErr(EINVAL, "Invalid input parameters ...");
596: return RETCODE_ERR;
597: }
598:
599: for (i = 0; i < MAX_BINDKEY; i++)
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;
604: return i;
605: }
606:
607: return RETCODE_OK;
608: }
609:
610:
611: /*
612: * cli_addCommand() - Add command to CLI session
613: *
614: * @cli_buffer = CLI buffer
615: * @csCmd = Command name
616: * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
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
623: cli_addCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd,
624: int cliLevel, cmd_func_t funcCmd,
625: const char *csInfo, const char *csHelp)
626: {
627: struct tagCommand *cmd;
628:
629: if (!cli_buffer || !csCmd) {
630: cli_SetErr(EINVAL, "Invalid input parameters ...");
631: return RETCODE_ERR;
632: }
633:
634: cmd = e_malloc(sizeof(struct tagCommand));
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);
648: SLIST_INSERT_HEAD(&cli_buffer->line_cmds, cmd, cmd_next);
649: return RETCODE_OK;
650: }
651:
652: /*
653: * cli_delCommand() - Delete command from CLI session
654: *
655: * @cli_buffer = CLI buffer
656: * @csCmd = Command name
657: * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
658: * return: RETCODE_ERR error, RETCODE_OK ok
659: */
660: int
661: cli_delCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd, int cliLevel)
662: {
663: struct tagCommand *cmd;
664: int ret = RETCODE_OK;
665:
666: if (!cli_buffer || !csCmd) {
667: cli_SetErr(EINVAL, "Invalid input parameters ...");
668: return RETCODE_ERR;
669: }
670:
671: SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next)
672: if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
673: ret = 1;
674: SLIST_REMOVE(&cli_buffer->line_cmds, cmd, tagCommand, cmd_next);
675: e_free(cmd);
676: break;
677: }
678:
679: return ret;
680: }
681:
682: /*
683: * cli_updCommand() - Update command in CLI session
684: *
685: * @cli_buffer = CLI buffer
686: * @csCmd = Command name
687: * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
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
694: cli_updCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd,
695: int cliLevel, cmd_func_t funcCmd,
696: const char *csInfo, const char *csHelp)
697: {
698: struct tagCommand *cmd;
699: int ret = RETCODE_OK;
700:
701: if (!cli_buffer || !csCmd) {
702: cli_SetErr(EINVAL, "Invalid input parameters ...");
703: return RETCODE_ERR;
704: }
705:
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;
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: }
720:
721: return ret;
722: }
723:
724:
725: /*
726: * cli_addHistory() - Add line to history
727: *
728: * @cli_buffer = CLI buffer
729: * @str = Add custom text or if NULL use readed line from CLI buffer
730: * return: RETCODE_ERR error, RETCODE_OK ok
731: */
732: int
733: cli_addHistory(linebuffer_t * __restrict cli_buffer, const char * __restrict str)
734: {
735: struct tagHistory *h;
736:
737: if (!cli_buffer) {
738: cli_SetErr(EINVAL, "Invalid input parameters ...");
739: return RETCODE_ERR;
740: }
741:
742: if (!(h = e_malloc(sizeof(struct tagHistory)))) {
743: LOGERR;
744: return RETCODE_ERR;
745: } else
746: memset(h, 0, sizeof(struct tagHistory));
747:
748: if (str) {
749: if (!*str) {
750: e_free(h);
751: return RETCODE_OK;
752: }
753:
754: h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
755: } else {
756: if (!*cli_buffer->line_buf || cli_buffer->line_len < 2) {
757: e_free(h);
758: return RETCODE_OK;
759: }
760:
761: memcpy(h->hist_line, cli_buffer->line_buf, (h->hist_len = cli_buffer->line_len));
762: str_Trim(h->hist_line);
763: h->hist_len = strlen(h->hist_line);
764: }
765:
766: TAILQ_INSERT_HEAD(&cli_buffer->line_history, h, hist_next);
767: return h->hist_len;
768: }
769:
770: /*
771: * cli_saveHistory() - Save history to file
772: *
773: * @cli_buffer = CLI buffer
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
779: cli_saveHistory(linebuffer_t * __restrict cli_buffer, const char *histfile, int lines)
780: {
781: FILE *f;
782: mode_t mode;
783: char szFName[MAXPATHLEN];
784: struct tagHistory *h;
785:
786: if (!cli_buffer) {
787: cli_SetErr(EINVAL, "Invalid input parameters ...");
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) {
798: LOGERR;
799: return RETCODE_ERR;
800: }
801:
802: TAILQ_FOREACH(h, &cli_buffer->line_history, hist_next) {
803: fprintf(f, "%s\n", h->hist_line);
804:
805: if (lines)
806: lines--;
807: else
808: break;
809: }
810:
811: fclose(f);
812: umask(mode);
813:
814: return RETCODE_OK;
815: }
816:
817: /*
818: * cli_loadHistory() - Load history from file
819: *
820: * @cli_buffer = CLI buffer
821: * @histfile = History filename, if NULL will be use default name
822: * return: RETCODE_ERR error, RETCODE_OK ok
823: */
824: int
825: cli_loadHistory(linebuffer_t * __restrict cli_buffer, const char *histfile)
826: {
827: FILE *f;
828: char szFName[MAXPATHLEN], buf[BUFSIZ];
829: struct tagHistory *h;
830:
831: if (!cli_buffer) {
832: cli_SetErr(EINVAL, "Invalid input parameters ...");
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
848: str_Trim(buf);
849:
850: if (!(h = e_malloc(sizeof(struct tagHistory)))) {
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);
858: TAILQ_INSERT_TAIL(&cli_buffer->line_history, h, hist_next);
859: }
860:
861: fclose(f);
862:
863: return RETCODE_OK;
864: }
865:
866: /*
867: * cli_resetHistory() - Reset history search in CLI session
868: *
869: * @cli_buffer = CLI buffer
870: * return: none
871: */
872: void
873: cli_resetHistory(linebuffer_t * __restrict cli_buffer)
874: {
875: cli_buffer->line_h = NULL;
876: }
877:
878:
879: /*
880: * cli_freeLine() - Clear entire line
881: *
882: * @cli_buffer = CLI buffer
883: * return: RETCODE_ERR error, RETCODE_OK ok
884: */
885: int
886: cli_freeLine(linebuffer_t * __restrict cli_buffer)
887: {
888: int code = RETCODE_ERR;
889:
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;
899:
900: code = RETCODE_OK;
901: } else
902: LOGERR;
903: } else
904: cli_SetErr(EINVAL, "Invalid input parameters ...");
905:
906: return code;
907: }
908:
909: /*
910: * cli_setPrompt() - Set new prompt for CLI session
911: *
912: * @cli_buffer = CLI buffer
913: * @prompt = new text for prompt or if NULL disable prompt
914: * return: none
915: */
916: void
917: cli_setPrompt(linebuffer_t * __restrict cli_buffer, const char *prompt)
918: {
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;
924: }
925:
926: if (prompt) {
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;
932: } else
933: LOGERR;
934: }
935: } else
936: cli_SetErr(EINVAL, "Invalid input parameters ...");
937: }
938:
939:
940: /*
941: * cliEnd() - Clear data, Free resources and close CLI session
942: *
943: * @cli_buffer = CLI buffer
944: * return: RETCODE_ERR error, RETCODE_OK ok
945: */
946: void
947: cliEnd(linebuffer_t * __restrict cli_buffer)
948: {
949: struct tagHistory *h;
950: struct tagCommand *c;
951:
952: if (cli_buffer) {
953: while ((c = SLIST_FIRST(&cli_buffer->line_cmds))) {
954: SLIST_REMOVE_HEAD(&cli_buffer->line_cmds, cmd_next);
955: e_free(c);
956: }
957: while ((h = TAILQ_FIRST(&cli_buffer->line_history))) {
958: TAILQ_REMOVE(&cli_buffer->line_history, h, hist_next);
959: e_free(h);
960: }
961:
962: if (cli_buffer->line_prompt)
963: e_free(cli_buffer->line_prompt);
964:
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);
969:
970: e_free(cli_buffer);
971: cli_buffer = NULL;
972: } else
973: cli_SetErr(EINVAL, "Invalid input parameters ...");
974: }
975:
976: /*
977: * cliInit() - Start CLI session, allocate memory for resources and bind keys
978: *
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: {
987: linebuffer_t *cli_buffer;
988: bindkey_t *keys;
989: register int i;
990: char szPrompt[STRSIZ + 16] = {[0 ... STRSIZ + 15] = 0};
991:
992: /* init buffer */
993: cli_buffer = e_malloc(sizeof(linebuffer_t));
994: if (!cli_buffer) {
995: LOGERR;
996: return NULL;
997: } else {
998: memset(cli_buffer, 0, sizeof(linebuffer_t));
999:
1000: cli_buffer->line_in = fin;
1001: cli_buffer->line_out = fout;
1002:
1003: TAILQ_INIT(&cli_buffer->line_history);
1004: SLIST_INIT(&cli_buffer->line_cmds);
1005:
1006: if (prompt) {
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);
1010: if (!cli_buffer->line_prompt) {
1011: LOGERR;
1012: e_free(cli_buffer);
1013: return NULL;
1014: } else
1015: cli_buffer->line_eol = cli_buffer->line_bol =
1016: strlen(cli_buffer->line_prompt);
1017: } else
1018: cli_buffer->line_mode = LINEMODE_OVER;
1019: }
1020: cli_buffer->line_buf = e_malloc(BUFSIZ);
1021: if (!cli_buffer->line_buf) {
1022: LOGERR;
1023: if (cli_buffer->line_prompt)
1024: e_free(cli_buffer->line_prompt);
1025: e_free(cli_buffer);
1026: return NULL;
1027: } else {
1028: memset(cli_buffer->line_buf, 0, BUFSIZ);
1029: cli_buffer->line_len = 1 + cli_buffer->line_eol;
1030: }
1031: keys = e_calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
1032: if (!keys) {
1033: LOGERR;
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);
1038: return NULL;
1039: } else
1040: memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
1041:
1042: /* add helper functions */
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);
1053:
1054: /* fill key bindings */
1055: /* ascii chars & ctrl+chars */
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;
1064: if (cli_buffer->line_prompt && (i == *K_CTRL_H || i == *K_BACKSPACE))
1065: keys[i].key_func = bufBS;
1066: if (i == *K_CTRL_C)
1067: keys[i].key_func = bufCLR;
1068: if (cli_buffer->line_prompt && i == *K_CTRL_A)
1069: keys[i].key_func = bufBEGIN;
1070: if (cli_buffer->line_prompt && i == *K_CTRL_E)
1071: keys[i].key_func = bufEND;
1072: if (cli_buffer->line_prompt && i == *K_TAB)
1073: keys[i].key_func = bufComp;
1074: if (i == *K_CTRL_Z)
1075: keys[i].key_func = bufEndNode;
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;
1080: if (cli_buffer->line_prompt && i == '?')
1081: keys[i].key_func = bufHelp;
1082: }
1083: /* alt+chars */
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:
1090: /* 3 bytes */
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;
1176: if (cli_buffer->line_prompt)
1177: keys[i].key_func = bufBEGIN;
1178: memcpy(keys[i].key_ch, K_HOME, keys[i].key_len);
1179: i++;
1180: keys[i].key_len = sizeof K_END - 1;
1181: if (cli_buffer->line_prompt)
1182: keys[i].key_func = bufEND;
1183: memcpy(keys[i].key_ch, K_END, keys[i].key_len);
1184: i++;
1185: keys[i].key_len = sizeof K_UP - 1;
1186: if (cli_buffer->line_prompt)
1187: keys[i].key_func = bufUP;
1188: memcpy(keys[i].key_ch, K_UP, keys[i].key_len);
1189: i++;
1190: keys[i].key_len = sizeof K_DOWN - 1;
1191: if (cli_buffer->line_prompt)
1192: keys[i].key_func = bufDOWN;
1193: memcpy(keys[i].key_ch, K_DOWN, keys[i].key_len);
1194: i++;
1195: keys[i].key_len = sizeof K_RIGHT - 1;
1196: if (cli_buffer->line_prompt)
1197: keys[i].key_func = bufRIGHT;
1198: memcpy(keys[i].key_ch, K_RIGHT, keys[i].key_len);
1199: i++;
1200: keys[i].key_len = sizeof K_LEFT - 1;
1201: if (cli_buffer->line_prompt)
1202: keys[i].key_func = bufLEFT;
1203: memcpy(keys[i].key_ch, K_LEFT, keys[i].key_len);
1204: i++;
1205: keys[i].key_len = sizeof K_BTAB - 1;
1206: if (cli_buffer->line_prompt)
1207: keys[i].key_func = bufBTAB;
1208: memcpy(keys[i].key_ch, K_BTAB, keys[i].key_len);
1209: i++;
1210: /* 4 bytes */
1211: keys[i].key_len = sizeof K_INS - 1;
1212: if (cli_buffer->line_prompt)
1213: keys[i].key_func = bufMODE;
1214: memcpy(keys[i].key_ch, K_INS, keys[i].key_len);
1215: i++;
1216: keys[i].key_len = sizeof K_DEL - 1;
1217: if (cli_buffer->line_prompt)
1218: keys[i].key_func = bufDEL;
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++;
1227: /* 5 bytes */
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:
1253: cli_buffer->line_keys = keys;
1254: return cli_buffer;
1255: }
1256:
1257: /*
1258: * cliSetLine() - Set CLI input line terminal
1259: *
1260: * @cli_buffer = CLI buffer
1261: * @old = Old terminal settings
1262: * return: -1 error or 0 ok
1263: */
1264: int
1265: cliSetLine(linebuffer_t * __restrict cli_buffer, struct termios * __restrict old)
1266: {
1267: struct termios t;
1268:
1269: memset(&t, 0, sizeof t);
1270: tcgetattr(cli_buffer->line_in, &t);
1271: if (old)
1272: memcpy(old, &t, sizeof(struct termios));
1273: t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO |
1274: ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
1275: t.c_iflag |= IGNBRK;
1276: t.c_cc[VMIN] = 1;
1277: t.c_cc[VTIME] = 0;
1278: return tcsetattr(cli_buffer->line_in, TCSANOW, &t);
1279: }
1280:
1281: /*
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: /*
1295: * cliReadLine() - Read line from opened CLI session
1296: *
1297: * @cli_buffer = CLI buffer
1298: * @timeout = Session timeout (-1 infinit)
1299: * return: NULL if error or !=NULL readed line, must be e_free after use!
1300: */
1301: char *
1302: cliReadLine(linebuffer_t * __restrict cli_buffer, int timeout)
1303: {
1304: int code, readLen, ret;
1305: register int i;
1306: struct pollfd fds;
1307: char buf[BUFSIZ], *str = NULL;
1308:
1309: if (!cli_buffer) {
1310: cli_SetErr(EINVAL, "Invalid input parameters ...");
1311: return NULL;
1312: } else if (timeout > 0)
1313: timeout *= 1000; /* convert from sec to ms */
1314:
1315: memset(&fds, 0, sizeof fds);
1316: fds.fd = cli_buffer->line_in;
1317: fds.events = POLLIN;
1318:
1319: printfCR(cli_buffer, 1);
1320: while (42) {
1321: if ((ret = poll(&fds, 1, timeout)) < 1) {
1322: if (!ret) {
1323: cli_buffer->line_kill = 1;
1324: if (str) {
1325: e_free(str);
1326: str = NULL;
1327: }
1328: } else
1329: LOGERR;
1330: return str;
1331: }
1332:
1333: memset(buf, 0, sizeof buf);
1334: readLen = read(cli_buffer->line_in, buf, BUFSIZ);
1335: if (readLen < 1) {
1336: if (readLen)
1337: LOGERR;
1338: return NULL;
1339: }
1340:
1341: recheck:
1342: for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
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;
1347: if (readLen)
1348: memmove(buf, buf + cli_buffer->line_keys[i].key_len, readLen);
1349: else
1350: memset(buf, 0, cli_buffer->line_keys[i].key_len);
1351:
1352: if (cli_buffer->line_keys[i].key_func)
1353: if ((code = cli_buffer->line_keys[i].key_func(i, cli_buffer)))
1354: readLen = 0;
1355:
1356: if (readLen)
1357: goto recheck;
1358: else
1359: break;
1360: }
1361:
1362: if (code)
1363: break;
1364: }
1365:
1366: if (code != RETCODE_ERR && code != RETCODE_EOF && cli_buffer->line_buf)
1367: str = e_strdup(cli_buffer->line_buf);
1368: return str;
1369: }
1370:
1371:
1372: /*
1373: * cliNetLoop() - CLI network main loop binded to socket
1374: *
1375: * @cli_buffer = CLI buffer
1376: * @csHistFile = History file name
1377: * @sock = client socket
1378: * @timeout = Session timeout (-1 infinit)
1379: * return: RETCODE_ERR error, RETCODE_OK ok
1380: */
1381: int
1382: cliNetLoop(linebuffer_t * __restrict cli_buffer, const char *csHistFile,
1383: int sock, int timeout)
1384: {
1385: u_char buf[BUFSIZ];
1386: int pid, stat, pty, s, alen, flg, attrlen = 0, ret = 0;
1387: fd_set fds;
1388: struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
1389: struct telnetAttrs *a, Attr[10];
1390:
1391: switch ((pid = forkpty(&pty, NULL, NULL, NULL))) {
1392: case -1:
1393: LOGERR;
1394: return -1;
1395: case 0:
1396: if (!cli_buffer) {
1397: cli_SetErr(EINVAL, "Invalid input parameters ...");
1398: return -1;
1399: } else
1400: close(sock);
1401:
1402: ret = cliLoop(cli_buffer, csHistFile, timeout) < 0 ? 1 : 0;
1403: cliEnd(cli_buffer);
1404:
1405: _exit(ret);
1406: default:
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)
1413: return -1;
1414: else
1415: flg = 0;
1416:
1417: while (42) {
1418: if (waitpid(pid, &stat, WNOHANG))
1419: break;
1420:
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:
1433: if (FD_ISSET(sock, &fds)) {
1434: memset(buf, 0, BUFSIZ);
1435: if ((ret = cli_telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
1436: if (a)
1437: e_free(a);
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) {
1448: cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
1449: cli_telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
1450: }
1451: if (2 == flg && alen) {
1452: cli_telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
1453: cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
1454: LFLOW_OFF, NULL, 0);
1455: cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
1456: LFLOW_RESTART_XON, NULL, 0);
1457: cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
1458: }
1459: if (a)
1460: e_free(a);
1461:
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);
1470: if ((ret = read(pty, buf, BUFSIZ)) < 1) {
1471: if (ret)
1472: LOGERR;
1473: break;
1474: }
1475:
1476: if ((ret = cli_telnetSend(sock, Attr, pty == s ? 0 : attrlen,
1477: buf, ret, 0)) == -1)
1478: break;
1479: else
1480: flg++;
1481: }
1482: }
1483:
1484: close(pty);
1485: }
1486:
1487: return ret;
1488: }
1489:
1490: /*
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: /*
1554: * cliLoop() - CLI main loop
1555: *
1556: * @cli_buffer = CLI buffer
1557: * @csHistFile = History file name
1558: * @timeout = Session timeout (-1 infinit)
1559: * return: RETCODE_ERR error, RETCODE_OK ok
1560: */
1561: int
1562: cliLoop(linebuffer_t * __restrict cli_buffer, const char *csHistFile, int timeout)
1563: {
1564: char *line;
1565: int ret = RETCODE_OK;
1566: struct termios t;
1567:
1568: /* --- main body of CLI --- */
1569: cliSetLine(cli_buffer, &t);
1570:
1571: if (cli_loadHistory(cli_buffer, csHistFile) == RETCODE_ERR)
1572: return RETCODE_ERR;
1573:
1574: do {
1575: line = cliReadLine(cli_buffer, timeout);
1576: if (!line) {
1577: printfNL(cli_buffer, 0);
1578: break;
1579: } else
1580: cli_addHistory(cli_buffer, NULL);
1581:
1582: ret = cliRun(cli_buffer, line, 42);
1583:
1584: cli_freeLine(cli_buffer);
1585: cli_resetHistory(cli_buffer);
1586: e_free(line);
1587: } while (!cli_buffer->line_kill);
1588:
1589: cli_saveHistory(cli_buffer, csHistFile, HISTORY_LINES);
1590:
1591: /* --- restore tty --- */
1592: cliResetLine(cli_buffer, &t);
1593:
1594: return ret;
1595: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>