使用getopt_long()从命令行获取参数,struct option

本文来自:http://blog.csdn.net/yui/article/details/5669922

众所周知,C程序的主函数有两个参数,其中,第一个参数是整型,可以获得包括程序名字的参数个数,第二个参数是字符数组指针或字符指针的指针,可以按顺序获得命令行上各个字符串参数。其原形是:

int main(int argc, char *argv[]);

或者

int main(int argc, char **argv);

 

如果有一个解析CDR的程序,名叫destroy,负责将一个二进制格式的CDR文件转换为文本文件,输出的文本的样式由另外一个描述文件定义,那么,命令行要求输入的参数就有三个:CDR文件名、输出文件名和描述文件名。其中,前两个参数是必须输入的,第三个的描述文件名可以不输入,程序会自动采用默认的输出样式。很自然,主函数的三个参数就应该这样排列:

./destroy cdr cdr.txt [cdr.desc]

 

这样做在一般情况下不会有太大问题,问题来源于扩展性的需求。如果有一天,用户要求解析程序能够按关键字解析,只有含有关键字的CDR才能够输出。解决方法很简单,只要在参数列表的最后,加上它就可以了。不过,这样就使得原本可选的描述文件名变为必须输入:

./destroy cdr cdr.txt cdr.desc [keyword]

 

因为不改的话,你就不知道,第三个参数究竟是描述文件名,还是关键字。现在还算好办,如果以后陆续有增加参数的需求,关键字也变成必须输入了,这个时候,如果要查找全部CDR,你还得定义一个“特殊的关键字”,告诉程序,把数据统统给我捞出来……

 

有鉴于此,在Unix/Linux的正式的项目上,程序员通常会使用getopt()或者getopt_long()来获得输入的参数。两者的一个区别在于getopt()只支持短格式参数,而getopt_long()既支持短格式参数,又支持长格式参数。

短格式:./destroy -f cdr -o cdr.txt -c cdr.desc -k 123456

长格式:./destroy --file cdr --output cdr.txt --config cdr.desc --keyword 123456

 

引入了getopt()和getopt_long()的项目,设计者可以按需要,方便地增加参数,或者随意地放置参数的先后次序,只需要在程序中判断,哪些参数是必须的就可以了。关于这两个函数的用法,大家可以上网搜索一下,不再累述。附件destroy_linux.c给出了在Linux下使用getopt_long()的实例。

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <getopt.h>  
  5.   
  6. void print_usage(const char *program_name) {  
  7.     printf("%s 1.0.0 (2010-06-13)/n", program_name);  
  8.     printf("This is a program decoding a BER encoded CDR file/n");  
  9.     printf("Usage: %s -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>]/n", program_name);  
  10.     printf("    -f --file       the CDR file to be decoded/n");  
  11.     printf("    -o --output     the output file in plain text format/n");  
  12.     printf("    -c --config     the description file of the CDR file, if not given, use default configuration/n");  
  13.     printf("    -k --keyword    the keyword to search, if not given, all records will be written into output file/n");  
  14. }  
  15.   
  16. int main(int argc, char *argv[]) {  
  17.     char *file_name = NULL;  
  18.     char *output_name = NULL;  
  19.     char *config_name = NULL;  
  20.     char *keyword = NULL;  
  21.   
  22.     const char *short_opts = "hf:o:c:k:";  
  23.     const struct option long_opts[] = {  
  24.         {"help", no_argument, NULL, 'h'},  
  25.         {"file", required_argument, NULL, 'f'},  
  26.         {"output", required_argument, NULL, 'o'},  
  27.         {"config", required_argument, NULL, 'c'},  
  28.         {"keyword", required_argument, NULL, 'k'},  
  29.         {0, 0, 0, 0}  
  30.     };  
  31.     int hflag = 0;  
  32.   
  33.     int c;  
  34.     opterr = 0;  
  35.   
  36.     while ( (c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1 ) {  
  37.         switch ( c ) {  
  38.             case 'h' :  
  39.                 hflag = 1;  
  40.                 break;  
  41.             case 'f' :  
  42.                 file_name = optarg;  
  43.                 break;  
  44.             case 'o' :  
  45.                 output_name = optarg;  
  46.                 break;  
  47.             case 'c' :  
  48.                 config_name = optarg;  
  49.                 break;  
  50.             case 'k' :  
  51.                 keyword = optarg;  
  52.                 break;  
  53.             case '?' :  
  54.                 if ( optopt == 'f' || optopt == 'o' || optopt == 'c' || optopt == 'k' )  
  55.                     printf("Error: option -%c requires an argument/n", optopt);  
  56.                 else if ( isprint(optopt) )  
  57.                     printf("Error: unknown option '-%c'/n", optopt);  
  58.                 else  
  59.                     printf("Error: unknown option character '//x%x'/n", optopt);  
  60.                 return 1;  
  61.             default :  
  62.                 abort();  
  63.         }  
  64.     }  
  65.   
  66.     if ( hflag || argc == 1 ) {  
  67.         print_usage(argv[0]);  
  68.         return 0;  
  69.     }  
  70.     if ( !file_name ) {  
  71.         printf("Error: file name must be specified/n");  
  72.         return 1;  
  73.     }  
  74.     if ( !output_name ) {  
  75.         printf("Error: output name must be specified/n");  
  76.         return 1;  
  77.     }  
  78.   
  79.     // if not setting default, Linux OK, but SunOS core dump  
  80.     if ( !config_name ) config_name = "(null)";  
  81.     if ( !keyword ) keyword = "(null)";  
  82.     printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);  
  83.     return 0;  
  84. }  

 

