dw_fluffos_v1/
dw_fluffos_v1/fluffos-1.22c11/
dw_fluffos_v1/fluffos-1.22c11/ChangeLog.old/
dw_fluffos_v1/fluffos-1.22c11/Win32/
dw_fluffos_v1/fluffos-1.22c11/compat/
dw_fluffos_v1/fluffos-1.22c11/compat/simuls/
dw_fluffos_v1/fluffos-1.22c11/include/
dw_fluffos_v1/fluffos-1.22c11/mudlib/
dw_fluffos_v1/fluffos-1.22c11/testsuite/
dw_fluffos_v1/fluffos-1.22c11/testsuite/clone/
dw_fluffos_v1/fluffos-1.22c11/testsuite/command/
dw_fluffos_v1/fluffos-1.22c11/testsuite/data/
dw_fluffos_v1/fluffos-1.22c11/testsuite/etc/
dw_fluffos_v1/fluffos-1.22c11/testsuite/include/
dw_fluffos_v1/fluffos-1.22c11/testsuite/inherit/
dw_fluffos_v1/fluffos-1.22c11/testsuite/inherit/master/
dw_fluffos_v1/fluffos-1.22c11/testsuite/log/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/tests/compiler/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/tests/efuns/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/tests/operators/
dw_fluffos_v1/fluffos-1.22c11/testsuite/u/
dw_fluffos_v1/fluffos-1.22c11/tmp/
dw_fluffos_v1/lib/
dw_fluffos_v1/lib/binaries/cmds/
dw_fluffos_v1/lib/binaries/cmds/creator/
dw_fluffos_v1/lib/binaries/cmds/living/
dw_fluffos_v1/lib/binaries/cmds/player/
dw_fluffos_v1/lib/binaries/d/admin/obj/
dw_fluffos_v1/lib/binaries/d/liaison/
dw_fluffos_v1/lib/binaries/global/virtual/
dw_fluffos_v1/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v1/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v1/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v1/lib/binaries/obj/misc/
dw_fluffos_v1/lib/binaries/obj/misc/buckets/
dw_fluffos_v1/lib/binaries/obj/monster/
dw_fluffos_v1/lib/binaries/obj/reactions/
dw_fluffos_v1/lib/binaries/obj/reagents/
dw_fluffos_v1/lib/binaries/secure/cmds/creator/
dw_fluffos_v1/lib/binaries/secure/master/
dw_fluffos_v1/lib/binaries/std/
dw_fluffos_v1/lib/binaries/std/dom/
dw_fluffos_v1/lib/binaries/std/effects/object/
dw_fluffos_v1/lib/binaries/std/guilds/
dw_fluffos_v1/lib/binaries/std/languages/
dw_fluffos_v1/lib/binaries/std/races/
dw_fluffos_v1/lib/binaries/std/room/
dw_fluffos_v1/lib/binaries/std/room/basic/
dw_fluffos_v1/lib/binaries/std/shops/
dw_fluffos_v1/lib/binaries/std/shops/inherit/
dw_fluffos_v1/lib/binaries/www/
dw_fluffos_v1/lib/cmds/guild-race/
dw_fluffos_v1/lib/cmds/guild-race/crafts/
dw_fluffos_v1/lib/cmds/guild-race/other/
dw_fluffos_v1/lib/cmds/playtester/
dw_fluffos_v1/lib/cmds/playtester/senior/
dw_fluffos_v1/lib/d/admin/
dw_fluffos_v1/lib/d/admin/log/
dw_fluffos_v1/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v1/lib/d/admin/meetings/
dw_fluffos_v1/lib/d/admin/obj/
dw_fluffos_v1/lib/d/admin/room/we_care/
dw_fluffos_v1/lib/d/admin/save/
dw_fluffos_v1/lib/d/dist/
dw_fluffos_v1/lib/d/dist/mtf/
dw_fluffos_v1/lib/d/dist/pumpkin/
dw_fluffos_v1/lib/d/dist/pumpkin/chars/
dw_fluffos_v1/lib/d/dist/pumpkin/desert/
dw_fluffos_v1/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v1/lib/d/dist/pumpkin/hospital/
dw_fluffos_v1/lib/d/dist/pumpkin/inherit/
dw_fluffos_v1/lib/d/dist/pumpkin/map/
dw_fluffos_v1/lib/d/dist/pumpkin/plain/
dw_fluffos_v1/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v1/lib/d/dist/pumpkin/save/
dw_fluffos_v1/lib/d/dist/pumpkin/squash/
dw_fluffos_v1/lib/d/dist/pumpkin/terrain/
dw_fluffos_v1/lib/d/dist/pumpkin/woods/
dw_fluffos_v1/lib/d/dist/start/
dw_fluffos_v1/lib/d/learning/TinyTown/buildings/
dw_fluffos_v1/lib/d/learning/TinyTown/map/
dw_fluffos_v1/lib/d/learning/TinyTown/roads/
dw_fluffos_v1/lib/d/learning/add_command/
dw_fluffos_v1/lib/d/learning/arms_and_weps/
dw_fluffos_v1/lib/d/learning/chars/
dw_fluffos_v1/lib/d/learning/cutnpaste/
dw_fluffos_v1/lib/d/learning/examples/npcs/
dw_fluffos_v1/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v1/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v1/lib/d/learning/functions/
dw_fluffos_v1/lib/d/learning/handlers/
dw_fluffos_v1/lib/d/learning/help_topics/npcs/
dw_fluffos_v1/lib/d/learning/help_topics/objects/
dw_fluffos_v1/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v1/lib/d/learning/help_topics/rooms/
dw_fluffos_v1/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v1/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v1/lib/d/learning/items/
dw_fluffos_v1/lib/d/learning/save/
dw_fluffos_v1/lib/d/liaison/
dw_fluffos_v1/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v1/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v1/lib/db/
dw_fluffos_v1/lib/doc/
dw_fluffos_v1/lib/doc/creator/
dw_fluffos_v1/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v1/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v1/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v1/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v1/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v1/lib/doc/creator/autodoc/std/key/
dw_fluffos_v1/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v1/lib/doc/creator/autodoc/std/map/
dw_fluffos_v1/lib/doc/creator/autodoc/std/race/
dw_fluffos_v1/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v1/lib/doc/creator/files/
dw_fluffos_v1/lib/doc/creator/policy/
dw_fluffos_v1/lib/doc/creator/room/
dw_fluffos_v1/lib/doc/effects/
dw_fluffos_v1/lib/doc/ideas/
dw_fluffos_v1/lib/doc/known_command/
dw_fluffos_v1/lib/doc/lpc/basic_manual/
dw_fluffos_v1/lib/doc/lpc/intermediate/
dw_fluffos_v1/lib/doc/new/add_command/
dw_fluffos_v1/lib/doc/new/handlers/
dw_fluffos_v1/lib/doc/new/living/
dw_fluffos_v1/lib/doc/new/living/race/
dw_fluffos_v1/lib/doc/new/living/spells/
dw_fluffos_v1/lib/doc/new/player/
dw_fluffos_v1/lib/doc/new/room/guild/
dw_fluffos_v1/lib/doc/new/room/outside/
dw_fluffos_v1/lib/doc/new/room/storeroom/
dw_fluffos_v1/lib/doc/object/
dw_fluffos_v1/lib/doc/playtesters/
dw_fluffos_v1/lib/doc/policy/
dw_fluffos_v1/lib/doc/weapons/
dw_fluffos_v1/lib/global/handlers/
dw_fluffos_v1/lib/global/virtual/setup_compiler/
dw_fluffos_v1/lib/include/
dw_fluffos_v1/lib/include/cmds/
dw_fluffos_v1/lib/include/effects/
dw_fluffos_v1/lib/include/npc/
dw_fluffos_v1/lib/include/shops/
dw_fluffos_v1/lib/net/daemon/chars/
dw_fluffos_v1/lib/net/inherit/
dw_fluffos_v1/lib/net/intermud3/
dw_fluffos_v1/lib/net/intermud3/services/
dw_fluffos_v1/lib/net/obj/
dw_fluffos_v1/lib/net/save/
dw_fluffos_v1/lib/net/smnmp/
dw_fluffos_v1/lib/net/snmp/
dw_fluffos_v1/lib/obj/amulets/
dw_fluffos_v1/lib/obj/b_day/
dw_fluffos_v1/lib/obj/examples/
dw_fluffos_v1/lib/obj/food/alcohol/
dw_fluffos_v1/lib/obj/food/chocolates/
dw_fluffos_v1/lib/obj/food/fruits/
dw_fluffos_v1/lib/obj/food/meat/
dw_fluffos_v1/lib/obj/food/nuts/
dw_fluffos_v1/lib/obj/food/seafood/
dw_fluffos_v1/lib/obj/food/vegetables/
dw_fluffos_v1/lib/obj/fungi/
dw_fluffos_v1/lib/obj/furnitures/artwork/
dw_fluffos_v1/lib/obj/furnitures/bathroom/
dw_fluffos_v1/lib/obj/furnitures/beds/
dw_fluffos_v1/lib/obj/furnitures/cabinets/
dw_fluffos_v1/lib/obj/furnitures/chairs/
dw_fluffos_v1/lib/obj/furnitures/chests/
dw_fluffos_v1/lib/obj/furnitures/clocks/
dw_fluffos_v1/lib/obj/furnitures/crockery/
dw_fluffos_v1/lib/obj/furnitures/cupboards/
dw_fluffos_v1/lib/obj/furnitures/cushions/
dw_fluffos_v1/lib/obj/furnitures/fake_plants/
dw_fluffos_v1/lib/obj/furnitures/lamps/
dw_fluffos_v1/lib/obj/furnitures/mirrors/
dw_fluffos_v1/lib/obj/furnitures/outdoor/
dw_fluffos_v1/lib/obj/furnitures/safes/
dw_fluffos_v1/lib/obj/furnitures/shelves/
dw_fluffos_v1/lib/obj/furnitures/sideboards/
dw_fluffos_v1/lib/obj/furnitures/sofas/
dw_fluffos_v1/lib/obj/furnitures/stoves/
dw_fluffos_v1/lib/obj/furnitures/tables/
dw_fluffos_v1/lib/obj/furnitures/wardrobes/
dw_fluffos_v1/lib/obj/handlers/
dw_fluffos_v1/lib/obj/handlers/autodoc/
dw_fluffos_v1/lib/obj/jewellery/anklets/
dw_fluffos_v1/lib/obj/jewellery/bracelets/
dw_fluffos_v1/lib/obj/jewellery/earrings/
dw_fluffos_v1/lib/obj/jewellery/misc/
dw_fluffos_v1/lib/obj/jewellery/necklaces/
dw_fluffos_v1/lib/obj/jewellery/rings/
dw_fluffos_v1/lib/obj/media/
dw_fluffos_v1/lib/obj/misc/buckets/
dw_fluffos_v1/lib/obj/misc/jars/
dw_fluffos_v1/lib/obj/misc/papers/
dw_fluffos_v1/lib/obj/misc/player_shop/
dw_fluffos_v1/lib/obj/misc/shops/
dw_fluffos_v1/lib/obj/misc/traps/
dw_fluffos_v1/lib/obj/monster/
dw_fluffos_v1/lib/obj/monster/godmother/
dw_fluffos_v1/lib/obj/monster/transport/
dw_fluffos_v1/lib/obj/plants/inherit/
dw_fluffos_v1/lib/obj/potions/
dw_fluffos_v1/lib/open/boards/
dw_fluffos_v1/lib/save/autodoc/
dw_fluffos_v1/lib/save/bank_accounts/
dw_fluffos_v1/lib/save/boards/frog/
dw_fluffos_v1/lib/save/books/bed_catalog/
dw_fluffos_v1/lib/save/creators/
dw_fluffos_v1/lib/save/mail/
dw_fluffos_v1/lib/save/mail/p/
dw_fluffos_v1/lib/save/newsrc/b/
dw_fluffos_v1/lib/save/newsrc/c/
dw_fluffos_v1/lib/save/newsrc/d/
dw_fluffos_v1/lib/save/newsrc/f/
dw_fluffos_v1/lib/save/newsrc/p/
dw_fluffos_v1/lib/save/newsrc/s/
dw_fluffos_v1/lib/save/newsrc/w/
dw_fluffos_v1/lib/save/players/c/
dw_fluffos_v1/lib/save/players/d/
dw_fluffos_v1/lib/save/players/g/
dw_fluffos_v1/lib/save/players/p/
dw_fluffos_v1/lib/save/players/s/
dw_fluffos_v1/lib/save/soul/data/
dw_fluffos_v1/lib/save/tasks/
dw_fluffos_v1/lib/save/vaults/
dw_fluffos_v1/lib/secure/cmds/lord/
dw_fluffos_v1/lib/secure/config/
dw_fluffos_v1/lib/secure/items/
dw_fluffos_v1/lib/secure/player/
dw_fluffos_v1/lib/soul/
dw_fluffos_v1/lib/soul/i/
dw_fluffos_v1/lib/soul/j/
dw_fluffos_v1/lib/soul/k/
dw_fluffos_v1/lib/soul/o/
dw_fluffos_v1/lib/soul/q/
dw_fluffos_v1/lib/soul/to_approve/
dw_fluffos_v1/lib/soul/u/
dw_fluffos_v1/lib/soul/v/
dw_fluffos_v1/lib/soul/wish_list/
dw_fluffos_v1/lib/soul/y/
dw_fluffos_v1/lib/soul/z/
dw_fluffos_v1/lib/std/creator/
dw_fluffos_v1/lib/std/effects/
dw_fluffos_v1/lib/std/effects/attached/
dw_fluffos_v1/lib/std/effects/external/
dw_fluffos_v1/lib/std/effects/fighting/
dw_fluffos_v1/lib/std/effects/other/
dw_fluffos_v1/lib/std/environ/
dw_fluffos_v1/lib/std/guilds/
dw_fluffos_v1/lib/std/hospital/
dw_fluffos_v1/lib/std/house/
dw_fluffos_v1/lib/std/house/onebedhouse/
dw_fluffos_v1/lib/std/house/onebedhut/
dw_fluffos_v1/lib/std/house/tworoomflat/
dw_fluffos_v1/lib/std/languages/
dw_fluffos_v1/lib/std/liquids/
dw_fluffos_v1/lib/std/nationality/
dw_fluffos_v1/lib/std/nationality/accents/
dw_fluffos_v1/lib/std/nationality/accents/national/
dw_fluffos_v1/lib/std/nationality/accents/regional/
dw_fluffos_v1/lib/std/npc/goals/
dw_fluffos_v1/lib/std/npc/goals/basic/
dw_fluffos_v1/lib/std/npc/goals/misc/
dw_fluffos_v1/lib/std/npc/inherit/
dw_fluffos_v1/lib/std/npc/plans/
dw_fluffos_v1/lib/std/npc/plans/basic/
dw_fluffos_v1/lib/std/outsides/
dw_fluffos_v1/lib/std/races/shadows/
dw_fluffos_v1/lib/std/room/basic/topography/
dw_fluffos_v1/lib/std/room/controller/
dw_fluffos_v1/lib/std/room/controller/topography/
dw_fluffos_v1/lib/std/room/furniture/games/
dw_fluffos_v1/lib/std/room/furniture/inherit/
dw_fluffos_v1/lib/std/room/inherit/carriage/
dw_fluffos_v1/lib/std/room/inherit/topography/
dw_fluffos_v1/lib/std/room/punishments/
dw_fluffos_v1/lib/std/room/topography/area/
dw_fluffos_v1/lib/std/room/topography/iroom/
dw_fluffos_v1/lib/std/room/topography/milestone/
dw_fluffos_v1/lib/std/shadows/
dw_fluffos_v1/lib/std/shadows/attached/
dw_fluffos_v1/lib/std/shadows/curses/
dw_fluffos_v1/lib/std/shadows/disease/
dw_fluffos_v1/lib/std/shadows/fighting/
dw_fluffos_v1/lib/std/shadows/room/
dw_fluffos_v1/lib/std/shops/controllers/
dw_fluffos_v1/lib/std/shops/objs/
dw_fluffos_v1/lib/std/shops/player_shop/
dw_fluffos_v1/lib/std/shops/player_shop/office_code/
dw_fluffos_v1/lib/std/socket/
dw_fluffos_v1/lib/www/
dw_fluffos_v1/lib/www/external/autodoc/
dw_fluffos_v1/lib/www/external/java/telnet/Documentation/
dw_fluffos_v1/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v1/lib/www/external/java/telnet/examples/
dw_fluffos_v1/lib/www/external/java/telnet/tools/
dw_fluffos_v1/lib/www/pics/
dw_fluffos_v1/lib/www/secure/creator/
dw_fluffos_v1/lib/www/secure/editors/
dw_fluffos_v1/lib/www/secure/survey_results/
/*  -*- LPC -*-  */
/*
 * $Locker: pinkfish $
 * $Id: help.c,v 1.43 2003/02/19 08:58:20 ceres Exp pinkfish $
 *
 */

