zoukankan      html  css  js  c++  java
  • busybox中getopt32用法

    函数实现原型:
    uint32_t getopt32(char **argv, const char *applet_opts, ...)
    参数说明:
      applet_opts:命令行选项必须声明为类似const char *applet_opts的字符串形式,如果其中一个选项被找到了, 就有一个标志值(unsigned long类型)中的某一位被置为1,
    最终这个标志值被作为返回值返回,标志值在哪一个位由字符在applet_opts种的位置决定
    最新代码链接:https://git.busybox.net/busybox/tree/libbb/getopt32.c
    注:本文所用的代码是https://git.busybox.net/busybox/snapshot/busybox-1_8_2.tar.gz
    例如:
      uint32_t flags = getopt32(argv, "rnug");
      applet_opts = "rnug"
      applet_opts[0] = "r"将添加1 (0比特位)  1<0   applet_opts[1] = "n"将添加2 (1比特位) 1<1   applet_opts[2] = "u"将添加4 (2比特位)  1<2   applet_opts[3] = "g"讲添加8 (3比特位)  1<3

      等等, 你也可以通过位域方式查看返回值,每个选项就是其中一位.
      一旦退出, 全局变量optind就被设置,因此如果你做argc -= optind; argv+= optind; argc就会等于剩下的非选项的参数个数,
    第一个放在argv[0],下一个放在argv[1]等等(选项和它们的参数在argv[optind]之前都会被移到argv[]中).

    applet_opts格式的说明(特殊字符代表不同的意思):

    ":"  如果一个选项需要一个参数, 那就在applet_opts字符后面添加":"然后提供指向参数的指针. 例如:
      char *pointer_to_arg_for_a;
      char *pointer_to_arg_for_b;
      char *pointer_to_arg_for_c;
      char *pointer_to_arg_for_d;

      flags = getopt32(argv, "a:b:c:d:",
      &pointer_to_arg_for_a, &pointer_to_arg_for_b,
      &pointer_to_arg_for_c, &pointer_to_arg_for_d);
      applet_opts = "a:b:c:d:"
    代码块1
    ...........
      while (*s) {
            if (c >= 32) break;//  sizeof(uint32_t)=4B=32b
            on_off->opt = *s;
            on_off->switch_on = (1 << c);//c是字符出现的位置也就是index
            if (*++s == ':') {//c: c->arg = va_arg 当一个字符后面加:此时会给这个选项加一个返回值,也就是pointer_to_arg_for_a等等
                on_off->optarg = va_arg(p, void **);
                while (*++s == ':') /* skip */;
            }
            on_off++;
            c++;
        }
      ............

    指针类型(char* 或 llist_t *)可以由"::"特殊分隔符控制,该分隔符由外部字符串opt_complementary设置(详细信息请看下面)

    "::"   如果选项有一个可选参数, 那就在applet_opts字符后面添加一个"::"并提供一个存储参数的指针. 注意可选参数必需紧跟着选项: -oparam而不是-o param.

    "+"   如果applet_opts字符串第一个字符是加号, 那就argv数组中一旦遇上非选项字符就马上停止选项处理. 对于像env那样的applet就不会处理参数为子程序了: env -i ls -d /
    这里我们希望env仅仅处理'-i'不是'-d'.
    函数内部实现(代码截取)
    uint32_t
    getopt32(char **argv, const char *applet_opts, ...)
    代码部分基本可以分成四个处理块:

    1.applet_opts代码块,这个可以看上面关于参数的说明

    2.applet_long_options代码块处理

      const char *applet_long_options
    该结构体允许你定义长选项:
    static const char applet_longopts[] ALIGN1 =
     //"name" has_arg val
     "verbose" No_argument "v";
     applet_long_options = applet_longopts;

    结构体的最后一个成员(val)通常在applet_opts里面设置用来匹配短选项. 如果在applet_opts里面没有匹配到, 如是:
    – 短选项的下一个位置的比特数
    – 如果has_arg不是"No_argument", 也可以使用ptr作为参数
    – opt_complementary也可以影响到它.

    注意: 一个好的applet可以让长参数可配置,通过配置处理而不是必需的特征. 当前标准命名配置选项为CONFIG_FEATURE_<applet>_LONG_OPTIONS.
    代码说明:
    #if ENABLE_GETOPT_LONG
    if (applet_long_options) {
      const char *optstr;
      unsigned i, count;       
     
    count = 1;
     //计算长选项的个数
      optstr = applet_long_options;
      while (optstr[0]) {
       optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */
       count++;
      }
     /* count == no. of longopts + 1 */
      long_options = alloca(count * sizeof(*long_options));
      memset(long_options, 0, count * sizeof(*long_options));
      i = 0;
      optstr = applet_long_options;
    //填充长选项中的数据
      while (--count) {
       long_options[i].name = optstr;
       optstr += strlen(optstr) + 1;
       long_options[i].has_arg = (unsigned char)(*optstr++);
       /* long_options[i].flag = NULL; */
       long_options[i].val = (unsigned char)(*optstr++);
       i++;
      }
      //下面这个for循环查找代码块1中没用定义的选项,如果没用就继续添加,占用的位是排在代码块1后面的
      for (l_o = long_options; l_o->name; l_o++) {
       if (l_o->flag)
         continue;
       for (on_off = complementary; on_off->opt != 0; on_off++){
         if (on_off->opt == l_o->val){
           goto next_long;
         }
       }
       if (c >= 32) break;
       on_off->opt = l_o->val;
       on_off->switch_on = (1 << c);//c=index index是代码块1向后推移的
       if (l_o->has_arg != no_argument){//如果有参数也是从代码块1开始添加后面的参数列表
         on_off->optarg = va_arg(p, void **);
       }
       c++;
     next_long: ;
     }
    }
    #endif /* ENABLE_GETOPT_LONG */


    3.opt_complementary代码块处理

    const char *opt_complementary
    ":"   冒号用来分隔两个或多个字符并/或字符组以及特殊字符(表示要检查一些状态)

    "abc" 如果指定了两个或多个字符组, 第一个字符就是主选项,其余的字符是副选项。 如果找到了主选项, 他们的标志就出现了即使没有在命令行中指定他们. 例如:

    opt_complementary = "abc";
    flags = getopt32(argv, "abcd");

    如果getopt()找到命令行中的"-a", getopt32的返回值就好比找到了"-a -b -c".

    "ww" 调整有计数器关联来指示选项的发生次数的双选项. 例如ps applet的需要:
     如果w给定一次, GNU ps设置宽度为132,
     如果w给定的多于一次, 就是"无限制"

     int w_counter = 0; //必需初始化
     opt_complementary = "ww";
     getopt32(argv, "w", &w_counter);
     if (w_counter)
      width = (w_counter == 1) ? 132 : INT_MAX;
     else
      get_terminal_width(…&width…);

     w_counter是一个指向整数的指针, 它得在所有选项参数沉下去之后传给getopt32().

     例如: 接受多个-v来表示冗长级别和每一个-b optarg级别,添加optarg到my_b. 最后, 如果给定了b就关闭c或者反过来:
     llist_t *my_b = NULL;
     int verbose_level = 0;
     opt_complementary = "vv:b::b-c:c-b";
     f = getopt32(argv, "vb:c", &my_b, &verbose_level);
     if (f & 2) //-c在-b后面, 取消设置-b标志
      while (my_b) dosomething_with(llist_pop(&my_b));
     if (my_b) //但是如果指定了-b就存储llist
      free_llist(my_b);
     if (verbose_level) printf("verbose level is %dn", verbose_level);

    特殊字符:
    "-" opt_complementary组中的起始字符是横线强制要求所有参数作为选项来看待,尽管它们没有横线打头. 这种情况下下一个字符不能是数字(0-9), 使用':'或行结束符. 例如:
    opt_complementary = "-:w-x:x-w";
    getopt32(argv, "wx");
    允许不适用横线而给定所有参数(./program w x), 就像带有横线一样(./program -x).
    NB: getopt32()会泄露一小部分内存如果你使用这个项. 如果有递归调用getopt32()的可能就不要使用.

    "–" opt_complementary开始的双横线表示argv[1]字符串应该总是作为选项来看待, 尽管不是以"-"做前缀. 这在诸如"ar"和"tar"的applet中有特别语义:
    tar xvf foo.tar
    NB: 如果你使用这个项,getopt32()将会泄露一小部分内存. 如果有递归调用getopt32()的可能就不要使用.

    "-N" opt_complementary组的起始横线紧跟一个数字(0-9)意味着至少要在命令行里面出现N个非选项的参数.
    "=N" opt_complementary起始的等号符紧跟打个数字(0-9)意味着要在命令行里出现恰好N个非选项参数.
    代码说明:
    //下面这个其实就是对选项的属性做辅助描述
    for (s = (const unsigned char *)opt_complementary; s && *s; s++) {
     t_complementary *pair;
     unsigned *pair_switch;

     bb_info_msg("opt_complementary %s *s:%c" ,opt_complementary, *s);
     if (*s == ':')
       continue;
     c = s[1];
     bb_info_msg("opt_complementary %s *s:%c c:%c c - '0':%d" ,opt_complementary,*s, c,c - '0');
     if (*s == '?') {//?N表示命令里头最多出现N个非选项参数
     if (c < '0' || c > '9') {
       spec_flgs |= SHOW_USAGE_IF_ERROR;
     } else {
       max_arg = c - '0';
       s++;
     }
     continue;
    }
    if (*s == '-') {
     if (c < '0' || c > '9') {//-N 表示命令参数里头至少出现N个非选项参数
      if (c == '-') {
       spec_flgs |= FIRST_ARGV_IS_OPT;
       s++;
      } else
       spec_flgs |= ALL_ARGV_IS_OPTS;
      } else {
       min_arg = c - '0';
       s++;
      }
     continue;
     }
     if (*s == '=') {//=N 表示命令参数里头刚好出现N个非选项参数
       min_arg = max_arg = c - '0';
       s++;
       continue;
     }
    //下面的代码主要描述多个参数互相的关系,o::, o:,o-:,a?b, a-b,a--b,a--bc.....
     for (on_off = complementary; on_off->opt; on_off++)
       if (on_off->opt == *s){
         bb_info_msg("opt_complementary %s on_off->opt:%c *s:%c" ,opt_complementary,on_off->opt, *s);
         break;
       }
       if (c == ':' && s[2] == ':') {//此选项是一个列表
        on_off->list_flg++;
        continue;
       }
       if (c == ':' || c == '') {//此选项必选
         requires |= on_off->switch_on;
         continue;
       }
       if (c == '-' && (s[2] == ':' || s[2] == '')) {//o-: 这个不知道是什么鬼 看着是自己排斥自己
         flags |= on_off->switch_on;
         on_off->incongruously |= on_off->switch_on;
         s++;
         continue;
       }
       if (c == *s) {//ww counter类型的,调整有计数器关联来指示选项的发生次数的双选项
         on_off->counter = va_arg(p, int *);
         s++;
       }
       pair = on_off;
       pair_switch = &(pair->switch_on);
       for (s++; *s && *s != ':'; s++) {
         if (*s == '?') {//a?
          pair_switch = &(pair->requires);
         } else if (*s == '-') {//a- or a--
          if (pair_switch == &(pair->switch_off))//a--
            pair_switch = &(pair->incongruously);
          else//a-
            pair_switch = &(pair->switch_off);
         } else {//a?b a-b a--b a--bc......
            for (on_off = complementary; on_off->opt; on_off++)
              if (on_off->opt == *s) {
              //这个地方pair_swith代表第一个出现的选项的不同的数据的地址,比如是a--bc那么pair_switch=&pair->incongruously
              //然后当解析到b的时候a的排斥项就多了一个b,到c的时候a的排斥就再多一个c,也就是排斥bc
                *pair_switch |= on_off->switch_on;
                break;
              }
         }
        }
        s--;
    }
    4.主程序参数处理设置返回参数
      没啥说的,看代码
    #if ENABLE_GETOPT_LONG
    while ((c = getopt_long(argc, argv, applet_opts,
                long_options, NULL)) != -1) {
    #else
    while ((c = getopt(argc, argv, applet_opts)) != -1) {//getopt glibc可以看源代名
    #endif
       c &= 0xff; /* fight libc's sign extends */
     loop_arg_is_opt:
       for (on_off = complementary; on_off->opt != c; on_off++) {
         /* c==0 if long opt have non NULL flag */
         if (on_off->opt == 0 && c != 0)
            bb_show_usage();
        }
        if (flags & on_off->incongruously)//互斥选项存在不对
           bb_show_usage();
        trigger = on_off->switch_on & on_off->switch_off;
        flags &= ~(on_off->switch_off ^ trigger);
        flags |= on_off->switch_on ^ trigger;
        flags ^= trigger;
            
        printf("flags:0x%x c=%c ", flags, c);
        if (on_off->counter)
           (*(on_off->counter))++;
        if (on_off->list_flg) {
            llist_add_to_end((llist_t **)(on_off->optarg), optarg);
        } else if (on_off->optarg) {
            *(char **)(on_off->optarg) = optarg;//这个optarg是getopt产生的一个全局变量 可以看相关代码
        }
        bb_info_msg("on_off->opt:%c on_off->optarg:%p" ,on_off->opt,on_off->optarg);
        if (pargv != NULL)
           break;
    }
    关于测试程序可以看看 http://blog.chinaunix.net/uid-26009923-id-5104930.html 这篇文章

     

     
  • 相关阅读:
    react native的注释
    p标签在div中垂直居中,并且div高度随着p标签文字内容的变化而变化
    express框架搭建服务端
    react native项目的创建和运行
    ES6通过使用babel兼容到ie9
    react父传子
    经典排序之 归并排序
    经典排序之 冒泡排序
    C++基础题
    关于虚函数的应用(10个例子)
  • 原文地址:https://www.cnblogs.com/chriszsy/p/12701515.html
Copyright © 2011-2022 走看看