]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge branch 'cli-completion' into bash-completion
authorJan Moskyto Matejka <mq@ucw.cz>
Wed, 31 May 2017 13:04:27 +0000 (15:04 +0200)
committerJan Moskyto Matejka <mq@ucw.cz>
Wed, 31 May 2017 13:04:27 +0000 (15:04 +0200)
1  2 
client/client.c
client/client.h
client/commands.c
client/complete.c
nest/cli.c

diff --cc client/client.c
index b4e2c60518d434906230a1a4740b966693572f2a,cab5601e6e8105515966cdf2d9ee7eb71b48ef41..741cae5b457667a16eaa0377dcf375c3ce9f1cd7
@@@ -47,9 -50,9 +50,11 @@@ static byte server_read_buf[SERVER_READ
  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;
@@@ -119,12 -114,7 +127,12 @@@ parse_args(int argc, char **argv
          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;
@@@ -199,7 -230,7 +248,7 @@@ init_commands(void
         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;
  }
@@@ -279,8 -317,57 +341,59 @@@ server_connect(void
      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] == ' ')
        {
@@@ -370,11 -511,11 +538,17 @@@ select_loop(void
        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);
@@@ -459,10 -600,6 +633,14 @@@ main(int argc, char **argv
    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;
diff --cc client/client.h
index 28b5416973b778fa69e007bc30180d563dd84389,b4a45cc5ce90b7f24f23d307c07c8aba26989eec..5d3ee8763ca478e70542baa4453ac6acec061523
@@@ -6,7 -6,10 +6,10 @@@
   *    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 */
@@@ -30,16 -29,47 +33,52 @@@ void submit_command(char *cmd_raw)
  /* 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
index 84fcc89d23027079eb3dcd6247281520d5b079f3,ae313d7f8ef259f1b2472f3ab3bc4718805763c5..5ea3f3ff06681a5751d935634703a64b5acc7fef
@@@ -100,7 -109,7 +109,7 @@@ cmd_display_help(struct cmd_info *c1, s
  }
  
  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
@@@ -173,14 -187,43 +193,43 @@@ cmd_help(const char *cmd, int len
      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;
index 559f3c65f0371c19282cd22a98b2355f93f00e91,0000000000000000000000000000000000000000..f30cdd989139cba536ec436bd3a91fcb0d0c3361
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,59 @@@
-   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;
 +}
 +
diff --cc nest/cli.c
index aceb5770cf5bc0afc1fd44453d05abe1d677e16f,af37c5975a2947c6156421dc0509c3e2022caced..70d1042fc4b5e0dfd4ea844db50005ba3fe88381
@@@ -196,13 -199,13 +199,6 @@@ cli_copy_message(cli *c
    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)
  {
@@@ -312,7 -314,7 +307,7 @@@ cli_new(void *priv
    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);