/**
 * Does all the help stuff you know and love.
 *
 * @author Pinkfish
 * @started Tue Nov  4 14:55:39 EST 1997
 */
inherit "/cmds/base";

#include <nroff.h>
#include <soul.h>
#include <log.h>
#include <autodoc.h>
#include <login_handler.h>
#include <player_handler.h>
#include <command.h>

#define SYNONYMS "/doc/SYNONYMS"
#define MATCH_THRESHOLD 55

void do_help(mixed *stuff);
private void list_help(string title, string dir);
private mixed *help_list(string name);
int command_cmd(string name);
int here_cmd();
int object_cmd(object ob);
int spell_cmd(string name, int spell);
int file_help(string name);
void rehash_dirs();
mixed *query_help_on(string name, int creator, int, int);
mixed *create_help_files(string *names, string nroff_dir);
private mapping read_directories(string *directories, int recurse);
private mapping read_synonyms();
private string letter_name(int letter, mixed *things);
private string start_letter( mixed *things );
private string end_letter( mixed *things );
private int query_number_from_string(string name, mixed *things );

private nosave mapping _help_files_player;
private nosave mapping _help_files_creator;
private nosave mapping _help_files_playtester;
private nosave mapping _help_files_lord;
private nosave mapping _synonyms;
private nosave string *_player_dirs;
private nosave string *_creator_dirs;
private nosave string *_lord_dirs;
private nosave string *_playtester_dirs;

