Merge of Fredrik Hultin command_line
[people/xl0/gpxe.git] / src / commandline / cmdlinelib.c
1 #include "cmdlinelib.h"
2 #include "command.h"
3 #include <gpxe/tables.h>
4 #include <console.h>
5 #include <malloc.h>
6 #include <string.h>
7 #include <stdarg.h>
8
9 static struct command cmd_start[0] __table_start ( commands );
10 static struct command cmd_end[0] __table_end ( commands );
11
12 void cmdl_setgetchar(cmd_line* cmd, cmdl_getchar_t in)
13 {
14         cmd->getchar = in;
15 }
16
17 void cmdl_setputchar(cmd_line* cmd, cmdl_putchar_t in)
18 {
19         cmd->putchar = in;
20 }
21
22 void cmdl_setprintf(cmd_line* cmd, cmdl_printf_t in)
23 {
24         cmd->printf = in;
25 }
26       
27 int cmdl_getexit(cmd_line* cmd)
28 {
29         if(cmdl_check(cmd) && !cmd->exit){
30                 return 0;
31         }else{
32                 return 1;
33         }
34
35 }
36
37 void cmdl_setexit(cmd_line* cmd, int exit)
38 {
39         if(cmdl_check(cmd)){
40                 cmd->exit = exit;
41         }
42 }
43
44 int cmdl_printf(cmd_line* cmd, const char *format, ...)
45 {
46         int ret;
47         char string[CMDL_BUFFER_SIZE];
48         va_list ap;
49
50         va_start(ap, format);
51         ret = vsprintf(string, format, ap);
52         cmdl_addstr(cmd, string);
53         va_end(ap);
54         return ret;
55 }
56
57 void cmdl_addstr(cmd_line* cmd, char* str)
58 {
59         unsigned int i;
60         for(i = 0; i < strlen(str); i++){
61                 cmdl_addchar(cmd, str[i]);
62         }
63 }
64
65 /*void cmdl_addoutput_str(cmd_line* cmd, char output[CMDL_OUTPUT_SIZE])
66 {
67         if(cmdl_check(cmd) && output != NULL){
68                 if(!cmd->has_output){
69                         cmdl_clearoutput(cmd);
70                 }
71                 strncat(cmd->output, output, CMDL_OUTPUT_SIZE);
72                 cmd->has_output = 1;
73         }
74 }*/
75
76 /*char* cmdl_getoutput(cmd_line* cmd)
77 {
78         if(cmdl_check(cmd) && cmd->has_output){
79                 cmd->has_output = 0;
80                 return cmd->output;
81         }else{
82                 return "";
83         }
84 }*/
85
86 void cmdl_setpropmt(cmd_line* cmd, char prompt[CMDL_PROMPT_SIZE])
87 {
88         if(cmdl_check(cmd) && prompt != NULL){
89                 strncat(cmd->prompt, prompt, CMDL_PROMPT_SIZE);
90         }
91 }
92
93 char *cmdl_getprompt(cmd_line* cmd)
94 {
95         if(cmdl_check(cmd)){
96                 return cmd->prompt;
97         }else{
98                 return "";
99         }
100 }
101
102 char* cmdl_getbuffer(cmd_line* cmd){
103         if(cmdl_check(cmd)){
104                 return cmd->buffer;
105         }else{
106                 return "";
107         }
108 }
109
110 void cmdl_enterloop(cmd_line* cmd)
111 {
112         while(!cmdl_getexit(cmd)){
113                 if(cmd->refresh){
114                         cmd->printf("%s %s", cmd->prompt, cmd->buffer);
115                         cmd->refresh = 0;
116                 }
117 //              cmd->printf("Got %d\n", cmd->getchar());
118                 cmdl_parsechar(cmd, cmd->getchar());
119         }
120 }
121
122 void cmdl_addreplace(cmd_line* cmd, char in)
123 {
124         if(cmd->cursor < CMDL_BUFFER_SIZE - 2){
125                 cmd->buffer[cmd->cursor] = in;
126                 cmd->cursor++;
127                 cmd->putchar((int)in);
128         }
129 }
130
131 void cmdl_addinsert(cmd_line* cmd, char in)
132 {
133         int i;
134         int to;
135         if(cmd->cursor < CMDL_BUFFER_SIZE - 2 && cmd->cursor >= 0){
136                 if(strlen(cmd->buffer) < CMDL_BUFFER_SIZE - 2){
137                         to = strlen(cmd->buffer);
138                 }else{
139                         to = CMDL_BUFFER_SIZE - 2;
140                 }
141                         for(i=to; i > cmd->cursor; i--){
142                                 cmd->buffer[i] = cmd->buffer[i-1];
143                         }
144                         cmd->buffer[cmd->cursor] = in;
145
146                         for(i=cmd->cursor; i < to; i++){
147                                 cmd->putchar(cmd->buffer[i]);
148                         }
149                         
150                         for(i=cmd->cursor; i < to - 1; i++){
151                                 cmd->putchar(CMDLK_BS);
152                         }
153                         cmd->cursor++;
154                         //cmdl_movecursor(cmd, CMDL_RIGHT);
155         }
156 }
157
158 void cmdl_addchar(cmd_line* cmd, char in){
159         if(cmd->insert){
160                 cmdl_addinsert(cmd, in);
161         }else{
162                 cmdl_addreplace(cmd, in);
163         }
164 }
165
166 void cmdl_parsechar(cmd_line* cmd, char in)
167 {
168         if(cmdl_check(cmd)){
169                 if(in >= 32){
170                         cmdl_addchar(cmd, in);
171                 }else{
172                         switch(in){
173                                 case CMDLK_BS:
174                                         if(cmdl_movecursor(cmd, CMDL_LEFT)){
175                                                 cmdl_del(cmd);
176                                         }
177                                         break;
178
179                                 case CMDLK_RETURN:
180                                         cmd->putchar('\n');
181                                         cmdl_exec(cmd);
182                                         cmd->refresh = 1;
183                                         break;
184
185                                 case CMDLK_BW:
186                                         cmdl_movecursor(cmd, CMDL_LEFT);
187                                         break;
188
189                                 case CMDLK_FW:
190                                         //cmdl_movecursor(cmd, CMDL_RIGHT);
191                                         break;
192                                 
193                                 case CMDLK_TAB:
194                                         cmdl_tabcomplete(cmd);
195                                         break;
196
197                         }
198                 }
199         }
200 }
201
202 void cmdl_tabcomplete(cmd_line *cmd)
203 {
204         struct command *ccmd;
205         int count=0;
206         char* result[CMDL_MAX_TAB_COMPLETE_RESULT];
207
208         for ( ccmd = cmd_start ; ccmd < cmd_end ; ccmd++ ) {
209                 if(!strncmp(ccmd->name, cmd->buffer, strlen(cmd->buffer))){
210                         if(count <= CMDL_MAX_TAB_COMPLETE_RESULT){
211                                 result[count++] = (char*)(ccmd->name);
212                         }
213                 }
214         }
215         
216
217         if( count == 1 ){
218                 cmdl_addstr(cmd, (char*)(result[0] + strlen(cmd->buffer)));
219                 cmd->tabstate = 0;
220                 cmdl_addchar(cmd, ' ');
221         } else if( count > 1 ) {
222                 int i, i2, minlen=CMDL_BUFFER_SIZE, same=1;
223                 char last;
224
225                 for(i = 0; i < count; i ++) {
226                         if(minlen > (int)strlen( result[i] ) ){
227                                 minlen = strlen(result[i]);
228                         }
229                 
230                 }
231                 if((int)strlen(cmd->buffer) < minlen){
232                         for(i = strlen(cmd->buffer); i < minlen; i++){
233                                 last = result[0][i];
234                                 for(i2 = 1; i2 < count; i2 ++) {
235                                         if(result[i2][i] != last){
236                                                 same = 0;
237                                                 break;
238                                         }
239                                 }
240                                 if(same){
241                                         cmdl_addchar(cmd, last);
242                                 }
243                                 
244                         }
245                 }
246                 cmd->tabstate++;
247         }
248         
249         if(count > 1 && cmd->tabstate > 1){
250                 int i;
251                 cmd->tabstate = 0;
252                 cmd->refresh = 1;
253                 cmd->putchar('\n');
254                 for(i = 0; i < count; i ++){
255                         cmd->printf("%s\t", result[i]);
256                 }
257                 cmd->putchar('\n');
258         }
259
260         
261
262 }
263
264
265 void cmdl_exec(cmd_line* cmd)
266 {
267         cmdl_param_list* params;
268         int unknown=1;
269         struct command *ccmd;
270
271         params = cmdl_getparams(cmd->buffer);
272         
273         if(params == NULL){
274                 cmdl_clearbuffer(cmd);
275                 return;
276         }
277
278         if(params->argc > 0){
279                 if(!strcmp(params->argv[0], "exit") || !strcmp(params->argv[0], "quit")){
280                         cmdl_setexit(cmd, 1);
281 /*              }else if(!strcmp(params->argv[0], "help")){
282                         if(params->argc > 1){
283                                 cmdl_builtin_help(cmd, params->argv[1]);
284                         }else{
285                                 cmdl_builtin_help(cmd, "");
286                         }*/
287                 }else{
288                         for ( ccmd = cmd_start ; ccmd < cmd_end ; ccmd++ ) {
289                                 if(!strcmp(ccmd->name, params->argv[0])){
290                                         unknown = 0;
291                                         ccmd->exec(params->argc, params->argv);
292                                         break;
293                                 }
294                         }
295                         if(unknown){
296                                 cmd->printf("%s: unknown command\n", params->argv[0]);
297                         }
298                 }
299         }
300
301         free(params);   
302         cmdl_clearbuffer(cmd);
303 }
304
305 /*void cmdl_builtin_help(cmd_line* cmd, char* command){
306         struct command *ccmd;
307         int unknown = 1;
308         if(strcmp(command, "") == 0){
309                 cmd->printf("Built in commands:\n\n\thelp\t\tCommand usage help (\"help help\" for more info)\n\texit, quit\t\tExit the command line and boot\n\nCompiled in commands:\n\n");
310
311                 for ( ccmd = cmd_start ; ccmd < cmd_end ; ccmd++ ) {
312                         cmd->printf ("\t%s\t\t%s\n", ccmd->name, ccmd->desc );
313                 }
314         }else{
315                 if(!strcmp(command, "help")){
316                         cmd->printf("help - The help command\n\nUsage: help <command>\n\n\tExample:\n\t\thelp help\n");
317                 }else if(!strcmp(command, "exit") || !strcmp(command, "quit")){
318                         cmd->printf("exit, quit - The quit command\n\nUsage:\nquit or exit\n\n\tExample:\n\t\texit\n");
319                 }else{
320                         for ( ccmd = cmd_start ; ccmd < cmd_end ; ccmd++ ) {
321                                 if(!strcmp(ccmd->name, command)){
322                                         unknown = 0;
323                                         cmd->printf ("\t%s - %s\n\nUsage:\n%s\n", ccmd->name, ccmd->desc, ccmd->usage );
324                                         break;
325                                 }
326                                 if(unknown){
327                                         cmd->printf("\"%s\" isn't compiled in (does it exist?).\n", command);
328                                 }
329                         }
330                 }
331                 
332         }
333 }*/
334
335 cmdl_param_list* cmdl_getparams(const char* command){
336         cmdl_param_list* this;
337         char *result = NULL;
338         int count=0;
339         char *command2;
340         
341         this = (cmdl_param_list*)malloc(sizeof(cmdl_param_list));
342         
343         if(this == NULL){
344                 return NULL;
345         }
346
347         command2 = malloc(strlen(command) + 1);
348         
349         this->argc=0;
350
351         strcpy(command2, command);
352         result = strtok(command2, " ");
353         
354         while( result != NULL ) {
355                 this->argc++;
356                 result = strtok( NULL, " ");
357         }
358         
359         this->argv = (char**)malloc(sizeof(char*) * this->argc);
360         if(this->argv == NULL){
361                 free(this);
362                 return NULL;
363         }
364         
365         
366         strcpy(command2, command);
367         result = strtok(command2, " ");
368         
369         while( result != NULL && this->argc > count) {
370                 this->argv[count] = (char*)malloc(sizeof(char) * (strlen(result) + 1));
371                 if(this->argv[count] == NULL){
372                         free(this);
373                         return NULL;
374                 }
375                 strcpy(this->argv[count], result);
376                 count++;
377                 result = strtok( NULL, " ");
378         }   
379         free(command2); 
380         return this;
381 }
382
383 /*char* cmdl_parse_getcmd(cmd_line* cmd){
384         int i;
385         char* ret;
386         ret = (char*)malloc(1);
387         ret[0] = 0;
388
389         for(i=0; i < CMDL_BUFFER_SIZE - 1; i++){
390                 if(cmd->buffer[i + 1] == ' ' || cmd->buffer[i + 1] == '\0'){
391                         free(ret);
392                         ret = (char*)malloc(i+1);
393                         strncat(ret, cmd->buffer, i+1);
394                         break;
395                 }
396         }
397         return ret;
398 }*/
399
400 void cmdl_clearbuffer(cmd_line* cmd)
401 {
402         if(cmdl_check(cmd)){
403                 int i;
404                 cmd->cursor = 0;
405                 for(i=0; i < CMDL_BUFFER_SIZE; i++){
406                         cmd->buffer[i] = 0;
407                 }
408         }
409 }
410
411 /*void cmdl_clearoutput(cmd_line* cmd)
412 {
413         if(cmdl_check(cmd)){
414                 int i;
415                 for(i=0; i < CMDL_OUTPUT_SIZE; i++){
416                         cmd->output[i] = 0;
417                 }
418         }
419 }*/
420
421 int cmdl_movecursor(cmd_line* cmd, int direction)
422 {
423         if(cmdl_check(cmd)){
424                 switch(direction){
425                         case CMDL_LEFT:
426                                 if(cmd->cursor > 0){
427                                         cmd->cursor--;
428                                         cmd->putchar(CMDLK_BS);
429                                 }else{
430                                         return 0;
431                                 }
432                                 break;
433                         case CMDL_RIGHT:
434                                 if(cmd->cursor < CMDL_BUFFER_SIZE - 2){
435                                         cmd->cursor++;
436                                         cmd->putchar(' ');
437                                 }else{
438                                         return 0;
439                                 }
440                                 break;
441                 }
442         }
443         return 1;
444 }
445
446 void cmdl_del(cmd_line* cmd)
447 {
448         if(cmdl_check(cmd) && cmd->cursor < CMDL_BUFFER_SIZE - 2 && cmd->cursor >= 0){
449                 int i;
450                 for(i = cmd->cursor; i < (int)strlen(cmd->buffer); i++){
451                         cmd->buffer[i] = cmd->buffer[i + 1];
452                         if(!cmd->buffer[i]){
453                                 cmd->putchar(' ');
454                         }else{
455                                 cmd->putchar(cmd->buffer[i]);
456                         }
457                 }
458                 for(i = cmd->cursor; i < (int)strlen(cmd->buffer) + 1; i++){
459                         cmd->putchar(CMDLK_BS);
460                 }
461         }
462 }
463
464
465 int cmdl_check(cmd_line* cmd)
466 {
467         if(
468                 cmd != NULL && 
469                 cmd->buffer != NULL &&
470                 cmd->prompt != NULL &&
471                 cmd->cursor >= 0 && 
472                 cmd->cursor < CMDL_BUFFER_SIZE - 1 &&
473                 cmd->buffer[CMDL_BUFFER_SIZE - 1] == 0 &&
474                 cmd->prompt[CMDL_PROMPT_SIZE - 1] == 0
475         ){
476                 return 1;
477         }else{
478                 return 0;
479         }
480 }
481
482 cmd_line* cmdl_create()
483 {
484         cmd_line* this;
485         int i;
486         
487         /* Initiate the command line */
488         
489         this = (cmd_line*)malloc(sizeof(cmd_line));
490         
491         if(this == NULL){
492                 return NULL;
493         }
494         
495
496         /* Allocate output buffer */
497         
498         /*this->output = (char*)malloc(CMDL_OUTPUT_SIZE);
499         if(this->output == NULL){
500                 free(this);
501                 return NULL;
502         }*/
503         
504 /*      for(i = 0; i < CMDL_OUTPUT_SIZE; i++){
505                 this->output[i] = 0;
506         }*/
507
508         /* Allocate command line buffer */
509         
510         this->buffer = (char*)malloc(CMDL_BUFFER_SIZE);
511         if(this->buffer == NULL){
512                 free(this);
513                 return NULL;
514         }
515         
516         for(i = 0; i < CMDL_BUFFER_SIZE; i++){
517                 this->buffer[i] = 0;
518         }
519         
520         /* Allocate prompt buffer */
521         
522         this->prompt = (char*)malloc(CMDL_PROMPT_SIZE);
523         if(this->prompt == NULL){
524                 free(this);
525                 return NULL;
526         }
527         
528         for(i = 0; i < CMDL_PROMPT_SIZE; i++){
529                 this->prompt[i] = 0;
530         }
531         
532         /* Initiate cursor position etc.*/
533         
534         this->cursor = 0;
535         //this->has_output = 0;
536         this->exit = 0;
537         this->refresh = 1;
538         this->tabstate = 0;
539         this->insert = 0;
540
541         /* set callbacks to NULL */
542
543         this->getchar = NULL;
544         this->putchar = NULL;
545         this->printf = NULL;
546
547         /* List the commands */
548
549         struct command *cmd;
550
551         printf ( "Compiled in commands: ");
552         for ( cmd = cmd_start ; cmd < cmd_end ; cmd++ ) {
553                 printf("%s ", cmd->name);
554         }
555         printf("\n");
556
557         return this;
558 }
559
560 void cmdl_free(cmd_line* cmd)
561 {
562         free(cmd);
563 }
564