Blame carg_parser.c

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