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