+sub addCmdHook {
+ my ($hashname, $ident, %hash) = @_;
+
+ if (exists ${"hooks_$hashname"}{$ident}) {
+### &WARN("aCH: cmd hooks \%$hashname{$ident} already exists.");
+ return;
+ }
+
+ &VERB("aCH: added $ident",2); # use $hash{'Identifier'}?
+ ### hrm... prevent warnings?
+ ${"hooks_$hashname"}{$ident} = \%hash;
+}
+
+# RUN IF ADDRESSED.
+sub parseCmdHook {
+ my ($hashname, $line) = @_;
+ $line =~ s/^\s+|\s+$//g; # again.
+ $line =~ /^(\S+)(\s+(.*))?$/;
+ my $cmd = $1; # command name is whitespaceless.
+ my $flatarg = $3;
+ my @args = split(/\s+/, $flatarg || '');
+ my $done = 0;
+
+ &shmFlush();
+
+ if (!defined %{"hooks_$hashname"}) {
+ &WARN("cmd hooks \%$hashname does not exist.");
+ return 0;
+ }
+
+ if (!defined $cmd) {
+ &WARN("cstubs: cmd == NULL.");
+ return 0;
+ }
+
+ foreach (keys %{"hooks_$hashname"}) {
+ # rename to something else! like $id or $label?
+ my $ident = $_;
+
+ next unless ($cmd =~ /^$ident$/i);
+
+ if ($done) {
+ &WARN("pCH: Multiple hook match: $ident");
+ next;
+ }
+
+ &status("hooks($hashname): $cmd matched '$ident' '$flatarg'");
+ my %hash = %{ ${"hooks_$hashname"}{$ident} };
+
+ if (!scalar keys %hash) {
+ &WARN("CmdHook: hash is NULL?");
+ return 1;
+ }
+
+ if ($hash{NoArgs} and $flatarg) {
+ &DEBUG("cmd $ident does not take args ('$flatarg'); skipping.");
+ next;
+ }
+
+ if (!exists $hash{CODEREF}) {
+ &ERROR("CODEREF undefined for $cmd or $ident.");
+ return 1;
+ }
+
+ ### DEBUG.
+ foreach (keys %hash) {
+ &VERB(" $cmd->$_ => '$hash{$_}'.",2);
+ }
+
+ ### HELP.
+ if (exists $hash{'Help'} and !scalar(@args)) {
+ &help( $hash{'Help'} );
+ return 1;
+ }
+
+ ### IDENTIFIER.
+ if (exists $hash{'Identifier'}) {
+ return 1 unless (&hasParam($hash{'Identifier'}));
+ }
+
+ ### USER FLAGS.
+ if (exists $hash{'UserFlag'}) {
+ return 1 unless (&hasFlag($hash{'UserFlag'}));
+ }
+
+ ### FORKER,IDENTIFIER,CODEREF.
+ if (exists $hash{'Forker'}) {
+ $hash{'Identifier'} .= "-" if ($hash{'Forker'} eq "NULL");
+
+ if (exists $hash{'ArrayArgs'}) {
+ &Forker($hash{'Identifier'}, sub { \&{ $hash{'CODEREF'} }(@args) } );
+ } else {
+ &Forker($hash{'Identifier'}, sub { \&{ $hash{'CODEREF'} }($flatarg) } );
+ }
+
+ } else {
+ if (exists $hash{'Module'}) {
+ &loadMyModule($myModules{ $hash{'Module'} });
+ }
+
+ # check if CODEREF exists.
+ if (!defined &{ $hash{'CODEREF'} }) {
+ &WARN("coderef $hash{'CODEREF'} does not exist.");
+ if (defined $who) {
+ &msg($who, "coderef does not exist for $ident.");
+ }
+
+ return 1;
+ }
+
+ if (exists $hash{'ArrayArgs'}) {
+ &{ $hash{'CODEREF'} }(@args);
+ } else {
+ &{ $hash{'CODEREF'} }($flatarg);
+ }
+ }
+
+ ### CMDSTATS.
+ if (exists $hash{'Cmdstats'}) {
+ $cmdstats{ $hash{'Cmdstats'} }++;
+ }
+
+ &VERB("hooks: End of command.",2);
+
+ $done = 1;
+ }
+
+ return 1 if ($done);
+ return 0;
+}
+
+###
+### START ADDING HOOKS.
+###
+&addCmdHook("extra", 'd?bugs', ('CODEREF' => 'DBugs::Parse',
+ 'Forker' => 1, 'Identifier' => 'debianExtra',
+ 'Cmdstats' => 'Debian Bugs') );
+&addCmdHook("extra", 'dauthor', ('CODEREF' => 'Debian::searchAuthor',
+ 'Forker' => 1, 'Identifier' => 'debian',
+ 'Cmdstats' => 'Debian Author Search', 'Help' => "dauthor" ) );
+&addCmdHook("extra", '(d|search)desc', ('CODEREF' => 'Debian::searchDescFE',
+ 'Forker' => 1, 'Identifier' => 'debian',
+ 'Cmdstats' => 'Debian Desc Search', 'Help' => "ddesc" ) );
+&addCmdHook("extra", 'dnew', ('CODEREF' => 'DebianNew',
+ 'Identifier' => 'debian' ) );
+&addCmdHook("extra", 'dincoming', ('CODEREF' => 'Debian::generateIncoming',
+ 'Forker' => 1, 'Identifier' => 'debian' ) );
+&addCmdHook("extra", 'dstats', ('CODEREF' => 'Debian::infoStats',
+ 'Forker' => 1, 'Identifier' => 'debian',
+ 'Cmdstats' => 'Debian Statistics' ) );
+&addCmdHook("extra", 'd?contents', ('CODEREF' => 'Debian::searchContents',
+ 'Forker' => 1, 'Identifier' => 'debian',
+ 'Cmdstats' => 'Debian Contents Search', 'Help' => "contents" ) );
+&addCmdHook("extra", 'd?find', ('CODEREF' => 'Debian::DebianFind',
+ 'Forker' => 1, 'Identifier' => 'debian',
+ 'Cmdstats' => 'Debian Search', 'Help' => "find" ) );
+#&addCmdHook("extra", 'insult', ('CODEREF' => 'Insult::Insult',
+# 'Forker' => 1, 'Identifier' => 'insult', 'Help' => "insult" ) );
+&addCmdHook("extra", 'kernel', ('CODEREF' => 'Kernel::Kernel',
+ 'Forker' => 1, 'Identifier' => 'kernel',
+ 'Cmdstats' => 'Kernel', 'NoArgs' => 1) );
+&addCmdHook("extra", 'listauth', ('CODEREF' => 'CmdListAuth',
+ 'Identifier' => 'search', Module => 'factoids',
+ 'Help' => 'listauth') );
+&addCmdHook("extra", 'quote', ('CODEREF' => 'Quote::Quote',
+ 'Forker' => 1, 'Identifier' => 'quote',
+ 'Help' => 'quote', 'Cmdstats' => 'Quote') );
+&addCmdHook("extra", 'countdown', ('CODEREF' => 'Countdown',
+ 'Module' => 'countdown', 'Identifier' => 'countdown',
+ 'Cmdstats' => 'Countdown') );
+&addCmdHook("extra", 'lart', ('CODEREF' => 'lart',
+ 'Identifier' => 'lart', 'Help' => 'lart') );
+&addCmdHook("extra", 'convert', ('CODEREF' => 'convert',
+ 'Forker' => 1, 'Identifier' => 'units',
+ 'Help' => 'convert') );
+&addCmdHook("extra", '(cookie|random)', ('CODEREF' => 'cookie',
+ 'Forker' => 1, 'Identifier' => 'factoids') );
+&addCmdHook("extra", 'u(ser)?info', ('CODEREF' => 'userinfo',
+ 'Identifier' => 'userinfo', 'Help' => 'userinfo',
+ 'Module' => 'userinfo') );
+&addCmdHook("extra", 'rootWarn', ('CODEREF' => 'CmdrootWarn',
+ 'Identifier' => 'rootWarn', 'Module' => 'rootwarn') );
+&addCmdHook("extra", 'seen', ('CODEREF' => 'seen', 'Identifier' =>
+ 'seen') );
+&addCmdHook("extra", 'dict', ('CODEREF' => 'Dict::Dict',
+ 'Identifier' => 'dict', 'Help' => 'dict',
+ 'Forker' => 1, 'Cmdstats' => 'Dict') );
+&addCmdHook("extra", 'slashdot', ('CODEREF' => 'Slashdot::Slashdot',
+ 'Identifier' => 'slashdot', 'Forker' => 1,
+ 'Cmdstats' => 'Slashdot') );
+&addCmdHook("extra", 'plug', ('CODEREF' => 'Plug::Plug',
+ 'Identifier' => 'plug', 'Forker' => 1,
+ 'Cmdstats' => 'Plug') );
+&addCmdHook("extra", 'uptime', ('CODEREF' => 'uptime', 'Identifier' => 'uptime',
+ 'Cmdstats' => 'Uptime') );
+&addCmdHook("extra", 'nullski', ('CODEREF' => 'nullski', ) );
+&addCmdHook("extra", 'verstats', ('CODEREF' => 'do_verstats' ) );
+&addCmdHook("extra", 'weather', ('CODEREF' => 'Weather::Weather',
+ 'Identifier' => 'weather', 'Help' => 'weather',
+ 'Cmdstats' => 'weather', 'Forker' => 1) );
+&addCmdHook("extra", 'bzflist', ('CODEREF' => 'BZFlag::list',
+ 'Identifier' => 'bzflag', 'Cmdstats' => 'BZFlag',
+ 'Forker' => 1) );
+&addCmdHook("extra", 'bzfquery', ('CODEREF' => 'BZFlag::query',
+ 'Identifier' => 'bzflag', 'Cmdstats' => 'BZFlag',
+ 'Forker' => 1, 'Help' => 'bzflag') );
+&addCmdHook("extra", 'zfi', ('CODEREF' => 'zfi::query',
+ 'Identifier' => 'zfi', 'Cmdstats' => 'zfi',
+ 'Forker' => 1) );
+&addCmdHook("extra", '(zippy|yow)', ('CODEREF' => 'zippy::get',
+ 'Identifier' => 'zippy', 'Cmdstats' => 'zippy',
+ 'Forker' => 1) );
+&addCmdHook("extra", 'zsi', ('CODEREF' => 'zsi::query',
+ 'Identifier' => 'zsi', 'Cmdstats' => 'zsi',
+ 'Forker' => 1) );
+&addCmdHook("extra", '(ex)?change', ('CODEREF' => 'Exchange::query',
+ 'Identifier' => 'exchange', 'Cmdstats' => 'exchange',
+ 'Forker' => 1) );
+&addCmdHook("extra", '(botmail|message)', ('CODEREF' => 'botmail::parse',
+ 'Identifier' => 'botmail', 'Cmdstats' => 'botmail') );
+&addCmdHook("extra", 'httpdtype', ('CODEREF' => 'HTTPDtype::HTTPDtype',
+ 'Identifier' => 'httpdtype', 'Cmdstats' => 'httpdtype',
+ 'Forker' => 1) );
+
+###
+### END OF ADDING HOOKS.
+###
+&status("CMD: loaded ".scalar(keys %hooks_extra)." EXTRA command hooks.");
+