Blame carg_parser.c

Packit Bot a3ac83
/*  Arg_parser - POSIX/GNU command line argument parser. (C version)
Packit Bot a3ac83
    Copyright (C) 2006-2017 Antonio Diaz Diaz.
Packit Bot a3ac83
Packit Bot a3ac83
    This library is free software. Redistribution and use in source and
Packit Bot a3ac83
    binary forms, with or without modification, are permitted provided
Packit Bot a3ac83
    that the following conditions are met:
Packit Bot a3ac83
Packit Bot a3ac83
    1. Redistributions of source code must retain the above copyright
Packit Bot a3ac83
    notice, this list of conditions and the following disclaimer.
Packit Bot a3ac83
Packit Bot a3ac83
    2. Redistributions in binary form must reproduce the above copyright
Packit Bot a3ac83
    notice, this list of conditions and the following disclaimer in the
Packit Bot a3ac83
    documentation and/or other materials provided with the distribution.
Packit Bot a3ac83
Packit Bot a3ac83
    This library is distributed in the hope that it will be useful,
Packit Bot a3ac83
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot a3ac83
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Packit Bot a3ac83
*/
Packit Bot a3ac83
Packit Bot a3ac83
#include <stdlib.h>
Packit Bot a3ac83
#include <string.h>
Packit Bot a3ac83
Packit Bot a3ac83
#include "carg_parser.h"
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* assure at least a minimum size for buffer 'buf' */
Packit Bot a3ac83
static void * ap_resize_buffer( void * buf, const int min_size )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( buf ) buf = realloc( buf, min_size );
Packit Bot a3ac83
  else buf = malloc( min_size );
Packit Bot a3ac83
  return buf;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static char push_back_record( struct Arg_parser * const ap,
Packit Bot a3ac83
                              const int code, const char * const argument )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const int len = strlen( argument );
Packit Bot a3ac83
  struct ap_Record * p;
Packit Bot a3ac83
  void * tmp = ap_resize_buffer( ap->data,
Packit Bot a3ac83
                 ( ap->data_size + 1 ) * sizeof (struct ap_Record) );
Packit Bot a3ac83
  if( !tmp ) return 0;
Packit Bot a3ac83
  ap->data = (struct ap_Record *)tmp;
Packit Bot a3ac83
  p = &(ap->data[ap->data_size]);
Packit Bot a3ac83
  p->code = code;
Packit Bot a3ac83
  p->argument = 0;
Packit Bot a3ac83
  tmp = ap_resize_buffer( p->argument, len + 1 );
Packit Bot a3ac83
  if( !tmp ) return 0;
Packit Bot a3ac83
  p->argument = (char *)tmp;
Packit Bot a3ac83
  strncpy( p->argument, argument, len + 1 );
Packit Bot a3ac83
  ++ap->data_size;
Packit Bot a3ac83
  return 1;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static char add_error( struct Arg_parser * const ap, const char * const msg )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const int len = strlen( msg );
Packit Bot a3ac83
  void * tmp = ap_resize_buffer( ap->error, ap->error_size + len + 1 );
Packit Bot a3ac83
  if( !tmp ) return 0;
Packit Bot a3ac83
  ap->error = (char *)tmp;
Packit Bot a3ac83
  strncpy( ap->error + ap->error_size, msg, len + 1 );
Packit Bot a3ac83
  ap->error_size += len;
Packit Bot a3ac83
  return 1;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void free_data( struct Arg_parser * const ap )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  int i;
Packit Bot a3ac83
  for( i = 0; i < ap->data_size; ++i ) free( ap->data[i].argument );
Packit Bot a3ac83
  if( ap->data ) { free( ap->data ); ap->data = 0; }
Packit Bot a3ac83
  ap->data_size = 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static char parse_long_option( struct Arg_parser * const ap,
Packit Bot a3ac83
                               const char * const opt, const char * const arg,
Packit Bot a3ac83
                               const struct ap_Option options[],
Packit Bot a3ac83
                               int * const argindp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  unsigned len;
Packit Bot a3ac83
  int index = -1, i;
Packit Bot a3ac83
  char exact = 0, ambig = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  for( len = 0; opt[len+2] && opt[len+2] != '='; ++len ) ;
Packit Bot a3ac83
Packit Bot a3ac83
  /* Test all long options for either exact match or abbreviated matches. */
Packit Bot a3ac83
  for( i = 0; options[i].code != 0; ++i )
Packit Bot a3ac83
    if( options[i].name && strncmp( options[i].name, &opt[2], len ) == 0 )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( strlen( options[i].name ) == len )	/* Exact match found */
Packit Bot a3ac83
        { index = i; exact = 1; break; }
Packit Bot a3ac83
      else if( index < 0 ) index = i;		/* First nonexact match found */
Packit Bot a3ac83
      else if( options[index].code != options[i].code ||
Packit Bot a3ac83
               options[index].has_arg != options[i].has_arg )
Packit Bot a3ac83
        ambig = 1;		/* Second or later nonexact match found */
Packit Bot a3ac83
      }
