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/
/* Copyright 1994, 1995 - Tim Hollebeek
 *
 * Permission is granted to copy and use this code elsewhere, provided
 * that all code derived from this code retain this header and credit
 * the author (Tim Hollebeek) for the source, that all improvements
 * to this code be communicated to the above mentioned author for possible
 * inclusion in this code, that all derived works are made publicly
 * available to whoever wants them, and no profit is made off of this
 * code or any other derived works or any other package or system this
 * is used in without express written permission of the author.
 */
/* written quickly by Beek, Oct 12, 1994 */
#include <lib.h>
#include <daemons.h>

#define NEWSRC_SAVE_DIR "/save/news/rn/"

inherit LIB_DAEMON;

private void main_loop();

private void unsubscribe(string);
private void show_headers();
private void post(string, function);

static void ask_what_next(string);
static void ask_about_group(string);

#define STRIP_UNSUB(x) ((x) ? ((x)[0] == '#' ? (x)[1..strlen(x)] : x) : 0)
#define IS_UNSUB(x) ((x) && ((x)[0] == '#'))

#define ALL_ARTICLES    1

int options = 0;
string restrict_poster;
object who;
mapping read = ([]);
string *groups;
int *ids;

/* The following code is imported from the first newsreader I ever
   wrote, which is also one of the first LPC objects I ever wrote :) */
/* It's hideously inefficient and in need of a rewrite, but it will
   do for now */
int included(int id,string read) {
    string *ranges;
    int i,begin,end;

    if (!read) return 0;
    ranges=explode(read,",");
    for (i=0;i<sizeof(ranges);i++) {
	if (sscanf(ranges[i],"%d-%d",begin,end)==2) {
	    if ((begin<=id) && (id<=end)) return 1;
	} else {
	    sscanf(ranges[i],"%d",begin);
	    if (id==begin) return 1;
	}
    }
    return 0;
}

int GetRight(string range) {
    int begin,end;

    if (sscanf(range,"%d-%d",begin,end)==2) return end;
    sscanf(range,"%d",begin);
    return begin;
}

int GetLeft(string range) {
    int begin,end;

    if (sscanf(range,"%d-%d",begin,end)==2) return begin;
    sscanf(range,"%d",begin);
    return begin;
}

string AddToList(string list,int num) {
    string *ranges;
    int i,left,right;

    if (list == "") list = 0;
    if (included(num,list)) return list;
    if (!list) return sprintf("%d",num);
    ranges=explode(list,",");
    for (i=0;i<sizeof(ranges);i++) {
	left=GetLeft(ranges[i]);
	if (num<left) {
	    right=-1;
	    if (i) right=GetRight(ranges[i-1]);
	    if ((right+1==num) && (num==left-1)) {
		ranges[i-1]=sprintf("%d-%d",GetLeft(ranges[i-1]),GetRight(ranges[i]));
		ranges=ranges[0..i-1]+ranges[i+1..(sizeof(ranges)-1)];
	    } else if (right+1==num) {
		ranges[i-1]=sprintf("%d-%d",GetLeft(ranges[i-1]),num);
	    } else if (num==left-1) {
		ranges[i]=sprintf("%d-%d",num,GetRight(ranges[i]));
	    } else {
		ranges=ranges[0..i-1]+({sprintf("%d",num)})+ranges[i..(sizeof(ranges)-1)];
	    }
	    return implode(ranges,",");
	}
    }
    if (GetRight(ranges[sizeof(ranges)-1])==num-1) {
	ranges[sizeof(ranges)-1]=sprintf("%d-%d",GetLeft(ranges[sizeof(ranges)-1]),num);
    } else ranges+=({sprintf("%d",num)});
    return implode(ranges,",");
}
/* end imported code */

private void
add_read(string group, int id) {
    if (read[group] && read[group][0] == '#') {
	read[group] = "#" + AddToList(read[group][1..strlen(read[group])], id);
    } else {
	read[group] = AddToList(read[group], id);
    }
}

private void
exit(int aborted) {
    string *groups;
    int i, n;

    if (!aborted) 
	write("End of news.");
    unguarded( (: write_file, NEWSRC_SAVE_DIR+who->GetKeyName(), save_variable(read), 1 :) );
    destruct(this_object());
}

