tmi2_fluffos_v2/
tmi2_fluffos_v2/bin/
tmi2_fluffos_v2/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/ChangeLog.old/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/Win32/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/simuls/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/clone/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/command/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/data/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/master/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/log/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/u/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/tmp/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/windows/
tmi2_fluffos_v2/lib/
tmi2_fluffos_v2/lib/adm/
tmi2_fluffos_v2/lib/adm/daemons/languages/
tmi2_fluffos_v2/lib/adm/daemons/network/I3/
tmi2_fluffos_v2/lib/adm/daemons/virtual/
tmi2_fluffos_v2/lib/adm/daemons/virtual/template/
tmi2_fluffos_v2/lib/adm/news/
tmi2_fluffos_v2/lib/adm/obj/
tmi2_fluffos_v2/lib/adm/obj/master/
tmi2_fluffos_v2/lib/adm/priv/
tmi2_fluffos_v2/lib/adm/shell/
tmi2_fluffos_v2/lib/adm/tmp/
tmi2_fluffos_v2/lib/cmds/
tmi2_fluffos_v2/lib/d/
tmi2_fluffos_v2/lib/d/Conf/
tmi2_fluffos_v2/lib/d/Conf/adm/
tmi2_fluffos_v2/lib/d/Conf/boards/
tmi2_fluffos_v2/lib/d/Conf/cmds/
tmi2_fluffos_v2/lib/d/Conf/data/
tmi2_fluffos_v2/lib/d/Conf/logs/
tmi2_fluffos_v2/lib/d/Conf/obj/
tmi2_fluffos_v2/lib/d/Conf/text/help/
tmi2_fluffos_v2/lib/d/Fooland/adm/
tmi2_fluffos_v2/lib/d/Fooland/data/
tmi2_fluffos_v2/lib/d/Fooland/data/attic/
tmi2_fluffos_v2/lib/d/Fooland/items/
tmi2_fluffos_v2/lib/d/TMI/
tmi2_fluffos_v2/lib/d/TMI/adm/
tmi2_fluffos_v2/lib/d/TMI/boards/
tmi2_fluffos_v2/lib/d/TMI/data/
tmi2_fluffos_v2/lib/d/TMI/rooms/
tmi2_fluffos_v2/lib/d/grid/
tmi2_fluffos_v2/lib/d/grid/adm/
tmi2_fluffos_v2/lib/d/grid/data/
tmi2_fluffos_v2/lib/d/std/
tmi2_fluffos_v2/lib/d/std/adm/
tmi2_fluffos_v2/lib/data/adm/
tmi2_fluffos_v2/lib/data/adm/daemons/
tmi2_fluffos_v2/lib/data/adm/daemons/doc_d/
tmi2_fluffos_v2/lib/data/adm/daemons/emoted/
tmi2_fluffos_v2/lib/data/adm/daemons/network/http/
tmi2_fluffos_v2/lib/data/adm/daemons/network/services/mail_q/
tmi2_fluffos_v2/lib/data/adm/daemons/network/smtp/
tmi2_fluffos_v2/lib/data/adm/daemons/news/archives/
tmi2_fluffos_v2/lib/data/attic/connection/
tmi2_fluffos_v2/lib/data/attic/user/
tmi2_fluffos_v2/lib/data/std/connection/b/
tmi2_fluffos_v2/lib/data/std/connection/l/
tmi2_fluffos_v2/lib/data/std/user/a/
tmi2_fluffos_v2/lib/data/std/user/b/
tmi2_fluffos_v2/lib/data/std/user/d/
tmi2_fluffos_v2/lib/data/std/user/f/
tmi2_fluffos_v2/lib/data/std/user/l/
tmi2_fluffos_v2/lib/data/std/user/x/
tmi2_fluffos_v2/lib/data/u/d/dm/working/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/smtp/
tmi2_fluffos_v2/lib/doc/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/interactive/
tmi2_fluffos_v2/lib/doc/driverdoc/concepts/
tmi2_fluffos_v2/lib/doc/driverdoc/driver/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/arrays/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/buffers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/compile/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/ed/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/filesystem/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/floats/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/functions/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/general/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/mappings/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/numbers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/parsing/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/constructs/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/preprocessor/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/types/
tmi2_fluffos_v2/lib/doc/driverdoc/platforms/
tmi2_fluffos_v2/lib/doc/mudlib/
tmi2_fluffos_v2/lib/ftp/
tmi2_fluffos_v2/lib/include/driver/
tmi2_fluffos_v2/lib/log/
tmi2_fluffos_v2/lib/log/driver/
tmi2_fluffos_v2/lib/obj/net/
tmi2_fluffos_v2/lib/obj/shells/
tmi2_fluffos_v2/lib/obj/tools/
tmi2_fluffos_v2/lib/std/adt/
tmi2_fluffos_v2/lib/std/board/
tmi2_fluffos_v2/lib/std/body/
tmi2_fluffos_v2/lib/std/fun/
tmi2_fluffos_v2/lib/std/living/
tmi2_fluffos_v2/lib/std/object/
tmi2_fluffos_v2/lib/std/shop/
tmi2_fluffos_v2/lib/std/socket/
tmi2_fluffos_v2/lib/std/user/
tmi2_fluffos_v2/lib/std/virtual/
tmi2_fluffos_v2/lib/student/
tmi2_fluffos_v2/lib/student/kalypso/
tmi2_fluffos_v2/lib/student/kalypso/armor/
tmi2_fluffos_v2/lib/student/kalypso/rooms/
tmi2_fluffos_v2/lib/student/kalypso/weapons/
tmi2_fluffos_v2/lib/u/l/leto/
tmi2_fluffos_v2/lib/u/l/leto/cmds/
tmi2_fluffos_v2/lib/www/errors/
tmi2_fluffos_v2/lib/www/gateways/
tmi2_fluffos_v2/lib/www/images/
tmi2_fluffos_v2/old/
tmi2_fluffos_v2/win32/
/* Copyright 1994 - 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 <mudlib.h>
#include <newsreader.h>
#include <daemons.h>
#include <ansi.h>

inherit 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);
// Mobydick added the next one
static void do_done() ;

#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;
}

/* why don't you GetRight, try to GetRight baby?
 * you haven't been Right with me
 * why don't you GetRight, try to GetRight baby?
 * don't you remember how it used to be
 *  ooh, ooh, ooh
 *	- Bob Dylan and the Band
*/
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 */

