skylib_mudos_v1/
skylib_mudos_v1/bin/
skylib_mudos_v1/bin/db/
skylib_mudos_v1/mudlib/banish/a/
skylib_mudos_v1/mudlib/banish/b/
skylib_mudos_v1/mudlib/banish/c/
skylib_mudos_v1/mudlib/banish/d/
skylib_mudos_v1/mudlib/banish/e/
skylib_mudos_v1/mudlib/banish/f/
skylib_mudos_v1/mudlib/banish/g/
skylib_mudos_v1/mudlib/banish/h/
skylib_mudos_v1/mudlib/banish/j/
skylib_mudos_v1/mudlib/banish/l/
skylib_mudos_v1/mudlib/banish/m/
skylib_mudos_v1/mudlib/banish/n/
skylib_mudos_v1/mudlib/banish/o/
skylib_mudos_v1/mudlib/banish/p/
skylib_mudos_v1/mudlib/banish/r/
skylib_mudos_v1/mudlib/banish/s/
skylib_mudos_v1/mudlib/banish/t/
skylib_mudos_v1/mudlib/banish/u/
skylib_mudos_v1/mudlib/banish/w/
skylib_mudos_v1/mudlib/cmds/
skylib_mudos_v1/mudlib/cmds/admin/
skylib_mudos_v1/mudlib/cmds/guild-race/
skylib_mudos_v1/mudlib/cmds/guild-race/crafts/
skylib_mudos_v1/mudlib/cmds/guild-race/magic/
skylib_mudos_v1/mudlib/cmds/guild-race/other/
skylib_mudos_v1/mudlib/cmds/living/broken/
skylib_mudos_v1/mudlib/cmds/player/group_cmds/
skylib_mudos_v1/mudlib/d/admin/
skylib_mudos_v1/mudlib/d/admin/room/
skylib_mudos_v1/mudlib/d/admin/room/we_care/
skylib_mudos_v1/mudlib/d/admin/save/
skylib_mudos_v1/mudlib/d/admin/text/
skylib_mudos_v1/mudlib/d/learning/TinyTown/buildings/
skylib_mudos_v1/mudlib/d/learning/TinyTown/map/
skylib_mudos_v1/mudlib/d/learning/TinyTown/roads/
skylib_mudos_v1/mudlib/d/learning/chars/
skylib_mudos_v1/mudlib/d/learning/functions/
skylib_mudos_v1/mudlib/d/learning/handlers/
skylib_mudos_v1/mudlib/d/learning/help_topics/
skylib_mudos_v1/mudlib/d/learning/help_topics/npcs/
skylib_mudos_v1/mudlib/d/learning/help_topics/objects/
skylib_mudos_v1/mudlib/d/learning/help_topics/rcs_demo/
skylib_mudos_v1/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/crowd/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/situations/
skylib_mudos_v1/mudlib/d/learning/save/
skylib_mudos_v1/mudlib/d/learning/school/
skylib_mudos_v1/mudlib/d/learning/school/add_sc/
skylib_mudos_v1/mudlib/d/learning/school/characters/
skylib_mudos_v1/mudlib/d/learning/school/general/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/basic_commands/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/edtutor/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_mudos_v1/mudlib/d/learning/school/items/
skylib_mudos_v1/mudlib/d/learning/school/npc_school/
skylib_mudos_v1/mudlib/d/learning/school/room_school/
skylib_mudos_v1/mudlib/d/learning/school/room_school/room_basic/
skylib_mudos_v1/mudlib/d/learning/school/room_school/situations/
skylib_mudos_v1/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_mudos_v1/mudlib/d/learning/text/
skylib_mudos_v1/mudlib/d/liaison/
skylib_mudos_v1/mudlib/d/mudlib/
skylib_mudos_v1/mudlib/d/mudlib/changes/
skylib_mudos_v1/mudlib/d/playtesters/
skylib_mudos_v1/mudlib/d/playtesters/effects/
skylib_mudos_v1/mudlib/d/playtesters/handlers/
skylib_mudos_v1/mudlib/d/playtesters/items/
skylib_mudos_v1/mudlib/d/sage/
skylib_mudos_v1/mudlib/doc/
skylib_mudos_v1/mudlib/doc/creator/
skylib_mudos_v1/mudlib/doc/driver/
skylib_mudos_v1/mudlib/doc/driver/efuns/arrays/
skylib_mudos_v1/mudlib/doc/driver/efuns/buffers/
skylib_mudos_v1/mudlib/doc/driver/efuns/compile/
skylib_mudos_v1/mudlib/doc/driver/efuns/filesystem/
skylib_mudos_v1/mudlib/doc/driver/efuns/floats/
skylib_mudos_v1/mudlib/doc/driver/efuns/functions/
skylib_mudos_v1/mudlib/doc/driver/efuns/general/
skylib_mudos_v1/mudlib/doc/driver/efuns/mappings/
skylib_mudos_v1/mudlib/doc/driver/efuns/mixed/
skylib_mudos_v1/mudlib/doc/driver/efuns/mudlib/
skylib_mudos_v1/mudlib/doc/driver/efuns/numbers/
skylib_mudos_v1/mudlib/doc/driver/efuns/parsing/
skylib_mudos_v1/mudlib/doc/known_command/
skylib_mudos_v1/mudlib/doc/login/
skylib_mudos_v1/mudlib/doc/lpc/basic_manual/
skylib_mudos_v1/mudlib/doc/lpc/intermediate/
skylib_mudos_v1/mudlib/doc/new/add_command/
skylib_mudos_v1/mudlib/doc/new/events/
skylib_mudos_v1/mudlib/doc/new/handlers/
skylib_mudos_v1/mudlib/doc/new/living/race/
skylib_mudos_v1/mudlib/doc/new/living/spells/
skylib_mudos_v1/mudlib/doc/new/object/
skylib_mudos_v1/mudlib/doc/new/player/
skylib_mudos_v1/mudlib/doc/new/room/guild/
skylib_mudos_v1/mudlib/doc/new/room/outside/
skylib_mudos_v1/mudlib/doc/new/room/storeroom/
skylib_mudos_v1/mudlib/doc/object/
skylib_mudos_v1/mudlib/doc/playtesters/
skylib_mudos_v1/mudlib/doc/policy/
skylib_mudos_v1/mudlib/doc/weapons/
skylib_mudos_v1/mudlib/global/
skylib_mudos_v1/mudlib/global/creator/
skylib_mudos_v1/mudlib/global/handlers/
skylib_mudos_v1/mudlib/global/virtual/setup_compiler/
skylib_mudos_v1/mudlib/include/cmds/
skylib_mudos_v1/mudlib/include/effects/
skylib_mudos_v1/mudlib/include/npc/
skylib_mudos_v1/mudlib/include/room/
skylib_mudos_v1/mudlib/include/shops/
skylib_mudos_v1/mudlib/net/daemon/
skylib_mudos_v1/mudlib/net/daemon/chars/
skylib_mudos_v1/mudlib/net/inherit/
skylib_mudos_v1/mudlib/net/obj/
skylib_mudos_v1/mudlib/obj/amulets/
skylib_mudos_v1/mudlib/obj/b_day/
skylib_mudos_v1/mudlib/obj/clothes/
skylib_mudos_v1/mudlib/obj/dwarmours/plate/
skylib_mudos_v1/mudlib/obj/dwclothes/transport/horse/
skylib_mudos_v1/mudlib/obj/dwscabbards/
skylib_mudos_v1/mudlib/obj/dwweapons/axes/
skylib_mudos_v1/mudlib/obj/dwweapons/chains/
skylib_mudos_v1/mudlib/obj/faith/symbols/
skylib_mudos_v1/mudlib/obj/fungi/
skylib_mudos_v1/mudlib/obj/gatherables/
skylib_mudos_v1/mudlib/obj/instruments/
skylib_mudos_v1/mudlib/obj/magic/
skylib_mudos_v1/mudlib/obj/media/
skylib_mudos_v1/mudlib/obj/misc/player_shop/
skylib_mudos_v1/mudlib/obj/monster/godmother/
skylib_mudos_v1/mudlib/obj/monster/transport/
skylib_mudos_v1/mudlib/obj/rings/
skylib_mudos_v1/mudlib/obj/spells/
skylib_mudos_v1/mudlib/obj/stationery/
skylib_mudos_v1/mudlib/obj/stationery/envelopes/
skylib_mudos_v1/mudlib/obj/stationery/papers/
skylib_mudos_v1/mudlib/obj/toys/
skylib_mudos_v1/mudlib/obj/vessels/
skylib_mudos_v1/mudlib/obj/weapons/swords/
skylib_mudos_v1/mudlib/save/autodoc/
skylib_mudos_v1/mudlib/save/leaflets/
skylib_mudos_v1/mudlib/save/mail/
skylib_mudos_v1/mudlib/save/new_soul/data/
skylib_mudos_v1/mudlib/save/parcels/
skylib_mudos_v1/mudlib/save/playerinfo/
skylib_mudos_v1/mudlib/save/players/d/
skylib_mudos_v1/mudlib/save/random_names/
skylib_mudos_v1/mudlib/save/random_names/data/
skylib_mudos_v1/mudlib/save/terrains/
skylib_mudos_v1/mudlib/save/terrains/tutorial_desert/
skylib_mudos_v1/mudlib/save/terrains/tutorial_grassy_field/
skylib_mudos_v1/mudlib/save/terrains/tutorial_mountain/
skylib_mudos_v1/mudlib/save/todo_lists/
skylib_mudos_v1/mudlib/secure/
skylib_mudos_v1/mudlib/secure/cmds/admin/
skylib_mudos_v1/mudlib/secure/cmds/lord/
skylib_mudos_v1/mudlib/secure/config/
skylib_mudos_v1/mudlib/secure/handlers/autodoc/
skylib_mudos_v1/mudlib/secure/handlers/intermud/
skylib_mudos_v1/mudlib/secure/include/global/
skylib_mudos_v1/mudlib/secure/save/
skylib_mudos_v1/mudlib/secure/save/handlers/
skylib_mudos_v1/mudlib/secure/std/classes/
skylib_mudos_v1/mudlib/secure/std/modules/
skylib_mudos_v1/mudlib/std/commands/
skylib_mudos_v1/mudlib/std/commands/shadows/
skylib_mudos_v1/mudlib/std/creator/
skylib_mudos_v1/mudlib/std/dom/
skylib_mudos_v1/mudlib/std/effects/
skylib_mudos_v1/mudlib/std/effects/external/
skylib_mudos_v1/mudlib/std/effects/fighting/
skylib_mudos_v1/mudlib/std/effects/priest/
skylib_mudos_v1/mudlib/std/effects/room/
skylib_mudos_v1/mudlib/std/environ/
skylib_mudos_v1/mudlib/std/guilds/
skylib_mudos_v1/mudlib/std/guilds/old/
skylib_mudos_v1/mudlib/std/languages/
skylib_mudos_v1/mudlib/std/languages/BACKUPS/
skylib_mudos_v1/mudlib/std/liquids/
skylib_mudos_v1/mudlib/std/npc/
skylib_mudos_v1/mudlib/std/npc/goals/
skylib_mudos_v1/mudlib/std/npc/goals/basic/
skylib_mudos_v1/mudlib/std/npc/goals/misc/
skylib_mudos_v1/mudlib/std/npc/plans/
skylib_mudos_v1/mudlib/std/npc/plans/basic/
skylib_mudos_v1/mudlib/std/npc/types/
skylib_mudos_v1/mudlib/std/npc/types/helper/
skylib_mudos_v1/mudlib/std/npcs/
skylib_mudos_v1/mudlib/std/outsides/
skylib_mudos_v1/mudlib/std/races/shadows/
skylib_mudos_v1/mudlib/std/room/basic/topography/
skylib_mudos_v1/mudlib/std/room/controller/
skylib_mudos_v1/mudlib/std/room/inherit/topography/
skylib_mudos_v1/mudlib/std/room/topography/area/
skylib_mudos_v1/mudlib/std/room/topography/iroom/
skylib_mudos_v1/mudlib/std/room/topography/milestone/
skylib_mudos_v1/mudlib/std/shadows/curses/
skylib_mudos_v1/mudlib/std/shadows/disease/
skylib_mudos_v1/mudlib/std/shadows/fighting/
skylib_mudos_v1/mudlib/std/shadows/healing/
skylib_mudos_v1/mudlib/std/shadows/magic/
skylib_mudos_v1/mudlib/std/shadows/poison/
skylib_mudos_v1/mudlib/std/shadows/rituals/
skylib_mudos_v1/mudlib/std/shadows/room/
skylib_mudos_v1/mudlib/std/shops/controllers/
skylib_mudos_v1/mudlib/std/shops/objs/
skylib_mudos_v1/mudlib/std/shops/player_shop/
skylib_mudos_v1/mudlib/std/socket/
skylib_mudos_v1/mudlib/std/soul/
skylib_mudos_v1/mudlib/std/soul/d/
skylib_mudos_v1/mudlib/std/soul/e/
skylib_mudos_v1/mudlib/std/soul/i/
skylib_mudos_v1/mudlib/std/soul/j/
skylib_mudos_v1/mudlib/std/soul/k/
skylib_mudos_v1/mudlib/std/soul/l/
skylib_mudos_v1/mudlib/std/soul/n/
skylib_mudos_v1/mudlib/std/soul/o/
skylib_mudos_v1/mudlib/std/soul/q/
skylib_mudos_v1/mudlib/std/soul/u/
skylib_mudos_v1/mudlib/std/soul/v/
skylib_mudos_v1/mudlib/std/soul/y/
skylib_mudos_v1/mudlib/std/soul/z/
skylib_mudos_v1/mudlib/std/stationery/
skylib_mudos_v1/mudlib/w/
skylib_mudos_v1/mudlib/w/default/
skylib_mudos_v1/mudlib/w/default/armour/
skylib_mudos_v1/mudlib/w/default/clothes/
skylib_mudos_v1/mudlib/w/default/item/
skylib_mudos_v1/mudlib/w/default/npc/
skylib_mudos_v1/mudlib/w/default/room/
skylib_mudos_v1/mudlib/w/default/weapon/
skylib_mudos_v1/mudlib/www/
skylib_mudos_v1/mudlib/www/download/
skylib_mudos_v1/mudlib/www/java/
skylib_mudos_v1/mudlib/www/secure/
skylib_mudos_v1/mudlib/www/secure/lpc/advanced/
skylib_mudos_v1/mudlib/www/secure/lpc/intermediate/
skylib_mudos_v1/v22.2b14-DSv10/
skylib_mudos_v1/v22.2b14-DSv10/ChangeLog.old/
skylib_mudos_v1/v22.2b14-DSv10/Win32/
skylib_mudos_v1/v22.2b14-DSv10/compat/
skylib_mudos_v1/v22.2b14-DSv10/compat/simuls/
skylib_mudos_v1/v22.2b14-DSv10/include/
skylib_mudos_v1/v22.2b14-DSv10/mudlib/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/clone/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/command/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/data/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/etc/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/include/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/inherit/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/inherit/master/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/log/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/compiler/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/efuns/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/operators/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/u/
skylib_mudos_v1/v22.2b14-DSv10/tmp/
skylib_mudos_v1/v22.2b14-DSv10/windows/
/**
 * The automatic document generator.  It takes source files from various
 * directories and creates help files from the comments embeded in the
 * code.
 *
 * @see /obj/handlers/autodoc/autodoc_handler
 * @author Pinkfish
 * @started Fri Oct 24 16:03:57 EDT 1997
 */

