dsIIr4/bin/
dsIIr4/extra/creremote/
dsIIr4/extra/wolfpaw/
dsIIr4/lib/cmds/admins/
dsIIr4/lib/cmds/common/
dsIIr4/lib/cmds/creators/include/
dsIIr4/lib/cmds/creators/include/SCCS/
dsIIr4/lib/daemon/services/
dsIIr4/lib/doc/
dsIIr4/lib/domains/Ylsrim/
dsIIr4/lib/domains/Ylsrim/adm/
dsIIr4/lib/domains/Ylsrim/armor/
dsIIr4/lib/domains/Ylsrim/broken/
dsIIr4/lib/domains/Ylsrim/fish/
dsIIr4/lib/domains/Ylsrim/meal/
dsIIr4/lib/domains/Ylsrim/npc/
dsIIr4/lib/domains/Ylsrim/virtual/
dsIIr4/lib/domains/Ylsrim/weapon/
dsIIr4/lib/domains/campus/adm/
dsIIr4/lib/domains/campus/etc/
dsIIr4/lib/domains/campus/meals/
dsIIr4/lib/domains/campus/npc/
dsIIr4/lib/domains/campus/save/
dsIIr4/lib/domains/campus/txt/
dsIIr4/lib/domains/campus/txt/ai/charles/
dsIIr4/lib/domains/campus/txt/ai/charles/bak2/
dsIIr4/lib/domains/campus/txt/ai/charles/bak2/bak1/
dsIIr4/lib/domains/campus/txt/ai/charly/
dsIIr4/lib/domains/campus/txt/ai/charly/bak/
dsIIr4/lib/domains/campus/txt/jenny/
dsIIr4/lib/domains/default/creator/
dsIIr4/lib/domains/default/doors/
dsIIr4/lib/domains/default/etc/
dsIIr4/lib/domains/default/virtual/
dsIIr4/lib/domains/default/weap/
dsIIr4/lib/domains/town/virtual/
dsIIr4/lib/lib/comp/
dsIIr4/lib/lib/lvs/
dsIIr4/lib/lib/user/
dsIIr4/lib/lib/virtual/
dsIIr4/lib/log/
dsIIr4/lib/obj/book_source/
dsIIr4/lib/obj/include/
dsIIr4/lib/realms/template/
dsIIr4/lib/realms/template/adm/
dsIIr4/lib/realms/template/area/armor/
dsIIr4/lib/realms/template/area/npc/
dsIIr4/lib/realms/template/area/obj/
dsIIr4/lib/realms/template/area/room/
dsIIr4/lib/realms/template/area/weap/
dsIIr4/lib/realms/template/bak/
dsIIr4/lib/realms/template/cmds/
dsIIr4/lib/save/
dsIIr4/lib/save/kills/o/
dsIIr4/lib/secure/cfg/classes/
dsIIr4/lib/secure/cmds/creators/include/
dsIIr4/lib/secure/cmds/players/
dsIIr4/lib/secure/cmds/players/include/
dsIIr4/lib/secure/daemon/include/
dsIIr4/lib/secure/lib/
dsIIr4/lib/secure/lib/include/
dsIIr4/lib/secure/lib/net/include/
dsIIr4/lib/secure/lib/std/
dsIIr4/lib/secure/modules/
dsIIr4/lib/secure/npc/
dsIIr4/lib/secure/obj/include/
dsIIr4/lib/secure/room/
dsIIr4/lib/secure/save/
dsIIr4/lib/secure/save/boards/
dsIIr4/lib/secure/save/players/g/
dsIIr4/lib/secure/tmp/
dsIIr4/lib/secure/verbs/creators/
dsIIr4/lib/shadows/
dsIIr4/lib/spells/
dsIIr4/lib/std/board/
dsIIr4/lib/std/lib/
dsIIr4/lib/tmp/
dsIIr4/lib/verbs/admins/include/
dsIIr4/lib/verbs/common/
dsIIr4/lib/verbs/common/include/
dsIIr4/lib/verbs/creators/include/
dsIIr4/lib/verbs/players/include/SCCS/
dsIIr4/lib/verbs/rooms/
dsIIr4/lib/verbs/rooms/include/
dsIIr4/lib/www/
dsIIr4/v22.2b14-dsouls2/
dsIIr4/v22.2b14-dsouls2/ChangeLog.old/
dsIIr4/v22.2b14-dsouls2/Win32/
dsIIr4/v22.2b14-dsouls2/compat/
dsIIr4/v22.2b14-dsouls2/compat/simuls/
dsIIr4/v22.2b14-dsouls2/include/
dsIIr4/v22.2b14-dsouls2/mudlib/
dsIIr4/v22.2b14-dsouls2/testsuite/
dsIIr4/v22.2b14-dsouls2/testsuite/clone/
dsIIr4/v22.2b14-dsouls2/testsuite/command/
dsIIr4/v22.2b14-dsouls2/testsuite/data/
dsIIr4/v22.2b14-dsouls2/testsuite/etc/
dsIIr4/v22.2b14-dsouls2/testsuite/include/
dsIIr4/v22.2b14-dsouls2/testsuite/inherit/
dsIIr4/v22.2b14-dsouls2/testsuite/inherit/master/
dsIIr4/v22.2b14-dsouls2/testsuite/log/
dsIIr4/v22.2b14-dsouls2/testsuite/single/
dsIIr4/v22.2b14-dsouls2/testsuite/single/tests/compiler/
dsIIr4/v22.2b14-dsouls2/testsuite/single/tests/efuns/
dsIIr4/v22.2b14-dsouls2/testsuite/single/tests/operators/
dsIIr4/v22.2b14-dsouls2/testsuite/u/
dsIIr4/v22.2b14-dsouls2/tmp/
dsIIr4/win32/
/*    /secure/cmds/player/bug.c
 *    from the Dead Soulsr1 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], (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( !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 "/" + (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.  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 = (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());
	    }

	    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 = (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()->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 = (string)this_player()->GetKeyName();
		    bugs = (mapping)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((string)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 str) {
		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\n"
		"See also: praise";
		return tmp;
	    }