另外一个区别是,getopt()几乎通用于所有类Unix系统,而getopt_long()只有在GNU的Unix/Linux下才能用。如果把上述程序放到Tru64上编译,就会出现以下错误:

cc -o destroy destroy_linux.c

cc: Error: destroy_linux.c, line 24: In the initializer for long_opts, an array's element type is incomplete, which precludes its initialization. (incompelinit)

                {"help", no_argument, NULL, 'h'},

----------------^

 

所以,如果一定要在Tru64等非GNU的OS上做到长格式的效果,除了自己另起炉灶之外,基本上只好借助一些跨平台的开源项目了。附件里的getopt_long.c和getopt.h是从opensolaris的网站上抄下来的,是包含在sg3_utils软件包中的程序。sg3_utils具体是什么,我也不知道,据说是一个Linux的开发包,用来直接使用SCSI命令集访问设备。(sg3_utils is a package of utilities for accessing devices that use SCSI command sets.)反正拿来能用就是了!

  1. /*  $NetBSD: getopt.h,v 1.7 2005/02/03 04:39:32 perry Exp $ */  
  2.   
  3. /*- 
  4.  * Copyright (c) 2000 The NetBSD Foundation, Inc. 
  5.  * All rights reserved. 
  6.  * 
  7.  * This code is derived from software contributed to The NetBSD Foundation 
  8.  * by Dieter Baron and Thomas Klausner. 
  9.  * 
  10.  * Redistribution and use in source and binary forms, with or without 
  11.  * modification, are permitted provided that the following conditions 
  12.  * are met: 
  13.  * 1. Redistributions of source code must retain the above copyright 
  14.  *    notice, this list of conditions and the following disclaimer. 
  15.  * 2. Redistributions in binary form must reproduce the above copyright 
  16.  *    notice, this list of conditions and the following disclaimer in the 
  17.  *    documentation and/or other materials provided with the distribution. 
  18.  * 3. All advertising materials mentioning features or use of this software 
  19.  *    must display the following acknowledgement: 
  20.  *        This product includes software developed by the NetBSD 
  21.  *        Foundation, Inc. and its contributors. 
  22.  * 4. Neither the name of The NetBSD Foundation nor the names of its 
  23.  *    contributors may be used to endorse or promote products derived 
  24.  *    from this software without specific prior written permission. 
  25.  * 
  26.  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 
  27.  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  28.  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  29.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
  30.  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  31.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  32.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  33.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  34.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  35.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  36.  * POSSIBILITY OF SUCH DAMAGE. 
  37.  */  
  38.   
  39. /* 
  40.  * modified May 12, 2005 by Jim Basney <jbasney@ncsa.uiuc.edu> 
  41.  * 
  42.  * removed #include of non-POSIX <sys/cdefs.h> and <sys/featuretest.h> 
  43.  * removed references to _NETBSD_SOURCE and HAVE_NBTOOL_CONFIG_H 
  44.  * added #if !HAVE_GETOPT_LONG 
  45.  * removed __BEGIN_DECLS and __END_DECLS 
  46.  */  
  47.   
  48. #ifndef _MYPROXY_GETOPT_H_  
  49. #define _MYPROXY_GETOPT_H_  
  50.   
  51. #if !HAVE_GETOPT_LONG  
  52.   
  53. #include <unistd.h>  
  54.   
  55. /* 
  56.  * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions 
  57.  */  
  58. #define no_argument        0  
  59. #define required_argument  1  
  60. #define optional_argument  2  
  61.   
  62. extern char *optarg;  
  63. extern int optind;  
  64. extern int optopt;  
  65. extern int opterr;  
  66.   
  67. struct option {  
  68.     /* name of long option */  
  69.     const char *name;  
  70.     /* 
  71.      * one of no_argument, required_argument, and optional_argument: 
  72.      * whether option takes an argument 
  73.      */  
  74.     int has_arg;  
  75.     /* if not NULL, set *flag to val when option found */  
  76.     int *flag;  
  77.     /* if flag not NULL, value to set *flag to; else return value */  
  78.     int val;  
  79. };  
  80.   
  81. int getopt_long(int, char * const *, const char *,  
  82.     const struct option *, int *);  
  83.    
  84. #endif /* !HAVE_GETOPT_LONG */  
  85.   
  86. #endif /* !_MYPROXY_GETOPT_H_ */  

  1. /*  $NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $ */  
  2.   
  3. /*- 
  4.  * Copyright (c) 2000 The NetBSD Foundation, Inc. 
  5.  * All rights reserved. 
  6.  * 
  7.  * This code is derived from software contributed to The NetBSD Foundation 
  8.  * by Dieter Baron and Thomas Klausner. 
  9.  * 
  10.  * Redistribution and use in source and binary forms, with or without 
  11.  * modification, are permitted provided that the following conditions 
  12.  * are met: 
  13.  * 1. Redistributions of source code must retain the above copyright 
  14.  *    notice, this list of conditions and the following disclaimer. 
  15.  * 2. Redistributions in binary form must reproduce the above copyright 
  16.  *    notice, this list of conditions and the following disclaimer in the 
  17.  *    documentation and/or other materials provided with the distribution. 
  18.  * 3. All advertising materials mentioning features or use of this software 
  19.  *    must display the following acknowledgement: 
  20.  *        This product includes software developed by the NetBSD 
  21.  *        Foundation, Inc. and its contributors. 
  22.  * 4. Neither the name of The NetBSD Foundation nor the names of its 
  23.  *    contributors may be used to endorse or promote products derived 
  24.  *    from this software without specific prior written permission. 
  25.  * 
  26.  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 
  27.  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  28.  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  29.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
  30.  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  31.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  32.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  33.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  34.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  35.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  36.  * POSSIBILITY OF SUCH DAMAGE. 
  37.  */  
  38.   
  39. /* 
  40.  * modified May 12, 2005 by Jim Basney <jbasney@ncsa.uiuc.edu> 
  41.  * 
  42.  * removed #include of non-POSIX <sys/cdefs.h> <err.h> 
  43.  * removed #include of "namespace.h" 
  44.  * use local "port_getopt.h" instead of <getopt.h> 
  45.  * removed REPLACE_GETOPT and HAVE_NBTOOL_CONFIG_H sections 
  46.  * removed __P() from function declarations 
  47.  * use ANSI C function parameter lists 
  48.  * removed optreset support 
  49.  * replace _DIAGASSERT() with assert() 
  50.  * replace non-POSIX warnx(...) with fprintf(stderr, ...) 
  51.  * added extern declarations for optarg, optind, opterr, and optopt 
  52.  */  
  53.   
  54. #if defined(LIBC_SCCS) && !defined(lint)  
  55. __RCSID("$NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $");  
  56. #endif /* LIBC_SCCS and not lint */  
  57.   
  58. #include <assert.h>  
  59. #include <errno.h>  
  60. #include "getopt.h"  
  61. #include <stdio.h>  
  62. #include <stdlib.h>  
  63. #include <string.h>  
  64.   
  65. #ifdef __weak_alias  
  66. __weak_alias(getopt_long,_getopt_long)  
  67. #endif  
  68.   
  69. #if !HAVE_GETOPT_LONG  
  70. #define IGNORE_FIRST    (*options == '-' || *options == '+')  
  71. #define PRINT_ERROR ((opterr) && ((*options != ':') /  
  72.                       || (IGNORE_FIRST && options[1] != ':')))  
  73. #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)  
  74. #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)  
  75. /* XXX: GNU ignores PC if *options == '-' */  
  76. #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')  
  77.   
  78. /* return values */  
  79. #define BADCH   (int)'?'  
  80. #define BADARG      ((IGNORE_FIRST && options[1] == ':') /  
  81.              || (*options == ':') ? (int)':' : (int)'?')  
  82. #define INORDER (int)1  
  83.   
  84. #define EMSG    ""  
  85.   
  86. extern char *optarg;  
  87. extern int optind, opterr, optopt;  
  88.   
  89. static int getopt_internal (int, char * const *, const char *);  
  90. static int gcd (int, int);  
  91. static void permute_args (int, int, int, char * const *);  
  92.   
  93. static char *place = EMSG; /* option letter processing */  
  94.   
  95. static int nonopt_start = -1; /* first non option argument (for permute) */  
  96. static int nonopt_end = -1;   /* first option after non options (for permute) */  
  97.   
  98. /* Error messages */  
  99. static const char recargchar[] = "option requires an argument -- %c";  
  100. static const char recargstring[] = "option requires an argument -- %s";  
  101. static const char ambig[] = "ambiguous option -- %.*s";  
  102. static const char noarg[] = "option doesn't take an argument -- %.*s";  
  103. static const char illoptchar[] = "unknown option -- %c";  
  104. static const char illoptstring[] = "unknown option -- %s";  
  105.   
  106.   
  107. /* 
  108.  * Compute the greatest common divisor of a and b. 
  109.  */  
  110. static int  
  111. gcd(int a, int b)  
  112. {  
  113.     int c;  
  114.   
  115.     c = a % b;  
  116.     while (c != 0) {  
  117.         a = b;  
  118.         b = c;  
  119.         c = a % b;  
  120.     }  
  121.          
  122.     return b;  
  123. }  
  124.   
  125. /* 
  126.  * Exchange the block from nonopt_start to nonopt_end with the block 
  127.  * from nonopt_end to opt_end (keeping the same order of arguments 
  128.  * in each block). 
  129.  */  
  130. static void  
  131. permute_args(int panonopt_start, int panonopt_end, int opt_end,  
  132.          char * const *nargv)  
  133. {  
  134.     int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;  
  135.     char *swap;  
  136.   
  137.     assert(nargv != NULL);  
  138.   
  139.     /* 
  140.      * compute lengths of blocks and number and size of cycles 
  141.      */  
  142.     nnonopts = panonopt_end - panonopt_start;  
  143.     nopts = opt_end - panonopt_end;  
  144.     ncycle = gcd(nnonopts, nopts);  
  145.     cyclelen = (opt_end - panonopt_start) / ncycle;  
  146.   
  147.     for (i = 0; i < ncycle; i++) {  
  148.         cstart = panonopt_end+i;  
  149.         pos = cstart;  
  150.         for (j = 0; j < cyclelen; j++) {  
  151.             if (pos >= panonopt_end)  
  152.                 pos -= nnonopts;  
  153.             else  
  154.                 pos += nopts;  
  155.             swap = nargv[pos];  
  156.             /* LINTED const cast */  
  157.             ((char **) nargv)[pos] = nargv[cstart];  
  158.             /* LINTED const cast */  
  159.             ((char **)nargv)[cstart] = swap;  
  160.         }  
  161.     }  
  162. }  
  163.   
  164. /* 
  165.  * getopt_internal -- 
  166.  *  Parse argc/argv argument vector.  Called by user level routines. 
  167.  *  Returns -2 if -- is found (can be long option or end of options marker). 
  168.  */  
  169. static int  
  170. getopt_internal(int nargc, char * const *nargv, const char *options)  
  171. {  
  172.     char *oli;              /* option letter list index */  
  173.     int optchar;  
  174.   
  175.     assert(nargv != NULL);  
  176.     assert(options != NULL);  
  177.   
  178.     optarg = NULL;  
  179.   
  180.     /* 
  181.      * XXX Some programs (like rsyncd) expect to be able to 
  182.      * XXX re-initialize optind to 0 and have getopt_long(3) 
  183.      * XXX properly function again.  Work around this braindamage. 
  184.      */  
  185.     if (optind == 0)  
  186.         optind = 1;  
  187.   
  188. start:  
  189.     if (!*place) {                      /* update scanning pointer */  
  190.         if (optind >= nargc) {          /* end of argument vector */  
  191.             place = EMSG;  
  192.             if (nonopt_end != -1) {  
  193.                 /* do permutation, if we have to */  
  194.                 permute_args(nonopt_start, nonopt_end,  
  195.                     optind, nargv);  
  196.                 optind -= nonopt_end - nonopt_start;  
  197.             }  
  198.             else if (nonopt_start != -1) {  
  199.                 /* 
  200.                  * If we skipped non-options, set optind 
  201.                  * to the first of them. 
  202.                  */  
  203.                 optind = nonopt_start;  
  204.             }  
  205.             nonopt_start = nonopt_end = -1;  
  206.             return -1;  
  207.         }  
  208.         if ((*(place = nargv[optind]) != '-')  
  209.             || (place[1] == '/0')) {    /* found non-option */  
  210.             place = EMSG;  
  211.             if (IN_ORDER) {  
  212.                 /* 
  213.                  * GNU extension:  
  214.                  * return non-option as argument to option 1 
  215.                  */  
  216.                 optarg = nargv[optind++];  
  217.                 return INORDER;  
  218.             }  
  219.             if (!PERMUTE) {  
  220.                 /* 
  221.                  * if no permutation wanted, stop parsing 
  222.                  * at first non-option 
  223.                  */  
  224.                 return -1;  
  225.             }  
  226.             /* do permutation */  
  227.             if (nonopt_start == -1)  
  228.                 nonopt_start = optind;  
  229.             else if (nonopt_end != -1) {  
  230.                 permute_args(nonopt_start, nonopt_end,  
  231.                     optind, nargv);  
  232.                 nonopt_start = optind -  
  233.                     (nonopt_end - nonopt_start);  
  234.                 nonopt_end = -1;  
  235.             }  
  236.             optind++;  
  237.             /* process next argument */  
  238.             goto start;  
  239.         }  
  240.         if (nonopt_start != -1 && nonopt_end == -1)  
  241.             nonopt_end = optind;  
  242.         if (place[1] && *++place == '-') {  /* found "--" */  
  243.             place++;  
  244.             return -2;  
  245.         }  
  246.     }  
  247.     if ((optchar = (int)*place++) == (int)':' ||  
  248.         (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {  
  249.         /* option letter unknown or ':' */  
  250.         if (!*place)  
  251.             ++optind;  
  252.         if (PRINT_ERROR)  
  253.             fprintf(stderr, illoptchar, optchar);  
  254.         optopt = optchar;  
  255.         return BADCH;  
  256.     }  
  257.     if (optchar == 'W' && oli[1] == ';') {      /* -W long-option */  
  258.         /* XXX: what if no long options provided (called by getopt)? */  
  259.         if (*place)   
  260.             return -2;  
  261.   
  262.         if (++optind >= nargc) { /* no arg */  
  263.             place = EMSG;  
  264.             if (PRINT_ERROR)  
  265.                 fprintf(stderr, recargchar, optchar);  
  266.             optopt = optchar;  
  267.             return BADARG;  
  268.         } else              /* white space */  
  269.             place = nargv[optind];  
  270.         /* 
  271.          * Handle -W arg the same as --arg (which causes getopt to 
  272.          * stop parsing). 
  273.          */  
  274.         return -2;  
  275.     }  
  276.     if (*++oli != ':') {            /* doesn't take argument */  
  277.         if (!*place)  
  278.             ++optind;  
  279.     } else {                /* takes (optional) argument */  
  280.         optarg = NULL;  
  281.         if (*place)         /* no white space */  
  282.             optarg = place;  
  283.         /* XXX: disable test for :: if PC? (GNU doesn't) */  
  284.         else if (oli[1] != ':') {   /* arg not optional */  
  285.             if (++optind >= nargc) { /* no arg */  
  286.                 place = EMSG;  
  287.                 if (PRINT_ERROR)  
  288.                     fprintf(stderr, recargchar, optchar);  
  289.                 optopt = optchar;  
  290.                 return BADARG;  
  291.             } else  
  292.                 optarg = nargv[optind];  
  293.         }  
  294.         place = EMSG;  
  295.         ++optind;  
  296.     }  
  297.     /* dump back option letter */  
  298.     return optchar;  
  299. }  
  300.   
  301. /* 
  302.  * getopt_long -- 
  303.  *  Parse argc/argv argument vector. 
  304.  */  
  305. int  
  306. getopt_long(int nargc, char * const *nargv, const char *options,  
  307.         const struct option *long_options, int *idx)  
  308. {  
  309.     int retval;  
  310.   
  311.     assert(nargv != NULL);  
  312.     assert(options != NULL);  
  313.     assert(long_options != NULL);  
  314.     /* idx may be NULL */  
  315.   
  316.     if ((retval = getopt_internal(nargc, nargv, options)) == -2) {  
  317.         char *current_argv, *has_equal;  
  318.         size_t current_argv_len;  
  319.         int i, match;  
  320.   
  321.         current_argv = place;  
  322.         match = -1;  
  323.   
  324.         optind++;  
  325.         place = EMSG;  
  326.   
  327.         if (*current_argv == '/0') {        /* found "--" */  
  328.             /* 
  329.              * We found an option (--), so if we skipped 
  330.              * non-options, we have to permute. 
  331.              */  
  332.             if (nonopt_end != -1) {  
  333.                 permute_args(nonopt_start, nonopt_end,  
  334.                     optind, nargv);  
  335.                 optind -= nonopt_end - nonopt_start;  
  336.             }  
  337.             nonopt_start = nonopt_end = -1;  
  338.             return -1;  
  339.         }  
  340.         if ((has_equal = strchr(current_argv, '=')) != NULL) {  
  341.             /* argument found (--option=arg) */  
  342.             current_argv_len = has_equal - current_argv;  
  343.             has_equal++;  
  344.         } else  
  345.             current_argv_len = strlen(current_argv);  
  346.           
  347.         for (i = 0; long_options[i].name; i++) {  
  348.             /* find matching long option */  
  349.             if (strncmp(current_argv, long_options[i].name,  
  350.                 current_argv_len))  
  351.                 continue;  
  352.   
  353.             if (strlen(long_options[i].name) ==  
  354.                 (unsigned)current_argv_len) {  
  355.                 /* exact match */  
  356.                 match = i;  
  357.                 break;  
  358.             }  
  359.             if (match == -1)        /* partial match */  
  360.                 match = i;  
  361.             else {  
  362.                 /* ambiguous abbreviation */  
  363.                 if (PRINT_ERROR)  
  364.                     fprintf(stderr, ambig, (int)current_argv_len,  
  365.                          current_argv);  
  366.                 optopt = 0;  
  367.                 return BADCH;  
  368.             }  
  369.         }  
  370.         if (match != -1) {          /* option found */  
  371.                 if (long_options[match].has_arg == no_argument  
  372.                 && has_equal) {  
  373.                 if (PRINT_ERROR)  
  374.                     fprintf(stderr, noarg, (int)current_argv_len,  
  375.                          current_argv);  
  376.                 /* 
  377.                  * XXX: GNU sets optopt to val regardless of 
  378.                  * flag 
  379.                  */  
  380.                 if (long_options[match].flag == NULL)  
  381.                     optopt = long_options[match].val;  
  382.                 else  
  383.                     optopt = 0;  
  384.                 return BADARG;  
  385.             }  
  386.             if (long_options[match].has_arg == required_argument ||  
  387.                 long_options[match].has_arg == optional_argument) {  
  388.                 if (has_equal)  
  389.                     optarg = has_equal;  
  390.                 else if (long_options[match].has_arg ==  
  391.                     required_argument) {  
  392.                     /* 
  393.                      * optional argument doesn't use 
  394.                      * next nargv 
  395.                      */  
  396.                     optarg = nargv[optind++];  
  397.                 }  
  398.             }  
  399.             if ((long_options[match].has_arg == required_argument)  
  400.                 && (optarg == NULL)) {  
  401.                 /* 
  402.                  * Missing argument; leading ':' 
  403.                  * indicates no error should be generated 
  404.                  */  
  405.                 if (PRINT_ERROR)  
  406.                     fprintf(stderr, recargstring, current_argv);  
  407.                 /* 
  408.                  * XXX: GNU sets optopt to val regardless 
  409.                  * of flag 
  410.                  */  
  411.                 if (long_options[match].flag == NULL)  
  412.                     optopt = long_options[match].val;  
  413.                 else  
  414.                     optopt = 0;  
  415.                 --optind;  
  416.                 return BADARG;  
  417.             }  
  418.         } else {            /* unknown option */  
  419.             if (PRINT_ERROR)  
  420.                 fprintf(stderr, illoptstring, current_argv);  
  421.             optopt = 0;  
  422.             return BADCH;  
  423.         }  
  424.         if (long_options[match].flag) {  
  425.             *long_options[match].flag = long_options[match].val;  
  426.             retval = 0;  
  427.         } else   
  428.             retval = long_options[match].val;  
  429.         if (idx)  
  430.             *idx = match;  
  431.     }  
  432.     return retval;  
  433. }  
  434. #endif /* !GETOPT_LONG */  

 

拿过来后,把他们放到与destroy_linux.c同一目录下,只需要把destroy_linux.c的头文件改一个地方,#include <getopt.h>改为#include “getopt.h”,就能够编译运行了。而且,这样改好后,不仅在Tru64上能运行,在Linux、SunOS上也能运行。

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include "getopt.h"  
  5.   
  6. void print_usage(const char *program_name) {  
  7.     printf("%s 1.0.0 (2010-06-13)/n", program_name);  
  8.     printf("This is a program decoding a BER encoded CDR file/n");  
  9.     printf("Usage: %s -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>]/n", program_name);  
  10.     printf("    -f --file       the CDR file to be decoded/n");  
  11.     printf("    -o --output     the output file in plain text format/n");  
  12.     printf("    -c --config     the description file of the CDR file, if not given, use default configuration/n");  
  13.     printf("    -k --keyword    the keyword to search, if not given, all records will be written into output file/n");  
  14. }  
  15.   
  16. int main(int argc, char *argv[]) {  
  17.     char *file_name = NULL;  
  18.     char *output_name = NULL;  
  19.     char *config_name = NULL;  
  20.     char *keyword = NULL;  
  21.   
  22.     const char *short_opts = "hf:o:c:k:";  
  23.     const struct option long_opts[] = {  
  24.         {"help", no_argument, NULL, 'h'},  
  25.         {"file", required_argument, NULL, 'f'},  
  26.         {"output", required_argument, NULL, 'o'},  
  27.         {"config", required_argument, NULL, 'c'},  
  28.         {"keyword", required_argument, NULL, 'k'},  
  29.         {0, 0, 0, 0}  
  30.     };  
  31.     int hflag = 0;  
  32.   
  33.     int c;  
  34.     opterr = 0;  
  35.   
  36.     while ( (c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1 ) {  
  37.         switch ( c ) {  
  38.             case 'h' :  
  39.                 hflag = 1;  
  40.                 break;  
  41.             case 'f' :  
  42.                 file_name = optarg;  
  43.                 break;  
  44.             case 'o' :  
  45.                 output_name = optarg;  
  46.                 break;  
  47.             case 'c' :  
  48.                 config_name = optarg;  
  49.                 break;  
  50.             case 'k' :  
  51.                 keyword = optarg;  
  52.                 break;  
  53.             case '?' :  
  54.                 if ( optopt == 'f' || optopt == 'o' || optopt == 'c' || optopt == 'k' )  
  55.                     printf("Error: option -%c requires an argument/n", optopt);  
  56.                 else if ( isprint(optopt) )  
  57.                     printf("Error: unknown option '-%c'/n", optopt);  
  58.                 else  
  59.                     printf("Error: unknown option character '//x%x'/n", optopt);  
  60.                 return 1;  
  61.             default :  
  62.                 abort();  
  63.         }  
  64.     }  
  65.   
  66.     if ( hflag || argc == 1 ) {  
  67.         print_usage(argv[0]);  
  68.         return 0;  
  69.     }  
  70.     if ( !file_name ) {  
  71.         printf("Error: file name must be specified/n");  
  72.         return 1;  
  73.     }  
  74.     if ( !output_name ) {  
  75.         printf("Error: output name must be specified/n");  
  76.         return 1;  
  77.     }  
  78.   
  79.     // if not setting default, Linux OK, but SunOS core dump  
  80.     if ( !config_name ) config_name = "(null)";  
  81.     if ( !keyword ) keyword = "(null)";  
  82.     printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);  
  83.     return 0;  
  84. }  

 

Linux下编译

-bash-3.2$ gcc -o destroy destroy.c getopt_long.c

短格式,全部输入

-bash-3.2$ ./destroy -f aaa -o aaa.txt -c ccc -k 222

Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222

前两个长格式,后两个短格式

-bash-3.2$ ./destroy --file aaa --output aaa.txt -c ccc -k 222

Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222

漏掉一个必须输入的参数会报错

-bash-3.2$ ./destroy -output aaa.txt

Error: file name must be specified

次序随意,长短混用

-bash-3.2$ ./destroy -c ccc -o aaa.txt -k 222 --file aaa

Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222

 

题外话,#include <filename.h>与#include “filename.h”有什么区别,是面试C程序员经常问到的一个问题。答案大家都知道了,#include <filename.h>,编译器从标准库路径搜索filename.h,而#include “filename.h”,编译器从用户的工作路径搜索filename.h。

 

此外,网上也有人说从glibc(http://sourceware.org/glibc/)上把getopt.h、getopt.c和getoptl.c拿过来也能够用。我也试过,但是不清楚什么原因不成功。

 

在这个小实验的过程中,还发现了C语言在各个OS下的一些细小差异,比如destroy.c里,79行到82行:

// if not setting default, Linux OK, but SunOS core dump

if ( !config_name ) config_name = "(null)";

if ( !keyword ) keyword = "(null)";

printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);

 

如果在81行和82行不设置空指针的默认值,Linux和Tru64都会自动帮你转换而避免运行时错误,但是SunOS不会,它会死给你看。

./destroy -f aaa -o aaa.txt

Segmentation Fault (core dumped)

 

再比如第62行的abort()在头文件stdlib.h中定义,如果不包含此文件,SunOS与Tru64编译都没问题,Linux编译时会警告:

warning: incompatible implicit declaration of built-in function abort

 

由此看来,虽然C也公认是可移植性比较好的语言,但是在跨平台的项目中,也应该注意这些微小的差别。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值