--- toast	2003/09/01 22:25:59	1.190
+++ toast	2003/09/02 03:31:06	1.191
@@ -158,6 +158,7 @@
     "armdir" => superuser ? "/usr/local" : "armed",
     "username" => "toast",
     "postarmprog" => superuser ? "/sbin/ldconfig" : "",
+    "defaultcmd" => "help",
     "verbose" => true,
     "autofind" => true,
     "autochange" => true,
@@ -207,6 +208,12 @@
     my($name) = @_;
     return isopt($name) && isboolean($optdefault{$name});
   }
+  
+  sub checkoptname($)
+  {
+    my($name) = @_;
+    isopt($name) || error("no such option: $name");
+  }
 
   sub loadopt($)
   {
@@ -242,7 +249,7 @@
   sub setopt($$)
   {
     my($name, $val) = @_;
-    exists($optdefault{$name}) || error("no such option: $name");
+    checkoptname($name);
     if(isboolopt($name))
     {
       error("$name is a boolean option") unless isboolean($val);
@@ -3538,48 +3545,66 @@
 
 ##############################################################################
 
-sub nocmd() { help; }
-
-sub defaultcmd(@)
+sub badcmd(@)
 {
+  return &cmd(defaultcmd, @_) if defaultcmd ne "help" && iscmd(defaultcmd);
   my($cmd) = @_;
   select(STDERR);
   help;
-  error("no such command: $cmd");
+  error("bad defaultcmd: " . defaultcmd) if defaultcmd ne "help";
+  error("no such command: $cmd") if defined($cmd);
+  error("command expected");
 }
 
+sub iscmd($)
+{
+  my($cmd) = @_;
+  !!(getsub("cmd_$cmd") || getsub("parse_$cmd"));
+}
+
 sub parseopts(@)
 {
-  my($opt);
+  my($opt, $cmd);
   while(($opt = shift) && $opt =~ /^-/ && $opt ne "--")
   {
+    $opt = "--help" if $opt =~ /^--?[h\?]$/i;
     if($opt =~ /^--?(\w+)=(.*)$/)
     {
-      setopt($1, $2);
+      setopt(lc($1), $2);
     }
-    elsif($opt =~ /^--?no(\w+)$/)
+    elsif($opt =~ /^--?no(\w+)$/i)
     {
-      setopt($1, false);
+      setopt(lc($1), false);
     }
     elsif($opt =~ /^--?(\w+)$/)
     {
-      my($name, $val) = ($1, @_);
-      if(isboolopt($name) && !(defined($val) && isboolean($val)))
+      my($name, $val) = (lc($1), @_);
+      if(iscmd($name))
       {
-        setopt($name, true);
+        error("conflicting command options: $cmd and $name")
+            if $cmd && $cmd ne $name;
+        $cmd = $name;
       }
-      elsif(!defined($val))
-      {
-        error("option $name requires an argument");
-      }
-      elsif($val =~ /^-/)
-      {
-        error("option $name requires an argument; found \"$val\" instead");
-      }
       else
       {
-        setopt($1, $val);
-        shift;
+        checkoptname($name);
+        if(isboolopt($name) && !(defined($val) && isboolean($val)))
+        {
+          setopt($name, true);
+        }
+        elsif(!defined($val))
+        {
+          error("option $name requires an argument");
+        }
+        elsif($val =~ /^-/)
+        {
+          error("option $name requires an argument; found \"$val\" instead");
+        }
+        else
+        {
+          setopt($name, $val);
+          shift;
+        }
       }
     }
     else
@@ -3587,18 +3612,22 @@
       error("unable to parse option: \"$opt\"");
     }
   }
-  return defined($opt) && $opt eq "--" ? @_ : ($opt, @_);
+
+  my(@result) = @_;
+  unshift(@result, $opt) if defined($opt) && $opt ne "--";
+  unshift(@result, $cmd) if $cmd;
+  return @result;
 }
 
 sub cmd(@)
 {
-  return nocmd unless @_;
-  my($cmd, @args) = parseopts(@_);
-  error("no arguments found after options") unless defined($cmd);
+  my($cmd, @args) = @_;
+  return badcmd unless defined($cmd);
+  $cmd = lc($cmd);
   my($cmdsub) = getsub("cmd_$cmd");
   return &$cmdsub(@args) if $cmdsub;
   my($parser) = getsub("parse_$cmd");
-  return defaultcmd($cmd, @args) unless $parser;
+  return badcmd(@_) unless $parser;
   my($doer) = getsub($cmd);
   my($result) = true;
   my($pid) = $$;
@@ -3615,12 +3644,19 @@
   return $result;
 }
 
+sub cmdline(@)
+{
+  my(@argv) = @_;
+  return help unless @argv;
+  return cmd(parseopts(@argv));
+}
+
 ##############################################################################
 
 sub main(@)
 {
   $| = 1;
-  my($result) = cmd(@_) ? 0 : 1;
+  my($result) = cmdline(@_) ? 0 : 1;
 warn("$myname: returning failure ($result)") if $result != 0;
   close(STDOUT) || error("close stdout: $!");
   exit($result);
@@ -3783,7 +3819,7 @@
 
 =head1 SYNOPSIS
 
-B<toast> S<[ I<OPTION> ... ]> I<COMMAND> S<[ I<ARGUMENT> ... ]>
+B<toast> S<[ I<OPTION> ... ]> S<[ I<COMMAND> ]> S<[ I<ARGUMENT> ... ]>
 
 =head1 DESCRIPTION
 
@@ -4094,6 +4130,17 @@
 the command will also fail.  Default: C</sbin/ldconfig> if invoked by
 root, empty string otherwise.
 
+=item B<--defaultcmd=>I<COMMAND>
+
+Sets an implicit command to be assumed if B<toast> is invoked with
+at least one command-line option or argument but no explicit command.
+I<COMMAND> may be the name of any valid toast command.  As a special
+case, the value C<help> causes B<toast> to print an error message and
+a list of valid commands if no explicit command is given.  Note that
+invoking B<toast> without command-line options or arguments is always
+equivalent to running B<toast help>, regardless of this option's setting.
+Default: C<help>.
+
 =item S<B<--verbose> | B<--noverbose>>
 
 Enables or disables verbose command output.  When disabled, most commands
@@ -4297,6 +4344,12 @@
 this document for the specific default value used for each option.
 
 =back
+
+Any I<COMMAND> can also be written as if it were a command-line option by
+preceding it with one or two dashes.  For example, S<B<toast --help>> and
+S<B<toast help>> mean the same thing.  Commands do not behave like options
+in the environment or the configuration file, but see the B<defaultcmd>
+option above for an alternative.
 
 =head1 AUTHOR