#include <autodoc.h>

#define EOF -1

nosave mapping private_functions;

mapping public_functions,
        protected_functions,
        inherits,
        main_docs,
        define_docs,
        includes,
        class_docs;

string file_name;

int last_changed, num_failed_tries;

// Temporary.
nosave string current_comment, current_file;
nosave int current_position, changed;

// We will only handle simple defines.  Function ones we will ignore.
nosave mapping defines;
nosave mixed exclude_methods;

private mapping parse_comment( string stuff );
private void do_parse_file( function func );
private int query_file_position();
private void handle_inherit( mixed bits );

/** @ignore yes */
private void setup() {
    changed = 0;
    main_docs = 0;
    file_name = "";
    private_functions = ([ ]);
    public_functions = ([ ]);
    protected_functions = ([ ]);
    inherits = ([ ]);
    defines = ([ ]);
    define_docs = ([ ]);
    includes = ([ ]);
    class_docs = ([ ]);
    current_comment = 0;
    current_file = "";
    current_position = 0;
    last_changed = 0;
    exclude_methods = ({ "setup", "create", "init", "dest_me", "reset" });
} /* setup() */

/** @ignore yes */
void create() {
    seteuid(getuid());
    setup();
} /* create() */

/** @ignore yes */
private int query_file_position() { return current_position; }

