/* Arg_parser - A POSIX/GNU command line argument parser. C version Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "carg_parser.h" /* assure at least a minimum size for buffer `buf' */ void * ap_resize_buffer( void *buf, const int min_size ) { if( buf ) buf = realloc( buf, min_size ); else buf = malloc( min_size ); return buf; } char push_back_record( Arg_parser * ap, const int code, const char * argument ) { const int len = strlen( argument ); ap_Record *p; void * tmp = ap_resize_buffer( ap->data, ( ap->data_size + 1 ) * sizeof( ap_Record ) ); if( !tmp ) return 0; ap->data = (ap_Record *)tmp; p = &(ap->data[ap->data_size]); p->code = code; p->argument = 0; tmp = ap_resize_buffer( p->argument, len + 1 ); if( !tmp ) return 0; p->argument = (char *)tmp; strncpy( p->argument, argument, len + 1 ); ++ap->data_size; return 1; } char add_error( Arg_parser * ap, const char * msg ) { const int len = strlen( msg ); void * tmp = ap_resize_buffer( ap->error, ap->error_size + len + 1 ); if( !tmp ) return 0; ap->error = (char *)tmp; strncpy( ap->error + ap->error_size, msg, len + 1 ); ap->error_size += len; return 1; } void free_data( Arg_parser * ap ) { int i; for( i = 0; i < ap->data_size; ++i ) free( ap->data[i].argument ); if( ap->data ) { free( ap->data ); ap->data = 0; } ap->data_size = 0; } char parse_long_option( Arg_parser * ap, const char * const opt, const char * const arg, const ap_Option options[], int * argindp ) { unsigned int len; int index = -1; int i; char exact = 0, ambig = 0; for( len = 0; opt[len+2] && opt[len+2] != '='; ++len ) ; // Test all long options for either exact match or abbreviated matches. for( i = 0; options[i].code != 0; ++i ) if( options[i].name && !strncmp( options[i].name, &opt[2], len ) ) { if( strlen( options[i].name ) == len ) // Exact match found { index = i; exact = 1; break; } else if( index < 0 ) index = i; // First nonexact match found else if( options[index].code != options[i].code || options[index].has_arg != options[i].has_arg ) ambig = 1; // Second or later nonexact match found } if( ambig && !exact ) { add_error( ap, "option `" ); add_error( ap, opt ); add_error( ap, "' is ambiguous" ); return 1; } if( index < 0 ) // nothing found { add_error( ap, "unrecognized option `" ); add_error( ap, opt ); add_error( ap, "'" ); return 1; } ++*argindp; if( opt[len+2] ) // `--=' syntax { if( options[index].has_arg == ap_no ) { add_error( ap, "option `--" ); add_error( ap, options[index].name ); add_error( ap, "' doesn't allow an argument" ); return 1; } if( options[index].has_arg == ap_yes && !opt[len+3] ) { add_error( ap, "option `--" ); add_error( ap, options[index].name ); add_error( ap, "' requires an argument" ); return 1; } return push_back_record( ap, options[index].code, &opt[len+3] ); } if( options[index].has_arg == ap_yes ) { if( !arg || !arg[0] ) { add_error( ap, "option `--" ); add_error( ap, options[index].name ); add_error( ap, "' requires an argument" ); return 1; } ++*argindp; return push_back_record( ap, options[index].code, arg ); } return push_back_record( ap, options[index].code, "" ); } char parse_short_option( Arg_parser * ap, const char * const opt, const char * const arg, const ap_Option options[], int * argindp ) { int cind = 1; // character index in opt while( cind > 0 ) { int index = -1; int i; const unsigned char code = opt[cind]; const char code_str[2] = { code, 0 }; if( code != 0 ) for( i = 0; options[i].code; ++i ) if( code == options[i].code ) { index = i; break; } if( index < 0 ) { add_error( ap, "invalid option -- " ); add_error( ap, code_str ); return 1; } if( opt[++cind] == 0 ) { ++*argindp; cind = 0; } // opt finished if( options[index].has_arg != ap_no && cind > 0 && opt[cind] ) { if( !push_back_record( ap, code, &opt[cind] ) ) return 0; ++*argindp; cind = 0; } else if( options[index].has_arg == ap_yes ) { if( !arg || !arg[0] ) { add_error( ap, "option requires an argument -- " ); add_error( ap, code_str ); return 1; } ++*argindp; cind = 0; if( !push_back_record( ap, code, arg ) ) return 0; } else if( !push_back_record( ap, code, "" ) ) return 0; } return 1; } char ap_init( Arg_parser * ap, const int argc, const char * const argv[], const ap_Option options[], const char in_order ) { const char ** non_options = 0; // skipped non-options int non_options_size = 0; // number of skipped non-options int argind = 1; // index in argv int i; ap->data = 0; ap->error = 0; ap->data_size = 0; ap->error_size = 0; if( argc < 2 || !argv || !options ) return 1; while( argind < argc ) { const unsigned char ch1 = argv[argind][0]; const unsigned char ch2 = ( ch1 ? argv[argind][1] : 0 ); if( ch1 == '-' && ch2 ) // we found an option { const char * const opt = argv[argind]; const char * const arg = (argind + 1 < argc) ? argv[argind+1] : 0; if( ch2 == '-' ) { if( !argv[argind][2] ) { ++argind; break; } // we found "--" else if( !parse_long_option( ap, opt, arg, options, &argind ) ) return 0; } else if( !parse_short_option( ap, opt, arg, options, &argind ) ) return 0; if( ap->error ) break; } else { if( !in_order ) { void * tmp = ap_resize_buffer( non_options, ( non_options_size + 1 ) * sizeof( *non_options ) ); if( !tmp ) return 0; non_options = (const char **)tmp; non_options[non_options_size++] = argv[argind++]; } else if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; } } if( ap->error ) free_data( ap ); else { for( i = 0; i < non_options_size; ++i ) if( !push_back_record( ap, 0, non_options[i] ) ) return 0; while( argind < argc ) if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; } if( non_options ) free( non_options ); return 1; } void ap_free( Arg_parser * ap ) { free_data( ap ); if( ap->error ) { free( ap->error ); ap->error = 0; } ap->error_size = 0; } const char * ap_error( const Arg_parser * ap ) { return ap->error; } int ap_arguments( const Arg_parser * ap ) { return ap->data_size; } int ap_code( const Arg_parser * ap, const int i ) { if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code; else return 0; } const char * ap_argument( const Arg_parser * ap, const int i ) { if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].argument; else return ""; }