skylib_fluffos_v3/
skylib_fluffos_v3/bin/
skylib_fluffos_v3/bin/db/
skylib_fluffos_v3/fluffos-2.9-ds2.04/
skylib_fluffos_v3/fluffos-2.9-ds2.04/ChangeLog.old/
skylib_fluffos_v3/fluffos-2.9-ds2.04/Win32/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/simuls/
skylib_fluffos_v3/fluffos-2.9-ds2.04/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/clone/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/command/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/data/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/etc/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/master/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/log/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/compiler/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/efuns/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/operators/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/u/
skylib_fluffos_v3/fluffos-2.9-ds2.04/tmp/
skylib_fluffos_v3/fluffos-2.9-ds2.04/windows/
skylib_fluffos_v3/mudlib/
skylib_fluffos_v3/mudlib/cmds/
skylib_fluffos_v3/mudlib/cmds/admin/
skylib_fluffos_v3/mudlib/cmds/guild-race/
skylib_fluffos_v3/mudlib/cmds/living/broken/
skylib_fluffos_v3/mudlib/cmds/player/group_cmds/
skylib_fluffos_v3/mudlib/cmds/playtester/
skylib_fluffos_v3/mudlib/d/admin/
skylib_fluffos_v3/mudlib/d/admin/room/
skylib_fluffos_v3/mudlib/d/admin/room/we_care/
skylib_fluffos_v3/mudlib/d/admin/save/
skylib_fluffos_v3/mudlib/d/admin/text/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/buildings/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/map/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/roads/
skylib_fluffos_v3/mudlib/d/learning/chars/
skylib_fluffos_v3/mudlib/d/learning/functions/
skylib_fluffos_v3/mudlib/d/learning/handlers/
skylib_fluffos_v3/mudlib/d/learning/help_topics/
skylib_fluffos_v3/mudlib/d/learning/help_topics/npcs/
skylib_fluffos_v3/mudlib/d/learning/help_topics/objects/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/crowd/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/situations/
skylib_fluffos_v3/mudlib/d/learning/save/
skylib_fluffos_v3/mudlib/d/learning/school/
skylib_fluffos_v3/mudlib/d/learning/school/add_sc/
skylib_fluffos_v3/mudlib/d/learning/school/characters/
skylib_fluffos_v3/mudlib/d/learning/school/general/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/basic_commands/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/edtutor/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_fluffos_v3/mudlib/d/learning/school/items/
skylib_fluffos_v3/mudlib/d/learning/school/npc_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/room_basic/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/situations/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_fluffos_v3/mudlib/d/learning/text/
skylib_fluffos_v3/mudlib/d/liaison/
skylib_fluffos_v3/mudlib/d/mudlib/
skylib_fluffos_v3/mudlib/d/mudlib/changes/
skylib_fluffos_v3/mudlib/d/playtesters/
skylib_fluffos_v3/mudlib/d/playtesters/effects/
skylib_fluffos_v3/mudlib/d/playtesters/handlers/
skylib_fluffos_v3/mudlib/d/playtesters/items/
skylib_fluffos_v3/mudlib/d/sage/
skylib_fluffos_v3/mudlib/doc/
skylib_fluffos_v3/mudlib/doc/creator/
skylib_fluffos_v3/mudlib/doc/driver/
skylib_fluffos_v3/mudlib/doc/driver/efuns/arrays/
skylib_fluffos_v3/mudlib/doc/driver/efuns/buffers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/calls/
skylib_fluffos_v3/mudlib/doc/driver/efuns/compile/
skylib_fluffos_v3/mudlib/doc/driver/efuns/filesystem/
skylib_fluffos_v3/mudlib/doc/driver/efuns/floats/
skylib_fluffos_v3/mudlib/doc/driver/efuns/functions/
skylib_fluffos_v3/mudlib/doc/driver/efuns/general/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mappings/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mixed/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mudlib/
skylib_fluffos_v3/mudlib/doc/driver/efuns/numbers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/parsing/
skylib_fluffos_v3/mudlib/doc/login/
skylib_fluffos_v3/mudlib/doc/lpc/basic_manual/
skylib_fluffos_v3/mudlib/doc/lpc/intermediate/
skylib_fluffos_v3/mudlib/doc/new/add_command/
skylib_fluffos_v3/mudlib/doc/new/events/
skylib_fluffos_v3/mudlib/doc/new/handlers/
skylib_fluffos_v3/mudlib/doc/new/living/race/
skylib_fluffos_v3/mudlib/doc/new/living/spells/
skylib_fluffos_v3/mudlib/doc/new/object/
skylib_fluffos_v3/mudlib/doc/new/player/
skylib_fluffos_v3/mudlib/doc/new/room/guild/
skylib_fluffos_v3/mudlib/doc/new/room/outside/
skylib_fluffos_v3/mudlib/doc/new/room/storeroom/
skylib_fluffos_v3/mudlib/doc/object/
skylib_fluffos_v3/mudlib/doc/playtesters/
skylib_fluffos_v3/mudlib/doc/policy/
skylib_fluffos_v3/mudlib/doc/weapons/
skylib_fluffos_v3/mudlib/global/
skylib_fluffos_v3/mudlib/global/creator/
skylib_fluffos_v3/mudlib/handlers/
skylib_fluffos_v3/mudlib/include/casino/
skylib_fluffos_v3/mudlib/include/cmds/
skylib_fluffos_v3/mudlib/include/effects/
skylib_fluffos_v3/mudlib/include/npc/
skylib_fluffos_v3/mudlib/include/room/
skylib_fluffos_v3/mudlib/include/shops/
skylib_fluffos_v3/mudlib/net/daemon/
skylib_fluffos_v3/mudlib/net/daemon/chars/
skylib_fluffos_v3/mudlib/net/inherit/
skylib_fluffos_v3/mudlib/net/obj/
skylib_fluffos_v3/mudlib/net/obj/BACKUPS/
skylib_fluffos_v3/mudlib/obj/amulets/
skylib_fluffos_v3/mudlib/obj/armours/plate/
skylib_fluffos_v3/mudlib/obj/b_day/
skylib_fluffos_v3/mudlib/obj/clothes/transport/horse/
skylib_fluffos_v3/mudlib/obj/faith/symbols/
skylib_fluffos_v3/mudlib/obj/fungi/
skylib_fluffos_v3/mudlib/obj/gatherables/
skylib_fluffos_v3/mudlib/obj/instruments/
skylib_fluffos_v3/mudlib/obj/media/
skylib_fluffos_v3/mudlib/obj/misc/player_shop/
skylib_fluffos_v3/mudlib/obj/monster/godmother/
skylib_fluffos_v3/mudlib/obj/monster/transport/
skylib_fluffos_v3/mudlib/obj/rings/
skylib_fluffos_v3/mudlib/obj/scabbards/
skylib_fluffos_v3/mudlib/obj/spells/
skylib_fluffos_v3/mudlib/obj/stationery/
skylib_fluffos_v3/mudlib/obj/stationery/envelopes/
skylib_fluffos_v3/mudlib/obj/toys/
skylib_fluffos_v3/mudlib/obj/vessels/
skylib_fluffos_v3/mudlib/obj/weapons/axes/
skylib_fluffos_v3/mudlib/obj/weapons/chains/
skylib_fluffos_v3/mudlib/obj/weapons/maces/BACKUPS/
skylib_fluffos_v3/mudlib/save/autodoc/
skylib_fluffos_v3/mudlib/save/book_handler/
skylib_fluffos_v3/mudlib/save/books/history/calarien/
skylib_fluffos_v3/mudlib/save/mail/
skylib_fluffos_v3/mudlib/save/new_soul/data/
skylib_fluffos_v3/mudlib/save/parcels/
skylib_fluffos_v3/mudlib/save/playerinfo/
skylib_fluffos_v3/mudlib/save/players/d/
skylib_fluffos_v3/mudlib/save/players/s/
skylib_fluffos_v3/mudlib/save/random_names/
skylib_fluffos_v3/mudlib/save/random_names/data/
skylib_fluffos_v3/mudlib/save/terrains/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_desert/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_grassy_field/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_mountain/
skylib_fluffos_v3/mudlib/save/todo_lists/
skylib_fluffos_v3/mudlib/secure/
skylib_fluffos_v3/mudlib/secure/cmds/admin/
skylib_fluffos_v3/mudlib/secure/cmds/lord/
skylib_fluffos_v3/mudlib/secure/config/
skylib_fluffos_v3/mudlib/secure/handlers/autodoc/
skylib_fluffos_v3/mudlib/secure/handlers/intermud/
skylib_fluffos_v3/mudlib/secure/include/global/
skylib_fluffos_v3/mudlib/secure/save/
skylib_fluffos_v3/mudlib/secure/save/handlers/
skylib_fluffos_v3/mudlib/secure/std/
skylib_fluffos_v3/mudlib/secure/std/classes/
skylib_fluffos_v3/mudlib/secure/std/modules/
skylib_fluffos_v3/mudlib/std/creator/
skylib_fluffos_v3/mudlib/std/dom/
skylib_fluffos_v3/mudlib/std/effects/
skylib_fluffos_v3/mudlib/std/effects/external/
skylib_fluffos_v3/mudlib/std/effects/fighting/
skylib_fluffos_v3/mudlib/std/effects/magic/
skylib_fluffos_v3/mudlib/std/effects/magic/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/other/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/priest/
skylib_fluffos_v3/mudlib/std/effects/room/
skylib_fluffos_v3/mudlib/std/environ/
skylib_fluffos_v3/mudlib/std/guilds/
skylib_fluffos_v3/mudlib/std/guilds/old/
skylib_fluffos_v3/mudlib/std/languages/
skylib_fluffos_v3/mudlib/std/liquids/
skylib_fluffos_v3/mudlib/std/npc/
skylib_fluffos_v3/mudlib/std/npc/goals/
skylib_fluffos_v3/mudlib/std/npc/goals/basic/
skylib_fluffos_v3/mudlib/std/npc/goals/misc/
skylib_fluffos_v3/mudlib/std/npc/plans/
skylib_fluffos_v3/mudlib/std/npc/plans/basic/
skylib_fluffos_v3/mudlib/std/npc/types/
skylib_fluffos_v3/mudlib/std/npc/types/helper/
skylib_fluffos_v3/mudlib/std/npcs/
skylib_fluffos_v3/mudlib/std/outsides/
skylib_fluffos_v3/mudlib/std/races/shadows/
skylib_fluffos_v3/mudlib/std/room/basic/BACKUPS/
skylib_fluffos_v3/mudlib/std/room/basic/topography/
skylib_fluffos_v3/mudlib/std/room/controller/
skylib_fluffos_v3/mudlib/std/room/inherit/topography/
skylib_fluffos_v3/mudlib/std/room/topography/area/
skylib_fluffos_v3/mudlib/std/room/topography/iroom/
skylib_fluffos_v3/mudlib/std/room/topography/milestone/
skylib_fluffos_v3/mudlib/std/shadows/curses/
skylib_fluffos_v3/mudlib/std/shadows/disease/
skylib_fluffos_v3/mudlib/std/shadows/fighting/
skylib_fluffos_v3/mudlib/std/shadows/healing/
skylib_fluffos_v3/mudlib/std/shadows/magic/
skylib_fluffos_v3/mudlib/std/shadows/poison/
skylib_fluffos_v3/mudlib/std/shadows/room/
skylib_fluffos_v3/mudlib/std/shops/controllers/
skylib_fluffos_v3/mudlib/std/shops/objs/
skylib_fluffos_v3/mudlib/std/shops/player_shop/
skylib_fluffos_v3/mudlib/std/socket/
skylib_fluffos_v3/mudlib/std/soul/d/
skylib_fluffos_v3/mudlib/std/soul/e/
skylib_fluffos_v3/mudlib/std/soul/i/
skylib_fluffos_v3/mudlib/std/soul/j/
skylib_fluffos_v3/mudlib/std/soul/k/
skylib_fluffos_v3/mudlib/std/soul/l/
skylib_fluffos_v3/mudlib/std/soul/n/
skylib_fluffos_v3/mudlib/std/soul/o/
skylib_fluffos_v3/mudlib/std/soul/q/
skylib_fluffos_v3/mudlib/std/soul/r/
skylib_fluffos_v3/mudlib/std/soul/u/
skylib_fluffos_v3/mudlib/std/soul/v/
skylib_fluffos_v3/mudlib/std/soul/y/
skylib_fluffos_v3/mudlib/std/soul/z/
skylib_fluffos_v3/mudlib/std/stationery/
skylib_fluffos_v3/mudlib/w/
skylib_fluffos_v3/mudlib/w/default/
skylib_fluffos_v3/mudlib/w/default/armour/
skylib_fluffos_v3/mudlib/w/default/clothes/
skylib_fluffos_v3/mudlib/w/default/item/
skylib_fluffos_v3/mudlib/w/default/npc/
skylib_fluffos_v3/mudlib/w/default/room/
skylib_fluffos_v3/mudlib/w/default/weapon/
skylib_fluffos_v3/mudlib/www/
skylib_fluffos_v3/mudlib/www/java/
skylib_fluffos_v3/mudlib/www/secure/
skylib_fluffos_v3/mudlib/www/secure/lpc/advanced/
skylib_fluffos_v3/mudlib/www/secure/lpc/intermediate/
skylib_fluffos_v3/win32/
/**
 * This is the new errors command to deal with the new error handler
 * code.
 * @author Pinkfish
 * @started Mon Apr 28 12:01:45 PDT 2003
 */