/** @ignore yes */
private int lookahead_character( int num ) {
    if( current_position + num - 1 < strlen(current_file) )
        return current_file[ current_position + num - 1 ];
    return EOF;
} /* lookahead_character() */

/** @ignore yes */
private int next_character() {
    if( current_position < strlen(current_file) )
        return current_file[current_position++];
    return EOF;
} /* next_character() */

/** @ignore yes */
private int pop_character( int num ) { current_position += num; }

/**
 * @ignore yes
 * Throw away everything to the end of the line.
 */
private void skip_to_end_of_line() {
    int ch;

    do {
        ch = next_character();
    } while( ch != '\r' && ch != '\n' && ch != EOF );

} /* skip_to_end_of_line() */

/**
 * @ignore yes
 * Throw away all the characters until the end of the comment.
 */
private string skip_to_end_of_comment() {
    string data;
    int ch, ok;

    /*
     * This will pull all the stuff out of a comment and stick them into
     * a nice string.  Wheeee!
     */
    data = "";
    do {
        ok = 1;
        ch = next_character();
        if( ch == '*' && lookahead_character(1) == '/' ) {
            /* End of comment... */
            ok = 0;
            pop_character(1);
        } else if( ch == '\r' || ch == '\n' ) {
            data += "\n";
            if( lookahead_character(1) == ' ' )
                pop_character(1);
            if( lookahead_character(1) == '*' && lookahead_character(2) != '/' ) {
                pop_character(1);
                if( lookahead_character(1) == ' ' )
                    pop_character(1);
            }
        } else if( ch == EOF ) {
            ok = 0;
        } else if( ch == '\\' && (
            lookahead_character(1) == '/' ||
            lookahead_character(1) == '*' ||
            lookahead_character(1) == '\\' ) ) {
            /* Build up our comment stuff... */
        } else {
            data += sprintf("%c", ch );
        }
    } while( ok );

    return data;

} /* skip_to_end_of_comment() */