/* don't call this on an unsubscribed group */
private void
add_read(string group, int id) {
    read[group] = AddToList(read[group], id);
}

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

    if (!aborted) 
	write("End of news.\n") ;
    who->set("news", read) ;
    destruct(this_object());
}

void
end_follow(mixed *data) {
    string file;
    string message;
    string group = data[0];
    int id = data[1];

    file = who->query_edit_filename();
    message = read_file(file);
    rm(file);
    NEWS_D->followup(group, id, message);
    ask_what_next(0);
}

private void
abort(function when_done) {
    string file;

    file = who->query_edit_filename();
    if (file && file_exists(file)) rm (file);
    write("Post aborted.");
    evaluate(when_done);
}

private void
follow(string group, int id, string insert) {
    string fname ;

    fname = "/tmp/rm."+who->query("name") ;

    write_file(fname, insert, 1);
    who->edit(fname, "end_follow", this_object(), ({ group, id }) ) ;
}

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

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

    article = NEWS_D->get_message(groups[0], id);
    if (!article["MESSAGE"]) article["MESSAGE"] = "";
    if (!this_player()->query_env("NEWS_NO_CLEAR")) {
	write (CLR+HOME) ;
    }
    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) ;
}

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)];
    if (sizeof(groups)==0) {
	do_done() ;
	return ;
    }
    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 = NEWS_D->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 "?":
    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, NEWS_D->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? [cfFhnpqu?=] ");
    input_to("ask_what_next");
}

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

    for (n=sizeof(ids); i<n; i++) {
	article = NEWS_D->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;
    function filter;

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

    temp_kludge = STRIP_UNSUB(read[group]);
    ret = NEWS_D->get_messages(group, filter);
    if (!sizeof(ret)) return 0;
    return ret;
}

