final_realms_fluffos_v1/
final_realms_fluffos_v1/bin/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/ChangeLog.old/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/Win32/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/compat/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/compat/simuls/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/include/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/clone/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/command/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/data/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/etc/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/include/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/inherit/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/inherit/master/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/log/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/tests/compiler/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/tests/efuns/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/tests/operators/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/u/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/tmp/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/windows/
final_realms_fluffos_v1/lib/baseobs/guilds/
final_realms_fluffos_v1/lib/baseobs/misc/
final_realms_fluffos_v1/lib/baseobs/races/shadows/
final_realms_fluffos_v1/lib/cmds/god/
final_realms_fluffos_v1/lib/cmds/handlers/
final_realms_fluffos_v1/lib/cmds/handlers/cmds/
final_realms_fluffos_v1/lib/d/heaven/
final_realms_fluffos_v1/lib/d/heaven/heaven/ave/
final_realms_fluffos_v1/lib/d/mudlib/
final_realms_fluffos_v1/lib/d/newbie/
final_realms_fluffos_v1/lib/d/newbie/docs/
final_realms_fluffos_v1/lib/d/newbie/drow/armour/
final_realms_fluffos_v1/lib/d/newbie/drow/items/
final_realms_fluffos_v1/lib/d/newbie/drow/mobs/
final_realms_fluffos_v1/lib/d/newbie/drow/oldmobs/
final_realms_fluffos_v1/lib/d/newbie/drow/weapons/
final_realms_fluffos_v1/lib/d/newbie/duergar/weapons/
final_realms_fluffos_v1/lib/d/newbie/dwarf/weapons/
final_realms_fluffos_v1/lib/d/newbie/elf/cafe/
final_realms_fluffos_v1/lib/d/newbie/elf/chars/equip/
final_realms_fluffos_v1/lib/d/newbie/elf/items/armours/
final_realms_fluffos_v1/lib/d/newbie/elf/items/obj/
final_realms_fluffos_v1/lib/d/newbie/elf/items/weapons/
final_realms_fluffos_v1/lib/d/newbie/elf/quick_fix/
final_realms_fluffos_v1/lib/d/newbie/gnome/armour/
final_realms_fluffos_v1/lib/d/newbie/gnome/buildings/
final_realms_fluffos_v1/lib/d/newbie/gnome/items/
final_realms_fluffos_v1/lib/d/newbie/gnome/npcs/clones/
final_realms_fluffos_v1/lib/d/newbie/gnome/rooms/northrooms/
final_realms_fluffos_v1/lib/d/newbie/gnome/weapons/
final_realms_fluffos_v1/lib/d/newbie/goblin/armour/
final_realms_fluffos_v1/lib/d/newbie/goblin/items/
final_realms_fluffos_v1/lib/d/newbie/grads/log/
final_realms_fluffos_v1/lib/d/newbie/grads/npcs/
final_realms_fluffos_v1/lib/d/newbie/grads/rooms/
final_realms_fluffos_v1/lib/d/newbie/grads/rooms/cave1/
final_realms_fluffos_v1/lib/d/newbie/grads/temp/
final_realms_fluffos_v1/lib/d/newbie/guests/weapons/
final_realms_fluffos_v1/lib/d/newbie/half-elf/items/
final_realms_fluffos_v1/lib/d/newbie/half-elf/newroomss/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/castle/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/drows/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/savannah/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/secret/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/town/
final_realms_fluffos_v1/lib/d/newbie/halfling/
final_realms_fluffos_v1/lib/d/newbie/halfling/misc/
final_realms_fluffos_v1/lib/d/newbie/halfling/rooms/cave/
final_realms_fluffos_v1/lib/d/newbie/human/
final_realms_fluffos_v1/lib/d/newbie/human/armour/
final_realms_fluffos_v1/lib/d/newbie/human/monsters/
final_realms_fluffos_v1/lib/d/newbie/human/obj/
final_realms_fluffos_v1/lib/d/newbie/human/weapons/
final_realms_fluffos_v1/lib/d/newbie/lizard/armour/
final_realms_fluffos_v1/lib/d/newbie/lizard/items/
final_realms_fluffos_v1/lib/d/newbie/lizard/underwater/
final_realms_fluffos_v1/lib/d/newbie/lizard/weapons/
final_realms_fluffos_v1/lib/d/newbie/logs/
final_realms_fluffos_v1/lib/d/newbie/new_halfelf/
final_realms_fluffos_v1/lib/d/newbie/new_halfelf/npcs/
final_realms_fluffos_v1/lib/d/newbie/newdrow/npcs/
final_realms_fluffos_v1/lib/d/newbie/newdrow/rooms/
final_realms_fluffos_v1/lib/d/newbie/newelf/
final_realms_fluffos_v1/lib/d/newbie/newelf/chars/
final_realms_fluffos_v1/lib/d/newbie/newelf/npcs/
final_realms_fluffos_v1/lib/d/newbie/newelf/npcs/recopied/
final_realms_fluffos_v1/lib/d/newbie/newelf/obj/
final_realms_fluffos_v1/lib/d/newbie/newelf/quest_docs./
final_realms_fluffos_v1/lib/d/newbie/newken/
final_realms_fluffos_v1/lib/d/newbie/newken/chars/
final_realms_fluffos_v1/lib/d/newbie/newken/misc/
final_realms_fluffos_v1/lib/d/newbie/newken/npcs/
final_realms_fluffos_v1/lib/d/newbie/newken/obj/
final_realms_fluffos_v1/lib/d/newbie/newliz/
final_realms_fluffos_v1/lib/d/newbie/newliz/cave/
final_realms_fluffos_v1/lib/d/newbie/newliz/npcs/
final_realms_fluffos_v1/lib/d/newbie/orc/items/misc/
final_realms_fluffos_v1/lib/d/newbie/orc/items/weapons/
final_realms_fluffos_v1/lib/d/newbie/orc/tower/
final_realms_fluffos_v1/lib/d/vehicle/
final_realms_fluffos_v1/lib/doc/
final_realms_fluffos_v1/lib/doc/driver/
final_realms_fluffos_v1/lib/doc/driver/concepts/
final_realms_fluffos_v1/lib/doc/driver/driver/
final_realms_fluffos_v1/lib/doc/driver/efuns/arrays/
final_realms_fluffos_v1/lib/doc/driver/efuns/bitstrings/
final_realms_fluffos_v1/lib/doc/driver/efuns/communication/
final_realms_fluffos_v1/lib/doc/driver/efuns/core/
final_realms_fluffos_v1/lib/doc/driver/efuns/debugging/
final_realms_fluffos_v1/lib/doc/driver/efuns/filesystem/
final_realms_fluffos_v1/lib/doc/driver/efuns/interactive/
final_realms_fluffos_v1/lib/doc/driver/efuns/mappings/
final_realms_fluffos_v1/lib/doc/driver/efuns/objects/
final_realms_fluffos_v1/lib/doc/driver/efuns/security/
final_realms_fluffos_v1/lib/doc/driver/efuns/strings/
final_realms_fluffos_v1/lib/doc/driver/efuns/system/
final_realms_fluffos_v1/lib/doc/driver/efuns/types/
final_realms_fluffos_v1/lib/doc/driver/lpc/constructs/
final_realms_fluffos_v1/lib/doc/driver/lpc/types/
final_realms_fluffos_v1/lib/doc/driver/platforms/
final_realms_fluffos_v1/lib/doc/lpc/
final_realms_fluffos_v1/lib/doc/mail/
final_realms_fluffos_v1/lib/doc/man/
final_realms_fluffos_v1/lib/doc/man/html/
final_realms_fluffos_v1/lib/doc/man/html/applies/
final_realms_fluffos_v1/lib/doc/man/html/applies/parsing/
final_realms_fluffos_v1/lib/doc/man/html/driver/
final_realms_fluffos_v1/lib/doc/man/html/efuns/
final_realms_fluffos_v1/lib/doc/man/html/efuns/arrays/
final_realms_fluffos_v1/lib/doc/man/html/efuns/buffers/
final_realms_fluffos_v1/lib/doc/man/html/efuns/compile/
final_realms_fluffos_v1/lib/doc/man/html/efuns/floats/
final_realms_fluffos_v1/lib/doc/man/html/efuns/functions/
final_realms_fluffos_v1/lib/doc/man/html/efuns/general/
final_realms_fluffos_v1/lib/doc/man/html/efuns/numbers/
final_realms_fluffos_v1/lib/doc/man/html/efuns/parsing/
final_realms_fluffos_v1/lib/doc/man/local/
final_realms_fluffos_v1/lib/doc/man/local/applies/
final_realms_fluffos_v1/lib/doc/man/local/applies/interactive/
final_realms_fluffos_v1/lib/doc/man/local/applies/master/
final_realms_fluffos_v1/lib/doc/man/local/concepts/
final_realms_fluffos_v1/lib/doc/man/local/defines/
final_realms_fluffos_v1/lib/doc/man/local/driver/
final_realms_fluffos_v1/lib/doc/man/local/efuns/
final_realms_fluffos_v1/lib/doc/man/local/efuns/arrays/
final_realms_fluffos_v1/lib/doc/man/local/efuns/buffers/
final_realms_fluffos_v1/lib/doc/man/local/efuns/calls/
final_realms_fluffos_v1/lib/doc/man/local/efuns/compile/
final_realms_fluffos_v1/lib/doc/man/local/efuns/filesystem/
final_realms_fluffos_v1/lib/doc/man/local/efuns/floats/
final_realms_fluffos_v1/lib/doc/man/local/efuns/functions/
final_realms_fluffos_v1/lib/doc/man/local/efuns/general/
final_realms_fluffos_v1/lib/doc/man/local/efuns/interactive/
final_realms_fluffos_v1/lib/doc/man/local/efuns/internals/
final_realms_fluffos_v1/lib/doc/man/local/efuns/mappings/
final_realms_fluffos_v1/lib/doc/man/local/efuns/mudlib/
final_realms_fluffos_v1/lib/doc/man/local/efuns/numbers/
final_realms_fluffos_v1/lib/doc/man/local/efuns/objects/
final_realms_fluffos_v1/lib/doc/man/local/efuns/parsing/
final_realms_fluffos_v1/lib/doc/man/local/efuns/sockets/
final_realms_fluffos_v1/lib/doc/man/local/efuns/strings/
final_realms_fluffos_v1/lib/doc/man/local/efuns/system/
final_realms_fluffos_v1/lib/doc/man/local/historical/
final_realms_fluffos_v1/lib/doc/man/local/lfun/QC/
final_realms_fluffos_v1/lib/doc/man/local/lfun/events/
final_realms_fluffos_v1/lib/doc/man/local/lfun/monster/
final_realms_fluffos_v1/lib/doc/man/local/lfun/properties/
final_realms_fluffos_v1/lib/doc/man/local/lpc/
final_realms_fluffos_v1/lib/doc/man/local/lpc/constructs/
final_realms_fluffos_v1/lib/doc/man/local/lpc/types/
final_realms_fluffos_v1/lib/doc/man/local/standards/
final_realms_fluffos_v1/lib/doc/man/local/tutorials/
final_realms_fluffos_v1/lib/doc/man/local/tutorials/basic/
final_realms_fluffos_v1/lib/doc/man/local/tutorials/intermediate/
final_realms_fluffos_v1/lib/doc/man/mudos/applies/
final_realms_fluffos_v1/lib/doc/man/mudos/applies/interactive/
final_realms_fluffos_v1/lib/doc/man/mudos/applies/parsing/
final_realms_fluffos_v1/lib/doc/man/mudos/concepts/
final_realms_fluffos_v1/lib/doc/man/mudos/driver/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/arrays/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/buffers/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/calls/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/compile/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/filesystem/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/floats/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/functions/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/general/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/mappings/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/mixed/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/mudlib/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/numbers/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/parsing/
final_realms_fluffos_v1/lib/doc/man/mudos/lpc/constructs/
final_realms_fluffos_v1/lib/doc/man/mudos/lpc/types/
final_realms_fluffos_v1/lib/doc/races/
final_realms_fluffos_v1/lib/doc/races/old_race/
final_realms_fluffos_v1/lib/global/virtual/
final_realms_fluffos_v1/lib/global/wiz_backup/
final_realms_fluffos_v1/lib/net/config/
final_realms_fluffos_v1/lib/net/daemon/chars/
final_realms_fluffos_v1/lib/net/inherit/
final_realms_fluffos_v1/lib/net/intermud3/
final_realms_fluffos_v1/lib/net/intermud3/cmds/
final_realms_fluffos_v1/lib/net/intermud3/save/
final_realms_fluffos_v1/lib/net/intermud3/services/
final_realms_fluffos_v1/lib/net/obj/
final_realms_fluffos_v1/lib/net/old/
final_realms_fluffos_v1/lib/net/old/intermud/
final_realms_fluffos_v1/lib/net/old/intermud/adm/
final_realms_fluffos_v1/lib/net/old/intermud/services/
final_realms_fluffos_v1/lib/net/old/intermud/udp/
final_realms_fluffos_v1/lib/net/virtual/
final_realms_fluffos_v1/lib/obj/b_day/
final_realms_fluffos_v1/lib/obj/chars/
final_realms_fluffos_v1/lib/obj/handlers/lists/
final_realms_fluffos_v1/lib/obj/handlers/useless/
final_realms_fluffos_v1/lib/obj/monsters/
final_realms_fluffos_v1/lib/obj/roomgen/
final_realms_fluffos_v1/lib/obj/soul/
final_realms_fluffos_v1/lib/obj/vegetation/
final_realms_fluffos_v1/lib/obj/weapons/oldsys/
final_realms_fluffos_v1/lib/open/
final_realms_fluffos_v1/lib/players/g/
final_realms_fluffos_v1/lib/releasefiles/d/heaven/
final_realms_fluffos_v1/lib/releasefiles/d/mudlib/
final_realms_fluffos_v1/lib/releasefiles/d/newbie/
final_realms_fluffos_v1/lib/releasefiles/doc/
final_realms_fluffos_v1/lib/releasefiles/players/g/
final_realms_fluffos_v1/lib/releasefiles/save/
final_realms_fluffos_v1/lib/releasefiles/save/environ/
final_realms_fluffos_v1/lib/releasefiles/save/roomgen/
final_realms_fluffos_v1/lib/releasefiles/secure/
final_realms_fluffos_v1/lib/releasefiles/w/
final_realms_fluffos_v1/lib/releasefiles/w/god/
final_realms_fluffos_v1/lib/room/
final_realms_fluffos_v1/lib/save/
final_realms_fluffos_v1/lib/save/environ/
final_realms_fluffos_v1/lib/save/roomgen/
final_realms_fluffos_v1/lib/scripts/
final_realms_fluffos_v1/lib/secure/crerem/
final_realms_fluffos_v1/lib/secure/dom/
final_realms_fluffos_v1/lib/secure/log/
final_realms_fluffos_v1/lib/secure/misc/
final_realms_fluffos_v1/lib/std/adnd/
final_realms_fluffos_v1/lib/std/commands/shadows/
final_realms_fluffos_v1/lib/std/creator/
final_realms_fluffos_v1/lib/std/curses/
final_realms_fluffos_v1/lib/std/curses/old_sys/
final_realms_fluffos_v1/lib/std/curses/shadows/
final_realms_fluffos_v1/lib/std/dom/
final_realms_fluffos_v1/lib/std/effects/
final_realms_fluffos_v1/lib/std/effects/healing/
final_realms_fluffos_v1/lib/std/effects/other/
final_realms_fluffos_v1/lib/std/effects/poisons/
final_realms_fluffos_v1/lib/std/environ/
final_realms_fluffos_v1/lib/std/guilds/
final_realms_fluffos_v1/lib/std/guilds/priests/samples/
final_realms_fluffos_v1/lib/std/guilds/wizards/
final_realms_fluffos_v1/lib/std/living/baldy/
final_realms_fluffos_v1/lib/std/living/divstuff/
final_realms_fluffos_v1/lib/std/paran/
final_realms_fluffos_v1/lib/std/poisons/
final_realms_fluffos_v1/lib/std/poisons/shadows/
final_realms_fluffos_v1/lib/std/poisons/weapons/
final_realms_fluffos_v1/lib/std/race_groups/
final_realms_fluffos_v1/lib/std/room/
final_realms_fluffos_v1/lib/std/room/old/
final_realms_fluffos_v1/lib/std/rooms/
final_realms_fluffos_v1/lib/std/shadows/
final_realms_fluffos_v1/lib/std/shadows/test_shad/
final_realms_fluffos_v1/lib/std/socket/
final_realms_fluffos_v1/lib/std/spells/
final_realms_fluffos_v1/lib/std/vaults/
final_realms_fluffos_v1/lib/tmp/
final_realms_fluffos_v1/lib/w/
final_realms_fluffos_v1/lib/w/god/
final_realms_fluffos_v1/old/
final_realms_fluffos_v1/win32/
/* Hamlet, March 1996 -- This object displays menus in a VMS HELP-like
                         system.  It must be cloned for each user,
                         And must be told who the user is, where to 
                         enter the menuing system, and what object
                         holds the index for the system.
   Hamlet, Aug 1997   -- Minor reworks to allow the indexing system
                         to be changeable.  Also some minor code fixes.
                      -- Turned into a major project of adding features,
                         making this version 2.0
*/