/**
 * @ignore yes
 * Skips all the spaces and comments that are in our way.
 */
private void skip_spaces_and_comments() {
    int ok;

    do {
        switch( lookahead_character(1) ) {
            case ' ' :
            case '\t' :
            case '\n' :
            case '\r' :
              ok = 1;
              /* Move our index up one... */
              pop_character(1);
            break;
            case '/' :
              /* Could be a comment... */
              if( lookahead_character(2) == '/' ) {
                  ok = 1;
                  skip_to_end_of_line();
              } else if( lookahead_character(2) == '*' ) {
                  ok = 1;
                  if( lookahead_character(3) != '*' ||
                      lookahead_character(4) == '*' ) {
                      /* Make sure it is not a code comment... */
                      pop_character(2);
                      skip_to_end_of_comment();
                      current_comment = 0;
                  } else {
                      pop_character(3);
                      if( lookahead_character(1) == ' ' )
                          pop_character(1);
                      if( !main_docs )
                          main_docs = parse_comment( skip_to_end_of_comment() );
                      else
                          current_comment = skip_to_end_of_comment();
                  }
              } else {
                  ok = 0;
              }
            break;
            default :
              ok = 0;
              break;
        }
    } while (ok);

} /* skip_spaces_and_comments() */

/**
 * @ignore yes
 * Expands the defines...
 */
private string expand_token( string token ) { return defines[token]; }

/*
 * Gets the next token...
 */
