/* 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) ; }