/* All code copyright 1996 and 1997 by Isaac Charles (baked@potato.org).
   Permission is granted to use, copy, redistribute, and modify this
   code.  Credit must remain, changes should be marked as such.
*/

/* Notes for possible future expansion:
   * Might be useful to have some way to alias menu entries....
     To make meaningless entries into meaningful ones, perhaps.
   * If this were inherited, would be useful to have a way to
     add commands to input_prompt().
   * Possibly support 'man 3 stringp' sort of queries.  Where
     chapters are defined by primary subdir?
*/

#include <colors.h>

inherit "/std/basic/wildcard.c";

/* Very temporary defines.  REPLACE ME */
#define NROFF_HAND this_object()
// #define nroff_file(string x, int y) put actual function name here
// Then remove the line below. 
string nroff_file(string x, int y) { return read_file(x); }

/* Use this if you like to be able to pick entries by number */
#define NUMBERS

/* Maximum documents "man -a <thing>" will display at once. */
#define MAX_SPEW_DOCS 5

/* Message used when a delay is needed. */
#define PRESS_RETURN "[Press return to continue.]"

object user;        /* Display owner */
int MENUING;        /* If we're in the menu system */
object handler;     /* Which index handler */
string index_root;  /* Root directory of this index */ 
int has_merge_dirs; /* If the index merges directories */
string *mergedirs;  /* The merged directories */ 
string *curpath;    /* What directory we're in */
string *view_files; /* Files we have queued for viewing. */
int all_matches;    /* If we want to view all matches. */
int expert;         /* Avoid the help message at the bottom of menus */