Packit Bot a3ac83
Packit Bot a3ac83
  if( ambig && !exact )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    add_error( ap, "option '" ); add_error( ap, opt );
Packit Bot a3ac83
    add_error( ap, "' is ambiguous" );
Packit Bot a3ac83
    return 1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
Packit Bot a3ac83
  if( index < 0 )		/* nothing found */
Packit Bot a3ac83
    {
Packit Bot a3ac83
    add_error( ap, "unrecognized option '" ); add_error( ap, opt );
Packit Bot a3ac83
    add_error( ap, "'" );
Packit Bot a3ac83
    return 1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
Packit Bot a3ac83
  ++*argindp;
Packit Bot a3ac83
Packit Bot a3ac83
  if( opt[len+2] )		/* '--<long_option>=<argument>' syntax */
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( options[index].has_arg == ap_no )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      add_error( ap, "option '--" ); add_error( ap, options[index].name );
Packit Bot a3ac83
      add_error( ap, "' doesn't allow an argument" );
Packit Bot a3ac83
      return 1;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    if( options[index].has_arg == ap_yes && !opt[len+3] )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      add_error( ap, "option '--" ); add_error( ap, options[index].name );
Packit Bot a3ac83
      add_error( ap, "' requires an argument" );
Packit Bot a3ac83
      return 1;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    return push_back_record( ap, options[index].code, &opt[len+3] );
Packit Bot a3ac83
    }
Packit Bot a3ac83
Packit Bot a3ac83
  if( options[index].has_arg == ap_yes )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( !arg || !arg[0] )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      add_error( ap, "option '--" ); add_error( ap, options[index].name );
Packit Bot a3ac83
      add_error( ap, "' requires an argument" );
Packit Bot a3ac83
      return 1;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    ++*argindp;
Packit Bot a3ac83
    return push_back_record( ap, options[index].code, arg );
Packit Bot a3ac83
    }
Packit Bot a3ac83
Packit Bot a3ac83
  return push_back_record( ap, options[index].code, "" );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static char parse_short_option( struct Arg_parser * const ap,
Packit Bot a3ac83
                                const char * const opt, const char * const arg,
Packit Bot a3ac83
                                const struct ap_Option options[],
Packit Bot a3ac83
                                int * const argindp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  int cind = 1;			/* character index in opt */
Packit Bot a3ac83
Packit Bot a3ac83
  while( cind > 0 )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int index = -1, i;
Packit Bot a3ac83
    const unsigned char code = opt[cind];
Packit Bot a3ac83
    char code_str[2];
Packit Bot a3ac83
    code_str[0] = code; code_str[1] = 0;
Packit Bot a3ac83
Packit Bot a3ac83
    if( code != 0 )
Packit Bot a3ac83
      for( i = 0; options[i].code; ++i )
Packit Bot a3ac83
        if( code == options[i].code )
Packit Bot a3ac83
          { index = i; break; }
Packit Bot a3ac83
Packit Bot a3ac83
    if( index < 0 )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      add_error( ap, "invalid option -- '" ); add_error( ap, code_str );
Packit Bot a3ac83
      add_error( ap, "'" );
Packit Bot a3ac83
      return 1;
Packit Bot a3ac83
      }
Packit Bot a3ac83
Packit Bot a3ac83
    if( opt[++cind] == 0 ) { ++*argindp; cind = 0; }	/* opt finished */
Packit Bot a3ac83
Packit Bot a3ac83
    if( options[index].has_arg != ap_no && cind > 0 && opt[cind] )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !push_back_record( ap, code, &opt[cind] ) ) return 0;
Packit Bot a3ac83
      ++*argindp; cind = 0;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else if( options[index].has_arg == ap_yes )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !arg || !arg[0] )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        add_error( ap, "option requires an argument -- '" );
Packit Bot a3ac83
        add_error( ap, code_str ); add_error( ap, "'" );
Packit Bot a3ac83
        return 1;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      ++*argindp; cind = 0;
Packit Bot a3ac83
      if( !push_back_record( ap, code, arg ) ) return 0;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else if( !push_back_record( ap, code, "" ) ) return 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return 1;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
