Annotation of libaitcli/src/aitcli.c, revision 1.2.2.4
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.2.2.4 ! misho 6: * $Id: aitcli.c,v 1.2.2.3 2010/06/04 13:51:21 misho Exp $
1.1 misho 7: *
8: *************************************************************************/
9: #include "global.h"
10:
11:
12: #pragma GCC visibility push(hidden)
13:
1.2.2.2 misho 14: /*
15: commands_t cli_stdCmds[] = {
1.1 misho 16: { "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename },
17: { "-------", NULL, "---------------------", NULL, NULL },
18: { "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL },
19: { "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL },
20: { NULL, NULL, NULL, NULL }
21: };
1.2.2.2 misho 22: */
1.1 misho 23:
24: // ------------------------------------------------
25:
26: int cli_Errno;
27: char cli_Error[STRSIZ];
28:
29: #pragma GCC visibility pop
30:
31: // cli_GetErrno() Get error code of last operation
1.2.2.2 misho 32: inline int
33: cli_GetErrno()
1.1 misho 34: {
35: return cli_Errno;
36: }
37:
38: // io_GetError() Get error text of last operation
1.2.2.2 misho 39: inline const char *
40: cli_GetError()
1.1 misho 41: {
42: return cli_Error;
43: }
44:
45: // cli_SetErr() Set error to variables for internal use!!!
1.2.2.2 misho 46: inline void
47: cli_SetErr(int eno, char *estr, ...)
1.1 misho 48: {
49: va_list lst;
50:
51: cli_Errno = eno;
52: memset(cli_Error, 0, STRSIZ);
53: va_start(lst, estr);
54: vsnprintf(cli_Error, STRSIZ, estr, lst);
55: va_end(lst);
56: }
57:
58: // ------------------------------------------------------------
59:
1.2.2.2 misho 60: static inline void
61: clrscrEOL(linebuffer_t * __restrict buf)
1.1 misho 62: {
1.2.2.2 misho 63: register int i;
1.1 misho 64:
1.2.2.2 misho 65: if (buf) {
66: write(buf->line_out, K_CR, 1);
1.1 misho 67:
1.2.2.2 misho 68: for (i = 0; i < buf->line_len; i++)
69: write(buf->line_out, K_SPACE, 1);
70: }
1.1 misho 71: }
72:
1.2.2.2 misho 73: static inline void
74: printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
1.2 misho 75: {
1.2.2.2 misho 76: if (buf) {
77: write(buf->line_out, K_CR, 1);
1.2 misho 78:
1.2.2.2 misho 79: if (prompt && buf->line_prompt)
80: write(buf->line_out, buf->line_prompt, buf->line_bol);
1.2 misho 81:
1.2.2.2 misho 82: write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len);
83: }
1.2 misho 84: }
85:
1.2.2.2 misho 86: static inline void
87: printfCR(linebuffer_t * __restrict buf, int prompt)
1.2 misho 88: {
1.2.2.2 misho 89: if (buf) {
90: write(buf->line_out, K_CR, 1);
91:
92: if (prompt)
93: if (prompt && buf->line_prompt)
94: write(buf->line_out, buf->line_prompt, buf->line_bol);
95: }
1.2 misho 96: }
97:
1.2.2.2 misho 98: static inline void
1.2.2.4 ! misho 99: printfNL(linebuffer_t * __restrict buf, int prompt)
! 100: {
! 101: if (buf) {
! 102: write(buf->line_out, K_ENTER, 1);
! 103:
! 104: if (prompt)
! 105: if (prompt && buf->line_prompt)
! 106: write(buf->line_out, buf->line_prompt, buf->line_bol);
! 107: }
! 108: }
! 109:
! 110: static inline void
1.2.2.2 misho 111: printfCLI(linebuffer_t * __restrict buf, const unsigned char *text, int textlen, int prompt)
1.2 misho 112: {
1.2.2.2 misho 113: if (buf && text && textlen) {
114: if (prompt && buf->line_prompt)
115: write(buf->line_out, buf->line_prompt, buf->line_bol);
1.2 misho 116:
1.2.2.2 misho 117: write(buf->line_out, text, textlen);
118: }
1.2 misho 119: }
120:
1.2.2.2 misho 121: // ------------------------------------------------------------
122:
1.2.2.3 misho 123: static int
124: bufCHAR(int idx, void * __restrict buffer)
125: {
126: linebuffer_t *buf = buffer;
127: int pos;
128:
129: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
130: return RETCODE_ERR;
131:
132: pos = buf->line_eol - buf->line_bol;
133:
134: if (buf->line_mode == LINEMODE_INS)
135: memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos,
136: buf->line_len - buf->line_eol);
137: if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1)
138: buf->line_len += buf->line_keys[idx].key_len;
139: buf->line_eol += buf->line_keys[idx].key_len;
140:
141: memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
142: buf->line_buf[buf->line_len - 1] = 0;
143:
144: write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
145:
146: if (buf->line_mode == LINEMODE_INS) {
147: printfCLI(buf, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len,
148: buf->line_len - buf->line_eol, 0);
149: printfEOL(buf, -1, 1);
150: }
151: return RETCODE_OK;
152: }
153:
154: static int
155: bufEOL(int idx, void * __restrict buffer)
156: {
157: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
158: return RETCODE_ERR;
159:
160: printfCR(buffer, 1);
161: return RETCODE_EOL;
162: }
163:
164: static int
165: bufEOF(int idx, void * __restrict buffer)
166: {
167: linebuffer_t *buf = buffer;
168:
169: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
170: return RETCODE_ERR;
171:
172: write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
173: return RETCODE_EOF;
174: }
175:
176: static int
177: bufUP(int idx, void * __restrict buffer)
178: {
179: linebuffer_t *buf = buffer;
180: int pos;
181:
182: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
183: return RETCODE_ERR;
184:
185: if (!buf->line_h)
186: buf->line_h = TAILQ_FIRST(&buf->line_history);
187: else
188: buf->line_h = TAILQ_NEXT(buf->line_h, hist_next);
189: if (!buf->line_h)
190: return RETCODE_OK;
191:
192: clrscrEOL(buf);
193: cli_freeLine(buf);
194:
195: pos = buf->line_eol - buf->line_bol;
196:
197: buf->line_len += buf->line_h->hist_len;
198: buf->line_eol += buf->line_h->hist_len;
199:
200: memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
201: buf->line_buf[buf->line_len - 1] = 0;
202:
203: printfEOL(buf, -1, 1);
204: return RETCODE_OK;
205: }
206:
207: static int
208: bufDOWN(int idx, void * __restrict buffer)
209: {
210: linebuffer_t *buf = buffer;
211: int pos;
212:
213: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
214: return RETCODE_ERR;
215:
216: if (!buf->line_h)
217: buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead);
218: else
219: buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next);
220: if (!buf->line_h)
221: return RETCODE_OK;
222:
223: clrscrEOL(buf);
224: cli_freeLine(buf);
225:
226: pos = buf->line_eol - buf->line_bol;
227:
228: buf->line_len += buf->line_h->hist_len;
229: buf->line_eol += buf->line_h->hist_len;
230:
231: memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
232: buf->line_buf[buf->line_len - 1] = 0;
233:
234: printfEOL(buf, -1, 1);
235: return RETCODE_OK;
236: }
237:
238: static int
239: bufCLR(int idx, void * __restrict buffer)
240: {
241: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
242: return RETCODE_ERR;
243:
244: clrscrEOL(buffer);
245: cli_freeLine(buffer);
246:
247: printfCR(buffer, 1);
248: return RETCODE_OK;
249: }
250:
251: static int
252: bufBS(int idx, void * __restrict buffer)
253: {
254: linebuffer_t *buf = buffer;
255:
256: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
257: return RETCODE_ERR;
258:
259: if (buf->line_bol < buf->line_eol) {
260: clrscrEOL(buf);
261:
262: buf->line_eol--;
263: buf->line_len--;
264: memmove(buf->line_buf + buf->line_eol - buf->line_bol,
265: buf->line_buf + buf->line_eol - buf->line_bol + 1,
266: buf->line_len - buf->line_eol);
267: buf->line_buf[buf->line_len - 1] = 0;
268:
269: printfEOL(buf, buf->line_len - 1, 1);
270: printfEOL(buf, -1, 1);
271: }
272:
273: return RETCODE_OK;
274: }
275:
276: static int
277: bufBTAB(int idx, void * __restrict buffer)
278: {
279: linebuffer_t *buf = buffer;
280:
281: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
282: return RETCODE_ERR;
283:
284: if (buf->line_bol < buf->line_eol) {
285: clrscrEOL(buf);
286:
287: buf->line_len = buf->line_eol - buf->line_bol + 1;
288: buf->line_buf[buf->line_len - 1] = 0;
289:
290: printfEOL(buf, -1, 1);
291: }
292:
293: return RETCODE_OK;
294: }
295:
296: static int
297: bufMODE(int idx, void * __restrict buffer)
298: {
299: linebuffer_t *buf = buffer;
300:
301: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
302: return RETCODE_ERR;
303:
304: buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS;
305: return RETCODE_OK;
306: }
307:
308: static int
309: bufBEGIN(int idx, void * __restrict buffer)
310: {
311: linebuffer_t *buf = buffer;
312:
313: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
314: return RETCODE_ERR;
315:
316: buf->line_eol = buf->line_bol;
317:
318: printfCR(buf, 1);
319: return RETCODE_OK;
320: }
321:
322: static int
323: bufEND(int idx, void * __restrict buffer)
324: {
325: linebuffer_t *buf = buffer;
326:
327: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
328: return RETCODE_ERR;
329:
330: buf->line_eol = buf->line_len - 1;
331:
332: printfEOL(buf, -1, 1);
333: return RETCODE_OK;
334: }
335:
336: static int
337: bufLEFT(int idx, void * __restrict buffer)
338: {
339: linebuffer_t *buf = buffer;
340:
341: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
342: return RETCODE_ERR;
343:
344: if (buf->line_bol < buf->line_eol)
345: printfEOL(buf, --buf->line_eol - buf->line_bol, 1);
346:
347: return RETCODE_OK;
348: }
349:
350: static int
351: bufRIGHT(int idx, void * __restrict buffer)
352: {
353: linebuffer_t *buf = buffer;
354:
355: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
356: return RETCODE_ERR;
357:
358: if (buf->line_eol < buf->line_len - 1)
359: printfEOL(buf, ++buf->line_eol - buf->line_bol, 1);
360:
361: return RETCODE_OK;
362: }
363:
364: static int
365: bufDEL(int idx, void * __restrict buffer)
366: {
367: linebuffer_t *buf = buffer;
368:
369: if (!buffer || idx < 0 || idx > MAX_BINDKEY)
370: return RETCODE_ERR;
371:
372: clrscrEOL(buf);
373:
374: buf->line_len--;
375: memmove(buf->line_buf + buf->line_eol - buf->line_bol,
376: buf->line_buf + buf->line_eol - buf->line_bol + 1,
377: buf->line_len - buf->line_eol);
378: buf->line_buf[buf->line_len - 1] = 0;
379:
380: printfEOL(buf, buf->line_len - 1, 1);
381: printfEOL(buf, -1, 1);
382:
383: return RETCODE_OK;
384: }
385:
386: // ---------------------------------------------------------------
387:
388: /*
1.2.2.4 ! misho 389: * cli_Printf() Send message to CLI session
! 390: * @buffer = CLI buffer
! 391: * @fmt = printf format string
! 392: * @... = arguments defined in fmt
! 393: * return: none
! 394: */
! 395: inline void
! 396: cli_Printf(linebuffer_t * __restrict buffer, char *fmt, ...)
! 397: {
! 398: va_list lst;
! 399: FILE *f;
! 400:
! 401: if (fmt) {
! 402: f = fdopen(buffer->line_out, "a");
! 403: if (!f) {
! 404: LOGERR;
! 405: return;
! 406: }
! 407:
! 408: va_start(lst, fmt);
! 409: vfprintf(f, fmt, lst);
! 410: va_end(lst);
! 411: } else
! 412: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
! 413: }
! 414:
! 415: /*
1.2.2.3 misho 416: * cli_BindKey() Bind function to key
417: * @key = key structure
418: * @buffer = CLI buffer
419: * return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position
420: */
421: int
422: cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict buffer)
423: {
424: register int i;
425:
426: if (!key || !buffer) {
427: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
428: return RETCODE_ERR;
429: }
430:
431: for (i = 0; i < MAX_BINDKEY; i++)
432: if (key->key_len == buffer->line_keys[i].key_len &&
433: !memcmp(key->key_ch, buffer->line_keys[i].key_ch, key->key_len)) {
434: buffer->line_keys[i].key_func = key->key_func;
435: return i;
436: }
437:
438: return RETCODE_OK;
439: }
440:
441:
442: /*
443: * cli_addHistory() Add line to history
444: * @buffer = CLI buffer
1.2.2.4 ! misho 445: * @str = Add custom text or if NULL use readed line from CLI buffer
1.2.2.3 misho 446: * return: RETCODE_ERR error, RETCODE_OK ok
447: */
448: int
449: cli_addHistory(linebuffer_t * __restrict buffer, const char * __restrict str)
450: {
451: struct tagHistory *h;
452:
453: if (!buffer) {
454: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
455: return RETCODE_ERR;
456: }
457:
458: if (!(h = malloc(sizeof(struct tagHistory)))) {
459: LOGERR;
460: return RETCODE_ERR;
461: } else
462: memset(h, 0, sizeof(struct tagHistory));
463:
464: if (str) {
465: if (!*str) {
466: free(h);
467: return RETCODE_OK;
468: }
469:
470: h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
471: } else {
472: if (!*buffer->line_buf || buffer->line_len < 2) {
473: free(h);
474: return RETCODE_OK;
475: }
476:
477: memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len));
478: io_TrimStr((u_char*) h->hist_line);
479: h->hist_len = strlen(h->hist_line);
480: }
481:
482: TAILQ_INSERT_HEAD(&buffer->line_history, h, hist_next);
483: return h->hist_len;
484: }
485:
486: /*
487: * cli_saveHistory() Save history to file
488: * @buffer = CLI buffer
489: * @histfile = History filename, if NULL will be use default name
490: * @lines = Maximum history lines to save
491: * return: RETCODE_ERR error, RETCODE_OK ok
492: */
493: int
494: cli_saveHistory(linebuffer_t * __restrict buffer, const char *histfile, int lines)
495: {
496: FILE *f;
497: mode_t mode;
498: char szFName[MAXPATHLEN];
499: struct tagHistory *h;
500:
501: if (!buffer) {
502: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
503: return RETCODE_ERR;
504: }
505: if (!histfile)
506: strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
507: else
508: strlcpy(szFName, histfile, MAXPATHLEN);
509:
510: mode = umask(0177);
511: f = fopen(szFName, "w");
512: if (!f) {
513: LOGERR;
514: return RETCODE_ERR;
515: }
516:
517: TAILQ_FOREACH(h, &buffer->line_history, hist_next) {
518: fprintf(f, "%s\n", h->hist_line);
519:
520: if (lines)
521: lines--;
522: else
523: break;
524: }
525:
526: fclose(f);
527: umask(mode);
528:
529: return RETCODE_OK;
530: }
531:
532: /*
533: * cli_loadHistory() Load history from file
534: * @buffer = CLI buffer
535: * @histfile = History filename, if NULL will be use default name
536: * return: RETCODE_ERR error, RETCODE_OK ok
537: */
538: int
539: cli_loadHistory(linebuffer_t * __restrict buffer, const char *histfile)
540: {
541: FILE *f;
542: char szFName[MAXPATHLEN], buf[BUFSIZ];
543: struct tagHistory *h;
544:
545: if (!buffer) {
546: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
547: return RETCODE_ERR;
548: }
549: if (!histfile)
550: strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
551: else
552: strlcpy(szFName, histfile, MAXPATHLEN);
553:
554: f = fopen(szFName, "r");
1.2.2.4 ! misho 555: if (!f)
! 556: return RETCODE_OK;
1.2.2.3 misho 557:
558: while (fgets(buf, BUFSIZ, f)) {
559: if (!*buf || *buf == '#')
560: continue;
561: else
562: io_TrimStr((u_char*) buf);
563:
564: if (!(h = malloc(sizeof(struct tagHistory)))) {
565: LOGERR;
566: fclose(f);
567: return RETCODE_ERR;
568: } else
569: memset(h, 0, sizeof(struct tagHistory));
570:
571: h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ);
572: TAILQ_INSERT_TAIL(&buffer->line_history, h, hist_next);
573: }
574:
575: fclose(f);
576:
577: return RETCODE_OK;
578: }
579:
580: /*
581: * cli_resetHistory() Reset history search in CLI session
582: * @buffer = CLI buffer
583: * return: none
584: */
585: inline void
586: cli_resetHistory(linebuffer_t * __restrict buffer)
587: {
588: buffer->line_h = NULL;
589: }
590:
591:
592: /*
593: * cli_freeLine() Clear entire line
594: * @buffer = CLI buffer
595: * return: RETCODE_ERR error, RETCODE_OK ok
596: */
597: inline int
598: cli_freeLine(linebuffer_t * __restrict buffer)
599: {
600: int code = RETCODE_ERR;
601:
602: if (buffer) {
603: if (buffer->line_buf)
604: free(buffer->line_buf);
605:
606: buffer->line_buf = malloc(BUFSIZ);
607: if (buffer->line_buf) {
608: memset(buffer->line_buf, 0, BUFSIZ);
609: buffer->line_eol = buffer->line_bol;
610: buffer->line_len = 1 + buffer->line_eol;
611:
612: code = RETCODE_OK;
613: } else
614: LOGERR;
615: } else
616: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
617:
618: return code;
619: }
620:
621: /*
622: * cli_setPrompt() Set new prompt for CLI session
623: * @buffer = CLI buffer
624: * @prompt = new text for prompt or if NULL disable prompt
625: * return: none
626: */
627: inline void
628: cli_setPrompt(linebuffer_t * __restrict buffer, const char *prompt)
629: {
630: if (buffer) {
631: if (buffer->line_prompt) {
632: free(buffer->line_prompt);
633: buffer->line_prompt = NULL;
634: buffer->line_bol = 0;
635: }
636:
637: if (prompt) {
638: buffer->line_prompt = strdup(prompt);
639: if (buffer->line_prompt) {
640: buffer->line_bol = strlen(buffer->line_prompt);
641: buffer->line_eol = buffer->line_bol;
642: buffer->line_len = 1 + buffer->line_eol;
643: } else
644: LOGERR;
645: }
646: } else
647: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
648: }
649:
650:
651: /*
652: * cliEnd() Clear data, Free resources and close CLI session
653: * @buffer = CLI buffer
654: * return: RETCODE_ERR error, RETCODE_OK ok
655: */
656: void
657: cliEnd(linebuffer_t * __restrict buffer)
658: {
659: struct tagHistory *h;
1.2.2.4 ! misho 660: struct tagCommand *c;
1.2.2.3 misho 661:
662: if (buffer) {
1.2.2.4 ! misho 663: while ((c = SLIST_FIRST(&buffer->line_cmds))) {
! 664: SLIST_REMOVE_HEAD(&buffer->line_cmds, cmd_next);
! 665: free(c);
! 666: }
1.2.2.3 misho 667: while ((h = TAILQ_FIRST(&buffer->line_history))) {
668: TAILQ_REMOVE(&buffer->line_history, h, hist_next);
669: free(h);
670: }
671:
672: if (buffer->line_prompt)
673: free(buffer->line_prompt);
674:
675: if (buffer->line_keys)
676: free(buffer->line_keys);
677: if (buffer->line_buf)
678: free(buffer->line_buf);
679:
680: free(buffer);
681: buffer = NULL;
682: } else
683: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
684: }
685:
686: /*
687: * cliInit() Start CLI session, allocate memory for resources and bind keys
688: * @fin = Input device handle
689: * @fout = Output device handle
690: * @prompt = text for prompt, if NULL disable prompt
691: * return: NULL if error or !=NULL CLI buffer
692: */
693: linebuffer_t *
694: cliInit(int fin, int fout, const char *prompt)
695: {
696: linebuffer_t *buffer;
697: bindkey_t *keys;
698: register int i;
699: struct termios t;
700:
701: memset(&t, 0, sizeof t);
702: /* init buffer */
703: buffer = malloc(sizeof (linebuffer_t));
704: if (!buffer) {
705: LOGERR;
706: return NULL;
707: } else {
708: memset(buffer, 0, sizeof(linebuffer_t));
709:
710: buffer->line_in = fin;
711: buffer->line_out = fout;
712:
713: TAILQ_INIT(&buffer->line_history);
1.2.2.4 ! misho 714: SLIST_INIT(&buffer->line_cmds);
1.2.2.3 misho 715:
716: if (prompt) {
717: buffer->line_prompt = strdup(prompt);
718: if (!buffer->line_prompt) {
719: LOGERR;
720: free(buffer);
721: return NULL;
722: } else
723: buffer->line_eol = buffer->line_bol = strlen(buffer->line_prompt);
724: }
725: }
726: buffer->line_buf = malloc(BUFSIZ);
727: if (!buffer->line_buf) {
728: LOGERR;
729: if (buffer->line_prompt)
730: free(buffer->line_prompt);
731: free(buffer);
732: return NULL;
733: } else {
734: memset(buffer->line_buf, 0, BUFSIZ);
735: buffer->line_len = 1 + buffer->line_eol;
736: }
737: keys = calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
738: if (!keys) {
739: LOGERR;
740: if (buffer->line_prompt)
741: free(buffer->line_prompt);
742: free(buffer->line_buf);
743: free(buffer);
744: return NULL;
745: } else
746: memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
747:
748: /* fill key bindings */
749: // ascii chars & ctrl+chars
750: for (i = 0; i < 256; i++) {
751: *keys[i].key_ch = (u_char) i;
752: keys[i].key_len = 1;
753:
754: if (!i || i == *K_CTRL_D)
755: keys[i].key_func = bufEOF;
756: if (i == *K_CTRL_M || i == *K_CTRL_J)
757: keys[i].key_func = bufEOL;
758: if (i == *K_CTRL_H || i == *K_BACKSPACE)
759: keys[i].key_func = bufBS;
760: if (i == *K_CTRL_C)
761: keys[i].key_func = bufCLR;
762: if (i == *K_CTRL_A)
763: keys[i].key_func = bufBEGIN;
764: if (i == *K_CTRL_E)
765: keys[i].key_func = bufEND;
766: if (i >= *K_SPACE && i < *K_BACKSPACE)
767: keys[i].key_func = bufCHAR;
768: if (i > *K_BACKSPACE && i < 0xff)
769: keys[i].key_func = bufCHAR;
770: }
771: // alt+chars
772: for (i = 256; i < 512; i++) {
773: keys[i].key_ch[0] = 0x1b;
774: keys[i].key_ch[1] = (u_char) i - 256;
775: keys[i].key_len = 2;
776: }
777:
778: // 3 bytes
779: keys[i].key_len = sizeof K_F1 - 1;
780: memcpy(keys[i].key_ch, K_F1, keys[i].key_len);
781: i++;
782: keys[i].key_len = sizeof K_F2 - 1;
783: memcpy(keys[i].key_ch, K_F2, keys[i].key_len);
784: i++;
785: keys[i].key_len = sizeof K_F3 - 1;
786: memcpy(keys[i].key_ch, K_F3, keys[i].key_len);
787: i++;
788: keys[i].key_len = sizeof K_F4 - 1;
789: memcpy(keys[i].key_ch, K_F4, keys[i].key_len);
790: i++;
791: keys[i].key_len = sizeof K_CTRL_SH_F1 - 1;
792: memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i].key_len);
793: i++;
794: keys[i].key_len = sizeof K_CTRL_SH_F2 - 1;
795: memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i].key_len);
796: i++;
797: keys[i].key_len = sizeof K_CTRL_SH_F3 - 1;
798: memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i].key_len);
799: i++;
800: keys[i].key_len = sizeof K_CTRL_SH_F4 - 1;
801: memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i].key_len);
802: i++;
803: keys[i].key_len = sizeof K_CTRL_SH_F5 - 1;
804: memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i].key_len);
805: i++;
806: keys[i].key_len = sizeof K_CTRL_SH_F6 - 1;
807: memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i].key_len);
808: i++;
809: keys[i].key_len = sizeof K_CTRL_SH_F7 - 1;
810: memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i].key_len);
811: i++;
812: keys[i].key_len = sizeof K_CTRL_SH_F8 - 1;
813: memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i].key_len);
814: i++;
815: keys[i].key_len = sizeof K_CTRL_SH_F9 - 1;
816: memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i].key_len);
817: i++;
818: keys[i].key_len = sizeof K_CTRL_SH_F10 - 1;
819: memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i].key_len);
820: i++;
821: keys[i].key_len = sizeof K_CTRL_SH_F11 - 1;
822: memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i].key_len);
823: i++;
824: keys[i].key_len = sizeof K_CTRL_SH_F12 - 1;
825: memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i].key_len);
826: i++;
827: keys[i].key_len = sizeof K_CTRL_F1 - 1;
828: memcpy(keys[i].key_ch, K_CTRL_F1, keys[i].key_len);
829: i++;
830: keys[i].key_len = sizeof K_CTRL_F2 - 1;
831: memcpy(keys[i].key_ch, K_CTRL_F2, keys[i].key_len);
832: i++;
833: keys[i].key_len = sizeof K_CTRL_F3 - 1;
834: memcpy(keys[i].key_ch, K_CTRL_F3, keys[i].key_len);
835: i++;
836: keys[i].key_len = sizeof K_CTRL_F4 - 1;
837: memcpy(keys[i].key_ch, K_CTRL_F4, keys[i].key_len);
838: i++;
839: keys[i].key_len = sizeof K_CTRL_F5 - 1;
840: memcpy(keys[i].key_ch, K_CTRL_F5, keys[i].key_len);
841: i++;
842: keys[i].key_len = sizeof K_CTRL_F6 - 1;
843: memcpy(keys[i].key_ch, K_CTRL_F6, keys[i].key_len);
844: i++;
845: keys[i].key_len = sizeof K_CTRL_F7 - 1;
846: memcpy(keys[i].key_ch, K_CTRL_F7, keys[i].key_len);
847: i++;
848: keys[i].key_len = sizeof K_CTRL_F8 - 1;
849: memcpy(keys[i].key_ch, K_CTRL_F8, keys[i].key_len);
850: i++;
851: keys[i].key_len = sizeof K_CTRL_F9 - 1;
852: memcpy(keys[i].key_ch, K_CTRL_F9, keys[i].key_len);
853: i++;
854: keys[i].key_len = sizeof K_CTRL_F10 - 1;
855: memcpy(keys[i].key_ch, K_CTRL_F10, keys[i].key_len);
856: i++;
857: keys[i].key_len = sizeof K_CTRL_F11 - 1;
858: memcpy(keys[i].key_ch, K_CTRL_F11, keys[i].key_len);
859: i++;
860: keys[i].key_len = sizeof K_CTRL_F12 - 1;
861: memcpy(keys[i].key_ch, K_CTRL_F12, keys[i].key_len);
862: i++;
863: keys[i].key_len = sizeof K_HOME - 1;
864: keys[i].key_func = bufBEGIN;
865: memcpy(keys[i].key_ch, K_HOME, keys[i].key_len);
866: i++;
867: keys[i].key_len = sizeof K_END - 1;
868: keys[i].key_func = bufEND;
869: memcpy(keys[i].key_ch, K_END, keys[i].key_len);
870: i++;
871: keys[i].key_len = sizeof K_UP - 1;
872: keys[i].key_func = bufUP;
873: memcpy(keys[i].key_ch, K_UP, keys[i].key_len);
874: i++;
875: keys[i].key_len = sizeof K_DOWN - 1;
876: keys[i].key_func = bufDOWN;
877: memcpy(keys[i].key_ch, K_DOWN, keys[i].key_len);
878: i++;
879: keys[i].key_len = sizeof K_RIGHT - 1;
880: keys[i].key_func = bufRIGHT;
881: memcpy(keys[i].key_ch, K_RIGHT, keys[i].key_len);
882: i++;
883: keys[i].key_len = sizeof K_LEFT - 1;
884: keys[i].key_func = bufLEFT;
885: memcpy(keys[i].key_ch, K_LEFT, keys[i].key_len);
886: i++;
887: keys[i].key_len = sizeof K_BTAB - 1;
888: keys[i].key_func = bufBTAB;
889: memcpy(keys[i].key_ch, K_BTAB, keys[i].key_len);
890: i++;
891: // 4 bytes
892: keys[i].key_len = sizeof K_INS - 1;
893: keys[i].key_func = bufMODE;
894: memcpy(keys[i].key_ch, K_INS, keys[i].key_len);
895: i++;
896: keys[i].key_len = sizeof K_DEL - 1;
897: keys[i].key_func = bufDEL;
898: memcpy(keys[i].key_ch, K_DEL, keys[i].key_len);
899: i++;
900: keys[i].key_len = sizeof K_PGUP - 1;
901: memcpy(keys[i].key_ch, K_PGUP, keys[i].key_len);
902: i++;
903: keys[i].key_len = sizeof K_PGDN - 1;
904: memcpy(keys[i].key_ch, K_PGDN, keys[i].key_len);
905: i++;
906: // 5 bytes
907: keys[i].key_len = sizeof K_F5 - 1;
908: memcpy(keys[i].key_ch, K_F5, keys[i].key_len);
909: i++;
910: keys[i].key_len = sizeof K_F6 - 1;
911: memcpy(keys[i].key_ch, K_F6, keys[i].key_len);
912: i++;
913: keys[i].key_len = sizeof K_F7 - 1;
914: memcpy(keys[i].key_ch, K_F7, keys[i].key_len);
915: i++;
916: keys[i].key_len = sizeof K_F8 - 1;
917: memcpy(keys[i].key_ch, K_F8, keys[i].key_len);
918: i++;
919: keys[i].key_len = sizeof K_F9 - 1;
920: memcpy(keys[i].key_ch, K_F9, keys[i].key_len);
921: i++;
922: keys[i].key_len = sizeof K_F10 - 1;
923: memcpy(keys[i].key_ch, K_F10, keys[i].key_len);
924: i++;
925: keys[i].key_len = sizeof K_F11 - 1;
926: memcpy(keys[i].key_ch, K_F11, keys[i].key_len);
927: i++;
928: keys[i].key_len = sizeof K_F12 - 1;
929: memcpy(keys[i].key_ch, K_F12, keys[i].key_len);
930: i++;
931:
932: tcgetattr(buffer->line_in, &t);
933: t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
934: t.c_iflag |= IGNBRK;
935: t.c_cc[VMIN] = 1;
936: t.c_cc[VTIME] = 0;
937: tcsetattr(buffer->line_in, TCSANOW, &t);
938:
939: buffer->line_keys = keys;
940: return buffer;
941: }
942:
1.2 misho 943: /*
1.2.2.4 ! misho 944: * cliReadLine() Read line from opened CLI session
! 945: * @buffer = CLI buffer
! 946: * return: NULL if error or !=NULL readed line, must be free after use!
1.2 misho 947: */
1.2.2.4 ! misho 948: char *
! 949: cliReadLine(linebuffer_t * __restrict buffer)
1.2 misho 950: {
1.2.2.4 ! misho 951: int code, readLen;
! 952: register int i;
! 953: struct pollfd fds;
! 954: char buf[BUFSIZ], *str = NULL;
1.2 misho 955:
1.2.2.4 ! misho 956: if (!buffer) {
! 957: cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
! 958: return NULL;
1.2 misho 959: }
960:
1.2.2.4 ! misho 961: memset(&fds, 0, sizeof fds);
! 962: fds.fd = buffer->line_in;
! 963: fds.events = POLLIN;
! 964:
! 965: printfCR(buffer, 1);
! 966: while (42) {
! 967: if (poll(&fds, 1, -1) < 1) {
! 968: LOGERR;
! 969: return str;
! 970: }
1.2 misho 971:
1.2.2.4 ! misho 972: memset(buf, 0, sizeof buf);
! 973: readLen = read(buffer->line_in, buf, BUFSIZ);
! 974: if (readLen == -1) {
! 975: LOGERR;
! 976: return str;
! 977: }
! 978: if (!readLen) {
! 979: if (buffer->line_buf)
! 980: str = strdup(buffer->line_buf);
! 981: else
! 982: cli_SetErr(EPIPE, "Error:: unknown state ...");
! 983: return str;
! 984: }
1.2 misho 985:
1.2.2.4 ! misho 986: recheck:
! 987: for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
! 988: if (readLen >= buffer->line_keys[i].key_len &&
! 989: !memcmp(buffer->line_keys[i].key_ch, buf,
! 990: buffer->line_keys[i].key_len)) {
! 991: readLen -= buffer->line_keys[i].key_len;
! 992: if (readLen)
! 993: memmove(buf, buf + buffer->line_keys[i].key_len, readLen);
! 994: else
! 995: memset(buf, 0, buffer->line_keys[i].key_len);
! 996:
! 997: if (buffer->line_keys[i].key_func)
! 998: if ((code = buffer->line_keys[i].key_func(i, buffer)))
! 999: readLen = 0;
! 1000:
! 1001: if (readLen)
! 1002: goto recheck;
! 1003: else
! 1004: break;
! 1005: }
1.2 misho 1006:
1.2.2.4 ! misho 1007: if (code)
! 1008: break;
! 1009: }
! 1010:
! 1011: if (code != RETCODE_ERR && buffer->line_buf)
! 1012: str = strdup(buffer->line_buf);
! 1013: return str;
1.2 misho 1014: }
1.2.2.4 ! misho 1015:
! 1016:
1.2 misho 1017: /*
1.2.2.4 ! misho 1018: * cliNetLoop() CLI network main loop binded to socket
! 1019: * @buffer = CLI buffer
! 1020: * @csHistFile = History file name
1.2 misho 1021: * @sock = client socket
1022: * @term = stdin termios
1023: * @win = window size of tty
1.2.2.4 ! misho 1024: * return: RETCODE_ERR error, RETCODE_OK ok
1.2 misho 1025: */
1.2.2.1 misho 1026: int
1.2.2.4 ! misho 1027: cliNetLoop(linebuffer_t * __restrict buffer, const char *csHistFile, int sock,
! 1028: struct termios *term, struct winsize *win)
1.2 misho 1029: {
1.2.2.4 ! misho 1030: u_char buf[BUFSIZ];
! 1031: int pty, r, s, alen, attrlen, flg, ret = 0;
1.2 misho 1032: fd_set fds;
1033: struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
1034: struct telnetAttrs *a, Attr[10];
1035:
1036: switch (forkpty(&pty, NULL, term, win)) {
1037: case -1:
1038: LOGERR;
1039: return -1;
1040: case 0:
1041: close(sock);
1042:
1.2.2.4 ! misho 1043: // buffer = cliInit(STDIN_FILENO, STDOUT_FILENO, csPrompt);
! 1044: // if (!buffer)
! 1045: // return RETCODE_ERR;
! 1046:
! 1047: ret = cliLoop(buffer, csHistFile) < 0 ? 1 : 0;
! 1048:
! 1049: // cliEnd(buffer);
! 1050:
1.2 misho 1051: /* spawn Shell mode */
1052: /*
1053: execl("/bin/tcsh", "tcsh", NULL);
1054: */
1.2.2.4 ! misho 1055:
1.2 misho 1056: _exit(ret);
1057: default:
1058: /* spawn Shell mode */
1059: telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
1060: telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
1061: telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
1062: telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
1063: telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
1064: if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) {
1065: cli_Errno = telnet_GetErrno();
1066: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
1067: return -1;
1068: } else
1069: flg = 0;
1070:
1071: while (42) {
1072: FD_ZERO(&fds);
1073: FD_SET(sock, &fds);
1074: FD_SET(pty, &fds);
1075: if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
1076: if (!ret)
1077: cli_SetErr(ETIMEDOUT, "Client session timeout ...");
1078:
1079: break;
1080: }
1081:
1082: r = FD_ISSET(sock, &fds) ? sock : pty;
1083: s = FD_ISSET(sock, &fds) ? pty : sock;
1084:
1085: if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
1086: if (a)
1087: free(a);
1088:
1089: if (-2 == ret)
1090: continue;
1091: // EOF
1092: if (-3 == ret)
1093: shutdown(r, SHUT_RD);
1094: else {
1095: cli_Errno = telnet_GetErrno();
1096: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
1097: }
1098: break;
1099: }
1100: attrlen = 0;
1101: if (1 == flg && alen) {
1102: telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
1103: telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
1104: }
1105: if (2 == flg && alen) {
1106: telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
1107: telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
1108: LFLOW_OFF, NULL, 0);
1109: telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
1110: LFLOW_RESTART_XON, NULL, 0);
1111: telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
1112: }
1113: if (a)
1114: free(a);
1115:
1116: if ((ret = telnetSend(s, Attr, pty == s ? 0 : attrlen, buf, ret, 0)) == -1) {
1117: cli_Errno = telnet_GetErrno();
1118: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
1119: break;
1120: } else
1121: flg++;
1122: }
1123:
1124: close(pty);
1125: }
1126:
1127: return ret;
1128: }
1129:
1130: /*
1.2.2.4 ! misho 1131: * cliLoop() CLI main loop
! 1132: * @buffer = CLI buffer
! 1133: * @csHistFile = History file name
! 1134: * return: RETCODE_ERR error, RETCODE_OK ok
1.1 misho 1135: */
1.2.2.4 ! misho 1136: int
! 1137: cliLoop(linebuffer_t * __restrict buffer, const char *csHistFile)
1.1 misho 1138: {
1139: char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
1140: register int i;
1.2.2.4 ! misho 1141: int ret = RETCODE_OK;
! 1142: struct tagCommand *cmd;
1.1 misho 1143:
1.2.2.4 ! misho 1144: /*
1.1 misho 1145: inline int inline_help()
1146: {
1147: cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
1148: rl_on_new_line();
1149: return 0;
1150: }
1151:
1152: char **cli_stdCompletion(const char *text, int start, int end)
1153: {
1154: register int i;
1155: char **matches = NULL;
1156:
1157: char *cmdCompGet(const char *text, int state)
1158: {
1159: int len = strlen(text);
1160:
1161: for (i = state; cmdList[i].cmd_name; i++) {
1162: if (strncmp(cmdList[i].cmd_name, "---", 3) &&
1163: !strncmp(cmdList[i].cmd_name, text, len))
1164: return strdup(cmdList[i].cmd_name);
1165: }
1166:
1167: return NULL;
1168: }
1169:
1170: if (!start)
1171: matches = rl_completion_matches(text, cmdCompGet);
1172: else
1173: for (i = 0; cmdList[i].cmd_name; i++) {
1174: if (!cmdList[i].cmd_comp)
1175: continue;
1176: if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
1177: matches = rl_completion_matches(text, cmdList[i].cmd_comp);
1178: }
1179:
1180: return matches;
1181: }
1.2.2.4 ! misho 1182:
! 1183:
! 1184: */
! 1185:
1.1 misho 1186: /* --- main body of CLI --- */
1.2.2.4 ! misho 1187:
! 1188: if (cli_loadHistory(buffer, csHistFile) == RETCODE_ERR)
! 1189: return RETCODE_ERR;
1.1 misho 1190:
1191: do {
1.2.2.4 ! misho 1192: line = cliReadLine(buffer);
! 1193: if (!line)
1.1 misho 1194: break;
1.2.2.4 ! misho 1195: else
! 1196: cli_addHistory(buffer, NULL);
1.1 misho 1197: // clear whitespaces
1198: for (s = line; isspace(*s); s++);
1199: if (*s) {
1200: for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
1201: *++t = 0;
1202: }
1203:
1204: if (*s) {
1205: memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
1206: for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));
1207: *app ? app++ : app);
1208:
1209: // exec_cmd ...
1.2.2.4 ! misho 1210: i = 0;
! 1211: SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) {
! 1212: if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0])))
1.1 misho 1213: break;
1.2.2.4 ! misho 1214: else
! 1215: i++;
! 1216: }
1.1 misho 1217: if (!cmd) {
1.2.2.4 ! misho 1218: cli_Printf(buffer, "Command '%s' not found!\n", items[0]);
1.1 misho 1219: ret = -1;
1220: } else
1.2.2.4 ! misho 1221: ret = cmd->cmd_func(buffer, i, items);
1.1 misho 1222: }
1223:
1.2.2.4 ! misho 1224: cli_freeLine(buffer);
! 1225: cli_resetHistory(buffer);
1.1 misho 1226: free(line);
1227: } while (ret < 1);
1228:
1.2.2.4 ! misho 1229: cli_saveHistory(buffer, csHistFile, HISTORY_LINES);
1.1 misho 1230: return ret;
1231: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>