inherit COMMAND_BASE;
#include <error_handler.h>
#include <db.h>
#include <nroff.h>
#include <player_handler.h>

class user_data {
   class error_query query;
   class error_summary* errors;
   int pos;
   int last_touched;
   string direction;
   string report;
}

private mapping _user_query;

void create() {
   _user_query = ([ ]);
}

int find_index(object person, int id) {
   int i;
   class error_summary* errors;

   if (!_user_query[person->query_name()]) {
      return 0;
   }
   _user_query[person->query_name()]->last_touched = time();
   errors = _user_query[person->query_name()]->errors;
   for (i = 0; i < sizeof(errors); i++) {
      if (id == errors[i]->id) {
         return i;
      }
   }
   return 0;
}

int max_index(object person) {
   if (!_user_query[person->query_name()]) {
      return 0;
   }
   _user_query[person->query_name()]->last_touched = time();
   return sizeof(_user_query[person->query_name()]->errors);
}

int id_at(object person, int index) {
   if (!_user_query[person->query_name()] || index < 0 ||
       index >= sizeof(_user_query[person->query_name()]->errors)) {
      return 0;
   }

   _user_query[person->query_name()]->last_touched = time();
   return _user_query[person->query_name()]->errors[index]->id;
}

string menu_line(object person) {
   string ret;

   // Figure out the nice little menu line.
   ret = "[" + (_user_query[person->query_name()]->pos + 1) + " of " +
          sizeof(_user_query[person->query_name()]->errors) + "] "
         "STFCOLHA-+PNGQ : ";
   return ret;
}