void create() {
   ::create();
   /* These dirs will all be depth searched... */
   _player_dirs = ({ "/doc/helpdir/",
                    "/doc/concepts/",
                    "/doc/known_command/",
                    "/doc/room/",
                    "/doc/spells/",
                    "/doc/object/" });
   _creator_dirs = ({"/doc/creator/",
                    "/doc/driver/",
                    "/doc/policy/",
                    "/doc/new/" });
   _playtester_dirs = ({ "/doc/playtesters/" });
   _lord_dirs = ({"/doc/lord/"});
   unguarded((: rehash_dirs() :));
} /* create() */

int *find_match_in_array( string entry, string *items )  {
   int i;
   int j;
   int elength;
   int ilength;
   int this_match;
   int best_match;
   int best_try;

   elength = strlen( entry );
   best_match = this_match = -1;
   for ( i = sizeof( items ) - 1; i >= 0; i--, this_match = 0 )  {
      ilength = strlen( items[ i ] );
      for ( j = 0; j < elength  &&  j < ilength; j++ )
        if ( entry[ j ] == items[ i ][ j ] ||
             entry[ j ] == items[ i ][ j - 1 + ( j == 0 ) ]  ||
             entry[ j ] == items[ i ][ j + 1 - ( j + 1 == ilength ) ] )
          ++this_match;

      this_match = 100 * this_match / ( j == elength ? ilength : elength );

      if ( this_match > best_match )  {
        best_match = this_match;
        best_try = i;
      }
   }

   return ({ best_try, best_match });
} /* find_match_in_array() */