private void
main_loop() {
    if (!sizeof(groups)) {
	do_done() ;
	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) {
	do_done() ;
	return;
    }
    ask_about_group(0);
}

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

private void
list_groups() {
    string *groups = NEWS_D->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, NEWS_D->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 */
	if (sizeof(groups)==0) {
	    write ("No current group.\n") ;
	    do_done() ;
	    return ;
	}
	unsubscribe(groups[0]);
	break;
    case "p":
	if (sizeof(groups)==0) {
	    write ("No current group: use rn -post <groupname>\n") ;
	    do_done() ;
	    return ;
	}
	post(groups[0], (: ask_about_group, 0 :));
	return;
    case "n": /* next group */
	break;
    case "=": /* show headers */
	if (sizeof(groups)==0) {
	    write ("No current group to show headers.\n") ;
	    do_done() ;
	    return ;
	}
	show_headers();
	ids = ({ -1 }) + ids;
	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 "": /* show article */
    case "y":
	if (sizeof(groups)==0) {
	    do_done() ;
	    return ;
	}
	show_article(ids[0]);
	return;
    default:
	if (response && response[0..1]=="g ") { /* group */
	    goto_group(response[2..strlen(response)]);
	    return;
	}
	if (sizeof(groups)==0) {
	    do_done() ;
	    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 group) {
    string *new_groups;
    mapping file ;

    read = who->query("news") ;
    if (!read) read = ([ ]) ;
    if (group) {
	if (member_array(group, NEWS_D->get_groups()) == -1) {
	    write("No such group.");
	    return;
	}
	if (IS_UNSUB(read[group])) {
	    groups = ({});
	    ask_subscribe(0, group);
	    return;
	} else {
	    groups = ({ group });
	}
    } else {
	new_groups = (string *)NEWS_D->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, NEWS_D->get_groups()) == -1) {
	write("No such group.\n");
	return;
    }
    printf("Subject: ");
    input_to("get_subject", 0, group, when_done);
}

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

    subj = args[0] ;
    group = args[1] ;
    when_done = args[2] ;
    file = who->query_edit_filename();
    message = read_file(file);
    rm(file);
    NEWS_D->post(group, subj, message);
    evaluate(when_done);
}

static void
get_subject(string subj, string group, function when_done) {
    who->edit("/tmp/rn."+who->query("name"), "end_post", this_object(),
	({ subj, group, when_done })) ;
}

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

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

    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":
		posting = 1;
		break;
	    case "-poster":
		if (n == 1) {
		    write("rn: must specify poster after -poster\n");
		    return 1;
		}
		restrict_poster = args[1];
		args = args[1..n--];
		break;
	    default:
		write("rn: unknown flag '"+args[0]+"'\n");
		return 1;
	    }
	    args = args[1..n--];
	}
	if (n>1)
	    syntax_error();
            return 0;
	if (n)
	    gname = args[0];
    }
    if (posting) {
	if (!gname)
	    return notify_fail("Must specify group to post to.\n");
	post(gname, 0);
    } else
	load_newsrc(gname) ;
    return 1;
}

// Local Additions Begin here

// This is necessary to accomodate TMI-2's more system.

void done_more() {
	end_article() ;
}

// This lets people enter news even if there is no new news.

void do_done() {
    write ("No more news: do what? [chLnpquy?=] ") ;
    input_to ("ask_about_group", 0) ;
}