void finish_details(object person, int verbose,
                    int type, mixed data) {
   string ret;
   class error_details details;
   class error_complete complete;
   class error_comment comment;
   class error_forward forward;
   class error_replies reply;
   class user_data user_data;

   if (type != DB_SUCCESS) {
      tell_object(person, "%^BOLD%^%^RED%^Error: " + data + "%^RESET%^\n");
      return ;
   }

   user_data = _user_query[person->query_name()];

   complete = data[0];
   details = complete->details;
   ret = "%^BOLD%^%^CYAN%^Bug #" + details->summary->id + "%^RESET%^ " +
         details->summary->status + " " + details->summary->type + " " +
         details->summary->category + "\n";

   ret += "%^BOLD%^Date Reported%^RESET%^     : " +
             ctime(details->summary->entry_date) + "\n";
   ret += "%^BOLD%^Assigned To%^RESET%^       : " +
          details->summary->assigned_to + "\n";
   ret += "%^BOLD%^Reporter%^RESET%^          : " + details->summary->reporter + "\n";
   ret += "%^BOLD%^File name%^RESET%^         : " + details->summary->filename + "\n";
   if (verbose) {
      ret += "%^BOLD%^Directory%^RESET%^         : " + details->summary->directory + "\n";
   }

   if (details->summary->status == "FIXED" ||
       details->summary->status == "DENIED") {
      ret += "%^BOLD%^Fixed By%^RESET%^          : " + details->fixer +
             " (" + ctime(details->fix_date) + ")\n";
   }

   if (verbose) {
      if (sizeof(complete->forwards)) {
         ret += "%^BOLD%^Forwards%^RESET%^          :\n";
         // Print out the forwards.
         foreach (forward in complete->forwards) {
            ret += "$I$5=     " + forward->forwarder + " from " +
                   forward->old_directory + " at " +
                   ctime(forward->date)[4..15] + "\n";
         }
      }
   }

   ret += "$I$0=" + details->report;
   if (verbose) {
      ret += details->runtime;
   } else if (details->runtime && details->runtime != "") {
      ret += "[Runtime Available]\n";
   }

   user_data->report = details->report;

   if (verbose) {
      ret += "$I$3=   ";
      // Show the error replies before the comments.
      foreach (reply in complete->replies) {
         ret += "%^BOLD%^Date Sent%^RESET%^   : " + ctime(reply->senddate)[4..15] +
                "\n%^BOLD%^From%^RESET%^        : " + reply->sender +
                "\n%^BOLD%^To%^RESET%^          : " + reply->recipient +
                "\n%^BOLD%^Subject%^RESET%^     : " + reply->subject + "<br>" +
                reply->message + "\n";
      }
      if (sizeof(complete->replies) && sizeof(complete->comments)) {
         ret += "$I$0=-------------------------------------------------------------\n$I$3=   ";
      }
      foreach (comment in complete->comments) {
         ret += "%^BOLD%^Comment by " + comment->commenter + " at " +
                ctime(comment->date)[4..15] + "%^RESET%^\n" +
                comment->comment + "\n";
      }
   } else {
      if (sizeof(complete->replies) > 0) {
         ret += sizeof(complete->replies) + " error replies.\n";
      }
      if (sizeof(complete->comments) > 0) {
         ret += sizeof(complete->comments) + " comments.\n";
      }
   }
   person->set_finish_func("finish_more_details", this_object());
   tell_object(person, "$P$Bug #" + details->summary->id + "$P$" + ret);
}