/**
 * This method deals with the case where an entire string matches.
 *
 * @param name the name to look for help on
 * @return 1 if the help was found, 0 if not
 */
int cmd(string name) {
  class command cmd;
   mixed *list;
   string str;
   string suggestion;
   int i;
   int *matches;

   list = help_list(name);

   // find out if they're looking for a synonym
   // eg. colour == colour or plan == finger.
   if (sizeof(list) == 0 &&
       mapp(_synonyms) &&
       _synonyms[name]) {
      list = help_list(_synonyms[name]);
   }

   if (sizeof(list) == 0) {
      if (PLAYER_HANDLER->test_user(name)) {
         add_failed_mess("That is a player, silly.\n");
         return 0;
      }

      // try a match for similarity.
      list = keys(_help_files_player) + ({ "command_list", "concepts"});
      matches = find_match_in_array(name, list);
      if (matches[1] > MATCH_THRESHOLD) {
         suggestion = list[matches[0]];
      } else {
         // try a match for similarity among the synonyms
         list = keys(_synonyms);
         matches = find_match_in_array(name, list);
         if(matches[1] > MATCH_THRESHOLD) {
            suggestion = _synonyms[list[matches[0]]];
         }
      }

      if (!this_player()->query_creator()) {
         log_file("MISSING_HELP", "%s %s looked for help on %s, "
                  "recommended %s\n",
                  ctime(time()), this_player()->query_name(),
                  name, suggestion);
      }

      // No help so lets try some options.
      str = "Could not find any help on '" + name + "'.  ";
      if (!suggestion && this_player()->query_known_command("newbie"))
        str += "Many useful commands are explained in 'help essentials'.  ";
      else if(suggestion)
        str += "Perhaps you are looking for 'help " +suggestion + "'?  ";

      cmd = new(class command, verb : name);

      // Is it a command?
      if((CMD_D->IsGRCommand(name) &&
          this_player()->query_known_command(name)) ||
         (CMD_D->HandleStars(cmd) &&
          sizeof(((mixed *)CMD_D->GetPaths(cmd->verb) &
                  (mixed *)this_player()->GetSearchPath()))) ||
         sizeof(this_player()->query_parse_command_objects(name)))
        str += "The syntax for the command '" + name + "' can be found by "
          "entering 'syntax " + name + "'.  ";

      if (environment(this_player()) &&
          environment(this_player())->help_function())
         str += "Type 'help here' for help on how to use this room.";

      return notify_fail(str + "\n");

   }

   if (sizeof(list) == 1) {
      /* Cool... */
      do_help(list[0]);
      return 1;
   }

   str = "";

   for (i = 0; i < sizeof(list); i++) {
      // str += sprintf("%c) %s\n", ('a' + i), list[i][0]);
      str += sprintf("%s) %s\n", letter_name(i, list), list[i][0]);
   }

   printf("Discworld help found multiple matches, please choose one of:\n"
          "%-*#s\nChoice: ", this_player()->query_cols(), str);
   input_to("help_input", 0, list);
   return 1;
} /* cmd() */

