mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			324 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	BIRD Client -- Command Handling
 | |
|  *
 | |
|  *	(c) 1999--2000 Martin Mares <mj@ucw.cz>
 | |
|  *
 | |
|  *	Can be freely distributed and used under the terms of the GNU GPL.
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| 
 | |
| #include "nest/bird.h"
 | |
| #include "lib/resource.h"
 | |
| #include "lib/string.h"
 | |
| #include "client/client.h"
 | |
| 
 | |
| struct cmd_info {
 | |
|   char *command;
 | |
|   char *args;
 | |
|   char *help;
 | |
|   int is_real_cmd;
 | |
| };
 | |
| 
 | |
| static struct cmd_info command_table[] = {
 | |
| #include "conf/commands.h"
 | |
| };
 | |
| 
 | |
| struct cmd_node {
 | |
|   struct cmd_node *sibling, *son, **plastson;
 | |
|   struct cmd_info *cmd, *help;
 | |
|   int len;
 | |
|   signed char prio;
 | |
|   char token[1];
 | |
| };
 | |
| 
 | |
| static struct cmd_node cmd_root;
 | |
| 
 | |
| #define isspace_(X) isspace((unsigned char) (X))
 | |
| 
 | |
| void
 | |
| cmd_build_tree(void)
 | |
| {
 | |
|   uint i;
 | |
| 
 | |
|   cmd_root.plastson = &cmd_root.son;
 | |
| 
 | |
|   for(i=0; i<ARRAY_SIZE(command_table); i++)
 | |
|     {
 | |
|       struct cmd_info *cmd = &command_table[i];
 | |
|       struct cmd_node *old, *new;
 | |
|       char *c = cmd->command;
 | |
| 
 | |
|       old = &cmd_root;
 | |
|       while (*c)
 | |
| 	{
 | |
| 	  char *d = c;
 | |
| 	  while (*c && !isspace_(*c))
 | |
| 	    c++;
 | |
| 	  for(new=old->son; new; new=new->sibling)
 | |
| 	    if (new->len == c-d && !memcmp(new->token, d, c-d))
 | |
| 	      break;
 | |
| 	  if (!new)
 | |
| 	    {
 | |
| 	      int size = sizeof(struct cmd_node) + c-d;
 | |
| 	      new = malloc(size);
 | |
| 	      bzero(new, size);
 | |
| 	      *old->plastson = new;
 | |
| 	      old->plastson = &new->sibling;
 | |
| 	      new->plastson = &new->son;
 | |
| 	      new->len = c-d;
 | |
| 	      memcpy(new->token, d, c-d);
 | |
| 	      new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */
 | |
| 	    }
 | |
| 	  old = new;
 | |
| 	  while (isspace_(*c))
 | |
| 	    c++;
 | |
| 	}
 | |
|       if (cmd->is_real_cmd)
 | |
| 	old->cmd = cmd;
 | |
|       else
 | |
| 	old->help = cmd;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| cmd_do_display_help(struct cmd_info *c)
 | |
| {
 | |
|   char buf[strlen(c->command) + strlen(c->args) + 4];
 | |
| 
 | |
|   sprintf(buf, "%s %s", c->command, c->args);
 | |
|   printf("%-45s  %s\n", buf, c->help);
 | |
| }
 | |
| 
 | |
| static void
 | |
| cmd_display_help(struct cmd_info *c1, struct cmd_info *c2)
 | |
| {
 | |
|   if (c1)
 | |
|     cmd_do_display_help(c1);
 | |
|   else if (c2)
 | |
|     cmd_do_display_help(c2);
 | |
| }
 | |
| 
 | |
| static struct cmd_node *
 | |
| cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous)
 | |
| {
 | |
|   struct cmd_node *m, *best = NULL, *best2 = NULL;
 | |
| 
 | |
|   *pambiguous = 0;
 | |
|   for(m=root->son; m; m=m->sibling)
 | |
|     {
 | |
|       if (m->len == len && !memcmp(m->token, cmd, len))
 | |
| 	return m;
 | |
|       if (m->len > len && !memcmp(m->token, cmd, len))
 | |
| 	{
 | |
| 	  if (best && best->prio > m->prio)
 | |
| 	    continue;
 | |
| 	  if (best && best->prio == m->prio)
 | |
| 	    best2 = best;
 | |
| 	  best = m;
 | |
| 	}
 | |
|     }
 | |
|   if (best2)
 | |
|     {
 | |
|       *pambiguous = 1;
 | |
|       return NULL;
 | |
|     }
 | |
|   return best;
 | |
| }
 | |
| 
 | |
| static void
 | |
| cmd_list_ambiguous(struct cmd_node *root, 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);
 | |
| }
 | |
| 
 | |
| void
 | |
| cmd_help(char *cmd, int len)
 | |
