setenv
是操作uboot中环境变量的一个命令
命令操作的简单回顾:
新建一个环境变量
命令行:set newenv 1
结果: newenv=1
删掉一个环境变量
命令行:set newenv
结果:这个命令就没了,printenv也不会出现。
从代码开始分析:
路径跟printenv一样
在uboot/common/cmd_nve.c
在之前的一篇忘记了,这些命令的执行都是通过U_BOOT_CMD去执行这些命令。
在setenv对应的U_BOOT_CMD中是通过调用do_setenv函数来执行命令
do_setenv
:
int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
if (argc < 2) {
printf ("Usage:\n%s\n", cmdtp->usage);
return 1;
}
return _do_setenv (flag, argc, argv);
}
分析可知,setenv的传参个数要>=2个,小于的话会输出Usage:。。。。。
另外,do_setenv
是调用的_do_setenv
函数。
题外话:
函数之间的层次关系
如果函数名没有下划线_
,则是自己写的,可以改动
如果函数名前面有一条_
,则代表是系统调用的
如果有两条下划线__
,则代表是系统内部调用的
再深一层,三条下划线___
,层次进一步深入了,不要碰了。
_do_setenv
关于这个函数,分析的方法跟上一篇do_printenv是相类似的
通过分析,简要的可以抽出三部分来看的
第一部分,找到你要改的变量
第二部分:删掉这个变量,其实就是把这个变量的后面的往上挪移,去覆盖掉你要删或者修改单变量
第三部分:增加新的变量,或者说把你改的变量重新加在后面。
前面两个部分我都有很认真的分析过,第三部分直接从源码拷贝过来了,看了一下,大概的思路还是差不多的。
下面是整体的测试代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
/*DEBUG宏*/
#define __DEBUG__
//#undef __DEBUG__
#ifdef __DEBUG__
#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"", __LINE__, ##__VA_ARGS__)
#else
#define DEBUG(format,...)
#endif
#define ENV_SIZE 30
static char env_get_char_init (int index);
char env_get_char (int index);
int envmatch (char *s1, int i2);
char default_environment[30] = {"a=2\0bc=3\0wtf=45\0"};
int main(int argc,char *argv[])
//*******************************//
//解引用后指向的是\0会怎样
//*******************************//
int i = 0;
int k = -1;
char *env;
char *env_data;
char default_environment[] = {"a=2\0bc=3\0bd=45\0"};
env_data = &default_environment[0];
env = env_data;
for(i=0; *env; env++)
{
k = 1;
printf("i=%d\n",++i);
}
if(k<0)
{
printf("jump out of loop\n");
}
//*******************************//
//结论:会认为是空,然后退出循环,像这里的话a = \0 \0只会循环到a = ,后面一遇到\0,表达式2不成立,转而执行表达式3了。
//*******************************//
#endif
#if 1
//*******************************//
//测试setenv
//编译出来之后用法:./a.out 变量名字 更改值
//char default_environment[] = {"a=2\0bc=3\0wtf=45\0"};
//需要变量从a bc wtf里面选 ,新增变量名字也可以
//*******************************//
// 初始化用到的变量
int i = 0;
int k = -1;
int len;
char *name = argv[1];
char *env, *nxt = NULL;
char *env_data;
//char default_environment[] = {"a=2\0bc=3\0wtf=45\0"};
//第一部分,找到你要改的变量
env_data = &default_environment[0];
env = env_data;
int oldval = -1;
for (env=env_data; *env; env=nxt+1)
{
for (nxt=env; *nxt; ++nxt)
;
if ((oldval = envmatch((char *)name, env-env_data)) >= 0)
break;
}
DEBUG("oldval = %d\n",oldval);
DEBUG("*env = %c\n",*env);
DEBUG("*nxt = %c\n",*nxt);
DEBUG("*++nxt = %c\n",*++nxt);
nxt = nxt-1;
//第二部分:删掉这个变量,其实就是把这个变量的后面的往上挪移,去覆盖掉你要删或者修改单变量
if (*++nxt == '\0')
{
if (env > env_data)
{
env--;
DEBUG(" aaaa *env = %c\n",*env);
}
else
{
*env = '\0';
DEBUG(" bbbb *env = %c\n",*env);
}
}
else
{
for (;;)
{
DEBUG("before \n");
DEBUG("*env = %c\n",*env);
DEBUG("*nxt = %c\n",*nxt);
*env = *nxt++;
DEBUG("after \n");
DEBUG("*env = %c\n",*env);
DEBUG("*nxt = %c\n",*nxt);
if ((*env == '\0') && (*nxt == '\0'))
{
break;
}
++env;
}
}
//第三部分:增加新的变量,或者说把你改的变量重新加在后面。
/* Append new definition at the end*/
for (env=env_data; *env || *(env+1); ++env)
;
if (env > env_data)
++env;
/*
* Overflow when:
* "name" + "=" + "val" +"\0\0" > ENV_SIZE - (env-env_data)
*/
len = strlen(name) + 2;
/* add '=' for first arg, ' ' for all others */
for (i=2; i<argc; ++i) {
len += strlen(argv[i]) + 1;
}
if (len > (&env_data[ENV_SIZE]-env)) {
printf ("## Error: environment overflow, \"%s\" deleted\n", name);
return 1;
}
while ((*env = *name++) != '\0')
env++;
for (i=2; i<argc; ++i) {
char *val = argv[i];
*env = (i==2) ? '=' : ' ';
while ((*++env = *val++) != '\0')
;
}
/* end is marked with double '\0' */
*++env = '\0';
//测试代码,看看修改后的环境变量数组是怎样的
int num = 0;
for(num=0; num<sizeof(default_environment); num++)
{
DEBUG("default_environment[%d] = %c\n",num,default_environment[num]);
}
//*******************************//
//结论:打印了这么多出来,其实可以发现,删掉了是真没了,是用后面的往前挪来覆盖要删掉的。
//然后新的环境变量是会被挪移到最后,在最后面添加的。
#endif
return 0;
}
int envmatch (char *s1, int i2)
{
/* printf("before while: i2 = %d\n",i2);
printf("before while: s1 = %c\n",*s1);
printf("before while: env_get_char(i2++) = %c\n",env_get_char(i2++));
i2=i2-1; */
while (*s1 == env_get_char(i2++))
{
/* printf("after while : *s1 = %c\n",*s1);
printf("after while : i2 = %d\n",i2);
printf("after while : *s1++ = %c\n",*s1++);
s1=s1-1; */
if (*s1++ == '=')
{
// printf("after while if : i2 = %d\n",i2);
return(i2);
}
//printf("after if: *s1 = %c\n",*s1);
}
if (*s1 == '\0' && env_get_char(i2-1) == '=')
return(i2);
return(-1);
}
char env_get_char (int index)
{
char c;
c = env_get_char_init(index);
return (c);
}
static char env_get_char_init (int index)
{
char c;
//char default_environment[] = {"a=2\0bc=3\0wtf=45\0"};
c = default_environment[index];
return (c);
}
这里我把环境变量数组的大小设置为30了,所以只有30个位置,但是够测试用。
下面是测试结果。
char default_environment[30] = {“a=2\0bc=3\0wtf=45\0”};
这是我设置的默认环境变量,数组大小30,里面默认存的值是a=2\0bc=3\0wtf=45\0,然后我测试的时候把a改成了33,结果a的环境变量被改了,后面的往前挪移,覆盖了原来a的环境变量,然后修改好的a的环境变量在最后面出现。这个测试结果是跟在uboot里面测试的时候是一样的。
总结一下
1.重点是要理解环境变量的存储方式
例如这样:char default_environment[30] = {“a=2\0bc=3\0wtf=45\0”};
2.如果指针指向的位置存的是\0的话,那么解引用之后就是为空的(假的),所以if或者for循环的表达式2都不会成立
3.能读懂代码最好,如果代码比较复杂的话,可以像这样做一些测试代码,在linux下的话可以printf打印调试信息来检查校验,帮助理解代码。