/* These are scratchpad sorts of things... 
   keeps us from having to re-query or recalculate. 
*/
string curmenu;
string menu_screen;
string *subdirs;
string *docs;
string *other_matches;
int cols, rows;

varargs void show_menu(string file, int redisplay);
void show_document(string file);
void show_mult_documents(string *thedocs);
void input_prompt(string str);
void delay_prompt(string str);
void menu_prompt(string str);
string insert_mergetext(string text, string fname);
void dest_me();

#ifdef NUMBERS
string *match_by_number(string str);
#endif

varargs void do_query(mixed hand, string str, object me, int match_all, 
                      int enter_menu, int menu_expert, int reenter) {
  string *bits;
  int building;
  string *fnames;
  string *dir_wants = ({ });
  string *doc_wants = ({ });
  int is_directory;
  int i, j;

  if(!reenter) { /* Most checks and assignments can be skipped if
                    we're reentering from somewhere else in the system. */  
    user = me;

    if(stringp(hand))
      if(catch(hand = load_object(hand))) {
        tell_object(user, "The handler for this system is broken.  "
                          "Please inform someone who can fix it.\n");
        dest_me();
        return;
      }

    building = hand->doc_index_building();
  }

  if(!str)
    str = "";
  else {
    bits = explode(str," ") - ({ "" });

    if(sizeof(bits) > 1)
      str = implode(bits,"/");

    str = replace_string(str, ",/", ","); /* fix for "blah, blah2" */
  }

  fnames = hand->get_doc_fnames(str);
  
  if(!sizeof(fnames)) {
    tell_object(user,"Your subject was not found in the index.\n");
    if(reenter) {
      tell_object(user,PRESS_RETURN);
      input_to("delay_prompt");
      return;
    }
    else if(building)
      tell_object(user,"However, the system is currently loading.  "
                  "Try again in a few seconds.\n");
    dest_me();
    return;
  }

  if(!reenter) {
    mergedirs = hand->get_mergedirs();
    if(sizeof(mergedirs))
      has_merge_dirs = 1;
  }

  /* Okay!  We've got a match.  Do we enter the menuing system or
     just display a page?
  */
  i = member_array("", fnames);

  if(match_all || (i > MAX_SPEW_DOCS))
    fnames -= ({ "" });
  else {
    if(i > -1) {
      if(!has_merge_dirs)
        other_matches = fnames[i+1..];
      else {
        other_matches = ({ });
        for(j=i+1; j < sizeof(fnames); j++)
          other_matches += ({ implode(explode(fnames[j], "/")[1..], "/") });
      }

      other_matches = sort_array(other_matches, 1);
      fnames = fnames[0..i-1];
    }
  }

  for(i=0; i < sizeof(fnames); i++)
    if(fnames[i][<6..<1] == "README")
      dir_wants += ({ fnames[i] });
    else
      doc_wants += ({ fnames[i] });

  if(!reenter) {
    handler = hand;
    index_root = hand->get_index_root(user);

    all_matches = match_all;

    if(menu_expert)
      expert = 1;
    else if(user->query_property("MENU_EXPERT"))
      expert = 1;

    rows = user->query_rows();
    cols = user->query_cols();
  }

  if(!sizeof(dir_wants) && (sizeof(doc_wants) <= MAX_SPEW_DOCS)) { 
    /* Show them all the docs */
    if(enter_menu || (reenter == '^')) {
      if(building) {
        tell_object(user,"The menu system is currently loading.  "
                    "Please wait a few seconds.\n");
        dest_me();
        return;
      }
      doc_wants += ({ hand->get_doc_fnames(implode(explode(
                        doc_wants[<1], "/")[has_merge_dirs..<2], "/"))[0] });
      MENUING = 1;
    }
    show_mult_documents(doc_wants);

  }
  else if((!sizeof(doc_wants) && (sizeof(dir_wants) == 1))) {
    if(building) {
      tell_object(user,"The menu system is currently loading.  "
                  "Please wait a few seconds.\n");
      dest_me();
      return;
    }
    else
      MENUING = 1;

    bits = explode(dir_wants[0], "/");

    if(sizeof(bits) == (has_merge_dirs + 1))
      curpath = ({ });
    else
      curpath = bits[has_merge_dirs..<2];    
    
    curmenu = dir_wants[0];

    show_menu(curmenu);
  }
  else { /* Mixed bag.  Fake a menu for them to pick better. */
    curpath = ({ });

    for(i = 0; i < sizeof(dir_wants); i++)
      dir_wants[i] = implode(explode(
                       dir_wants[i], "/")[has_merge_dirs..<2], "/");
    if(has_merge_dirs)
      for(i = 0; i < sizeof(doc_wants); i++)
        doc_wants[i] = implode(explode(
                         doc_wants[i], "/")[1..], "/");

    if(sizeof(other_matches)) {
      for(i=0; i < sizeof(other_matches); i++)
        if(other_matches[i][<6..<1] == "README")
          dir_wants += ({ other_matches[i] });
        else
          doc_wants += ({ other_matches[i] });
      other_matches = 0;
    }

    subdirs = sort_array(dir_wants,1);
    docs = sort_array(doc_wants,1);
    curmenu = "_FAKE_README";
    MENUING = 1;

    show_menu(curmenu, -1);
  }
}