int finish_more_details() {
   object ob;

   ob = this_player();
   write(menu_line(ob));
   input_to("menu_response");
}

int finish_forward(object person, int pos, string new_dir, int type, mixed mess) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (type != DB_SUCCESS) {
      tell_object(person, "Unable to forward bug #" +
                          data->errors[data->pos]->id +
                          " to " + new_dir + " (" + mess + ")");
   } else {
      tell_object(person, "Forwarded bug #" +
                          data->errors[data->pos]->id +
                          " from " + data->errors[data->pos]->directory +
                          " to " + new_dir);
      data->errors[data->pos]->directory = new_dir;
   }
}

void finish_change_status(object person, string status, int command,
                          string subject, int no_reply, int type, mixed mess) {
   class user_data data;


   data = _user_query[person->query_name()];

   if (type != DB_SUCCESS) {
      tell_object(person, "Database error: " + mess);
      if (!command) {
         tell_object(person, menu_line(person));
      }
      return ;
   }

   tell_object(person, "Updated status of bug #" + data->errors[data->pos]->id +
               " to " + status + (no_reply ?  " (no reply) " : "") +"\n");
   if (!command) {
      data = _user_query[person->query_name()];
      tell_object(person, menu_line(person));
      data->errors[data->pos]->status = status;
   }
}

void finish_status_message(object person, string status, int command,
                           string subject, string mess) {
   class user_data data;
   int pos;

   data = _user_query[person->query_name()];
   if (!mess) {
      tell_object(person, "Message aborted.\n");
      if (!command) {
         tell_object(person, menu_line(person));
      }
      return ;
   }

   if (status == "THANKS" || status == "TEMPORARY") {
      status = "FIXED";
   }
   if (status == "NOTREPRODUCIBLE" || status == "NOINFO" ||
       status == "NOTPOSSIBLE") {
      status = "DENIED";
   }

   pos = strsrch("$report$", mess);
   if (pos == -1) {
      mess += "\n\n" + data->report;
   } else {
      mess = replace_string(mess, "$report$", data->report);
   }

   if (!command) {
      input_to("menu_response");
   }
   // Do it.
   if (!ERROR_HANDLER->do_change_status(data->errors[data->pos]->id,
                        status, subject == 0, subject, person->query_name(),
                        mess,
                        (: finish_change_status, person, status, command, 
                                          subject, subject == 0 :))) {
      tell_object(person, "Unhappily an error occured.\n");
      if (!command) {
         tell_object(person, menu_line(person));
      }
   }
}

