libpopt的使用(譯)


更新日志:
2012.02.27 更新(校正並基本完成選項表部分內容的翻譯)
2012.02.28 更新(完成popt基本使用的第1部分以及第2部分的內容翻譯)
2012.03.01 更新(完成popt基本使用的全部翻譯——剩下的3、4、5部分,增加示例部分)

軟件安裝:
ubuntu (deb)

apt-cache search popt | head
libpopt-dev - lib  for parsing cmdline parameters - development files
libpopt0 - lib  for parsing cmdline parameters
...
sudo  apt-get install libpopt-dev

CentOS (rpm)

sudo  yum install popt-devel

本文譯自linux程序員指南(Linux Programmer's Manual)

man  3 popt

 

概要

#include <popt.h>
poptContext poptGetContext ( const  char  * name ,  int argc ,
                                   const  char  ** argv ,
                                   const  struct poptOption  * options ,
                                   int flags ) ;
void poptFreeContext (poptContext con ) ;
void poptResetContext (poptContext con ) ;
int poptGetNextOpt (poptContext con ) ;
const  char  * poptGetOptArg (poptContext con ) ;
const  char  * poptGetArg (poptContext con ) ;
const  char  * poptPeekArg (poptContext con ) ;
const  char  ** poptGetArgs (poptContext con ) ;
const  char  * const poptStrerror ( const  int error ) ;
const  char  * poptBadOption (poptContext con ,  int flags ) ;
int poptReadDefaultConfig (poptContext con ,  int flags ) ;
int poptReadConfigFile (poptContext con ,  char  * fn ) ;
int poptAddAlias (poptContext con ,  struct poptAlias alias ,
                         int flags ) ;
int poptParseArgvString ( char  * s ,  int  *  argcPtr ,
                                const  char  *** argvPtr ) ;
int poptDupArgv ( int argc ,  const  char  ** argv ,  int  * argcPtr ,
                                const  char  *** argvPtr ) ;
int poptStuffArgs (poptContext con ,  const  char  ** argv ) ;

描述

popt 庫的存在,主要是為了解析命令行選項。相比手動的解析argv數組或者使用getopt函數getopt()或getopt_long(),使用popt庫在很多方面都占有優勢。popt的一些特點:它不使用全局變量,因此可以並行解析argv;它可以解析任意的argv風格的元素數組,可以解析來自任何源文件的命令行字符串(ps:估計是命令行字符串參數可保存在文件中,程序通過導入配置文件解析獲得參數);它提供了一個使用選項別名的標准方法(后面會再討論);它可以執行外部選項過濾器;最后,它能夠自動生成程序的help和usage信息。

類似getopt_long(),popt庫支持短風格和長風格選項。一個短風格選項由一個連字符 ‘-’ 后面接一個字母或數字組成。在GNU實用程序(工具)中,通常的,一個長風格選項,由兩個連字符 ‘-’ 后面接一個由字母、數字或連字符組成的字符串。長選項可選的允許由一個連字符 ‘-’ 開頭, 主要是為了允許基於popt的程序與基於X toolkit(工具包)的程序命令行兼容。不管那種類型(短風格或長風格)選項,后面都跟隨一個參數。短選項及其參數由一個空格隔開,長選項及其參數由一個空格或者一個 = 號隔開。

popt庫是高度可移植的,而且將能夠工作於任何POSIX兼容的平台。最新的發布版本可從 ftp://ftp.rpm.org/pub/rpm/dist 獲得。

popt的基本使用

1. 選項表

應用程序通過“選項表”的方式為popt提供命令行選項信息,例如,一個 struct poptOption 的結構數組:

#include <popt.h>

struct poptOption  {
     const  char  * longName ;  /* may be NULL */
     char shortName ;         /* may be '\0' */
     int argInfo ;
     void  * arg ;             /* depends on argInfo */
     int val ;                /* 0 means don't return, just update flag */
     char  * descrip ;         /* description for autohelp -- may be NULL */
     char  * argDescrip ;      /* argument description for autohelp */
} ;

(為表達方便,下文用選項域指代struct poptOption的每一個成員)
選項表(struct poptOption 結構數組)的每一個成員(struct poptOption 結構體)定義了一個將要傳給應用程序的單一選項,長選項或短選項視為一個單一選項可能出現兩種不同形式。結構體(struct poptOption)最前面的兩個成員,longName 和 shortName,定義了選項的名字,前者是長名字,后者是一個單一字符。

選項域 argInfo 指定了 popt 選項后面期待的參數類型,如果選項后面沒有參數, 宏 POPT_ARG_NONE 將被使用,其他的有效值如下表所示:

Value Description arg Type
POPT_ARG_NONE No argument expected int
POPT_ARG_STRING No type checking to be performed char *
POPT_ARG_ARGV No type checking to be performed char **
POPT_ARG_INT An integer argument is expected int
POPT_ARG_LONG A long integer is expected long
POPT_ARG_LONGLONG A long long integer is expected long long
POPT_ARG_VAL Integer value taken from val int
POPT_ARG_FLOAT An float argument is expected float
POPT_ARG_DOUBLE A double argument is expected double

對於數值類型的變量,如果argInfo選項域的值與 POPT_ARGFLAG_OR, POPT_ARGFLAG_AND 或 POPT_ARGFLAG_XOR 按位相或,保存的值將是進行 OR(或), AND(與), XOR(異或)操作之后的值。如果argInfo選項域的值與 POPT_ARGFLAG_NOT 按位相或,保存的值將是取反后的值。對於通常的置位(setting bits)和復位(clearing bits)操作,可以設置 POPT_BIT_SET 或 POPT_BIT_CLR 標志來執行位操作。

如果argInfo選項域的值與 POPT_ARGFLAG_ONEDASH 按位相或,長參數將可以替換兩個'-'為單個'-'(即只使用單個連字符'-'),例如,如果 --longopt 是一個設置了 POPT_ARGFLAG_ONEDASH 標志的選項,那么 -longopt 也同樣被接受。

下一個選項域 arg ,如果使用(即arg不為NULL),則允許 popt 自動更新程序參數。如果 arg 為 NULL,那么它將被忽略,popt不執行任何額外的操作,否則,它將指向一個上述表格最右列所指示類型的變量。POPT_ARG_ARGV 類型參數將(再)分配一個 char * 字符串指針數組添加字符串參數,並以NULL作為字符數組的結束。POPT_ARG_ARGV 參數的地址 char ** 應初始化為 NULL。

如果選項不帶參數(argInfo 選項域的值為 POPT_ARG_NONE,值得注意的是,POPT_ARG_NONE 的值為 0),則選項被啟用時,由 arg 指向的變量被設置為 1 。如果選項帶有參數(argInfo 不為 POPT_ARG_NONE),選項被啟用時,arg 指向的變量將被更新為參數的值。 POPT_ARG_STRING 和 POPT_ARG_ARGV 類型的參數可接受任意的字符串,但 POPT_ARG_INT, POPT_ARG_LONG, POPT_ARG_LONGLONG, POPT_ARG_FLOAT 和 POPT_ARG_DOUBLE 類型參數將會做適當的轉換,如果轉換失敗,則返回錯誤。

選項信息(argInfo)中如果指定了 POPT_ARG_VAL, 選項域 arg 將被設置為域 val 的值。這通常在允許多個互斥參數同時出現,而你希望最后指定的參數有效的情況下有用。例如, “rm -i -f”。 POPT_ARG_VAL 導致語法分析函數不返回值,因為已經使用了選項域 val 的值。

如果選項域 argInfo 的值與 POPT_ARGFLAG_OPTIONAL 按位或,則該選項的長選項的參數可以省略,如果該長選項不帶參數,則保存一個默認值0或NULL(選項域 arg 不為空),否則,等同於帶參數的長選項。

下一個選項域 val,是popt語法分析函數在遇到該選項時將要返回的值。如果該選項域為0,語法分析函數將不返回值,而繼續分析下一個命令行參數。

最后兩個選項域 descrip 和 argDescrip,僅僅在想得到自動幫助信息(automatic help messages)時才需要(生成自動用法信息(automatic usage messages)可以不需要它們)。descrip 是關於參數的文本描述,argdescrip 是關於選項參數的類型的簡短概要,如果選項不需要參數,則可設置為NULL。(descrip is a text description of the argument and argdescrip is a short summary of the type of arguments the option expects, or NULL if the option doesn't require any arguments.)

如果 popt 需要自動提供 --usage 和 --help (-?) 選項,選項表中應該有一行是宏POPT_AUTOHELP,這個宏在主選項表中包含進另外一個選項表(通過 POPT_ARG_INCLUDE_TABLE, 見下文)。當 --usage 或 --help 傳遞給使用了popt自動幫助的應用程序,popt發現該選項時將打印適當的信息(help message)到標准錯誤,然后退出程序執行並返回 0。如果你希望使用不同的方式生成popt的自動幫助,你需要顯式的添加選項條目到你的程序的選項表中,以取代使用 POPT_AUTOHELP。

如果選項域 argInfo 的值與 POPT_ARGFLAG_DOC_HIDDEN 按位或,該參數將不會在幫助信息輸出(help output)中顯示。

如果選項域 argInfo 的值與 POPT_ARGFLAG_SHOW_DEFAULT 按位或,該參數的初始值將顯示在幫助信息輸出(help output)中。

選項表中的最后的一個結構體(struct poptOption),所有的指針設置為NULL,所有的算術值設置為0,以標記選項表的結束。宏 POPT_TABLEEND 提供了這個實現。

有兩種類型的選項表項(entry)不指定命令行選項,不管是其中的哪一種類型的項,選項域 longName 必須為NULL,且 shortName 必須為 '\0'。

這兩種表項類型中第一種類型允許程序在當前的選項表中嵌套另外一個選項表,這種嵌套可以擴展的很深(實際的深度為程序的棧大小所限制)。包含進其他選項表,則允許一個庫(library)為任何使用它的程序提供一個標准的命令行選項集合(例如,這通常在圖形程序設計工具包中實現)。為實現這個功能,需要設置選項域 argInfo 為 POPT_ARG_INCLUDE_TABLE,而選項域 arg 則指向了將要被包含進來的選項表。如果使用了自動生成幫助信息的功能,則選項域 descrip 需要包含將要被包含進來的選項表的所有描述信息。

另外的一種特別的選項表項類型則告訴popt在遇到該選項表中的選項時調用一個函數(一個回調),這在包含一個正在使用的選項表時尤其有用,因為提供頂級(top-level)選項表的程序不需要知道包含進來的選項表所提供的選項。如果為選項表設置了回調函數,語法分析函數則不返回選項表中選項的信息,而是通過回調函數保存選項信息或讓popt通過選項域 arg 設置一個變量。選項回調函數應匹配下面的原型:

void poptCallbackType (poptContext con ,
                       const  struct poptOption  * opt ,
                       const  char  * arg ,  void  * data ) ;

第一個參數是將要進行分析的上下文(context)(請查看下一節關於上下文的信息),opt 指向觸發該回調函數的選項, arg 是選項的參數,如果該選項不帶參數,則 arg 為NULL,最后的一個參數 data 從定義該回調函數的選項的選項域 decript 獲取,這允許給回調函數傳入一個任意的數據集合(盡管要使用類型轉換)

定義回調函數的選項表項,選項域 argInfo 值為 POPT_ARG_CALLBACK, 選項域 arg 則指向回調函數,而選項域 descrip 則指定一個將要傳給回調函數的任意指針。

2. 創建一個上下文

popt 可以交錯分析多個命令行選項集合,這通過為每一個特定的命令行參數集合保存其所有的狀態信息到一個 poptContex 數據結構來實現的,poptContex 數據結構是一個不透明類型(opaque type),不能在popt庫之外修改。(即實現了信息隱藏,接口與數據分離)
可以通過 poptGetContext() 函數創建一個 popt 上下文:

poptContext poptGetContext ( const  char  * name ,  int argc ,
                            const  char  ** argv ,
                            const  struct poptOption  * options ,
                            int flags ) ;

第一個參數 name 僅僅用作別名處理,應該設置為程序(其選項正在被分析)的名稱或者為NULL(如果不需要選項別名)。往后的兩個參數指定了將要分析的命令行參數,傳給poptGetContext()的它們通常等同於傳給程序的main()函數的參數。options 參數指向命令行選項表(上一節描述)。最后的一個參數 flag, 可以從以下的三個值中選擇:

Value Description
POPT_CONTEXT_NO_EXEC Ignore exec expansions
POPT_CONTEXT_KEEP_FIRST Do not ignore argv[0]
POPT_CONTEXT_POSIXMEHARDER Options cannot follow arguments

一個 poptContex 跟蹤記錄了哪些選項已經被分析和哪些選項仍未分析。如果程序希望重新開始一個參數集合的處理過程,可以通過把該上下文(contex)作為唯一的參數傳給poptResetContext().

如果參數處理完成了,進程應該釋放 poptContex, 因為它包含了動態分配的組成部分。poptFreeContext() 函數使用 poptContext 作為唯一的參數釋放該上下文所使用的資源。

下面是poptResetContext()和poptFreeContext()的原型:

#include <popt.h>
void poptFreeContext (poptContext con ) ;
void poptResetContext (poptContext con ) ;

3. 分析命令行

應用程序創建一個 poptContext 之后,就將開始分析參數,由poptGetNextOpt()函數完成實際的參數分析。

#include <popt.h>
int poptGetNextOpt (poptContext con ) ;

使用contex作為唯一的參數,這個函數分析命令行上的參數。當找到選項表中的下一個參數,如果改選項的選項域arg指針不為空,則填寫arg指針所指向的對象。如果該選項的選項域val的值不為0,函數將返回該值,否則,poptGetNextOpt()繼續分析下一個參數。

當最后一個參數分析完成,poptGetNextOpt()返回-1,當出現錯誤時,返回其他的負值。因此,設置選項的選項域val的值大於0會是一個好的做法。

如果所有的命令行選項都是通過選項域arg指針(不為NULL)處理,命令行分析將簡化到如下的一行代碼:

rc  = poptGetNextOpt (poptcon ) ;

然而,許多應用程序需要比這個更復雜的命令行分析,則可以使用如下的語句結構:

while  ( (rc  = poptGetNextOpt (poptcon ) )  >  0 )  {
      switch  (rc )  {
          /* specific arguments are handled here */
      }
}

當選項處理返回,如果應用程序需要知道指定在選項之后的任意參數的值(即獲取選項的參數值),有兩種方法可以發現它們。一種方法是請求popt填寫該參數值到選項域arg指向的變量,另一種方法是使用poptGetOptArg()函數:

#include <popt.h>
char  * poptGetOptArg (poptContext con ) ;

這個函數返回提供給poptGetNextOpt()返回的最后一個選項的參數,或者返回NULL,如果沒有指定任何參數。調用函數負責釋放該字符串。(這段的翻譯有點糾結,個人理解即是返回選項的參數值,不論是通過arg指針還是上述的poptGetOptArg()函數,附原文)

When returned options are handled, the application needs to know the value of any arguments that were specified after the option. There are two ways to discover them. One is to ask popt to fill in a variable with the value of the option through the option table's arg elements. The other is to use poptGetOptArg():

#include <popt.h>char * poptGetOptArg(poptContext con);

This function returns the argument given for the final option returned by poptGetNextOpt(), or it returns NULL if no argument was specified. The calling function is responsible for deallocating this string.

4. 剩余參數(leftover arguments)

許多應用程序接受任意數量的命令行參數,例如一個文件名列表。當popt遇到一個不是以一個 - 開始的參數,它(popt)將認為它(參數)是個剩余參數(leftover argument),並把它(參數)添加到一個剩余參數列表。有三個函數允許應用程序訪問這些參數:

const  char  * poptGetArg (poptContext con ) ;

這個函數返回下一個剩余參數,並標記它已處理。

const  char  * poptPeekArg (poptContext con ) ;

返回下一個剩余參數,但不標記它已處理,這允許應用程序繼續讀取參數列表而不修改它。

const  char  ** poptGetArgs (poptContext con ) ;

所有的剩余參數以等同於argv的方式返回,返回數組的最后一個元素指向NULL,表明參數的結尾。

5. 自動幫助信息

popt庫能夠自動生成描述程序所接受的選項的信息。可以生成有兩種類型的幫助信息,用法(usage)信息是一個簡短的有效選項列表(沒有描述),幫助(help)信息是描述每一個選項的一行(或多行)文本,需要為每一個選項填寫的選項域descrip和argDescrip。
使用宏 POPT_AUTOHELP 可以很方便的添加 --usage 和 --help 信息到你的應用程序,關於POPT_AUTOHELP,本幫助文檔的第一部分已講述。如果需要更好的控制你的程序的幫助信息,可以使用下面的兩個函數:

#include <popt.h>
void poptPrintHelp (poptContext con , FILE  * f ,  int flags ) ;
void poptPrintUsage (poptContext con , FILE  * f ,  int flags ) ;

poptPrintHelp() 打印標准的幫助(help)信息到標准輸入輸出文件描述符 f,而 poptPrintUsage() 則打印簡短的用法信息。兩個函數當前都忽略 falgs 參數,該參數是為了以后擴展使用。

錯誤處理

。。。
(待續~ 任務還很艱巨)

EXAMPLE

下面的例子是程序"robin"的一個簡單版本。這個程序(Robin)已經裁剪掉除了參數分析邏輯部分外的所有東西,經過稍微的修改,並重命名為"parse"。
這在舉例說明功能強大的popt庫的一些特征將是十分有用的。

#include <popt.h>
#include <stdio.h>
#include <stdlib.h>

void usage (poptContext optCon ,  int exitcode ,  char  *error ,  char  *addl )  {
    poptPrintUsage (optCon , stderr ,  0 ) ;
     if  (error )  fprintf (stderr ,  "%s: %s\n" , error , addl ) ;
     exit (exitcode ) ;
}

int main ( int argc ,  char  *argv [ ] )  {
     int     c ;             /* used for argument parsing */
     int     i  =  0 ;         /* used for tracking options */
     int     speed  =  0 ;     /* used in argument parsing to set speed */
     int     raw  =  0 ;       /* raw mode? */
     int     j ;
     char    buf [BUFSIZ + 1 ] ;
     const  char  *portname ;
    poptContext optCon ;    /* context for parsing command-line options */

     struct poptOption optionsTable [ ]  =  {

     {  "bps" ,  'b' , POPT_ARG_INT ,  &speed ,  0 ,
                 "signaling rate in bits-per-second" ,  "BPS"  } ,
     {  "crnl" ,  'c' ,  0 ,  0 ,  'c' ,
         "expand cr characters to cr/lf sequences" , NULL  } ,
     {  "hwflow" ,  'h' ,  0 ,  0 ,  'h' ,
         "use hardware (RTS/CTS) flow control" , NULL  } ,
     {  "noflow" ,  'n' ,  0 ,  0 ,  'n' ,
         "use no flow control" , NULL  } ,
     {  "raw" ,  'r' ,  0 ,  &raw ,  0 ,
                 "don't perform any character conversions" , NULL  } ,
     {  "swflow" ,  's' ,  0 ,  0 ,  's' ,
         "use software (XON/XOF) flow control" , NULL  }  ,
    POPT_AUTOHELP
     { NULL ,  0 ,  0 , NULL ,  0  }
} ;

    optCon  = poptGetContext (NULL , argc ,  ( const  char  ** )argv , optionsTable ,  0 ) ;
    poptSetOtherOptionHelp (optCon ,  "[OPTIONS]* <port>" ) ;

     if  (argc  <  2 )  {
        poptPrintUsage (optCon , stderr ,  0 ) ;
         exit ( 1 ) ;
     }

     /* Now do options processing, get portname */
     while  ( (= poptGetNextOpt (optCon ) )  >=  0 )  {
         switch  (c )  {

         case  'c' :
            buf [i ++ ]  =  'c' ;
             break ;
         case  'h' :
            buf [i ++ ]  =  'h' ;
             break ;
         case  's' :
            buf [i ++ ]  =  's' ;
             break ;
         case  'n' :
            buf [i ++ ]  =  'n' ;
             break ;
         }
     }
    portname  = poptGetArg (optCon ) ;
     if ( (portname  == NULL )  ||  ! (poptPeekArg (optCon )  == NULL ) )
        usage (optCon ,  1 ,  "Specify a single port" ,  ".e.g., /dev/cua0" ) ;

     if  (<  - 1 )  {
         /* an error occurred during option processing */
         fprintf (stderr ,  "%s: %s\n" ,
                poptBadOption (optCon , POPT_BADOPTION_NOALIAS ) ,
                poptStrerror (c ) ) ;
         return  1 ;
     }

     /* Print out options, portname chosen */

     printf ( "Options  chosen: " ) ;
     for (=  0 ; j  < i  ; j ++ )
         printf ( "-%c " , buf [j ] ) ;
     if (raw )  printf ( "-r " ) ;
     if (speed )  printf ( "-b %d " , speed ) ;
     printf ( "\nPortname chosen: %s\n" , portname ) ;

    poptFreeContext (optCon ) ;
     exit ( 0 ) ;
}

保存代碼到文件 parse.c 並編譯:

jarson@ubuntu:~$  gcc  -Wall  -o parse parse.c  -lpopt

jarson@ubuntu:~$ . /parse 
Usage: parse  [-chnrs? ]  [-b |--bps=BPS ]  [-c |--crnl ]  [-h |--hwflow ]  [-n |--noflow ]  [-r |--raw ]
         [-s |--swflow ]  [-? |--help ]  [--usage ]  [OPTIONS ] *  <port >

jarson@ubuntu:~$ . /parse -?
Usage: parse  [OPTIONS ] *  <port >
  -b,  --bps=BPS     signaling rate  in bits-per-second
  -c,  --crnl        expand cr characters to cr /lf sequences
  -h,  --hwflow      use hardware  (RTS /CTS ) flow control
  -n,  --noflow      use no flow control
  -r,  --raw          do not perform any character conversions
  -s,  --swflow      use software  (XON /XOF ) flow control

Help options:
  -?,  --help        Show this  help message
       --usage       Display brief usage message

RPM, a popular Linux package management program, makes heavy use of popt's features.
Many of its command-line arguments are implemented through popt aliases, which makes
RPM an excellent example of how to take advantage of the popt library. For more
information on RPM, see http://www.rpm.org. The popt source code distribution
includes test program(s) which use all of the features of the popt libraries in vari‐
ous ways. If a feature isn't working for you, the popt test code is the first place
to look.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM