diff --git a/bird-complete.bash b/bird-complete.bash new file mode 100644 index 00000000..16795da3 --- /dev/null +++ b/bird-complete.bash @@ -0,0 +1,54 @@ +#!/bin/bash + +function _bird_complete { + CMD=$1 + NOW=$2 + PREV=$3 + + case $PREV in + -c|-D|-P|-s) + COMPREPLY=( $(compgen -f -- $NOW) ) + ;; + -g) + COMPREPLY=( $(compgen -g -- $NOW) ) + ;; + -u) + COMPREPLY=( $(compgen -u -- $NOW) ) + ;; + *) + COMPREPLY=( $(compgen -W '-c -d -D -f -g -h -l -p -P -R -s -u --help --version' -- $NOW) ) + ;; + esac +} + +function _birdc_complete { + CMD=$1 + NOW=$2 + PREV=$3 + + echo "bagr" >>xxb + + case $PREV in + -*([lvr])s) + COMPREPLY=( $(compgen -W "$(find -maxdepth 1 -type s)" -- $NOW) ) + return + ;; + esac + + case $NOW in + -*([lvr])s) + COMPREPLY=( $(compgen -W "$(find -maxdepth 1 -type s | sed 's#^#'$NOW'#') ${NOW}l ${NOW}r ${NOW}v" -- $NOW) ) + return + ;; + -*) + COMPREPLY=( $(compgen -W "${NOW}l ${NOW}v ${NOW}r ${NOW}s") ) + return + ;; + esac + + COMPREPLY=( $($CMD -C "$NOW" "$COMP_TYPE" "$COMP_CWORD" "$COMP_POINT" "${COMP_WORDS[@]}") ) +} + +complete -F _bird_complete bird +complete -F _birdc_complete birdc +complete -F _birdc_complete birdcl diff --git a/client/Makefile b/client/Makefile index fccb8346..0584c206 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -src := commands.c util.c client.c +src := commands.c util.c client.c complete.c obj := $(src-o-files) $(all-client) diff --git a/client/client.c b/client/client.c index 0d4bdf3e..032c7fe2 100644 --- a/client/client.c +++ b/client/client.c @@ -49,6 +49,7 @@ static byte *server_read_pos = server_read_buf; int init = 1; /* During intial sequence */ int busy = 1; /* Executing BIRD command */ int interactive; /* Whether stdin is terminal */ +int complete = 0; static int num_lines, skip_input; int term_lns, term_cls; @@ -69,6 +70,13 @@ parse_args(int argc, char **argv) int server_changed = 0; int c; + if ((argc > 1) && !strcmp(argv[1], "-C")) { + complete_init(argc-2, argv+2); + argv += 6; + argc -= 6; + complete = 1; + } + while ((c = getopt(argc, argv, opt_list)) >= 0) switch (c) { @@ -87,6 +95,8 @@ parse_args(int argc, char **argv) server_path = xbasename(server_path); break; default: + if (complete) + exit(0); usage(argv[0]); } @@ -442,6 +452,10 @@ main(int argc, char **argv) interactive = isatty(0); parse_args(argc, argv); cmd_build_tree(); + + if (complete) + return do_complete(init_cmd); + server_connect(); select_loop(); return 0; diff --git a/client/client.h b/client/client.h index f9693def..7e2c438a 100644 --- a/client/client.h +++ b/client/client.h @@ -6,8 +6,7 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ - -extern int init, busy, interactive; +extern int init, busy, interactive, complete; extern int term_lns, term_cls; /* birdc.c / birdcl.c */ @@ -24,16 +23,21 @@ void more_end(void); void cleanup(void); -/* 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); -char *cmd_expand(char *cmd); - /* client.c */ void submit_command(char *cmd_raw); +/* commands.c */ + +void cmd_build_tree(void); +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); + +/* complete.c */ + +void complete_init(int argc, char **argv); +int do_complete(char *cmd); + /* die() with system error messages */ #define DIE(x, y...) die(x ": %s", ##y, strerror(errno)) diff --git a/client/commands.c b/client/commands.c index f2134c1b..84fcc89d 100644 --- a/client/commands.c +++ b/client/commands.c @@ -100,7 +100,7 @@ cmd_display_help(struct cmd_info *c1, struct cmd_info *c2) } static struct cmd_node * -cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) +cmd_find_abbrev(struct cmd_node *root, const char *cmd, int len, int *pambiguous) { struct cmd_node *m, *best = NULL, *best2 = NULL; @@ -127,21 +127,24 @@ cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) } static void -cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len) +cmd_list_ambiguous(struct cmd_node *root, const char *cmd, int 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 (m->len > len && !memcmp(m->token, cmd, len)) + if (complete) + printf("%s\n", m->token); + else + cmd_display_help(m->help, m->cmd); } void -cmd_help(char *cmd, int len) +cmd_help(const char *cmd, int len) { - char *end = cmd + len; + const char *end = cmd + len; struct cmd_node *n, *m; - char *z; + const char *z; int ambig; n = &cmd_root; @@ -171,7 +174,7 @@ cmd_help(char *cmd, int len) } static int -cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf) +cmd_find_common_match(struct cmd_node *root, const char *cmd, int len, int *pcount, char *buf) { struct cmd_node *m; int best, best_prio, i; @@ -212,13 +215,13 @@ cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, ch } 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; - char *z; + const char *z; int ambig, cnt = 0, common; /* Find the last word we want to complete */ @@ -242,9 +245,11 @@ cmd_complete(char *cmd, int len, char *buf, int again) { if (!again) return -1; - input_start_list(); + if (!complete) + input_start_list(); cmd_list_ambiguous(n, z, cmd-z); - input_stop_list(); + if (!complete) + input_stop_list(); return 0; } if (!m) @@ -273,9 +278,11 @@ cmd_complete(char *cmd, int len, char *buf, int again) } if (!again) return -1; - input_start_list(); + if (!complete) + input_start_list(); cmd_list_ambiguous(n, fin, end-fin); - input_stop_list(); + if (!complete) + input_stop_list(); return 0; } diff --git a/client/complete.c b/client/complete.c new file mode 100644 index 00000000..4e478b9e --- /dev/null +++ b/client/complete.c @@ -0,0 +1,122 @@ +/* + * BIRD Client Bash Expansion + * + * (c) 2017 Jan Moskyto Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + +#include +#include +#include + +#include "nest/bird.h" +#include "client/client.h" + +static const char *c_now; +static int comp_type, comp_cword, comp_point; + +void complete_init(int argc, char **argv) { + /* In argv, there are: + * $NOW + * $COMP_TYPE + * $COMP_CWORD + * $COMP_POINT + * ${COMP_WORDS[@]} + */ + + c_now = argv[0]; + + if (argc < 4) + 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 (sscanf(argv[3], "%d", &comp_point) != 1) + die("Strange COMP_POINT=\"%s\".", argv[3]); + + return; +} + +int do_complete(char *cmd) { + if ((*cmd == 0) && (comp_type == 63)) + printf("-s\n-l\n-v\n-r\n"); + + char buf[256]; + int res = cmd_complete(cmd, strlen(cmd), buf, (comp_type == 63)); + if (res == 1) + printf("%s%s\n", c_now, buf); + + + return 0; +} + +#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