ds3.0/bin/
ds3.0/extra/
ds3.0/extra/crat/
ds3.0/extra/creremote/
ds3.0/extra/mingw/
ds3.0/extra/wolfpaw/
ds3.0/fluffos-2.18-ds07/
ds3.0/fluffos-2.18-ds07/Win32/
ds3.0/fluffos-2.18-ds07/compat/
ds3.0/fluffos-2.18-ds07/compat/simuls/
ds3.0/fluffos-2.18-ds07/testsuite/
ds3.0/fluffos-2.18-ds07/testsuite/clone/
ds3.0/fluffos-2.18-ds07/testsuite/command/
ds3.0/fluffos-2.18-ds07/testsuite/data/
ds3.0/fluffos-2.18-ds07/testsuite/etc/
ds3.0/fluffos-2.18-ds07/testsuite/include/
ds3.0/fluffos-2.18-ds07/testsuite/inherit/
ds3.0/fluffos-2.18-ds07/testsuite/inherit/master/
ds3.0/fluffos-2.18-ds07/testsuite/log/
ds3.0/fluffos-2.18-ds07/testsuite/single/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/compiler/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/efuns/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/operators/
ds3.0/fluffos-2.18-ds07/testsuite/u/
ds3.0/fluffos-2.18-ds07/tmp/
ds3.0/lib/cmds/admins/
ds3.0/lib/cmds/common/
ds3.0/lib/cmds/creators/include/
ds3.0/lib/daemon/services/
ds3.0/lib/daemon/tmp/
ds3.0/lib/doc/
ds3.0/lib/doc/bguide/
ds3.0/lib/doc/efun/all/
ds3.0/lib/doc/efun/arrays/
ds3.0/lib/doc/efun/buffers/
ds3.0/lib/doc/efun/compile/
ds3.0/lib/doc/efun/floats/
ds3.0/lib/doc/efun/functions/
ds3.0/lib/doc/efun/mixed/
ds3.0/lib/doc/efun/numbers/
ds3.0/lib/doc/efun/parsing/
ds3.0/lib/doc/help/classes/
ds3.0/lib/doc/help/races/
ds3.0/lib/doc/lfun/
ds3.0/lib/doc/lfun/all/
ds3.0/lib/doc/lfun/lib/abilities/
ds3.0/lib/doc/lfun/lib/armor/
ds3.0/lib/doc/lfun/lib/bank/
ds3.0/lib/doc/lfun/lib/bot/
ds3.0/lib/doc/lfun/lib/clay/
ds3.0/lib/doc/lfun/lib/clean/
ds3.0/lib/doc/lfun/lib/clerk/
ds3.0/lib/doc/lfun/lib/client/
ds3.0/lib/doc/lfun/lib/combat/
ds3.0/lib/doc/lfun/lib/connect/
ds3.0/lib/doc/lfun/lib/container/
ds3.0/lib/doc/lfun/lib/corpse/
ds3.0/lib/doc/lfun/lib/creator/
ds3.0/lib/doc/lfun/lib/daemon/
ds3.0/lib/doc/lfun/lib/damage/
ds3.0/lib/doc/lfun/lib/deterioration/
ds3.0/lib/doc/lfun/lib/donate/
ds3.0/lib/doc/lfun/lib/door/
ds3.0/lib/doc/lfun/lib/equip/
ds3.0/lib/doc/lfun/lib/file/
ds3.0/lib/doc/lfun/lib/fish/
ds3.0/lib/doc/lfun/lib/fishing/
ds3.0/lib/doc/lfun/lib/flashlight/
ds3.0/lib/doc/lfun/lib/follow/
ds3.0/lib/doc/lfun/lib/ftp_client/
ds3.0/lib/doc/lfun/lib/ftp_data_connection/
ds3.0/lib/doc/lfun/lib/fuel/
ds3.0/lib/doc/lfun/lib/furnace/
ds3.0/lib/doc/lfun/lib/genetics/
ds3.0/lib/doc/lfun/lib/holder/
ds3.0/lib/doc/lfun/lib/id/
ds3.0/lib/doc/lfun/lib/interactive/
ds3.0/lib/doc/lfun/lib/lamp/
ds3.0/lib/doc/lfun/lib/leader/
ds3.0/lib/doc/lfun/lib/light/
ds3.0/lib/doc/lfun/lib/limb/
ds3.0/lib/doc/lfun/lib/living/
ds3.0/lib/doc/lfun/lib/load/
ds3.0/lib/doc/lfun/lib/look/
ds3.0/lib/doc/lfun/lib/manipulate/
ds3.0/lib/doc/lfun/lib/meal/
ds3.0/lib/doc/lfun/lib/messages/
ds3.0/lib/doc/lfun/lib/player/
ds3.0/lib/doc/lfun/lib/poison/
ds3.0/lib/doc/lfun/lib/position/
ds3.0/lib/doc/lfun/lib/post_office/
ds3.0/lib/doc/lfun/lib/potion/
ds3.0/lib/doc/lfun/lib/room/
ds3.0/lib/doc/lfun/lib/server/
ds3.0/lib/doc/lfun/lib/spell/
ds3.0/lib/doc/lfun/lib/torch/
ds3.0/lib/doc/lfun/lib/vendor/
ds3.0/lib/doc/lfun/lib/virt_sky/
ds3.0/lib/doc/lfun/lib/weapon/
ds3.0/lib/doc/lfun/lib/worn_storage/
ds3.0/lib/doc/lpc/constructs/
ds3.0/lib/doc/lpc/etc/
ds3.0/lib/doc/lpc/intermediate/
ds3.0/lib/doc/lpc/types/
ds3.0/lib/doc/misc/
ds3.0/lib/doc/old/
ds3.0/lib/doc/phints/
ds3.0/lib/domains/
ds3.0/lib/domains/Praxis/adm/
ds3.0/lib/domains/Praxis/attic/
ds3.0/lib/domains/Praxis/cemetery/mon/
ds3.0/lib/domains/Praxis/data/
ds3.0/lib/domains/Praxis/death/
ds3.0/lib/domains/Praxis/mountains/
ds3.0/lib/domains/Praxis/obj/armour/
ds3.0/lib/domains/Praxis/obj/magic/
ds3.0/lib/domains/Praxis/obj/weapon/
ds3.0/lib/domains/Praxis/orc_valley/
ds3.0/lib/domains/Ylsrim/
ds3.0/lib/domains/Ylsrim/adm/
ds3.0/lib/domains/Ylsrim/armor/
ds3.0/lib/domains/Ylsrim/broken/
ds3.0/lib/domains/Ylsrim/fish/
ds3.0/lib/domains/Ylsrim/meal/
ds3.0/lib/domains/Ylsrim/npc/
ds3.0/lib/domains/Ylsrim/obj/
ds3.0/lib/domains/Ylsrim/virtual/
ds3.0/lib/domains/Ylsrim/weapon/
ds3.0/lib/domains/campus/adm/
ds3.0/lib/domains/campus/chamber/
ds3.0/lib/domains/campus/etc/
ds3.0/lib/domains/campus/meals/
ds3.0/lib/domains/campus/txt/ai/charles/
ds3.0/lib/domains/campus/txt/ai/charles/bak2/
ds3.0/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds3.0/lib/domains/campus/txt/ai/charly/
ds3.0/lib/domains/campus/txt/ai/charly/bak/
ds3.0/lib/domains/campus/txt/jenny/
ds3.0/lib/domains/cave/doors/
ds3.0/lib/domains/cave/etc/
ds3.0/lib/domains/cave/meals/
ds3.0/lib/domains/cave/weap/
ds3.0/lib/domains/default/chamber/
ds3.0/lib/domains/default/creator/
ds3.0/lib/domains/default/doors/
ds3.0/lib/domains/default/etc/
ds3.0/lib/domains/default/vehicle/
ds3.0/lib/domains/default/virtual/
ds3.0/lib/domains/town/save/
ds3.0/lib/domains/town/txt/shame/
ds3.0/lib/domains/town/virtual/
ds3.0/lib/domains/town/virtual/bottom/
ds3.0/lib/domains/town/virtual/space/
ds3.0/lib/estates/
ds3.0/lib/ftp/
ds3.0/lib/lib/comp/
ds3.0/lib/lib/daemons/
ds3.0/lib/lib/daemons/include/
ds3.0/lib/lib/lvs/
ds3.0/lib/lib/user/
ds3.0/lib/lib/virtual/
ds3.0/lib/log/
ds3.0/lib/log/adm/
ds3.0/lib/log/archive/
ds3.0/lib/log/chan/
ds3.0/lib/log/errors/
ds3.0/lib/log/law/adm/
ds3.0/lib/log/law/email/
ds3.0/lib/log/law/names/
ds3.0/lib/log/law/sites-misc/
ds3.0/lib/log/law/sites-register/
ds3.0/lib/log/law/sites-tempban/
ds3.0/lib/log/law/sites-watch/
ds3.0/lib/log/open/
ds3.0/lib/log/reports/
ds3.0/lib/log/router/
ds3.0/lib/log/secure/
ds3.0/lib/log/watch/
ds3.0/lib/obj/book_source/
ds3.0/lib/obj/include/
ds3.0/lib/powers/prayers/
ds3.0/lib/powers/spells/
ds3.0/lib/realms/template/
ds3.0/lib/realms/template/adm/
ds3.0/lib/realms/template/area/
ds3.0/lib/realms/template/area/armor/
ds3.0/lib/realms/template/area/npc/
ds3.0/lib/realms/template/area/obj/
ds3.0/lib/realms/template/area/room/
ds3.0/lib/realms/template/area/weap/
ds3.0/lib/realms/template/bak/
ds3.0/lib/realms/template/cmds/
ds3.0/lib/save/kills/o/
ds3.0/lib/secure/cfg/classes/
ds3.0/lib/secure/cmds/builders/
ds3.0/lib/secure/cmds/creators/include/
ds3.0/lib/secure/cmds/players/
ds3.0/lib/secure/cmds/players/include/
ds3.0/lib/secure/daemon/imc2server/
ds3.0/lib/secure/daemon/include/
ds3.0/lib/secure/lib/
ds3.0/lib/secure/lib/include/
ds3.0/lib/secure/lib/net/include/
ds3.0/lib/secure/lib/std/
ds3.0/lib/secure/log/adm/
ds3.0/lib/secure/log/bak/
ds3.0/lib/secure/log/intermud/
ds3.0/lib/secure/log/network/
ds3.0/lib/secure/modules/
ds3.0/lib/secure/npc/
ds3.0/lib/secure/obj/include/
ds3.0/lib/secure/room/
ds3.0/lib/secure/save/
ds3.0/lib/secure/save/backup/
ds3.0/lib/secure/save/boards/
ds3.0/lib/secure/save/players/g/
ds3.0/lib/secure/tmp/
ds3.0/lib/secure/upgrades/files/
ds3.0/lib/secure/verbs/creators/
ds3.0/lib/std/board/
ds3.0/lib/std/lib/
ds3.0/lib/verbs/admins/include/
ds3.0/lib/verbs/builders/
ds3.0/lib/verbs/common/
ds3.0/lib/verbs/common/include/
ds3.0/lib/verbs/creators/
ds3.0/lib/verbs/creators/include/
ds3.0/lib/verbs/rooms/
ds3.0/lib/verbs/rooms/include/
ds3.0/lib/www/client/
ds3.0/lib/www/errors/
ds3.0/lib/www/images/
ds3.0/win32/
/*    /secure/cmds/player/bug.c
 *    from the Dead Souls Object Library
 *    the command interface to the NM IV bug tracking system
 *    created by Descartes of Borg 950925
 */