/* This one shows both a directory's doc file (dir/README) and a list
   of available documents and subtopics in the dir.  It relies on 
   curpath being properly set.
*/
varargs void show_menu(string file, int redisplay) {
  string ftext;
  int i;
  string *thesubdirs;
  string *thedocs;

  if((redisplay != 1) || !menu_screen) {
    if(file_size(index_root + file) > 0)
      ftext = insert_mergetext(NROFF_HAND->nroff_file(index_root + file, 
                                                      cols), file);
    else if(file_size(index_root + "_NO_README") > 0)
      ftext = NROFF_HAND->nroff_file(index_root + "_NO_README", cols);
    else
      ftext = "This directory has no description.\n";

    if(redisplay != -1) {
      subdirs = handler->get_subdirs(implode(curpath,"/"));
      docs = handler->get_docs_in_dir(implode(curpath,"/"));
    }

    if(sizeof(curpath))
      menu_screen = MTOPIC_COLOR + "Topic: "+implode(curpath,"/")+
             "%^RESET%^\n\n";
    else
      menu_screen = "";
    
    menu_screen += ftext+"\n";

#ifdef NUMBERS
    thesubdirs = allocate(sizeof(subdirs));
    thedocs = allocate(sizeof(docs));

    for(i=0; i < sizeof(subdirs); i++)
       thesubdirs[i] = (i + 1) + ") " + subdirs[i];
    for(i=0; i < sizeof(docs); i++)
       thedocs[i] = (i + sizeof(subdirs) + 1) + ") " + docs[i];
#else
    thesubdirs = subdirs;
    thedocs = docs;
#endif

    if(sizeof(thesubdirs))
      menu_screen += "Subtopics:\n\n" + MSUBTOP_COLOR + 
              sprintf("  %-#*s", cols,
              implode(thesubdirs,"\n"))+"\n\n%^RESET%^";
    if(sizeof(thedocs))
      menu_screen += "Documents:\n\n" + MDOC_COLOR +
              sprintf("  %-#*s", cols,
              implode(thedocs,"\n"))+"\n\n%^RESET%^";
    if(sizeof(other_matches)) {
      menu_screen += MTOPIC_COLOR + sprintf("*Other topics: %-=*s\n\n", 
                     (cols - 15), implode(other_matches, ", ")) +
                     "%^RESET%^";
      other_matches = 0;
    }

    if(!expert)
      menu_screen += "Type 'q' to quit, 'h' for help.  Enter subtopic "
            "or document to continue.\n'.' will redisplay this menu.  "
            "Press <ret> to go one level lower.\n";
 
  }

  if(sizeof(explode(menu_screen,"\n")) < (rows-1)) {
    tell_object(user, menu_screen);
    menu_prompt(0);
  }
  else {
    user->set_finish_func("menu_prompt");
    user->more_string(menu_screen,"[Menu]");
  }
}