string query_subject_from_type(string type, class error_summary error) {
   switch (type) {
   case "THANKS" :
      return "Thanks for " + error->category + " " + error->type;
   case "NOTPOSSIBLE" :
      return "Not possible to fix " + error->category + " " + error->type;
   case "NOTREPRODUCIBLE" :
      return "Not able to reproduce " + error->category + " " + error->type;
   case "FIXED" :
      return "Fixed bug #" + error->id;
   case "DENIED" :
      return "Denied bug #" + error->id;
   default :
      return "Change status of bug #" + error->id + " to " +
             type;
   }
}

string query_message_from_type(object person, string type,
                               class error_summary error) {
   switch (upper_case(type)) {
   case "THANKS" :
      return "Thanks for the " + lower_case(error->type) + ".\n\nGood luck!\n" +
             person->query_cap_name();
   case "TEMPORARY" :
      return "Your " + lower_case(error->type) +
             " report was a temporary problem in the system and has "
             "since been resolved.  Thanks for reporting it."
             "\n\nGood luck!\n" +
             person->query_cap_name();
   case "NOTENOUGHINFORMATION" :
      return "Your " + lower_case(error->type) +
             " report did not contain enough information for this " +
             lower_case(error->type) + " to be resolved.  Please report "
             "again with more detailed information."
             "\n\nGood luck!\n" +
             person->query_cap_name();
   case "NOTREPRODUCIBLE" :
      return "Unable to reproduce your " + lower_case(error->type) +
             " report.  If you can track down exactly the circumstances "
             "which generate this error, please report it again."
             "\n\nGood luck!\n" +
             person->query_cap_name();
   case "NOTPOSSIBLE" :
      return "Thank you for your " + lower_case(error->type) +
             " report, this " + lower_case(error->type) +
             " is not possible to fix, because of code restrictions."
             "\n\nGood luck!\n" +
             person->query_cap_name();
   case "FIXED" :
   case "DENIED" :
      return "Thank you for your " + lower_case(error->type) +
             " report, this " + lower_case(error->type) +
             " has now been " + lower_case(type) + ".\n\nGood luck!\n" +
             person->query_cap_name();
   case "FIXING" :
      return "Thank you for your " + lower_case(error->type) +
             " report, this " + lower_case(error->type) +
             " is currently being fixed.\n\n"
             "Good luck!\n" +
             person->query_cap_name();
   case "CONSIDERING" :
      return "Thank you for your " + lower_case(error->type) +
             " report, this " + lower_case(error->type) +
             " is currently being considered for future fixing.\n\n"
             "Good luck!\n" +
             person->query_cap_name();
   case "CUSTOM" :
      return "custom";
   case "NONE" :
      return "none";
   case "OPEN" :
      return "Thanks for the " + lower_case(error->type) + ".\n\nGood luck!\n" +
             person->query_cap_name();
   }
}

void finish_change_type(object person, string new_type, int type, mixed mess) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (type != DB_SUCCESS) {
      tell_object(person, "Sql Error: " + mess);
      tell_object(person, menu_line(person));
      return ;
   }

   tell_object(person, "Bug #" + data->errors[data->pos]->id +
                       " has a new type of " + new_type + "\n");
   tell_object(person, menu_line(person));
   data->errors[data->pos]->type = new_type;
}

void finish_change_category(object person, string new_category, int type, mixed mess) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (type != DB_SUCCESS) {
      tell_object(person, "Sql Error: " + mess);
      tell_object(person, menu_line(person));
      return ;
   }

   tell_object(person, "Bug #" + data->errors[data->pos]->id +
                       " has a new category of " + new_category + "\n");
   tell_object(person, menu_line(person));
   data->errors[data->pos]->type = new_category;
}

void finish_assign_bug(object person, string assigner, int type, mixed mess) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (type != DB_SUCCESS) {
      tell_object(person, "Sql Error: " + mess);
      tell_object(person, menu_line(person));
      return ;
   }

   tell_object(person, "Bug #" + data->errors[data->pos]->id +
                       " is assigned to " + assigner + "\n");
   tell_object(person, menu_line(person));
   data->errors[data->pos]->assigned_to = assigner;
}

void finish_adding_comment(object person, int type, mixed mess) {
   if (type != DB_SUCCESS) {
      tell_object(person, "SQL Error: " + mess);
      tell_object(person, menu_line(person));
   }

   tell_object(person, "Added a comment to the bug.\n");
   tell_object(person, menu_line(person));
}

void finish_comment(object person, int id, string comment) {
   if (!comment) {
      write("Aborted adding the comment.\n");
      write(menu_line(person));
      input_to("menu_response");
      return ;
   }
   input_to("menu_response");
   if (!ERROR_HANDLER->do_comment(id, person->query_name(), comment,
                             (: finish_adding_comment, person :))) {
      write("Big bad error.\n");
      write(menu_line(person));
   }
}

int do_bug_details(object person, int id, int verbose) {
   if (!ERROR_HANDLER->do_query_bug_details(id, (: finish_details, person,
                                                   verbose :))) {
      tell_object(person, "Error doing stuff.");
   }
   return 1;
}

int do_forward(object person, string new_dir) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (!ERROR_HANDLER->do_forward(data->errors[data->pos]->id,
                person->query_name(), new_dir,
                    (: finish_forward, person, data->pos, new_dir :))) {
      write("A horrible error occured.\n");
      return 0;
   }
   return 1;
}

int do_change_status(object person, string status, int command, string subject,
                     string mess) {
   if (mess == "custom") {
      tell_object(person, "User $report$ to place the error report in the "
                  "mail.  If $report$ is not included, the report will be "
                  "added onto the end.\n");
      person->do_edit("", (: finish_status_message, person, status, command,
                                                    subject :));
   } else if (mess == "none") {
      finish_status_message(person, status, command, 0, "");
   } else {
      finish_status_message(person, status, command, subject, mess);
   }
}