#include <lib.h>
#include <daemons.h>
#include <message_class.h>

inherit LIB_DAEMON;

void PreMenu(string str);
static varargs void MainMenu(string str);
static void Assign(string *args);
static void EndAssign(string *args);
static void Complete(string *args);
void EndComplete(int x);
static void Delete(string *args);
static void Report(string *args);
static void EndReport(string type, string data, string file);
varargs static void View(string *args, int print);
static string GetBugString(int id, mapping bugs);

static void create() {
    daemon::create();
    SetNoClean(1);
}

mixed cmd(string str) {
    string *args;
    int i;

    if( !str || str == "" ) args = ({});
    else args = explode(str, " ");
    if( !(i = sizeof(args)) ) MainMenu();
    else {
        string opt;

        opt = args[0];
        if( i == 1 ) args = ({});
        else args = args[1..];
        switch(opt) {
            case "-a": Assign(args); break;
            case "-c": Complete(args); break;
            case "-d": Delete(args); break;
            case "-r": Report(args); break;
            case "-p": View(args, 1); break;
            case "-v": View(args); break;
            default: return "To report a bug, use \"bug -r\".";
        }
    }
    return 1;
}

void PreMenu(string str) { 
    if( str == "q" ) {
        message("system", "Exiting the bug tracking system.", this_player());
        return;
    }
    MainMenu(); 
}