| {
 | |
|   char *end = cmd + len;
 | |
|   struct cmd_node *n, *m;
 | |
|   char *z;
 | |
|   int ambig;
 | |
| 
 | |
|   n = &cmd_root;
 | |
|   while (cmd < end)
 | |
|     {
 | |
|       if (isspace_(*cmd))
 | |
| 	{
 | |
| 	  cmd++;
 | |
| 	  continue;
 | |
| 	}
 | |
|       z = cmd;
 | |
|       while (cmd < end && !isspace_(*cmd))
 | |
| 	cmd++;
 | |
|       m = cmd_find_abbrev(n, z, cmd-z, &ambig);
 | |
|       if (ambig)
 | |
| 	{
 | |
| 	  cmd_list_ambiguous(n, z, cmd-z);
 | |
| 	  return;
 | |
| 	}
 | |
|       if (!m)
 | |
| 	break;
 | |
|       n = m;
 | |
|     }
 | |
|   cmd_display_help(n->cmd, NULL);
 | |
|   for (m=n->son; m; m=m->sibling)
 | |
|     cmd_display_help(m->help, m->cmd);
 | |
| }
 | |
| 
 | |
| static int
 | |
| cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf)
 | |
| {
 | |
|   struct cmd_node *m;
 | |
|   int best, best_prio, i;
 | |
| 
 | |
|   *pcount = 0;
 | |
|   best = -1;
 | |
|   best_prio = -1;
 | |
|   for(m=root->son; m; m=m->sibling)
 | |
|     {
 | |
|       if (m->len < len || memcmp(m->token, cmd, len))
 | |
| 	continue;
 | |
| 
 | |
|       if (best_prio > m->prio)
 | |
| 	continue;
 | |
| 
 | |
|       if (best_prio < m->prio)
 | |
| 	{
 | |
| 	  *pcount = 0;
 | |
| 	  best = -1;
 | |
| 	}
 | |
| 
 | |
|       (*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;
 | |
| 	}
 | |
|     }
 | |
|   return best;
 | |
| }
 | |
| 
 | |
| int
 | |
| cmd_complete(char *cmd, int len, char *buf, int again)
 | |
| {
 | |
|   char *start = cmd;
 | |
|   char *end = cmd + len;
 | |
|   char *fin;
 | |
|   struct cmd_node *n, *m;
 | |
|   char *z;
 | |
|   int ambig, cnt = 0, common;
 | |
| 
 | |
|   /* Find the last word we want to complete */
 | |
|   for(fin=end; fin > start && !isspace_(fin[-1]); fin--)
 | |
|     ;
 | |
| 
 | |
|   /* Find the context */
 | |
|   n = &cmd_root;
 | |
|   while (cmd < fin && n->son)
 | |
|     {
 | |
|       if (isspace_(*cmd))
 | |
| 	{
 | |
| 	  cmd++;
 | |
| 	  continue;
 | |
| 	}
 | |
|       z = cmd;
 | |
|       while (cmd < fin && !isspace_(*cmd))
 | |
| 	cmd++;
 | |
|       m = cmd_find_abbrev(n, z, cmd-z, &ambig);
 | |
|       if (ambig)
 | |
| 	{
 | |
| 	  if (!again)
 | |
| 	    return -1;
 | |
| 	  input_start_list();
 | |
| 	  cmd_list_ambiguous(n, z, cmd-z);
 | |
| 	  input_stop_list();
 | |
| 	  return 0;
 | |
| 	}
 | |
|       if (!m)
 | |
| 	return -1;
 | |
|       n = m;
 | |
|     }
 | |
| 
 | |
|   /* Completion of parameters is not yet supported */
 | |
|   if (!n->son)
 | |
|     return -1;
 | |
| 
 | |
|   /* We know the context, let's try to complete */
 | |
|   common = cmd_find_common_match(n, fin, end-fin, &cnt, buf);
 | |
|   if (!cnt)
 | |
|     return -1;
 | |
|   if (cnt == 1)
 | |
|     {
 | |
|       buf[common++] = ' ';
 | |
|       buf[common] = 0;
 | |
|       return 1;
 | |
|     }
 | |
|   if (common > 0)
 | |
|     {
 | |
|       buf[common] = 0;
 | |
|       return 1;
 | |
|     }
 | |
|   if (!again)
 | |
|     return -1;
 | |
|   input_start_list();
 | |
|   cmd_list_ambiguous(n, fin, end-fin);
 | |
|   input_stop_list();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| char *
 | |
| cmd_expand(char *cmd)
 | |
| {
 | |
|   struct cmd_node *n, *m;
 | |
|   char *c, *b, *args;
 | |
|   int ambig;
 | |
| 
 | |
|   args = c = cmd;
 | |
|   n = &cmd_root;
 | |
|   while (*c)
 | |
|     {
 | |
|       if (isspace_(*c))
 | |
| 	{
 | |
| 	  c++;
 | |
| 	  continue;
 | |
| 	}
 | |
|       b = c;
 | |
|       while (*c && !isspace_(*c))
 | |
| 	c++;
 | |
|       m = cmd_find_abbrev(n, b, c-b, &ambig);
 | |
|       if (!m)
 | |
| 	{
 | |
| 	  if (!ambig)
 | |
| 	    break;
 | |
| 	  puts("Ambiguous command, possible expansions are:");
 | |
| 	  cmd_list_ambiguous(n, b, c-b);
 | |
| 	  return NULL;
 | |
| 	}
 | |
|       args = c;
 | |
|       n = m;
 | |
|     }
 | |
|   if (!n->cmd)
 | |
|     {
 | |
|       puts("No such command. Press `?' for help.");
 | |
|       return NULL;
 | |
|     }
 | |
|   b = malloc(strlen(n->cmd->command) + strlen(args) + 1);
 | |
|   sprintf(b, "%s%s", n->cmd->command, args);
 | |
|   return b;
 | |
| }
 |