private string get_word() {
   string data;
   int ok;
   int ch;

   skip_spaces_and_comments();
   ok = 1;
   data = "";
   ch = lookahead_character(1);
   if ((ch >= 'a' && ch <= 'z') ||
       (ch >= 'A' && ch <= 'Z') ||
       (ch == '_')) {
      do {
         ch = lookahead_character(1);
         if ((ch >= 'a' && ch <= 'z') ||
             (ch >= 'A' && ch <= 'Z') ||
             (ch >= '0' && ch <= '9') ||
             (ch == '_')) {
            ch = next_character();
            data += sprintf("%c", ch);
         } else {
            ok = 0;
         }
      } while (ok);
      if (expand_token(data)) {
         current_file = expand_token(data) + current_file[current_position..];
         current_position = 0;
         return get_word();
      }
   } else if ((ch >= '0' && ch <= '9') ||
              (ch == '-')) {
      if (ch == '-') {
         data += sprintf("%c", next_character());
      }
      /* Number, only search for number bits... */
      do {
         ch = lookahead_character(1);
         if ((ch >= '0' && ch <= '9') ||
             (ch >= '.')) {
            ch = next_character();
            data += sprintf("%c", ch);
         } else {
            ok = 0;
         }
      } while (ok);
   } else if (ch == '\"' || ch == '\'') {
      int end_ch;

      end_ch = ch;
      ch = next_character();
      data += sprintf("%c", ch);
      do {
         ch = next_character();
         if (ch == end_ch) {
            ok = 0;
            data += sprintf("%c", ch);
         } else if (ch == '\\') {
            /* Skip the next character... */
            ch = next_character();
            data += sprintf("\\%c", ch);
         } else if (ch == EOF) {
            ok = 0;
         } else {
            data += sprintf("%c", ch);
         }
      } while (ok);
   } else if (ch == '(') {
      if (lookahead_character(2) == '{' ||
          lookahead_character(2) == '[') {
         return sprintf("%c%c", next_character(), next_character());
      }
      return sprintf("%c", next_character());
   } else if (ch == '}' || ch == ']') {
      if (lookahead_character(2) == ')') {
         return sprintf("%c%c", next_character(), next_character());
      }
      return sprintf("%c", next_character());
   } else if (ch == ';' ||
              ch == ')' ||
              ch == '=' ||
              ch == '{' ||
              ch == '}') {
      /* open brace, semi colon, close brace... is a special thingy... */
      return sprintf("%c", next_character());
   } else if (ch == EOF) {
      return "";
   } else {
      /* All non-space, non alphanumeric...  Dump together... */
      do {
         ch = lookahead_character(1);
         if ((ch >= 'a' && ch <= 'z') ||
             (ch >= 'A' && ch <= 'Z') ||
             (ch >= '0' && ch <= '9') ||
             (ch == '_') ||
             (ch == ' ') ||
             (ch == '\t') ||
             (ch == '\n') ||
             (ch == '(') ||
             (ch == ')') ||
             (ch == EOF) ||
             (ch == ';') ||
             (ch == '=') ||
             (ch == '{') ||
             (ch == '}') ||
             (ch == '\'') ||
             (ch == '\"') ||
             (ch == '\r')) {
            ok = 0;
         } else {
            ch = next_character();
            data += sprintf("%c", ch);
         }
      } while (ok);
   }

   return data;
} /* get_word() */

/*
 * Attempts to get a complete statement...
 */
private mixed get_statement(string start) {
   mixed bits;
   string curr;
   int depth;
   string temp_comment;
   int last_pos;
   int in_class;

   /* If it starts with the class keyword, then is must be a class... */

   /* We rip until a semi colon or an open brace... */
   /*
    * If we find a semi colon...  Then it is a predef or an inherit
    * statement...
    */
   bits = ({ start });
   do {
      last_pos = query_file_position();
      curr = get_word();
      bits += ({ curr });
      if (last_pos != query_file_position()) {
         /* Make sure it is doing something... */
         reset_eval_cost();
      }
   } while (curr != ";" && curr != "{" && curr != "");

   if (curr == "{") {
      if (member_array("class", bits) != -1 &&
          member_array("(", bits) == -1) {
         /*
          * We have a class, now we need to parse this in a useful way.
          * Sadly this cannot fit into the normal way of parsing.  It is an
          * exception to one of those rules :)
          */
         in_class = 1;
      }
      /* Grab the rest...  but ignore it... */
      /* Keep the current comment though... */
      temp_comment = current_comment;
      depth = 1;
      do {
        last_pos = query_file_position();
        curr = get_word();
        if (curr == "{") {
           depth++;
        } else if (curr == "}") {
           depth--;
        } else if (curr == "") {
           /* End of file... */
           depth = 0;
        }
        if (last_pos != query_file_position()) {
           /* Make sure it is doing something... */
           reset_eval_cost();
        }
        if (in_class) {
           bits += ({ curr });
        }
      } while (depth > 0);
      current_comment = temp_comment;
   }

   return bits;
} /* get_statement() */

/*
 * This parses the comment into the appropriate bits...
 */
private mapping parse_comment(string stuff) {
   string *bits;
   int i;
   mapping frog;
   string name;
   int j;
   int rabbit;

   if (!stuff) {
      return ([ ]);
   }

   if (stuff[0] == '@') {
      stuff = "\n" + stuff;
   } else {
      stuff = "\n@main " + stuff;
   }
   bits = explode(stuff, "\n@");
   frog = ([ ]);
   for (i = 0; i< sizeof(bits); i++) {
      j = strsrch(bits[i], " ");
      rabbit = strsrch(bits[i], "\n");
      if (j == -1 || (rabbit != -1 && rabbit < j)) {
         j = rabbit;
      }
      if (j > 0) {
         name = bits[i][0..j - 1];
         stuff = bits[i][j+1..];
         if (!frog[name]) {
            frog[name] = ({ stuff });
         } else {
            frog[name] += ({ stuff });
         }
      }
   }
   return frog;
} /* parse_comment() */

/*
 * Handles a class...
 */