/* This one displays a document, more'ing it if necessary. */
void show_document(string file) {
  string *fdata;
  string topic;
  string data;
  
  if(file_size(index_root + file) <= 0) {
    tell_object(user,"Oops.  That file seems to be empty.\n" +
                PRESS_RETURN);
    input_to("delay_prompt");
    return;
  }
  else {
    fdata = explode(file,"/") - ({ "" });
    if(has_merge_dirs)
      topic = implode(fdata[1..], "/");
    else
      topic = file;

    if(sizeof(fdata[has_merge_dirs..]))
      data = "\n" + MTOPIC_COLOR + "Topic: "+topic+"%^RESET%^\n\n";
    else
      data = "\n";

    data += insert_mergetext(NROFF_HAND->nroff_file(index_root + file,
                                                    cols), file);

    if(!sizeof(view_files) && sizeof(other_matches)) {
      data += MTOPIC_COLOR + sprintf("*Other topics: %-=*s\n\n", 
              (cols - 15), implode(other_matches, ", ")) +
              "%^RESET%^";
      other_matches = 0;
    }

    if(sizeof(explode(data,"\n")) < (rows-1)) {
      tell_object(user,data+"\n");
      if(MENUING || sizeof(view_files)) {
        tell_object(user,PRESS_RETURN);
        input_to("delay_prompt");
      }
      else
        dest_me();
      return;
    }
    else {
      user->set_finish_func("finish_func");
      user->more_string(data,"["+topic+"]");
    }
  }
}