private void
end_follow(string group, int id, function when_done) {
    string file;
    string message;

    file = "/tmp/rn."+who->GetKeyName();
    message = read_file(file);
    rm(file);
    NEWSSERVER->followup(group, id, message);
    evaluate(when_done);
}

private void
abort_post(function when_done) {
    string file;

    file = "/tmp/rn."+who->GetKeyName();
    if (file && file_exists(file)) rm (file);
    write("Post aborted.");
    evaluate(when_done);
}

private void
follow(string group, int id, string insert) {
    string fname = "/tmp/rn."+who->GetKeyName();

    write_file(fname, insert, 1);
    who->eventEdit(fname,
      (: end_follow, group, id, (: ask_what_next, 0 :) :),
      (: abort_post, (: ask_what_next, 0 :) :) );
}

private void
end_article() {
    printf("End of article -- ");
    ask_what_next(0);
}

private void
show_article(int id) {
    string *tmp;
    mapping article;

    article = NEWSSERVER->get_message(groups[0], id);
    if (!article["MESSAGE"]) article["MESSAGE"] = "";
    tmp = ({ sprintf("Post %i (%i more) in %s:",
      ids[0], sizeof(ids)-1, groups[0])
    , "Poster: " + article["POSTER"]
    , "Subject: %^BOLD%^" + article["SUBJECT"] + "%^RESET%^"
    , "Date: " + ctime(article["TIME"])
    , "" })
    + explode(article["MESSAGE"], "\n");
    add_read(groups[0], id);
    who->more(tmp, "news", (: end_article :));
}

private string quote_text(string orig) {
    if (orig[strlen(orig)-1] == '\n') orig = orig[0..strlen(orig)-2];
    return "> " + replace_string(orig, "\n", "\n> ") + "\n";
}

private void
next_group() {
    groups = groups[1..sizeof(groups)];
    main_loop();
}

static void
ask_what_next(string response) {
    int num;

    switch (response) {
    case "u": /* unsubscribe */
	unsubscribe(groups[0]);
	next_group();
	return;
    case "p":
	post(groups[0], (: ask_what_next, 0 :));
	return;
    case "c": /* catch up */
	{
	    int i,n;
	    for (i=0, n=sizeof(ids); i<n; i++) 
		add_read(groups[0], ids[i]);
	}
	next_group();
	return;
    case "q": /* quit group */
	next_group();
	return;
    case "=": /* list headers */
	show_headers();
	break;
    case "F":
    case "f": /* followup */
	{ 
	    string insert;

	    if (ids[0] == -1) {
		write("No current article");
		break;
	    }
	    insert = "";
	    if (response == "F") {
		mapping article = NEWSSERVER->get_message(groups[0], ids[0]);
		insert = sprintf("%s writes:\n%s", article["POSTER"],
		  quote_text(article["MESSAGE"]));
	    }
	    follow(groups[0], ids[0], insert);
	    return;
	}
    case "r":
	if (ids[0] == -1) {
	    write("No current article");
	    break;
	}
	if (NEWSSERVER->remove_post(groups[0], ids[0])) {
	    write("Removed.");
	    ids = ids[1..sizeof(ids)];
	    if (!sizeof(ids)) {
		write("End of group " + groups[0] + "\n");
		next_group();
		return;
	    }
	    break;
	} else {
	    write("Failed.");
	}
	break;
    case "?":
    case "h":
	write(@ENDHELP
c - mark all articles in this group as read
f - post followup to last article
F - same as 'f' but include article preceded by "> "
h or ? - this help
n - read next article
p - post an article to this group
q - go back to newsgroup mode
u - unsubscribe to this group
= - list articles remaining in group
a number - read a specified article
ENDHELP);
	break;
    case "":
    case "n":
	if (ids[0] == -1)
	    ids = ids[1..sizeof(ids)];
	if (sizeof(ids) > 1) {
	    ids = ids[1..sizeof(ids)];
	    show_article(ids[0]);
	} else {
	    write("End of group " + groups[0] + "\n");
	    next_group();
	}
	return;
    default:
	if (response && sscanf(response, "%d", num)) {
	    if (member_array(num, NEWSSERVER->get_messages(groups[0])) == -1) {
		write("No such message.");
	    } else {
		if (ids[0] != -1) ids = ids[1..sizeof(ids)];
		ids -= ({ num, -1 });
		ids = ({ num }) + ids;
		show_article(ids[0]);
		return;
	    }
	}
    }
    printf("What next? [cfFhnpqru?=] ");
    input_to("ask_what_next");
}