private void handle_class(mixed bits) {
   string name;
   int i;
   string *types;
   mapping comm;

   name = bits[1];
   types = ({ });
   i = member_array("{", bits);
   if (i != -1) {
      /* Ok, figure out all the elements and types. */
      bits = bits[i + 1..];
      while (sizeof(bits)) {
         i = member_array(";", bits);
         if (i != -1) {
            types += ({ ({ bits[i - 1], bits[0..i - 2] }) });
            bits = bits[i + 1..];
         } else {
            bits = ({ });
         }
      }
      comm = parse_comment(current_comment);
      if (!comm["ignore"]) {
         class_docs[name] = ({ 0, types,
                               parse_comment(current_comment) });
      }
   }
} /* handle_class() */

/*
 * Handles an inherit...  Sticks all the needed stuff in like the
 * thing being inherited and the state of it and stuff.
 */
private void handle_inherit(mixed bits) {
   int pos;
   string name;

   pos = member_array("inherit", bits);
   if( pos >= 0 ) {
       /* Need to strip off the last thingy which should be a semi colon. */
       name = implode( map( bits[pos+1.. sizeof(bits)-2],
           function( string str ) {
               if( str[0] == '\"') {
                   sscanf( str, "\"%s\"", str );
                   return str;
               }
               if( str = string_to_define(str) )
                   return str;
               return "";
           } ), "");
       inherits[name] = bits[0..pos-1];
   }
} /* handle_inherit() */

/*
 * We have found a function definition...  Create the information
 * we need from it.
 */
private void handle_function_definition(mixed bits) {
   int pos;
   int end_pos;
   int new_pos;
   string name;
   string *type;
   mixed args;
   mapping comm;

   pos = member_array("(", bits);
   if (pos > 0) {
      name = bits[pos-1];
      if (member_array(name, exclude_methods) == -1) {
         type = bits[0..pos-2];
         if (sizeof(type) == 0) {
            type = ({ "int" });
         }
         end_pos = member_array(")", bits, pos);
         args = ({ });
         if (end_pos > pos + 1) {
            /* Whoo, there are some arguments... */
            pos++;
            while (member_array(",", bits, pos) != -1) {
               new_pos = member_array(",", bits, pos);
               args += ({ bits[pos..new_pos-2], bits[new_pos-1] });
               pos = new_pos + 1;
            }
            args += ({ bits[pos..end_pos -2], bits[end_pos-1] });
         }
         comm = parse_comment(current_comment);
         if( !comm["ignore"] ) {
             if( member_array("private", type) != -1 ) {
                 type -= ({ "private" });
                 private_functions[name] = ({ type, args, comm });
             } else if (member_array("protected", type) != -1) {
                 type -= ({ "protected" });
                 protected_functions[name] = ({ type, args, comm });
             } else {
                 type -= ({ "public" });
                 public_functions[name] = ({ type, args, comm });
             }
         }
      }
      current_comment = 0;
   }
} /* handle_function_definition() */

/*
 * Gets the rest of the line.  Mostly used by the hash stuff...
 */
private string get_rest_of_line() {
   string value;
   int ch;
   int last_pos;

   value = "";
   /* Skip the spaces at the start... */
   ch = lookahead_character(1);
   while (ch == ' ' || ch == '\t') {
      pop_character(1);
      ch = lookahead_character(1);
   }

   /* Get the whole definition */
   do {
      last_pos = query_file_position();
      ch = next_character();
      if (ch == '\\') {
         /* Skip one!  This is escaped... */
         ch = next_character();
         if (ch == '\r' && lookahead_character(1) == '\n') {
          /* Handle MSDOS sillyness */
            ch = next_character();
         }
         ch = ' ';
      }
      if (last_pos != query_file_position()) {
         reset_eval_cost();
      }
      value += sprintf("%c", ch);
   } while (ch != '\n' && ch != '\r' && ch != EOF);

   return value;

} /* get_rest_of_line() */

/*
 * Handles the hairy #define's and #includes.
 */
private void handle_hash() {
   int i;
   string token;
   string name;
   string value;
   string *bits;
   string stuff;
   string inc_name;
   string curr_comm;
   mapping comm;

   token = get_word();
   /* Not really sure what to do about these right now... */
   switch (token) {
      case "define" :
      case "defin" :
         /* Ok, with a define...  we... get a name and a substitution value. */
         curr_comm = current_comment;
         value = get_rest_of_line();
         if (sscanf(value, "%s %s", name, value) == 2) {
            defines[name] = value;
            if (token == "define") {
               comm = parse_comment(curr_comm);
               if (!comm["ignore"]) {
                  define_docs[name] = comm;
               }
               current_comment = 0;
            }
         }
         break;
      case "include" :
         /*
          * Eeeek!  This will be evil... At the moment assume they don't
          * do arithmetic in the #include line...
          */
         value = get_rest_of_line();
         if (value[0] == '\"') {
            /* This means a local inherit... */
            bits = explode(file_name, "/");
            sscanf(value, "\"%s\"", name);
            stuff = read_file(implode(bits[0..<2], "/") +
                              "/" + name);
            if (stuff) {
               inc_name = "/" + implode(bits[0..<2], "/") +
                              "/" + name;
            }
         } else if (value[0] == '<') {
            /* This means it could be a global inherit... */
            sscanf(value, "<%s>", name);
         }

         if (name[0] == '/') {
            stuff = read_file(name);
            if (stuff) {
               inc_name = name;
            }
         }

         bits = master()->define_include_dirs();
         while (!stuff && i < sizeof(bits)) {
            stuff = read_file(sprintf(bits[i], name));
            if (stuff) {
               /* Remove all the autodoc comments from the include file... */
               stuff = replace_string(stuff, "/**", "/* ");
               /* Ignore included classes. */
               stuff = replace_string(stuff, "class ", "clas ");
               stuff = replace_string(stuff, "#define ", "#defin ");
               inc_name = sprintf(bits[i], name);
            }
            i++;
         }

         if (inc_name) {
            /* Zap double '//'s */
            inc_name = replace(inc_name, "//", "/");
            if (inc_name[0] != '/') {
               inc_name = "/" + inc_name;
            }
         }

         if( stuff ) {
             /* We found it!  Stick the include bit in where it is needed... */
             if( !includes[inc_name] ) {
                 current_file = stuff + current_file[current_position..];
                 current_position = 0;
             }
             /* Update the time as well. */
             includes[inc_name] = unguarded( (: stat($(inc_name)) :) )[1];
         }
         break;
      default :
         /* Pragma's and other silly hash things */
         skip_to_end_of_line();
         break;
   }
} /* handle_hash() */