/**
 * The input loop for the help routines.
 *
 * @param str the just inputed string
 * @param list the set of helps to choose from
 */
void help_input(string str, mixed *list) {
   int num;

   str = lower_case(str);
   if (str == "quit" || str == "**" || str == "." || str == "") {
      write("OK, exiting help.\n");
      return ;
   }

   if ( ( num = query_number_from_string( str, list ) ) == -1 ) {
       printf("Incorrect choice, must be between %s and %s.\nChoice: ",
        start_letter( list ), end_letter( list ) );
      input_to("help_input", 0, list);
      return ;
   }

   // num = str[0] - 'a';
   do_help(list[num]);
} /* help_input() */

/** @ignore yes */
void do_help(mixed *stuff) {
   string str;

   str = evaluate(stuff[1]);
   if (!str || !strlen(str)) {
      write("Broken help file!\n");
   } else {
      write("$P$" + stuff[0] + "$P$" + str);
      //this_player()->more_string(str, stuff[0]);
   }
} /* do_help() */

/**
 * This method deals with 'help here'.
 */
int here_cmd() {
  mixed str;
  mixed *list;
  int i;

  str = environment(this_player())->help_function();
  if(pointerp(str))
    list = str;
  else if(str)
    list += ({ ({ environment(this_player())->short(), str }) });
  else {
    write("There is no help available for this room.\n");
    return 1;
  }

  if (sizeof(list) == 1) {
    /* Cool... */
    do_help(list[0]);
    return 1;
  }

  str = "";

  for (i = 0; i < sizeof(list); i++) {
    str += sprintf("%s) %s\n", letter_name(i, list), list[i][0]);
  }

  printf("Discworld help found multiple matches, please choose one of:\n"
         "%-*#s\nChoice: ", this_player()->query_cols(), str);
  input_to("help_input", 0, list);
  return 1;
}