/* This one loops around (with the help of finish_func) showing
   more than one document.
*/
void show_mult_documents(string *thedocs) {
  string *bits;

  view_files = thedocs[1..];

  /* The last document could conceivably actually be a directory. */
  if((sizeof(thedocs) == 1) && (thedocs[0][<6..<1] == "README")) {
    bits = explode(thedocs[0], "/");

    if(sizeof(bits) == (has_merge_dirs + 1))
      curpath = ({ });
    else
      curpath = bits[has_merge_dirs..<2];    
    
    curmenu = thedocs[0];
    show_menu(curmenu);
  }
  else
    show_document(thedocs[0]);
}

void menu_prompt(string str) {
  tell_object(user, MTOPIC_COLOR + "/" + implode(curpath,"/") + 
                    ">%^RESET%^ ");
  input_to("input_prompt");
}

/* Called when they press <ret> after viewing something. */
void delay_prompt(string str) {
  if(str == "Q")
    view_files = 0;

  if(sizeof(view_files))
    show_mult_documents(view_files);
  else if(MENUING)
    show_menu(curmenu,1);
  else
    dest_me();
}

/* Called when more_string() gets done. */
varargs void finish_func(string lvl) {
  if(lvl || (!MENUING && !sizeof(view_files)))
    delay_prompt(lvl);
  else {
    tell_object(user,PRESS_RETURN);
    input_to("delay_prompt");
  }
}