private void
show_headers() {
    int i = 1, n;
    mapping article;

    for (n=sizeof(ids); i<n; i++) {
	article = NEWSSERVER->get_message(groups[0], ids[i]);
	printf("[%3d] %-15s %s\n", ids[i], article["POSTER"], article["SUBJECT"]);
    }
}

private void
unsubscribe(string group) {
    if (read[group])
	read[group] = "#" + read[group];
    else
	read[group] = "#";
}

private void 
subscribe(string group) {
    read[group] = STRIP_UNSUB(read[group]);
}

private string temp_kludge;

private int *
get_unread(string group) {
    int *ret;
    int *deleted;
    function filter;
    int i;

    if (options & ALL_ARTICLES) {
	if (restrict_poster) filter = (: $2["POSTER"] == restrict_poster :);
	else filter = (: 1 :);
    } else {
	temp_kludge = STRIP_UNSUB(read[group]);
	if (restrict_poster) filter = (: !included($1, temp_kludge) && $2["POSTER"] == restrict_poster :);
	else filter = (: !included($1, temp_kludge) :);
    }

    ret = NEWSSERVER->get_messages(group, filter);
    if (!sizeof(ret)) return 0;
    deleted = NEWSSERVER->query_removed(group, ret);
    i = sizeof(deleted);
    while (i--) {
	add_read(group, deleted[i]);
    }
    ret -= deleted;
    if (!sizeof(ret)) return 0;
    return ret;
}

private void
main_loop() {
    if (!sizeof(groups)) {
	exit(0);
	return;
    }
    ids = get_unread(groups[0]);
    while (!ids) {
	groups = groups[1..sizeof(groups)];
	if (sizeof(groups))
	    ids = get_unread(groups[0]);
	else break;
    }
    if (!ids) {
	exit(0);
	return;
    }
    ask_about_group(0);
}

private void
start_main_loop() {
    if (!groups)
	groups = NEWSSERVER->get_groups();
    /* remove unsubscribed groups */
    groups = filter_array( groups, (: !read[$1] || read[$1][0] != '#' :) );
    if (!sizeof(groups)) {
	exit(0);
	return;
    }
    main_loop();
}

private void
list_groups() {
    string *groups = NEWSSERVER->get_groups();
    int i,n;
    int ur;
    string urs;

    for (i=0, n=sizeof(groups); i<n; i++) {
	ur = sizeof(get_unread(groups[i]));
	urs = (ur ? "(" + ur + ")" : "(READ)");
	if (IS_UNSUB(read[groups[i]]))
	    urs = "(UNSUB)";
	printf("%7s %s\n", urs, groups[i]);
    }
}

static void
ask_subscribe(string response, string group) {
    switch (response) {
    case "y":
	subscribe(group);
	groups = ({ group }) + groups;
	main_loop();
	return;
    case "n":
	main_loop();
	return;
    default:
	printf("%s is currently unsubscribed.  Resubscribe? [yn] ", group);
	input_to("ask_subscribe", 0, group);
    }
}

private void
goto_group( string group ) {
    if (member_array(group, NEWSSERVER->get_groups()) == -1) {
	write("No such group.");
	ask_about_group(0);
	return;
    }
    if (IS_UNSUB(read[group])) {
	ask_subscribe(0, group);
    } else {
	groups = ({ group }) + groups;
	main_loop();
    }
}

