d689282016-11-05Martin Nilsson #pike __REAL_VERSION__
09be262007-09-16Martin Nilsson 
87a1982014-08-25Per Hedbor //! Argument parsing module
d689282016-11-05Martin Nilsson //! //! This module supports two rather different methods of argument //! parsing. The first is suitable quick argument parsing without //! much in the way of checking:
87a1982014-08-25Per Hedbor //! //! @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 //!
6ef6282016-07-03Martin Nilsson //! The second way to use this module is to inherit the Options class //! and add supported arguments.
87a1982014-08-25Per Hedbor //! //! @code //! class MyArguments {
6ef6282016-07-03Martin Nilsson //! inherit Arg.Options;
87a1982014-08-25Per Hedbor //! 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.
275ec32016-07-04Martin Nilsson //! This class contains a library of parser for different type of //! options.
6244262008-05-03Martin Nilsson class OptLibrary
09be262007-09-16Martin Nilsson {
57238a2008-05-02Martin Nilsson  //! Base class for parsing an argument. Inherit this class to create
6244262008-05-03Martin Nilsson  //! custom made option types. class Opt
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  constant is_opt = 1;
9eaf1d2008-06-28Martin Nilsson  protected Opt next;
57238a2008-05-02Martin Nilsson 
275ec32016-07-04Martin Nilsson  //! The overloading method should calculate the value of the //! option and return it. Methods processing @[argv] should only
eb5a5f2017-01-27Martin Nilsson  //! look at argv[0] and if it matches, set it to 0. Returning //! @expr{UNDEFINED@} means the option was not set (or //! matched). To properly chain arguments parsers, return //! @expr{::get_value(argv, env, previous)@} instead of //! @expr{UNDEFINED@}, unless you want to explicitly stop the //! chain and not set this option.
685c7c2013-06-03Martin Nilsson  mixed get_value(array(string) argv, mapping(string:string) env,
275ec32016-07-04Martin Nilsson  mixed previous)
57238a2008-05-02Martin Nilsson  {
685c7c2013-06-03Martin Nilsson  if(next) return next->get_value(argv, env, previous);
eb5a5f2017-01-27Martin Nilsson  return UNDEFINED;
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson 
6244262008-05-03Martin Nilsson  //! Should return a list of options that are parsed. To properly //! chain argument parsers, return @expr{your_opts + //! ::get_opts()@}. array(string) get_opts()
57238a2008-05-02Martin Nilsson  {
4c19032008-05-03Martin Nilsson  if(!next) return ({});
6244262008-05-03Martin Nilsson  return next->get_opts();
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected this_program `|(mixed thing)
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  if( !objectp(thing) || !thing->is_opt )
57238a2008-05-02Martin Nilsson  error("Can only or %O with another %O.\n", this, this_program); if( next ) { next = next | thing; return this; } next = thing;
09be262007-09-16Martin Nilsson  return this; }
57238a2008-05-02Martin Nilsson  //! This function will be called by @expr{_sprintf@}, which //! handles formatting of chaining between objects.
9eaf1d2008-06-28Martin Nilsson  protected string __sprintf()
57238a2008-05-02Martin Nilsson  { return sprintf("%O()", this_program); }
09be262007-09-16Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf(int t)
57238a2008-05-02Martin Nilsson  { if( t!='O' ) return UNDEFINED; if( !next ) return __sprintf(); else return sprintf("%s|%O", __sprintf(), next); }
09be262007-09-16Martin Nilsson  }
5abafb2016-07-10Martin Nilsson  //! Wrapper class that converts the option argument to an integer. //! @example //! Opt foo = Int(HasOpt("--foo")|Default(4711)); class Int(Opt opt) { inherit Opt; mixed get_value(array(string) argv, mapping(string:string) env, mixed previous) { return (int)opt->get_value(argv, env, previous); } array(string) get_opts() { return opt->get_opts(); } protected this_program `|(mixed thing) { error("OR:ing Int objects not supported.\n"); } protected string _sprintf(int t) { return t=='O' && sprintf("%O(%O)", this_program, opt); } }
eb5a5f2017-01-27Martin Nilsson  //! Wrapper class that allows multiple instances of an option. //! @example //! Opt filter = Multiple(HasOpt("--filter")); class Multiple(Opt opt) { inherit Opt; protected mixed values = ({}); mixed get_value(array(string) argv, mapping(string:string) env, mixed previous) { mixed v = opt->get_value(argv, env, previous); if(undefinedp(v)) return UNDEFINED; values += ({ v }); return values; } array(string) get_opts() { return opt->get_opts(); } protected this_program `|(mixed thing) { error("OR:ing Multiple objects not supported.\n"); } protected string _sprintf(int t) { return t=='O' && sprintf("%O(%O)", this_program, values); } }
6244262008-05-03Martin Nilsson  //! Parses an option without parameter, such as --help, -x or "x"
57238a2008-05-02Martin Nilsson  //! from -axb. //! //! @example
6244262008-05-03Martin Nilsson  //! Opt verbose = NoOpt("-v")|NoOpt("--verbose"); class NoOpt
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  inherit Opt;
9eaf1d2008-06-28Martin Nilsson  protected string opt;
09be262007-09-16Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected void create(string _opt)
57238a2008-05-02Martin Nilsson  {
51c1662017-10-29Stephen R. van den Berg  switch (sizeof(_opt)) { default: if (has_prefix(_opt, "--")) break; case 2: if (_opt[0]=='-' && _opt[1]!='-') break; case 1: case 0: error("%O not a valid option.\n", _opt); }
6244262008-05-03Martin Nilsson  opt = _opt;
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson 
275ec32016-07-04Martin Nilsson  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)
09be262007-09-16Martin Nilsson  {
685c7c2013-06-03Martin Nilsson  if( !sizeof(argv) ) return previous || ::get_value(argv, env, previous);
57238a2008-05-02Martin Nilsson 
51c1662017-10-29Stephen R. van den Berg  if( argv[0]==opt )
09be262007-09-16Martin Nilsson  {
51c1662017-10-29Stephen R. van den Berg  argv[0] = 0; return (int)previous+1;
09be262007-09-16Martin Nilsson  }
57238a2008-05-02Martin Nilsson 
51c1662017-10-29Stephen R. van den Berg  if (sizeof(opt) == 2 && sizeof(argv[0])>2 && argv[0][0]=='-' && argv[0][1]!='-' && has_value(argv[0], opt[1..1]))
57238a2008-05-02Martin Nilsson  {
51c1662017-10-29Stephen R. van den Berg  argv[0] -= opt[1..1]; return (int)previous+1;
57238a2008-05-02Martin Nilsson  }
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
09be262007-09-16Martin Nilsson  }
6244262008-05-03Martin Nilsson  array(string) get_opts()
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  return ({ opt }) + ::get_opts();
09be262007-09-16Martin Nilsson  }
9eaf1d2008-06-28Martin Nilsson  protected string __sprintf()
57238a2008-05-02Martin Nilsson  {
6244262008-05-03Martin Nilsson  return sprintf("Arg.NoOpt(%O)", opt);
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson  }
6244262008-05-03Martin Nilsson  //! Environment fallback for an option. Can of course be used as //! only Opt source.
57238a2008-05-02Martin Nilsson  //! //! @example
6244262008-05-03Martin Nilsson  //! Opt debug = NoOpt("--debug")|Env("MY_DEBUG");
57238a2008-05-02Martin Nilsson  class Env
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  inherit Opt;
9eaf1d2008-06-28Martin Nilsson  protected string name;
09be262007-09-16Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected void create(string _name)
57238a2008-05-02Martin Nilsson  { name = _name; }
09be262007-09-16Martin Nilsson 
275ec32016-07-04Martin Nilsson  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)
57238a2008-05-02Martin Nilsson  { if( env[name] ) return env[name];
685c7c2013-06-03Martin Nilsson  return ::get_value(argv, env, previous);
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected string __sprintf()
57238a2008-05-02Martin Nilsson  {
6244262008-05-03Martin Nilsson  return sprintf("Arg.Env(%O)", name);
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson  }
57238a2008-05-02Martin Nilsson  //! Default value for a setting. //! //! @example
6244262008-05-03Martin Nilsson  //! Opt output = HasOpt("-o")|Default("a.out");
57238a2008-05-02Martin Nilsson  class Default
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  inherit Opt;
5abafb2016-07-10Martin Nilsson  protected mixed value;
09be262007-09-16Martin Nilsson 
5abafb2016-07-10Martin Nilsson  protected void create(mixed _value)
57238a2008-05-02Martin Nilsson  { value = _value; }
09be262007-09-16Martin Nilsson 
5abafb2016-07-10Martin Nilsson  mixed get_value(array(string) argv, mapping(string:string) env,
275ec32016-07-04Martin Nilsson  mixed previous)
57238a2008-05-02Martin Nilsson  { return value; }
09be262007-09-16Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected string __sprintf()
57238a2008-05-02Martin Nilsson  {
6244262008-05-03Martin Nilsson  return sprintf("Arg.Default(%O)", value);
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson  }
6244262008-05-03Martin Nilsson  //! Parses an option that may have a parameter. @tt{--foo@},
57238a2008-05-02Martin Nilsson  //! @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
6244262008-05-03Martin Nilsson  //! Opt debug = MaybeOpt("--debug"); class MaybeOpt
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  inherit NoOpt;
09be262007-09-16Martin Nilsson 
275ec32016-07-04Martin Nilsson  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)
09be262007-09-16Martin Nilsson  {
685c7c2013-06-03Martin Nilsson  if( !sizeof(argv) ) return previous || ::get_value(argv, env, previous);
09be262007-09-16Martin Nilsson 
51c1662017-10-29Stephen R. van den Berg  if( sizeof(opt) > 2 )
09be262007-09-16Martin Nilsson  {
57238a2008-05-02Martin Nilsson  // --foo
6244262008-05-03Martin Nilsson  if( argv[0]==opt )
57238a2008-05-02Martin Nilsson  { argv[0] = 0;
685c7c2013-06-03Martin Nilsson  return (int)previous+1;
57238a2008-05-02Martin Nilsson  }
09be262007-09-16Martin Nilsson 
57238a2008-05-02Martin Nilsson  // --foo=bar
6244262008-05-03Martin Nilsson  if( sscanf(argv[0], opt+"=%s", string ret)==1 )
57238a2008-05-02Martin Nilsson  { argv[0] = 0;
685c7c2013-06-03Martin Nilsson  // FIXME: Make an array if previous is set?
57238a2008-05-02Martin Nilsson  return ret; }
09be262007-09-16Martin Nilsson 
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
09be262007-09-16Martin Nilsson  }
57238a2008-05-02Martin Nilsson  // -x if( sizeof(argv[0])>1 && argv[0][0]=='-' && argv[0][1]!='-' )
09be262007-09-16Martin Nilsson  {
57238a2008-05-02Martin Nilsson  array parts = argv[0]/"=";
6244262008-05-03Martin Nilsson  if( has_value(parts[0], opt[1..1]) &&
57238a2008-05-02Martin Nilsson  ( sizeof(parts)==1 ||
6244262008-05-03Martin Nilsson  parts[0][-1]!=opt[1] ) )
57238a2008-05-02Martin Nilsson  { // -xy, -xy=z
6244262008-05-03Martin Nilsson  parts[0] -= opt[1..1];
57238a2008-05-02Martin Nilsson  argv[0] = parts*"="; if(argv[0]=="-") argv[0] = 0;
685c7c2013-06-03Martin Nilsson  return (int)previous+1;
57238a2008-05-02Martin Nilsson  }
6244262008-05-03Martin Nilsson  else if( sizeof(parts)>1 && parts[0][-1]==opt[1] )
57238a2008-05-02Martin Nilsson  { // -yx=z
6244262008-05-03Martin Nilsson  parts[0] -= opt[1..1];
09be262007-09-16Martin Nilsson  if( parts[0]=="-" ) argv[0] = 0; else argv[0] = parts[0]; return parts[1..]*"=";
57238a2008-05-02Martin Nilsson  }
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
09be262007-09-16Martin Nilsson  }
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
09be262007-09-16Martin Nilsson  }
6244262008-05-03Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected string __sprintf()
6244262008-05-03Martin Nilsson  { return sprintf("Arg.MaybeOpt(%O)", opt); }
09be262007-09-16Martin Nilsson  }
6244262008-05-03Martin Nilsson  //! Parses an option that has a parameter. @tt{--foo=bar@}, @tt{-x
57238a2008-05-02Martin Nilsson  //! bar@} and @tt{-x=bar@} will set the variable to @expr{bar@}. //! //! @example
6244262008-05-03Martin Nilsson  //! Opt user = HasOpt("--user")|HasOpt("-u"); class HasOpt
09be262007-09-16Martin Nilsson  {
6244262008-05-03Martin Nilsson  inherit NoOpt;
57238a2008-05-02Martin Nilsson 
275ec32016-07-04Martin Nilsson  mixed get_value(array(string) argv, mapping(string:string) env, mixed previous)
09be262007-09-16Martin Nilsson  {
685c7c2013-06-03Martin Nilsson  if( !sizeof(argv) ) return previous || ::get_value(argv, env, previous);
57238a2008-05-02Martin Nilsson 
51c1662017-10-29Stephen R. van den Berg  if( sizeof(opt) > 2 )
09be262007-09-16Martin Nilsson  {
57238a2008-05-02Martin Nilsson  // --foo bar
6244262008-05-03Martin Nilsson  if( argv[0]==opt )
57238a2008-05-02Martin Nilsson  { if( sizeof(argv)>1 ) { argv[0] = 0; string ret = argv[1]; argv[1] = 0; return ret; } return 0; // FIXME: Signal failure } // --foo=bar
6244262008-05-03Martin Nilsson  if( sscanf(argv[0], opt+"=%s", string ret)==1 )
09be262007-09-16Martin Nilsson  { argv[0] = 0; return ret; }
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
09be262007-09-16Martin Nilsson  }
57238a2008-05-02Martin Nilsson  if( sizeof(argv[0])>1 && argv[0][0]=='-' && argv[0][1]!='-' )
09be262007-09-16Martin Nilsson  {
57238a2008-05-02Martin Nilsson  array parts = argv[0]/"=";
6244262008-05-03Martin Nilsson  if( sizeof(parts[0]) && parts[0][-1]==opt[1] )
09be262007-09-16Martin Nilsson  {
57238a2008-05-02Martin Nilsson  if( sizeof(parts)==1 ) { // "-xxxy z" if(sizeof(argv)>1) {
6244262008-05-03Martin Nilsson  parts[0] -= opt[1..1];
57238a2008-05-02Martin Nilsson  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.
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
57238a2008-05-02Martin Nilsson  } else
09be262007-09-16Martin Nilsson  {
57238a2008-05-02Martin Nilsson  // "-xxxy=z"
6244262008-05-03Martin Nilsson  parts[0] -= opt[1..1];
09be262007-09-16Martin Nilsson  if( parts[0]=="-" ) argv[0] = 0; else argv[0] = parts[0];
57238a2008-05-02Martin Nilsson  return parts[1..]*"=";
09be262007-09-16Martin Nilsson  } } }
685c7c2013-06-03Martin Nilsson  return previous || ::get_value(argv, env, previous);
57238a2008-05-02Martin Nilsson  }
6244262008-05-03Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected string __sprintf()
6244262008-05-03Martin Nilsson  { return sprintf("Arg.HasOpt(%O)", opt); }
09be262007-09-16Martin Nilsson  }
57238a2008-05-02Martin Nilsson 
6244262008-05-03Martin Nilsson } // -- OptLibrary
09be262007-09-16Martin Nilsson 
35b78f2016-11-05Martin Nilsson protected class LookupKey { protected string name; protected void create(string name) { this::name = name; } protected string _sprintf(int t) { return t=='O' && "Arg."+name; } } LookupKey REST = LookupKey("REST"); LookupKey APP = LookupKey("APP"); LookupKey PATH = LookupKey("PATH");
1f901a2008-05-01Martin Nilsson 
d689282016-11-05Martin Nilsson //! @decl constant REST; //! //! Constant used to represent command line arguments not identified //! as options. Can be used both in the response mapping from @[parse] //! and when indexing an @[Options] object. //! @decl constant APP; //! //! Constant used to represent the name of the application from the //! command line. Can be used when indexing an @[Options] object. //! @decl constant PATH;
58f1652014-07-29Per Hedbor //!
d689282016-11-05Martin Nilsson //! Constant used to represent the full paht of the applicatiojn from //! the command line. Can be used when indexing an @[Options] object.
58f1652014-07-29Per Hedbor 
6244262008-05-03Martin Nilsson // FIXME: Support for rc files? ( Opt x = Opt("--x")|INIFile(path, name); ) // FIXME: Support for type casts? ( Opt level = Integer(Opt("--level"));
09be262007-09-16Martin Nilsson  class LowOptions
58f1652014-07-29Per Hedbor  //!
09be262007-09-16Martin Nilsson {
9eaf1d2008-06-28Martin Nilsson  protected inherit OptLibrary;
57238a2008-05-02Martin Nilsson 
58f1652014-07-29Per Hedbor  //!
9eaf1d2008-06-28Martin Nilsson  protected mapping(string:Opt) opts = ([]);
58f1652014-07-29Per Hedbor  //!
9eaf1d2008-06-28Martin Nilsson  protected mapping(string:int(1..1)|string) values = ([]);
58f1652014-07-29Per Hedbor  //!
9eaf1d2008-06-28Martin Nilsson  protected array(string) argv;
58f1652014-07-29Per Hedbor  //!
9eaf1d2008-06-28Martin Nilsson  protected string application;
09be262007-09-16Martin Nilsson 
58f1652014-07-29Per Hedbor  //!
9eaf1d2008-06-28Martin Nilsson  protected void create(array(string) _argv, void|mapping(string:string) env)
09be262007-09-16Martin Nilsson  { if(!env) env = getenv(); // Make a list of all the arguments we can parse.
cef7802016-12-06Henrik Grubbström (Grubba)  foreach(::_indices(this, 0), string index)
09be262007-09-16Martin Nilsson  {
6abaa02013-06-11Martin Nilsson  mixed val = ::`[](index, this, 0);
6244262008-05-03Martin Nilsson  if(objectp(val) && val->is_opt) opts[index]=val;
09be262007-09-16Martin Nilsson  }
4c19032008-05-03Martin Nilsson  application = _argv[0];
09be262007-09-16Martin Nilsson  argv = _argv[1..];
6244262008-05-03Martin Nilsson  mapping(string:Opt) unset = opts+([]);
57238a2008-05-02Martin Nilsson 
685c7c2013-06-03Martin Nilsson  while(sizeof(argv))
09be262007-09-16Martin Nilsson  {
685c7c2013-06-03Martin Nilsson  array(string) pre = argv+({}); foreach(opts; string index; Opt arg)
09be262007-09-16Martin Nilsson  {
eb5a5f2017-01-27Martin Nilsson  mixed value = arg->get_value(argv, env, values[index]); if(!undefinedp(value))
09be262007-09-16Martin Nilsson  { m_delete(unset, index); values[index] = value;
685c7c2013-06-03Martin Nilsson  argv -= ({ 0 });
09be262007-09-16Martin Nilsson  } }
685c7c2013-06-03Martin Nilsson  argv -= ({ 0 });
09be262007-09-16Martin Nilsson 
685c7c2013-06-03Martin Nilsson  if(equal(pre, argv)) { unhandled_argument(argv, env); if(equal(pre, argv)) break; }
09be262007-09-16Martin Nilsson  }
6244262008-05-03Martin Nilsson 
09be262007-09-16Martin Nilsson  if( sizeof(unset) ) {
eb5a5f2017-01-27Martin Nilsson  mixed value;
6244262008-05-03Martin Nilsson  foreach(unset; string index; Opt arg) {
685c7c2013-06-03Martin Nilsson  value = arg->get_value(({}), env, values[index]);
eb5a5f2017-01-27Martin Nilsson  if(!undefinedp(value))
6244262008-05-03Martin Nilsson  values[index] = value; }
09be262007-09-16Martin Nilsson  } }
2389922016-11-23Henrik Grubbström (Grubba)  protected string index(string i)
1f901a2008-05-01Martin Nilsson  {
7826f72016-11-30Henrik Grubbström (Grubba)  string s = ::`[](i, this, 1);
2389922016-11-23Henrik Grubbström (Grubba)  if( !s ) return 0; if( !stringp(s) ) error("%O is not a string.\n", i); if( sizeof(s) )
1f901a2008-05-01Martin Nilsson  {
2389922016-11-23Henrik Grubbström (Grubba)  return s;
1f901a2008-05-01Martin Nilsson  }
2389922016-11-23Henrik Grubbström (Grubba)  return 0;
ef0c792007-09-16Martin Nilsson  }
7826f72016-11-30Henrik Grubbström (Grubba)  protected void usage()
09be262007-09-16Martin Nilsson  { string s = index("help_pre"); if( s )
cf5b952016-11-22Henrik Grubbström (Grubba)  write( "%s\n", s );
09be262007-09-16Martin Nilsson 
2765cd2016-12-07Martin Nilsson  foreach(sort((array)opts), [string i, Opt opt])
09be262007-09-16Martin Nilsson  {
cf5b952016-11-22Henrik Grubbström (Grubba)  string opts = opt->get_opts()*", ";
09be262007-09-16Martin Nilsson  s = index(i+"_help");
cf5b952016-11-22Henrik Grubbström (Grubba)  if ((sizeof(opts) > 23) || !s) { write( "\t" + opts + "\n"); if (s) write("\t\t\t\t"); } else if (sizeof(opts) > 15) { write( "\t" + opts + "\t"); } else if (sizeof(opts) > 7) { write( "\t" + opts + "\t\t"); } else { write( "\t" + opts + "\t\t\t"); } if (s) { array(string) lines = s/"\n"; foreach(lines, string line) { if (sizeof(line) <= 46) { write("%s\n", line); } else { line = sprintf("%-46=s", line); array(string) a = line/"\n"; write("%s\n", a[0]); foreach(a[1..], string l) { write("\t\t\t\t%s\n", l); } } } }
09be262007-09-16Martin Nilsson  } s = index("help_post"); if( s )
cf5b952016-11-22Henrik Grubbström (Grubba)  write( "%s\n", s );
09be262007-09-16Martin Nilsson  }
2389922016-11-23Henrik Grubbström (Grubba)  //! protected int(0..1) unhandled_argument(array(string) argv, mapping(string:string) env)
09be262007-09-16Martin Nilsson  {
2389922016-11-23Henrik Grubbström (Grubba)  return 0; } //! protected mixed cast(string to) { switch( to )
09be262007-09-16Martin Nilsson  {
2389922016-11-23Henrik Grubbström (Grubba)  case "mapping": return values + ([ REST : argv ]); case "array": return argv;
09be262007-09-16Martin Nilsson  }
2389922016-11-23Henrik Grubbström (Grubba)  return UNDEFINED; } } //! The option parser class that contains all the argument objects. //! class Options { inherit LowOptions; //! Options that trigger help output. Opt help = NoOpt("--help");
7826f72016-11-30Henrik Grubbström (Grubba)  protected string help_help = "Help about usage.";
2389922016-11-23Henrik Grubbström (Grubba)  protected void create(array(string) argv, void|mapping(string:string) env) { ::create(argv, env); if (values->help) { usage(); } } protected mixed `[](mixed id) { if( id==REST ) return argv; if( id==PATH ) return application; if( id==APP ) return basename(application); return values[id]; }
5e39fb2016-12-02Henrik Grubbström (Grubba)  protected array _indices(object|void ctx, int|void access) { if (!access) {
cef7802016-12-06Henrik Grubbström (Grubba)  return ::_indices(this, access) + ({ REST, PATH, APP });
5e39fb2016-12-02Henrik Grubbström (Grubba)  } return ::_indices(ctx, access); } protected array _values(object|void ctx, int|void access) { if (!access) {
cef7802016-12-06Henrik Grubbström (Grubba)  return ::_values(this, access) +
5e39fb2016-12-02Henrik Grubbström (Grubba)  ({ argv, application, basename(application) }); } return ::_values(ctx, access); } protected array _types(object|void ctx, int|void access) { if (!access) { return map(predef::values(values) + ({ argv, application, basename(application) }), _typeof); } return ::_types(ctx, access); }
2389922016-11-23Henrik Grubbström (Grubba)  protected mixed `->(string id) { return values[id]; } //! protected int(0..1) unhandled_argument(array(string) argv, mapping(string:string) env) { if( !sizeof(argv) || argv[0]!="--help" ) return 0; if(!values->help) values->help=1;
09be262007-09-16Martin Nilsson  } } // --- Simple interface class SimpleOptions
58f1652014-07-29Per Hedbor //! Options parser with a unhandled_argument implementation that //! parses most common argument formats.
09be262007-09-16Martin Nilsson { inherit LowOptions;
58f1652014-07-29Per Hedbor  //! Handles arguments as described in @[Arg.parse]
09be262007-09-16Martin Nilsson  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, "--") ) {
1f901a2008-05-01Martin Nilsson  sscanf( arg, "--%s=%s", name, value ) || sscanf( arg, "--%s", name );
09be262007-09-16Martin Nilsson  if(!name) return 0; // arg == "--"
685c7c2013-06-03Martin Nilsson  if( value ) values[name] = value; else values[name]++;
1f901a2008-05-01Martin Nilsson  argv[0]=0;
09be262007-09-16Martin Nilsson  return 1; }
1f901a2008-05-01Martin Nilsson  sscanf( arg, "-%s=%s", name, value ) || sscanf( arg, "-%s", name );
9f67e72008-05-01Martin Nilsson  if( !name || !sizeof(name) ) return 0;
09be262007-09-16Martin Nilsson  foreach( name/1; int pos; string c )
685c7c2013-06-03Martin Nilsson  { if( value && pos == sizeof(name)-1 ) values[c] = value;
09be262007-09-16Martin Nilsson  else
685c7c2013-06-03Martin Nilsson  values[c]++; }
1f901a2008-05-01Martin Nilsson  argv[0]=0;
09be262007-09-16Martin Nilsson  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
685c7c2013-06-03Martin Nilsson // --foo - --bar -> "foo":1 // --foo x --bar -> "foo":1 (?) // -foo -> "f":1,"o":2 // -x -x -x -> "x":3
09be262007-09-16Martin Nilsson // // void main(int n, array argv) // {
6244262008-05-03Martin Nilsson // mapping opts = Arg.parse(argv); // argv = opts[Arg.REST];
09be262007-09-16Martin Nilsson // }
58f1652014-07-29Per Hedbor //! 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)
09be262007-09-16Martin Nilsson { return (mapping)SimpleOptions(argv); }