/**
 * This method deals with 'help here'.
 */
int object_cmd(object ob) {
  mixed str;
  mixed *list;
  int i;

  str = ob->help_function();
  if(pointerp(str))
    list = str;
  else if(str)
    list += ({ ({ environment(this_player())->short(), str }) });
  else {
    add_failed_mess("There is no help available for $I.\n", ({ob}));
    return -1;
  }

  if (sizeof(list) == 1) {
    /* Cool... */
    do_help(list[0]);
    return 1;
  }

  str = "";

  for (i = 0; i < sizeof(list); i++) {
    str += sprintf("%s) %s\n", letter_name(i, list), list[i][0]);
  }

  printf("Discworld help found multiple matches, please choose one of:\n"
         "%-*#s\nChoice: ", this_player()->query_cols(), str);
  input_to("help_input", 0, list);
  return 1;
}

/**
 * This method deals with the case where a command pattern was matched.
 *
 * @param name the command to get help on
 * @return 0 if the command does not exist, 1 if it does exist
 */
int command_cmd(string name) {
   mixed help;

   help = this_player()->help_command(name);
   if (!help) {
      notify_fail("No such command as '" + name + "'.\n");
      return 0;
   } else {
      if (functionp(help)) {
         help = evaluate(help);
      }
      write("$P$" + name + "$P$P" + help);
      return 1;
   }
} /* command_cmd() */

/**
 * This method deals with the case where a soul pattern was matched.
 *
 * @param name the soul to get help on
 * @return 0 if the soul does not exist, 1 if it does exist
 */
int soul_cmd(string name) {
   string help;

   help = SOUL_OBJECT->help_string(name);
   if (!help) {
      notify_fail("No such soul as '" + name + "'.\n");
      return 0;
   } else {
      write("$P$" + name + "$P$P" + help);
      return 1;
   }
} /* soul_cmd() */

/**
 * This method deals with the case where a ritual or spell pattern was matched.
 *
 * @param name the ritual or spell to get help on
 * @param spell 0 if it is a spell, 1 if it is a ritual
 * @return 0 if the ritual or spell does not exist, 1 if it does exist
 */
int spell_cmd(string name, int spell) {
   mixed help;

   help = this_player()->help_spell(name);
   if (!help) {
      notify_fail("No such spell as '" + name + "'.\n");
      return 0;
   } else {
      if (functionp(help)) {
         this_player()->move_string(evaluate(help), name);
      } else {
         write("$P$" + name + "$P$P" + help);
      }
      return 1;
   }
} /* spell_cmd() */

/**
 * This method gives the list of commands currently available.
 *
 * @return always returns 1
 */
int command_list_cmd() {
   list_help("Command list, try 'help concepts' for a list of concepts.",
       "/doc/helpdir/");
   return 1;
} /* command_list_cmd() */

/**
 * This method gives the list of concepts currently available.
 *
 * @return always returns 1
 */
int concepts_list_cmd() {
   list_help("Concepts list, try 'help command_list' for a list of commands.",
       "/doc/concepts/");
   return 1;
} /* concepts_list_cmd() */

/**
 * This method traps the error log stuff for some reason.
 *
 * @return always returns 0
 */
int error_log_cmd() {
   notify_fail("Unable to get help on '" + ERROR_LOG + "'.\n");
   return 0;
} /* error_log_cmd() */

/*
 * Print all the names of all the files in a dir...
 */
private void list_help(string title, string dir) {
   string *files;

   files = get_dir(dir + "*") - ({ ".", "..", "ERROR_REPORTS", "RCS", "old" });
   write("$P$Help$P$" + sprintf("%s\n%-#*s\n", title,
                                     (int)this_player()->query_cols(),
                                     implode(files, "\n")));
} /* list_help() */

/*
 * Returns a list of possible help files...
 */
/*private */ mixed *help_list(string name) {
   string* stuff;
   mixed str;
   object* fluff;
   object blue;

   stuff = query_help_on(name, this_player()->query_creator(),
                         this_player()->query_lord(),
                         this_player()->query_playtester());

   if (name == "room" || name == "here") {
      str = environment(this_player())->help_function();
      if (pointerp(str)) {
         stuff += str;
      } else if (str) {
         stuff += ({ ({ environment(this_player())->short(),
                        str }) });
      } else {
         add_failed_mess("There is no help available for this room.\n");
         return 0;
      }
   }

   str = this_player()->help_spell(name);
   if (stringp(str)) {
      stuff += ({ ({ name + " (Spell)",
                    (: $(str) :) }) });
   }
   if (functionp(str)) {
      stuff += ({ ({ name + " (Spell)", str }) });
   }

   str = SOUL_OBJECT->help_string(name);
   if (str) {
      stuff += ({ ({ name + " (Soul)",
                    (: $(SOUL_OBJECT)->help_string($(name)) :) }) });
   }

   fluff = filter(match_objects_for_existence(name, ({ this_player(),
                                      environment(this_player()) })),
                    (: $1 && $1->help_function() :));
   if (sizeof(fluff)) {
      foreach (blue in fluff) {
         stuff += blue->help_function();
      }
   }

   return stuff;
} /* help_list() */

