foundation2_fluffos_v1/
foundation2_fluffos_v1/bin/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/ChangeLog.old/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/Win32/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/compat/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/compat/simuls/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/include/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/clone/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/command/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/data/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/etc/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/include/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/inherit/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/inherit/master/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/log/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/single/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/single/tests/compiler/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/single/tests/efuns/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/single/tests/operators/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/testsuite/u/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/tmp/
foundation2_fluffos_v1/fluffos-2.9-ds2.13/windows/
foundation2_fluffos_v1/lib/cfg/
foundation2_fluffos_v1/lib/cmds/adm/
foundation2_fluffos_v1/lib/daemon/save/
foundation2_fluffos_v1/lib/daemon/services/
foundation2_fluffos_v1/lib/daemon/soul/
foundation2_fluffos_v1/lib/doc/build/
foundation2_fluffos_v1/lib/doc/classes/
foundation2_fluffos_v1/lib/doc/driver/
foundation2_fluffos_v1/lib/doc/driver/applies/
foundation2_fluffos_v1/lib/doc/driver/applies/interactive/
foundation2_fluffos_v1/lib/doc/driver/concepts/
foundation2_fluffos_v1/lib/doc/driver/driver/
foundation2_fluffos_v1/lib/doc/driver/efuns/arrays/
foundation2_fluffos_v1/lib/doc/driver/efuns/buffers/
foundation2_fluffos_v1/lib/doc/driver/efuns/calls/
foundation2_fluffos_v1/lib/doc/driver/efuns/compile/
foundation2_fluffos_v1/lib/doc/driver/efuns/filesystem/
foundation2_fluffos_v1/lib/doc/driver/efuns/floats/
foundation2_fluffos_v1/lib/doc/driver/efuns/functions/
foundation2_fluffos_v1/lib/doc/driver/efuns/general/
foundation2_fluffos_v1/lib/doc/driver/efuns/mappings/
foundation2_fluffos_v1/lib/doc/driver/efuns/numbers/
foundation2_fluffos_v1/lib/doc/driver/efuns/parsing/
foundation2_fluffos_v1/lib/doc/driver/lpc/constructs/
foundation2_fluffos_v1/lib/doc/driver/lpc/types/
foundation2_fluffos_v1/lib/doc/driver/platforms/
foundation2_fluffos_v1/lib/doc/efun/
foundation2_fluffos_v1/lib/doc/etc/
foundation2_fluffos_v1/lib/doc/faq/
foundation2_fluffos_v1/lib/doc/help/creator/
foundation2_fluffos_v1/lib/doc/help/player/
foundation2_fluffos_v1/lib/doc/lpc/basic/
foundation2_fluffos_v1/lib/doc/lpc/data_types/
foundation2_fluffos_v1/lib/doc/lpc/etc/
foundation2_fluffos_v1/lib/doc/lpc/intermediate/
foundation2_fluffos_v1/lib/doc/lpc/types/
foundation2_fluffos_v1/lib/doc/mudlib/
foundation2_fluffos_v1/lib/doc/mudlib/Features/
foundation2_fluffos_v1/lib/domains/Examples/armour/
foundation2_fluffos_v1/lib/domains/Examples/etc/
foundation2_fluffos_v1/lib/domains/Examples/npc/
foundation2_fluffos_v1/lib/domains/Examples/room/
foundation2_fluffos_v1/lib/domains/Examples/virtual/
foundation2_fluffos_v1/lib/domains/Examples/virtual/exaA/
foundation2_fluffos_v1/lib/domains/Examples/virtual/exaB/
foundation2_fluffos_v1/lib/domains/Examples/weapon/
foundation2_fluffos_v1/lib/domains/School/doc/
foundation2_fluffos_v1/lib/domains/School/room/
foundation2_fluffos_v1/lib/domains/School/room/Classes/
foundation2_fluffos_v1/lib/domains/School/room/Offices/
foundation2_fluffos_v1/lib/domains/Standard/
foundation2_fluffos_v1/lib/domains/Standard/pools/
foundation2_fluffos_v1/lib/domains/Standard/std/
foundation2_fluffos_v1/lib/domains/Standard/xtra/
foundation2_fluffos_v1/lib/include/
foundation2_fluffos_v1/lib/lib/obj/
foundation2_fluffos_v1/lib/news/
foundation2_fluffos_v1/lib/save/
foundation2_fluffos_v1/lib/secure/cfg/
foundation2_fluffos_v1/lib/secure/cmds/player/
foundation2_fluffos_v1/lib/secure/lib/
foundation2_fluffos_v1/old/
foundation2_fluffos_v1/win32/
/*    /secure/cmds/player/bug.c
 *    from the Foundation IIr1 Object Library
 *    the command interface to the NM IV bug tracking system
 *    created by Descartes of Borg 950925
 */

#include <lib.h>
#include <daemons.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);
static void View(string *args);
static string GetBugString(int id, mapping bugs);

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 "-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 "v": View(({})); return;
	    case "q": 
	        message("system", "Exiting the bug tracking system.", 
			this_player());
		return;
	}
    }
    cols = ((int *)this_player()->GetScreen())[0] || 80;
    tmp = center("Foundation II 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], (string)this_player()->GetCapName() }) );
	    else {
		message("prompt", "Enter the creator to assign it to [" +
			(string)this_player()->GetCapName() + "]: ",
			this_player());
		input_to(function(string str, string id) {
		    if( !str || str == "" ) 
		      str = (string)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 "/" + (string)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) !=
				      (string)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( !((int)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 "/" + (string)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( (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 "/" + (string)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 "/" + (string)this_player()->GetKeyName();
    if( !(stuff = read_file(file)) ) {
	message("system", "Edit aborted.", this_player());
	rm(file);
	return;
    }
    rm(file);
    if( !((int)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( !((int)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 = (int)BUGS_D->eventReport((string)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 "/" + (string)this_player()->GetKeyName();
	rm(file);
	message("system", "Enter in a description of the bug...", 
		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 = (mapping)this_player()->GetLastError() )
	  data += "\n" + (string)master()->standard_trace(last_error) + "\n";
    }
    if( !(x = (int)BUGS_D->eventReport((string)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());
}

static void View(string *args) {
    mapping bugs;
    function f;
    string whose;
    int x;
    
    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) {
	    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 }) );
	});
	return;
    }
    else if( sizeof(args) == 1 && !creatorp(this_player()) ) {
	mapping bug;
	string tmp = "";
	int bug_id;

	bugs = (mapping)BUGS_D->GetBugs();
	foreach( bug_id, bug in bugs ) {
	    if( bug["who"] != (string)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()->more(explode(tmp, "\n"), "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) {
	    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 }) );
	}, args[0]);
	return;
    }
    else {
	mapping bug;
	string nom, tmp = "";
	int bug_id;
	
	nom = (string)this_player()->GetKeyName();
	bugs = (mapping)BUGS_D->GetBugs();
	if( !creatorp(this_player()) ) {
	    View( ({ args[0] }) );
	    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;
	}
	this_player()->more(explode(tmp, "\n"), "info", 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;
}

void help() {
    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 Foundation II 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\n"
      "See also: praise";
    message("help", tmp, this_player());
}