static byte *server_read_pos = server_read_buf;
int init = 1; /* During intial sequence */
- int busy = 1; /* Executing BIRD command */
+ int busy = 0; /* Executing BIRD command */
int interactive; /* Whether stdin is terminal */
+int complete = 0;
+ int welcomed = 0; /* Was welcome message with BIRD version printed out? */
++int symread = 0; /* During symbol receipt */
static int num_lines, skip_input;
int term_lns, term_cls;
tmp += strlen(tmp);
*tmp++ = ' ';
}
- tmp[-1] = 0; /* Put ending null-terminator */
+
+ if (complete) {
+ strcpy(tmp, comp_now);
+ tmp += strlen(comp_now);
+ } else
- tmp[-1] = 0;
++ tmp[-1] = 0; /* Drop last space character */
once = 1;
interactive = 0;
return;
}
-- if (init_cmd)
++ if (init_cmd && !complete)
{
/* First transition - client received hello from BIRD
and there is waiting initial command */
return;
}
-- if (once)
++ if (once && !complete)
{
/* Initial command is finished and we want to exit */
cleanup();
exit(0);
}
- input_init();
+ init_list(&symbols);
+ longest_symbol_len = 1; /* Be careful, it's used as denominator! */
+
+ /* In symbol list is a BIRD version for welcome message too */
- if (interactive)
- retrieve_symbols();
++ if (interactive || complete)
++ {
++ retrieve_symbols();
++ symread = 1;
++ }
+
- input_init();
++ if (!complete)
++ {
++ input_init();
-- term_lns = (term_lns > 0) ? term_lns : 25;
-- term_cls = (term_cls > 0) ? term_cls : 80;
++ term_lns = (term_lns > 0) ? term_lns : 25;
++ term_cls = (term_cls > 0) ? term_cls : 80;
++ }
init = 0;
}
DIE("fcntl");
}
+ list *
+ cli_get_symbol_list(void)
+ {
+ return &symbols;
+ }
- #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
+ uint
+ cli_get_symbol_maxlen(void)
+ {
+ return longest_symbol_len;
+ }
+
+ static void
+ process_internal_message(int reply_code, const char *name)
+ {
+ u32 flag = 0;
+
+ switch (reply_code)
+ {
+ case RC_BIRD_VERSION_NUM:
- if (interactive && !welcomed)
++ if (interactive && !welcomed && !complete)
+ {
+ welcomed = 1;
+ printf("BIRD %s ready.\n", name);
+ }
+ return;
+
+ case RC_NOTIFY:
+ if (interactive)
+ retrieve_symbols();
+ return;
+
+ /* Symbols */
+ case RC_CONSTANT_NAME: flag = CLI_SF_CONSTANT; break;
+ case RC_VARIABLE_NAME: flag = CLI_SF_VARIABLE; break;
+ case RC_FILTER_NAME: flag = CLI_SF_FILTER; break;
+ case RC_FUNCTION_NAME: flag = CLI_SF_FUNCTION; break;
+ case RC_PROTOCOL_NAME: flag = CLI_SF_PROTOCOL; break;
+ case RC_TABLE_NAME: flag = CLI_SF_TABLE; break;
+ case RC_TEMPLATE_NAME: flag = CLI_SF_TEMPLATE; break;
+ case RC_INTERFACE_NAME: flag = CLI_SF_INTERFACE; break;
+ default:
+ printf("Undefined %d: %s", reply_code, name);
+ return;
+ }
+
- if (flag && name && *name)
++ if (flag && name && *name) {
++ symread = 1;
+ add_to_symbols(flag, name);
++ }
+ }
+
-#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
++#define PRINTF(LEN, PARGS...) do { if (!skip_input && !complete) len = printf(PARGS); } while(0)
static void
server_got_reply(char *x)
sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
(x[4] == ' ' || x[4] == '-'))
{
- if (code)
- PRINTF(len, "%s\n", verbose ? x : x+5);
+ if (code >= 3000 && code < 4000)
+ {
+ process_internal_message(code, x+5);
+ }
+ else if (code)
+ {
+ PRINTF(len, "%s\n", verbose ? x : x+5);
- }
++ } else if (symread)
++ symread = 0;
if (x[4] == ' ')
{
if (!init)
input_notify(!busy);
++ if (!init && !symread && complete)
++ {
++ do_complete(init_cmd);
++ exit(0);
++ }
++
fd_set select_fds;
FD_ZERO(&select_fds);
FD_SET(server_fd, &select_fds);
-- if (!busy)
++ if (!busy && !complete)
FD_SET(0, &select_fds);
rv = select(server_fd+1, &select_fds, NULL, NULL, NULL);
interactive = isatty(0);
parse_args(argc, argv);
cmd_build_tree();
- if (complete)
+
++ /* Shell completion of first word */
++ if (complete && !strchr(init_cmd, ' '))
++ {
++ init_list(&symbols);
+ return do_complete(init_cmd);
++ }
+
server_connect();
select_loop();
return 0;
* Can be freely distributed and used under the terms of the GNU GPL.
*/
-extern int init, busy, interactive;
+ #ifndef _BIRD_CLIENT_H_
+ #define _BIRD_CLIENT_H_
+
+extern int init, busy, interactive, complete;
extern int term_lns, term_cls;
/* birdc.c / birdcl.c */
/* commands.c */
void cmd_build_tree(void);
-void cmd_help(char *cmd, int len);
-int cmd_complete(char *cmd, int len, char *buf, int again);
+void cmd_help(const char *cmd, int len);
+int cmd_complete(const char *cmd, int len, char *buf, int again);
char *cmd_expand(char *cmd);
-/* client.c */
+/* complete.c */
+
+void complete_init(int argc, char **argv);
+int do_complete(char *cmd);
+#define COMPLETE_ARGC 3
+extern const char *comp_now, *comp_last;
+ /* Client Symbol Flags: Types */
+ #define CLI_SF_CONSTANT (1 << 0)
+ #define CLI_SF_VARIABLE (1 << 1)
+ #define CLI_SF_FILTER (1 << 2)
+ #define CLI_SF_FUNCTION (1 << 3)
+ #define CLI_SF_PROTOCOL (1 << 4)
+ #define CLI_SF_TABLE (1 << 5)
+ #define CLI_SF_TEMPLATE (1 << 6)
+ #define CLI_SF_INTERFACE (1 << 7)
+
+ #define CLI_SF_OPTIONAL (1 << 8) /* This node is optional not mandatory */
+ #define CLI_SF_PARAMETER (1 << 9) /* A parameter/word will follow after this node */
+
+ /* Client Symbol Flags: Keywords */
+ #define CLI_SF_KW_ALL (1 << 10)
+ #define CLI_SF_KW_OFF (1 << 11)
+
+
+ struct cli_symbol
+ {
+ node n;
+ const char *name;
+ uint len;
+ u32 flags; /* CLI_SF_* */
+ };
+
+ void submit_command(char *cmd_raw);
+
/* die() with system error messages */
#define DIE(x, y...) die(x ": %s", ##y, strerror(errno))
+
+ void retrieve_symbols(void);
+ void add_keywords_to_symbols(void);
+ list *cli_get_symbol_list(void);
+ uint cli_get_symbol_maxlen(void);
+ void simple_input_read(void);
+
+ #endif
}
static struct cmd_node *
- cmd_find_abbrev(struct cmd_node *root, const char *cmd, int len, int *pambiguous)
-cmd_find_abbrev(struct cmd_node *root, char *cmd, uint len, int *pambiguous)
++cmd_find_abbrev(struct cmd_node *root, const char *cmd, uint len, int *pambiguous)
{
struct cmd_node *m, *best = NULL, *best2 = NULL;
}
static void
- cmd_list_ambiguous(struct cmd_node *root, const char *cmd, int len)
-cmd_list_ambiguous(struct cmd_node *root, char *cmd, uint len)
++cmd_list_ambiguous(struct cmd_node *root, const char *cmd, uint len)
{
struct cmd_node *m;
for(m=root->son; m; m=m->sibling)
if (m->len > len && !memcmp(m->token, cmd, len))
- cmd_display_help(m->help, m->cmd);
+ if (complete)
+ printf("%s\n", m->token);
+ else
+ cmd_display_help(m->help, m->cmd);
+
- struct cli_symbol *sym;
+ list *syms = cli_get_symbol_list();
++ if (!syms)
++ return;
++
++ struct cli_symbol *sym;
+ WALK_LIST(sym, *syms)
+ {
+ if ((sym->flags & root->flags) && sym->len > len && memcmp(sym->name, cmd, len) == 0)
+ printf("%s\n", sym->name);
+ }
}
void
cmd_display_help(m->help, m->cmd);
}
+ /*
+ * Return length of common prefix of all matches,
+ * Write common prefix string into buf
+ */
+ static int
+ cmd_merge_match_with_others(int max_common_len, const char *token_name, int token_len, char *buf, int from)
+ {
+ if (max_common_len < 0)
+ {
+ /* For a case that we'll have exactly one match */
+ strcpy(buf, token_name + from);
+ max_common_len = token_len - from;
+ }
+ else
+ {
+ int i = 0;
+ while (i < max_common_len && i < token_len - from && buf[i] == token_name[from+i])
+ i++;
+ max_common_len = i;
+ }
+ return max_common_len;
+ }
+
+ /*
+ * Return length of common prefix of all matches,
+ * Write count of all matches into pcount,
+ * Write common prefix string into buf
+ */
static int
- cmd_find_common_match(struct cmd_node *root, const char *cmd, int len, int *pcount, char *buf)
-cmd_find_common_match(struct cmd_node *root, char *cmd, uint len, int *pcount, char *buf)
++cmd_find_common_match(struct cmd_node *root, const char *cmd, uint len, int *pcount, char *buf)
{
struct cmd_node *m;
- int best, best_prio, i;
+ int max_common_len;
+ int best_prio;
*pcount = 0;
- best = -1;
+ max_common_len = -1;
best_prio = -1;
for(m=root->son; m; m=m->sibling)
{
if (best_prio < m->prio)
{
*pcount = 0;
- best = -1;
+ max_common_len = -1;
}
+ if (max_common_len < 0)
+ best_prio = m->prio;
+
(*pcount)++;
- if (best < 0)
- {
- strcpy(buf, m->token + len);
- best = m->len - len;
- best_prio = m->prio;
- }
- else
- {
- i = 0;
- while (i < best && i < m->len - len && buf[i] == m->token[len+i])
- i++;
- best = i;
- }
+ max_common_len = cmd_merge_match_with_others(max_common_len, m->token, m->len, buf, len);
}
- return best;
+
+ list *syms = cli_get_symbol_list();
++ if (!syms)
++ return max_common_len;
++
+ struct cli_symbol *sym;
+ WALK_LIST(sym, *syms)
+ {
+ if (!(sym->flags & root->flags))
+ continue;
+
+ if (sym->len < len || memcmp(sym->name, cmd, len))
+ continue;
+
+ (*pcount)++;
+ max_common_len = cmd_merge_match_with_others(max_common_len, sym->name, sym->len, buf, len);
+ }
+
+ return max_common_len;
}
int
-cmd_complete(char *cmd, int len, char *buf, int again)
+cmd_complete(const char *cmd, int len, char *buf, int again)
{
- char *start = cmd;
- char *end = cmd + len;
- char *fin;
+ const char *start = cmd;
+ const char *end = cmd + len;
+ const char *fin;
- struct cmd_node *n, *m;
+ struct cmd_node *n, *m = NULL;
- char *z;
+ const char *z;
int ambig, cnt = 0, common;
/* Find the last word we want to complete */
buf[common] = 0;
return 1;
}
-- if (common > 0)
++ if (!complete && (common > 0))
{
buf[common] = 0;
return 1;
--- /dev/null
- int res = cmd_complete(cmd, strlen(cmd), buf, (comp_type == 63));
+/*
+ * BIRD Client Bash Expansion
+ *
+ * (c) 2017 Jan Moskyto Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nest/bird.h"
+#include "client/client.h"
+
+static int comp_type, comp_cword;
+const char *comp_now, *comp_last;
+
+void complete_init(int argc, char **argv) {
+ /* In argv, there are:
+ * $NOW
+ * $COMP_TYPE
+ * $COMP_CWORD
+ * $COMP_POINT
+ * ${COMP_WORDS[@]}
+ */
+
+ comp_now = argv[0];
+
+ if (argc < COMPLETE_ARGC)
+ die("Not enough args.");
+
+ if (sscanf(argv[1], "%d", &comp_type) != 1)
+ die("Strange COMP_TYPE=\"%s\".", argv[1]);
+
+ if (sscanf(argv[2], "%d", &comp_cword) != 1)
+ die("Strange COMP_CWORD=\"%s\".", argv[2]);
+
+ if (comp_cword + COMPLETE_ARGC >= argc)
+ die("COMP_CWORD=%d points after end of arg list.", comp_cword);
+
+ comp_last = argv[COMPLETE_ARGC + comp_cword];
+ return;
+}
+
+int do_complete(char *cmd) {
+ if ((*cmd == 0) && (comp_type == 63))
+ printf("-s\n-l\n-v\n-r\n");
+
+ char buf[256];
- #if 0
-
- /* Environment and input check */
- if (!comp_line || !index(comp_line, ' '))
- die("Environment variable COMP_LINE not found.");
-
- /* Drop the command name */
- comp_line = index(comp_line, ' ') + 1;
-
- /* StrTok copy */
- char *clt = strdup(comp_line);
- char *tok = strtok(clt, " ");
- do {
- if (!tok)
- break;
- if (!tok[0])
- goto next;
-
- if (want_socket) {
- opt_s = tok;
- goto next;
- }
-
- if (tok[0] == '-')
- switch(tok[1]) {
- case 's':
- if (tok[2])
- opt_s = tok+2;
- else
- want_socket = 1;
- goto next;
- case 'v':
- opt_v++;
- goto next;
- case 'r':
- opt_r++;
- goto next;
- case 'l':
- opt_l++;
- goto next;
- default:
- return 0;
- }
-
- next:
- tok = strtok(NULL, " ");
- } while (tok);
-
-
-
- fprintf(stderr, "KEY \"%s\"\nLINE \"%s\"\nPOINT \"%s\"\nTYPE \"%s\"\n",
- comp_key, comp_line, comp_point, comp_type);
-
- char buf[256];
- int result = cmd_complete(comp_line, atoi(comp_point), buf, 0);
-
- if (result < 0)
- return 0;
-
- puts(buf);
-
- return 0;
- }
- #endif
++ int res = cmd_complete(cmd, strlen(cmd), buf, 1);
+ if (res == 1)
+ printf("%s%s\n", comp_now, buf);
+
+
+ return 0;
+}
+
q[-1] = '\n';
}
--static void
--cli_hello(cli *c)
--{
-- cli_printf(c, 1, "BIRD " BIRD_VERSION " ready.");
-- c->cont = NULL;
--}
--
static void
cli_free_out(cli *c)
{
c->event = ev_new(p);
c->event->hook = cli_event;
c->event->data = c;
-- c->cont = cli_hello;
++ c->cont = NULL;
c->parser_pool = lp_new_default(c->pool);
c->show_pool = lp_new_default(c->pool);
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);