1 /* libcli -- a small commandline interpreter libraray
2 * Copyright (C) 2002 �yvind Kol�s
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include "cli.h"
24 #include "cli_tokenize.h"
25
26 /*#define HIDE_NULL_HELP
27 */
28 /* TODO:
29
30 allow removal of commands/variables
31 scripts? (with simple flow-control?)
32 */
33
34 #ifdef WIN32
35 #define snprintf(a,b,args...) sprintf(a,args)
36 #endif
37 /*
38 wordwrapping outputting function
39 */
default_output(char * data)40 static void default_output(char *data){
41 #define COLS 78
42 char *tbuf=malloc(COLS+1);
43 char *word=malloc(COLS+1);
44 char *bp=tbuf,
45 *wp=word,
46 *dp=data;
47
48 *bp=*wp='\0';
49
50 while(1+1==2){
51 if(isspace((unsigned char)*dp) || *dp=='\0'){
52 if( (bp-tbuf) + (wp-word) +1 < COLS){
53 strcpy(bp,word);
54 bp+=(wp-word);
55 *(bp++)=' ';
56 *bp='\0';
57 wp=word;
58 *wp='\0';
59 } else {
60 printf("%s\n",tbuf);
61 bp=tbuf;
62 *bp='\0';
63 strcpy(bp,word);
64 bp+=(wp-word);
65 *(bp++)=' ';
66 *bp='\0';
67 wp=word;
68 *wp='\0';
69 }
70 if(!*dp)break;
71 } else {
72 if(wp-word>=COLS){
73 printf("%s\n",tbuf);
74 printf("%s\n",word);
75 wp=word;
76 }
77 *(wp++)=*dp;
78 *wp='\0';
79 }
80 dp++;
81 }
82 printf("%s\n",tbuf);
83
84 free(word);
85 free(tbuf);
86 }
87
default_unknown_command(int argc,char ** argv,void * data)88 static void default_unknown_command (int argc,char **argv, void *data){
89 cli_outfunf ("unknown command '%s' type '?' to see allowed commands.\n",argv[0]);
90 }
91
92 void (*cli_outfun) (char *) = default_output;
93 void (*cli_precmd) (char *) = NULL;
94 void (*cli_postcmd) (char *) = NULL;
95 void (*cli_unknown) (int,char **,void *) = default_unknown_command;
96 int cli_width = 40;
97
98
cli_outfunf(char * format,...)99 void cli_outfunf(char *format, ...){
100 va_list arglist;
101 char buf[128];
102
103 va_start( arglist, format );
104 vsnprintf(buf,127,format,arglist);
105 va_end(arglist);
106
107 buf[127]=0;
108 cli_outfun(buf);
109 }
110
111
112 static int item_matches (const char *itemname);
113
114 typedef struct ItemT {
115 char *name; /* what the user types */
116 uint64_t (*func) (int argc,char **argv, void *data); /* function that is the command */
117 int *integer; /* pointer to integer (set to NULL if string) */
118 char *string; /* pointer to string (set to NULL if integer) */
119 char *usage; /* helptext for this command */
120 char *help;
121 int flags;
122 struct ItemT *next;
123 } ItemT;
124
125 #define is_command(a) (a->func && (a->integer==NULL) && (a->string==NULL))
126 #define is_variable(a) (!is_command(a))
127
128 static ItemT *items = NULL;
129
130 void
cli_add_item(char * name,int * integer,char * string,uint64_t (* func)(int argc,char ** argv,void * data),char * usage)131 cli_add_item (char *name,
132 int *integer, char *string,
133 uint64_t (*func) (int argc,char **argv, void *data), char *usage)
134 {
135 ItemT *titem = items;
136
137 while(titem){
138 if(!strcmp(titem->name,name)){
139 cli_outfunf ("libcli: attempted to add item '%s' more than once\n", name);
140 return;
141 }
142 titem=titem->next;
143 }
144 titem=items;
145
146 if (!titem) {
147 titem = items = malloc (sizeof (ItemT));
148 titem->next = NULL;
149 } else {
150 ItemT *tmp;
151
152 while (titem->next && ((strcmp ((titem->next)->name, name)) < 0)) {
153 titem = titem->next;
154 }
155
156 tmp = titem->next;
157 titem->next = malloc (sizeof (ItemT));
158 titem = titem->next;
159 titem->next = tmp;
160 }
161
162 titem->name = strdup (name);
163 titem->func = func;
164 titem->integer = integer;
165 titem->string = string;
166 if(usage)titem->usage = strdup (usage);
167 titem->help=strdup("");
168
169 if (strcmp (items->name, titem->name) > 0) {
170 ItemT *tmp = items;
171 ItemT *tmp_next = titem->next;
172
173 items = titem;
174 items->next = tmp;
175 items->next->next = tmp_next;
176 }
177 }
178
cli_add_help(char * name,char * helptext)179 void cli_add_help(char *name, char *helptext){
180 ItemT *titem = items;
181
182 while (titem) {
183 if (!strcmp (name, titem->name)){
184 free(titem->help);
185 titem->help=strdup(helptext);
186 return;
187 }
188 titem=titem->next;
189 }
190 cli_outfunf("libcli: attempted to add help for '%s' which is not registered",name);
191 }
192
193
194 static uint64_t help (int argc,char **argv, void *data);
195 static uint64_t vars (int argc,char **argv, void *data);
196
197 static int inited = 0;
198
cli_cleanup(void)199 void cli_cleanup(void){
200 inited=0;
201 cli_outfun = default_output;
202 cli_precmd = NULL;
203 cli_postcmd = NULL;
204 cli_unknown = default_unknown_command;
205 cli_width = 40;
206
207 while(items){
208 ItemT *titem=items;
209 if(items->name)free(items->name);
210 if(items->usage)free(items->usage);
211 if(items->help)free(items->help);
212 items=items->next;
213 free(titem);
214 titem=NULL;
215 }
216 }
217
init_cli(void)218 static void init_cli (void)
219 {
220 cli_add_command ("?", help, "? - this listing");
221 cli_add_command ("show_vars", vars, "show all variables");
222 inited = 1;
223 }
224
225 int cli_calllevel=0;
226
cli_docmd(char * commandline,void * data)227 uint64_t cli_docmd (char *commandline, void *data)
228 {
229 int largc=0;
230 char **largv;
231
232 ItemT *titem = items;
233 uint64_t ret=(uint64_t)data;
234 cli_calllevel++;
235
236 if (cli_precmd)
237 cli_precmd (commandline);
238
239 if (!inited) {
240 init_cli ();
241 titem = items;
242 inited = 1;
243 }
244
245 largv=argv_tokenize(commandline);
246 if(largv)largc=argc_of_argv(largv);
247
248 if((!largc) || largv[0][0]=='\0' ){
249 free(largv);
250 return ret;
251 }
252
253 while (titem) {
254 if (!strcmp (largv[0], titem->name)) {
255 if (is_command (titem)) {
256
257 ret=titem->func (largc,largv, data);
258
259 if (cli_postcmd)
260 cli_postcmd (commandline);
261 cli_calllevel--;
262
263 free(largv);
264 return ret;
265 } else if (is_variable (titem)) {
266 if (largc==1) {
267 if (titem->string) {
268 cli_outfunf ("%s\t[%s]\t- %s\n", titem->name,
269 titem->string, titem->usage);
270 } else if (titem->integer) {
271 cli_outfunf ("%s\t[%i]\t- %s\n", titem->name,
272 *titem->integer, titem->usage);
273 } else {
274 cli_outfunf ("%s\tis a broken variable\n", titem->name);
275 }
276 } else {
277 if (titem->integer)
278 *titem->integer = atoi (largv[1]);
279 if (titem->string)
280 strcpy (titem->string, largv[1]);
281 if (titem->func)
282 ret=titem->func (largc,largv, data);
283 }
284 if (cli_postcmd)
285 cli_postcmd (commandline);
286 cli_calllevel--;
287
288 free(largv);
289 return ret;
290 }
291 }
292 titem = titem->next;
293 }
294 if(cli_unknown)
295 cli_unknown(1,&commandline,data);
296 if (cli_postcmd)
297 cli_postcmd (commandline);
298 cli_calllevel--;
299
300 free(largv);
301 return ret;
302 }
303
304 static char newcommand[100];
305
item_matches(const char * itemname)306 static int item_matches (const char *itemname)
307 {
308 int matches = 0;
309 ItemT *titem = items;
310
311 while (titem) {
312 if (!strncmp (itemname, titem->name, strlen (itemname)))
313 matches++;
314 titem = titem->next;
315 }
316 return matches;
317 }
318
cli_complete(const char * commandline)319 char *cli_complete (const char *commandline)
320 {
321 int matches = 0;
322 char str_matches[4096]="";
323
324 strncpy (newcommand, commandline, 99);
325 newcommand[99] = 0;
326
327 if (commandline[0]) {
328 matches = item_matches (newcommand);
329
330 if (matches == 1) {
331 ItemT *titem = items;
332
333 while (titem) {
334 if (!strncmp (newcommand, titem->name, strlen (newcommand))) {
335 int pos;
336
337 strcpy (newcommand, titem->name);
338 pos = strlen (newcommand);
339 newcommand[pos++] = ' ';
340 newcommand[pos] = '\0';
341 break;
342 }
343 titem = titem->next;
344 }
345 } else if (matches > 1) {
346 ItemT *titem = items;
347 strcpy(str_matches,"matches: ");
348 while (titem) {
349 if (!strncmp (newcommand, titem->name, strlen (newcommand))) {
350 strcat (str_matches,titem->name);
351 strcat (str_matches," ");
352 }
353 titem = titem->next;
354 }
355 cli_outfun(str_matches);
356 while (item_matches (newcommand) == matches) {
357 ItemT *titem = items;
358
359 while (titem) {
360 int len = strlen (newcommand);
361
362 if (!strncmp (newcommand, titem->name, len)) {
363
364 strcpy (newcommand, titem->name);
365 newcommand[len + 1] = '\0';
366 if(!strcmp(newcommand,titem->name)){
367 return newcommand;
368 }
369 break;
370 }
371 titem = titem->next;
372 }
373 }
374 newcommand[strlen (newcommand) - 1] = '\0';
375 } else {
376 cli_outfunf ("no match");
377 }
378 }
379
380 return newcommand;
381 }
382
383 /* internal commands */
384
help(int argc,char ** argv,void * data)385 static uint64_t help (int argc,char **argv, void *data)
386 {
387 if (argc == 1) { /* show all help */
388 ItemT *titem = items;
389
390 cli_outfunf ("available commands:");
391
392 while (titem) {
393 #ifdef HIDE_NULL_HELP
394 if(titem->usage)
395 #endif
396 if (is_command (titem))
397 cli_outfunf ("%14s %s", titem->name, titem->usage);
398 titem = titem->next;
399 };
400 } else { /* show help for specified command */
401 ItemT *titem = items;
402
403 cli_outfunf ("HELP for '%s'", argv[1] );
404
405 while (titem) {
406 if (is_command (titem)) {
407 if (!strcmp (argv[1], titem->name)) {
408 cli_outfunf ("usage: %s %s", titem->name, titem->usage);
409 if(titem->help[0]){
410 cli_outfun ("");
411 cli_outfun(titem->help);
412 }
413 return(uint64_t)data;
414 }
415 }
416 titem = titem->next;
417 }
418 cli_outfunf ("unknown command '%s'", argv[1]);
419 }
420 return(uint64_t)data;
421 }
422
vars(int argc,char ** argv,void * data)423 static uint64_t vars (int argc, char **argv, void *data)
424 {
425 ItemT *titem = items;
426
427 cli_outfunf ("all variables:");
428
429 while (titem) {
430 #ifdef HIDE_NULL_HELP
431 if(titem->usage)
432 #endif
433 if (is_variable (titem)) {
434 if (titem->string) {
435 cli_outfunf ("%15s [%s]\t- %s", titem->name,
436 titem->string, titem->usage);
437 } else if (titem->integer) {
438 cli_outfunf ("%15s [%i]\t- %s", titem->name,
439 *titem->integer, titem->usage);
440 } else {
441 cli_outfunf ("%s\tis a broken variable", titem->name);
442 }
443 }
444 titem = titem->next;
445 }
446
447 cli_outfunf ("----------------");
448 cli_outfunf ("to change a variable: \"variablename newvalue\"");
449 return(uint64_t)data;
450 }
451
cli_getstring(char * variable)452 char *cli_getstring(char *variable){
453 ItemT *titem = items;
454 while (titem) {
455 if (is_variable (titem)) {
456 if (!strcmp (variable, titem->name)) {
457 if(titem->string)
458 return(titem->string);
459 if(titem->integer)
460 return NULL; /* FIXME: use a static buffer perhaps */
461 }
462 }
463 titem = titem->next;
464 }
465 return "";
466 }
467
468 #include <stdio.h>
469
cli_load_file(char * filename)470 uint64_t cli_load_file(char *filename){
471 char buf[255];
472 FILE *file;
473
474 file=fopen(filename,"r");
475 if(!file){
476 return -1;
477 }
478
479 while(fgets(buf,255,file)){
480 char *c=strchr(buf,'\n');
481 char *t;
482 t=buf;
483 if(c)*c='\0';
484 if(*buf){
485 while(*t==' ' || *t=='\t')t++;
486 cli_docmd(buf,NULL);
487 }
488 }
489 fclose(file);
490
491 return 0;
492 }
493