/**
 * Gives a letter in the array for a given array position.
 * @param letter the letter number to return.
 * @param things the help list array to look for
 * @return Returns a letter between 'a' and 'z', if things is less
 * than 27 elements. Otherwise, it returns an array between 'aa' and 'zz'.
 */
private string letter_name(int letter, mixed *things) {
   string bing;

   if (sizeof(things) > 26) {
      bing = "aa";
      bing[0] = 'a' + (letter / 26);
      bing[1] = 'a' + (letter % 26);
      return bing;
   }
   bing = "a";
   bing[0] = 'a' + letter;
   return bing;
} /* letter_name() */

/**
 * Returns the first letter in an help list
 * @param things the help list array to look in
 */
private string start_letter( mixed *things ) {
   return letter_name(0, things);
} /* start_letter() */

/**
 * Returns the last letter in an help list
 * @param things the help list array to look in
 */
private string end_letter( mixed *things ) {
   return letter_name(sizeof(things) - 1, things);
} /* end_letter() */

/**
 * This method translates a user inputted help id into an
 * actual array position.
 * @param name the text to look for
 * @param things the array to look in
 * @return an array index, with -1 for a bounds error.
 */
private int query_number_from_string(string name, mixed *things ) {
   int pos;

   if (sizeof(things) > 26) {
      if (strlen(name) != 2) {
         return -1;
      }
      name = lower_case(name);
      if (name[0] < 'a' || name[0] > 'z') {
         return -1;
      }
      if (name[1] < 'a' || name[1] > 'z') {
         return -1;
      }
      pos = (name[0] - 'a') * 26 + name[1] - 'a';
      if (pos >= sizeof(things)) {
         return -1;
      }
      return pos;
   }

   if (strlen(name) != 1) {
      return -1;
   }
   name = lower_case(name);
   if (name[0] < 'a' || name[0] > 'z') {
      return -1;
   }
   pos = name[0] - 'a';
   if (pos >= sizeof(things)) {
      return -1;
   }
   return pos;
} /* query_number_from_string() */

/** @ignore yes */
mixed *query_patterns() {
   return ({ "<string>", (: cmd($4[0]) :),
               "here", (: here_cmd() :),
               "object <indirect:object:me-here'item'>", (: object_cmd($1[0]) :),
               "command <string>", (: command_cmd($4[0]) :),
               "spell <string>", (: spell_cmd($4[0], 0) :),
               "ritual <string>", (: spell_cmd($4[0], 1) :),
               "soul <string>", (: soul_cmd($4[0]) :),
               "command_list", (: command_list_cmd() :),
               "concepts", (: concepts_list_cmd() :),
               //              ERROR_LOG, (: error_log_cmd() :),
               "", (: concepts_list_cmd() :) });
} /* query_patterns() */

/**
 * This goes through and recreates the hash table for the dirs.
 */
void rehash_dirs() {
   _help_files_player = read_directories(_player_dirs, 1);
   _help_files_creator = read_directories(_creator_dirs, 1);
   _help_files_lord = read_directories(_lord_dirs, 1);
   _help_files_playtester = read_directories(_playtester_dirs, 1);
   _synonyms = read_synonyms();
} /* rehash_dirs() */

/**
 * This method rehashes a specific directory.
 * @param dir the directory to rehash
 */
void rehash_specific_dir(string dir) {
   string start;
   mapping ret;
   string name;
   string* files;

   if (dir[<1] != '/') {
      dir += "/";
   }
   start = "/" + implode(explode(dir, "/")[0..1], "/") + "/";
   if (member_array(start, _player_dirs) != -1) {
      ret = read_directories(({ dir }), 0);
      foreach (name, files in ret) {
         if (_help_files_player[name]) {
            _help_files_player[name] &= files;
         } else {
            _help_files_player[name] = files;
         }
      }
   }
   if (member_array(start, _creator_dirs) != -1) {
      ret = read_directories(({ dir }), 0);
      foreach (name, files in ret) {
         if (_help_files_creator[name]) {
            _help_files_creator[name] &= files;
         } else {
            _help_files_creator[name] = files;
         }
      }
   }
   if (member_array(start, _lord_dirs) != -1) {
      ret = read_directories(({ dir }), 0);
      foreach (name, files in ret) {
         if (_help_files_lord[name]) {
            _help_files_lord[name] &= files;
         } else {
            _help_files_lord[name] = files;
         }
      }
   }
   if (member_array(start, _playtester_dirs) != -1) {
      ret = read_directories(({ dir }), 0);
      foreach (name, files in ret) {
         if (_help_files_playtester[name]) {
            _help_files_playtester[name] &= files;
         } else {
            _help_files_playtester[name] = files;
         }
      }
   }
}

private mapping read_synonyms() {
  string *bits, *bits2;
  int i;
  mapping tmp;

  tmp = ([ ]);
  bits = explode(read_file(SYNONYMS), "\n");
  for(i=0; i<sizeof(bits); i++) {
    bits2 = explode(bits[i], " ");
    tmp[bits2[0]] = bits2[1];
  }
  return tmp;
}