/*
 * Gets the next token...  This is state dependant, we need to know
 * what state we are in to figure out what sort of token we are probably
 * looking for...
 */
private void next_statement() {
   string token;
   string *bits;

   token = get_word();
   /* A hash directive... */
   if( token[0] == '#') {
       return handle_hash();
   } else if (token == ";") {
       return ;
   } else if (token != "") {
       bits = get_statement(token);
       if( member_array("inherit", bits) != -1 ) {
           /* An inherit statement. */
           return handle_inherit(bits);
       } else if( bits[0] == "class" && member_array("(", bits) == -1 ) {
           return handle_class(bits);
       } else if( bits[sizeof(bits) - 1] == "{" &&
           member_array("=", bits) == -1 ) {
           return handle_function_definition(bits);
       } else {
           /* It was a predef or a variable declaration... */
           return;
       }
   }
} /* next_statement() */

/**
 * Loads up the currently set file name from the archives.
 */
void load_file() {
   unguarded( (: restore_object( AUTODOC_SAVE_DIR +
                 replace_string(file_name, "/", ".") ) :) );

   if( !includes )
       includes = ([ ]);
   if( !class_docs )
       class_docs = ([ ]);

} /* load_file() */

/**
 * Saves the current file name to the archives.
 */
void save_file() {
   unguarded( (: save_object( AUTODOC_SAVE_DIR +
                 replace_string(file_name, "/", ".") ) :) );
} /* save_file() */

/**
 * Parses the input file figuring out all the documentation bits of it.
 * @param name the name of the file to parse
 * @param func the function to call when the parsing is finished
 * @param only_load a flag telling us to only load the information
 */
void parse_file( string name, function func, int only_load ) {
   int curr_change, reload;
   string my_name, new_file, tmp_str;

   setup();
   file_name = name;
   load_file();

   if( !only_load ) {
       if( sizeof( unguarded( (: stat($(name)) :) ) ) ) {
           if( file_size(name) != -2 ) {
               curr_change = unguarded( (: stat($(name)) :) )[1];
           } else {
               AUTODOC_H->remove_file(name);
               if( name[<1] != '/' )
                   name += "/";
               foreach( new_file in unguarded( (: stat($(name)) :) ) ) {
                   if( file_size(name+new_file) != -2 &&
                       new_file[sizeof(new_file)-2..sizeof(new_file)] )
                       AUTODOC_H->add_file(name+new_file);
               }
               call_out( (: evaluate($1) :), 2, func );
               return;
           }

           reload = curr_change > last_changed || file_name(PO) == AUTODOC_H;

           if( !reload ) {
               /* Check to see if the include files have changed. */
               foreach( my_name in keys(includes) ) {
                   if( unguarded( (: file_exists($(my_name)) :) ) ) {
                       new_file = AUTODOC_SAVE_DIR +
                                  replace_string(my_name, "/", ".")+".o";

                       // Add all missing includes to the handler.
                       if( !unguarded( (: file_exists($(new_file)) :) ) ) {
                           event( users(), "inform", "Autodoc: Adding \""+
                               my_name+"\"", "autodoc");
                           AUTODOC_H->add_file(my_name);
                           reload = 1;
                           continue;
                       }

                       if( unguarded( (: stat($(my_name)) :) )[1] >
                           unguarded( (: stat($(new_file)) :) )[1] ) {
                           event( users(), "inform", "Autodoc: Updating \""+
                               my_name+"\" - \""+new_file+"\" outdated",
                               "autodoc");
                           reload = 1;
                           break;
                       }

                       tmp_str = implode( explode( my_name, "/" ), ".");
                       tmp_str = "/www/external/autodoc/"+tmp_str+".html";

                       if( !unguarded( (: file_exists($(tmp_str)) :) ) ) {
                           event( users(), "inform", "Autodoc: Updating \""+
                               my_name+"\" - \""+tmp_str+"\" doesn't exist",
                               "autodoc");
                           reload = 1;
                           break;
                       }

                       if( unguarded( (: stat($(my_name)) :) )[1] >
                           unguarded( (: stat($(tmp_str)) :) )[1] ) {
                           event( users(), "inform", "Autodoc: Updating \""+
                               my_name+"\" - \""+tmp_str+"\" outdated",
                               "autodoc");
                           reload = 1;
                           break;
                       }
                   }
               }
           }

           if( reload ) {
               setup();
               num_failed_tries = 0;
               file_name = name;
               changed = 1;
               last_changed = curr_change;
               current_file = read_file(name);
               current_position = 0;
               event( users(), "inform", "Autodoc: Parsing \""+file_name+"\"",
                   "autodoc");
               if( catch( do_parse_file(func) ) )
                   evaluate(func);
           } else {
               if( num_failed_tries ) {
                   num_failed_tries = 0;
                   save_file();
               }
               event( users(), "inform", "Autodoc: Skipping \""+file_name+"\"",
                   "autodoc");
               call_out( (: evaluate($1) :), 2, func );
           }
       } else {
           event( users(), "inform", "Autodoc: No file \""+file_name+"\"",
               "autodoc");
           num_failed_tries++;
           save_file();
           call_out( (: evaluate($1) :), 2, func );
       }
   } else {
       call_out( (: evaluate($1) :), 2, func );
   }

} /* parse_file() */