char ap_init( struct Arg_parser * const ap,
Packit Bot a3ac83
              const int argc, const char * const argv[],
Packit Bot a3ac83
              const struct ap_Option options[], const char in_order )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const char ** non_options = 0;	/* skipped non-options */
Packit Bot a3ac83
  int non_options_size = 0;		/* number of skipped non-options */
Packit Bot a3ac83
  int argind = 1;			/* index in argv */
Packit Bot a3ac83
  int i;
Packit Bot bac822
  int Status = 0;
Packit Bot bac822
  void * tmp = NULL;
Packit Bot a3ac83
Packit Bot a3ac83
  ap->data = 0;
Packit Bot a3ac83
  ap->error = 0;
Packit Bot a3ac83
  ap->data_size = 0;
Packit Bot a3ac83
  ap->error_size = 0;
Packit Bot a3ac83
  if( argc < 2 || !argv || !options ) return 1;
Packit Bot a3ac83
Packit Bot a3ac83
  while( argind < argc )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const unsigned char ch1 = argv[argind][0];
Packit Bot a3ac83
    const unsigned char ch2 = ch1 ? argv[argind][1] : 0;
Packit Bot a3ac83
Packit Bot a3ac83
    if( ch1 == '-' && ch2 )		/* we found an option */
Packit Bot a3ac83
      {
Packit Bot a3ac83
      const char * const opt = argv[argind];
Packit Bot a3ac83
      const char * const arg = ( argind + 1 < argc ) ? argv[argind+1] : 0;
Packit Bot a3ac83
      if( ch2 == '-' )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        if( !argv[argind][2] ) { ++argind; break; }	/* we found "--" */
Packit Bot bac822
        else if( !parse_long_option( ap, opt, arg, options, &argind ) ) {
Packit Bot bac822
          Status = 0;
Packit Bot bac822
          goto Exit;
Packit Bot bac822
          }
Packit Bot a3ac83
        }
Packit Bot bac822
      else if( !parse_short_option( ap, opt, arg, options, &argind ) ) {
Packit Bot bac822
          Status = 0;
Packit Bot bac822
          goto Exit;
Packit Bot bac822
          }
Packit Bot a3ac83
      if( ap->error ) break;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( in_order )
Packit Bot a3ac83
        { if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; }
Packit Bot a3ac83
      else
Packit Bot a3ac83
        {
Packit Bot bac822
        tmp = ap_resize_buffer( non_options,
Packit Bot a3ac83
                       ( non_options_size + 1 ) * sizeof *non_options );
Packit Bot a3ac83
        if( !tmp ) return 0;
Packit Bot a3ac83
        non_options = (const char **)tmp;
Packit Bot a3ac83
        non_options[non_options_size++] = argv[argind++];
Packit Bot a3ac83
        }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( ap->error ) free_data( ap );
Packit Bot a3ac83
  else
Packit Bot a3ac83
    {
Packit Bot a3ac83
    for( i = 0; i < non_options_size; ++i )
Packit Bot bac822
      if( !push_back_record( ap, 0, non_options[i] ) ) {
Packit Bot bac822
        Status = 0;
Packit Bot bac822
        goto Exit;
Packit Bot bac822
      }
Packit Bot a3ac83
    while( argind < argc )
Packit Bot bac822
      if( !push_back_record( ap, 0, argv[argind++] ) ) {
Packit Bot bac822
        Status = 0;
Packit Bot bac822
        goto Exit;
Packit Bot bac822
      }
Packit Bot a3ac83
    }
Packit Bot bac822
  Status = 1;
Packit Bot bac822
  goto Exit;
Packit Bot bac822
Exit:
Packit Bot a3ac83
  if( non_options ) free( non_options );
Packit Bot bac822
  return(Status);
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
void ap_free( struct Arg_parser * const ap )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  free_data( ap );
Packit Bot a3ac83
  if( ap->error ) { free( ap->error ); ap->error = 0; }
Packit Bot a3ac83
  ap->error_size = 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
const char * ap_error( const struct Arg_parser * const ap )
Packit Bot a3ac83
  { return ap->error; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int ap_arguments( const struct Arg_parser * const ap )
Packit Bot a3ac83
  { return ap->data_size; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int ap_code( const struct Arg_parser * const ap, const int i )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code;
Packit Bot a3ac83
  else return 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
const char * ap_argument( const struct Arg_parser * const ap, const int i )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].argument;
Packit Bot a3ac83
  else return "";
Packit Bot a3ac83
  }