[PATCH] Implement support for command line argument parsing using <argp.h>

Sergio Durigan Junior sergiodj en sergiodj.net
Sab Mar 15 21:48:33 UTC 2014


On Saturday, March 15 2014, Thadeu Lima de Souza Cascardo wrote:

>> diff --git a/configure.ac b/configure.ac
>> index c7a85dc..e06ecdf 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -7,4 +7,5 @@ PKG_CHECK_MODULES(GNUTLS, gnutls >= 1.4.0, , AC_MSG_ERROR(Could not find gnutls)
>>  AM_PATH_LIBGCRYPT(,,AC_MSG_ERROR(Could not find gcrypt))
>>  LIBS="$LIBGCRYPT_LIBS $GNUTLS_LIBS $LIBS -lz"
>>  CFLAGS="$LIBGCRYPT_CFLAGS $GNUTLS_CFLAGS $CFLAGS"
>> +AC_CONFIG_HEADERS([config.h])
>>  AC_OUTPUT(Makefile)
>> diff --git a/rnetclient.c b/rnetclient.c
>> index d264c72..eb5a391 100644
>> --- a/rnetclient.c
>> +++ b/rnetclient.c
>> @@ -27,10 +27,81 @@
>>  #include <netdb.h>
>>  #include <gnutls/gnutls.h>
>>  #include <zlib.h>
>> +#include <argp.h>
>> +#include "config.h"
>>  #include "decfile.h"
>>  #include "rnet_message.h"
>>  #include "rnet_encode.h"
>>  
>> +/* Program version and bug report address.  */
>> +
>> +const char *argp_program_version = PACKAGE_VERSION;
>> +const char *argp_program_bug_address = PACKAGE_BUGREPORT;
>
> Esse bug report vai usar o meu endereço de email que está no configure.ac,
> correto? Melhor mudarmos para o endereço da lista.

Sim, correto.  Endereço alterado.

>> +
>> +/* Documentation strings.  */
>> +
>> +static const char rnetclient_doc[] =
>> +	"Send the Brazilian Income Tax Declaration to the Brazilian "
>> +	"Tax Authority";
>
> Eu prefiro Tax Report. Pra quem está procurando uma oportunidade de
> contribuir, já vamos precisar internacionalizar o rnetclient.

OK, coloquei Income Tax Report.

BTW, pra quem quiser internacionalizar, gettext na cabeça.

>> +static const char rnetclient_args_doc[] =
>> +	"[-d|--declaration] FILE";
>> +
>
> Será que não tem um nome de opção mais neutro entre as duas línguas? Ia
> sugerir input e output. report e receipt não vai funcionar muito bem.
> Talvez a gente tenha que deixar esse "declaration" mesmo.

Eu prefiro/preferi deixar declaration.  Acho que report e receipt não
ficam legais, mesmo.

>> +/* Description and definition of each option accepted by the program.  */
>> +
>> +static const struct argp_option rnetclient_options_desc[] = {
>> +	{ "declaration", 'd', "FILE", OPTION_ARG_OPTIONAL,
>> +	  "The Income Tax Declaration file that will be sent.",
>> +	  0 },
>> +
>> +	{ NULL },
>> +};
>> +
>> +struct rnetclient_args {
>> +	/* File representing the declaration.  */
>> +	char *file;
>> +};
>
> Sugiro já usar algo como input_file, pois a sequência lógica a esse
> patch é permitir passar o caminho do arquivo do recibo como parâmetro.

Feito.

>> +
>> +/* Parser for command line arguments.  */
>> +
>> +static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
>> +{
>> +	struct rnetclient_args *a = state->input;
>> +	switch (key) {
>> +	case 'd':
>> +		/* The user has explicitly provided a filename through
>> +		   the '-d' switch.  */
>> +		a->file = arg;
>> +		break;
>> +
>> +	case ARGP_KEY_ARG:
>> +		/* The user has possibly provided a filename without
>> +		   using any switches (e.g., by running './rnetclient
>> +		   file').  */
>> +		a->file = arg;
>> +		break;
>> +
>> +	case ARGP_KEY_END:
>> +		/* We have reached the end of the argument parsing.
>> +		   Let's check if the user has provided a filename.  */
>> +		if (arg == NULL && a->file == NULL)
>
> Quando arg não é NULL nesse caso? O que acontece?

Ops, falha minha, esse código era diferente mas eu esqueci de tirar essa
checagem.  Só verifico por a->input_file == NULL agora.

>> @@ -329,17 +394,17 @@ int main(int argc, char **argv)
>>  	int r;
>>  	struct rnet_decfile *decfile;
>>  	struct rnet_message *message = NULL;
>> +	struct rnetclient_args rnet_args;
>>  	gnutls_session_t session;
>>  	int finish = 0;
>>  	char *cpf;
>>  	
>> -	if (argc < 2) {
>> -		usage();
>> -	}
>> +	memset (&rnet_args, 0, sizeof (rnet_args));
>> +	argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
>
> argp_parse pode falhar por outra razão que não chame exit, e retorne um
> código de erro?

De acordo com a documentação da função, presente em /usr/include/argp.h:

  /* Parse the options strings in ARGC & ARGV according to the options in ARGP.
     FLAGS is one of the ARGP_ flags above.  If ARG_INDEX is non-NULL, the
     index in ARGV of the first unparsed option is returned in it.  If an
     unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser
     routine returned a non-zero value, it is returned; otherwise 0 is
     returned.  This function may also call exit unless the ARGP_NO_HELP flag
     is set.  INPUT is a pointer to a value to be passed in to the parser.  */
  extern error_t argp_parse (const struct argp *__restrict __argp,
                             int __argc, char **__restrict __argv,
                             unsigned __flags, int *__restrict __arg_index,
                             void *__restrict __input);

É possível, em casos isolados, que a função retorne algum código
diferente de zero caso o parser interno (definido por nós) retorne esse
código.  No entanto, do jeito que ele está feito hoje, esse cenário é
impossível.  Portanto, eu coloquei um check pra verificar isso.

> Note também o estilo do código: memset () vs memset().

Ouch, eu juro que me esforcei pra seguir, mas justo nessa eu escorreguei
:-).

Bem, vou mandar o v2 em seguida, já que esse tal de git dificulta o
patch review absurdamente!

-- 
Sergio


Más información sobre la lista de distribución Softwares-impostos