/** @ignore yes */
private void do_parse_file( function func ) {
   int num;

   /* Darn, changed while we were reading it. */
   if( file_size(file_name) != -2 &&
       unguarded( (: stat(file_name) :) )[1] > last_changed )
       return parse_file( file_name, func, 0 );

   num = 0;

   while( lookahead_character(1) != EOF && num < 2 ) {
       next_statement();
       num++;
   }

   if( lookahead_character(1) == EOF ) {
       save_file();
       call_out( (: evaluate($1) :), 2, func );
   } else {
       call_out( (: do_parse_file($1) :), 2, func );
   }

} /* do_parse_file() */

/**
 * Returns the inherits mapping of the system.  This returns a mapping of the
 * form ([ inherit_name : ({ flags }) ]).  Where the name of the inherit is
 * something like "/std/object" and the flags are things you can apply to
 * an inherit, like "protected" or "private".  If there are no flags then
 * the flags will be an empty array.
 * @return a mapping of things inherited by this file
 */
mapping query_inherits() { return inherits; }

/**
 * The mapping of private functions.
 * The mapping is of the form ([ func_name : ({ type, args, docs }) ]).
 * The type bit is an array of the type name, ie: ({ "int" }) or
 * ({ "mixed", "*" }).  The args bit looks like ({ "name", type }),
 * where the type is the same as in the previous array.  The docs is
 * a mapping of the form ([ "tag" : ({ data }) ]), where each reference
 * to a tag creates a new element in the data array.
 * @return a mapping containing the private functions
 */
mapping query_private_functions() { return private_functions; }

/**
 * The mapping of public functions.
 * The mapping is of the form ([ func_name : ({ type, args, docs }) ]).
 * The type bit is an array of the type name, ie: ({ "int" }) or
 * ({ "mixed", "*" }).  The args bit looks like ({ "name", type }),
 * where the type is the same as in the previous array.  The docs is
 * a mapping of the form ([ "tag" : ({ data }) ]), where each reference
 * to a tag creates a new element in the data array.
 * @return a mapping containing the public functions
 */
mapping query_public_functions() { return public_functions; }

/**
 * The mapping of protected functions.
 * The mapping is of the form ([ func_name : ({ type, args, docs }) ]).
 * The type bit is an array of the type name, ie: ({ "int" }) or
 * ({ "mixed", "*" }).  The args bit looks like ({ "name", type }),
 * where the type is the same as in the previous array.  The docs is
 * a mapping of the form ([ "tag" : ({ data }) ]), where each reference
 * to a tag creates a new element in the data array.
 * @return a mapping containing the protected functions
 */
mapping query_protected_functions() { return protected_functions; }

/**
 * Returns the main docs for the class.  The mapping is of
 * the form ([ "tag" : ({ data }) ]), where each reference
 * to a tag creates a new element in the data array.
 * @return a mapping containing the main docs for the file
 */
mapping query_main_docs() { return main_docs || ([ ]); }

/**
 * The file name being processed.
 * @return the name of the file being processed
 */
string query_file_name() { return file_name; }

/**
 * The defines which were setup in the class.  This is the mapping of the
 * defines which were processed.  The format of the mapping is
 * ([ "name" : "value" ]), where the name is the name of the define and
 * the value is what to replace it with.
 * @return the mapping of defines
 */
mapping query_defines() { return defines; }

/**
 * Did the file change?  Checks to see if the file changed since it
 * was last read.
 * @return 1 if it changed, 0 if it has not changed
 */
int query_changed() { return changed; }

/**
 * This method returns the number of times the file was attempted to
 * be read and it was discovered not to exist at all.
 * @return the number of times it was unable to be read
 */
int query_num_failed_tries() { return num_failed_tries; }

/**
 * The files included by this one.
 * @return an array of included files
 */
string *query_includes() { return keys(includes); }

/** @ignore yes */
mapping query_all_includes() { return includes; }

/**
 * The documentation for the defines.  This is mostly used by the include
 * file documentation system.
 * @return the mapping of define names to documentation
 */
mapping query_define_docs() { return define_docs; }

/**
 * The documentation for the classes.
 * @return the mapping of the class names to documentation
 */
mapping query_class_docs() { return class_docs; }

/** @ignore yes */
void dest_me() { destruct(TO); }