Newsgroups: comp.sources.unix
From: davison@borland.com (Wayne Davison)
Subject: v27i101: trn-3.3 - threaded newsreader based on RN, V3.3, Part08/12
References: <1.754431075.7231@gw.home.vix.com>
Sender: unix-sources-moderator@gw.home.vix.com
Approved: vixie@gw.home.vix.com

Submitted-By: davison@borland.com (Wayne Davison)
Posting-Number: Volume 27, Issue 101
Archive-Name: trn-3.3/part08

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 12)."
# Contents:  common.h parsedate.y rcstuff.c rt-select.c
# Wrapped by vixie@gw.home.vix.com on Sun Nov 21 01:14:07 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'common.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'common.h'\"
else
echo shar: Extracting \"'common.h'\" \(25539 characters\)
sed "s/^X//" >'common.h' <<'END_OF_FILE'
X/* $Id: common.h,v 3.0 1992/02/23 21:25:39 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction of this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <ctype.h>
X#include "config.h"	/* generated by installation script */
X#ifndef isalnum
X#   define isalnum(c) (isalpha(c) || isdigit(c))
X#endif
X
X#include <errno.h>
X#include <signal.h>
X#ifdef I_SYS_IOCTL
X#include <sys/ioctl.h>
X#endif
X#ifdef I_VFORK
X#  include <vfork.h>
X#endif
X#include <fcntl.h>
X
X#ifdef I_TERMIO
X#   include <termio.h>
X#else
X# ifdef I_TERMIOS
X#   include <termios.h>
X#   if !defined (O_NDELAY)
X#     define O_NDELAY O_NONBLOCK	/* Posix-style non-blocking i/o */
X#   endif
X# else
X#   include <sgtty.h>
X# endif
X#endif
X
X#ifdef HAS_GETPWENT
X#   include <pwd.h>
X#endif
X
X#ifdef I_PTEM
X#include <sys/stream.h>
X#include <sys/ptem.h>
X#endif
X
X#ifdef I_UNISTD
X#include <unistd.h>
X#endif
X#ifdef I_STDLIB
X#include <stdlib.h>
X#else
Xchar	*malloc();
Xchar	*realloc();
Xchar	*getenv();
X#endif
X
X#ifdef I_STRING
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#ifdef I_TIME
X#include <time.h>
X#endif
X#ifdef I_SYS_TIME
X#include <sys/time.h>
X#endif
X
X#define BITSPERBYTE 8
X#define LBUFLEN 1024	/* line buffer length */
X			/* (don't worry, .newsrc lines can exceed this) */
X#define CBUFLEN 512	/* command buffer length */
X#define PUSHSIZE 256
X#define MAXFILENAME 512
X#define LONGKEY 15	/* longest keyword: currently "posting-version" */
X#define FINISHCMD 0177
X
X/* some handy defs */
X
X#define bool char
X#define bool_int int
X#define char_int int
X#ifndef TRUE
X#define TRUE (1)
X#endif
X#ifndef FALSE
X#define FALSE (0)
X#endif
X#define Null(t) ((t)0)
X#define Nullch Null(char*)
X#define Nullfp Null(FILE*)
X
X#define Ctl(ch) (ch & 037)
X
X#define strNE(s1,s2) (strcmp(s1,s2))
X#define strEQ(s1,s2) (!strcmp(s1,s2))
X#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
X#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
X
X/* Things we can figure out ourselves */
X
X#ifdef SIGTSTP
X#   define BERKELEY 	/* include job control signals? */
X#endif
X
X#if defined(FIONREAD) || defined(HAS_RDCHK) || defined(O_NDELAY)
X#   define PENDING
X#endif
X
X#ifdef EUNICE
X#   define LINKART		/* add 1 level of possible indirection */
X#   define UNLINK(victim) while (!unlink(victim))
X#else
X#   define UNLINK(victim) unlink(victim)
X#endif
X
X/* Valid substitutions for strings marked with % comment are:
X *	%a	Current article number
X *	%A	Full name of current article (%P/%c/%a)
X *		(if LINKART defined, is the name of the real article)
X *	%b	Destination of a save command, a mailbox or command
X *	%B	The byte offset to the beginning of the article for saves
X *		with or without the header
X *	%c	Current newsgroup, directory form
X *	%C	Current newsgroup, dot form
X *	%d	%P/%c
X *	%D	Old Distribution: line
X *	%e	Extract program
X *	%E	Extract destination directory
X *	%f	Old From: line or Reply-To: line
X *	%F	Newsgroups to followup to from Newsgroups: and Followup-To:
X *	%h	Name of header file to pass to mail or news poster
X *	%H	Host name (yours)
X *	%i	Old Message-I.D.: line, with <>
X *	%I	Inclusion indicator
X *	%l	News administrator login name
X *	%L	Login name (yours)
X *	%m	The current mode of trn.
X *	%M	Number of articles marked with M
X *	%n	Newsgroups from source article
X *	%N	Full name (yours)
X *	%o	Organization (yours)
X *	%O	Original working directory (where you ran trn from)
X *	%p	Your private news directory (-d switch)
X *	%P	Public news spool directory (NEWSSPOOL)
X *	%r	Last reference (parent article id)
X *	%R	New references list
X *	%s	Subject, with all Re's and (nf)'s stripped off
X *	%S	Subject, with one Re stripped off
X *	%t	New To: line derived from From: and Reply-To (Internet always)
X *	%T	New To: line derived from Path:
X *	%u	Number of unread articles
X *	%U	Number of unread articles disregarding current article
X *	%v	Number of unselected articles disregarding current article
X *	%W	The thread directory root
X *	%x	News library directory, usually /usr/lib/news
X *	%X	Rn library directory, usually %x/rn
X *	%z	Size of current article in bytes.
X *	%Z	Number of selected threads.
X *	%~	Home directory
X *	%.	Directory containing . files
X *	%#	count of articles saved in current command (from 1 to n)
X *	%$	current process number
X *	%{name} Environment variable "name".  %{name-default} form allowed.
X *	%[name]	Header line beginning with "Name: ", without "Name: " 
X *	%"prompt"
X *		Print prompt and insert what is typed.
X *	%`command`
X *		Insert output of command.
X *	%(test_text=pattern?if_text:else_text)
X *		Substitute if_text if test_text matches pattern, otherwise
X *		substitute else_text.  Use != for negated match.
X *		% substitutions are done on test_text, if_text, and else_text.
X *		(Note: %() only works if CONDSUB defined.)
X *	%digit	Substitute the text matched by the nth bracket in the last
X *		pattern that had brackets.  %0 matches the last bracket
X *		matched, in case you had alternatives.
X *
X *	Put ^ in the middle to capitalize the first letter: %^C = Rec.humor
X *	Put _ in the middle to capitalize last component: %_c = net/Jokes
X *	Put \ in the middle to quote regexp and % characters in the result
X *	Put > in the middle to return the address portion of a name.
X *	Put ) in the middle to return the comment portion of a name.
X *	Put :FMT in the middle to format the result: %:-30.30t
X *
X *	~ interpretation in filename expansion happens after % expansion, so
X *	you could put ~%{NEWSLOGNAME-news} and it will expand correctly.
X */
X
X/* *** System Dependent Stuff *** */
X
X/* NOTE: many of these are defined in the config.h file */
X
X/* name of organization */
X#ifndef ORGNAME
X#   define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota"
X#endif
X
X#ifndef MBOXCHAR
X#   define MBOXCHAR 'F'	/* how to recognize a mailbox by 1st char */
X#endif
X
X#ifndef ROOTID
X#   define ROOTID 0        /* uid of superuser */
X#endif
X
X#ifdef NORMSIG
X#   define sigset signal
X#   define sigignore(sig) signal(sig,SIG_IGN)
X#endif
X
X#ifndef LOGDIRFIELD
X#   define LOGDIRFIELD 6		/* Which field (origin 1) is the */
X					/* login directory in /etc/passwd? */
X					/* (If it is not kept in passwd, */
X					/* but getpwnam() returns it, */
X					/* define the symbol HAS_GETPWENT) */
X#endif
X#ifndef GCOSFIELD
X#   define GCOSFIELD 5
X#endif
X
X#ifndef NEGCHAR
X#   define NEGCHAR '!'
X#endif
X
X/* Space conservation section */
X
X/* To save D space, cut down size of NGMAX and  VARYSIZE. */
X#define NGMAX 100	/* number of newsgroups allowed on command line */
X			/* undefine ONLY symbol to disable "only" feature */
X#define VARYSIZE 256	/* this makes a block 1024 bytes long in DECville */
X			/* (used by virtual array routines) */
X
X/* Undefine any of the following features to save both I and D space */
X/* In general, earlier ones are easier to get along without */
X#define CUSTOMLINES	/* include code for HIDELINE and PAGESTOP */
X#define WORDERASE	/* enable ^W to erase a word */
X#define MAILCALL	/* check periodically for mail */
X#define CLEAREOL	/* use clear to end-of-line instead of clear screen */
X#define NOFIREWORKS	/* keep whole screen from flashing on certain */
X			/* terminals such as older Televideos */
X#define VERIFY		/* echo the command they just typed */
X#define HASHNG		/* hash newsgroup lines for fast lookup-- */
X			/* linear search used if not defined */
X#define CONDSUB		/* allow %(cond?text:text) */
X#define BACKTICK	/* allow %`command` */
X#define PROMPTTTY	/* allow %"prompt" */
X#define ULSMARTS	/* catch _^H in text and do underlining */
X#define TERMMOD		/* allow terminal type modifier on switches */
X#define BAUDMOD		/* allow baudrate modifier on switches */
X#define GETLOGIN	/* use getlogin() routine as backup to environment */
X			/* variables USER or LOGNAME */
X#define ORGFILE		/* if organization begins with /, look up in file */
X#define TILDENAME	/* allow ~logname expansion */
X#define SETENV		/* allow command line environment variable setting */
X#define MAKEDIR		/* use our makedir() instead of shell script */
X#define MEMHELP		/* keep help messages in memory */
X#define VERBOSE		/* compile in more informative messages */
X#define TERSE		/* compile in shorter messages */
X			/* (Note: both VERBOSE and TERSE can be defined; -t
X			 * sets terse mode.  One or the other MUST be defined.
X			 */
X#define ROTATION	/* enable x, X and ^X commands to work */
X#define DELBOGUS	/* ask if bogus newsgroups should be deleted */
X#define RELOCATE	/* allow newsgroup rearranging */
X#define ESCSUBS		/* escape substitutions in multi-character commands */
X#undef MCHASE		/* unmark xrefed articles on m or M */
X#define MUNGHEADER	/* allow alternate header formatting via */
X			/* environment variable ALTHEADER (not impl) */
X#define ASYNC_PARSE	/* allow parsing headers asyncronously to reading */
X			/* used by MCHASE and MUNGHEADER */
X#define FINDNEWNG	/* check for new newsgroups on startup */
X#define FASTNEW		/* do optimizations on FINDNEWNG for faster startup */
X			/* (this optimization can make occasional mistakes */
X			/* if a group is removed and another group of the */
X			/* same length is added, and if no softpointers are */
X			/* affected by said change.) */
X#define INNERSEARCH	/* search command 'g' with article */
X#define CATCHUP		/* catchup command at newsgroup level */
X#define NGSEARCH	/* newsgroup pattern matching */
X#define ONLY		/* newsgroup restrictions by pattern */
X#define KILLFILES	/* automatic article killer files */
X#define ARTSEARCH	/* pattern searches among articles */
X			/* /, ?, ^N, ^P, k, K */
X#define EDIT_DISTANCE	/* Allow -G to specify a fuzzy 'go' command */
X#undef	VALIDATE_XREF_SITE /* are xrefs possibly invalid? */
X
X/* some dependencies among options */
X
X#ifndef ARTSEARCH
X#   undef KILLFILES
X#   undef INNERSEARCH
X#endif
X
X#ifndef SETUIDGID
X#   define eaccess access
X#endif
X
X#ifdef ONLY				/* idiot lint doesn't grok #if */
X#   define NGSORONLY
X#else
X#   ifdef NGSEARCH
X#	define NGSORONLY
X#   endif
X#endif
X
X#ifdef VERBOSE
X#   ifdef TERSE
X#	define IF(c) if (c)
X#	define ELSE else
X#   else
X#	define IF(c)
X#	define ELSE
X#   endif
X#else /* !VERBOSE */
X#   ifndef TERSE
X#	define TERSE
X#   endif
X#   define IF(c) "IF" outside of VERBOSE???
X#   define ELSE "ELSE" outside of VERBOSE???
X#endif
X
X#ifdef DEBUG
X#   define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}}
X#else
X#   define assert(ex) ;
X#endif
X
X/* If you're strapped for space use the help messages in shell scripts */
X/* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */
X#ifdef MEMHELP  /* undef MEMHELP above to get them all as sh scripts */
X#   undef NGHELP
X#   undef ARTHELP
X#   undef PAGERHELP
X#   undef SUBSHELP
X#else
X#   ifndef NGHELP			/* % and ~ */
X#	define NGHELP "%X/ng.help"
X#   endif
X#   ifndef ARTHELP			/* % and ~ */
X#	define ARTHELP "%X/art.help"
X#   endif
X#   ifndef PAGERHELP		/* % and ~ */
X#	define PAGERHELP "%X/pager.help"
X#   endif
X#   ifndef SUBSHELP		/* % and ~ */
X#	define SUBSHELP "%X/subs.help"
X#   endif
X#endif
X
X#define TCSIZE 512	/* capacity for termcap strings */
X
X#ifdef EDIT_DISTANCE
X#   define MIN_DIST 7	/* Maximum error count for acceptable match */
X#endif
X
X/* Additional ideas:
X *	Make the do_newsgroup() routine a separate process.
X *	Keep .newsrc on disk instead of in memory.
X *	Overlays, if you have them.
X *	Get a bigger machine.
X */
X
X/* End of Space Conservation Section */
X
X/* More System Dependencies */
X
X/* news library */
X#ifndef NEWSLIB		/* ~ and %l only ("~%l" is permissable) */
X#   define NEWSLIB "/usr/lib/news"
X#endif
X
X/* path to private executables */
X#ifndef PRIVLIB		/* ~, %x and %l only */
X#   define PRIVLIB "%x/trn"
X#endif
X
X/* system-wide RNINIT switches */
X#ifndef GLOBINIT
X#   define GLOBINIT "%X/INIT"
X#endif
X
X/* where to find news files */
X#ifndef NEWSSPOOL		/* % and ~ */
X#   define NEWSSPOOL "/usr/spool/news"
X#endif
X
X#ifndef THREAD_DIR
X#   undef LONG_THREAD_NAMES
X#endif
X
X/* default characters to use in the selection menu */
X#ifndef SELECTCHARS
X#   define SELECTCHARS "abdefgijlorstuvwxyz1234567890BCFGHIKVW"
X#endif
X
X/* file containing list of active newsgroups and max article numbers */
X#ifdef USE_NNTP
X#   undef ACTIVE
X#   define ACTIVE "%P/rrnact.%$"
X#else
X# ifndef ACTIVE			/* % and ~ */
X#   define ACTIVE "%x/active"
X# endif
X#endif
X#ifndef DBINIT
X#   define DBINIT "%W/db.init"
X#endif
X
X#ifdef USE_NNTP
X# ifndef ACTIVE_TIMES
X#   define APPEND_UNSUB
X# endif
X#else
X# ifdef USE_XTHREAD
X#   undef USE_XTHREAD
X# endif
X# ifdef USE_XOVER
X#   undef USE_XOVER
X# endif
X#endif
X
X/* location of history file */
X#ifndef ARTFILE			/* % and ~ */
X#    define ARTFILE "%x/history"
X#endif
X
X/* command to setup a new .newsrc */
X#ifndef NEWSETUP		/* % and ~ */
X#   define NEWSETUP "newsetup"
X#endif
X
X/* command to display a list of un-subscribed-to newsgroups */
X#ifndef NEWSGROUPS		/* % and ~ */
X#   define NEWSGROUPS "newsgroups"
X#endif
X
X/* preferred shell for use in doshell routine */
X/*  ksh or sh would be okay here */
X#ifndef PREFSHELL
X#   define PREFSHELL "/bin/csh"
X#endif
X
X/* path to fastest starting shell */
X#ifndef SH
X#   define SH "/bin/sh"
X#endif
X
X/* default unshar'ing program */
X#ifndef UNSHAR
X#   define UNSHAR "/bin/sh"
X#endif
X
X/* path to default editor */
X#ifndef DEFEDITOR
X#   define DEFEDITOR "/usr/ucb/vi"
X#endif
X
X/* location of macro file for trn and rn modes */
X#ifndef TRNMACRO
X#   define TRNMACRO "%./.trnmac"
X#endif
X#ifndef RNMACRO
X#   define RNMACRO "%./.rnmac"
X#endif
X
X/* location of full name */
X#ifndef FULLNAMEFILE
X#   ifndef PASSNAMES
X#	define FULLNAMEFILE "%./.fullname"
X#   endif
X#endif
X
X/* virtual array file name template */
X#ifndef VARYNAME		/* % and ~ */
X#   define VARYNAME "/tmp/rnvary.%$"
X#endif
X
X/* where to compile a new newsgroup list */
X#ifndef RNEWNAME
X#   define RNEWNAME "/tmp/rnew.%$"
X#endif
X
X/* file to pass header to followup article poster */
X#ifndef HEADNAME		/* % and ~ */
X#   define HEADNAME "%./.rnhead"
X/* or alternately #define HEADNAME "/tmp/rnhead.%$" */
X#endif
X
X#ifndef MAKEDIR
X/* shell script to make n-deep subdirectories */
X#   ifndef DIRMAKER		/* % and ~ */
X#	define DIRMAKER "%X/makedir"
X#   endif
X#endif
X
X/* location of newsrc file */
X#ifndef RCNAME		/* % and ~ */
X#   define RCNAME "%./.newsrc"
X#endif
X
X/* temporary newsrc file in case we crash while writing out */
X#ifndef RCTNAME		/* % and ~ */
X#   define RCTNAME "%./.newnewsrc"
X#endif
X
X/* newsrc file at the beginning of this session */
X#ifndef RCBNAME		/* % and ~ */
X#   define RCBNAME "%./.oldnewsrc"
X#endif
X
X/* if existent, contains process number of current or crashed trn */
X#ifndef LOCKNAME		/* % and ~ */
X#   define LOCKNAME "%./.rnlock"
X#endif
X
X/* information from last invocation of trn */
X#ifndef LASTNAME		/* % and ~ */
X#   define LASTNAME "%./.rnlast"
X#endif
X
X/* file with soft pointers into the active file */
X#ifndef SOFTNAME		/* % and ~ */
X#   define SOFTNAME "%./.rnsoft"
X#endif
X
X/* list of article numbers to mark as unread later (see M and Y cmmands) */
X#ifndef RNDELNAME		/* % and ~ */
X#   define RNDELNAME "%./.rndelay"
X#endif
X
X/* a motd-like file for trn */
X#ifndef NEWSNEWSNAME		/* % and ~ */
X#   define NEWSNEWSNAME "%X/newsnews"
X#endif
X
X/* command to send a reply */
X#ifndef MAILPOSTER		/* % and ~ */
X#   define MAILPOSTER "QUOTECHARS=%I Rnmail -h %h"
X#endif
X
X#ifdef INTERNET
X#   ifndef MAILHEADER		/* % */
X#	ifdef CONDSUB
X#	    define MAILHEADER "To: %t\nSubject: Re: %S\n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Newsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
X#	else
X#	    define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
X#	endif
X#   endif
X#else
X#   ifndef MAILHEADER		/* % */
X#	ifdef CONDSUB
X#	    define MAILHEADER "To: %T\nSubject: %(%i=^$?:Re: %S\nNewsgroups: %n\nIn-Reply-To: %i)\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
X#	else
X#	    define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
X#	endif
X#   endif
X#endif
X
X#ifndef YOUSAID			/* % */
X#   define YOUSAID "In article %i you write:"
X#endif
X
X/* command to submit a followup article */
X#ifndef NEWSPOSTER		/* % and ~ */
X#   define NEWSPOSTER "QUOTECHARS=%I Pnews -h %h"
X#endif
X
X#ifndef NEWSHEADER		/* % */
X#   ifdef CONDSUB
X#	define NEWSHEADER "%(%[followup-to]=^$?:X-ORIGINAL-NEWSGROUPS: %n\n)Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Distribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\nCc: \n\n"
X#   else
X#	define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\nCc: \n\n"
X#   endif
X#endif
X
X#ifndef ATTRIBUTION		/* % */
X#   define ATTRIBUTION "In article %i,%?%)f <%>f> wrote:"
X#endif
X
X#ifndef PIPESAVER		/* % */
X#   ifdef CONDSUB
X#	define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b"
X#   else
X#	define PIPESAVER "tail +%Bc %A | %b"
X#   endif
X#endif
X
X#ifndef EXSAVER
X#   define EXSAVER "tail +%Bc %A | %e"
X#endif
X
X#ifdef MIME_SUPPORT
X#  ifndef EXMIMESAVER
X#    define EXMIMESAVER "%e %A"
X#  endif
X#endif
X
X#ifndef NORMSAVER		/* % and ~ */
X#   define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\""
X#endif
X
X#ifndef MBOXSAVER		/* % and ~ */
X#   ifndef ANCIENT_NEWS
X#	define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %t %`LANG= date`\""
X#   else
X#	ifdef CONDSUB
X#	    define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %t %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\""
X					/* header munging with a vengeance */
X#	else
X#	    define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %t %[posted]\""
X#	endif
X#   endif
X#endif
X
X#ifdef MKDIRS
X
X#   ifndef SAVEDIR			/* % and ~ */
X#	define SAVEDIR "%p/%c"
X#   endif
X#   ifndef SAVENAME		/* % */
X#	define SAVENAME "%a"
X#   endif
X
X#else
X
X#   ifndef SAVEDIR			/* % and ~ */
X#	define SAVEDIR "%p"
X#   endif
X#   ifndef SAVENAME		/* % */
X#	define SAVENAME "%^C"
X#   endif
X
X#endif
X
X#ifndef KILLGLOBAL		/* % and ~ */
X#   define KILLGLOBAL "%p/KILL"
X#endif
X
X#ifndef KILLLOCAL		/* % and ~ */
X#   define KILLLOCAL "%p/%c/KILL"
X#endif
X
X/* how to cancel an article */
X#ifndef CANCEL
X#   ifdef BNEWS
X#	define CANCEL "%x/inews -h < %h"
X#   else
X#	define CANCEL "inews -h < %h"
X#   endif
X#endif
X
X/* how to cancel an article, continued */
X#ifndef CANCELHEADER
X#   define CANCELHEADER "Newsgroups: %n\nSubject: cancel\nControl: cancel %i\nDistribution: %D\n\n%i was cancelled from within trn.\n"
X#endif
X
X/* how to supersede an article */
X#ifndef SUPERSEDEHEADER
X#   define SUPERSEDEHEADER "Newsgroups: %n\nSubject: %S\nSummary: %[summary]\nExpires: %[expires]\nReferences: %[references]\nSupersedes: %i\nSender: %[sender]\nFollowup-To: %[followup-to]\nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
X#endif
X
X#ifndef LOCALTIMEFMT
X#   define LOCALTIMEFMT "%a %b %d %X %Z %Y"
X#endif
X
X/* where to find the mail file */
X#ifndef MAILFILE
X#   define MAILFILE "/usr/spool/mail/%L"
X#endif
X
X/* how to open binary format files */
X#ifndef FOPEN_RB
X#   define FOPEN_RB "r"
X#endif
X#ifndef FOPEN_WB
X#   define FOPEN_WB "w"
X#endif
X
X/* what to do with ansi prototypes -- '()' == ignore, 'x' == use */
X#ifndef _
X#   ifdef __STDC__
X#	define _(x) x
X#	ifndef CONST
X#	    define CONST const
X#	endif
X#   else
X#	define _(x) ()
X#	ifndef CONST
X#	    define CONST
X#	endif
X#   endif
X#endif
X
X/* how many characters is a newline in a text file? */
X#ifndef NL_SIZE
X#   define NL_SIZE 1
X#endif
X
X/* some important types */
X
Xtypedef int		NG_NUM;		/* newsgroup number */
Xtypedef long		ART_NUM;	/* article number */
Xtypedef long		ART_UNREAD;	/* could be short to save space */
Xtypedef long		ART_POS;	/* char position in article file */
Xtypedef int		ART_LINE;	/* line position in article file */
Xtypedef long		ACT_POS;	/* char position in active file */
Xtypedef unsigned int	MEM_SIZE;	/* for passing to malloc */
X
X/* some slight-of-hand for compatibility issues */
X
X#ifdef HAS_STRCHR
X# ifndef index
X#   define index strchr
X# endif
X# ifndef rindex
X#   define rindex strrchr
X# endif
X#endif
X#ifdef HAS_MEMCMP
X# ifndef bcmp
X#   define bcmp(s,d,l) memcmp((s),(d),(l))
X# endif
X#endif
X#ifdef HAS_MEMCPY
X# ifndef bcopy
X#   define bcopy(s,d,l) memcpy((d),(s),(l))
X# endif
X#endif
X#ifdef HAS_MEMSET
X# ifndef bzero
X#   define bzero(s,l) memset((s),0,(l))
X# endif
X#endif
X
X/* *** end of the machine dependent stuff *** */
X
X/* GLOBAL THINGS */
X
X/* file statistics area */
X
XEXT struct stat filestat;
X
X/* various things of type char */
X
X#ifdef SUPPLEMENT_STRING_H
Xchar	*index();
Xchar	*rindex();
Xchar	*strcat();
Xchar	*strcpy();
X#endif
X
XEXT char buf[LBUFLEN+1];	/* general purpose line buffer */
XEXT char cmd_buf[CBUFLEN];	/* buffer for formatting system commands */
XEXT char *indstr INIT(">");	/* indent for old article embedded in followup */
X
XEXT char *cwd INIT(Nullch);		/* current working directory */
XEXT char *dfltcmd INIT(Nullch);	/* 1st char is default command */
X
X/* switches */
X
X#ifdef DEBUG
X    EXT int debug INIT(0);				/* -D */
X#   define DEB_COREDUMPSOK 2
X#   define DEB_HEADER 4
X#   define DEB_INTRP 8
X#   define DEB_NNTP 16
X#   define DEB_INNERSRCH 32
X#   define DEB_FILEXP 64 
X#   define DEB_HASH 128
X#   define DEB_XREF_MARKER 256
X#   define DEB_CTLAREA_BITMAP 512
X#   define DEB_SOFT_POINTERS 1024
X#   define DEB_NEWSRC_LINE 2048
X#   define DEB_SEARCH_AHEAD 4096
X#   define DEB_CHECKPOINTING 8192
X#   define DEB_FEED_XREF 16384
X#endif
X
X#ifdef ARTSEARCH
X    EXT int scanon INIT(0);				/* -S */
X#endif
X
XEXT bool use_threads INIT(THREAD_INIT);			/* -x */
XEXT int max_tree_lines INIT(6);
XEXT char select_order[4] INIT("lms");
XEXT int select_on INIT(SELECT_INIT);			/* -X */
XEXT char end_select INIT('Z');
XEXT char page_select INIT('>');
X
XEXT bool dont_filter_control INIT(FALSE);		/* -j */
XEXT bool mbox_always INIT(FALSE);			/* -M */
XEXT bool norm_always INIT(FALSE);			/* -N */
XEXT bool thread_always INIT(FALSE);			/* -a */
XEXT bool auto_arrow_macros INIT(TRUE);			/* -B */
XEXT bool breadth_first INIT(FALSE);			/* -b */
XEXT bool bkgnd_spinner INIT(FALSE);			/* -B */
XEXT bool novice_delays INIT(TRUE);			/* +f */
XEXT int olden_days INIT(FALSE);				/* -o */
XEXT bool auto_select_postings INIT(FALSE);		/* -p */
XEXT bool checkflag INIT(FALSE);				/* -c */
XEXT bool suppress_cn INIT(FALSE);			/* -s */
XEXT int countdown INIT(5);	/* how many lines to list before invoking -s */
XEXT bool muck_up_clear INIT(FALSE);			/* -loco */
XEXT bool erase_screen INIT(FALSE);			/* -e */
XEXT bool can_home INIT(FALSE);
X#ifdef CLEAREOL
XEXT bool can_home_clear INIT(FALSE);		/* fancy -e */
X#endif
XEXT bool findlast INIT(FALSE);			/* -r */
XEXT bool typeahead INIT(FALSE);			/* -T */
X#ifdef EDIT_DISTANCE
XEXT bool fuzzyGet INIT(FALSE);			/* -G */
X#endif
X#ifdef VERBOSE
X#   ifdef TERSE
XEXT bool verbose INIT(TRUE);				/* +t */
X#   endif
X#endif
XEXT bool unbroken_subjects INIT(FALSE);			/* -u */
XEXT bool unsafe_rc_saves INIT(FALSE);			/* -U */
X#ifdef VERIFY
XEXT bool verify INIT(FALSE);				/* -v */
X#endif
XEXT bool quickstart INIT(FALSE);			/* -q */
XEXT time_t actFetchTime					/* -z */
X#ifdef USE_NNTP
X	INIT(5*60);
X#else
X	INIT(0);
X#endif
XEXT bool try_ov						/* -Z */
X#ifdef USE_OV
X	INIT(TRUE);
X#else
X	INIT(FALSE);
X#endif
XEXT bool try_mt
X#ifdef USE_MT
X	INIT(TRUE);
X#else
X	INIT(FALSE);
X#endif
X
X#define NOMARKING 0
X#define STANDOUT 1
X#define UNDERLINE 2
XEXT int marking INIT(NOMARKING);			/* -m */
X
XEXT ART_LINE initlines INIT(0);				/* -i */
XEXT bool initlines_specified INIT(FALSE);
X#ifdef APPEND_UNSUB
XEXT bool append_unsub INIT(1);				/* -I */
X#else
XEXT bool append_unsub INIT(0);
X#endif
X
X/* miscellania */
X
X#ifndef __STDC__
Xint fseek();
Xlong atol(), ftell();
Xextern int errno;
X#endif
X
XEXT bool in_ng INIT(FALSE);		/* current state of trn */
XEXT char mode INIT('i');		/* current state of trn */
X
XEXT FILE *tmpfp INIT(Nullfp);	/* scratch fp used for .rnlock, .rnlast, etc. */
X
XEXT NG_NUM nextrcline INIT(0);	/* 1st unused slot in rcline array */
X				/* startup to avoid checking twice in a row */
X
X/* Factored strings */
X
XEXT char nullstr[1] INIT("");
XEXT char sh[] INIT(SH);
XEXT char defeditor[] INIT(DEFEDITOR);
XEXT char hforhelp[] INIT("Type h for help.\n");
X#ifdef STRICTCR
XEXT char badcr[] INIT("\nUnnecessary CR ignored.\n");
X#endif
XEXT char readerr[] INIT("rn read error");
XEXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n");
XEXT char cantopen[] INIT("Can't open %s\n");
XEXT char cantcreate[] INIT("Can't create %s\n");
XEXT char cantrecreate[] INIT("Can't recreate %s -- restoring older version.\n");
X
X#ifdef VERBOSE
X    EXT char nocd[] INIT("Can't chdir to directory %s\n");
X#else
X    EXT char nocd[] INIT("Can't find %s\n");
X#endif
X
X#ifdef MIME_SUPPORT
XEXT bool mime_article INIT(FALSE);
X#endif
X
X#ifdef NOLINEBUF
X#define FLUSH ,fflush(stdout)
X#else
X#define FLUSH
X#endif
X
X#ifdef lint
X#undef FLUSH
X#define FLUSH
X#undef putchar
X#define putchar(c)
X#endif
X
X#define advise(str) fputs(str,stdout)
X#define fatal_error(str) fputs(str,stderr), finalize(1)
END_OF_FILE
if test 25539 -ne `wc -c <'common.h'`; then
    echo shar: \"'common.h'\" unpacked with wrong size!
