Annotation of libaitcli/src/aitcli.c, revision 1.1.1.1.2.7
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.1.1.1.2.7! misho 6: * $Id: aitcli.c,v 1.1.1.1.2.6 2010/04/20 12:16:52 misho Exp $
1.1 misho 7: *
8: *************************************************************************/
9: #include "global.h"
10:
11:
12: #pragma GCC visibility push(hidden)
13:
14: cliCommands_t cli_stdCmds[] = {
15: { "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename },
16: { "-------", NULL, "---------------------", NULL, NULL },
17: { "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL },
18: { "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL },
19: { NULL, NULL, NULL, NULL }
20: };
21:
22: // ------------------------------------------------
23:
24: int cli_Errno;
25: char cli_Error[STRSIZ];
26:
27: #pragma GCC visibility pop
28:
29:
1.1.1.1.2.4 misho 30: static void cli_Null_Prep_Term(int meta)
31: {
32: }
33:
34: static void cli_Null_Deprep_Term()
35: {
36: }
37:
38: static int cli_Pre_Input_Change_Mode()
39: {
40: return 0;
41: }
42:
43: static int cli_GetC(FILE *dummy)
44: {
45: int ch = rl_getc(stdin);
46:
47: /*
48: if (is_special_char(ch)) {
49: pending_special_char = ch;
50: return '\r';
51: }
52: */
53:
54: return ch;
55: }
56:
57:
1.1 misho 58: // cli_GetErrno() Get error code of last operation
59: inline int cli_GetErrno()
60: {
61: return cli_Errno;
62: }
63:
64: // io_GetError() Get error text of last operation
65: inline const char *cli_GetError()
66: {
67: return cli_Error;
68: }
69:
70: // cli_SetErr() Set error to variables for internal use!!!
71: inline void cli_SetErr(int eno, char *estr, ...)
72: {
73: va_list lst;
74:
75: cli_Errno = eno;
76: memset(cli_Error, 0, STRSIZ);
77: va_start(lst, estr);
78: vsnprintf(cli_Error, STRSIZ, estr, lst);
79: va_end(lst);
80: }
81:
82: // ------------------------------------------------------------
83:
84: /*
85: * cli_Printf() Printf CLI features
86: * @out = Output stream
87: * @csFormat = Printf format string
1.1.1.1.2.4 misho 88: * return: -1 error, != -1 printed chars
1.1 misho 89: */
90: inline int cli_Printf(FILE *out, const char *csFormat, ...)
91: {
92: va_list lst;
93: int ret;
94:
95: va_start(lst, csFormat);
96:
97: ret = vfprintf(out, csFormat, lst);
98: if (-1 == ret)
99: LOGERR;
100:
101: va_end(lst);
102: return ret;
103: }
104:
105:
106: /*
107: * cliComp() Initialize completion CLI features
108: * @cmdComplete = Completion function
109: * @cmdEntry = Compentry function
110: * return: none
111: */
112: inline void cliComp(cli_Completion_t *cmdComplete, cli_CompEntry_t *cmdEntry)
113: {
114: // command completon
115: rl_attempted_completion_function = cmdComplete;
116: rl_completion_entry_function = cmdEntry;
117: }
118:
119: /*
1.1.1.1.2.1 misho 120: * cliTTY() Initialize I/O TTY CLI features
1.1.1.1.2.3 misho 121: * @term = terminal name
1.1.1.1.2.1 misho 122: * @inp = input handle
123: * @out = output handle
1.1.1.1.2.4 misho 124: * @win = window size
125: * return: -1 error, != -1 ok
126: */
127: inline int cliTTY(const char *term, FILE *inp, FILE *out, struct winsize *win)
128: {
129: if (term)
130: rl_terminal_name = term;
131:
132: if (inp)
133: rl_instream = inp;
134: if (out)
135: rl_outstream = out;
136:
137: if (win)
138: if (ioctl(!rl_outstream ? STDOUT_FILENO : fileno(rl_outstream), TIOCSWINSZ, win) == -1) {
139: LOGERR;
140: return -1;
141: }
142:
143: return 0;
144: }
145:
146: /*
1.1.1.1.2.5 misho 147: * cli_ReadHistory() Read CLI History from file
148: * @csFile = history file name, if NULL default history name is ".aitcli.history"
149: * return: -1 error; != -1 readed ok
150: */
151: inline int cli_ReadHistory(const char *csFile)
152: {
153: return read_history(!csFile ? ".aitcli.history" : csFile);
154: }
155:
156: /*
157: * cli_WriteHistory() Write CLI History to file
158: * @csFile = history file name, if NULL default history name is ".aitcli.history"
1.1.1.1.2.6 misho 159: * @lineNum = save number of history entry lines, if -1 all lines saved without limit
1.1.1.1.2.5 misho 160: * return: -1 error; != -1 readed ok
161: */
1.1.1.1.2.6 misho 162: inline int cli_WriteHistory(const char *csFile, int lineNum)
1.1.1.1.2.5 misho 163: {
1.1.1.1.2.6 misho 164: int ret;
165: const char *psFile = !csFile ? ".aitcli.history" : csFile;
166:
167: ret = write_history(psFile);
168: if (-1 != ret && -1 != lineNum)
169: history_truncate_file(psFile, lineNum);
170:
171: return ret;
1.1.1.1.2.5 misho 172: }
173:
174: /*
1.1.1.1.2.4 misho 175: * cliInit() Initialize Readline
176: * @csProg = program name
1.1.1.1.2.1 misho 177: * return: none
178: */
1.1.1.1.2.4 misho 179: inline void cliInit(const char *csProg)
1.1.1.1.2.1 misho 180: {
1.1.1.1.2.4 misho 181: rl_readline_name = csProg;
182:
183: rl_variable_bind("editing-mode", "emacs");
184: }
185:
186: /*
187: * cliNetInit() Initialize Readline if CLI bind to socket
188: * @csProg = program name
189: * @pty = Master pty
190: * @term = stdin termios
191: * return: none
192: */
193: void cliNetInit(const char *csProg, int pty, struct termios *term)
194: {
195: struct termios t;
196:
197: if (term) {
198: t = *term;
1.1.1.1.2.7! misho 199: // t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
1.1.1.1.2.4 misho 200: t.c_iflag &= ~ICRNL;
201: t.c_iflag |= IGNBRK;
202: t.c_cc[VMIN] = 1;
203: t.c_cc[VTIME] = 0;
204: tcsetattr(pty, TCSANOW, &t);
205: }
206:
207: cliInit(csProg);
208:
209: rl_instream = fdopen(pty, "r");
210:
211: rl_prep_term_function = cli_Null_Prep_Term;
212: rl_deprep_term_function = cli_Null_Deprep_Term;
213: rl_pre_input_hook = cli_Pre_Input_Change_Mode;
214:
215: rl_getc_function = cli_GetC;
216:
1.1.1.1.2.1 misho 217: }
218:
219: /*
1.1.1.1.2.7! misho 220: * cliNetExec() Execute net CLI main loop
! 221: * @cmdList = Commands list
! 222: * @csPrompt = Prompt text
! 223: * @sock = client socket
! 224: * @term = stdin termios
! 225: * @win = window size of tty
! 226: * return: -1 error, 0 = exit w/^+D, 1 done.
! 227: */
! 228: int cliNetExec(cliCommands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)
! 229: {
! 230: int pty, ret = 0, r, s, alen;
! 231: fd_set fds;
! 232: struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
! 233: u_char buf[BUFSIZ];
! 234: struct telnetAttrs *a, Attr[3];
! 235:
! 236: switch (forkpty(&pty, NULL, term, win)) {
! 237: case -1:
! 238: LOGERR;
! 239: return -1;
! 240: case 0:
! 241: close(sock);
! 242:
! 243: cliTTY(NULL, NULL, NULL, win);
! 244: ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
! 245: _exit(ret);
! 246: default:
! 247: cliNetInit(getprogname(), pty, term);
! 248:
! 249: telnet_SetCmd(Attr, DO, TELOPT_TTYPE);
! 250: telnet_SetCmd(Attr + 1, DO, TELOPT_LINEMODE);
! 251: telnet_SetCmd(Attr + 2, GA, 0);
! 252: if ((ret = telnetSend(sock, Attr, 3, NULL, 0, 0)) == -1) {
! 253: cli_Errno = telnet_GetErrno();
! 254: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
! 255: return -1;
! 256: }
! 257:
! 258: while (42) {
! 259: FD_ZERO(&fds);
! 260: FD_SET(sock, &fds);
! 261: FD_SET(pty, &fds);
! 262: if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) < 1)
! 263: break;
! 264:
! 265: r = FD_ISSET(sock, &fds) ? sock : pty;
! 266: s = FD_ISSET(sock, &fds) ? pty : sock;
! 267:
! 268: if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
! 269: if (-2 == ret)
! 270: continue;
! 271: // EOF
! 272: if (-3 == ret)
! 273: shutdown(r, SHUT_RD);
! 274: else {
! 275: cli_Errno = telnet_GetErrno();
! 276: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
! 277: }
! 278: break;
! 279: }
! 280: if ((ret = telnetSend(s, NULL, 0, buf, ret, 0)) == -1) {
! 281: cli_Errno = telnet_GetErrno();
! 282: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
! 283: break;
! 284: }
! 285:
! 286: /*
! 287: if ((ret = read(r, &ch, 1)) < 1) {
! 288: if (!ret)
! 289: shutdown(r, SHUT_RD);
! 290: break;
! 291: }
! 292: if (write(s, &ch, 1) == -1)
! 293: break;
! 294: */
! 295: }
! 296:
! 297: close(pty);
! 298: }
! 299:
! 300: return ret;
! 301: }
! 302:
! 303: /*
1.1 misho 304: * cliExec() Execute CLI main loop
305: * @cmdList = Commands list
306: * @csPrompt = Prompt text
307: * return: -1 error, 0 = exit w/^+D, 1 done.
308: */
1.1.1.1.2.2 misho 309: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
1.1 misho 310: {
311: char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
312: int ret = 0;
313: register int i;
314: cliCommands_t *cmd = NULL;
1.1.1.1.2.2 misho 315: FILE *out;
1.1 misho 316:
317: inline int inline_help()
318: {
319: cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
320: rl_on_new_line();
321: return 0;
322: }
323:
324: char **cli_stdCompletion(const char *text, int start, int end)
325: {
326: register int i;
327: char **matches = NULL;
328:
329: char *cmdCompGet(const char *text, int state)
330: {
331: int len = strlen(text);
332:
333: for (i = state; cmdList[i].cmd_name; i++) {
334: if (strncmp(cmdList[i].cmd_name, "---", 3) &&
335: !strncmp(cmdList[i].cmd_name, text, len))
336: return strdup(cmdList[i].cmd_name);
337: }
338:
339: return NULL;
340: }
341:
342: if (!start)
343: matches = rl_completion_matches(text, cmdCompGet);
344: else
345: for (i = 0; cmdList[i].cmd_name; i++) {
346: if (!cmdList[i].cmd_comp)
347: continue;
348: if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
349: matches = rl_completion_matches(text, cmdList[i].cmd_comp);
350: }
351:
352: return matches;
353: }
354: char *cli_stdCompEntry(const char *ignore, int invoking_key)
355: {
356: return NULL;
357: }
358:
359: /* --- main body of CLI --- */
360:
1.1.1.1.2.2 misho 361: out = rl_outstream;
362: if (!out)
363: out = stdout;
364:
1.1 misho 365: rl_bind_key('?', inline_help);
366: if (!rl_attempted_completion_function)
367: cliComp(cli_stdCompletion, cli_stdCompEntry);
368:
369: do {
370: line = readline(csPrompt);
371: if (!line) { // ^+d
372: cli_Printf(out, "\n");
373: break;
374: }
375: // clear whitespaces
376: for (s = line; isspace(*s); s++);
377: if (*s) {
378: for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
379: *++t = 0;
380: }
381:
382: if (*s) {
383: add_history(s);
384:
385: memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
386: for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));
387: *app ? app++ : app);
388:
389: /*
390: for (i = 0; i < MAX_PROMPT_ITEMS; i++)
391: cli_Printf(out, "i=%d %s\n", i, items[i]);
392: */
393:
394: // exec_cmd ...
395: for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
396: if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
397: cmd = &cmdList[i];
398: break;
399: }
400: if (!cmd) {
401: cli_Printf(out, "Command '%s' not found!\n", items[0]);
402: ret = -1;
403: } else
404: ret = cmd->cmd_func(cmdList, i, out, items);
405: }
406:
407: free(line);
408: } while (ret < 1);
409:
410: return ret;
411: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>