home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume23
/
trn
/
part04
/
ng.c
< prev
Wrap
C/C++ Source or Header
|
1991-08-22
|
35KB
|
1,561 lines
/* $Header: ng.c,v 4.3.3.3 91/01/16 03:18:16 davison Trn $
*
* $Log: ng.c,v $
* Revision 4.3.3.3 91/01/16 03:18:16 davison
* Integrated rn patches 48-54. Fixed in_char/verify interaction.
*
* Revision 4.3.3.2 90/08/20 16:03:45 davison
* Fixed bug in backpage code.
*
* Revision 4.3.3.1 90/07/21 20:27:17 davison
* Initial Trn Release
*
* Revision 4.3.2.9 91/01/05 14:56:47 sob
* Removed bogus "fast skip" for NNTP.
*
* Revision 4.3.2.8 90/11/22 16:14:17 sob
* Added changes to accomodate picky C preprocessors
*
* Revision 4.3.2.7 90/04/21 14:44:23 sob
* Revised previous patch insure that it does not decrement below zero.
*
* Revision 4.3.2.6 90/03/22 23:04:49 sob
* Fixes provided by Wayne Davison <drivax!davison>
*
* Revision 4.3.2.5 89/12/09 01:18:42 sob
* Fixed a bad call to nntpopen().
*
* Revision 4.3.2.4 89/11/28 01:51:20 sob
* Removed redundant #include directive.
*
* Revision 4.3.2.3 89/11/27 01:31:03 sob
* Altered NNTP code per ideas suggested by Bela Lubkin
* <filbo@gorn.santa-cruz.ca.us>
*
* Revision 4.3.2.2 89/11/26 22:53:35 sob
* Add new patches to make RRN be faster.
*
* Revision 4.3.2.1 89/11/06 00:54:27 sob
* Added RRN support from NNTP 1.5
*
* Revision 4.3.1.6 85/09/10 11:03:42 lwall
* Improved %m in in_char().
*
* Revision 4.3.1.5 85/09/05 12:34:37 lwall
* Catchup command could make unread article count too big.
*
* Revision 4.3.1.4 85/07/23 18:19:46 lwall
* Added MAILCALL environment variable.
*
* Revision 4.3.1.3 85/05/16 16:48:09 lwall
* Fixed unsubsubscribe.
*
* Revision 4.3.1.2 85/05/13 09:29:28 lwall
* Added CUSTOMLINES option.
*
* Revision 4.3.1.1 85/05/10 11:36:00 lwall
* Branch for patches.
*
* Revision 4.3 85/05/01 11:43:43 lwall
* Baseline for release with 4.3bsd.
*
*/
#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "term.h"
#include "final.h"
#include "util.h"
#include "artsrch.h"
#include "cheat.h"
#include "help.h"
#include "kfile.h"
#include "rcstuff.h"
#include "head.h"
#include "bits.h"
#include "art.h"
#include "artio.h"
#include "ngstuff.h"
#include "intrp.h"
#include "respond.h"
#include "ngdata.h"
#include "backpage.h"
#include "rcln.h"
#include "last.h"
#include "search.h"
#ifdef SERVER
#include "server.h"
#endif
#ifdef USETHREADS
#include "rthreads.h"
#endif
#include "uudecode.h"
#include "INTERN.h"
#include "ng.h"
#include "artstate.h" /* somebody has to do it */
/* art_switch() return values */
#define AS_NORM 0
#define AS_INP 1
#define AS_ASK 2
#define AS_CLEAN 3
ART_NUM recent_art = -1; /* previous article # for '-' command */
ART_NUM curr_art = -1; /* current article # */
int exit_code = NG_NORM;
void
ng_init()
{
#ifdef KILLFILES
open_kfile(KF_GLOBAL);
#endif
#ifdef CUSTOMLINES
init_compex(&hide_compex);
init_compex(&page_compex);
#endif
}
/* do newsgroup on line ng with name ngname */
/* assumes that we are chdir'ed to SPOOL, and assures that that is
* still true upon return, but chdirs to SPOOL/ngname in between
*
* If you can understand this routine, you understand most of the program.
* The basic structure is:
* for each desired article
* for each desired page
* for each line on page
* if we need another line from file
* get it
* if it's a header line
* do special things
* for each column on page
* put out a character
* end loop
* end loop
* end loop
* end loop
*
* (Actually, the pager is in another routine.)
*
* The chief problem is deciding what is meant by "desired". Most of
* the messiness of this routine is due to the fact that people want
* to do unstructured things all the time. I have used a few judicious
* goto's where I thought it improved readability. The rest of the messiness
* arises from trying to be both space and time efficient. Have fun.
*/
int
do_newsgroup(start_command)
char *start_command; /* command to fake up first */
{
#ifdef SERVER
char ser_line[256];
char artname[32];
static long our_pid=0;
#endif /* SERVER */
char oldmode = mode;
register long i; /* scratch */
int skipstate; /* how many unavailable articles */
/* have we skipped already? */
char *whatnext = "%sWhat next? [%s]";
#ifdef SERVER
if (our_pid == 0) /* Agreed, this is gross */
our_pid = getpid();
#endif /* SERVER */
#ifdef ARTSEARCH
srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
/* did they say -S? */
#endif
mode = 'a';
#ifdef USETHREADS
recent_p_art = curr_p_art = Nullart;
#endif
recent_art = curr_art = -1;
exit_code = NG_NORM;
#ifdef SERVER
sprintf(ser_line, "GROUP %s", ngname);
put_server(ser_line);
if (get_server(ser_line, sizeof(ser_line)) < 0) {
fprintf(stderr, "rrn: Unexpected close of server socket.\n");
finalize(1);
}
if (*ser_line != CHAR_OK) {
if (atoi(ser_line) != ERR_NOGROUP)
fprintf(stderr, "rrn: server response to GROUP %s:\n%s\n",
ngname, ser_line);
return (-1);
}
#else /* not SERVER */
if (eaccess(ngdir,5)) { /* directory read protected? */
if (eaccess(ngdir,0)) {
#ifdef VERBOSE
IF(verbose)
printf("\nNewsgroup %s does not have a spool directory!\n",
ngname) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\nNo spool for %s!\n",ngname) FLUSH;
#endif
#ifdef CATCHUP
catch_up(ng);
#endif
toread[ng] = TR_NONE;
}
else {
#ifdef VERBOSE
IF(verbose)
printf("\nNewsgroup %s is not currently accessible.\n",
ngname) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\n%s not readable.\n",ngname) FLUSH;
#endif
toread[ng] = TR_NONE; /* make this newsgroup invisible */
/* (temporarily) */
}
mode = oldmode;
return -1;
}
/* chdir to newsgroup subdirectory */
if (chdir(ngdir)) {
printf(nocd,ngdir) FLUSH;
mode = oldmode;
return -1;
}
#endif /* SERVER */
#ifdef CACHESUBJ
subj_list = Null(char **); /* no subject list till needed */
#endif
/* initialize control bitmap */
if (initctl()) {
mode = oldmode;
return -1;
}
/* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */
/* grab threaded data */
#ifdef USETHREADS
if (ThreadedGroup && (ThreadedGroup = use_data(thread_name(ngname)))) {
/* check if thread file is newer than the active2 entry (this is
** possible when mthreads is still running.) */
if (total.last > lastart) {
grow_ctl(total.last); /* sets lastart */
}
else if (total.last < lastart) {
/* If the active2 entry is newer than the data file, something
** bad is going on. Forget using the thread data. */
unuse_data(0);
ThreadedGroup = FALSE;
}
}
#endif
in_ng = TRUE; /* tell the world we are here */
forcelast = TRUE; /* if 0 unread, do not bomb out */
/* remember what newsgroup we were in for sake of posterity */
writelast();
/* see if there are any special searches to do */
#ifdef KILLFILES
open_kfile(KF_LOCAL);
#ifdef VERBOSE
IF(verbose)
kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE);
ELSE
#endif
#ifdef TERSE
kill_unwanted(firstart,"Killing...\n\n",TRUE);
#endif
#endif
#ifdef USETHREADS
first_art();
#else
art=firstart;
#endif
/* do they want a special top line? */
firstline = getval("FIRSTLINE",Nullch);
/* custom line suppression, custom page ending */
#ifdef CUSTOMLINES
if (hideline = getval("HIDELINE",Nullch))
compile(&hide_compex,hideline,TRUE,TRUE);
if (pagestop = getval("PAGESTOP",Nullch))
compile(&page_compex,pagestop,TRUE,TRUE);
#endif
/* now read each unread article */
rc_changed = doing_ng = TRUE; /* enter the twilight zone */
skipstate = 0; /* we have not skipped anything (yet) */
checkcount = 0; /* do not checkpoint for a while */
do_fseek = FALSE; /* start 1st article at top */
if (art > lastart)
#ifdef USETHREADS
first_art();
#else
art=firstart; /* init the for loop below */
#endif
for (; art<=lastart+1; ) { /* for each article */
/* do we need to "grow" the newsgroup? */
#ifdef USETHREADS
if (ThreadedGroup) {
if ((art > lastart || forcegrow) && getngsize(ng) > total.last) {
unuse_data(1); /* free data with selections saved */
if ((ThreadedGroup = use_data(thread_name(ngname))) != 0) {
grow_ctl(total.last); /* sets lastart */
find_article(art);
curr_p_art = p_art;
}
forcegrow = FALSE;
}
}
else
#endif
if (art > lastart || forcegrow)
grow_ctl(getngsize(ng));
check_first(art); /* make sure firstart is still 1st */
if (start_command) { /* fake up an initial command? */
prompt = whatnext;
strcpy(buf,start_command);
free(start_command);
start_command = Nullch;
#ifdef USETHREADS
p_art = Nullart;
#endif
art = lastart+1;
goto article_level;
}
if (art>lastart) { /* are we off the end still? */
ART_NUM ucount = 0; /* count of unread articles left */
for (i=firstart; i<=lastart; i++)
if (!(ctl_read(i)))
ucount++; /* count the unread articles */
#ifdef DEBUGGING
/*NOSTRICT*/
if (debug && ((ART_NUM)toread[ng]) != ucount)
printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount)
FLUSH;
#endif
/*NOSTRICT*/
toread[ng] = (ART_UNREAD)ucount; /* this is perhaps pointless */
art = lastart + 1; /* keep bitmap references sane */
if (art != curr_art) {
#ifdef USETHREADS
recent_p_art = curr_p_art;
find_article(art);
curr_p_art = p_art;
#endif
recent_art = curr_art;
/* remember last article # (for '-') */
curr_art = art; /* remember this article # */
}
if (erase_screen)
clear(); /* clear the screen */
else
fputs("\n\n",stdout) FLUSH;
#ifdef VERBOSE
IF(verbose)
printf("End of newsgroup %s.",ngname);
/* print pseudo-article */
ELSE
#endif
#ifdef TERSE
printf("End of %s",ngname);
#endif
#ifdef USETHREADS
if (ThreadedGroup)
ucount -= unthreaded;
#endif
if (ucount) {
#ifdef USETHREADS
if (selected_root_cnt)
printf(" (%ld + %ld articles still unread)",
(long)selected_count,(long)ucount-selected_count);
else
#endif
printf(" (%ld article%s still unread)",
(long)ucount,ucount==1?nullstr:"s");
}
else {
if (!forcelast)
goto cleanup; /* actually exit newsgroup */
}
prompt = whatnext;
#ifdef ARTSEARCH
srchahead = 0; /* no more subject search mode */
#endif
fputs("\n\n",stdout) FLUSH;
skipstate = 0; /* back to none skipped */
}
else if (!reread && was_read(art)) {
/* has this article been read? */
#ifdef USETHREADS
follow_thread('n');
#else
art++; /* then skip it */
#endif
continue;
}
else if
(!reread && !was_read(art)
#ifdef SERVER
&& nntpopen(art,GET_HEADER) == Nullfp) {
#else
&& artopen(art) == Nullfp) { /* never read it, & cannot find it? */
if (errno != ENOENT) { /* has it not been deleted? */
#ifdef VERBOSE
IF(verbose)
printf("\n(Article %ld exists but is unreadable.)\n",
(long)art) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\n(%ld unreadable.)\n",(long)art) FLUSH;
#endif
skipstate = 0;
sleep(2);
}
#endif
switch(skipstate++) {
case 0:
clear();
#ifdef VERBOSE
IF(verbose)
fputs("Skipping unavailable article",stdout);
ELSE
#endif
#ifdef TERSE
fputs("Skipping",stdout);
#endif
pad(just_a_sec/3);
sleep(1);
break;
case 1:
fputs("..",stdout);
fflush(stdout);
break;
default:
putchar('.');
fflush(stdout);
#ifndef SERVER
#define READDIR
#ifdef READDIR
{ /* fast skip patch */
ART_NUM newart;
if (! (newart=getngmin(".",art)))
newart = lastart+1;
for (i=art; i<newart; i++)
oneless(i);
art = newart - 1;
}
#endif /* READDIR */
#endif /* SERVER */
break;
}
oneless(art); /* mark deleted as read */
#ifdef USETHREADS
count_roots(FALSE); /* Keep selected_count accurate */
find_article(art);
follow_thread('n');
#else
art++; /* try next article */
#endif
continue;
}
else { /* we have a real live article */
skipstate = 0; /* back to none skipped */
if (art != curr_art) {
#ifdef USETHREADS
recent_p_art = curr_p_art;
find_article(art);
curr_p_art = p_art;
#endif
recent_art = curr_art;
/* remember last article # (for '-') */
curr_art = art; /* remember this article # */
}
if (!do_fseek) { /* starting at top of article? */
artline = 0; /* start at the beginning */
topline = -1; /* and remember top line of screen */
/* (line # within article file) */
}
clear(); /* clear screen */
if (!artopen(art)) { /* make sure article is found & open */
#ifdef USETHREADS
char tmpbuf[256];
/* see if we have tree data for this article anyway */
init_tree();
sprintf(tmpbuf,"%s #%ld is not available.",ngname,(long)art);
tree_puts(tmpbuf,0,0);
vwtary((ART_LINE)0,(ART_POS)0);
finish_tree(1);
prompt = whatnext;
#else
printf("Article %ld of %s is not available.\n\n",
(long)art,ngname) FLUSH;
prompt = whatnext;
#endif
#ifdef ARTSEARCH
srchahead = 0;
#endif
}
else { /* found it, so print it */
switch (do_article()) {
case DA_CLEAN: /* quit newsgroup */
goto cleanup;
case DA_TOEND: /* do not mark as read */
goto reask_article;
case DA_RAISE: /* reparse command at end of art */
goto article_level;
case DA_NORM: /* normal end of article */
break;
}
}
if (art >= absfirst) /* don't mark non-existant articles */
mark_as_read(); /* mark current article as read */
reread = FALSE;
do_hiding = TRUE;
#ifdef ROTATION
rotate = FALSE;
#endif
}
/* if these gotos bother you, think of this as a little state machine */
reask_article:
#ifdef MAILCALL
setmail();
#endif
setdfltcmd();
#ifdef CLEAREOL
if (erase_screen && can_home_clear)
clear_rest();
#endif /* CLEAREOL */
unflush_output(); /* disable any ^O in effect */
standout(); /* enter standout mode */
printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */
un_standout(); /* leave standout mode */
putchar(' ');
fflush(stdout);
reinp_article:
eat_typeahead();
#ifdef PENDING
look_ahead(); /* see what we can do in advance */
if (!input_pending())
collect_subjects(); /* loads subject cache until */
/* input is pending */
#endif
getcmd(buf);
if (errno || *buf == '\f') {
if (LINES < 100 && !int_count)
*buf = '\f'; /* on CONT fake up refresh */
else {
putchar('\n') FLUSH; /* but only on a crt */
goto reask_article;
}
}
article_level:
/* parse and process article level command */
switch (art_switch()) {
case AS_INP: /* multichar command rubbed out */
goto reinp_article;
case AS_ASK: /* reprompt "End of article..." */
goto reask_article;
case AS_CLEAN: /* exit newsgroup */
goto cleanup;
case AS_NORM: /* display article art */
break;
}
} /* end of article selection loop */
/* shut down newsgroup */
cleanup:
uud_end();
#ifdef KILLFILES
kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
/* do cleanup from KILL file, if any */
#endif
#ifdef USETHREADS
if (ThreadedGroup)
unuse_data(0); /* free article thread data */
#endif
in_ng = FALSE; /* leave newsgroup state */
if (artfp != Nullfp) { /* article still open? */
fclose(artfp); /* close it */
artfp = Nullfp; /* and tell the world */
#ifdef SERVER
sprintf(artname, "/tmp/rrn%ld.%ld", (long) openart, our_pid);
UNLINK(artname);
#endif /* SERVER */
openart = 0;
}
putchar('\n') FLUSH;
yankback(); /* do a Y command */
restore_ng(); /* reconstitute .newsrc line */
doing_ng = FALSE; /* tell sig_catcher to cool it */
free(ctlarea); /* return the control area */
#ifdef CACHESUBJ
if (subj_list) {
for (i=OFFSET(lastart); i>=0; --i)
if (subj_list[i])
free(subj_list[i]);
#ifndef lint
free((char*)subj_list);
#endif /* lint */
}
#endif
write_rc(); /* and update .newsrc */
rc_changed = FALSE; /* tell sig_catcher it is ok */
if (chdir(spool)) {
printf(nocd,spool) FLUSH;
sig_catcher(0);
}
#ifdef KILLFILES
if (localkfp) {
fclose(localkfp);
localkfp = Nullfp;
}
#endif
mode = oldmode;
return exit_code;
} /* Whew! */
/* decide what to do at the end of an article */
int
art_switch()
{
register ART_NUM i;
setdef(buf,dfltcmd);
#ifdef VERIFY
printcmd();
#endif
switch (*buf) {
#ifdef USETHREADS
case '<': /* goto previous thread */
if (!ThreadedGroup) {
goto group_unthreaded;
}
prev_root();
return AS_NORM;
case '>': /* goto next thread */
if (!ThreadedGroup) {
goto group_unthreaded;
}
next_root();
return AS_NORM;
case 'U': { /* unread some articles */
char *u_prompt, *u_help_thread;
if (!ThreadedGroup) {
dfltcmd = "a";
u_help_thread = nullstr;
#ifdef VERBOSE
IF(verbose)
u_prompt = "\nSet unread: all articles? [an] ";
ELSE
#endif
#ifdef TERSE
u_prompt = "\nUnread? [an] ";
#endif
}
else if (!p_art || art > lastart) {
dfltcmd = "+";
u_help_thread = nullstr;
#ifdef VERBOSE
IF(verbose)
u_prompt = "\nSet unread: +select or all? [+an] ";
ELSE
#endif
#ifdef TERSE
u_prompt = "\nUnread? [+an] ";
#endif
}
else {
dfltcmd = "t";
#ifdef VERBOSE
IF(verbose) {
u_prompt = "\n\
Set unread: thread, subthread, +select, or all? [ts+an] ";
u_help_thread = "\
Type t or SP to mark this thread's articles as unread.\n\
Type s to mark the current article and its descendants as unread.\n";
}
ELSE
#endif
#ifdef TERSE
{
u_prompt = "\nUnread? [ts+an] ";
u_help_thread = "\
t or SP to mark thread unread.\n\
s to mark subthread unread.\n";
}
#endif
}
reask_unread:
in_char(u_prompt,'u');
setdef(buf,dfltcmd);
#ifdef VERIFY
printcmd();
#endif
putchar('\n') FLUSH;
if (*buf == 'h') {
fputs(u_help_thread,stdout);
#ifdef VERBOSE
IF(verbose)
{
if (ThreadedGroup)
fputs("\
Type + to enter select thread mode using all the unread articles.\n\
(The selected threads will be marked as unread and displayed as usual.)\n\
",stdout) FLUSH;
fputs("\
Type a to mark all articles in this group as unread.\n\
Type n to change nothing.\n\
",stdout) FLUSH;
}
ELSE
#endif
#ifdef TERSE
{
if (ThreadedGroup)
fputs("\
+ to select threads from the unread.\n\
",stdout) FLUSH;
fputs("\
a to mark all articles unread.\n\
n to change nothing.\n\
",stdout) FLUSH;
}
#endif
goto reask_unread;
}
else if (*buf == 'n' || *buf == 'q') {
return AS_ASK;
}
else if (*buf == 't' && *dfltcmd == 't')
follow_thread('u');
else if (*buf == 's' && *dfltcmd == 't') {
follow_thread('U');
}
else if (*buf == 'a') {
check_first(absfirst);
for (i = absfirst; i <= lastart; i++) {
onemore(i); /* mark as unread */
}
scan_all_roots = FALSE;
count_roots(FALSE);
if (art > lastart) {
first_art();
}
}
else if (ThreadedGroup && *buf == '+') {
*buf = 'U';
goto select_threads;
}
else {
fputs(hforhelp,stdout) FLUSH;
settle_down();
goto reask_unread;
}
return AS_NORM;
}
case '[': /* goto parent article */
case '{': /* goto thread's root article */
if (p_art) {
if (!p_art->parent) {
if (p_art == p_articles + p_roots[p_art->root].articles) {
register char *cp = (*buf=='['?"parent":"root");
#ifdef VERBOSE
IF(verbose)
fprintf(stdout,"\n\
There is no %s article prior to this one.\n",cp) FLUSH;
ELSE
#endif
#ifdef TERSE
fprintf(stdout,"\nNo prior %s.\n",cp) FLUSH;
#endif
return AS_ASK;
}
*buf = '{';
p_art--;
}
else
p_art += p_art->parent;
if (*buf == '{')
while (p_art->parent)
p_art += p_art->parent;
art = p_art->num;
reread = TRUE;
return AS_NORM;
}
not_threaded:
if (ThreadedGroup) {
#ifdef VERBOSE
IF(verbose)
fputs("\nThis article is not threaded.\n",stdout) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("\nUnthreaded article.\n",stdout) FLUSH;
#endif
return AS_ASK;
}
group_unthreaded:
#ifdef VERBOSE
IF(verbose)
fputs("\nThis group is not threaded.\n",stdout) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("\nUnthreaded group.\n",stdout) FLUSH;
#endif
return AS_ASK;
case ']': /* goto child article */
case '}': /* goto thread's leaf article */
if (p_art) {
if (!(p_art++)->child_cnt) {
PACKED_ARTICLE *root_limit = upper_limit(p_art-1,FALSE);
if (p_art == root_limit) {
#ifdef VERBOSE
IF(verbose)
fputs("\n\
This is the last leaf in this tree.\n",stdout) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("\nLast leaf.\n",stdout) FLUSH;
#endif
p_art--;
return AS_ASK;
}
if (*buf == ']')
*buf = '}';
else {
while (++p_art != root_limit && p_art->parent)
;
p_art--;
*buf = ' ';
}
}
if( *buf == '}' )
while (p_art->child_cnt)
p_art++;
art = p_art->num;
reread = TRUE;
return AS_NORM;
}
goto not_threaded;
case 'T':
if (p_art) {
sprintf(buf,"T%ld\t# %s",(long)p_roots[p_art->root].root_num,
subject_ptrs[p_art->subject]);
fputs(buf,stdout);
kf_append(buf);
*buf = 'J';
goto follow_threads;
}
goto not_threaded;
case 'K':
if (p_art) {
/* first, write kill-subject command */
(void)art_search(buf, (sizeof buf), TRUE);
art = curr_art;
p_art = curr_p_art;
*buf = 'k'; /* then take care of any prior subjs */
goto follow_threads;
}
goto normal_search;
case ',': /* kill this node and all descendants */
mark_as_read();
*buf = 'K';
case 'k': /* kill current subject # (e.g. [1]) */
case 'J': /* Junk all nodes in this thread */
if (!ThreadedGroup) {
*buf = 'k';
goto normal_search;
}
follow_threads:
follow_thread(*buf);
if (!reread && !toread[ng])
return AS_CLEAN;
if (!reread && selected_root_cnt && !selected_count)
goto select_threads;
return AS_NORM;
case 't':
carriage_return();
#ifndef CLEAREOL
erase_eol(); /* erase the prompt */
#else
if (erase_screen && can_home_clear)
clear_rest();
else
erase_eol(); /* erase the prompt */
#endif /* CLEAREOL */
fflush(stdout);
page_line = 1;
p_art = curr_p_art;
entire_tree();
return AS_ASK;
case ':': /* execute command on selected articles */
if (!ThreadedGroup) {
goto group_unthreaded;
}
page_line = 1;
if (!use_selected())
return AS_INP;
putchar('\n');
art = curr_art;
p_art = curr_p_art;
return AS_ASK;
#endif /* USETHREADS */
case 'p': /* find previous unread article */
#ifdef USETHREADS
if (ThreadedGroup) {
goto backtrack_threads;
}
#endif
do {
if (art <= firstart)
break;
art--;
#ifdef SERVER
} while (was_read(art) || nntpopen(art,GET_HEADER) == Nullfp);
#else
} while (was_read(art) || artopen(art) == Nullfp);
#endif
#ifdef ARTSEARCH
srchahead = 0;
#endif
return AS_NORM;
case 'P': /* goto previous article */
#ifdef USETHREADS
if (ThreadedGroup) {
backtrack_threads:
backtrack_thread( *buf );
art++; /* prepare for art-- below */
}
#endif
if (art > absfirst)
art--;
else {
#ifdef VERBOSE
IF(verbose)
fprintf(stdout,"\n\
There are no%s articles prior to this one.\n\
",*buf=='P'?nullstr:" unread") FLUSH;
ELSE
#endif
#ifdef TERSE
fprintf(stdout,"\n\
No previous%s articles\n\
",*buf=='P'?nullstr:" unread") FLUSH;
#endif
art = curr_art;
#ifdef USETHREADS
p_art = curr_p_art;
#endif
return AS_ASK;
}
reread = TRUE;
#ifdef ARTSEARCH
srchahead = 0;
#endif
return AS_NORM;
case '-':
if (recent_art >= 0) {
#ifdef USETHREADS
p_art = recent_p_art;
#endif
art = recent_art;
reread = TRUE;
#ifdef ARTSEARCH
srchahead = -(srchahead != 0);
#endif
return AS_NORM;
}
else {
exit_code = NG_MINUS;
return AS_CLEAN;
}
case 'n': /* find next unread article? */
#ifdef USETHREADS
if (ThreadedGroup) {
goto follow_threads;
}
#endif
if (art > lastart) {
if (!toread[ng])
return AS_CLEAN;
art = firstart;
}
#ifdef ARTSEARCH
else if (scanon && srchahead) {
*buf = Ctl('n');
goto normal_search;
}
#endif
else
art++;
#ifdef ARTSEARCH
srchahead = 0;
#endif
return AS_NORM;
case 'N': /* goto next article */
#ifdef USETHREADS
if (ThreadedGroup) {
goto follow_threads;
}
#endif
if (art > lastart)
art = absfirst;
else
art++;
if (art <= lastart)
reread = TRUE;
#ifdef ARTSEARCH
srchahead = 0;
#endif
return AS_NORM;
case '$':
art = lastart+1;
forcelast = TRUE;
#ifdef USETHREADS
p_art = Nullart;
#endif
#ifdef ARTSEARCH
srchahead = 0;
#endif
return AS_NORM;
case '1': case '2': case '3': /* goto specified article */
case '4': case '5': case '6': /* or do something with a range */
case '7': case '8': case '9': case '.':
forcelast = TRUE;
switch (numnum()) {
case NN_INP:
return AS_INP;
case NN_ASK:
return AS_ASK;
case NN_REREAD:
reread = TRUE;
#ifdef ARTSEARCH
if (srchahead)
srchahead = -1;
#endif
break;
case NN_NORM:
if (was_read(art)) {
#ifdef USETHREADS
first_art();
#else
art = firstart;
#endif
pad(just_a_sec/3);
}
else {
putchar('\n');
return AS_ASK;
}
break;
}
return AS_NORM;
case Ctl('k'):
edit_kfile();
return AS_ASK;
#ifndef USETHREADS
case 'K':
case 'k':
#endif
case Ctl('n'): /* search for next article with same subject */
#ifdef USETHREADS
if (ThreadedGroup) {
goto follow_threads;
}
#endif
case Ctl('p'): /* search for previous article with same subject */
#ifdef USETHREADS
if (ThreadedGroup) {
goto backtrack_threads;
}
#endif
case '/': case '?':
normal_search:
#ifdef ARTSEARCH
{ /* search for article by pattern */
char cmd = *buf;
reread = TRUE; /* assume this */
page_line = 1;
switch (art_search(buf, (sizeof buf), TRUE)) {
case SRCH_ERROR:
art = curr_art;
return AS_ASK;
case SRCH_ABORT:
art = curr_art;
return AS_INP;
case SRCH_INTR:
#ifdef VERBOSE
IF(verbose)
printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\n(Intr at %ld)\n",(long)art) FLUSH;
#endif
art = curr_art;
/* restore to current article */
return AS_ASK;
case SRCH_DONE:
fputs("done\n",stdout) FLUSH;
pad(just_a_sec/3); /* 1/3 second */
if (!srchahead) {
art = curr_art;
return AS_ASK;
}
#ifdef USETHREADS
first_art();
#else
art = firstart;
#endif
reread = FALSE;
return AS_NORM;
case SRCH_SUBJDONE:
#ifdef UNDEF
fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH;
pad(just_a_sec/3); /* 1/3 second */
#endif
#ifdef USETHREADS
first_art();
#else
art = firstart;
#endif
reread = FALSE;
return AS_NORM;
case SRCH_NOTFOUND:
fputs("\n\n\n\nNot found.\n",stdout) FLUSH;
art = curr_art; /* restore to current article */
return AS_ASK;
case SRCH_FOUND:
if (cmd == Ctl('n') || cmd == Ctl('p'))
oldsubject = TRUE;
break;
}
return AS_NORM;
}
#else
buf[1] = '\0';
notincl(buf);
return AS_ASK;
#endif
case 'u': /* unsubscribe from this newsgroup? */
rcchar[ng] = NEGCHAR;
return AS_CLEAN;
case 'M':
#ifdef DELAYMARK
if (art <= lastart) {
delay_unmark(art);
printf("\nArticle %ld will return.\n",(long)art) FLUSH;
}
#else
notincl("M");
#endif
return AS_ASK;
case 'm':
if (art <= lastart) {
unmark_as_read();
printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH;
}
return AS_ASK;
case 'c': /* catch up */
reask_catchup:
#ifdef VERBOSE
IF(verbose)
in_char("\nDo you really want to mark everything as read? [yn] ",
'C');
ELSE
#endif
#ifdef TERSE
in_char("\nReally? [ynh] ", 'C');
#endif
setdef(buf,"y");
#ifdef VERIFY
printcmd();
#endif
putchar('\n') FLUSH;
if (*buf == 'h') {
#ifdef VERBOSE
IF(verbose)
fputs("\
Type y or SP to mark all articles as read.\n\
Type n to leave articles marked as they are.\n\
Type u to mark everything read and unsubscribe.\n\
",stdout) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("\
y or SP to mark all read.\n\
n to forget it.\n\
u to mark all and unsubscribe.\n\
",stdout) FLUSH;
#endif
goto reask_catchup;
}
else if (*buf == 'n' || *buf == 'q') {
return AS_ASK;
}
else if (*buf != 'y' && *buf != 'u') {
fputs(hforhelp,stdout) FLUSH;
settle_down();
goto reask_catchup;
}
for (i = firstart; i <= lastart; i++) {
oneless(i); /* mark as read */
}
#ifdef USETHREADS
selected_root_cnt = selected_count = 0;
#endif
#ifdef DELAYMARK
if (dmfp)
yankback();
#endif
if (*buf == 'u') {
rcchar[ng] = NEGCHAR;
return AS_CLEAN;
}
#ifdef USETHREADS
p_art = Nullart;
selected_count = 0;
#endif
art = lastart+1;
forcelast = FALSE;
return AS_NORM;
case 'Q':
exit_code = NG_ASK;
/* FALL THROUGH */
case 'q': /* go back up to newsgroup level? */
return AS_CLEAN;
case 'j':
putchar('\n') FLUSH;
if (art <= lastart)
mark_as_read();
return AS_ASK;
case 'h': { /* help? */
int cmd;
if ((cmd = help_art()) > 0)
pushchar(cmd);
return AS_ASK;
}
case '&':
if (switcheroo()) /* get rest of command */
return AS_INP; /* if rubbed out, try something else */
return AS_ASK;
case '#':
#ifdef VERBOSE
IF(verbose)
printf("\nThe last article is %ld.\n",(long)lastart) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\n%ld\n",(long)lastart) FLUSH;
#endif
return AS_ASK;
#ifdef USETHREADS
case '+': /* enter thread selection mode */
if (ThreadedGroup) {
select_threads:
*buf = select_thread(*buf);
if (*buf == 'q') {
putchar( '\n' ) FLUSH;
return AS_ASK;
}
if (*buf == 'Q') {
exit_code = NG_ASK;
return AS_CLEAN;
}
if (*buf == 'N' || !toread[ng])
return AS_CLEAN;
return AS_NORM;
}
/* FALLTHROUGH */
#endif
case '=': { /* list subjects */
char tmpbuf[256];
ART_NUM oldart = art;
int cmd;
char *subjline = getval("SUBJLINE",Nullch);
#ifndef CACHESUBJ
char *s;
#endif
page_init();
#ifdef CACHESUBJ
if (!subj_list)
fetchsubj(art,TRUE,FALSE);
#endif
for (i=firstart; i<=lastart && !int_count; i++) {
#ifdef CACHESUBJ
if (!was_read(i) &&
(subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) &&
*subj_list[OFFSET(i)] ) {
sprintf(tmpbuf,"%5ld ", i);
if (subjline) {
art = i;
interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
}
else
safecpy(tmpbuf + 6, subj_list[OFFSET(i)],
(sizeof tmpbuf) - 6);
if (cmd = print_lines(tmpbuf,NOMARKING)) {
if (cmd > 0)
pushchar(cmd);
break;
}
}
#else
if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) {
sprintf(tmpbuf,"%5ld ", i);
if (subjline) { /* probably fetches it again! */
art = i;
interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
}
else
safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6);
if (cmd = print_lines(tmpbuf,NOMARKING)) {
if (cmd > 0)
pushchar(cmd);
break;
}
}
#endif
}
int_count = 0;
art = oldart;
return AS_ASK;
}
case '^':
#ifdef USETHREADS
first_art();
#else
art = firstart;
#endif
#ifdef ARTSEARCH
srchahead = 0;
#endif
return AS_NORM;
#if defined(CACHESUBJ) && defined(DEBUGGING)
case 'D':
printf("\nFirst article: %ld\n",(long)firstart) FLUSH;
if (!subj_list)
fetchsubj(art,TRUE,FALSE);
if (subj_list != Null(char **)) {
for (i=1; i<=lastart && !int_count; i++) {
if (subj_list[OFFSET(i)])
printf("%5ld %c %s\n",
i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH;
}
}
int_count = 0;
return AS_ASK;
#endif
case 'v':
if (art <= lastart) {
reread = TRUE;
do_hiding = FALSE;
}
return AS_NORM;
#ifdef ROTATION
case Ctl('x'):
#endif
case Ctl('r'):
#ifdef ROTATION
rotate = (*buf==Ctl('x'));
#endif
if (art <= lastart)
reread = TRUE;
return AS_NORM;
#ifdef ROTATION
case 'X':
rotate = !rotate;
/* FALL THROUGH */
#else
case Ctl('x'):
case 'x':
case 'X':
notincl("x");
return AS_ASK;
#endif
case 'l': case Ctl('l'): /* refresh screen */
if (art <= lastart) {
reread = TRUE;
clear();
do_fseek = TRUE;
artline = topline;
if (artline < 0)
artline = 0;
}
return AS_NORM;
case 'b': case Ctl('b'): /* back up a page */
if (art <= lastart) {
ART_LINE target;
reread = TRUE;
clear();
do_fseek = TRUE;
target = topline - (LINES - 2);
artline = topline;
if (artline >= 0) do {
artline--;
} while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
topline = artline;
if (artline < 0)
artline = 0;
}
return AS_NORM;
case '!': /* shell escape */
if (escapade())
return AS_INP;
return AS_ASK;
case 'C': {
cancel_article();
return AS_ASK;
}
case 'R':
case 'r': { /* reply? */
reply();
return AS_ASK;
}
case 'F':
case 'f': { /* followup command */
followup();
forcegrow = TRUE; /* recalculate lastart */
return AS_ASK;
}
case '|':
case 'w': case 'W':
case 's': case 'S': /* save command */
case 'e': /* extract command */
if (save_article() == SAVE_ABORT)
return AS_INP;
int_count = 0;
return AS_ASK;
case 'E':
if (uu_out != Nullfp) {
uud_end();
}
return AS_ASK;
#ifdef DELAYMARK
case 'Y': /* yank back M articles */
yankback();
#ifdef USETHREADS
first_art();
#else
art = firstart; /* from the beginning */
#endif
return AS_NORM; /* pretend nothing happened */
#endif
#ifdef STRICTCR
case '\n':
fputs(badcr,stdout) FLUSH;
return AS_ASK;
#endif
default:
printf("\n%s",hforhelp) FLUSH;
settle_down();
return AS_ASK;
}
}
#ifdef MAILCALL
/* see if there is any mail */
void
setmail()
{
if (! (mailcount++)) {
char *mailfile = filexp(getval("MAILFILE",MAILFILE));
if (stat(mailfile,&filestat) < 0 || !filestat.st_size
|| filestat.st_atime > filestat.st_mtime)
mailcall = nullstr;
else
mailcall = getval("MAILCALL","(Mail) ");
}
mailcount %= 10; /* check every 10 articles */
}
#endif
void
setdfltcmd()
{
#ifdef USETHREADS
if (toread[ng] == unthreaded) {
#else
if (!toread[ng]) {
#endif
if (art > lastart)
dfltcmd = "qnp";
else
dfltcmd = "npq";
}
else {
#ifdef USETHREADS
if (selected_root_cnt && !selected_count)
dfltcmd = "+npq";
else
# ifdef ARTSEARCH
if (!ThreadedGroup && srchahead)
dfltcmd = "^Nnpq";
else
# endif
#else
# ifdef ARTSEARCH
if (srchahead)
dfltcmd = "^Nnpq";
else
# endif
#endif
dfltcmd = "npq";
}
}