fi
# end of 'common.h'
fi
if test -f 'parsedate.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'parsedate.y'\"
else
echo shar: Extracting \"'parsedate.y'\" \(21261 characters\)
sed "s/^X//" >'parsedate.y' <<'END_OF_FILE'
X%{
X/* $Revision: 1.12 $
X**
X**  Originally written by Steven M. Bellovin <smb@research.att.com> while
X**  at the University of North Carolina at Chapel Hill.  Later tweaked by
X**  a couple of people on Usenet.  Completely overhauled by Rich $alz
X**  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
X**  Further revised (removed obsolete constructs and cleaned up timezone
X**  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
X**  helped in September, 1992.
X**
X**  This grammar has six shift/reduce conflicts.
X**
X**  This code is in the public domain and has no copyright.
X*/
X/* SUPPRESS 530 *//* Empty body for statement */
X/* SUPPRESS 593 on yyerrlab *//* Label was not used */
X/* SUPPRESS 593 on yynewstate *//* Label was not used */
X/* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include "config.h"
X#include <time.h>
X
X#define yyparse		date_parse
X#define yylex		date_lex
X#define yyerror		date_error
X
X
X    /* See the LeapYears table in Convert. */
X#define EPOCH		1970
X#define END_OF_TIME	2038
X    /* Constants for general time calculations. */
X#define DST_OFFSET	1
X#define SECSPERDAY	(24L * 60L * 60L)
X    /* Readability for TABLE stuff. */
X#define HOUR(x)		(x * 60)
X
X#define LPAREN		'('
X#define RPAREN		')'
X#define IS7BIT(x)	((unsigned int)(x) < 0200)
X
X#define SIZEOF(array)	((int)(sizeof array / sizeof array[0]))
X#define ENDOF(array)	(&array[SIZEOF(array)])
X
X
X/*
X**  An entry in the lexical lookup table.
X*/
Xtypedef struct _TABLE {
X    char	*name;
X    int		type;
X    time_t	value;
X} TABLE;
X
X/*
X**  Daylight-savings mode:  on, off, or not yet known.
X*/
Xtypedef enum _DSTMODE {
X    DSTon, DSToff, DSTmaybe
X} DSTMODE;
X
X/*
X**  Meridian:  am, pm, or 24-hour style.
X*/
Xtypedef enum _MERIDIAN {
X    MERam, MERpm, MER24
X} MERIDIAN;
X
X
X/*
X**  Global variables.  We could get rid of most of them by using a yacc
X**  union, but this is more efficient.  (This routine predates the
X**  yacc %union construct.)
X*/
Xstatic char	*yyInput;
Xstatic DSTMODE	yyDSTmode;
Xstatic int	yyHaveDate;
Xstatic int	yyHaveRel;
Xstatic int	yyHaveTime;
Xstatic time_t	yyTimezone;
Xstatic time_t	yyDay;
Xstatic time_t	yyHour;
Xstatic time_t	yyMinutes;
Xstatic time_t	yyMonth;
Xstatic time_t	yySeconds;
Xstatic time_t	yyYear;
Xstatic MERIDIAN	yyMeridian;
Xstatic time_t	yyRelMonth;
Xstatic time_t	yyRelSeconds;
X
X
Xextern struct tm	*localtime();
X
Xstatic void		date_error();
X%}
X
X%union {
X    time_t		Number;
X    enum _MERIDIAN	Meridian;
X}
X
X%token	tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
X%token	tUNUMBER tZONE
X
X%type	<Number>	tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
X%type	<Number>	tSNUMBER tUNUMBER tZONE numzone zone
X%type	<Meridian>	tMERIDIAN o_merid
X
X%%
X
Xspec	: /* NULL */
X	| spec item
X	;
X
Xitem	: time {
X	    yyHaveTime++;
X#ifdef lint
X	    /* I am compulsive about lint natterings... */
X	    if (yyHaveTime == -1) {
X		YYERROR;
X	    }
X#endif /* lint */
X	}
X	| time zone {
X	    yyHaveTime++;
X	    yyTimezone = $2;
X	}
X	| date {
X	    yyHaveDate++;
X	}
X	| rel {
X	    yyHaveRel = 1;
X	}
X	;
X
Xtime	: tUNUMBER o_merid {
X	    if ($1 < 100) {
X		yyHour = $1;
X		yyMinutes = 0;
X	    }
X	    else {
X		yyHour = $1 / 100;
X		yyMinutes = $1 % 100;
X	    }
X	    yySeconds = 0;
X	    yyMeridian = $2;
X	}
X	| tUNUMBER ':' tUNUMBER o_merid {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yySeconds = 0;
X	    yyMeridian = $4;
X	}
X	| tUNUMBER ':' tUNUMBER numzone {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yyTimezone = $4;
X	    yyMeridian = MER24;
X	    yyDSTmode = DSToff;
X	}
X	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yySeconds = $5;
X	    yyMeridian = $6;
X	}
X	| tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yySeconds = $5;
X	    yyTimezone = $6;
X	    yyMeridian = MER24;
X	    yyDSTmode = DSToff;
X	}
X	;
X
Xzone	: tZONE {
X	    $$ = $1;
X	    yyDSTmode = DSToff;
X	}
X	| tDAYZONE {
X	    $$ = $1;
X	    yyDSTmode = DSTon;
X	}
X	| tZONE numzone {
X	    /* Only allow "GMT+300" and "GMT-0800" */
X	    if ($1 != 0) {
X		YYABORT;
X	    }
X	    $$ = $2;
X	    yyDSTmode = DSToff;
X	}
X	| numzone {
X	    $$ = $1;
X	    yyDSTmode = DSToff;
X	}
X	;
X
Xnumzone	: tSNUMBER {
X	    int		i;
X
X	    /* Unix and GMT and numeric timezones -- a little confusing. */
X	    if ($1 < 0) {
X		/* Don't work with negative modulus. */
X		$1 = -$1;
X		if ($1 > 9999 || (i = $1 % 100) >= 60) {
X		    YYABORT;
X		}
X		$$ = ($1 / 100) * 60 + i;
X	    }
X	    else {
X		if ($1 > 9999 || (i = $1 % 100) >= 60) {
X		    YYABORT;
X		}
X		$$ = -(($1 / 100) * 60 + i);
X	    }
X	}
X	;
X
Xdate	: tUNUMBER '/' tUNUMBER {
X	    yyMonth = $1;
X	    yyDay = $3;
X	}
X	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
X	    if ($1 > 100) {
X		yyYear = $1;
X		yyMonth = $3;
X		yyDay = $5;
X	    }
X	    else {
X		yyMonth = $1;
X		yyDay = $3;
X		yyYear = $5;
X	    }
X	}
X	| tMONTH tUNUMBER {
X	    yyMonth = $1;
X	    yyDay = $2;
X	}
X	| tMONTH tUNUMBER ',' tUNUMBER {
X	    yyMonth = $1;
X	    yyDay = $2;
X	    yyYear = $4;
X	}
X	| tUNUMBER tMONTH {
X	    yyDay = $1;
X	    yyMonth = $2;
X	}
X	| tUNUMBER tMONTH tUNUMBER {
X	    yyDay = $1;
X	    yyMonth = $2;
X	    yyYear = $3;
X	}
X	| tDAY ',' tUNUMBER tMONTH tUNUMBER {
X	    yyDay = $3;
X	    yyMonth = $4;
X	    yyYear = $5;
X	}
X	;
X
Xrel	: tSNUMBER tSEC_UNIT {
X	    yyRelSeconds += $1 * $2;
X	}
X	| tUNUMBER tSEC_UNIT {
X	    yyRelSeconds += $1 * $2;
X	}
X	| tSNUMBER tMONTH_UNIT {
X	    yyRelMonth += $1 * $2;
X	}
X	| tUNUMBER tMONTH_UNIT {
X	    yyRelMonth += $1 * $2;
X	}
X	;
X
Xo_merid	: /* NULL */ {
X	    $$ = MER24;
X	}
X	| tMERIDIAN {
X	    $$ = $1;
X	}
X	;
X
X%%
X
X/* Month and day table. */
Xstatic TABLE	MonthDayTable[] = {
X    { "january",	tMONTH,  1 },
X    { "february",	tMONTH,  2 },
X    { "march",		tMONTH,  3 },
X    { "april",		tMONTH,  4 },
X    { "may",		tMONTH,  5 },
X    { "june",		tMONTH,  6 },
X    { "july",		tMONTH,  7 },
X    { "august",		tMONTH,  8 },
X    { "september",	tMONTH,  9 },
X    { "october",	tMONTH, 10 },
X    { "november",	tMONTH, 11 },
X    { "december",	tMONTH, 12 },
X	/* The value of the day isn't used... */
X    { "sunday",		tDAY, 0 },
X    { "monday",		tDAY, 0 },
X    { "tuesday",	tDAY, 0 },
X    { "wednesday",	tDAY, 0 },
X    { "thursday",	tDAY, 0 },
X    { "friday",		tDAY, 0 },
X    { "saturday",	tDAY, 0 },
X};
X
X/* Time units table. */
Xstatic TABLE	UnitsTable[] = {
X    { "year",		tMONTH_UNIT,	12 },
X    { "month",		tMONTH_UNIT,	1 },
X    { "week",		tSEC_UNIT,	7L * 24 * 60 * 60 },
X    { "day",		tSEC_UNIT,	1L * 24 * 60 * 60 },
X    { "hour",		tSEC_UNIT,	60 * 60 },
X    { "minute",		tSEC_UNIT,	60 },
X    { "min",		tSEC_UNIT,	60 },
X    { "second",		tSEC_UNIT,	1 },
X    { "sec",		tSEC_UNIT,	1 },
X};
X
X/* Timezone table. */
Xstatic TABLE	TimezoneTable[] = {
X    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
X    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
X    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
X    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
X    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
X    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
X    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
X    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
X    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
X    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
X    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
X    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
X    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
X    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
X    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
X    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
X    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
X    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
X    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
X    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
X    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
X    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
X    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
X    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
X    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
X    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
X    { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
X    { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
X    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
X    { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
X    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
X    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
X    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
X    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
X    { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
X    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
X    { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
X    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
X    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
X    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
X    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
X    { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
X    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
X    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
X    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
X    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
X    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
X    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
X
X    /* For completeness we include the following entries. */
X#if 0
X
X    /* Duplicate names.  Either they conflict with a zone listed above
X     * (which is either more likely to be seen or just been in circulation
X     * longer), or they conflict with another zone in this section and
X     * we could not reasonably choose one over the other. */
X    { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
X    { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
X    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
X    { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
X    { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
X    { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
X    { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
X    { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
X    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
X    { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
X    { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
X    { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
X    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
X    { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
X    { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
X    { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
X    { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
X    { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
X    { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
X    { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
X    { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
X    { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
X    { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
X
X    /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
X    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
X    { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
X    { "at",	tZONE,     HOUR( 2) },	/* Azores */
X    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
X    { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
X    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
X    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
X    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
X    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
X    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
X    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
X    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
X    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
X    { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
X    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
X    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
X    { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
X    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
X    { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
X    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
X    { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
X    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
X    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
X    { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
X    { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
X    { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
X    { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
X#endif /* 0 */
X};
X
X
X/* ARGSUSED */
Xstatic void
Xdate_error(s)
X    char	*s;
X{
X    /* NOTREACHED */
X}
X
X
Xstatic time_t
XToSeconds(Hours, Minutes, Seconds, Meridian)
X    time_t	Hours;
X    time_t	Minutes;
X    time_t	Seconds;
X    MERIDIAN	Meridian;
X{
X    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
X	return -1;
X    if (Meridian == MER24) {
X	if (Hours < 0 || Hours > 23)
X	    return -1;
X    }
X    else {
X	if (Hours < 1 || Hours > 12)
X	    return -1;
X	if (Hours == 12)
X	    Hours = 0;
X	if (Meridian == MERpm)
X	    Hours += 12;
X    }
X    return (Hours * 60L + Minutes) * 60L + Seconds;
X}
X
X
Xstatic time_t
XConvert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst)
X    time_t	Month;
X    time_t	Day;
X    time_t	Year;
X    time_t	Hours;
X    time_t	Minutes;
X    time_t	Seconds;
X    MERIDIAN	Meridian;
X    DSTMODE	dst;
X{
X    static int	DaysNormal[13] = {
X	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X    };
X    static int	DaysLeap[13] = {
X	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X    };
X    static int	LeapYears[] = {
X	1972, 1976, 1980, 1984, 1988, 1992, 1996,
X	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
X    };
X    register int	*yp;
X    register int	*mp;
X    register time_t	Julian;
X    register int	i;
X    time_t		tod;
X
X    if (Year < 0)
X	Year = -Year;
X    if (Year < 100)
X	Year += 1900;
X    if (Year < EPOCH)
X	Year += 100;
X    for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
X	if (Year == *yp) {
X	    mp = DaysLeap;
X	    break;
X	}
X    if (Year < EPOCH || Year > END_OF_TIME
X     || Month < 1 || Month > 12
X     /* NOSTRICT *//* conversion from long may lose accuracy */
X     || Day < 1 || Day > mp[(int)Month])
X	return -1;
X
X    Julian = Day - 1 + (Year - EPOCH) * 365;
X    for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
X	if (Year <= *yp)
X	    break;
X    for (i = 1; i < Month; i++)
X	Julian += *++mp;
X    Julian *= SECSPERDAY;
X    Julian += yyTimezone * 60L;
X    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
X	return -1;
X    Julian += tod;
X    tod = Julian;
X    if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
X	Julian -= DST_OFFSET * 60L * 60L;
X    return Julian;
X}
X
X
Xstatic time_t
XDSTcorrect(Start, Future)
X    time_t	Start;
X    time_t	Future;
X{
X    time_t	StartDay;
X    time_t	FutureDay;
X
X    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
X    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
X    return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L;
X}
X
X
Xstatic time_t
XRelativeMonth(Start, RelMonth)
X    time_t	Start;
X    time_t	RelMonth;
X{
X    struct tm	*tm;
X    time_t	Month;
X    time_t	Year;
X
X    tm = localtime(&Start);
X    Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
X    Year = Month / 12;
X    Month = Month % 12 + 1;
X    return DSTcorrect(Start,
X	    Convert(Month, (time_t)tm->tm_mday, Year,
X		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
X		MER24, DSTmaybe));
X}
X
X
Xstatic int
XLookupWord(buff, length)
X    char		*buff;
X    register int	length;
X{
X    register char	*p;
X    register char	*q;
X    register TABLE	*tp;
X    register int	c;
X
X    p = buff;
X    c = p[0];
X
X    /* See if we have an abbreviation for a month. */
X    if (length == 3 || (length == 4 && p[3] == '.'))
X	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
X	    q = tp->name;
X	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X	}
X    else
X	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
X	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X
X    /* Try for a timezone. */
X    for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
X	if (c == tp->name[0] && p[1] == tp->name[1]
X	 && strcmp(p, tp->name) == 0) {
X	    yylval.Number = tp->value;
X	    return tp->type;
X	}
X
X    /* Try the units table. */
X    for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
X	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
X	    yylval.Number = tp->value;
X	    return tp->type;
X	}
X
X    /* Strip off any plural and try the units table again. */
X    if (--length > 0 && p[length] == 's') {
X	p[length] = '\0';
X	for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
X	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
X		p[length] = 's';
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X	p[length] = 's';
X    }
X    length++;
X
X    /* Drop out any periods. */
X    for (p = buff, q = (char*)buff; *q; q++)
X	if (*q != '.')
X	    *p++ = *q;
X    *p = '\0';
X
X    /* Try the meridians. */
X    if (buff[1] == 'm' && buff[2] == '\0') {
X	if (buff[0] == 'a') {
X	    yylval.Meridian = MERam;
X	    return tMERIDIAN;
X	}
X	if (buff[0] == 'p') {
X	    yylval.Meridian = MERpm;
X	    return tMERIDIAN;
X	}
X    }
X
X    /* If we saw any periods, try the timezones again. */
X    if (p - buff != length) {
X	c = buff[0];
X	for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
X	    if (c == tp->name[0] && p[1] == tp->name[1]
X	    && strcmp(p, tp->name) == 0) {
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X    }
X
X    /* Unknown word -- assume GMT timezone. */
X    yylval.Number = 0;
X    return tZONE;
X}
X
X
Xint
Xdate_lex()
X{
X    register char	c;
X    register char	*p;
X    char		buff[20];
X    register int	sign;
X    register int	i;
X    register int	nesting;
X
X    for ( ; ; ) {
X	/* Get first character after the whitespace. */
X	for ( ; ; ) {
X	    while (isspace(*yyInput))
X		yyInput++;
X	    c = *yyInput;
X
X	    /* Ignore RFC 822 comments, typically time zone names. */
X	    if (c != LPAREN)
X		break;
X	    for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
X		if (c == LPAREN)
X		    nesting++;
X		else if (!IS7BIT(c) || c == '\0' || c == '\r'
X		     || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
X		    /* Lexical error: bad comment. */
X		    return '?';
X	    yyInput++;
X	}
X
X	/* A number? */
X	if (isdigit(c) || c == '-' || c == '+') {
X	    if (c == '-' || c == '+') {
X		sign = c == '-' ? -1 : 1;
X		yyInput++;
X		if (!isdigit(*yyInput))
X		    /* Skip the plus or minus sign. */
X		    continue;
X	    }
X	    else
X		sign = 0;
X	    for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); )
X		i = 10 * i + c - '0';
X	    yyInput--;
X	    yylval.Number = sign < 0 ? -i : i;
X	    return sign ? tSNUMBER : tUNUMBER;
X	}
X
X	/* A word? */
X	if (isalpha(c)) {
X	    for (p = buff; (c = *yyInput++) == '.' || isalpha(c); )
X		if (p < &buff[sizeof buff - 1])
X		    *p++ = isupper(c) ? tolower(c) : c;
X	    *p = '\0';
X	    yyInput--;
X	    return LookupWord(buff, p - buff);
X	}
X
X	return *yyInput++;
X    }
X}
X
X
Xtime_t
Xparsedate(p)
X    char		*p;
X{
X    extern int		date_parse();
X    time_t		Start;
X
X    yyInput = p;
X
X    yyYear = 0;
X    yyMonth = 0;
X    yyDay = 0;
X    yyTimezone = 0;
X    yyDSTmode = DSTmaybe;
X    yyHour = 0;
X    yyMinutes = 0;
X    yySeconds = 0;
X    yyMeridian = MER24;
X    yyRelSeconds = 0;
X    yyRelMonth = 0;
X    yyHaveDate = 0;
X    yyHaveRel = 0;
X    yyHaveTime = 0;
X
X    if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
X	return -1;
X
X    if (yyHaveDate || yyHaveTime) {
X	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
X		    yyMeridian, yyDSTmode);
X	if (Start < 0)
X	    return -1;
X    }
X    else
X	return -1;
X
X    Start += yyRelSeconds;
X    if (yyRelMonth)
X	Start += RelativeMonth(Start, yyRelMonth);
X
X    /* Have to do *something* with a legitimate -1 so it's distinguishable
X     * from the error return value.  (Alternately could set errno on error.) */
X    return Start == -1 ? 0 : Start;
X}
X
X
X#ifdef TEST
X
X#if YYDEBUG
Xextern int	yydebug;
X#endif /* YYDEBUG */
X
X/* ARGSUSED */
Xint
Xmain(ac, av)
X    int		ac;
X    char	*av[];
X{
X    char	buff[128];
X    time_t	d;
X
X#if YYDEBUG
X    yydebug = 1;
X#endif /* YYDEBUG */
X
X    (void)printf("Enter date, or blank line to exit.\n\t> ");
X    for ( ; ; ) {
X	(void)printf("\t> ");
X	(void)fflush(stdout);
X	if (gets(buff) == NULL || buff[0] == '\n')
X	    break;
X#if YYDEBUG
X	if (strcmp(buff, "yydebug") == 0) {
X	    yydebug = !yydebug;
X	    printf("yydebug = %s\n", yydebug ? "on" : "off");
X	    continue;
X	}
X#endif /* YYDEBUG */
X	d = parsedate(buff, (TIMEINFO *)NULL);
X	if (d == -1)
X	    (void)printf("Bad format - couldn't convert.\n");
X	else
X	    (void)printf("%s", ctime(&d));
X    }
X
X    exit(0);
X    /* NOTREACHED */
X}
X#endif /* TEST */
END_OF_FILE
if test 21261 -ne `wc -c <'parsedate.y'`; then
    echo shar: \"'parsedate.y'\" unpacked with wrong size!
fi
# end of 'parsedate.y'
fi
if test -f 'rcstuff.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rcstuff.c'\"
else
echo shar: Extracting \"'rcstuff.c'\" \(25592 characters\)
sed "s/^X//" >'rcstuff.c' <<'END_OF_FILE'
X/* $Id: rcstuff.c,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction of this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "cache.h"
X#include "bits.h"
X#include "ngdata.h"
X#include "term.h"
X#include "final.h"
X#include "trn.h"
X#include "intrp.h"
X#include "only.h"
X#include "rcln.h"
X#include "nntp.h"
X#include "autosub.h"
X#include "hash.h"
X#include "INTERN.h"
X#include "rcstuff.h"
X
Xchar *rcname INIT(Nullch);		/* path name of .newsrc file */
Xchar *rctname INIT(Nullch);		/* path name of temp .newsrc file */
Xchar *rcbname INIT(Nullch);		/* path name of backup .newsrc file */
Xchar *softname INIT(Nullch);		/* path name of .rnsoft file */
XFILE *rcfp INIT(Nullfp);		/* .newsrc file pointer */
X
Xstatic void grow_rc_arrays _((int));
Xstatic void parse_rcline _((NG_NUM));
X
X#ifdef HASHNG
Xstatic HASHTABLE *rc_hash;
Xstatic int rcline_cmp _((char*,int,HASHDATUM));
Xstatic void del_rc_line _((HASHDATUM*,int));
Xstatic void ins_rc_line _((HASHDATUM*,int));
X#endif
X
Xbool
Xrcstuff_init()
X{
X    register NG_NUM newng;
X    register int i;
X    register bool foundany = FALSE;
X    char *some_buf;
X    long length;
X#ifdef USE_NNTP
X    char *cp;
X#endif
X
X    /* make filenames */
X
X#ifdef USE_NNTP
X    if (cp = getenv("NEWSRC"))
X	rcname = savestr(filexp(cp));
X    else
X	rcname = savestr(filexp(RCNAME));
X#else
X    rcname = savestr(filexp(RCNAME));
X#endif
X
X    rctname = savestr(filexp(RCTNAME));
X    rcbname = savestr(filexp(RCBNAME));
X    softname = savestr(filexp(SOFTNAME));
X    
X    /* make sure the .newsrc file exists */
X
X    newsrc_check();
X
X    /* open .rnsoft file containing soft ptrs to active file */
X
X    tmpfp = fopen(softname,"r");
X    if (tmpfp == Nullfp)
X	writesoft = TRUE;
X
X    /* allocate memory for rc file globals */
X    grow_rc_arrays(1500);
X
X    /* read in the .newsrc file */
X
X    for (nextrcline = 0;
X	(some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch;
X	nextrcline++)			/* for each line in .newsrc */
X    {
X	char tmpbuf[10];
X
X	newng = nextrcline;		/* get it into a register */
X	length = len_last_line_got;	/* side effect of get_a_line */
X	if (length <= 1) {		/* only a newline??? */
X	    nextrcline--;		/* compensate for loop increment */
X	    continue;
X	}
X	if (newng >= maxrcline)		/* check for overflow */
X	    grow_rc_arrays(maxrcline + 500);
X	if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch)
X	    softptr[newng] = atol(tmpbuf);
X	else
X	    softptr[newng] = 0;
X	if (some_buf[--length] == '\n')
X	    some_buf[length] = '\0';	/* wipe out newline */
X	if (checkflag)			/* no extra mallocs for -c */
X	    rcline[newng] = some_buf;
X	else if (some_buf == buf)
X	    rcline[newng] = savestr(some_buf);  /* make semipermanent copy */
X	else {
X	    /*NOSTRICT*/
X#ifndef lint
X	    some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
X#endif
X	    rcline[newng] = some_buf;
X	}
X	if (*some_buf == ' ' || *some_buf == '\t'
X	 || strnEQ(some_buf,"options",7)) {	/* non-useful line? */
X	    toread[newng] = TR_JUNK;
X	    rcchar[newng] = ' ';
X	    rcnums[newng] = 0;
X	    continue;
X	}
X	parse_rcline(newng);
X	if (rcchar[newng] == NEGCHAR) {
X	    toread[newng] = TR_UNSUB;
X	    continue;
X	}
X
X	/* now find out how much there is to read */
X
X	if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
X	    toread[newng] = TR_NONE;	/* no need to calculate now */
X	else
X	    set_toread(newng);
X#ifdef VERBOSE
X	if (!checkflag && softmisses == 1) {
X	    softmisses++;		/* lie a little */
X	    fputs("(Revising soft pointers -- be patient.)\n",stdout) FLUSH;
X	}
X#endif
X	if (toread[newng] > TR_NONE) {	/* anything unread? */
X	    if (!foundany) {
X		starthere = newng;
X		foundany = TRUE;	/* remember that fact*/
X	    }
X	    if (suppress_cn) {		/* if no listing desired */
X		if (checkflag) {	/* if that is all they wanted */
X		    finalize(1);	/* then bomb out */
X		}
X	    }
X	    else {
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("Unread news in %-40s %5ld article%s\n",
X			rcline[newng],(long)toread[newng],
X			toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("%s: %ld article%s\n",
X			rcline[newng],(long)toread[newng],
X			toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
X#endif
X		if (int_count) {
X		    countdown = 1;
X		    int_count = 0;
X		}
X		if (countdown) {
X		    if (!--countdown) {
X			fputs("etc.\n",stdout) FLUSH;
X			if (checkflag)
X			    finalize(1);
X			suppress_cn = TRUE;
X		    }
X		}
X	    }
X	}
X    }
X    fclose(rcfp);			/* close .newsrc */
X    if (tmpfp != Nullfp)
X	fclose(tmpfp);			/* close .rnsoft */
X    if (checkflag)			/* were we just checking? */
X	finalize(foundany);		/* tell them what we found */
X    if (paranoid)
X	cleanup_rc();
X
X#ifdef HASHNG
X    rc_hash = hashcreate((int)nextrcline+50, rcline_cmp);
X    for (i = 0; i < nextrcline; i++)
X	if (toread[i] >= TR_UNSUB)
X	    sethash(i);
X#endif
X
X    return foundany;
X}
X
Xstatic void
Xparse_rcline(ngnum)
XNG_NUM ngnum;
X{
X    char *s;
X    int len;
X
X    for (s=rcline[ngnum]; *s && *s!=':' && *s!=NEGCHAR && !isspace(*s); s++) ;
X    len = s - rcline[ngnum];
X    if ((!*s || isspace(*s)) && !checkflag) {
X#ifndef lint
X	rcline[ngnum] = saferealloc(rcline[ngnum],(MEM_SIZE)len + 3);
X#endif
X	s = rcline[ngnum] + len;
X	strcpy(s, ": ");
X    }
X    if (*s == ':' && s[1] && s[2] == '0') {
X	rcchar[ngnum] = '0';
X	s[2] = '1';
X    } else
X	rcchar[ngnum] = *s;	/* salt away the : or ! */
X    rcnums[ngnum] = (char)len + 1;
X				/* remember where the numbers are */
X    *s = '\0';			/* null terminate newsgroup name */
X}
X
Xvoid
Xabandon_ng(ngnum)
XNG_NUM ngnum;
X{
X    char *some_buf = Nullch;
X
X    /* open .oldnewsrc and try to find the prior value for the group. */
X    if ((rcfp = fopen(rcbname, "r")) != Nullfp) {
X	int length = rcnums[ngnum] - 1;
X
X	while ((some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch) {
X	    if (len_last_line_got <= 0)
X		continue;
X	    some_buf[len_last_line_got-1] = '\0';	/* wipe out newline */
X	    if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR)
X	     && strnEQ(rcline[ngnum], some_buf, length)) {
X		break;
X	    }
X	    if (some_buf != buf)
X		free(some_buf);
X	}
X	fclose(rcfp);
X    } else if (errno != ENOENT) {
X	printf("Unable to open %s.\n", rcbname) FLUSH;
X	return;
X    }
X    if (some_buf == Nullch) {
X	some_buf = rcline[ngnum] + rcnums[ngnum];
X	if (*some_buf == ' ')
X	    some_buf++;
X	*some_buf = '\0';
X	abs1st[ngnum] = 0;	/* force group to be re-calculated */
X    }
X    else {
X	free(rcline[ngnum]);
X	if (some_buf == buf) {
X	    rcline[ngnum] = savestr(some_buf);
X	}
X	else {
X	    /*NOSTRICT*/
X#ifndef lint
X	    some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got));
X#endif /* lint */
X	    rcline[ngnum] = some_buf;
X	}
X    }
X    parse_rcline(ngnum);
X    if (rcchar[ngnum] == NEGCHAR)
X	rcchar[ngnum] = ':';
X    set_toread(ngnum);
X}
X
X/* try to find or add an explicitly specified newsgroup */
X/* returns TRUE if found or added, FALSE if not. */
X/* assumes that we are chdir'ed to NEWSSPOOL */
X
Xbool
Xget_ng(what,flags)
Xchar *what;
Xint flags;
X{
X    char *ntoforget;
X    char promptbuf[128];
X    int autosub;
X
X#ifdef VERBOSE
X    IF(verbose)
X	ntoforget = "Type n to forget about this newsgroup.\n";
X    ELSE
X#endif
X#ifdef TERSE
X	ntoforget = "n to forget it.\n";
X#endif
X    if (index(what,'/')) {
X	dingaling();
X	printf("\nBad newsgroup name.\n") FLUSH;
X      check_fuzzy_match:
X	if (fuzzyGet && (flags & GNG_FUZZY)) {
X	    if (find_close_match())
X		what = ngname;
X	    else
X		return FALSE;
X	} else
X	    return FALSE;
X    }
X    set_ngname(what);
X    ng = find_ng(ngname);
X    if (ng == nextrcline) {		/* not in .newsrc? */
X	if (ng >= maxrcline)		/* check for overflow */
X	    grow_rc_arrays(maxrcline + 25);
X#ifdef USE_NNTP
X	softptr[ng] = 0;
X	if (!nntp_group(ngname,ng))
X#else /* !USE_NNTP */
X	if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0)
X#endif /* !USE_NNTP */
X	{
X	    dingaling();
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\nNo %s!\n",ngname) FLUSH;
X#endif
X	    if (novice_delays)
X		sleep(2);
X	    goto check_fuzzy_match;
X	}
X	if (mode != 'i' || !(autosub = auto_subscribe(ngname)))
X	    autosub = addnewbydefault;
X	if (autosub) {
X	    if (append_unsub) {
X		printf("(Adding %s to end of your .newsrc %ssubscribed)\n",
X		       ngname, (autosub == ADDNEW_SUB) ? "" : "un") FLUSH;
X		ng = add_newsgroup(ngname, autosub);
X	    } else {
X		if (autosub == ADDNEW_SUB) {
X		    printf("(Subscribing to %s)\n", ngname) FLUSH;
X		    ng = add_newsgroup(ngname, autosub);
X		} else {
X		    printf("(Ignoring %s)\n", ngname) FLUSH;
X		    return FALSE;
X		}
X	    }
X	    flags &= ~GNG_RELOC;
X	} else {
X#ifdef VERBOSE
X	IF(verbose)
X	    sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe? [ynYN] ",ngname);
X	ELSE
X#endif
X#ifdef TERSE
X	    sprintf(promptbuf,"\nSubscribe %s? [ynYN] ",ngname);
X#endif
Xreask_add:
X	in_char(promptbuf,'A');
X	setdef(buf,"y");
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("Type y or SP to subscribe to %s.\n\
XType Y to subscribe to this and all remaining new groups.\n\
XType N to leave all remaining new groups unsubscribed.\n", ngname)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y or SP to subscribe, Y to subscribe all new groups, N to unsubscribe all\n",stdout) FLUSH;
X#endif
X	    fputs(ntoforget,stdout) FLUSH;
X	    goto reask_add;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    if (append_unsub) {
X		ng = add_newsgroup(ngname, NEGCHAR);
X	    }
X	    return FALSE;
X	}
X	else if (*buf == 'y') {
X	    ng = add_newsgroup(ngname, ':');
X	    flags |= GNG_RELOC;
X	}
X	else if (*buf == 'Y') {
X	    addnewbydefault = ADDNEW_SUB;
X	    if (append_unsub)
X		printf("(Adding %s to end of your .newsrc subscribed)\n",
X		       ngname) FLUSH;
X	    else
X		printf("(Subscribing to %s)\n", ngname) FLUSH;
X	    ng = add_newsgroup(ngname, ':');
X	    flags &= ~GNG_RELOC;
X	}
X	else if (*buf == 'N') {
X	    addnewbydefault = ADDNEW_UNSUB;
X	    if (append_unsub) {
X		printf("(Adding %s to end of your .newsrc unsubscribed)\n",
X		       ngname) FLUSH;
X		ng = add_newsgroup(ngname, NEGCHAR);
X		flags &= ~GNG_RELOC;
X	    } else {
X		printf("(Ignoring %s)\n", ngname) FLUSH;
X		return FALSE;
X	    }
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_add;
X	}
X      }
X    }
X    else if (mode == 'i')		/* adding new groups during init? */
X	return FALSE;
X    else if (rcchar[ng] == NEGCHAR) {	/* unsubscribed? */
X#ifdef VERBOSE
X	IF(verbose)
X	    sprintf(promptbuf,
X"\nNewsgroup %s is unsubscribed -- resubscribe? [yn] ",ngname)
X  FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    sprintf(promptbuf,"\nResubscribe %s? [yn] ",ngname)
X	      FLUSH;
X#endif
Xreask_unsub:
X	in_char(promptbuf,'R');
X	setdef(buf,"y");
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y or SP to resubscribe.\n",stdout) FLUSH;
X#endif
X	    fputs(ntoforget,stdout) FLUSH;
X	    goto reask_unsub;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    return FALSE;
X	}
X	else if (*buf == 'y') {
X	    register char *cp = rcline[ng] + rcnums[ng];
X	    rcchar[ng] = (*cp && cp[1] == '0' ? '0' : ':');
X	    flags &= ~GNG_RELOC;
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_unsub;
X	}
X    }
X
X    /* now calculate how many unread articles in newsgroup */
X
X    set_toread(ng);
X#ifdef RELOCATE
X    if (flags & GNG_RELOC) {
X	ng = relocate_newsgroup(ng,-1);
X	if (ng < 0)
X	    return FALSE;
X    }
X#endif
X    return toread[ng] >= TR_NONE;
X}
X
X/* add a newsgroup to the .newsrc file (eventually) */
X
XNG_NUM
Xadd_newsgroup(ngn, c)
Xchar *ngn;
Xchar_int c;
X{
X    register NG_NUM newng = nextrcline++;
X					/* increment max rcline index */
X
X    if (newng >= maxrcline)		/* check for overflow */
X	grow_rc_arrays(maxrcline + 25);
X
X    rcnums[newng] = strlen(ngn) + 1;
X    rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 2));
X    strcpy(rcline[newng],ngn);		/* and copy over the name */
X    strcpy(rcline[newng]+rcnums[newng], " ");
X    rcchar[newng] = c;			/* subscribe or unsubscribe */
X    toread[newng] = TR_NONE;	/* just for prettiness */
X#ifdef HASHNG
X    sethash(newng);			/* so we can find it again */
X#endif
X    return newng;
X}
X
X#ifdef RELOCATE
XNG_NUM
Xrelocate_newsgroup(ngx,newng)
XNG_NUM ngx;
XNG_NUM newng;
X{
X    char *dflt = (ngx!=current_ng ? "$^.Lq" : "$^Lq");
X    char *tmprcline;
X    ART_UNREAD tmptoread;
X    char tmprcchar;
X    char tmprcnums;
X    ACT_POS tmpsoftptr;
X    register NG_NUM i;
X    ART_NUM tmpngmax;
X    ART_NUM tmpabs1st;
X    
X    starthere = 0;                      /* Disable this optimization */
X    writesoft = TRUE;			/* Update soft pointer file */
X    if (ngx < nextrcline-1) {
X#ifdef HASHNG
X	if (rc_hash)
X	    hashwalk(rc_hash, del_rc_line, ngx);
X#endif
X	tmprcline = rcline[ngx];
X	tmptoread = toread[ngx];
X	tmprcchar = rcchar[ngx];
X	tmprcnums = rcnums[ngx];
X	tmpsoftptr = softptr[ngx];
X	tmpngmax = ngmax[ngx];
X	tmpabs1st = abs1st[ngx];
X	for (i=ngx+1; i<nextrcline; i++) {
X	    rcline[i-1] = rcline[i];
X	    toread[i-1] = toread[i];
X	    rcchar[i-1] = rcchar[i];
X	    rcnums[i-1] = rcnums[i];
X	    softptr[i-1] = softptr[i];
X	    ngmax[i-1] = ngmax[i];
X	    abs1st[i-1] = abs1st[i];
X	}
X	rcline[nextrcline-1] = tmprcline;
X	toread[nextrcline-1] = tmptoread;
X	rcchar[nextrcline-1] = tmprcchar;
X	rcnums[nextrcline-1] = tmprcnums;
X	softptr[nextrcline-1] = tmpsoftptr;
X	ngmax[nextrcline-1] = tmpngmax;
X	abs1st[nextrcline-1] = tmpabs1st;
X    }
X    if (current_ng > ngx)
X	current_ng--;
X    if (newng < 0) {
X      reask_reloc:
X	unflush_output();		/* disable any ^O in effect */
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\nPut newsgroup where? [%s] ", dflt);
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\nPut where? [%s] ", dflt);
X#endif
X	fflush(stdout);
X      reinp_reloc:
X	eat_typeahead();
X	getcmd(buf);
X	if (errno || *buf == '\f') {
X			    /* if return from stop signal */
X	    goto reask_reloc;	/* give them a prompt again */
X	}
X	setdef(buf,dflt);
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose) {
X		printf("\n\n\
XType ^ to put the newsgroup first (position 0).\n\
XType $ to put the newsgroup last (position %d).\n", nextrcline-1);
X		printf("\
XType . to put it before the current newsgroup (position %d).\n", current_ng);
X		printf("\
XType -newsgroup name to put it before that newsgroup.\n\
XType +newsgroup name to put it after that newsgroup.\n\
XType a number between 0 and %d to put it at that position.\n", nextrcline-1);
X		printf("\
XType L for a listing of newsgroups and their positions.\n\
XType q to abort the current action.\n") FLUSH;
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		printf("\n\n\
X^ to put newsgroup first (pos 0).\n\
X$ to put last (pos %d).\n", nextrcline-1);
X		printf("\
X. to put before current newsgroup (pos %d).\n", current_ng);
X		printf("\
X-newsgroup to put before newsgroup.\n\
X+newsgroup to put after.\n\
Xnumber in 0-%d to put at that pos.\n", nextrcline-1);
X		printf("\
XL for list of .newsrc.\n\
Xq to abort\n") FLUSH;
X	    }
X#endif
X	    goto reask_reloc;
X	}
X	else if (*buf == 'q')
X	    return -1;
X	else if (*buf == 'L') {
X	    putchar('\n') FLUSH;
X	    list_newsgroups();
X	    goto reask_reloc;
X	}
X	else if (isdigit(*buf)) {
X	    if (!finish_command(TRUE))	/* get rest of command */
X		goto reinp_reloc;
X	    newng = atol(buf);
X	    if (newng < 0)
X		newng = 0;
X	    if (newng >= nextrcline)
X		return nextrcline-1;
X	}
X	else if (*buf == '^') {
X	    putchar('\n') FLUSH;
X	    newng = 0;
X	}
X	else if (*buf == '$') {
X	    newng = nextrcline-1;
X	}
X	else if (*buf == '.') {
X	    putchar('\n') FLUSH;
X	    newng = current_ng;
X	}
X	else if (*buf == '-' || *buf == '+') {
X	    if (!finish_command(TRUE))	/* get rest of command */
X		goto reinp_reloc;
X	    newng = find_ng(buf+1);
X	    if (newng == nextrcline) {
X		fputs("Not found.",stdout) FLUSH;
X		goto reask_reloc;
X	    }
X	    if (*buf == '+')
X		newng++;
X	}
X	else {
X	    printf("\n%s",hforhelp) FLUSH;
X	    settle_down();
X	    goto reask_reloc;
X	}
X    }
X    if (newng < nextrcline-1) {
X#ifdef HASHNG
X	if (rc_hash)
X	    hashwalk(rc_hash, ins_rc_line, newng);
X#endif
X	tmprcline = rcline[nextrcline-1];
X	tmptoread = toread[nextrcline-1];
X	tmprcchar = rcchar[nextrcline-1];
X	tmprcnums = rcnums[nextrcline-1];
X	tmpsoftptr = softptr[nextrcline-1];
X	tmpngmax = ngmax[nextrcline-1];
X	tmpabs1st = abs1st[nextrcline-1];
X	for (i=nextrcline-2; i>=newng; i--) {
X	    rcline[i+1] = rcline[i];
X	    toread[i+1] = toread[i];
X	    rcchar[i+1] = rcchar[i];
X	    rcnums[i+1] = rcnums[i];
X	    softptr[i+1] = softptr[i];
X	    ngmax[i+1] = ngmax[i];
X	    abs1st[i+1] = abs1st[i];
X	}
X	rcline[newng] = tmprcline;
X	toread[newng] = tmptoread;
X	rcchar[newng] = tmprcchar;
X	rcnums[newng] = tmprcnums;
X	softptr[newng] = tmpsoftptr;
X	ngmax[newng] = tmpngmax;
X	abs1st[newng] = tmpabs1st;
X    }
X    if (current_ng >= newng)
X	current_ng++;
X    return newng;
X}
X#endif
X
X/* List out the newsrc with annotations */
X
Xvoid
Xlist_newsgroups()
X{
X    register NG_NUM i;
X    char tmpbuf[2048];
X    static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"};
X    int cmd;
X
X    page_init();
X    print_lines("\
X  #  Status  Newsgroup\n\
X",STANDOUT);
X    for (i=0; i<nextrcline && !int_count; i++) {
X	if (toread[i] >= 0)
X	    set_toread(i);
X	*(rcline[i] + rcnums[i] - 1) = RCCHAR(rcchar[i]);
X	if (toread[i] > 0)
X	    sprintf(tmpbuf,"%3d %6ld   ",i,(long)toread[i]);
X	else
X	    sprintf(tmpbuf,"%3d %7s  ",i,status[-toread[i]]);
X	safecpy(tmpbuf+13,rcline[i],2034);
X	*(rcline[i] + rcnums[i] - 1) = '\0';
X	if (cmd = print_lines(tmpbuf,NOMARKING)) {
X	    if (cmd > 0)
X		pushchar(cmd);
X	    break;
X	}
X    }
X    int_count = 0;
X}
X
X/* find a newsgroup in .newsrc */
X
XNG_NUM
Xfind_ng(ngnam)
Xchar *ngnam;
X{
X#ifdef HASHNG
X    HASHDATUM data;
X
X    assert(rc_hash != 0);
X    data = hashfetch(rc_hash, ngnam, strlen(ngnam));
X    if (!data.dat_ptr)
X	return nextrcline;		/* = notfound */
X    return data.dat_len;
X
X#else /* just do linear search */
X    register NG_NUM ngnum;
X
X    for (ngnum = 0; ngnum < nextrcline; ngnum++) {
X	if (strEQ(rcline[ngnum],ngnam))
X	    break;
X    }
X    return ngnum;
X#endif
X}
X
Xvoid
Xcleanup_rc()
X{
X    register NG_NUM ngx;
X    register NG_NUM bogosity = 0;
X
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("Checking out your .newsrc -- hang on a second...\n",stdout)
X	  FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("Checking .newsrc -- hang on...\n",stdout) FLUSH;
X#endif
X    for (ngx = 0; ngx < nextrcline; ngx++) {
X	if (toread[ngx] >= TR_UNSUB) {
X	    set_toread(ngx);		/* this may reset newsgroup */
X					/* or declare it bogus */
X	}
X	if (toread[ngx] == TR_BOGUS)
X	    bogosity++;
X    }
X    for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--)
X	bogosity--;			/* discount already moved ones */
X    if (nextrcline > 5 && bogosity > nextrcline / 2) {
X	fputs(
X"It looks like the active file is messed up.  Contact your news administrator,\n\
X",stdout);
X	fputs(
X"leave the \"bogus\" groups alone, and they may come back to normal.  Maybe.\n\
X",stdout) FLUSH;
X    }
X#ifdef RELOCATE
X    else if (bogosity) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("Moving bogus newsgroups to the end of your .newsrc.\n",
X		stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("Moving boguses to the end.\n",stdout) FLUSH;
X#endif
X	for (; ngx >= 0; ngx--) {
X	    if (toread[ngx] == TR_BOGUS)
X		relocate_newsgroup(ngx,nextrcline-1);
X	}
X#ifdef DELBOGUS
Xreask_bogus:
X	in_char("Delete bogus newsgroups? [ny] ", 'D');
X	setdef(buf,"n");
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\
XType y to delete bogus newsgroups.\n\
XType n or SP to leave them at the end in case they return.\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y to delete, n to keep\n",stdout) FLUSH;
X#endif
X	    goto reask_bogus;
X	}
X	else if (*buf == 'n' || *buf == 'q')
X	    ;
X	else if (*buf == 'y') {
X	    while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0)
X		--nextrcline;		/* real tough, huh? */
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_bogus;
X	}
X#endif
X    }
X#else
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("You should edit bogus newsgroups out of your .newsrc.\n",
X	    stdout) FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("Edit boguses from .newsrc.\n",stdout) FLUSH;
X#endif
X#endif
X    paranoid = FALSE;
X}
X
X#ifdef HASHNG
X/* make an entry in the hash table for the current newsgroup */
X
Xvoid
Xsethash(thisng)
XNG_NUM thisng;
X{
X    HASHDATUM data;
X
X    data.dat_ptr = nullstr;
X    data.dat_len = thisng;
X    hashstore(rc_hash, rcline[thisng], rcnums[thisng]-1, data);
X}
X
Xstatic int
Xrcline_cmp(key, keylen, data)
Xchar *key;
Xint keylen;
XHASHDATUM data;
X{
X    /* We already know that the lengths are equal, just compare the strings */
X    return bcmp(key, rcline[data.dat_len], keylen);
X}
X
Xstatic void
Xdel_rc_line(data, ngnum)
XHASHDATUM *data;
Xint ngnum;
X{
X    if (data->dat_len == ngnum)
X	data->dat_len = nextrcline-1;
X    else if (data->dat_len > ngnum)
X	data->dat_len--;
X}
X
Xstatic void
Xins_rc_line(data, ngnum)
XHASHDATUM *data;
Xint ngnum;
X{
X    if (data->dat_len == nextrcline-1)
X	data->dat_len = ngnum;
X    else if (data->dat_len >= ngnum)
X	data->dat_len++;
X}
X#endif
X
Xvoid
Xnewsrc_check()
X{
X    rcfp = fopen(rcname,"r");		/* open it */
X    if (rcfp == Nullfp) {			/* not there? */
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\nTrying to set up a .newsrc file -- running newsetup...\n\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("Setting up .newsrc...\n",stdout) FLUSH;
X#endif
X	if (doshell(sh,filexp(NEWSETUP)) ||
X	    (rcfp = fopen(rcname,"r")) == Nullfp) {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nCan't create a .newsrc -- you must do it yourself.\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("(Fatal)\n",stdout) FLUSH;
X#endif
X	    finalize(1);
X	}
X	get_anything();
X	putchar('\n') FLUSH;
X    }
X    else {
X	UNLINK(rcbname);		/* unlink backup file name */
X	safelink(rcname,rcbname);	/* and backup current name */
X    }
X}
X
X/* checkpoint the .newsrc */
X
Xvoid
Xcheckpoint_rc()
X{
X#ifdef DEBUG
X    if (debug & DEB_CHECKPOINTING) {
X	fputs("(ckpt)",stdout);
X	fflush(stdout);
X    }
X#endif
X    if (doing_ng)
X	bits_to_rc();			/* do not restore M articles */
X    if (rc_changed)
X	write_rc();
X#ifdef DEBUG
X    if (debug & DEB_CHECKPOINTING) {
X	fputs("(done)",stdout);
X	fflush(stdout);
X    }
X#endif
X}
X
X/* write out the (presumably) revised .newsrc */
X
Xvoid
Xwrite_rc()
X{
X    register NG_NUM tmpng;
X    register char *delim;
X
X    rcfp = fopen(rctname, "w");		/* open .newnewsrc */
X    if (rcfp == Nullfp) {
X	printf(cantrecreate,".newsrc") FLUSH;
X	finalize(1);
X    }
X    if (stat(rcname,&filestat)>=0) {	/* preserve permissions */
X	chmod(rctname,filestat.st_mode&0666);
X	chown(rctname,filestat.st_uid,filestat.st_gid);	/* if possible */
X    }
X
X    /* write out each line*/
X
X    for (tmpng = 0; tmpng < nextrcline; tmpng++) {
X	if (rcnums[tmpng]) {
X	    delim = rcline[tmpng] + rcnums[tmpng] - 1;
X	    *delim = RCCHAR(rcchar[tmpng]);
X	    if (rcchar[tmpng] == '0' && delim[2] == '1')
X		delim[2] = '0';
X	}
X	else
X	    delim = Nullch;
X#ifdef DEBUG
X	if (debug & DEB_NEWSRC_LINE)
X	    printf("%s\n",rcline[tmpng]) FLUSH;
X#endif
X	if (fprintf(rcfp,"%s\n",rcline[tmpng]) < 0) {
X	write_error:
X	    printf(cantrecreate,".newsrc") FLUSH;
X	    fclose(rcfp);		/* close .newnewsrc */
X	    UNLINK(rctname);
X	    finalize(1);
X	}
X	if (delim) {
X	    *delim = '\0';		/* might still need this line */
X	    if (rcchar[tmpng] == '0' && delim[2] == '0')
X		delim[2] = '1';
X	}
X    }
X    fflush(rcfp);
X    if (ferror(rcfp))
X	goto write_error;
X
X    fclose(rcfp);			/* close .newnewsrc */
X    UNLINK(rcname);
X#ifdef HAS_RENAME
X    rename(rctname,rcname);
X#else
X    safelink(rctname,rcname);
X    UNLINK(rctname);
X#endif
X
X    if (writesoft) {
X	tmpfp = fopen(filexp(softname), "w");	/* open .rnsoft */
X	if (tmpfp == Nullfp) {
X	    printf(cantcreate,filexp(softname)) FLUSH;
X	    return;
X	}
X	for (tmpng = 0; tmpng < nextrcline; tmpng++) {
X	    fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]);
X	}
X	fclose(tmpfp);
X    }
X}
X
Xvoid
Xget_old_rc()
X{
X    UNLINK(rctname);
X#ifdef HAS_RENAME
X    rename(rcname,rctname);
X    rename(rcbname,rcname);
X#else
X    safelink(rcname,rctname);
X    UNLINK(rcname);
X    safelink(rcbname,rcname);
X    UNLINK(rcbname);
X#endif
X}
X
Xstatic void
Xgrow_rc_arrays(newsize)
Xint newsize;
X{
X    abs1st = (ART_NUM*)saferealloc((char*)abs1st,
X		(MEM_SIZE)newsize * sizeof (ART_NUM));
X    ngmax = (ART_NUM*)saferealloc((char*)ngmax,
X		(MEM_SIZE)newsize * sizeof (ART_NUM));
X    rcline = (char**)saferealloc((char*)rcline,
X		(MEM_SIZE)newsize * sizeof (char*));
X    toread = (ART_UNREAD*)saferealloc((char*)toread,
X		(MEM_SIZE)newsize * sizeof(ART_UNREAD));
X    rcchar = (char *) saferealloc(rcchar,
X		(MEM_SIZE)newsize * sizeof (char));
X    rcnums = (char*)saferealloc(rcnums,
X		(MEM_SIZE)newsize * sizeof (char));
X    softptr = (ACT_POS*)saferealloc((char*)softptr,
X		(MEM_SIZE)newsize * sizeof (ACT_POS));
X
X    bzero((char*)(abs1st+maxrcline), (newsize-maxrcline) * sizeof (ART_NUM));
X    bzero((char*)(ngmax+maxrcline), (newsize-maxrcline) * sizeof (ART_NUM));
X    maxrcline = newsize;
X
X    return;
X}
END_OF_FILE
if test 25592 -ne `wc -c <'rcstuff.c'`; then
    echo shar: \"'rcstuff.c'\" unpacked with wrong size!
fi
# end of 'rcstuff.c'
fi
if test -f 'rt-select.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rt-select.c'\"
else
echo shar: Extracting \"'rt-select.c'\" \(23985 characters\)
sed "s/^X//" >'rt-select.c' <<'END_OF_FILE'
X/* $Id: rt-select.c,v 3.0 1992/12/14 00:14:12 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "trn.h"
X#include "term.h"
X#include "final.h"
X#include "util.h"
X#include "help.h"
X#include "cache.h"
X#include "bits.h"
X#include "artsrch.h"
X#include "ng.h"
X#include "ngdata.h"
X#include "ngstuff.h"
X#include "kfile.h"
X#include "rthread.h"
X#include "rt-page.h"
X#include "rt-util.h"
X#include "INTERN.h"
X#include "rt-select.h"
X
X/* When display mode is 'l', each author gets a separate line; when 's', no
X** authors are displayed.
X*/
Xchar *display_mode = select_order;
Xchar sel_disp_char[] = { ' ', '+', '-', '*' };
X
Xstatic char sel_ret;
Xstatic bool empty_ok;
Xstatic bool disp_status_line;
Xstatic bool clean_screen;
X
X/* Display a menu of threads/subjects/articles for the user to choose from.
X** If "cmd" is '+' we display all the unread items and allow the user to mark
X** them as selected and perform various commands upon them.  If "cmd" is 'U'
X** the list consists of previously-read items for the user to mark as unread.
X*/
Xchar
Xdo_selector(cmd)
Xchar_int cmd;
X{
X    register int j;
X    int got_dash;
X    int ch, action;
X    char page_char, end_char;
X    char promptbuf[80];
X    char oldmode = mode;
X    char *in_select;
X
X    mode = 't';
X    art = lastart+1;
X    sel_rereading = (cmd == 'U');
X    clear_on_stop = TRUE;
X    empty_ok = FALSE;
X
X    set_sel_mode(cmd);
X
X    if (!cache_range(sel_rereading? absfirst : firstart, lastart)) {
X	sel_ret = '+';
X	goto sel_exit;
X    }
X
X  start_selector:
X    /* Setup for selecting articles to read or set unread */
X    if (sel_rereading) {
X	page_char = '>';
X	end_char = 'Z';
X	sel_page_app = Null(ARTICLE**);
X	sel_page_sp = Nullsubj;
X	sel_mask = AF_DELSEL;
X    } else {
X	page_char = page_select;
X	end_char = end_select;
X	if (curr_artp) {
X	    sel_last_ap = curr_artp;
X	    sel_last_sp = curr_artp->subj;
X	}
X	sel_mask = AF_SEL;
X    }
X    selected_only = TRUE;
X    count_subjects(cmd ? CS_UNSEL_STORE : CS_NORM);
X
X    /* If nothing to display, we're done. */
X    if (!article_count && !empty_ok) {
X	empty_complaint();
X	sel_ret = '+';
X	goto sel_exit;
X    }
X    init_pages();
X    sel_item_index = 0;
X    *promptbuf = '\0';
X    disp_status_line = FALSE;
X    if (added_articles > 0) {
X	register long i = added_articles, j;
X	register ARTICLE *ap = article_ptr(lastart - i + 1);
X	for (j = 0; j < added_articles; j++, ap++) {
X	    if (ap->flags & AF_READ)
X		i--;
X	}
X	if (i == added_articles)
X	    sprintf(promptbuf, "** %ld new article%s arrived **  ",
X		(long)added_articles, added_articles == 1? nullstr : "s");
X	else
X	    sprintf(promptbuf, "** %ld of %ld new articles unread **  ",
X		i, (long)added_articles);
X	disp_status_line = TRUE;
X    }
X    added_articles = 0;
X    if (cmd && selected_count) {
X	sprintf(promptbuf+strlen(promptbuf), "%ld article%s selected.",
X		(long)selected_count, selected_count == 1? " is" : "s are");
X	disp_status_line = TRUE;
X    }
X    cmd = 0;
Xdisplay_selector:
X    /* Present a page of items to the user */
X    display_page();
X
X    /* Check if there is really anything left to display. */
X    if (!sel_item_cnt && !empty_ok) { /*TODO: this may not be needed anymore */
X	empty_complaint();
X	sel_ret = '+';
X	goto sel_exit;
X    }
X    empty_ok = FALSE;
X
X    if (sel_item_index >= sel_item_cnt)
X	sel_item_index = 0;
X    if (disp_status_line) {
X	printf("\n%s", promptbuf);
X	if (can_home) {
X	    carriage_return();
X	    goto_line(sel_last_line+1, sel_last_line);
X	} else
X	    putchar('\n');
X    }
Xreask_selector:
X    /* Prompt the user */
X#ifdef MAILCALL
X    setmail(FALSE);
X#endif
X    if (sel_at_end)
X	sprintf(cmd_buf, "%s [%c%c] --",
X		(!sel_prior_arts? "All" : "Bot"), end_char, page_char);
X    else
X	sprintf(cmd_buf, "%s%ld%% [%c%c] --",
X		(!sel_prior_arts? "Top " : nullstr),
X		(long)((sel_prior_arts+sel_page_arts)*100 / sel_total_arts),
X		page_char, end_char);
X    sprintf(promptbuf, "%s-- %s %s (%s%s order) -- %s", mailcall,
X	    sel_exclusive? "SELECTED" : "Select", sel_mode_string,
X	    sel_direction<0? "reverse " : nullstr, sel_sort_string, cmd_buf);
X#ifdef CLEAREOL
X    if (erase_screen && can_home_clear)
X	clear_rest();
X#endif
X    standout();
X    fputs(promptbuf, stdout);
X    un_standout();
X    if (can_home)
X	carriage_return();
X    sel_line = sel_last_line;
Xposition_selector:
X    got_dash = 0;
X    if (can_home)
X	goto_line(sel_line, sel_items[sel_item_index].line);
X    sel_line = sel_items[sel_item_index].line;
Xreinp_selector:
X    /* Grab some commands from the user */
X    fflush(stdout);
X    eat_typeahead();
X    spin_char = sel_chars[sel_item_index];
X    cache_until_key();
X    spin_char = ' ';
X#ifdef CONDSUB
X    getcmd(buf);
X    ch = *buf;
X#else
X    getcmd(cmd_buf);	/* If no conditionals, don't allow macros */ 
X    buf[0] = ch = *cmd_buf;
X    buf[1] = FINISHCMD;
X#endif
X    if (errno)
X	ch = Ctl('l');
X    if (disp_status_line) {
X	if (can_home) {
X	    goto_line(sel_line, sel_last_line+1);
X	    erase_eol();
X	    sel_line = sel_last_line+1;
X	}
X	disp_status_line = FALSE;
X    }
X    if (ch == '-') {
X	got_dash = 1;
X	if (!can_home)
X	    putchar('-'), fflush(stdout);
X	goto reinp_selector;
X    }
X    if (ch == ' ') {
X	if (sel_at_end)
X	    ch = end_char;
X	else
X	    ch = page_char;
X    }
X    in_select = index(sel_chars, ch);
X    if (in_select) {
X	j = in_select - sel_chars;
X	if (j >= sel_item_cnt) {
X	    dingaling();
X	    goto position_selector;
X	} else if (got_dash)
X	    ;
X	else if (sel_items[j].sel == 1)
X	    action = (sel_rereading ? 'k' : '-');
X	else
X	    action = '+';
X    } else if (ch == '*' && sel_mode == SM_ARTICLE) {
X	register ARTICLE *ap = (ARTICLE*)sel_items[sel_item_index].ptr;
X	if (sel_items[sel_item_index].sel)
X	    deselect_subject(ap->subj);
X	else
X	    select_subject(ap->subj, 0);
X	update_page();
X	goto position_selector;
X    } else if (ch == 'y' || ch == '.' || ch == '*') {
X	j = sel_item_index;
X	if (sel_items[j].sel == 1)
X	    action = (sel_rereading ? 'k' : '-');
X	else
X	    action = '+';
X    } else if (ch == 'k' || ch == 'j' || ch == ',') {
X	j = sel_item_index;
X	action = 'k';
X    } else if (ch == 'm' || ch == '\\') {
X	j = sel_item_index;
X	action = '-';
X    } else if (ch == 'M') {
X	j = sel_item_index;
X	action = 'M';
X    } else if (ch == '@') {
X	sel_item_index = 0;
X	j = sel_item_cnt-1;
X	got_dash = 1;
X	action = '@';
X    } else if (ch == '[' || ch == 'p') {
X	if (--sel_item_index < 0)
X	    sel_item_index = sel_item_cnt ? sel_item_cnt-1 : 0;
X	goto position_selector;
X    } else if (ch == ']' || ch == 'n') {
X	if (++sel_item_index >= sel_item_cnt)
X	    sel_item_index = 0;
X	goto position_selector;
X    } else {
X	sel_ret = ch;
X	switch (sel_command(ch)) {
X	case DS_POS:
X	    if (!clean_screen)
X		goto display_selector;
X	    goto position_selector;
X	case DS_ASK:
X	    if (!clean_screen)
X		goto display_selector;
X	    goto reask_selector;
X	case DS_DISPLAY:
X	ds_display:
X	    if (disp_status_line)
X		strcpy(promptbuf, buf);
X	    goto display_selector;
X	case DS_UPDATE:
X	    if (!clean_screen)
X		goto ds_display;
X	    if (disp_status_line) {
X		printf("\n%s",buf);
X		if (can_home) {
X		    carriage_return();
X		    up_line();
X		    erase_eol();
X		}
X	    }
X	    update_page();
X	    if (can_home) {
X		goto_line(sel_line, sel_last_line);
X		sel_line = sel_last_line;
X	    }
X	    goto reask_selector;
X	case DS_RESTART:
X	    goto start_selector;
X	case DS_QUIT:
X	    sel_cleanup();
X	    if (!output_chase_phrase)
X		putchar('\n') FLUSH;
X	    goto sel_exit;
X	case DS_STATUS:
X	    disp_status_line = TRUE;
X	    if (!clean_screen) {
X		strcpy(promptbuf, buf);
X		goto display_selector;
X	    }
X	    if (can_home) {
X		goto_line(sel_line, sel_last_line+1);
X		sel_line = sel_last_line+1;
X	    } else
X		putchar('\n');
X
X	    fputs(buf, stdout);
X
X	    if (can_home)
X		carriage_return();
X	    else
X		putchar('\n');
X	    goto position_selector;
X	}
X    }
X
X    /* The user manipulated one of the letters -- handle it. */
X    if (!got_dash)
X	sel_item_index = j;
X    else {
X	if (j < sel_item_index) {
X	    ch = sel_item_index-1;
X	    sel_item_index = j;
X	    j = ch;
X	}
X    }
X    if (++j == sel_item_cnt)
X	j = 0;
X    do {
X	register int sel = sel_items[sel_item_index].sel;
X	register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr;
X	if (can_home) {
X	    goto_line(sel_line, sel_items[sel_item_index].line);
X	    sel_line = sel_items[sel_item_index].line;
X	}
X	if (action == '@') {
X	    if (sel == 2)
X		ch = (sel_rereading ? '+' : ' ');
X	    else if (sel_rereading)
X		ch = 'k';
X	    else if (sel == 1)
X		ch = '-';
X	    else
X		ch = '+';
X	} else
X	    ch = action;
X	switch (ch) {
X	case '+':
X	    if (sel_mode == SM_THREAD)
X		select_thread(sp->thread, 0);
X	    else if (sel_mode == SM_SUBJECT)
X		select_subject(sp, 0);
X	    else
X		select_article((ARTICLE*)sp, 0);
X	    output_sel(1);
X	    break;
X	case '-':  case 'k':  case 'M':
X	   {
X	    bool sel_reread_save = sel_rereading;
X	    if (ch == 'M') {
X		if (sel_mode == SM_ARTICLE)
X		    delay_unmark((ARTICLE*)sp);
X		else {
X		    register ARTICLE *ap;
X		    if (sel_mode == SM_THREAD) {
X			for (ap = first_art(sp); ap; ap = next_art(ap))
X			    if (!(ap->flags & AF_READ) ^ sel_rereading)
X				delay_unmark(ap);
X		    } else {
X			for (ap = sp->articles; ap; ap = ap->subj_next)
X			    if (!(ap->flags & AF_READ) ^ sel_rereading)
X				delay_unmark(ap);
X		    }
X		}
X	    }
X	    if (ch == '-')
X		sel_rereading = FALSE;
X	    else
X		sel_rereading = TRUE;
X	    if (sel_mode == SM_THREAD)
X		deselect_thread(sp->thread);
X	    else if (sel_mode == SM_SUBJECT)
X		deselect_subject(sp);
X	    else
X		deselect_article((ARTICLE*)sp);
X	    sel_rereading = sel_reread_save;
X	    output_sel(ch == '-'? 0 : 2);
X	    break;
X	   }
X	}
X	fflush(stdout);
X	if (++sel_item_index == sel_item_cnt)
X	    sel_item_index = 0;
X	if (can_home)
X	    carriage_return();
X    } while (sel_item_index != j);
X    goto position_selector;
X
Xsel_exit:
X    if (sel_rereading) {
X	sel_rereading = 0;
X	sel_mask = AF_SEL;
X    }
X    if (sel_mode != SM_ARTICLE || sel_sort == SS_GROUPS
X     || sel_sort == SS_SUBJECT) {
X	if (artptr_list) {
X	    free((char*)artptr_list);
X	    artptr_list = sel_page_app = Null(ARTICLE**);
X	    sort_subjects();
X	}
X	artptr = Null(ARTICLE**);
X#ifdef ARTSEARCH
X	if (!ThreadedGroup)
X	    srchahead = -1;
X#endif
X    }
X#ifdef ARTSEARCH
X    else
X	srchahead = 0;
X#endif
X/*    selected_only = (selected_count || !article_count);*/
X    selected_only = (selected_count != 0);
X    if (sel_ret != '#')
X	count_subjects(sel_ret == '+'? CS_RESELECT : CS_UNSELECT);
X    clear_on_stop = FALSE;
X    mode = oldmode;
X    if (sel_ret == '+') {
X	art = curr_art;
X	artp = curr_artp;
X    } else
X	top_article();
X    return sel_ret;
X}
X
Xstatic void
Xsel_cleanup()
X{
X    if (sel_rereading) {
X	/* Turn selections into unread selected articles.  Let
X	** count_subjects() fix the counts after we're through.
X	*/
X	register SUBJECT *sp;
X	sel_last_ap = Nullart;
X	sel_last_sp = Nullsubj;
X	for (sp = first_subject; sp; sp = sp->next)
X	    unkill_subject(sp);
X    } else {
X	if (sel_mode == SM_ARTICLE) {
X	    register ARTICLE *ap;
X	    register ART_NUM an;
X	    for (an=absfirst, ap=article_ptr(an); an<=lastart; an++, ap++) {
X		if (ap->flags & AF_DEL) {
X		    ap->flags &= ~AF_DEL;
X		    set_read(ap);
X		}
X	    }
X	} else {
X	    register SUBJECT *sp;
X	    for (sp = first_subject; sp; sp = sp->next) {
X		if (sp->flags & SF_DEL) {
X		    sp->flags &= ~SF_DEL;
X		    if (sel_mode == SM_THREAD)
X			kill_thread(sp->thread, KF_UNSELECTED);
X		    else
X			kill_subject(sp, KF_UNSELECTED);
X		}
X	    }
X	}
X    }
X}
X
Xstatic int
Xsel_command(ch)
Xchar_int ch;
X{
X    if (can_home)
X	goto_line(sel_line, sel_last_line);
X    sel_line = sel_last_line;
X    clean_screen = TRUE;
X  do_command:
X    output_chase_phrase = TRUE;
X    switch (ch) {
X    case '>':
X	sel_item_index = 0;
X	if (next_page())
X	    return DS_DISPLAY;
X	return DS_POS;
X    case '<':
X	sel_item_index = 0;
X	if (prev_page())
X	    return DS_DISPLAY;
X	return DS_POS;
X    case '^':  case Ctl('r'):
X	sel_item_index = 0;
X	if (first_page())
X	    return DS_DISPLAY;
X	return DS_POS;
X    case '$':
X	sel_item_index = 0;
X	if (last_page())
X	    return DS_DISPLAY;
X	return DS_POS;
X    case Ctl('l'):
X	return DS_DISPLAY;
X    case Ctl('f'):
X	erase_eol();		/* erase the prompt */
X#ifdef MAILCALL
X	setmail(TRUE);		/* force a mail check */
X#endif
X	return DS_ASK;
X    case '#':
X       {
X	register SUBJECT *sp;
X	for (sp = first_subject; sp; sp = sp->next)
X	    sp->flags &= ~SF_VISIT;
X	selected_count = 0;
X	sp = (SUBJECT*)sel_items[sel_item_index].ptr;
X	switch (sel_mode) {
X	case SM_THREAD:
X	    deselect_thread(sp->thread);
X	    select_thread(sp->thread, 0);
X	    break;
X	case SM_SUBJECT:
X	    deselect_subject(sp);
X	    select_subject(sp, 0);
X	    break;
X	case SM_ARTICLE:
X	    deselect_article((ARTICLE*)sp);
X	    select_article((ARTICLE*)sp, 0);
X	    break;
X	}
X	return DS_QUIT;
X       }
X    case '\r':  case '\n':
X	if (!selected_count) {
X	    if (sel_rereading || sel_items[sel_item_index].sel != 2) {
X		register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr;
X		switch (sel_mode) {
X		case SM_THREAD:
X		    select_thread(sp->thread, 0);
X		    break;
X		case SM_SUBJECT:
X		    select_subject(sp, 0);
X		    break;
X		case SM_ARTICLE:
X		    select_article((ARTICLE*)sp, 0);
X		    break;
X		}
X	    }
X	}
X	return DS_QUIT;
X    case 'Z':  case '\t':
X	return DS_QUIT;
X    case 'q':  case 'Q':
X	return DS_QUIT;
X    case Ctl('Q'):  case '\033':  case '+':
X	sel_ret = '+';
X	return DS_QUIT;
X    case 'N':  case 'P':
X	return DS_QUIT;
X    case 'L':
X	if (!*++display_mode)
X	    display_mode = select_order;
X	return DS_DISPLAY;
X    case 'Y':
X	if (!dmcount) {
X	    sprintf(buf,"No marked articles to yank back.");
X	    return DS_STATUS;
X	}
X	yankback();
X	sel_line++;
X	if (!sel_rereading)
X	    sel_cleanup();
X	disp_status_line = TRUE;
X	count_subjects(CS_NORM);
X	sel_page_sp = Nullsubj;
X	sel_page_app = Null(ARTICLE**);
X	init_pages();
X	return DS_DISPLAY;
X    case 'U':
X	sel_cleanup();
X	sel_rereading = !sel_rereading;
X	sel_page_sp = Nullsubj;
X	sel_page_app = Null(ARTICLE**);
X	if (!cache_range(sel_rereading? absfirst : firstart, lastart))
X	    sel_rereading = !sel_rereading;
X	empty_ok = TRUE;
X	return DS_RESTART;
X    case '=':
X	if (!sel_rereading)
X	    sel_cleanup();
X	if (sel_mode == SM_ARTICLE) {
X	    set_selector(sel_threadmode, sel_threadsort);
X	    sel_page_sp = sel_page_app[0]->subj;
X	} else {
X	    set_selector(SM_ARTICLE, sel_artsort);
X	    sel_page_app = 0;
X	}
X	count_subjects(CS_NORM);
X	sel_item_index = 0;
X	init_pages();
X	return DS_DISPLAY;
X    case 'S':
X	if (!sel_rereading)
X	    sel_cleanup();
X	erase_eol();		/* erase the prompt */
X    reask_output:
X	in_char("Selector mode:  Threads, Subjects, Articles? [tsa] ", 'o');
X	setdef(buf,"t");
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\n\
XType t or SP to display/select thread groups (threads the group, if needed).\n\
XType s to display/select subject groups.\n\
XType a to display/select individual articles.\n\
XType q to leave things as they are.\n\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\n\
Xt or SP selects thread groups (threads the group too).\n\
Xs selects subject groups.\n\
Xa selects individual articles.\n\
Xq does nothing.\n\n\
X",stdout) FLUSH;
X#endif
X	    clean_screen = FALSE;
X	    goto reask_output;
X	} else if (*buf == 'q') {
X	    if (can_home) {
X		carriage_return();
X		erase_eol();
X	    }
X	    return DS_ASK;
X	}
X	set_sel_mode(*buf);
X	count_subjects(CS_NORM);
X	init_pages();
X	return DS_DISPLAY;
X    case 'O':
X	if (!sel_rereading)
X	    sel_cleanup();
X	erase_eol();		/* erase the prompt */
X    reask_sort:
X	if (sel_mode == SM_ARTICLE)
X	    in_char("Order by Date, Subject, Author, subject-date Groups? [dsagDSAG] ",
X		    'q');
X	else
X	    in_char("Order by Date, Subject, or Count? [dscDSC] ", 'q');
X	setdef(buf,"d");
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose) {
X		fputs("\n\
XType d or SP to order the displayed items by date.\n\
XType s to order the items by subject.\n\
X",stdout) FLUSH;
X		if (sel_mode == SM_ARTICLE)
X		    fputs("\
XType a to order the items by author.\n\
XType g to order the items in subject-groups by date.\n\
X",stdout) FLUSH;
X		else
X		    fputs("\
XType c to order the items by article-count.\n\
X",stdout) FLUSH;
X		fputs("\
XTyping your selection in upper case it will reverse the selected order.\n\
XType q to leave things as they are.\n\n\
X",stdout) FLUSH;
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		fputs("\n\
Xd or SP sorts by date.\n\
Xs sorts by subject.\n\
X",stdout) FLUSH;
X		if (sel_mode == SM_ARTICLE)
X		    fputs("\
Xa sorts by author.\n\
Xg sorts in subject-groups by date.\n\
X",stdout) FLUSH;
X		else
X		    fputs("\
Xc sorts by article-count.\n\
X",stdout) FLUSH;
X		fputs("\
XUpper case reverses the sort.\n\
Xq does nothing.\n\n\
X",stdout) FLUSH;
X	    }
X#endif
X	    clean_screen = FALSE;
X	    goto reask_sort;
X	} else if (*buf == 'q') {
X	    if (can_home) {
X		carriage_return();
X		erase_eol();
X	    }
X	    return DS_ASK;
X	}
X	set_sel_sort(*buf);
X	count_subjects(CS_NORM);
X	sel_page_sp = Nullsubj;
X	sel_page_app = Null(ARTICLE**);
X	init_pages();
X	return DS_DISPLAY;
X    case 'R':
X	if (!sel_rereading)
X	    sel_cleanup();
X	sel_direction *= -1;
X	count_subjects(CS_NORM);
X	sel_page_sp = Nullsubj;
X	sel_page_app = Null(ARTICLE**);
X	init_pages();
X	return DS_DISPLAY;
X    case 'E':
X	if (!sel_rereading)
X	    sel_cleanup();
X	sel_exclusive = !sel_exclusive;
X	count_subjects(CS_NORM);
X	sel_page_sp = Nullsubj;
X	sel_page_app = Null(ARTICLE**);
X	init_pages();
X	empty_ok = TRUE;
X	return DS_DISPLAY;
X    case 'X':  case 'D':  case 'J':
X	if (!sel_rereading) {
X	    if (sel_mode == SM_ARTICLE) {
X		register ARTICLE *ap, **app, **limit;
X		limit = artptr_list + artptr_list_size;
X		if (ch == 'D')
X		    app = sel_page_app;
X		else
X		    app = artptr_list;
X		for (;;) {
X		    ap = *app;
X		    if ((!(ap->flags & AF_SEL) ^ (ch == 'J'))
X		     || (ap->flags & AF_DEL))
X			if (!sel_exclusive || (ap->flags & AF_INCLUDED))
X			    set_read(ap);
X		    app++;
X		    if (app >= limit || (ch == 'D' && app == sel_next_app))
X			break;
X		}
X	    } else {
X		register SUBJECT *sp;
X		if (ch == 'D')
X		    sp = sel_page_sp;
X		else
X		    sp = first_subject;
X		for (;;) {
X		    if (((!(sp->flags & SF_SEL) ^ (ch == 'J')) && sp->misc)
X		     || (sp->flags & SF_DEL)) {
X			if (!sel_exclusive || (sp->flags & SF_INCLUDED))
X			    kill_subject(sp, ch=='J'? KF_ALL : KF_UNSELECTED);
X		    }
X		    sp = sp->next;
X		    if (!sp || (ch == 'D' && sp == sel_next_sp))
X			break;
X		}
X	    }
X	    count_subjects(CS_UNSELECT);
X	    if (article_count
X	     && (ch == 'J' || (ch == 'D' && !selected_count))) {
X		init_pages();
X		sel_item_index = 0;
X		return DS_DISPLAY;
X	    }
X	    if (artptr_list && article_count)
X		sort_articles();
X	    return DS_QUIT;
X	} else if (ch == 'J') {
X	    register SUBJECT *sp;
X	    for (sp = first_subject; sp; sp = sp->next)
X		deselect_subject(sp);
X	    selected_subj_cnt = selected_count = 0;
X	    return DS_DISPLAY;
X	}
X	return DS_QUIT;
X    case 'T':
X	if (!ThreadedGroup) {
X	    sprintf(buf,"Group is not threaded.");
X	    return DS_STATUS;
X	}
X	/* FALL THROUGH */
X    case 'A':
X	erase_eol();		/* erase the prompt */
X	if (sel_mode == SM_ARTICLE)
X	    artp = (ARTICLE*)sel_items[sel_item_index].ptr;
X	else {
X	    register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr;
X	    if (sel_mode == SM_THREAD) {
X		while (!sp->misc)
X		    sp = sp->next;
X	    }
X	    artp = sp->articles;
X	}
X	art = article_num(artp);
X	/* This call executes the action too */
X	switch (ask_memorize(ch)) {
X	case 'j':  case ',':
X	    count_subjects(sel_rereading ? CS_NORM : CS_UNSELECT);
X	    init_pages();
X	    sprintf(buf,"Kill memorized.");
X	    disp_status_line = TRUE;
X	    return DS_DISPLAY;
X	case '.':
X	    sprintf(buf,"Selection memorized.");
X	    disp_status_line = TRUE;
X	    return DS_DISPLAY;
X	case '+':
X	    sprintf(buf,"Selection memorized.");
X	    disp_status_line = TRUE;
X	    return DS_UPDATE;
X	case 'c':  case 'C':
X	    sprintf(buf,"Auto-commands cleared.");
X	    disp_status_line = TRUE;
X	    return DS_DISPLAY;
X	case 'q':
X	    return DS_DISPLAY;
X	case 'Q':
X	    break;
X	}
X	if (can_home) {
X	    carriage_return();
X	    erase_eol();
X	}
X	return DS_ASK;
X    case Ctl('k'):
X	edit_kfile();
X	return DS_DISPLAY;
X    case ':':  case '/':  case '&':  case '!':
X	erase_eol();		/* erase the prompt */
X	if (!finish_command(TRUE)) {	/* get rest of command */
X	    if (clean_screen)
X		return DS_ASK;
X	    goto extend_done;
X	}
X	if (ch == '&' || ch == '!') {
X	    one_command = TRUE;
X	    perform(buf, FALSE);
X	    one_command = FALSE;
X	    putchar('\n') FLUSH;
X	    clean_screen = FALSE;
X	} else {
X	    int sel_art_save = selected_count;
X
X	    if (ch == ':') {
X		clean_screen = (use_selected() == 2) && clean_screen;
X		if (!sel_rereading) {
X		    register SUBJECT *sp;
X		    for (sp = first_subject; sp; sp = sp->next) {
X			if (sp->flags & SF_DEL) {
X			    sp->flags = 0;
X			    if (sel_mode == SM_THREAD)
X				kill_thread(sp->thread, KF_UNSELECTED);
X			    else
X				kill_subject(sp, KF_UNSELECTED);
X			}
X		    }
X		}
X	    } else {
X		/* Force the search to begin at absfirst or firstart,
X		** depending upon whether they specified the 'r' option.
X		*/
X		art = lastart+1;
X		page_line = 1;
X		switch (art_search(buf, sizeof buf, FALSE)) {
X		case SRCH_ERROR:
X		case SRCH_ABORT:
X		case SRCH_INTR:
X		    fputs("\nInterrupted\n", stdout) FLUSH;
X		    break;
X		case SRCH_DONE:
X		case SRCH_SUBJDONE:
X		    fputs("Done\n", stdout) FLUSH;
X		    break;
X		case SRCH_NOTFOUND:
X		    fputs("\nNot found.\n", stdout) FLUSH;
X		    break;
X		case SRCH_FOUND:
X		    break;
X		}
X		clean_screen = FALSE;
X	    }
X	    /* Recount, in case something has changed. */
X	    count_subjects(sel_rereading ? CS_NORM : CS_UNSELECT);
X	    init_pages();
X	    sel_item_index = 0;
X
X	    sel_art_save -= selected_count;
X	    if (sel_art_save) {
X		putchar('\n');
X		if (sel_art_save < 0) {
X		    fputs("S", stdout);
X		    sel_art_save *= -1;
X		} else
X		    fputs("Des", stdout);
X		printf("elected %d article%s.",
X			sel_art_save, sel_art_save == 1 ? nullstr : "s");
X		clean_screen = FALSE;
X	    }
X	    if (!clean_screen)
X		putchar('\n') FLUSH;
X	}/* if !& else :/ */
X
X	if (clean_screen) {
X	    carriage_return();
X	    up_line();
X	    erase_eol();
X	    return DS_ASK;
X	}
X      extend_done:
X	if ((ch = pause_getcmd())) {
X	  got_cmd:
X	    if (ch > 0) {
X		/* try to optimize the screen update for some commands. */
X		if (!index(sel_chars, ch)
X		 && (index("<+>^$!?&:/hDEJLNOPqQRSUXYZ\n\r\t\033", ch)
X		  || ch == Ctl('k'))) {
X		    buf[0] = sel_ret = ch;
X		    buf[1] = FINISHCMD;
X		    goto do_command;
X		}
X		pushchar(ch | 0200);
X	    }
X	}
X	return DS_DISPLAY;
X    case 'c':
X	erase_eol();		/* erase the prompt */
X	if ((ch = ask_catchup()) == 'y' || ch == 'u') {
X	    count_subjects(CS_UNSELECT);
X	    if (ch != 'u' && article_count) {
X		sel_page_sp = Nullsubj;
X		sel_page_app = Null(ARTICLE**);
X		init_pages();
X		return DS_DISPLAY;
X	    }
X	    sel_ret = 'Z';
X	    return DS_QUIT;
X	}
X	if (ch != 'N')
X	    return DS_DISPLAY;
X	if (can_home) {
X	    carriage_return();
X	    erase_eol();
X	}
X	return DS_ASK;
X    case 'h':  case '?':
X	putchar('\n');
X	if ((ch = help_select()) || (ch = pause_getcmd()))
X	    goto got_cmd;
X        return DS_DISPLAY;
X    default:
X	sprintf(buf,"Type ? for help.");
X	settle_down();
X	if (clean_screen)
X	    return DS_STATUS;
X	printf("\n%s\n",buf);
X	goto extend_done;
X    }
X}
X
Xstatic void
Xempty_complaint()
X{
X    clear_on_stop = FALSE;
X    putchar('\n');
X    if (sel_rereading) {
X#ifdef VERBOSE
X	IF (verbose)
X	    fputs("\nNo articles to set unread.\n", stdout);
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("\nNo articles.\n", stdout) FLUSH;
X#endif
X	sel_rereading = 0;
X	sel_mask = AF_SEL;
X    } else {
X#ifdef VERBOSE
X	IF (verbose)
X	    fputs("\nNo unread articles to select.", stdout);
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("\nNo unread articles.", stdout);
X#endif
X	putchar('\n');	/* let "them" FLUSH */
X    }
X    selected_only = FALSE;
X    art = curr_art;
X    artp = curr_artp;
X}
END_OF_FILE
if test 23985 -ne `wc -c <'rt-select.c'`; then
    echo shar: \"'rt-select.c'\" unpacked with wrong size!
fi
# end of 'rt-select.c'
fi
echo shar: End of archive 8 \(of 12\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 12 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