void input_prompt(string str) {
  int i;
  string doc;
  int match_type;
  string *matches;
  string *tmp;
  string errmsg;

  if(!str)
    str = "";

  /* Command processing first */
  switch(str) {
    case ""      :
    case ".."    :
    case "cd .." : /* Drop a level. */
                   if(!sizeof(curpath)) { /* exit the system */
                     dest_me();
                     return;
                   }
    
                   /* Otherwise, just go one directory shallower */
                   if(sizeof(curpath) == 1)
                     curpath = ({ });
                   else
                     curpath = curpath[0..<2];
      
                   curmenu = handler->get_doc_fnames(
                                        implode(curpath,"/"))[0];
                   show_menu(curmenu);
                   return;

    case "q"     : /* They're quitting the system. */
                   dest_me();
                   return;

    case "h"     : /* They want menu help. */
                   show_document("_MENU_HELP");
                   return;

    case "H"     : /* They want examples */
                   show_document("_EXAMPLES");
                   return;
  
    case "."     : /* Redisplay menu */
                   show_menu(curmenu,1);
                   return;

    case "+x"    : /* Menu expert */
                   user->add_property("MENU_EXPERT", 1);
                   expert = 1;
                   menu_screen = 0;
                   tell_object(user, "You will no longer see the "
                     "help lines at the bottom of menus.\n" + 
                     PRESS_RETURN);
                   input_to("delay_prompt");
                   return;

    case "-x"    : /* Menu novice */
                   user->remove_property("MENU_EXPERT");
                   expert = 0;
                   menu_screen = 0;
                   tell_object(user, "You will again see the help "
                     "lines at the bottom of menus.\n" +
                     PRESS_RETURN);
                   input_to("delay_prompt");
                   return;
  }

  /* else */
  if((str[0] == '@') || (str[0] == '^')) { 
    /* They want to jump elsewhere in the system */
    do_query(handler, str[1..], user, all_matches, 0, expert, str[0]);
    return; 
  }

#ifdef NUMBERS
  /* else, they may be picking by number(s) */
  matches = match_by_number(str);

  if(sizeof(matches)) {
    if(member_array(matches[0],docs) > -1)
      match_type = 'd';
    else
      match_type = 's';
  }
#endif

  /* else, this is a real attempt to view a subtopic or document. */
  /* First, we look for exact matches. */
  if(!sizeof(matches)) {
    i = member_array(str,subdirs);

    if(i > -1) { 
      match_type = 's'; /* subdir */
      matches = ({ subdirs[i] });
    }
    else {
      i = member_array(str,docs);
      if(i > -1) {
        match_type = 'd'; /* doc */
        matches = ({ docs[i] });
      }
      else { /* here's where it gets tricky.  We're going back and
                trying for UNIX-type wildcards */
        /* need to "translate" wildcard-speak into regexp. */
        str = wild2regexp(str,1);

        if(errmsg = catch(matches = regexp(docs, str))) {
          tell_object(user, errmsg + "\n\n" + PRESS_RETURN);
          input_to("delay_prompt");
          return;
        }

        if(sizeof(matches))
          match_type = 'd';
        else if(sizeof(matches = regexp(subdirs,str)))
          match_type = 's';
        else { /* nothing valid was typed, whatsoever. */
          tell_object(user,"That selection is not valid.\n\n" + 
                      PRESS_RETURN);
          input_to("delay_prompt");
          return;
        }

        /* Does a stricter exact-wildcard match leave us with anything?
           The above allowed sloppy end-of-string things.
        */
        if(sizeof(tmp = regexp(matches, (str + "$"))))
          matches = tmp;
      }
    }
  }

  /* crap out immediately if they matched multiple subdirs. */
  if((sizeof(matches) > 1) && (match_type == 's')) {
     tell_object(user, "Your query matches multiple subtopics.\n\n" +
                       PRESS_RETURN);
     input_to("delay_prompt");
     return;
  }

  if(match_type == 'd') {
    for(i=0; i < sizeof(matches); i++) {
      if(sizeof(curpath))
        doc = implode(curpath,"/") + "/" + matches[i];
      else
        doc = matches[i];
      matches[i] = handler->get_doc_fnames(doc)[0];
    }
    show_mult_documents(matches); 
  }
  else { /* They want to view a new directory */
    if(sizeof(curpath))
      doc = implode(curpath,"/") + "/" + matches[0];
    else
      doc = matches[0];
    curmenu = handler->get_doc_fnames(doc)[0];
    curpath += explode(matches[0], "/");
    show_menu(curmenu);
  }
}

