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