/**
 * Reads in the directories and places the results neatly into a mapping.
 *
 * @param directories the directories to recursively read
 * @return a mapping with the locations of the help files
 */
private mapping read_directories(string *directories, int recurse) {
   string *files;
   string fname;
   int i;
   string dir;
   mapping ret;

   ret = ([ ]);
   for (i = 0; i < sizeof(directories); i++) {
     dir = directories[i];
     files = get_dir(dir + "*") - ({ "ERROR_REPORTS" });
     foreach (fname in files) {
       if (file_size( dir + fname) == -2) {
         if (fname != "." &&
             fname != ".." &&
             fname != "old" &&
             fname != "RCS") {
           directories += ({ dir + fname + "/" });
         }
       } else if (fname != "." &&
                  fname != ".." &&
                  fname != "old") {
         if (!ret[fname]) {
           ret[fname] = ({ dir + fname });
         } else {
           ret[fname] += ({ dir + fname });
         }

         /* Turn '_' into spaces... */
         if (strsrch(fname, "_") > 0) {
           fname = replace(fname, "_", " ");
           if (!ret[fname]) {
             ret[fname] = ({ dir + fname });
           } else {
             ret[fname] += ({ dir + fname });
           }
         }
       }
     }
   }
   return ret;
} /* read_directories() */

/**
 * Searches the lists for things which we might have help on.
 * The array which is returned is an array of arrays, each internal
 * array consists of a name and help string.
 *
 * @param name the help to search for
 * @param creator is this a creator searching
 * @param lord is this a lord searching
 * @return an array of arrays
 */
mixed *query_help_on(string name, int creator, int lord, int pt) {
   string *files;
   mapping map;

   files = ({ });
   name = replace_string(name, " ", "_");
   if (_help_files_player[name]) {
      files += create_help_files(_help_files_player[name], NROFF_DIR);
   }

   if (lord || creator || pt) {
     if(_help_files_playtester && _help_files_playtester[name]) {
         files += create_help_files(_help_files_playtester[name], NROFF_DIR);
      }
   }
   if (lord || creator) {
     if(_help_files_creator && _help_files_creator[name]) {
         files += create_help_files(_help_files_creator[name], NROFF_DIR);
      }
      map = AUTODOC_HANDLER->query_help_map();
      if (map && map[name]) {
         files += create_help_files(map[name], NROFF_DIR);
      }
   }
   if (lord) {
     if(_help_files_lord && _help_files_lord[name]) {
         files += create_help_files(_help_files_lord[name], NROFF_DIR);
      }
      map = AUTODOC_HANDLER->query_help_map();
      if (map && map[name]) {
         files += create_help_files(map[name], NROFF_DIR);
      }
  }

   return files;
} /* query_help_on() */

string query_synonym(string name) {
  if(mapp(_synonyms) && _synonyms[name])
    return _synonyms[name];
  return "";
}

/*
 * Makea string from a nroff input...
 */
private string nroff_file(string name, string nroff_dir) {
   string nroff_fn;
   string str;

   nroff_fn = nroff_dir + replace(name, "/", ".");
   str = NROFF_HAND->cat_file(nroff_fn, 1);
   if (!str) {
      NROFF_HAND->create_nroff(name, nroff_fn);
      str = NROFF_HAND->cat_file(nroff_fn, 0);
   }
   return str;
} /* nroff_file() */

/**
 * This method nips through the list of names doing the nroff stuff.
 * The array which is returned is an array of arrays, each internal
 * array consists of a name and help string.
 *
 * @param names the array of names to process
 * @param nroff_dir the nroff directory to use for the output
 * @return an array of arrays
 */
mixed *create_help_files(string *names, string nroff_dir) {
   int i;
   mixed *ret;
   string *bits;

   ret = ({ });
   for (i = 0; i < sizeof(names); i++) {
      bits = explode(names[i], "/");
      ret += ({ ({ bits[<1] + " (" +
                   names[i] + ")",
                   (: nroff_file($(names[i]), $(nroff_dir)) :) }) });
   }
   return ret;
} /* create_help_files() */

/**
 * This method returns the mapping of all the player help files.
 * @return the mapping of player help files
 */
mapping query_help_files_player() { return _help_files_player; }
/**
 * This method returns the mapping of all the creator help files.
 * @return the mapping of creator help files
 */
mapping query_help_files_creator() { return _help_files_creator; }

/** @ignore yes */
mixed *stats() {
  mapping map;

  map = AUTODOC_HANDLER->query_help_map();
  return ({
      ({ "player help files", sizeof( keys(_help_files_player) ) }),
      ({ "creator help files", sizeof( keys(_help_files_creator) ) }),
        ({ "autodoc help map", sizeof(keys(map)) }) ,
          });
} /* stats() */

/** @ignore yes
 * Prevent this command being cleaned up because it takes a long time
 * to initialise.
 */
int clean_up() {
  return 0;
}

/** Really make sure it cannot be unloaded. */
void dest_me() {
}