pike.git / lib / modules / Arg.pmod

version» Context lines:

pike.git/lib/modules/Arg.pmod:1: - // +    // Argument parser   // By Martin Nilsson - // $Id$ +    //    -  + //! Argument parsing module + //! This module supports two rather different methods of argument parsing. + //! The first is suitable quick argument parsing without much in the way of checking: + //! + //! @code + //! int main( int c, array(string) argv ) + //! { + //! mapping arguments = Arg.parse(argv); + //! array files = arguments[Arg.REST]; + //! if( arguments->help ) print_help(); + //! ... + //! } + //! @endcode + //! + //! The @[Arg.parse] method will return a mapping from argument name + //! to the argument value, if any. + //! + //! Non-option arguments will be placed in the index Arg.REST + //! + //! The second way to use this module is to inherit the LowOptions + //! class and then start adding supported arguments: + //! + //! @code + //! class MyArguments { + //! inherit Arg.LowOptions; + //! Opt verbose = NoOpt("-v")|NoOpt("--verbose"); + //! Opt help = MaybeOpt("--help"); + //! Opt output = HasOpt("--output")|HasOpt("-o"); + //! }; + //! @endcode + //! + //! Then, in main: + //! + //! @code + //! MyArguments args = MyArguments(argv); + //! @endcode + //! + //! See the documentation for @[OptLibrary] for details about the various + //! Opt classes. +    #pike __REAL_VERSION__      class OptLibrary -  + //!   {       //! Base class for parsing an argument. Inherit this class to create    //! custom made option types.    class Opt    {    constant is_opt = 1;    protected Opt next;       //! Should return 1 for set options or a string containing the    //! value of the option. Returning 0 means the option was not set    //! (or matched). To properly chain arguments parsers, return -  //! @expr{::get_value(argv, env)@} instead of @expr{0@}, unless -  //! you want to explicitly stop the chain and not set this option. -  int(0..1)|string get_value(array(string) argv, mapping(string:string) env) +  //! @expr{::get_value(argv, env, previous)@} instead of @expr{0@}, +  //! unless you want to explicitly stop the chain and not set this +  //! option. +  mixed get_value(array(string) argv, mapping(string:string) env, +  int|string previous)    { -  if(next) return next->get_value(argv, env); +  if(next) return next->get_value(argv, env, previous);    return 0;    }       //! Should return a list of options that are parsed. To properly    //! chain argument parsers, return @expr{your_opts +    //! ::get_opts()@}.    array(string) get_opts()    {    if(!next) return ({});    return next->get_opts();
pike.git/lib/modules/Arg.pmod:82:       protected void create(string _opt)    {    if( sizeof(_opt)>2 && has_prefix(_opt, "--") )    double = 1;    else if( sizeof(_opt)!=2 || _opt[0]!='-' || _opt=="--" )    error("%O not a valid option.\n", _opt);    opt = _opt;    }    -  int(0..1)|string get_value(array(string) argv, mapping(string:string) env) +  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)    { -  if( !sizeof(argv) ) return ::get_value(argv, env); +  if( !sizeof(argv) ) return previous || ::get_value(argv, env, previous);       if( double )    {    if( argv[0]==opt )    {    argv[0] = 0; -  return 1; +  return (int)previous+1;    } -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }       if( sizeof(argv[0])>1 && argv[0][0]=='-' && argv[0][1]!='-' )    {    array parts = argv[0]/"=";    if( has_value(parts[0], opt[1..1]) )    {    parts[0] -= opt[1..1];    argv[0] = parts*"=";    if(argv[0]=="-") argv[0] = 0; -  return 1; +  return (int)previous+1;    }    }    -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }       array(string) get_opts()    {    return ({ opt }) + ::get_opts();    }       protected string __sprintf()    {    return sprintf("Arg.NoOpt(%O)", opt);
pike.git/lib/modules/Arg.pmod:137:    class Env    {    inherit Opt;    protected string name;       protected void create(string _name)    {    name = _name;    }    -  int(0..1)|string get_value(array(string) argv, mapping(string:string) env) +  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)    {    if( env[name] ) return env[name]; -  return ::get_value(argv, env); +  return ::get_value(argv, env, previous);    }       protected string __sprintf()    {    return sprintf("Arg.Env(%O)", name);    }    }       //! Default value for a setting.    //!
pike.git/lib/modules/Arg.pmod:163:    class Default    {    inherit Opt;    protected string value;       protected void create(string _value)    {    value = _value;    }    -  string get_value(array(string) argv, mapping(string:string) env) +  string get_value(array(string) argv, mapping(string:string) env, mixed previous)    {    return value;    }       protected string __sprintf()    {    return sprintf("Arg.Default(%O)", value);    }    }   
pike.git/lib/modules/Arg.pmod:185:    //! @tt{-x@} and x in a sequence like @tt{-axb@} will set the    //! variable to @expr{1@}. @tt{--foo=bar@}, @tt{-x bar@} and    //! @tt{-x=bar@} will set the variable to @expr{bar@}.    //!    //! @example    //! Opt debug = MaybeOpt("--debug");    class MaybeOpt    {    inherit NoOpt;    -  int(0..1)|string get_value(array(string) argv, mapping(string:string) env) +  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)    { -  if( !sizeof(argv) ) return ::get_value(argv, env); +  if( !sizeof(argv) ) return previous || ::get_value(argv, env, previous);       if( double )    {    // --foo    if( argv[0]==opt )    {    argv[0] = 0; -  return 1; +  return (int)previous+1;    }       // --foo=bar    if( sscanf(argv[0], opt+"=%s", string ret)==1 )    {    argv[0] = 0; -  +  // FIXME: Make an array if previous is set?    return ret;    }    -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }       // -x    if( sizeof(argv[0])>1 && argv[0][0]=='-' && argv[0][1]!='-' )    {    array parts = argv[0]/"=";       if( has_value(parts[0], opt[1..1]) &&    ( sizeof(parts)==1 ||    parts[0][-1]!=opt[1] ) )    {    // -xy, -xy=z    parts[0] -= opt[1..1];    argv[0] = parts*"=";    if(argv[0]=="-") argv[0] = 0; -  return 1; +  return (int)previous+1;    }    else if( sizeof(parts)>1 && parts[0][-1]==opt[1] )    {    // -yx=z    parts[0] -= opt[1..1];    if( parts[0]=="-" )    argv[0] = 0;    else    argv[0] = parts[0];    return parts[1..]*"=";    }    -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }    -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }       protected string __sprintf()    {    return sprintf("Arg.MaybeOpt(%O)", opt);    }    }       //! Parses an option that has a parameter. @tt{--foo=bar@}, @tt{-x    //! bar@} and @tt{-x=bar@} will set the variable to @expr{bar@}.    //!    //! @example    //! Opt user = HasOpt("--user")|HasOpt("-u");    class HasOpt    {    inherit NoOpt;    -  int(0..1)|string get_value(array(string) argv, mapping(string:string) env) +  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)    { -  if( !sizeof(argv) ) return ::get_value(argv, env); +  if( !sizeof(argv) ) return previous || ::get_value(argv, env, previous);       if( double )    {    // --foo bar    if( argv[0]==opt )    {    if( sizeof(argv)>1 )    {    argv[0] = 0;    string ret = argv[1];
pike.git/lib/modules/Arg.pmod:280:    }    return 0; // FIXME: Signal failure    }       // --foo=bar    if( sscanf(argv[0], opt+"=%s", string ret)==1 )    {    argv[0] = 0;    return ret;    } -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }       if( sizeof(argv[0])>1 && argv[0][0]=='-' && argv[0][1]!='-' )    {    array parts = argv[0]/"=";    if( sizeof(parts[0]) && parts[0][-1]==opt[1] )    {    if( sizeof(parts)==1 )    {    // "-xxxy z"
pike.git/lib/modules/Arg.pmod:304:    if( parts[0]=="-" )    argv[0] = 0;    else    argv[0] = parts[0];    string ret = argv[1];    argv[1] = 0;    return ret;    }       // Fail. "-y" without any more elements in argv. -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }    else    {    // "-xxxy=z"    parts[0] -= opt[1..1];    if( parts[0]=="-" )    argv[0] = 0;    else    argv[0] = parts[0];    return parts[1..]*"=";    }    }    }    -  return ::get_value(argv, env); +  return previous || ::get_value(argv, env, previous);    }       protected string __sprintf()    {    return sprintf("Arg.HasOpt(%O)", opt);    }    }      } // -- OptLibrary      object REST = class {    protected string _sprintf(int t)    {    return "Arg.REST";    }    }();    -  + //! @decl constant REST = REST(); + //! + //! Constant used by Arg.parse() to indicate the remaining objects. +  +    // FIXME: Support for rc files? ( Opt x = Opt("--x")|INIFile(path, name); )   // FIXME: Support for type casts? ( Opt level = Integer(Opt("--level"));      class LowOptions -  +  //!   {    protected inherit OptLibrary;    -  +  //!    protected mapping(string:Opt) opts = ([]); -  +  +  //!    protected mapping(string:int(1..1)|string) values = ([]); -  +  +  //!    protected array(string) argv; -  +  +  //!    protected string application;    -  +  //!    protected void create(array(string) _argv, void|mapping(string:string) env)    {    if(!env)    env = getenv();       // Make a list of all the arguments we can parse.    foreach(::_indices(2), string index)    { -  mixed val = ::`[](index, 2); +  mixed val = ::`[](index, this, 0);    if(objectp(val) && val->is_opt) opts[index]=val;    }       application = _argv[0];    argv = _argv[1..];    mapping(string:Opt) unset = opts+([]);    -  while(1) +  while(sizeof(argv))    { -  if(!sizeof(argv)) break; -  -  int(0..1)|string value; -  foreach(unset; string index; Opt arg) +  array(string) pre = argv+({}); +  foreach(opts; string index; Opt arg)    { -  value = arg->get_value(argv, env); +  int(0..1)|string value = arg->get_value(argv, env, values[index]);    if(value)    {    m_delete(unset, index);    values[index] = value; -  break; +  argv -= ({ 0 });    }    } -  +  argv -= ({ 0 });    -  if(!value) -  value = unhandled_argument(argv, env); -  -  if(!value) +  if(equal(pre, argv)) +  { +  unhandled_argument(argv, env); +  if(equal(pre, argv))    break; -  else -  while( sizeof(argv) && argv[0] == 0 ) -  argv = argv[1..]; +     } -  +  }       if( sizeof(unset) )    {    int(0..1)|string value;    foreach(unset; string index; Opt arg)    { -  value = arg->get_value(({}), env); +  value = arg->get_value(({}), env, values[index]);    if(value) -  { -  m_delete(unset, index); +     values[index] = value;    }    } -  } +        }    -  +  //!    protected int(0..1) unhandled_argument(array(string) argv,    mapping(string:string) env)    {    return 0;    }       protected mixed cast(string to)    {    switch( to )    {
pike.git/lib/modules/Arg.pmod:436:   //! The option parser class that contains all the argument objects.   //!   class Options   {    inherit LowOptions;       protected string|int `[](string id)    {    return values[id];    } +     protected string|int `->(string id)    {    return values[id];    }    -  +  //!    protected int(0..1)|string unhandled_argument(array(string) argv,    mapping(string:string) env)    {    if( !sizeof(argv) || argv[0]!="--help" ) return 0;       string s = index("help_pre");    if( s )    write( s+"\n" );       foreach(opts; string i; Opt opt)
pike.git/lib/modules/Arg.pmod:465:    write( s ); // FIXME: Format    }       s = index("help_post");    if( s )    write( "\n"+s );    }       protected string index(string i)    { -  string s = ::`[](i, 2); +  string s = ::`[](i, this, 0);    if( !s ) return 0;    if( !stringp(s) ) error("%O is not a string.\n", i);    if( sizeof(s) )    {    if( s[-1]!='\n' )    s += "\n";    return s;    }    return 0;    }   }         // --- Simple interface      class SimpleOptions -  + //! Options parser with a unhandled_argument implementation that + //! parses most common argument formats.   {    inherit LowOptions;    -  +  //! Handles arguments as described in @[Arg.parse]    int(0..1) unhandled_argument(array(string) argv, mapping(string:string) env)    {    string arg = argv[0];    if(!sizeof(arg) || arg[0]!='-') return 0;       string name,value;       if( has_prefix(arg, "--") )    {    sscanf( arg, "--%s=%s", name, value ) || sscanf( arg, "--%s", name );    if(!name) return 0; // arg == "--" -  values[name] = value||1; +  if( value ) +  values[name] = value; +  else +  values[name]++;    argv[0]=0;    return 1;    }       sscanf( arg, "-%s=%s", name, value ) || sscanf( arg, "-%s", name );    if( !name || !sizeof(name) ) return 0;    foreach( name/1; int pos; string c ) -  if( pos == sizeof(name)-1 ) -  values[c] = value||1; +  { +  if( value && pos == sizeof(name)-1 ) +  values[c] = value;    else -  values[c] = 1; +  values[c]++; +  }    argv[0]=0;    return 1;    }   }      // Handles   // --foo -> "foo":1   // --foo=bar -> "foo":"bar"   // -bar -> "b":1,"a":1,"r":1   // -bar=foo -> "b":1,"a":1,"r":"foo" (?)   // --foo --bar -> "foo":1,"bar":1 - // --foo - --bar -> "foo":1 - // --foo x --bar -> "foo":1 (?) + // --foo - --bar -> "foo":1 + // --foo x --bar -> "foo":1 (?) + // -foo -> "f":1,"o":2 + // -x -x -x -> "x":3   //   // void main(int n, array argv)   // {   // mapping opts = Arg.parse(argv);   // argv = opts[Arg.REST];   // }    -  - mapping(string:string|int(1..1)) parse(array(string) argv) + //! Convenience function for simple argument parsing. + //! + //! Handles the most common cases. + //! + //! The return value is a mapping from option name to the option value. + //! + //! The special index Arg.REST will be set to the remaining arguments + //! after all options have been parsed. + //! + //! The following argument syntaxes are supported: + //! + //! @code + //! --foo -> "foo":1 + //! --foo=bar -> "foo":"bar" + //! -bar -> "b":1,"a":1,"r":1 + //! -bar=foo -> "b":1,"a":1,"r":"foo" (?) + //! --foo --bar -> "foo":1,"bar":1 + //! --foo - --bar -> "foo":1 + //! --foo x --bar -> "foo":1 (?) + //! -foo -> "f":1,"o":2 + //! -x -x -x -> "x":3 + //! @endcode + //! + //! @example + //! @code + //! void main(int n, array argv) + //! { + //! mapping opts = Arg.parse(argv); + //! argv = opts[Arg.REST]; + //! if( opts->help ) /*... */ + //! } + //! @endcode + mapping(string:string|int(1..)) parse(array(string) argv)   {    return (mapping)SimpleOptions(argv);   }