使用getopt_long()從命令列獲取引數,struct option

致守發表於2017-10-18

函式說明

#include
函式說明:
函式getopt用來解析命令列引數。
函式getopt_long支援長選項的命令列解析。

函式原型:
intgetopt_long(int argc, char* constargv[], <wbr>
                     const char*optstring,<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
                     const struct option*longopts, <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
                     int*longindex);<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
引數:
argc、argv直接從main函式中獲取。
opting是選項引數組成的字串,由下列元素組成:
1.單個字元,表示選項,
2.單個字元後接一個冒號:表示該選項後必須跟一個引數。引數緊跟在選項後或者以空格隔開。該引數的指標賦給optarg。
3.單個字元後跟兩個冒號,表示該選項後可以有引數也可以沒有引數。如果有引數,引數必須緊跟在選項後不能以空格隔開。該引數的指標賦給optarg。(這個特性是GNU的擴張)。
optstring是一個字串,表示可以接受的引數。例如,"a:b:cd",表示可以接受的引數是a,b,c,d,其中,a和b引數後面跟有更多的引數值。(例如:-ahost -b name)
longopts是一個結構的例項
structoption
{
   constchar *name; //name表示的是長引數名<wbr><wbr>
   inthas_arg; //has_arg有3個值,no_argument(或者是0),表示該引數後面不跟引數值<wbr><wbr>
              //required_argument(或者是1),表示該引數後面一定要跟個引數值<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
              //optional_argument(或者是2),表示該引數後面可以跟,也可以不跟引數值<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
   int*flag;   <wbr><wbr><wbr><wbr>//用來決定getopt_long()的返回值是什麼。
              //flag是null,則函式會返回與該項option匹配的val值。<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
   int val;     //和flag聯合決定返回值<wbr><wbr><wbr><wbr><wbr>
 };<wbr>
int *flag 如果這個指標為NULL,那麼getopt_long()返回該結構val欄位中的數值。如果該指標不NULL,getopt_long()會使得它所指向的變數中填入val欄位中的數值,並且getopt_long()返回0。如果flag不是NULL,但未發現長選項,那麼它所指向的變數的數值不變。<wbr>
int val 這個值是發現了長選項時的返回值,或者flag不是NULL時載入*flag中的值。典型情況下,若flag不是NULL,那麼val是個真/假值,譬如1或0;另一方面,如果flag是NULL,那麼val通常是字元常量,若長選項與短選項一致,那麼該字元常量應該與optstring中出現的這個選項的引數相同。
=========================================================================
給個例子:
struct option long_options[] ={
          {"a123", required_argument,0, 'a'},<wbr><wbr><wbr><wbr><wbr><wbr>
          {"c123", no_argument, 0,'c'},<wbr><wbr><wbr><wbr><wbr><wbr>
};
現在,如果命令列的引數是-a123,那麼呼叫getopt_long()將返回字元'a',並且將字串123由optarg返回(注意注意!字串123由optarg帶回!optarg不需要定義,在getopt.h中已經有定義)。
那麼,如果命令列引數是-c,那麼呼叫getopt_long()將返回字元'c',而此時,optarg是null。
最後,當getopt_long()將命令列所有引數全部解析完成後,返回-1。
===========================================================================
以上來自網路,便於學習記憶摘錄在這裡,下面是自己的理解:
函式原型:int getopt_long(intargc, char* const argv[], <wbr>
                     const char*optstring,<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
                     const struct option*longopts, <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
                     int*longindex);<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
其中optstring為單個字元引數,稱為short_opts。
而longopts為多個字元(即一個或多個單詞連線)引數,稱為long_opts。
引數longindex為longopts陣列中的索引返回值。
具體用法參考suricata中main()函式中解析命令列引數:
//短字元引數
charshort_opts[] = "c:TDhi:l:q:d:r:us:S:U:VF:";