static void
ask_about_group(string response) {
    switch (response) {     /* break; instead of return; goes to next group */
    case "q": /* quit */
	exit(1);
	return;
    case "c": /* catch up */
	{
	    int i,n;
	    for (i=0, n=sizeof(ids); i<n; i++) 
		add_read(groups[0], ids[i]);
	    break;
	}
    case "u": /* unsubscribe */
	unsubscribe(groups[0]);
	break;
    case "p":
	post(groups[0], (: ask_about_group, 0 :));
	return;
    case "n": /* next group */
	break;
    case "=": /* show headers */
	ids = ({ -1 }) + ids;
	show_headers();
	ask_what_next(0);
	return;
    case "L": /* list groups */
	list_groups();
	ask_about_group(0);
	return;
    case "h":
    case "?":
	write(@ENDHELP
c - mark all articles in this group as read
h or ? - this help
L - show all groups
n - go on to next group
p - post an article to this group
q - quit rn
u - unsubscribe to this group
y - read first article
= - show articles in group, and start reading
ENDHELP);
	ask_about_group(0);
	return;
    case "y":
    case "": /* show article */
	show_article(ids[0]);
	return;
    default:
	if (response && response[0..1]=="g ") { /* group */
	    goto_group(response[2..strlen(response)]);
	    return;
	}
	printf("******* %i unread posts in %s -- read now? [chLnpquy?=] ", sizeof(ids), groups[0]);
	input_to("ask_about_group", 0);
	return;
    }
    next_group();
}

static void
ask_about_new_groups(string response, string *groups_left) {
    int n;

    if (response) {
	read[groups_left[0]] = (response == "y" ? 0 : "#");
	if ((n=sizeof(groups_left)) <= 1) {
	    start_main_loop();
	    return;
	} 
	groups_left = groups_left[1..n];
    }
    printf("group '%s' is new; subscribe? (y/n) >", groups_left[0]);
    input_to("ask_about_new_groups", 0, groups_left);
}

private void
load_newsrc(string file) {
    file = read_file(file);
    if (file)
	read = restore_variable(file);
}

private void
start_reading(string group) {
    if (group) {
	if (member_array(group, NEWSSERVER->get_groups()) == -1) {
	    write("No such group.");
	    return;
	}
	if (IS_UNSUB(read[group])) {
	    groups = ({});
	    ask_subscribe(0, group);
	    return;
	} else {
	    groups = ({ group });
	}
    } else {
	string *new_groups;

	new_groups = (string *)NEWSSERVER->get_groups() - keys(read);
	if (sizeof(new_groups)) {
	    ask_about_new_groups(0, new_groups);
	    return;
	}
    }
    start_main_loop();
}

private void
post(string group, function when_done) {
    if (member_array(group, NEWSSERVER->get_groups()) == -1) {
	write("No such group.");
	return;
    }
    printf("Subject: ");
    input_to("get_subject", 0, group, when_done);
}

private void
end_post(string subj, string group, function when_done) {
    string file;
    string message;

    message = read_file("/tmp/rn."+who->GetKeyName());
    rm("/tmp/rn."+who->GetKeyName());
    NEWSSERVER->post(group, subj, message);
    evaluate(when_done);
}

static void
get_subject(string subj, string group, function when_done) {
    who->eventEdit("/tmp/rn."+who->GetKeyName(), 
      (: end_post, subj, group, when_done :), 
      (: abort_post, when_done :));
}

int syntax_error() {
    return notify_fail("rn [-all] [-post] [newsgroup]\n");
}

#define READING    0
#define POSTING    1
#define LISTING    2

int
start_up( string str ) {
    string *args;
    string gname;
    int n;
    int what;

    who = this_player();

    if (str) {
	args = explode(str, " ");
	n = sizeof(args);
	while (n && args[0][0]=='-') {
	    switch (args[0]) {
	    case "-all":
		options |= ALL_ARTICLES;
		break;
	    case "-post":
		what = POSTING;
		break;
	    case "-list":
		what = LISTING;
		break;
	    case "-poster":
		if (n == 1) {
		    write("rn: must specify poster after -poster");
		    return 1;
		}
		restrict_poster = args[1];
		args = args[1..n--];
		break;
	    default:
		write("rn: unknown flag '"+args[0]+"'");
		return 1;
	    }
	    args = args[1..n--];
	}
	if (n>1)
	    return syntax_error();
	if (n)
	    gname = args[0];
    }
    switch(what) {
    case POSTING:
	if (!gname)
	    return notify_fail("Must specify group to post to.\n");
	post(gname, 0);
	break;
    case LISTING:
	load_newsrc(NEWSRC_SAVE_DIR + this_player()->GetKeyName());
	list_groups();
	break;
    case READING:
	load_newsrc(NEWSRC_SAVE_DIR + this_player()->GetKeyName());
	start_reading(gname);
	break;
    }
    return 1;
}