/* libcli -- a small commandline interpreter libraray * Copyright (C) 2002 Øyvind Kolås * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "cli.h" #include "cli_tokenize.h" /*#define HIDE_NULL_HELP */ /* TODO: allow removal of commands/variables scripts? (with simple flow-control?) */ #ifdef WIN32 #define snprintf(a,b,args...) sprintf(a,args) #endif /* wordwrapping outputting function */ static void default_output(char *data){ #define COLS 78 char *tbuf=malloc(COLS+1); char *word=malloc(COLS+1); char *bp=tbuf, *wp=word, *dp=data; *bp=*wp='\0'; while(1+1==2){ if(isspace((unsigned char)*dp) || *dp=='\0'){ if( (bp-tbuf) + (wp-word) +1 < COLS){ strcpy(bp,word); bp+=(wp-word); *(bp++)=' '; *bp='\0'; wp=word; *wp='\0'; } else { printf(tbuf);printf("\n"); bp=tbuf; *bp='\0'; strcpy(bp,word); bp+=(wp-word); *(bp++)=' '; *bp='\0'; wp=word; *wp='\0'; } if(!*dp)break; } else { if(wp-word>=COLS){ printf("%s\n",tbuf); printf("%s\n",word); wp=word; } *(wp++)=*dp; *wp='\0'; } dp++; } printf("%s\n",tbuf); free(word); free(tbuf); } static void default_unknown_command (int argc,char **argv, void *data){ cli_outfunf ("unknown command '%s' type '?' to see allowed commands.\n",argv[0]); } void (*cli_outfun) (char *) = default_output; void (*cli_precmd) (char *) = NULL; void (*cli_postcmd) (char *) = NULL; void (*cli_unknown) (int,char **,void *) = default_unknown_command; int cli_width = 40; void cli_outfunf(char *format, ...){ va_list arglist; char buf[128]; va_start( arglist, format ); vsnprintf(buf,127,format,arglist); va_end(arglist); buf[127]=0; cli_outfun(buf); } static int item_matches (const char *itemname); typedef struct ItemT { char *name; /* what the user types */ int (*func) (int argc,char **argv, void *data); /* function that is the command */ int *integer; /* pointer to integer (set to NULL if string) */ char *string; /* pointer to string (set to NULL if integer) */ char *usage; /* helptext for this command */ char *help; int flags; struct ItemT *next; } ItemT; #define is_command(a) (a->func && (a->integer==NULL) && (a->string==NULL)) #define is_variable(a) (!is_command(a)) static ItemT *items = NULL; void cli_add_item (char *name, int *integer, char *string, int (*func) (int argc,char **argv, void *data), char *usage) { ItemT *titem = items; while(titem){ if(!strcmp(titem->name,name)){ cli_outfunf ("libcli: attempted to add item '%s' more than once\n", name); return; } titem=titem->next; } titem=items; if (!titem) { titem = items = malloc (sizeof (ItemT)); titem->next = NULL; } else { ItemT *tmp; while (titem->next && ((strcmp ((titem->next)->name, name)) < 0)) { titem = titem->next; } tmp = titem->next; titem->next = malloc (sizeof (ItemT)); titem = titem->next; titem->next = tmp; } titem->name = strdup (name); titem->func = func; titem->integer = integer; titem->string = string; if(usage)titem->usage = strdup (usage); titem->help=strdup(""); if (strcmp (items->name, titem->name) > 0) { ItemT *tmp = items; ItemT *tmp_next = titem->next; items = titem; items->next = tmp; items->next->next = tmp_next; } } void cli_add_help(char *name, char *helptext){ ItemT *titem = items; while (titem) { if (!strcmp (name, titem->name)){ free(titem->help); titem->help=strdup(helptext); return; } titem=titem->next; } cli_outfunf("libcli: attempted to add help for '%s' which is not registered",name); } static int help (int argc,char **argv, void *data); static int vars (int argc,char **argv, void *data); static int inited = 0; void cli_cleanup(void){ inited=0; cli_outfun = default_output; cli_precmd = NULL; cli_postcmd = NULL; cli_unknown = default_unknown_command; cli_width = 40; while(items){ ItemT *titem=items; if(items->name)free(items->name); if(items->usage)free(items->usage); if(items->help)free(items->help); items=items->next; free(titem); titem=NULL; } } static void init_cli (void) { cli_add_command ("?", help, "? - this listing"); cli_add_command ("show_vars", vars, "show all variables"); inited = 1; } int cli_calllevel=0; int cli_docmd (char *commandline, void *data) { int largc=0; char **largv; ItemT *titem = items; int ret=(int)data; cli_calllevel++; if (cli_precmd) cli_precmd (commandline); if (!inited) { init_cli (); titem = items; inited = 1; } largv=argv_tokenize(commandline); if(largv)largc=argc_of_argv(largv); if((!largc) || largv[0][0]=='\0' ){ free(largv); return ret; } while (titem) { if (!strcmp (largv[0], titem->name)) { if (is_command (titem)) { ret=titem->func (largc,largv, data); if (cli_postcmd) cli_postcmd (commandline); cli_calllevel--; free(largv); return ret; } else if (is_variable (titem)) { if (largc==1) { if (titem->string) { cli_outfunf ("%s\t[%s]\t- %s\n", titem->name, titem->string, titem->usage); } else if (titem->integer) { cli_outfunf ("%s\t[%i]\t- %s\n", titem->name, *titem->integer, titem->usage); } else { cli_outfunf ("%s\tis a broken variable\n", titem->name); } } else { if (titem->integer) *titem->integer = atoi (largv[1]); if (titem->string) strcpy (titem->string, largv[1]); if (titem->func) ret=titem->func (largc,largv, data); } if (cli_postcmd) cli_postcmd (commandline); cli_calllevel--; free(largv); return ret; } } titem = titem->next; } if(cli_unknown) cli_unknown(1,&commandline,data); if (cli_postcmd) cli_postcmd (commandline); cli_calllevel--; free(largv); return ret; } static char newcommand[100]; static int item_matches (const char *itemname) { int matches = 0; ItemT *titem = items; while (titem) { if (!strncmp (itemname, titem->name, strlen (itemname))) matches++; titem = titem->next; } return matches; } char *cli_complete (const char *commandline) { int matches = 0; char str_matches[4096]=""; strncpy (newcommand, commandline, 99); newcommand[99] = 0; if (commandline[0]) { matches = item_matches (newcommand); if (matches == 1) { ItemT *titem = items; while (titem) { if (!strncmp (newcommand, titem->name, strlen (newcommand))) { int pos; strcpy (newcommand, titem->name); pos = strlen (newcommand); newcommand[pos++] = ' '; newcommand[pos] = '\0'; break; } titem = titem->next; } } else if (matches > 1) { ItemT *titem = items; strcpy(str_matches,"matches: "); while (titem) { if (!strncmp (newcommand, titem->name, strlen (newcommand))) { strcat (str_matches,titem->name); strcat (str_matches," "); } titem = titem->next; } cli_outfun(str_matches); while (item_matches (newcommand) == matches) { ItemT *titem = items; while (titem) { int len = strlen (newcommand); if (!strncmp (newcommand, titem->name, len)) { strcpy (newcommand, titem->name); newcommand[len + 1] = '\0'; if(!strcmp(newcommand,titem->name)){ return newcommand; } break; } titem = titem->next; } } newcommand[strlen (newcommand) - 1] = '\0'; } else { cli_outfunf ("no match"); } } return newcommand; } /* internal commands */ static int help (int argc,char **argv, void *data) { if (argc == 1) { /* show all help */ ItemT *titem = items; cli_outfunf ("available commands:"); while (titem) { #ifdef HIDE_NULL_HELP if(titem->usage) #endif if (is_command (titem)) cli_outfunf ("%14s %s", titem->name, titem->usage); titem = titem->next; }; } else { /* show help for specified command */ ItemT *titem = items; cli_outfunf ("HELP for '%s'", argv[1] ); while (titem) { if (is_command (titem)) { if (!strcmp (argv[1], titem->name)) { cli_outfunf ("usage: %s %s", titem->name, titem->usage); if(titem->help[0]){ cli_outfun (""); cli_outfun(titem->help); } return(int)data; } } titem = titem->next; } cli_outfunf ("unknown command '%s'", argv[1]); } return(int)data; } static int vars (int argc, char **argv, void *data) { ItemT *titem = items; cli_outfunf ("all variables:"); while (titem) { #ifdef HIDE_NULL_HELP if(titem->usage) #endif if (is_variable (titem)) { if (titem->string) { cli_outfunf ("%15s [%s]\t- %s", titem->name, titem->string, titem->usage); } else if (titem->integer) { cli_outfunf ("%15s [%i]\t- %s", titem->name, *titem->integer, titem->usage); } else { cli_outfunf ("%s\tis a broken variable", titem->name); } } titem = titem->next; } cli_outfunf ("----------------"); cli_outfunf ("to change a variable: \"variablename newvalue\""); return(int)data; } char *cli_getstring(char *variable){ ItemT *titem = items; while (titem) { if (is_variable (titem)) { if (!strcmp (variable, titem->name)) { if(titem->string) return(titem->string); if(titem->integer) return NULL; /* FIXME: use a static buffer perhaps */ } } titem = titem->next; } return ""; } #include int cli_load_file(char *filename){ char buf[255]; FILE *file; file=fopen(filename,"r"); if(!file){ return -1; } while(fgets(buf,255,file)){ char *c=strchr(buf,'\n'); char *t; t=buf; if(c)*c='\0'; if(*buf){ while(*t==' ' || *t=='\t')t++; cli_docmd(buf,NULL); } } fclose(file); return 0; }