varargs static void MainMenu(string str) {
    string tmp;
    int cols;

    if( str && str != "" ) {
        switch(str) {
            case "a": Assign(({})); return;
            case "c": Complete(({})); return;
            case "d": Delete(({})); return;
            case "r": Report(({})); return;
            case "p": View(({}), 1); return;
            case "v": View(({})); return;
            case "q": 
                message("system", "Exiting the bug tracking system.", 
                        this_player());
            return;
        }
    }
    cols = ((int *)this_player()->GetScreen())[0] || 80;
    tmp = center("Dead Souls Bug Tracking System", cols) + "\n\n";
    if( creatorp(this_player()) ) {
        tmp += "a)ssign bug to creator\n";
        tmp += "c)omplete work on a bug\n";
    }
    if( archp(this_player()) ) tmp += "d)elete a bug from the system\n";
    tmp += "r)eport a new bug to the system\n";
    tmp += "v)iew an existing bug or a list of existing bugs\n";
    tmp += "\nq)uit the bug tracking system\n";
    message("system", tmp, this_player());
    message("prompt", "Enter your choice: ", this_player());
    input_to( (: MainMenu :) );
}

static void Assign(string *args) {
    int i;

    if( !creatorp(this_player()) ) {
        message("system", "Only creators may use the -a option.",
                this_player());
        return;
    }
    if( !(i = sizeof(args)) ) {
        message("prompt", "Enter in the bug ID: ", this_player());
        input_to(function(string str) { Assign(({ str })); });
        return;
    }
    else if( i == 1 ) {
        int x;

        if( (x = to_int(args[0])) > 0 ) { /* assume a bug id for now */
            if( !archp(this_player()) ) 
                Assign( ({ args[0], this_player()->GetCapName() }) );
            else {
                message("prompt", "Enter the creator to assign it to [" +
                        this_player()->GetCapName() + "]: ",
                        this_player());
                input_to(function(string str, string id) {
                        if( !str || str == "" ) 
                        str = this_player()->GetCapName();
                        Assign( ({ str, id }) );
                        }, args[0]);
            }
            return;
        }
        else {
            message("prompt", "Enter in the bug ID to assign to " +
                    capitalize(args[0]) + ": ", this_player());
            input_to(function(string id, string str) { 
                    Assign( ({ id, str }) );
                    }, args[0]);
            return;
        }
    }
    else if( i == 2 ) {
        message("prompt", "Do you wish to comment? [n]: ", this_player());
        input_to(function(string str, string *args) {
                if( !str || str == "" ) str = "n";
                else str = lower_case(str[0..0]);
                if( str == "y" ) {
                string file;

                message("system", "Enter comments on the bug...", 
                    this_player());
                file = DIR_TMP "/" + this_player()->GetKeyName();
                rm(file);
                this_player()->eventEdit(file, (: EndAssign, args :));
                return;
                }
                else Assign(args + ({ "" }));
                }, args);
        return;
    }
    else {
        string who, comments;
        int x;

        if( (x = to_int(args[0])) < 1 ) {
            who = args[0];
            if( (x = to_int(args[1])) < 1 ) {
                message("system", "Invalid bug ID " + x + ".", this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
            }
        }
        else who = args[1];
        comments = args[2];
        if( !archp(this_player()) && (convert_name(who) !=
                    this_player()->GetKeyName()) ) {
            message("system", "Only arches may assign bugs to other people.",
                    this_player());
            message("prompt", "Hit return: ", this_player());
            input_to( (: PreMenu :) );
            return;
        }
        if( !user_exists(convert_name(who)) ) {
            message("system", "No such creator: " + who, this_player());
            message("prompt", "Hit return: ", this_player());
            input_to( (: PreMenu :) );
            return;
        }
        if( !(BUGS_D->eventAssign(x, who)) ) {
            message("system", "Failed to assign bug.", this_player());
            return;
        }
        if( comments != "" ) BUGS_D->AddComment(x, comments);
        message("system", "Assigned bug to " + who + ".", this_player());
        return;
    }
}

static void EndAssign(string *args) {
    string file, contents;

    file = DIR_TMP "/" + this_player()->GetKeyName();
    contents = (read_file(file) || "");
    rm(file);
    Assign(args + ({ contents }));
}

static void Complete(string *args) {
    string file;
    int x;

    if( !sizeof(args) ) {
        message("prompt", "Enter the bug ID: ", this_player());
        input_to(function(string str) { Complete( ({ str }) ); });
        return;
    }
    else if( !creatorp(this_player()) ) {
        message("system", "Invalid command.", this_player());
        message("prompt", "Hit return: ", this_player());
        input_to( (: PreMenu :) );
        return;
    }	
    else if( (x = to_int(args[0])) < 1 ) {
        message("system", "Invalid bug ID.", this_player());
        message("prompt", "Hit return: ", this_player());
        input_to( (: PreMenu :) );
        return;
    }
    message("system", "Enter in your comments:", this_player());
    file = DIR_TMP "/" + this_player()->GetKeyName();
    if( file_exists(file) ) rm(file);
    this_player()->eventEdit(file, (: EndComplete, x :));
}

void EndComplete(int x) {
    string file, stuff;

    if( previous_object() != this_player(1) ) return;
    file = DIR_TMP "/" + this_player()->GetKeyName();
    if( !(stuff = read_file(file)) ) {
        message("system", "Edit aborted.", this_player());
        rm(file);
        return;
    }
    rm(file);
    if( !(BUGS_D->eventComplete(x, stuff)) ) {
        message("system", "Failed to set the bug completed.", this_player());
        return;
    }
    message("system", "Bug marked completed!", this_player());
}

static void Delete(string *args) {
    if( !archp(this_player()) ) {
        message("system","You must be an arch to delete bugs.", this_player());
        return;
    }
    if( !sizeof(args) ) {
        message("prompt", "Delete which bug? ", this_player());
        input_to(function(string str) { Delete( ({ str }) ); });
        return;
    }
    else {
        int x;

        if( (x = to_int(args[0])) < 1 ) 
            message("system", "Invalid bug ID.", this_player());
        else if( !(BUGS_D->eventDelete(x)) ) 
            message("system", "Delete failed.", this_player());
        else message("system", "Deletion succeeded.", this_player());
        return;
    }
}

static void Report(string *args) {
    if( archp(this_player()) && sizeof(args) ) {
        string data;
        string bug;
        int x;

        data = "Room: " + file_name(environment(this_player()));
        bug = implode(args, " ");
        if( x = BUGS_D->eventReport(this_player()->GetCapName(), 
                    "approval", bug, data) ) {
            BUGS_D->eventAssign(x, query_privs(environment(this_player())));
            message("system", "Bug reported.", this_player());
            return;
        }
        else {
            message("system", "Error in reporting bug.", this_player());
            return;
        }
    }
    else EndReport(0, "Room: " + file_name(environment(this_player())), 0);
}

static void EndReport(string type, string data, string file) {
    string tmp;
    int x;

    if( !type ) {
        message("system", "Choose a bug type from among the following:\n",
                this_player());
        message("system", "\tidea (some nifty idea to add to the game)",
                this_player());
        message("system", "\ttypo (misspelling, lexigraphical weirdness)",
                this_player());
        message("system", "\tunexplained behaviour (something "
            "contrary to how you would expect it)", this_player());
        message("system", "\truntime (one of those nasty error messages)\n",
                this_player());
        message("system", "\tother\n", this_player());
        message("prompt", "Enter type: ", this_player());
        input_to( (: EndReport :), data, 0);
        return;
    }
    if( !file ) {
        file = DIR_TMP "/" + this_player()->GetKeyName();
        rm(file);
        message("system", "Enter in a description of the bug.  When done, "
                "enter a period on a line by itself.", this_player());
        this_player()->eventEdit(file, (: EndReport, type, data, file :));
        return;
    }
    if( !(tmp = read_file(file)) ) {
        message("system", "Bug report aborted.", this_player());
        rm(file);
        return;
    }
    rm(file);
    if( type == "runtime" ) {
        mapping last_error;

        if( last_error = this_player()->GetLastError() )
            data += "\n" + master()->standard_trace(last_error) + "\n";
    }
    if( !(x = BUGS_D->eventReport(this_player()->GetCapName(),
                    type, tmp, data)) ) {
        message("system", "Bug report failed.", this_player());
        return;
    }
    message("system", "Bug reported, thank you!  Your tracking id is " +
            x + ".", this_player());
}

varargs static void View(string *args, int print) {
    mapping bugs;
    function f;

    f = function() { 
        message("prompt", "\nHit return: ", this_player());
        input_to( (: PreMenu :) );
    };
    if( !sizeof(args) ) {
        message("system", "View:\n\t1) all bugs\n"
            "\t2) unassigned bugs only\n"
            "\t3) assigned bugs only\n"
            "\t4) completed bugs only\n", this_player());
        if( creatorp(this_player()) )
            message("prompt", "Enter a choice [3]: " , this_player());
        else message("prompt", "Enter a choice [1]: ", this_player());
        input_to(function(string str, string it_sucks, int print) {
                if( !str || str == "" ) {
                if( creatorp(this_player()) ) str = "3";
                else str = "1";
                }
                if( str < "1" || str > "4" ) {
                message("system", "Invalid selection", this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
                }
                View( ({ str }), print );
                }, "", print);
        return;
    }
    else if( sizeof(args) == 1 && !creatorp(this_player()) ) {
        mapping bug;
        string tmp = "";
        int bug_id;

        bugs = BUGS_D->GetBugs();
        foreach( bug_id, bug in bugs ) {
            if( bug["who"] != this_player()->GetCapName() )
                continue;
            if( args[0] == "1" || (args[0] == "2" && !bug["assigned"]) ||
                    (args[0] == "3" && bug["assigned"] && !bug["date fixed"]) ||
                    (args[0] == "4" && bug["date fixed"]) )
                tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
        }
        if( tmp == "" ) {
            message("system", "No bugs meet your query criteria.", 
                    this_player());
            message("prompt", "Hit return: ", this_player());
            input_to( (: PreMenu :) );
            return;
        }
        this_player()->eventPage(explode(tmp, "\n"), MSG_SYSTEM, f);
        return;
    }
    else if( sizeof(args) == 1 ) {
        message("system", "View:\n\t1) all bugs\n"
            "\t2) bugs assigned to me\n"
            "\t3) bugs reported by me\n", this_player());
        message("prompt", "Enter choice [2]: ", this_player());
        input_to(function(string str, string one, int print) {
                if( !str || str == "" ) str = "2";
                else if( str < "1" || str > "3" ) {
                message("system", "Invalid selection.", this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
                }
                View( ({ one, str }), print );
                }, args[0], print);
        return;
    }
    else {
        mapping bug;
        string nom, tmp = "";
        int bug_id;

        nom = this_player()->GetKeyName();
        bugs = BUGS_D->GetBugs();
        if( !creatorp(this_player()) ) {
            View( ({ args[0] }), print );
            return;
        }
        else foreach(bug_id, bug in bugs) {
            string opt1, opt2;

            opt1 = args[0];
            opt2 = args[1];
            if( opt1 == "1" && opt2 == "1" ) 
                tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
            else {
                if( opt2 == "2" && (!bug["assigned"] || 
                            convert_name(bug["assigned"]) != nom) ) 
                    continue;
                else if(opt2 == "3" && convert_name(bug["who"]) != nom )
                    continue;
                if( opt1 == "2" && !bug["assigned"] )
                    tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                else if( opt1 == "3" && bug["assigned"] && !bug["date fixed"] )
                    tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                else if( opt1 == "4" && bug["date fixed"] )
                    tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
            }
        }
        if( tmp == "" ) {
            message("system", "No bugs match your query.", this_player());
            message("prompt", "Hit return: ", this_player());
            input_to( (: PreMenu :) );
            return;
        }
        if( print && creatorp(this_player()) ) {
            string file;

            rm(file = user_path(this_player()->GetKeyName()) + "bugs");
            write_file(file, strip_colours(tmp));
        }
        else this_player()->eventPage(explode(tmp, "\n"), MSG_SYSTEM, f);
        return;
    }
}

static string GetBugString(int id, mapping bugs) {
    string tmp;

    tmp = "%^YELLOW%^Bug ID:%^RESET%^ " + id + "\n";
    tmp += "%^YELLOW%^Reported by:%^RESET%^ " + 
        bugs[id]["who"] + "\n";
    if( bugs[id]["assigned"] ) {
        tmp += "%^YELLOW%^Status: %^RESET%^";
        if( !bugs[id]["date fixed"] ) 
            tmp += "assigned to " + bugs[id]["assigned"] + "\n";
        else tmp += "completed " + ctime(bugs[id]["date fixed"]) + "\n";
    }
    else tmp += "%^YELLOW%^Status:%^RESET%^ unassigned\n";
    tmp += "%^YELLOW%^Type:%^RESET%^ " + bugs[id]["type"] + "\n";
    if( bugs[id]["date fixed"] )
        tmp += "%^YELLOW%^Notes:%^RESET%^\n" + bugs[id]["resolution"] + "\n";
    if( creatorp(this_player()) ) 
        tmp += "\n%^YELLOW%^Creator info:%^RESET%^\n" + bugs[id]["data"] + "\n";
    tmp += "\n%^YELLOW%^Bug info:%^RESET%^\n" + bugs[id]["bug"] + "\n";
    return tmp;
}

string GetHelp(){
    string tmp;

    tmp = "Syntax: bug\n";
    if( creatorp(this_player()) ) {
        tmp += "        bug -a <BUG_ID CREATOR>\n";
        tmp += "        bug -c <BUG_ID>\n";
    }
    if( archp(this_player()) ) tmp += "        bug -d <BUG_ID>\n";
    tmp += "        bug -r\n        bug -v [1-4] [1-3]\n\n";
    tmp += "The command interface to the Dead Souls Bug Tracking System.  "
        "You can simply type \"bug\" and be prompted for further options, "
        "or, if you understand the system, pass command line arguments "
        "to the bug command to make things go faster.  This system allows "
        "players to report bugs or ideas and periodically see what has "
        "been done about their report.  It also allows creators a way "
        "to track bugs which have been reported to them and give feedback "
        "to the players who have reported them.  It gives admins a way to "
        "track and assign mudlib level bugs.  The options above correspond "
        "to assigning, completing, deleting, reporting, and viewing bugs "
        "respectively.\n"
        "See also: praise";
    return tmp;
}