/* Takes text of a file and its mergedir and inserts text from
   a "lower" mergedir in the location denoted by:
    #*(mergedir)#*

   If the entry is merely #*#*, the next lower mergedir is used
   that has a copy of the file.

   If form is #*[/path/to/file]#* it's an absolute path.
   If form is #*[path/to/file]#*, it's relative to index root.
   If form is #*[file]#*, it's in the current dir.
*/
string insert_mergetext(string text, string fname) {
  string begin, mid, end;
  int curdir, wantdir;
  string newfile, genfile;
  string ret = text;
  string *tmp, *tmp2;
  int FOUND;

  tmp = explode(fname, "/");
  genfile = implode(tmp[1..], "/");
  curdir = member_array(tmp[0] + "/", mergedirs);
 
  while(sscanf(ret, "%s#*%s#*%s", begin, mid, end)) {
    FOUND = 0;

    if(mid[0] == '[') {
      if(strsrch(mid, '/') == -1) { /* in current dir */
        tmp2 = handler->get_doc_fnames(implode(tmp[1..<2], "/") + 
                                       mid[1..<2]);
        if(sizeof(tmp2)) {
          FOUND = 1;
          newfile = index_root + tmp2[0];
        }
      }
      else if(mid[1] != '/') { /* path inside man system */
        tmp2 = handler->get_doc_fnames(mid[1..<2]);
        if(sizeof(tmp2)) {
          FOUND = 1;
          newfile = index_root + tmp2[0];
        }
      }
      else /* full path */
        if(file_size(mid[1..<2]) > 0) {
          FOUND = 1;
          newfile = mid[1..<2];
        }
    }
    else if(has_merge_dirs && (curdir < (sizeof(mergedirs) - 1))) { 
      /* We're messing with mergedirs */
      if(!strlen(mid)) { /* next lowest match */
        for(wantdir = curdir + 1; wantdir < sizeof(mergedirs); wantdir++)
          if(file_size(index_root + mergedirs[wantdir] + genfile) > 0) {
            FOUND = 1;
            newfile = index_root + mergedirs[wantdir] + genfile;
            break;
          }
      }
      else if(mid[0] == '(') {
        wantdir = member_array(mid[1..<2] + "/", mergedirs);
        if((wantdir > -1) && (wantdir > curdir) &&
           (file_size(index_root + mergedirs[wantdir] + genfile) > 0)) {
          FOUND = 1;
          newfile = index_root + mergedirs[wantdir] + genfile;
        }
      }
    }

    if(FOUND)
      ret = replace_string(ret, "#*" + mid + "#*", 
              insert_mergetext(NROFF_HAND->nroff_file(newfile, cols), 
                               newfile));
    else
      ret = replace_string(ret, "#*" + mid + "#*", "");
  }

  return ret;
}

#ifdef NUMBERS
/* Takes a space-separated list of numbers and translates them
   into associated documents.  If there is non-numerical
   text in the list, it'll just bomb out.  If there is *one*
   subtopic, it'll return a list of any documents and that
   one subtopic at the end.  Otherwise, it either returns
   just the documents, or, if none, just the directories.
*/
string *match_by_number(string str) {
  string *list_docs = ({ });
  string *list_subdirs = ({ });
  string *tmp;
  int i, num;

  tmp = explode(str, " ") - ({ "" });

  for(i=0; i < sizeof(tmp); i++)
    if((num = atoi(tmp[i])) && ((num + "") == tmp[i])) {
      if(num <= sizeof(subdirs))
        list_subdirs += ({ subdirs[num - 1] });
      else if(num <= (sizeof(subdirs) + sizeof(docs)))
        list_docs += ({ docs[num - sizeof(subdirs) - 1] });
      /* else index is out of range.  We'll just ignore it. */
    }
    else /* They entered something alphabetic or otherwise odd */
      return ({ });

  if(sizeof(list_subdirs) == 1)
    return list_docs + list_subdirs;
  else if(sizeof(list_docs))
    return list_docs;
  else
    return list_subdirs;
}
#endif

void create() {
  seteuid(getuid());
  curpath = ({ });
  MENUING = 0;  /* paranoia */
}

void dest_me() {
  destruct(this_object());
}

void reset() {
  if(!user && clonep(this_object()))
    dest_me();
}