//長字元引數
 structoption long_opts[] = {<wbr>
       {"dump-config", 0, &dump_config, 1},  // getopt_long返回值為0,dump_config儲存為1<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
       {"pfring", optional_argument, 0, 0},  // getopt_long返回值為0<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
       {"pfring-int", required_argument, 0, 0}, // getopt_long返回值為0,必須有引數<wbr><wbr><wbr><wbr><wbr><wbr>
       {"pfring-cluster-id", required_argument, 0,0},<wbr><wbr><wbr><wbr>
       {"pfring-cluster-type", required_argument, 0,0},<wbr><wbr><wbr><wbr>
       {"af-packet", optional_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"pcap", optional_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"pcap-buffer-size", required_argument, 0,0},<wbr><wbr><wbr><wbr>
       {"unittest-filter", required_argument, 0,'U'},// <wbr>getopt_long返回值為‘U’,必須有引數<wbr><wbr><wbr><wbr>
       {"list-app-layer-protos", 0,&list_app_layer_protocols, 1},<wbr><wbr><wbr><wbr>
       {"list-unittests", 0, &list_unittests,1},<wbr><wbr><wbr><wbr>
       {"list-cuda-cards", 0, &list_cuda_cards,1},<wbr><wbr><wbr><wbr>
       {"list-runmodes", 0, &list_runmodes,1},<wbr><wbr><wbr><wbr>
       {"list-keywords", optional_argument,&list_keywords, 1},<wbr><wbr><wbr><wbr>
       {"runmode", required_argument, NULL, 0},<wbr><wbr><wbr><wbr>
       {"engine-analysis", 0, &engine_analysis,1},<wbr><wbr><wbr><wbr>
       {"pidfile", required_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"init-errors-fatal", 0, 0, 0},<wbr><wbr><wbr><wbr>
       {"fatal-unittests", 0, 0, 0},<wbr><wbr><wbr><wbr>
       {"user", required_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"group", required_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"erf-in", required_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"dag", required_argument, 0, 0},<wbr><wbr><wbr><wbr>
       {"napatech", 0, 0, 0},<wbr><wbr><wbr><wbr>
       {"build-info", 0, &build_info, 1},<wbr><wbr><wbr><wbr>
       {NULL, 0, NULL, 0}<wbr><wbr><wbr><wbr>
    };<wbr><wbr>
   // 長字元陣列索引<wbr><wbr>
    intoption_index = 0;<wbr><wbr><wbr>
 <wbr>
 // 以下是用法 <wbr><wbr>
 while ((opt= getopt_long(argc, argv, short_opts, long_opts,&option_index)) != -1) <wbr><wbr>
 {<wbr>
       switch (opt) <wbr><wbr><wbr><wbr><wbr>
       {      // case為函式返回值<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
        case 0:<wbr><wbr><wbr><wbr><wbr>
          if(strcmp((long_opts[option_index]).name , "pfring") == 0 ||                                 strcmp((long_opts[option_index]).name , "pfring-int") ==0) <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
          {<wbr><wbr><wbr><wbr><wbr><wbr>
            // TO-DO...<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
          }<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
           else if(strcmp((long_opts[option_index]).name , "pcap") ==0) <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
          {<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
            // TO-DO...<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
          }<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
          break;<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
        case 'c':<wbr><wbr><wbr><wbr><wbr>
          break; <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
        case 'T':<wbr><wbr><wbr><wbr><wbr>
          break;<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
        default:<wbr><wbr><wbr><wbr><wbr>
          usage(argv[0]); <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>
          exit(EXIT_FAILURE);<wbr><wbr><wbr><wbr><wbr><wbr><wbr>
       }<wbr><wbr><wbr><wbr>
 }<wbr>

函式使用

眾所周知,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(intchar * 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 (intchar * const *, const char *);  
  90. static int gcd (intint);  
  91. static void permute_args (intintintchar * 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也公認是可移植性比較好的語言,但是在跨平臺的專案中,也應該注意這些微小的差別。

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

相關文章