int do_change_type(object person, string type) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (!ERROR_HANDLER->do_change_type(data->errors[data->pos]->id, type,
       (: finish_change_type, person, type :))) {
      tell_object(person, "Error doing stuff.\n");
   }
   return 1;
}

int do_change_category(object person, string category) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (!ERROR_HANDLER->do_change_category(data->errors[data->pos]->id, category,
       (: finish_change_category, person, category :))) {
      tell_object(person, "Error doing stuff.\n");
   }
   return 1;
}

int do_assign_bug(object person, string assigner) {
   class user_data data;

   data = _user_query[person->query_name()];
   if (!ERROR_HANDLER->do_assign_bug_to(data->errors[data->pos]->id, assigner,
       (: finish_assign_bug, person, assigner :))) {
      tell_object(person, "Error doing stuff.\n");
   }
   return 1;
}

int do_comment(object person) {
   class user_data data;

   data = _user_query[person->query_name()];
   this_player()->do_edit("", (: finish_comment, person, 
                                   data->errors[data->pos]->id :));
}

int do_help(object player) {
   string str;

   str = NROFF_HAND->cat_file("/doc/creatornr/errors", 1);
   if (!str) {
      NROFF_HAND->create_nroff("/doc/creator/commands/errors", "/doc/creatornr/errors");
      str = NROFF_HAND->cat_file("/doc/creatornr/errors", 1);
   }
   player->set_finish_func("finish_more_details", this_object());
   tell_object(player, "$P$Error help$P$" + str);
}

int do_list_errors() {
   class user_data data;
   class error_summary error;
   string str;
   int i;

   data = _user_query[this_player()->query_name()];
   str = "";
   i = 1;
   str += sprintf("ndx: #<num> %5-s %4-s %4-s by %11-s %15-s [%s]\n",
                  "Status", "Cat", "Type", "Reporter", "Filename", "Directory");
   foreach (error in data->errors) {
      str += sprintf("$I$3=%3d: #%d %5-s %4-s %4-s by %11-s %15-s [%s]\n",
                     i++, error->id, error->status[0..4], error->category[0..3],
                     error->type, error->reporter, error->filename[<15..],
                     error->directory[<15..]);
   }
   this_player()->set_finish_func("finish_more_details", this_object());
   write("$P$Error list$P$" + str);
}

int show_current_bug(object player, int verbose) {
   class user_data data;

   data = _user_query[player->query_name()];
   if (verbose == -1) {
      verbose = player->query_verbose("errors");
   }
   return do_bug_details(player, data->errors[data->pos]->id, verbose);
}

string expand_short_type(string name) {
   string str;

   str = upper_case(name);
   switch (str) {
   case "F" :
      return ERROR_STATUS_FIXED;
   case "D" :
      return ERROR_STATUS_DENIED;
   case "I" :
      return ERROR_STATUS_FIXING;
   case "C" :
      return ERROR_STATUS_CONSIDERING;
   case "O" :
      return ERROR_STATUS_OPEN;
   case "U" :
      return "CUSTOM";
   case "R" :
      return "NOTREPRODUCIBLE";
   case "R" :
      return "NOTPOSSIBLE";
   case "N" :
      return "NONE";
   case "T" :
      return "THANKS";
   case "M" :
      return "NOTENOUGHINFORMATION";
   case "E" :
      return "TEMPORARY";
   default :
      return str;
   }
}

void finish_change_status_command(object player, int id, string status,
                        string messtype, int type, mixed data) {
   string mess;
   string subject;
   class error_summary error;

   if (type != DB_SUCCESS) {
      tell_object(player, "SQL error: " + data + "\n");
      return ;
   }

   if (!sizeof(data)) {
      tell_object(player, "No bug found with an id of #" + id + "\n");
      return ;
   }

   error = data[0];

   _user_query[player->query_name()] = new(class user_data);
   _user_query[player->query_name()]->errors = data;
   _user_query[player->query_name()]->pos = 0;

   status = expand_short_type(status);
   // Now lets see if they asked for a standard reply or not.
   if (messtype) {
      messtype = expand_short_type(messtype);
      mess = query_message_from_type(player, messtype, error);
      subject = query_subject_from_type(messtype, error);
      if (!mess) {
         tell_object(player, "The reply type " + messtype + " was not valid.\n");
      } else {
         if (member_array(status, ERROR_TYPE) != -1) {
            tell_object(player, "The error type must be one of " +
                  query_multiple_short(ERROR_TYPE) + ".\n");
         } else {
            // Allow the person to enter their own message.
            do_change_status(player, status, 1, subject, mess);
            return ;
         }
      }
   } else {
      mess = query_message_from_type(player, status, error);
      subject = query_subject_from_type(status, error);
      // Change the status.
      if (mess) {
         do_change_status(player, status, 1, subject, mess);
         return ;
      }
   }
   tell_object(player, "Syntax: s {OPEN|FIXED|CONSIDERING|DENIED|THANKS|NOTPOSSIBLE|NOTREPRODUCIBLE|NOTENOUGHINFORMATION} [{none|custom|fixed|denied|notpossible|notreproducible|notenoughinformation}]\n");
   tell_object(player, "The default message is based on the type it is changed to.\n");
   tell_object(player, "The type has some short forms: o -> open, f -> fixed, d ->denied\n");
   tell_object(player, "   c ->considering, t -> thanks, n -> none, u -> custom, i -> fixing.\n");
   tell_object(player, "   r ->notreproducible, p -> notpossible, m -> notenoughinformation.\n");
   tell_object(player, "   t ->temporary.\n");
}

