6d3ccf2612e46542ed3a98658e76fc0dd277ab35
[mw/micromonitor-lm32.git] / umon_main / target / common / env.c
1 /* shell variable functions:
2  *      Used to load or retrieve shell variable information from the
3  *      shell variable table.
4  *
5  *      General notice:
6  *      This code is part of a boot-monitor package developed as a generic base
7  *      platform for embedded system designs.  As such, it is likely to be
8  *      distributed to various projects beyond the control of the original
9  *      author.  Please notify the author of any enhancements made or bugs found
10  *      so that all may benefit from the changes.  In addition, notification back
11  *      to the author will allow the new user to pick up changes that may have
12  *      been made by other users after this version of the code was distributed.
13  *
14  *      Note1: the majority of this code was edited with 4-space tabs.
15  *      Note2: as more and more contributions are accepted, the term "author"
16  *                 is becoming a mis-representation of credit.
17  *
18  *      Original author:        Ed Sutter
19  *      Email:                          esutter@lucent.com
20  *      Phone:                          908-582-2351
21  */
22 #include <stdarg.h>
23 #include "config.h"
24 #include "tfs.h"
25 #include "tfsprivate.h"
26 #include "ether.h"
27 #include "genlib.h"
28 #include "stddefs.h"
29 #include "cli.h"
30 #include "version.h"
31 #include "boardinfo.h"
32 #include "ee.h"
33
34 #if INCLUDE_SHELLVARS
35
36 #ifdef TARGET_ENV_SETUP
37 extern void TARGET_ENV_SETUP(void);
38 #endif
39
40 #ifndef PROMPT
41 #define PROMPT "uMON>"
42 #endif
43
44 int     shell_print(void);
45 int     envToExec(char *);
46 void clearenv(void);
47
48 /* Structure used for the shell variables: */
49 struct s_shell {
50         char    *val;           /* Value stored in shell variable */
51         char    *name;          /* Name of shell variable */
52         int             vsize;          /* Size of storage allocated for value */
53         struct  s_shell *next;
54 };
55
56 struct s_shell *shell_vars;
57
58 /* If no malloc, then use locally defined env_alloc() and env_free()...
59  */
60 #if INCLUDE_MALLOC
61
62 #define env_alloc       malloc
63 #define env_free        free
64
65 #else
66
67 #define ENV_ALLOC_TOT   48
68 #define ENV_ALLOC_SIZE  (sizeof(struct s_shell)+8)
69
70 struct env_space {
71         int inuse;
72         char space[ENV_ALLOC_SIZE];
73 } envSpace[ENV_ALLOC_TOT];
74
75
76 char *
77 env_alloc(int size)
78 {
79         int      i;
80
81         if (size > ENV_ALLOC_SIZE)
82                 return(0);
83
84         for(i=0;i<ENV_ALLOC_TOT;i++) {
85                 if (envSpace[i].inuse == 0) {
86                         envSpace[i].inuse = 1;
87                         memset(envSpace[i].space,0,ENV_ALLOC_SIZE);
88                         return(envSpace[i].space);
89                 }
90         }
91         return(0);
92 }
93
94 void
95 env_free(char *space)
96 {
97         int i;
98
99         for(i=0;i<ENV_ALLOC_TOT;i++) {
100                 if (envSpace[i].space == space) {
101                         envSpace[i].inuse = 0;
102                         break;
103                 }
104         }
105         return;
106 }
107 #endif
108
109 /*
110  *  Set()
111  *
112  *      Syntax:
113  *              set var                         clears the variable 'var'
114  *              set var value           assign "value" to variable 'var'
115  *              set -a var value        AND 'var' with 'value'
116  *              set -o var value        OR 'var' with 'value'
117  *              set -i var [value]      increment 'var' by 'value' (or 1 if no value)
118  *              set -d var [value]      decrement 'var' by 'value' (or 1 if no value)
119  *              set -x                          result of -i/-d is in hex
120  */
121 char *SetHelp[] = {
122         "Shell variable operations",
123 #if INCLUDE_EE
124         "-[ab:cdef:iox] [varname[=expression]] [value]",
125 #else
126         "-[ab:cdef:iox] [varname] [value]",
127 #endif
128 #if INCLUDE_VERBOSEHELP
129         " -a        AND var with value",
130         " -b        set console baudrate",
131         " -c        clear the environment",
132         " -d        decrease var by value (or 1)",
133         " -e        build an environ string",
134 #if INCLUDE_TFS
135         " -f{file}  create script from environment",
136 #endif
137         " -i        increase var by value (or 1)",
138         " -o        OR var with value",
139         " -x        result in hex (NA with expressions, use hex())",
140 #endif
141         0,
142 };
143
144 #define SET_NOOP        0
145 #define SET_INCR        1
146 #define SET_DECR        2
147 #define SET_OR          3
148 #define SET_AND         4
149
150 int
151 Set(int argc,char *argv[])
152 {
153         char    *file, *envp, buf[CMDLINESIZE];
154         int             opt, decimal, setop, i;
155
156         setop = SET_NOOP;
157         file = (char *)0;
158         envp = (char *)0;
159         decimal = 1;
160         while((opt=getopt(argc,argv,"ab:cdef:iox")) != -1) {
161                 switch(opt) {
162                 case 'a':               /* logical and */
163                         setop = SET_AND;
164                         decimal = 0;
165                         break;
166                 case 'b':
167                         ChangeConsoleBaudrate(atoi(optarg));
168                         return(CMD_SUCCESS);
169                 case 'c':               /* clear environment */
170                         clearenv();
171                         break;
172                 case 'd':               /* decrement */
173                         setop = SET_DECR;
174                         break;
175                 case 'e':       
176                         envp = getenvp();
177                         break;
178 #if INCLUDE_TFS 
179                 case 'f':               /* build script from environment */
180                         envToExec(optarg);
181                         return(0);
182 #endif
183                 case 'i':               /* increment */
184                         setop = SET_INCR;
185                         break;
186                 case 'o':               /* logical or */
187                         setop = SET_OR;
188                         decimal = 0;
189                         break;
190                 case 'x':
191                         decimal = 0;
192                         break;
193                 default:
194                         return(CMD_PARAM_ERROR);
195                 }
196         }
197
198         if (!shell_vars) {
199                 printf("No memory allocated for environment.\n");
200                 return(CMD_FAILURE);
201         }
202
203         if (setop != SET_NOOP) {        /* Do some operation on a shell variable */
204                 char    *varval;
205                 unsigned long   value, opval;
206
207                 /* For -i & -d, if value is not specified, then assume 1. */
208                 if (argc == optind+1) {
209                         if ((setop == SET_INCR) || (setop == SET_DECR))
210                                 opval = 1;
211                         else
212                                 return(CMD_PARAM_ERROR);
213                 }
214                 else if (argc == optind+2)
215                         opval = strtoul(argv[optind+1],0,0);
216                 else
217                         return(CMD_PARAM_ERROR);
218
219                 varval = getenv(argv[optind]);
220                 if (!varval) {
221                         printf("%s: not found\n", argv[optind]);
222                         return(CMD_FAILURE);
223                 }
224                         
225                 value = strtoul(varval,(char **)0,0);
226                 switch(setop) {
227                         case SET_INCR:
228                                 value += opval;
229                                 break;
230                         case SET_DECR:
231                                 value -= opval;
232                                 break;
233                         case SET_AND:
234                                 value &= opval;
235                                 break;
236                         case SET_OR:
237                                 value |= opval;
238                                 break;
239                 }
240                 if (decimal)
241                         sprintf(buf,"%ld",value);
242                 else
243                         sprintf(buf,"0x%lx",value);
244                 setenv(argv[optind],buf);
245         }
246         else if (argc == optind) {      /* display all variables */
247                 shell_print();
248         }
249         else if (argc == (optind+1)) {  /* run EE or clear one var or set envp */
250 #if INCLUDE_EE
251                 switch(setEE(argv[optind])) {
252                         case 1:
253                                 return(CMD_SUCCESS);
254                         case -1:
255                                 return(CMD_FAILURE);
256                 }
257 #endif
258                 if (envp) 
259                         shell_sprintf(argv[optind],"0x%lx",(ulong)envp);
260                 else
261                         setenv(argv[optind],0);
262         }
263         else if (argc >= (optind+2)) {  /* Set a specific variable */
264                 buf[0] = 0;
265                 for(i=optind+1;i<argc;i++) {
266                         if ((strlen(buf) + strlen(argv[i]) + 2) >= sizeof(buf)) {
267                                 printf("String too large\n");
268                                 break;
269                         }
270                         strcat(buf,argv[i]);
271                         if (i != (argc-1))
272                                 strcat(buf," ");
273                 }
274                 if (!decimal)
275                         shell_sprintf(argv[optind],"0x%lx",atoi(buf));
276                 else
277                         setenv(argv[optind],buf);
278         }
279         else 
280                 return(CMD_PARAM_ERROR);
281
282         return(CMD_SUCCESS);
283 }
284
285 /* Shell variable support routines...
286  *      The basic scheme is to malloc in the space needed for the variable
287  *      name and the value of that variable.  For each variable that 
288  *      exists there is one s_shell structure that is in the linked list.
289  *      As shell variables are removed, their corresonding s_shell structure
290  *      is NOT removed, but the data pointed to within the structure is
291  *      freed.  This keeps the linked list implementation extremely simple
292  *      but maintains the versatility gained by using malloc for the 
293  *      variables instead of some limted set of static arrays.
294  */
295
296
297 /* shell_alloc():
298  *      First scan through the entire list to see if the requested
299  *      shell variable name already exists in the list; if it does,
300  *      then just use the same s_shell entry but change the value.
301  *      Also, if the new value fits in the same space as the older value,
302  *      then just use the same memory space (don't do the free/malloc).
303  *      If it doesn't, then scan through the list again.  If there
304  *      is one that has no variable assigned to it (name = 0), then
305  *      use it for the new allocation.  If all s_shell structures do 
306  *      have valid entries, then malloc a new s_shell structure and then
307  *      place the new shell variable data in that structure.
308  */
309
310 static int
311 shell_alloc(char *name,char *value)
312 {
313         int     namelen, valuelen;
314         struct s_shell *sp;
315
316         sp = shell_vars;
317         namelen = strlen(name);
318         valuelen = strlen(value);
319         while(1) {
320                 if (sp->name == (char *)0) {
321                         if (sp->next != (struct s_shell *)0) {
322                                 sp = sp->next;
323                                 continue;
324                         }
325                         else
326                                 break;
327                 }
328                 if (strcmp(sp->name,name) == 0) {
329                         if (sp->vsize < valuelen+1) {           /* If new value is smaller      */
330                                 env_free(sp->val);                              /* than the old value, then */
331                                 sp->val = env_alloc(valuelen+1);/* don't re-allocate any        */
332                                 if (!sp->val)                                   /* memory, just copy into       */
333                                         return(-1);                                     /* the space used by the        */
334                                 sp->vsize = valuelen+1;                 /* previous value.                      */
335                         }
336                         strcpy(sp->val,value);
337                         return(0);
338                 }
339                 if (sp->next == (struct s_shell *)0) 
340                         break;
341                 sp = sp->next;
342         }
343         sp = shell_vars;
344         while(1) {
345                 if (sp->name == (char *)0) {
346                         sp->name = env_alloc(namelen+1);
347                         if (!sp->name)
348                                 return(-1);
349                         strcpy(sp->name,name);
350                         sp->val = env_alloc(valuelen+1);
351                         if (!sp->val)
352                                 return(-1);
353                         sp->vsize = valuelen+1;
354                         strcpy(sp->val,value);
355                         return(0);
356                 }
357                 if (sp->next != (struct s_shell *)0)
358                         sp = sp->next;
359                 else {
360                         sp->next = (struct s_shell *)env_alloc(sizeof(struct s_shell));
361                         if (!sp->next)
362                                 return(-1);
363                         sp = sp->next;
364                         sp->name = env_alloc(namelen+1);
365                         if (!sp->name)
366                                 return(-1);
367                         strcpy(sp->name,name);
368                         sp->val = env_alloc(valuelen+1);
369                         if (!sp->val)
370                                 return(-1);
371                         sp->vsize = valuelen+1;
372                         strcpy(sp->val,value);
373                         sp->next = (struct s_shell *)0;
374                         return(0);
375                 }
376         }
377 }
378
379 /* shell_dealloc():
380  *      Remove the requested shell variable from the list.  Return 0 if
381  *      the variable was removed successfully, otherwise return -1.
382  */
383 static int
384 shell_dealloc(char *name)
385 {
386         struct  s_shell *sp;
387
388         sp = shell_vars;
389         while(1) {
390                 if (sp->name == (char *)0) {
391                         if (sp->next == (struct s_shell *)0)
392                                 return(-1);
393                         else {
394                                 sp = sp->next;
395                                 continue;
396                         }
397                 }
398                 if (strcmp(name,sp->name) == 0) {
399                         env_free(sp->name);
400                         env_free(sp->val);
401                         sp->name = (char *)0;
402                         sp->val = (char *)0;
403                         return(0);
404                 }
405                 
406                 if (sp->next == (struct s_shell *)0)
407                         return(-1);
408                 else
409                         sp = sp->next;
410         }
411 }
412
413 /* ConsoleBaudEnvSet():
414  * Called by to load/reload the CONSOLEBAUD shell variable based on
415  * the content of the global variable 'ConsoleBaudRate'.
416  */
417 void
418 ConsoleBaudEnvSet(void)
419 {
420         char    buf[16];
421
422         sprintf(buf,"%d",ConsoleBaudRate);
423         setenv("CONSOLEBAUD",buf);
424 }
425
426 /* ShellVarInit();
427  *      Setup the shell_vars pointer appropriately for additional
428  *      shell variable assignments that will be made through shell_alloc().
429  */
430 int
431 ShellVarInit()
432 {
433         char    buf[16];
434
435 #if !INCLUDE_MALLOC
436         memset((char *)&envSpace,0,sizeof(envSpace));
437 #endif
438
439         shell_vars = (struct s_shell *)env_alloc(sizeof(struct s_shell));
440         if (!shell_vars) {
441                 printf("No memory for environment initialization\n");
442                 return(-1);
443         }
444         shell_vars->next = (struct s_shell *)0;
445         shell_vars->name = (char *)0;
446         setenv("PROMPT",PROMPT);
447         sprintf(buf,"0x%lx",APPLICATION_RAMSTART);
448         setenv("APPRAMBASE",buf);
449         sprintf(buf,"0x%lx",BOOTROM_BASE);
450         setenv("BOOTROMBASE",buf);
451         setenv("PLATFORM",PLATFORM_NAME);
452         setenv("MONITORBUILT",monBuilt());
453         shell_sprintf("MONCOMPTR","0x%lx",(ulong)&moncomptr);
454
455         /* Support the ability to have additional target-specific 
456          * shell variables initialized at startup...
457          */
458 #ifdef TARGET_ENV_SETUP
459         TARGET_ENV_SETUP();
460 #endif
461
462         shell_sprintf("VERSION_MAJ","%d",MAJOR_VERSION);
463         shell_sprintf("VERSION_MIN","%d",MINOR_VERSION);
464         shell_sprintf("VERSION_TGT","%d",TARGET_VERSION);
465         return(0);
466 }
467
468 /* getenv:
469  *      Return the pointer to the value entry if the shell variable
470  *      name is currently set; otherwise, return a null pointer.
471  */
472 char *
473 getenv(char *name)
474 {
475         register struct s_shell *sp;
476
477         for(sp = shell_vars;sp != (struct s_shell *)0;sp = sp->next) {
478                 if (sp->name != (char *)0) {
479                         if (strcmp(sp->name,name) == 0)
480                                 return(sp->val);
481                 }
482         }
483         return((char *)0);
484 }
485
486 /* getenvp:
487  *      Build an environment string consisting of all shell variables and
488  *      their values concatenated into one string.  The format is
489  *
490  *        NAME=VALUE LF NAME=VALUE LF NAME=VALUE LF NULL
491  *
492  *      with the limit in size being driven only by the space
493  *      available on the heap.  Note that this uses malloc, and it
494  *      the responsibility of the caller to free the pointer when done.
495  */
496 char *
497 getenvp(void)
498 {
499         int size;
500         char *envp, *cp;
501         register struct s_shell *sp;
502
503         size = 0;
504
505         /* Get total size of the current environment vars */
506         for(sp = shell_vars;sp != (struct s_shell *)0;sp = sp->next) {
507                 if (sp->name != (char *)0) {
508                         size += (strlen(sp->name) + strlen(sp->val) + 2);
509                 }
510         }
511         if (size == 0)
512                 return((char *)0);
513
514         envp = env_alloc(size+1);       /* leave room for final NULL */
515         if (envp == 0)
516                 return((char *)0);
517
518         cp = envp;
519         for(sp = shell_vars;sp != (struct s_shell *)0;sp = sp->next) {
520                 if (sp->name != (char *)0)
521                         cp += sprintf(cp,"%s=%s\n",sp->name,sp->val);
522         }
523         *cp = 0;                /* Append NULL after final separator */
524         return(envp);
525 }
526
527 /* clearenv():
528  * Clear out the entire environment.
529  */
530 void
531 clearenv(void)
532 {
533         struct  s_shell *sp;
534
535         for(sp = shell_vars;sp != (struct s_shell *)0;sp = sp->next) {
536                 if (sp->name != (char *)0) {
537                         env_free(sp->name);
538                         env_free(sp->val);
539                         sp->name = (char *)0;
540                         sp->val = (char *)0;
541                 }
542         }
543 }
544
545 /* setenv:
546  *      Interface to shell_dealloc() and shell_alloc().
547  */
548 int
549 setenv(char *name,char *value)
550 {
551         if (!shell_vars)
552                 return(-1);
553         if ((value == (char *)0) || (*value == 0))
554                 return(shell_dealloc(name));
555         else
556                 return(shell_alloc(name,value));
557 }
558
559 /* shell_print():
560  *      Print out all of the current shell variables and their values.
561  */
562 int
563 shell_print(void)
564 {
565         int     maxlen, len;
566         char format[8];
567         register struct s_shell *sp;
568
569         /* Before printing the list, pass through the list to determine the
570          * largest variable name.  This is used to create a format string
571          * that is then passed to printf() when printing the list of
572          * name/value pairs.  It guarantees that regardless of the length
573          * of the name, the format of the printed out put will be consistent
574          * for all variables.
575          */
576         maxlen = 0;
577         sp = shell_vars;
578         while(1) {
579                 if (sp->name) {
580                         len = strlen(sp->name);
581                         if (len > maxlen)
582                                 maxlen = len;
583                 }
584                 if (sp->next != (struct s_shell *)0)
585                         sp = sp->next;
586                 else
587                         break;
588         }
589         sprintf(format,"%%%ds = ",maxlen+1);
590
591         /* Now that we know the size of the largest variable, we can 
592          * print the list cleanly...
593          */
594         sp = shell_vars;
595         while(1) {
596                 if (sp->name != (char *)0) {
597                         printf(format, sp->name);
598                         puts(sp->val);          /* sp->val may overflow printf, so use puts */
599                 }
600                 if (sp->next != (struct s_shell *)0)
601                         sp = sp->next;
602                 else
603                         break;
604         }
605         return(0);
606 }
607
608 /* shell_sprintf():
609  *      Simple way to turn a printf-like formatted string into a shell variable.
610  */
611 int
612 shell_sprintf(char *varname, char *fmt, ...)
613 {
614         int     tot;
615         char buf[CMDLINESIZE];
616         va_list argp;
617
618         va_start(argp,fmt);
619         tot = vsnprintf(buf,CMDLINESIZE-1,fmt,argp);
620         va_end(argp);
621         setenv(varname,buf);
622         return(tot);
623 }
624
625
626 #if INCLUDE_TFS
627 /* validEnvToExecVar():
628  * Return 1 if the variable should be included in the script
629  * generated by envToExec(); else return 0.
630  * Specifically... if the variable is generated internally
631  * then we don't want to include it in the script.
632  */
633 int
634 validEnvToExecVar(char *varname)
635 {
636         char **vp;
637         static char *invalid_varprefixes[] = {
638                 "ARG",                  "TFS_PREFIX_",  "TFS_START_",
639                 "TFS_END_",             "TFS_SPARE_",   "TFS_SPARESZ_",
640                 "TFS_SCNT_",    "TFS_DEVINFO_", "FLASH_BASE_",
641                 "FLASH_SCNT_",  "FLASH_END_",
642                 0
643         };
644         static char *invalid_varnames[] = {
645                 "APPRAMBASE",   "BOOTROMBASE",  "CMDSTAT",
646                 "CONSOLEBAUD",  "MALLOC",               "MONCOMPTR",
647                 "MONITORBUILT", "PLATFORM",             "PROMPT",
648                 "TFS_DEVTOT",   "FLASH_DEVTOT", "PROMPT",
649                 "VERSION_MAJ",  "VERSION_MIN",  "VERSION_TGT",
650                 "MONCMD_SRCIP", "MONCMD_SRCPORT",
651                 0
652         };
653
654         if (varname == 0)
655                 return(0);
656
657         if (strncmp(varname,"ARG",3) == 0)
658                 return(0);
659
660 #if INCLUDE_BOARDINFO
661         if (BoardInfoVar(varname))
662                 return(0);
663 #endif
664
665         for(vp=invalid_varnames;*vp;vp++) {
666                 if (!strcmp(varname,*vp))
667                         return(0);
668         }
669         for(vp=invalid_varprefixes;*vp;vp++) {
670                 if (!strncmp(varname,*vp,strlen(*vp)))
671                         return(0);
672         }
673         return(1);
674 }
675
676 /* envToExec():
677    Create a file of "set" commands that can be run to recreate the
678    current environment.
679 */
680 int
681 envToExec(char *filename)
682 {
683         int             err, vartot;
684         char    *buf, *bp, *cp;
685         register struct s_shell *sp;
686
687         buf = bp = (char *)getAppRamStart();
688         sp = shell_vars;
689         vartot = 0;
690
691         while(1) {
692                 if (validEnvToExecVar(sp->name)) {
693                         bp += sprintf(bp,"set %s \"",sp->name);
694                         cp = sp->val;
695                         while(*cp) {
696                                 if (*cp == '$')
697                                         *bp++ = '\\';
698                                 *bp++ = *cp++;
699                         }
700                         *bp++ = '\"';
701                         *bp++ = '\n';
702                         *bp = 0;
703                         vartot++;
704                 }
705                 if (sp->next != (struct s_shell *)0)
706                         sp = sp->next;
707                 else
708                         break;
709         }
710         if (vartot > 0) {
711                 err = tfsadd(filename,"envsetup","e",(unsigned char *)buf,strlen(buf));
712                 if (err != TFS_OKAY) {
713                         printf("%s: %s\n",filename,(char *)tfsctrl(TFS_ERRMSG,err,0));
714                         return(-1);
715                 }
716         }
717         return(0);
718 }
719 #endif
720
721 #else
722
723 /* The 'set' command is part of the build even if INCLUDE_SHELLVARS
724  * is false.  This allows the user to still access teh "set -b ###"
725  * facility for changing the baudrate.
726  */
727 char *SetHelp[] = {
728         "Set baud",
729         "-[b:] (no args)",
730 #if INCLUDE_VERBOSEHELP
731         " -b        set console baudrate",
732 #endif
733         0,
734 };
735
736 int
737 Set(int argc,char *argv[])
738 {
739         int             opt;
740
741         while((opt=getopt(argc,argv,"b:")) != -1) {
742                 switch(opt) {
743                 case 'b':
744                         ChangeConsoleBaudrate(atoi(optarg));
745                         return(CMD_SUCCESS);
746                         break;
747                 default:
748                         return(CMD_PARAM_ERROR);
749                 }
750         }
751         printf("Shell vars not included in build.\n");
752         return(CMD_FAILURE);
753 }
754
755 int
756 setenv(char *name,char *value)
757 {
758         return(-1);
759 }
760
761 char *
762 getenv(char *name)
763 {
764         return(0);
765 }
766
767 int
768 shell_sprintf(char *varname, char *fmt, ...)
769 {
770         return(0);
771 }
772
773 void
774 ConsoleBaudEnvSet(void)
775 {
776 }
777
778 char *
779 getenvp(void)
780 {
781         return(0);
782 }
783
784 #endif
785
786 /* ChangeConsoleBaudrate():
787  * Called to attempt to adjust the console baudrate.
788  */
789 int
790 ChangeConsoleBaudrate(int baud)
791 {
792         if (ConsoleBaudSet(baud) < 0) {
793                 printf("Baud=%d failed\n",baud);
794                 return(-1);
795         }
796         ConsoleBaudRate = baud;
797         ConsoleBaudEnvSet();
798         return(0);
799 }
800