/**
 * This function does most of the work.  It handles the response from the
 * creator and does what it needs to with the error.
 */
void menu_response(string str) {
   string* bits;
   class user_data data;
   int print_menu_line;
   class error_summary error;
   string mess;
   string subject;
   int id;

   bits = explode(str, " ");
   if (!sizeof(bits)) {
/*
      write("You need to specify something to do.\n");
      write(menu_line(this_player()));
      input_to("menu_response");
      return ;
 */
      bits = ({ "n" });
   }

   data = _user_query[this_player()->query_name()];
   switch(lower_case(bits[0])) {
   case "v" :
      if (sizeof(bits) > 1) {
         show_current_bug(this_player(), bits[1] == "verbose");
      } else {
         show_current_bug(this_player(), -1);
      }
      break;
   case "+" :
   case "n" :
      // next bug.
      if (data->pos < sizeof(data->errors) - 1) {
         data->pos++;
         print_menu_line = !show_current_bug(this_player(), -1);
      } else {
         write("No next bug to go to.\n");
         print_menu_line = 1;
      }
      break;
   case "-" :
   case "p" :
      // previous bug.
      if (data->pos > 0) {
         data->pos--;
         print_menu_line = !show_current_bug(this_player(), -1);
      } else {
         write("No previous bug to go to.\n");
         print_menu_line = 1;
      }
      break;
   case "f" :
      // Forward the bug somewhere.
      if (sizeof(bits) == 2) {
         if (do_forward(this_player(), bits[1])) {
            input_to("menu_response");
         } else {
            print_menu_line = 1;
         }
      } else {
         write("Need to specify the destination directory (and nothing else)\n");
         print_menu_line = 1;
      }
      break;
   case "s" :
      // Change the status of the bug
      if (sizeof(bits) > 1) {
         bits[1] = expand_short_type(bits[1]);
         // Now lets see if they asked for a standard reply or not.
         error = data->errors[data->pos];
         if (sizeof(bits) > 2) {
            bits[2] = expand_short_type(bits[2]);
            mess = query_message_from_type(this_player(), bits[2], error);
            subject = query_subject_from_type(bits[1], error);
            if (!mess) {
               write("The reply type " + bits[2] + " was not valid.\n");
               print_menu_line = 1;
            } else {
               if (member_array(bits[1], ERROR_TYPE) != -1) {
                  write("The error type must be one of " +
                        query_multiple_short(ERROR_TYPE) + ".\n");
                  print_menu_line = 1;
               } else {
                  // Allow the person to enter their own message.
                  do_change_status(this_player(), bits[1], 0, subject, mess);
                  input_to("menu_response");
               }
            }
         } else {
            mess = query_message_from_type(this_player(), bits[1], error);
            subject = query_subject_from_type(bits[1], error);
            // Change the status.
            if (mess) {
               do_change_status(this_player(), bits[1], 0, subject, mess);
               input_to("menu_response");
            }
         }
         if (!mess) {
            write("Syntax: s {OPEN|FIXED|CONSIDERING|DENIED|THANKS|NOTPOSSIBLE|NOTREPRODUCIBLE|NOTENOUGHINFORMATION} [{none|custom|fixed|denied|notpossible|notreproducible|notenoughinformation}]\n");
            write("The default message is based on the type it is changed to.\n");
            write("The type has some short forms: o -> open, f -> fixed, d ->denied\n");
            write("   c ->considering, t -> thanks, n -> none, u -> custom, i -> fixing.\n");
            write("   r ->notreproducible, p -> notpossible, m -> notenoughinformation.\n");
            write("   t ->temporary.\n");
            print_menu_line = 1;
         }
      } else {
         write("Syntax: s {OPEN|FIXED|CONSIDERING|DENIED|THANKS|NOTPOSSIBLE|NOTREPRODUCIBLE|NOTENOUGHINFORMATION} [{none|custom|fixed|denied|notpossible|notreproducible|notenoughinformation}]\n");
         write("The default message is based on the type it is changed to.\n");
         write("The type has some short forms: o -> open, f -> fixed, d ->denied\n");
         write("   c ->considering, t -> thanks, n -> none, u -> custom, i -> fixing.\n");
         write("   r ->notreproducible, p -> notpossible, m -> notenoughinformation.\n");
         write("   t ->temporary.\n");
         print_menu_line = 1;
      }
      break;
   case "t" :
      // Change the type of the bug.
      if (sizeof(bits) > 1) {
         bits[1] = upper_case(bits[1]);
         if (member_array(bits[1], ERROR_TYPE) != -1) {
            do_change_type(this_player(), bits[1]);
            input_to("menu_response");
         } else {
            write("The type must be one of " +
                  query_multiple_short(ERROR_TYPE) + ".\n");
            print_menu_line = 1;
         }
      } else {
         write("Syntax: t {TYPO|BUG|IDEA}\n");
         print_menu_line = 1;
      }
      break;
   case "o" :
      // Change the category of the bug.
      if (sizeof(bits) > 1) {
         bits[1] = upper_case(bits[1]);
         if (member_array(bits[1], ERROR_CATEGORIES) != -1) {
            do_change_category(this_player(), bits[1]);
            input_to("menu_response");
         } else {
            write("The type must be one of " +
                  query_multiple_short(ERROR_CATEGORIES) + ".\n");
            print_menu_line = 1;
         }
      } else {
         write("Syntax: t {ROOM|OBJECT|COMMAND|HELP|RITUAL|SPELL|GENERAL}\n");
         print_menu_line = 1;
      }
      break;
   case "g" :
      // goto a specific bug.
      if (sizeof(bits) > 0) {
         id = to_int(bits[1]);
         if (id <= sizeof(data->errors)) {
            data->pos = id - 1;
            show_current_bug(this_player(), -1);
         } else {
            // See if we can find it.
            id = find_index(this_player(), id);
            if (id == -1) {
               write("Unable to find the bug #" + id + "\n");
               print_menu_line = 1;
            }
         }
      } else {
         write("Syntax: g <num>\nNum can either be the index number or the "
               "bug id.\n");
         print_menu_line = 1;
      }
      break;
   case "c" :
      // comment on the bug.
      do_comment(this_player());
      break;
   case "i" :
   case "l" :
      do_list_errors();
      break;
   case "q" :
      write("Bye bye.\n");
      return ;
   case "a" :
      if (sizeof(bits) > 1) {
         if (!PLAYER_HANDLER->test_creator(bits[1])) {
            write("You can only assign a bug to a creator.\n");
            print_menu_line = 1;
         } else {
            do_assign_bug(this_player(), bits[1]);
            input_to("menu_response");
         }
      } else {
         write("You need to specify someone to assign the error to.\n");
         print_menu_line = 1;
      }
      break;
   case "h" :
      do_help(this_player());
      break;
   default :
      write("Bad command.\n");
      print_menu_line = 1;
      break;
   }
   if (print_menu_line) {
      write(menu_line(this_player()));
      input_to("menu_response");
   }
}

void finish_query(object player, int type, mixed data) {
   if (type != DB_SUCCESS) {
      tell_object(player, "Error: " + data + "\n");
      return ;
   }

   _user_query[player->query_name()] = new(class user_data);
   _user_query[player->query_name()]->errors = data;
   _user_query[player->query_name()]->pos = 0;
   if (!sizeof(data)) {
      tell_object(player, "No bugs with that query.\n");
      return ;
   }
   show_current_bug(player, -1);
}

int setup_query(object player, class error_query query) {
   if (!ERROR_HANDLER->do_query_bug_summary(query, (: finish_query, player :))) {
      add_failed_mess("Unable to setup the query.\n");
      return 0;
   }
   return 1;
}

int errors_change_status(int id, string status, string messtype) {
   class error_query query;

   query = new (class error_query);
   query->id = id;
   if (!ERROR_HANDLER->do_query_bug_summary(query,
                     (: finish_change_status_command, this_player(), id, status,
                        messtype :))) {
      add_failed_mess("Unable to setup the query.\n");
      return 0;
   }
   write("Looking up bug #" + id + " to fix.\n");
   return 1;
}

int errors_this_dir(int recursive) {
   class error_query query;

   query = new (class error_query);
   query->dir = this_player()->query_path();
   query->recursive = recursive;
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

int errors_by_person(string person) {
   class error_query query;

   query = new (class error_query);
   query->reporter = lower_case(person);
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

int errors_assigned_to_person(string person) {
   class error_query query;

   query = new (class error_query);
   query->assigned_to = lower_case(person);
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

int errors_assigned_to_me() {
   return errors_assigned_to_person(this_player()->query_name());
}

int errors_in_dir(string str, int recursive) {
   class error_query query;
   string path;

   path = this_player()->get_path(str);
   query = new (class error_query);
   query->dir = path;
   query->recursive = recursive;
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

int errors_with_id(int id) {
   class error_query query;

   query = new (class error_query);
   query->id = id;
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

int errors_for_file(string file) {
   class error_query query;

   query = new (class error_query);
   query->file_name = file;
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

int errors_in_this_room() {
   class error_query query;

   query = new (class error_query);
   query->file_name = file_name(environment(this_player()));
   query->status = ({ ERROR_STATUS_OPEN, ERROR_STATUS_CONSIDERING,
                      ERROR_STATUS_FIXING });
   return setup_query(this_player(), query);
}

mixed* query_patterns() {
   return ({ "", (: errors_this_dir(0) :),
             "status <number'bug id'> <word'status'>",
                   (: errors_change_status($4[0], $4[1], 0) :),
             "status <number'bug id'> <word'status'> <word'reply'>",
                   (: errors_change_status($4[0], $4[1], $4[2]) :),
             "recursive", (: errors_this_dir(1) :),
             "mine", (: errors_assigned_to_me :),
             "here", (: errors_in_this_room :),
             "assigned <string'person'>", (: errors_assigned_to_person($4[0]) :),
             "by <string'person'>", (: errors_by_person($4[0]) :),
             "id <number'bug id'>", (: errors_with_id($4[0]) :),
             "file <string'file'>", (: errors_for_file($4[0]) :),
             "dir <string'dir'>", (: errors_in_dir($4[0], 0) :),
             "dir recursive <string'dir'>", (: errors_in_dir($4[0], 1) :) });
}