[net.sources] rn version 4.1 distribution kit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 1 (of 8).  If kit 1 is complete, the line"
echo '"'"End of kit 1 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting rn.man
cat >rn.man <<'!STUFFY!FUNK!'
''' $Header: rn.man,v 4.1 84/09/24 12:07:50 lwall Exp $
''' 
''' $Log:	rn.man,v $
''' Revision 4.1  84/09/24  12:07:50  lwall
''' Real baseline.
''' 
''' Revision 4.0.1.1  84/09/19  10:52:04  lwall
''' Additional warnings and "bugs".
''' 
''' Revision 4.0  84/09/04  09:52:24  lwall
''' Baseline for netwide release
''' 
''' 
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
.de Ip
.br
.ie \\n.$>=3 .ne \\$3
.el .ne 3
.IP "\\$1" \\$2
..
'''
'''     Set up \*(-- to give an unbreakable dash;
'''     string Tr holds user defined translation string.
'''     Bell System Logo is used as a dummy character.
'''
.tr \(bs-|\(bv\*(Tr
.ie n \{\
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" "
.ds R" "
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH RN 1 LOCAL
.SH NAME
rn - new read news program
.SH SYNOPSIS
.B rn [options] [newsgroups]
.SH DESCRIPTION
.I Rn
is a replacement for the readnews(1) program that was written to be as
efficient as possible, particularly in human interaction.
.I Rn
attempts to minimize the amount of \*(L"dead\*(R" time spent reading
news\*(--it tries to get things done while the user is reading or deciding
whether to read, and attempts to get useful information onto the screen as
soon as possible, highlighting spots that the eye makes frequent reference
to, like subjects and previously read lines.
Whether or not it's faster, it SEEMS faster.
.PP
If no newsgroups are specified, all the newsgroups which have unread news
are displayed, and then the user is asked for each one whether he wants to
read it, in the order in which the newsgroups occur in the
.I .newsrc
file.
With a list of newsgroups,
.I rn
will start up in \*(L"add\*(R" mode, using the list as a set of patterns to
add new newsgroups and restrict which newsgroups are displayed.
See the discussion of the \*(L'a\*(R' command on the newsgroup selection
level.
.PP
.I Rn
operates on three levels: the newsgroup selection level, the article
selection level, and the paging level.
Each level has its own set of commands, and its own help menu.
At the paging level (the bottom level),
.I rn
behaves much like the
.IR more (1)
program.
At the article selection level, you may specify which article you want
next, or read them in the default order, which is either in order of
arrival on your system, or by subject threads.
At the newsgroup selection level (the top level), you may specify which
newsgroup you want next, or read them in the default order, which is the
order that the newsgroups occur in your
.I .newsrc
file.
(You will therefore want to rearrange your
.I .newsrc
file to put the most interesting newsgroups first.
This can be done with the \*(L'm\*(R' command on the Newsgroup Selection level.
WARNING: invoking readnews/vnews (the old user interface) in any way (including
as a news checker in your login sequence!) will cause your
.I .newsrc
to be disarranged again.)
.PP
On any level, at ANY prompt, an \*(L'h\*(R' may be typed for a list of
available commands.
This is probably the most important command to remember, so don't you
forget it.
Typing space to any question means to do the normal thing.
You will know what that is because
every prompt has a list of several plausible commands enclosed in
square brackets.
The first command in the list is the one which will be done if you type
a space.
(All input is done in cbreak mode, so carriage returns should not be typed
to terminate anything except certain multi-character commands.
Those commands will be obvious in the discussion below because they take an
argument.)
.PP
Upon startup,
.I rn
will do several things:
.Ip 1. 4
It will look for your
.I .newsrc
file, which is your list of subscribed-to
newsgroups.
If
.I rn
doesn't find a
.IR .newsrc ,
it will create one.
If it does find one, it will back it up under the name \*(L".oldnewsrc\*(R".
.Ip 2. 4
It will input your
.I .newsrc
file, listing out the first several newsgroups
with unread news.
.Ip 3. 4
It will perform certain consistency checks on your
.IR .newsrc .
If your
.I .newsrc
is out of date in any of several ways,
.I rn
will warn you and patch it up for you, but you may have to wait a little
longer for it to start up.
.Ip 4. 4
.I Rn
will next check to see if any new newsgroups have been created, and give
you the opportunity to add them to your
.IR .newsrc .
.Ip 5. 4
.I Rn
goes into the top prompt level\*(--the newsgroup selection level.
.Sh "Newsgroup Selection Level"
In this section the words \*(L"next\*(R" and \*(L"previous\*(R" refer to
the ordering of the newsgroups in your
.I .newsrc
file.
On the newsgroup selection level, the prompt looks like this:
.Sp
******** 17 unread articles in net.blurfl\*(--read now? [ynq]
.Sp
and the following commands may be given at this level:
.Ip y,SP 8
Do this newsgroup now.
.Ip .command 8
Do this newsgroup now, but execute
.I command
before displaying anything.
The command will be interpreted as if given on the article selection level.
.Ip = 8
Do this newsgroup now, but list subjects before displaying articles.
.Ip n 8
Go to the next newsgroup with unread news.
.Ip N 8
Go to the next newsgroup.
.Ip p 8
Go to the previous newsgroup with unread news.
If there is none, stay at the current newsgroup.
.Ip P 8
Go to the previous newsgroup.
.Ip \- 8
Go to the previously displayed newsgroup (regardless of whether it is
before or after the current one in the list).
.Ip 1 8
Go to the first newsgroup.
.Ip ^ 8
Go to the first newsgroup with unread news.
.Ip $ 8
Go to the end of the newsgroups list.
.Ip "g newsgroup" 8
Go to
.IR newsgroup .
If it isn't currently subscribed to, you will be asked if you want to
subscribe.
.Ip "/pattern" 8
Scan forward for a newsgroup matching
.IR pattern .
Patterns do globbing like filenames, i.e., use ? to match a single
character, * to match any sequence of characters, and [] to specify a list
of characters to match.
(\*(L"all\*(R" may be used as a synonym for \*(L"*\*(R".)
Unlike normal filename globbing, newsgroup searching is not anchored to
the front and back of the filename, i.e. \*(L"/jok\*(R" will find
net.jokes.
You may use ^ or $ to anchor the front or back of the search:
\*(L"/^test$\*(R" will find newsgroup test and nothing else
If you want to include newsgroups with 0 unread articles, append /r.
If the newsgroup is not found between the current newsgroup and the last
newsgroup, the search will wrap around to the beginning.
.Ip "?pattern" 8
Same as /, but search backwards.
.Ip u 8
Unsubscribe from current newsgroup.
.Ip "l string" 8
List newsgroups not subscribed to which contain the string specified.
.Ip L 8 13v
Lists the current state of the
.IR .newsrc ,
along with status information.
.Sp
.nf
    \h'|0.5i'Status    \h'|2i'Meaning
    \h'|0.5i'<number>  \h'|2i'Count of unread articles in newsgroup.
    \h'|0.5i'READ      \h'|2i'No unread articles in newsgroup.
    \h'|0.5i'UNSUB     \h'|2i'Unsubscribed newsgroup.
    \h'|0.5i'BOGUS     \h'|2i'Bogus newsgroup.
    \h'|0.5i'JUNK      \h'|2i'Ignored line in .newsrc
\h'|2i'(e.g. readnews \*(L"options\*(R" line).
.fi
.Sp
(A bogus newsgroup is one that is not in the list of active newsgroups
in the active file, which on most systems is /usr/lib/news/active.)
.Ip "m name" 8
Move the named newsgroup somewhere else in the
.IR .newsrc .
If no name is given, the current newsgroup is moved.
There are a number of ways to specify where you want the newsgroup\*(--type
h for help when it asks where you want to put it.
.Ip c 8
Catch up\*(--mark all unread articles in this newsgroup as read.
.Ip "o pattern" 8
Only display those newsgroups whose name matches
.IR pattern .
Patterns are the same as for the \*(L'/\*(R' command.
Multiple patterns may be separated by spaces, just as on the
command line.
The restriction will remain in effect either until there are no articles
left in the restricted set of newsgroups, or another restriction command
is given.
Since
.I pattern
is optional, \*(L'o\*(R' by itself will remove the
restriction.
.Ip "a pattern" 8
Add new newsgroups matching
.IR pattern .
Newsgroups which are already in your
.I .newsrc
file, whether subscribed to or
not, will not be listed.
If any new newsgroups are found, you will be asked for each one whether
you would like to add it.
After any new newsgroups have been added, the \*(L'a\*(R' command also
restricts the current set of newsgroups just like the \*(L'o\*(R' command
does.
.Ip & 8
Print out the current status of command line switches and any newsgroup
restrictions.
.Ip "&switch {switch}" 8
Set additional command line switches.
.Ip !command 8
Escape to a subshell.
One exclamation mark (!) leaves you in your own news directory.
A double exclamation mark (!!) leaves you in the spool
directory for news, which on most systems is /usr/spool/news.
The environment variable SHELL will be used if defined.
If
.I command
is null, an interactive shell is started.
.Ip q 8
Quit.
.Ip ^K 8
Edit the global KILL file.
This is a file which contains /pattern/j commands (one per line) to be
applied to every newsgroup as it is started up, that is, when it is
selected on the newsgroup selection level.
The purpose of a KILL file is to mark articles as read on the basis of some
set of patterns.
This saves considerable wear and tear on your \*(L'n\*(R' key.
There is also a local KILL file for each newsgroup.
Because of the overhead involved in searching for articles to kill, it is
better if possible to use a local KILL file.
Local KILL files are edited with a \*(L'^K\*(R' on the article selection level.
There are also automatic ways of adding search commands to the local KILL
file\*(--see the \*(L'K\*(R' command and the K search modifier on the
article selection level.
.Sp
If either of the environment variables VISUAL or EDITOR is set, the
specified editor will be invoked; otherwise a default editor (normally vi(1))
is invoked on the KILL file.
.Sh "Article Selection Level"
On the article selection level,
.I rn
selects (by default) unread articles in numerical order (the order in which
articles have arrived at your site).
If you do a subject search (^N), the default order is modified to be
numerical order within each subject thread.
You may switch back and forth between numerical order and subject thread
order at will.
The
.B \-S
switch can be used to make subject search mode the default.
.Sp
On the article selection level you are
.I not
asked whether you want to read an article before the article is displayed;
rather,
.I rn
simply displays the first page (or portion of a page, at low baud rates) of the
article and asks if you want to continue.
The normal article selection prompt comes at the END of the article
(though article selection commands can be given from within the middle
of the article (the pager level) also).
The prompt at the end of an article looks like this:
.Sp 
End of article 248 (of 257)\*(--what next? [npq]
.Sp
The following are the options at this point:
.Ip n,SP 8
Scan forward for next unread article.
(Note: the \*(L'n\*(R' (next) command when typed at the end of an article
does not mark the article as read, since an article is automaticaly marked
as read after the last line of it is printed.
It is therefore possible to type a sequence such as \*(L'mn\*(R' and leave
the article marked as unread.
The fact that an article is marked as read by typing \*(L'n\*(R',
\&\*(L'N\*(R', \*(L'^N\*(R', \*(L's\*(R', or \*(L'S\*(R' within the MIDDLE of
the article is in fact a special case.)
.Ip N 8
Go to the next article.
.Ip ^N 8
Scan forward for the next article with the same subject, and make ^N default
(subject search mode).
.Ip p 8
Scan backward for previous unread article.
If there is none, stay at the current article.
.Ip P 8
Go to the previous article.
.Ip \- 8
Go to the previously displayed article (regardless of whether that article
is before or after this article in the normal sequence).
.Ip ^P 8
Scan backward for the previous article with the same subject, and make
^N default (subject search mode).
.Ip ^R 8
Restart the current article.
.Ip ^L 8
Refresh the screen.
.Ip ^X 8
Restart the current article, and decrypt as a rot13 message.
.Ip X 8
Refresh the screen, and decrypt as a rot13 message.
.Ip b 8
Back up one page.
.Ip q 8
Quit this newsgroup and go back to the newsgroup selection level.
.Ip ^ 8
Go to the first unread article.
.Ip $ 8
Go to the last article (actually, one past the last article).
.Ip "number" 8
Go to the numbered article.
.Ip "range{,range} command{:command}" 8
Apply a set of commands to a set of articles.
A range consists of either <article number> or
<article\ number>\-<article\ number>.
A dot \*(L'.\*(R' represents the current article, and a dollar
sign \*(L'$\*(R' represents the last article.
.Sp
Applicable commands include \*(L'm\*(R' (mark as unread), \*(L'M\*(R'
(delayed mark as unread), \*(L'j\*(R' (mark as read), \*(L"s dest\*(R"
(save to a destination), \*(L"!command\*(R" (shell escape), \*(L"=\*(R"
(print the subject) and \*(L"C\*(R" (cancel).
.Ip j 8
Junk the current article\*(--mark it as read.
If this command is used from within an article, you are left at the end of
the article, unlike \*(L'n\*(R', which looks for the next article.
.Ip m 8
Mark the current article as still unread.
(If you are in subject search mode you probably want to use M instead of m.
Otherwise the current article may be selected as the beginning of the next
subject thread.)
.Ip M 8
Mark the current article as still unread, but not until the newsgroup
is exited.
Until then, the current article will be marked as read.
This is useful for returning to an article in another session, or in another
newsgroup.
.Ip /pattern 8
Scan forward for article containing
.I pattern
in the subject.
See the section on Regular Expressions.
Together with the escape substitution facility described later, it becomes
easy to search for various attributes of the current article, such as
subject, article ID, author name, etc.
The previous pattern can be recalled with \*(L"<esc>/\*(R".
If
.I pattern
is omitted, the previous pattern is assumed.
.Ip /pattern/h 8
Scan forward for article containing
.I pattern
in the header.
.Ip /pattern/a 8
Scan forward for article containing
.I pattern
anywhere in article.
.Ip /pattern/r 8
Scan read articles also.
.Ip /pattern/c 8
Make search case sensitive.
Ordinarily upper and lower case are considered the same.
.Ip "/pattern/modifiers:command{:command}" 8
Apply the commands listed to articles matching the search command (possibly
with h, a, or r modifiers).
Applicable commands include \*(L'm\*(R' (mark as unread), \*(L'M\*(R'
(delayed mark as unread), \*(L'j\*(R' (mark as read), \*(L"s dest\*(R"
(save to a destination), \*(L"!command\*(R" (shell escape), \*(L"=\*(R"
(print the subject) and \*(L"C\*(R" (cancel).
If the first command is \*(L'm\*(R' or \*(L'M\*(R', modifier r is assumed.
A K may be included in the modifiers (not the commands) to cause the
entire command (sans K) to be saved to the local KILL file, where it will
be applied to every article that shows up in the newsgroup.
.Sp
For example, to save all articles in a given newsgroup to the line printer
and mark them read, use \*(L"/^/\||\|lpr:j\*(R".
If you say \*(L"/^/K\||\|lpr:j\*(R", this will happen every time you enter the
newsgroup.
.Ip ?pattern 8
Scan backward for article containing
.I pattern
in the subject.
May be modified as the forward search is: ?pattern?modifiers[:commands].
It is likely that you will want an r modifier when scanning backward.
.Ip k 8
Mark as read all articles with the same subject as the current article.
(Note: there is no single character command to temporarily mark as read (M
command) articles matching the current subject.
That can be done with \*(L"/<esc>s/M\*(R", however.)
.Ip K 8
Do the same as the k command, but also add a line to the local KILL file for
this newsgroup to kill this subject every time the newsgroup is started up.
For a discussion of KILL files, see the \*(L'^K\*(R' command below.
See also the K modifier on searches above.
.Ip ^K 8
Edit the local KILL file for this newsgroup.
Each line of the KILL file should be a command of the form /pattern/j.
(With the exception that
.I rn
will insert a line at the beginning of the form \*(L"THRU <number>\*(R",
which tells
.I rn
the maximum article number that the KILL file has been applied to.  You
may delete the THRU line to force a rescan of current articles.)
You may also have reason to use the m, h, or a modifiers.
Be careful with the M modifier in a kill file\*(--there are more efficient
ways to never read an article.
You might have reason to use it if a particular series of articles is posted
to multiple newsgroups.
In this case, M would force you to view the article in a different newsgroup.
.Sp
To see only newgroup articles in the control newsgroup, for instance, you
might put
.Sp
/^/j
.br
/newgroup/m
.Sp
which kills all subjects not containing \*(L"newgroup\*(R".
You can add lines automatically via the K command and K search modifiers,
but editing is the only way to remove lines.
If either of the environment variables VISUAL or EDITOR is set, the
specified editor will be invoked; otherwise a default editor (normally vi)
is invoked on the KILL file.
.Sp
The KILL file may also contain switch setting lines beginning with \*(L'&\*(R'.
Additionally, any line beginning with \*(L'X\*(R' is executed on exit
from the newsgroup rather than on entrance.
This can be used to set switches back to a default value.
.Ip r 8
Reply through net mail.
The environment variables MAILPOSTER and MAILHEADER may be used to modify
the mailing behavior of
.I rn
(see environment section).
.Ip R 8
Reply, including the current article in the header file generated.
(See \*(L'F\*(R' command below).
The YOUSAID environment variable controls the format of the attribution line.
.Ip f 8
Submit a followup article.
.Ip F 8
Submit a followup article, and include the old article, with lines prefixed
either by \*(L">\*(R" or by the argument to a
.B \-F
switch.
.I Rn
will attempt to provide an attribution line in front of the quoted article,
generated from the From: line of the article.
Unfortunately, the From: line doesn't always contain the right name; you
should double check it against the signature and change it if necessary,
or you may have to apologize for quoting the wrong person.
The environment variables NEWSPOSTER, NEWSHEADER and ATTRIBUTION may be
used to modify the posting behavior of
.I rn
(see environment section).
.Ip C 8
Cancel the current article, but only if you are the contributor or superuser.
.Ip c 8
Catch up in this newsgroup; i.e., mark all articles as read.
.Ip u 8
Unsubscribe to this newsgroup.
.Ip "s destination" 8
Save to a filename or pipe using sh.
If the first character of the destination is a vertical bar, the rest of
the command is considered a shell command to which the article is passed
through standard input.
The command is subject to filename expansion.
(See also the environment variable PIPESAVER.)
If the destination does not begin with a vertical bar, the rest of the
command is assumed to be a filename of some sort.
An initial tilde \*(L'~\*(R' will be translated to the name of the home
directory, and an initial environment variable substitution is also allowed.
If only a directory name is specified, the environment variable SAVENAME
is used to generate the actual name.
If only a filename is specified (i.e. no directory), the environment variable
SAVEDIR will be used to generate the actual directory.
If nothing is specified, then obviously both variables will be used.
Since the current directory for rn while doing a save command is your
private news directory, saying \*(L"s ./filename\*(R" will force the file
to your news directory.
Save commands are also run through % interpretation, so that you can
say \*(L"s %O/filename\*(R" to save to the directory you were in when you ran
.IR rn ,
and \*(L"s %t\*(R" to save to a filename consisting of the Internet address
of the sender.
.Sp
After generating the full pathname of the file to save to,
.I rn
determines if the file exists already, and if so, appends to it.
.I Rn
will attempt to determine if an existing file is a mailbox or a normal file,
and save the article in the same format.
If the output file does not yet exist,
.I rn
will by default ask you which format you want, or you can make it skip the
question with either the
.B \-M
or
.B \-N
switch.
If the article is to be saved in mailbox format, the command to do so is
generated from the environment variable MBOXSAVER.
Otherwise, NORMSAVER is used.
.Ip "S destination" 8
Save to a filename or pipe using a preferred shell, such as csh.
Which shell is used depends first on what you have the environment variable
SHELL set to, and in the absence of that, on what your news administrator
set for the preferred shell when he or she installed
.IR rn .
.Ip "| command" 8
Shorthand for \*(L"s | command\*(R".
.Ip "w destination" 8
The same as \*(L"s destination\*(R", but saves without the header.
.Ip "W destination" 8
The same as \*(L"S destination\*(R", but saves without the header.
.Ip & 8
Print out the current status of command line switches.
.Ip "&switch {switch}" 8
Set additional command line switches.
.Ip !command 8
Escape to a subshell.
One exclamation mark (!) leaves you in your own news directory.
A double exclamation mark (!!) leaves you in the spool
directory of the current newsgroup.
The environment variable SHELL will be used if defined.
If
.I command
is null, an interactive shell is started.
.Sp
You can use escape key substitutions described later to get to many
run-time values.
The command is also run through % interpretation, in case it is being called
from a range or search command.
.Ip = 8
List subjects of unread articles.
.Ip # 8
Print last article number.
.Sh "Pager Level"
At the pager level (within an article), the prompt looks like this:
.Sp
\*(--MORE\*(--(17%)
.Sp
and a number of commands may be given:
.Ip SP 8
Display next page.
.Ip x 8
Display next page and decrypt as a rot13 message.
.Ip d,^D 8
Display half a page more.
.Ip CR 8
Display one more line.
.Ip q 8
Go to the end of the current article (don't mark it either read or unread).
Leaves you at the \*(L"What next?\*(R" prompt.
.Ip j 8
Junk the current article.
Mark it read and go to the end of the article.
.Ip ^L 8
Refresh the screen.
.Ip X 8
Refresh the screen and decrypt as a rot13 message.
.Ip b,^B 8
Back up one page.
.Ip gpattern 8
Goto (search forward for)
.I pattern
within current article.
Note that there is no space between the command and the pattern.
If the pattern is found, the page containing the pattern will be displayed.
Where on the page the line matching the pattern goes depends on the value
of the
.B \-g
switch.
By default the matched line goes at the top of the screen.
.Ip G 8
Search for g pattern again.
.Ip ^G 8
This is a special version of the \*(L'g\*(R' command that is for skipping
articles in a digest.
It is equivalent to setting \*(L"\-g4\*(R" and then executing the command
\*(L"g^Subject:\*(R".
.Ip !command 8
Escape to a subshell.
.PP
The following commands skip the rest of the current article, then behave just
as if typed to the \*(L"What next?\*(R" prompt at the end of the article.
See the documentation at the article selection level for these commands.
.Sp	
    # $ & / = ? c C f F k K ^K m M r R ^R u v Y ^
.br
    number
.br
    range{,range} command{:command}
.Sp
The following commands also skip to the end of the article, but have the
additional effect of marking the current article as read:
.Sp
    n N ^N s S | w W
.Sp
.Sh "Miscellaneous facts about commands"
An \*(L'n\*(R' typed at either the \*(L"Last newsgroup\*(R" prompt or a
\*(L"Last article\*(R" prompt will cycle back to the top of the newsgroup
or article list, whereas a \*(L'q\*(R' will quit the level.
(Note that \*(L'n\*(R' does not mean \*(L"no\*(R", but rather
\*(L"next\*(R".)
A space will of course do whatever is shown as the
default, which will vary depending on whether rn thinks you have more
articles or newsgroups to read.
.PP
The \*(L'b\*(R' (backup page) command may be repeated until the beginning of
the article is reached.
If
.I rn
is suspended (via a ^Z), then when the job is resumed, a refresh (^L) will
automatically be done (Berkeley-type systems only).
If you type a command such as \*(L'!\*(R' or \*(L's\*(R' which takes you
from the middle of the article to the end, you can always get back into the
middle by typing \*(L'^L\*(R'.
.PP
In multi-character commands such as \*(L'!\*(R', \*(L's\*(R', \*(L'/\*(R',
etc, you can interpolate various run-time values by typing escape and a
character.
To find out what you can interpolate, type escape and \*(L'h\*(R', or check
out the single character % substitutions for environment variables in the
Interpretation and Interpolation section, which are the same.
Additionally, typing a double escape will cause any % substitutions in the
string already typed in to be expanded.
.Sh "Options"
.I Rn
has a nice set of options to allow you to tailor the interaction
to your liking.
(You might like to know that the author swears by \*(L"\-e \-m \-S \-/\*(R".)
These options may be set on the command line, via the RNINIT
environment variable, via a file pointed to by the RNINIT variable, or
from within rn via the & command.
Options may generally be unset by saying \*(L"+switch\*(R".
Options include:
.TP 5
.B \-c
checks for news without reading news.
If a list of newsgroups is given on the command line, only those newsgroups
will be checked; otherwise all subscribed-to newsgroups are checked.
Whenever the
.B \-c
switch is specified, a non-zero exit status from
.I rn
means that there is unread news in one of the checked newsgroups.
The
.B \-c
switch does not disable the printing of newsgroups with unread news;
this is controlled by the
.B \-s
switch.
(The
.B \-c
switch is not meaningful when given via the & command.)
.TP 5
.B \-C<number>
tells
.I rn
how often to checkpoint the
.IR .newsrc ,
in articles read.
Actually, this number says when to start thinking about doing a checkpoint
if the situation is right.
If a reasonable checkpointing situation doesn't arise within 10 more
articles, the
.I .newsrc
is checkpointed willy-nilly.
.TP 5
.B \-d<directory name>
sets the default save directory to something other than ~/News.
The directory name will be globbed (via csh) if necessary (and if possible).
Articles saved by
.I rn
may be placed in the save directory or in a subdirectory thereof depending
on the command that you give and the state of the environment variables
SAVEDIR and SAVENAME.
Any KILL files (see the K command in the Article Selection section)
also reside in this directory and its subdirectories, by default.
In addition, shell escapes leave you in this directory.
.TP 5
.B \-D<flags>
enables debugging output.
See rn.h for flag values.
.TP 5
.B \-e
causes each page within an article to be started at the top of the screen,
not just the first page.
(It is similar to the
.B \-c
switch of
.IR more (1).)
You never have to read scrolling text with this switch.
This is helpful especially at certain baud rates because you can start reading
the top of the next page without waiting for the whole page to be printed.
It works nicely in conjuction with the
.B \-m
switch, especially if you use half-intensity for your highlight mode.
.TP 5
.B \-E<name>=<val>
sets the environment variable <name> to the value specified.
Within
.IR rn ,
\*(L"&\-ESAVENAME=%t\*(R" is similar to \*(L"setenv SAVENAME '%t'\*(R" in
.IR csh ,
or \*(L"SAVENAME='%t'; export SAVENAME\*(R" in
.IR sh .
Any environment variables set with
.B \-E
will be inherited by subprocesses of
.IR rn .
.TP 5
.B \-F<string>
sets the prefix string for the \*(L'F\*(R' followup command to use in
prefixing each line of the quoted article.
For example, \*(L"\-F<tab>\*(R" inserts a tab on the front of each line
(which will cause long lines to wrap around, unfortunately),
\*(L"\-F>>>>\*(R" inserts \*(L">>>>\*(R" on every line, and
\*(L"\-F\*(R" by itself causes nothing to be inserted, in case you want to
reformat the text, for instance.
The initial default prefix is \*(L">\*(R".
.TP 5
.B \-g<line>
tells
.I rn
which line of the screen you want searched-for strings to show up on when
you search with the \*(L'g\*(R' command within an article.
The lines are numbered starting with 1.
The initial default is \*(L"\-g1\*(R", meaning the first line of the screen.
Setting the line to less than 1 or more than the number of lines on the screen
will set it to the last line of the screen.
.TP 5
.B \-h<string>
hides (disables the printing of) all header lines beginning with
.I string.
For instance, \-hexp will disable the printing of the \*(L"Expires:\*(R" line.
Case is insignificant.
If <string> is null, all header lines except Subject are hidden, and you
may then use
.B +h
to select those lines you want to see.
You may wish to use the baud-rate switch modifier below to hide more lines
at lower baud rates.
.TP 5
.B \-H<string>
works just like
.B \-h
except that instead of setting the hiding flag for a header line, it sets
the magic flag for that header line.
Certain header lines have magic behavior that can be controlled this way.
At present, the following actions are caused by the flag for the particular
line:
the Newsgroups line will only print when there are multiple newsgroups,
the Subject line will be underlined, and the Expires line will always be
suppressed if there is nothing on it.
In fact, all of these actions are the default, and you must use
.B +H
to undo them.
.TP 5
.B \-i=<number>
specifies how long (in lines) to consider the initial page of an
article\*(--normally this is determined automatically depending on baud rate.
(Note that an entire article header will always be printed regardless of the
specified initial page length.
If you are working at low baud rate and wish to reduce the size of the
headers, you may hide certain header lines with the
.B \(bsh
switch.)
.TP 5
.B \-l
disables the clearing of the screen at the beginning of each
article, in case you have a bizarre terminal.
.TP 5
.B \-m=<mode>
enables the marking of the last line of the previous page
printed, to help the user see where to continue reading.
This is most helpful when less than a full page is going to be displayed.
It may also be used in conjunction with the
.B \-e
switch, in which case the page is erased, and the first line (which is
the last line of the previous page) is highlighted.
If
.B \-m=s
is specified, the standout mode will be used, but if
.B \-m=u
is specified, underlining will be used.
If neither
.B =s
or
.B =u
is specified, standout is the default.
.TP 5
.B \-M
forces mailbox format in creating new save files.
Ordinarily you are asked which format you want.
.TP 5
.B \-N
forces normal (non-mailbox) format in creating new save files.
Ordinarily you are asked which format you want.
.TP 5
.B \-r
causes
.I rn
to restart in the last newsgroup read during a previous session with
.I rn.
It is equivalent to starting up normally and then getting to the newsgroup
with a g command.
.TP 5
.B \-s
with no argument suppresses the initial listing of newsgroups with unread
news, whether
.B \-c
is specified or not.
Thus
.B \-c
and
.B \-s
can be used together to test \*(L"silently\*(R" the status of news from
within your
.I .login
file.
If
.B \-s
is followed by a number, the initial listing is suppressed after that many
lines have been listed.
Presuming that you have your
.I .newsrc
sorted into order of interest,
.B \-s5
will tell you the 5 most interesting newsgroups that have unread news.
This is also a nice feature to use in your
.I .login
file, since it not only tells you whether there is unread news, but also how
important the unread news is, without having to wade through the entire
list of unread newsgroups.
If no 
.B \-s
switch is given 
.B \-s5
is assumed, so just putting \*(L"rn \-c\*(R"
into your
\&.login file is fine.
.TP 5
.B \-S<number>
causes
.I rn
to enter subject search mode (^N) automatically whenever a newsgroup is
started up with <number> unread articles or more.
Additionally, it causes any \*(L'n\*(R' typed while in subject search mode
to be interpreted as \*(L'^N\*(R' instead.
(To get back out of subject search mode, the best command is probably
\&\*(L'^\*(R'.)
If <number> is omitted, 3 is assumed.
.TP 5
.B \-t
puts
.I rn
into terse mode.
This is more cryptic but useful for low baud rates.
(Note that your system administrator may have compiled
.I rn
with either verbose or terse messages only to save memory.)
You may wish to use the baud-rate switch modifier below to enable terse mode
only at lower baud rates.
.TP 5
.B \-T
allows you to type ahead of rn.
Ordinarily rn will eat typeahead to prevent your autorepeating space bar from
doing a very frustrating thing when you accidentally hold it down.
If you don't have a repeating space bar, or you are working at low baud
rate, you can set this switch to prevent this behavior.
You may wish to use the baud-rate switch modifier below to disable typeahead
only at lower baud rates.
.TP 5
.B \-v
sets verification mode for commands.
When set, the command being executed is displayed to give some feedback that
the key has actually been typed.
Useful when the system is heavily loaded and you give a command that takes
a while to start up.
.TP 5
.B \-/
sets SAVEDIR to \*(L"%p/%c\*(R" and SAVENAME to \*(L"%a\*(R", which means
that by default articles are saved in a subdirectory of your private news
directory corresponding to the name of the the current newsgroup, with the
filename being the article number.
.B +/
sets SAVEDIR to \*(L"%p\*(R" and SAVENAME to \*(L"%^C\*(R", which by
default saves articles directly to your private news directory, with the
filename being the name of the current newsgroup, first letter capitalized.
(Either
.B +/
or
.B \-/
may be default on your system, depending on the feelings of your news
administrator when he, she or it installed
.IR rn .)
You may, of course, explicitly set SAVEDIR and SAVENAME to other values\*(--see
discussion in the environment section.
.PP
Any switch may be selectively applied according to the current baud-rate.
Simply prefix the switch with +speed to apply the switch at that speed or
greater, and \%\-speed to apply the switch at that speed or less.
Examples: \%\-1200\-hposted suppresses the Posted line at 1200 baud or less;
\%+9600\-m enables marking at 9600 baud or more.
You can apply the modifier recursively to itself also: \%+300\-1200\-t sets
terse mode from 300 to 1200 baud.
.PP
Some switch arguments, such as environment variable values, may require
spaces in them.
Such spaces should be quoted via ", ', or \e in the conventional fashion,
even when passed via RNINIT or the & command.
.Sh "Regular Expressions"
The patterns used in article searching are regular expressions such as
those used by
.IR ed (1).
In addition, \ew matches an alphanumeric character and \eW a nonalphanumeric.
Word boundaries may be matched by \eb, and non-boundaries by \eB.
The bracketing construct \e(\ ...\ \e) may also be used, and \edigit matches
the digit'th substring, where digit can range from 1 to 9.
\e0 matches whatever the last bracket match matched.
Up to 10 alternatives may given in a pattern, separated by \e|, with the
caveat that \e(\ ...\ \e|\ ...\ \e) is illegal.
.Sh "Interpretation and Interpolation"
Many of the strings that
.I rn
handles are subject to interpretations of several types.
Under filename expansion, an initial \*(L"~/\*(R" is translated to the name
of your home directory, and \*(L"~name\*(R" is translated to the login
directory for the user specified.
Filename expansion will also expand an initial environment variable, and
also does the backslash and percent expansion mentioned below.
.PP
All interpreted strings go through both backslash and percent interpretation.
The backslash escapes are the normal ones (such as \en, \et, \ennn, etc.).
The special percent escapes are similar to printf percent escapes.
These cause the substitution of various run-time values into the string.
The following are currently recognized:
.Ip %a 8
Current article number.
.Ip %A 8
Full name of current article (%P/%c/%a).
(On a Eunice system with the LINKART option, %P/%c/%a returns the name of
the article in the current newsgroup, while %A returns the real name of
the article, which may be different if the current article was posted to
multiple newsgroups.)
.Ip %b 8
Destination of last save command, often a mailbox.
.Ip %B 8
The byte offset to the beginning of the part of the article to be saved,
set by the save command.
The \*(L's\*(R' and \*(L'S\*(R' commands set it to 0, and the \*(L'w\*(R'
and \*(L'W\*(R' commands set it to the byte offset of the body of the article.
.Ip %c 8
Current newsgroup, directory form.
.Ip %C 8
Current newsgroup, dot form.
.Ip %d 8
Full name of newsgroup directory (%P/%c).
.Ip %D 8
\*(L"Distribution:\*(R" line from the current article.
.Ip %f 8
\*(L"From:\*(R" line from the current article, or the \*(L"Reply-To:\*(R"
line if there is one.
This differs from %t in that comments (such as the full name) are not
stripped out with %f.
.Ip %F 8
\*(L"Newsgroups:\*(R" line for a new article, constructed from
\*(L"Newsgroups:\*(R" and \*(L"Followup-To:\*(R" lines of current article.
.Ip %h 8
Name of the header file to pass to the mail or news poster,
containing all the information that the poster program needs in the
form of a message header.
It may also contain a copy of the current article.
The format of the header file is controlled by the MAILHEADER and NEWSHEADER
environment variables.
.Ip %H 8
Host name (your machine's name).
.Ip %i 8
\*(L"Message-I.D.:\*(R" line from the current article, with <> guaranteed.
.Ip %l 8
The news administrator's login name, if any.
.Ip %L 8
Login name (yours).
.Ip %M 8
The number of articles marked to return via the \*(L'M\*(R' command.
If the same article is Marked multiple times, \*(L"%M\*(R" counts it
multiple times in the current implementation.
.Ip %n 8
\*(L"Newsgroups:\*(R" line from the current article.
.Ip %N 8
Full name (yours).
.Ip %o 8
Organization (yours).
.Ip %O 8
Original working directory (where you ran rn from).
.Ip %p 8
Your private news directory, normally ~/News.
.Ip %P 8
Public news spool directory, normally /usr/spool/news.
.Ip %r 8
Last reference on references line of current article (parent article id).
.Ip %R 8
References list for a new article, constructed from the references and article
ID of the current article.
.Ip %s 8
Subject, with all Re's and (nf)'s stripped off.
.Ip %S 8
Subject, with one \*(L"Re:\*(R" stripped off.
.Ip %t 8
\*(L"To:\*(R" line derived from the \*(L"From:\*(R" and \*(L"Reply-To:\*(R"
lines of the current article.
This always returns an Internet format address.
.Ip %T 8
\*(L"To:\*(R" line derived constructed from the \*(L"Path:\*(R" line of the
current article to produce a uucp path.
.Ip %u 8
The number of unread articles in the current newsgroup.
.Ip %U 8
The number of unread articles in the current newsgroup, not counting the
current article.
.Ip %x 8
The news library directory.
.Ip %X 8
The rn library directory.
.Ip %~ 8
Your home directory.
.Ip %. 8
The directory containing your dot files, which is your home directory unless
the environment variable DOTDIR is defined when rn is invoked.
.Ip %$ 8
Current process number.
.Ip %/ 8
Last search string.
.Ip %% 8
A percent sign.
.Ip "%{name} or %{name\-default}" 8
The environment variable \*(L"name\*(R".
.Ip %[name] 8
The value of header line \*(L"Name:\*(R" from the current article.
The \*(L"Name:\ \*(R" is not included.
For example \*(L"%D\*(R" and \*(L"%[distribution]\*(R" are equivalent.
The name must be spelled out in full.
.Ip "%(test_text=pattern?then_text:else_text)" 8
If
.I test_text
matches
.IR pattern ,
has the value
.IR then_text ,
otherwise
.IR else_text .
The \*(L":else_text\*(R" is optional, and if absent, interpolates the null string.
The = may be replaced with != to negate the test.
To quote any of the metacharacters
(\*(L'=\*(R', \*(L'?\*(R', \*(L':\*(R', or \*(L')\*(R'),
precede with a backslash.
.Ip %digit 8
The digits 1 through 9 interpolate the string matched by the nth bracket
in the last pattern match that had brackets.
If the last pattern had alternatives, you may not know the number of the
bracket you want\*(--%0 will give you the last bracket matched.
.PP
Modifiers: to capitalize the first letter, insert \*(L'^\*(R':
\*(L"%^C\*(R" produces something like \*(L"Net.jokes\*(R".
Inserting \*(L'`\*(R' causes the first letter following the last
\&\*(L'/\*(R' to be capitalized: \*(L"%`c\*(R" produces \*(L"net/Jokes\*(R".
.SH ENVIRONMENT
The following environment variables are paid attention to by
.IR rn .
In general the default values assumed for these variables by
.I rn
are reasonable, so if you are using
.I rn
for the first time, you can safely ignore this section.
.PP
Those variables marked (%) are subject to % interpolation, and those marked
(~) are subject to both % interpolation and ~ interpretation.
.Ip "ATTRIBUTION (%)" 8
Gives the format of the attribution line in front of the quoted article
included by an F command.
.Sp
Default: In article %i %f writes:
.Ip "CANCEL (~)" 8
The shell command used to cancel an article.
.Sp
Default: inews \-h < %h
.Ip "CANCELHEADER (%)" 8 13v
The format of the file to pass to the CANCEL command in order to cancel
an article.
.Sp
Default:
.br
Newsgroups: %n
.br
Subject: cmsg cancel %i
.br
References: %R
.br
Reply-To: %L@%H.UUCP (%N)
.br
Distribution: %D
.br
Organization: %o
.sp 1
%i cancelled from rn.
.Ip DOTDIR 8
Where to find your dot files, if they aren't in your home directory.
Can be interpolated using \*(L"%.\*(R".
.Sp
Default: $HOME
.Ip "EDITOR (~)" 8
The name of your editor, if VISUAL is undefined.
.Sp
Default: whatever your news administrator compiled in, usually vi.
.Ip "FIRSTLINE (%)" 8
Controls the format of the line displayed at the top of an article.
Warning: this may go away.
.Sp
Default: Article %a %(%U%M!=^00$?(%U more%(%M!=^0$? + %M Marked to return)\e) )in %C:, more or less.
.Ip HOME 8
Your home directory.
Affects ~ interpretation, and the location of your
dot files if DOTDIR is not defined.
.Sp
Default: $LOGDIR
.Ip "KILLGLOBAL (~)" 8
Where to find the KILL file to apply to every newsgroup.
See the \*(L'^K\*(R' command at the newsgroup selection level.
.Sp
Default: %p/KILL
.Ip "KILLLOCAL (~)" 8
Where to find the KILL file for the current newsgroup.
See the commands \*(L'K\*(R' and \*(L'^K\*(R' at the article selection level,
and the search modifier \*(L'K\*(R'.
.Sp
Default: %p/%c/KILL
.Ip LOGDIR 8
Your home directory if HOME is undefined.
Affects ~ interpretation, and the location of your
dot files if DOTDIR is not defined.
.Sp
Default: none.
.Sp
Explanation: you must have either $HOME or $LOGDIR.
.Ip LOGNAME 8
Your login name, if USER is undefined.
May be interpolated using \*(L"%L\*(R".
.Sp
Default: value of getlogin().
.Ip "MAILFILE (~)" 8
Where to check for mail.
.Sp
Default: /usr/spool/mail/%L
.Ip "MAILHEADER (%)" 8
The format of the header file for replies.
See also MAILPOSTER.
.Sp
Default:
.Sp
To: %T
.br
Subject: Re: %S
.br
Newsgroups: %n
.br
In-Reply-To: %i
.br
%(%[references]!=^$?References\\: %[references]
.br
)Organization: %o
.br
Cc: 
.br
Bcc: \en\en
.Ip "MAILPOSTER (~)" 8
The shell command to be used by the reply commands (r and R)
in order to allow you to enter and deliver the response.
.I Rn
will not itself call upon an editor for replies\*(--this
is a function of the program called by
.IR rn .
See also MAILHEADER.
.Sp
Default: Rnmail \-h %h
.Ip "MBOXSAVER (~)" 8
The shell command to save an article in mailbox format.
.Sp
Default: %X/mbox.saver %A %P %c %a %B %C "%b" \e
.br
"From: %T %(%[posted]!=^$?%[posted]:\e
.br
%(%[date]=^\e(\ew*\e), \e(\ew*\e)-\e(\ew*\e)-\e(\ew*\e) \e([^ ]*\e)?\e
.br
%1 %3 %(%2=..?%2: %2) %5 19%4))"
.Sp
Explanation: the first seven arguments are the same as for NORMSAVER.
The eighth argument to the shell script is the new From: line
for the article, including the posting date,
derived either directly from the Posted: line, or not-so-directly from
the Date: line.
Header munging at its finest.
.Ip NAME 8
Your full name.
May be interpolated using \*(L"%N\*(R".
.Sp
Default: name from /etc/passwd, or ~/.fullname.
.Ip "NEWSHEADER (%)" 8 16v
The format of the header file for followups.
See also NEWSPOSTER.
.Sp
Default:
.Sp
Newsgroups: %F
.br
Subject: Re: %S
.br
Summary:
.br
Expires: 
.br
References: %R
.br
Sender: 
.br
Reply-To: %L@%H.UUCP (%N)
.br
Followup-To: 
.br
Distribution: %D
.br
Organization: %o
.br
Keywords: \en\en
.Ip "NEWSPOSTER (~)" 8
The shell command to be used by the followup commands (f and F)
in order to allow you to enter and post a followup news article.
.I Rn
will not itself call upon an editor for followups\*(--this
is a function of the program called by
.IR rn .
See also NEWSHEADER.
.Sp
Default: Pnews \-h %h
.Ip "NORMSAVER (~)" 8
The shell command to save an article in the normal (non-mailbox) format.
.Sp
Default: %X/norm.saver %A %P %c %a %B %C "%b"
.Ip ORGANIZATION 8
Either the name of your organization, or the name of a file containing the
name of your organization.
May be interpolated using \*(L"%o\*(R".
.Sp
Default: whatever your news administrator compiled in.
.Ip "PIPESAVER (%)" 8
The shell command to execute in order to accomplish a save to a pipe
(\*(L"s\ |\ command\*(R" or \*(L"w\ |\ command\*(R").
The command typed by the user is substituted in as %b.
.Sp
Default: %(%B=^0$?<%A:tail +%Bc %A |) %b
.Sp
Explanation: if %B is 0, the command is \*(L"<%A %b\*(R", otherwise
the command is \*(L"tail +%Bc %A | %b\*(R".
.Ip RNINIT 8
Default values for switches may be passed to
.I rn
by placing them in RNINIT.
Any switch that is set in RNINIT may be overruled 
on the command line, or via the \*(L'&\*(R' command from within
.IR rn .
Binary-valued switches that are set with \*(L"\-switch\*(R" may be unset
using \*(L"+switch\*(R".
.Sp
If RNINIT begins with a \*(L'/\*(R' it is assumed to be the name of a file
containing switches.
If you want to set many environment variables but don't want to keep
them all in your environment, or if the use of any of these variables
conflicts with other programs, you can use this feature along with the
.B \-E
switch to set the environment variables upon startup.
.Sp
Default: \*(L" \*(R".
.Ip "SAVEDIR (~)" 8
The name of the directory to save to, if the save command does not specify
a directory name.
.Sp
Default:
.br
   If
.B \-/
is set: %p/%c
.br
   If
.B +/
is set: %p
.Ip "SAVENAME (%)" 8
The name of the file to save to, if the save command contains only a
directory name.
.Sp
Default:
.br
   If
.B \-/
is set: %a
.br
   If
.B +/
is set: %^C
.Ip SHELL 8
The name of your preferred shell.
It will be used by the \*(L'!\*(R', \*(L'S\*(R' and \*(L'W\*(R' commands.
.Sp
Default: whatever your news administrator compiled in.
.Ip TERM 8
Determines which termcap entry to use, unless TERMCAP contains the entry.
.Ip TERMCAP 8
Holds either the name of your termcap file, or a termcap entry.
.Sp
Default: /etc/termcap, normally.
.Ip USER 8
Your login name.
May be interpolated using \*(L"%L\*(R".
.Sp
Default: $LOGNAME
.Ip "VISUAL (~)" 8
The name of your editor.
.Sp
Default: $EDITOR
.Ip "YOUSAID (%)" 8
Gives the format of the attribution line in front of the quoted article
included by an R command.
.Sp
Default: In article %i you write:
.SH AUTHOR
Larry Wall <lwall@sdcrdcf.UUCP>
.br
Regular expression routines are borrowed from emacs, by James Gosling.
.SH FILES
.Ip "%./.newsrc" 1.25i
status of your news reading
.Ip "%./.oldnewsrc" 1.25i
backup copy of your
.I .newsrc
from start of session
.Ip "%./.rnlock" 1.25i
lock file so you don't screw up your
.I .newsrc
.Ip "%./.rnlast" 1.25i
info from last run of rn
.Ip "%./.rnsoft" 1.25i
soft pointers into /usr/lib/active to speed startup, synchronous with
.I .newsrc
.Ip "%./.rnhead" 1.25i
temporary header file to pass to a mailer or news poster
.Ip "%p" 1.25i
your news save directory, usually ~/News
.Ip "%x/active" 1.25i
the list of active newsgroups, usually /usr/lib/news/active
.Ip "%P" 1.25i
the public news spool directory, usually /usr/spool/news
.SH SEE ALSO
newsrc(25), more(1), readnews(1), Pnews(1), Rnmail(1)
.SH DIAGNOSTICS
Generally self-documenting, as they say.
.SH BUGS
The
.B \-h
switch can only hide header lines that
.I rn
knows about.
.PP
The \*(L'\-\*(R' command doesn't cross newsgroup boundaries, and only undoes
the last article selection.
.PP
If you edit your
.I .newsrc
while
.I rn
is running,
.I rn
will happily wipe out your changes when it decides to
write out the
.I .newsrc
file.
.PP
.I Rn
doesn't do certain things (like ordering articles on posting date) that
the author feels should be handled by inews.
.PP
Marking of duplicate articles as read in cross-referenced newsgroups will
not work unless the Xref patch is installed in inews.
.PP
If you get carried away with % or escape substitutions, you can overflow
buffers.
.PP
There should be no fixed limit on the number of newsgroups.
.PP
Some of the more esoteric features may be missing on machines with limited
address space.
!STUFFY!FUNK!
echo Extracting bits.c
cat >bits.c <<'!STUFFY!FUNK!'
/* $Header: bits.c,v 4.1 84/09/24 11:43:17 lwall Exp $
 *
 * $Log:	bits.c,v $
 * Revision 4.1  84/09/24  11:43:17  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.7  84/09/21  15:49:29  lwall
 * New Xref format forced by newsgroup aliasing in 2.10.2.
 * 
 * Revision 4.0.1.6  84/09/14  15:16:14  lwall
 * Fixed nasty dangling else.
 * 
 * Revision 4.0.1.5  84/09/13  12:02:20  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.4  84/09/13  11:19:51  lwall
 * addartnum() now returns int.
 * 
 * Revision 4.0.1.3  84/09/10  15:06:49  lwall
 * Delinted.
 * 
 * Revision 4.0.1.2  84/09/06  17:00:58  lwall
 * Implemented newsgroup exit commands.
 * 
 * Revision 4.0.1.1  84/09/06  08:15:36  lwall
 * Include artio.h and interp.h.
 * 
 * Revision 4.0  84/09/04  09:49:52  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rcstuff.h"
#include "head.h"
#include "util.h"
#include "final.h"
#include "rn.h"
#include "cheat.h"
#include "ng.h"
#include "artio.h"
#include "intrp.h"
#include "ngdata.h"
#include "rcln.h"
#include "kfile.h"
#include "INTERN.h"
#include "bits.h"

MEM_SIZE ctlsize;			/* size of bitmap in bytes */

void
bits_init()
{
#ifdef DELAYMARK
    dmname = savestr(filexp(RNDELNAME));
#else
    ;
#endif
}

/* checkpoint the .newsrc */

void
checkpoint_rc()
{
#ifdef DEBUGGING
    if (debug & DEB_CHECKPOINTING) {
	fputs("(ckpt)",stdout);
	fflush(stdout);
    }
#endif
    if (doing_ng)
	restore_ng();			/* do not restore M articles */
    if (rc_changed)
	write_rc();
#ifdef DEBUGGING
    if (debug & DEB_CHECKPOINTING) {
	fputs("(done)",stdout);
	fflush(stdout);
    }
#endif
}

/* reconstruct the .newsrc line in a human readable form */

void
restore_ng()
{
    register char *s, *mybuf = buf;
    register ART_NUM i;
    ART_NUM count=0;
    int safelen = LBUFLEN - 16;

    strcpy(buf,rcline[ng]);		/* start with the newsgroup name */
    s = buf + rcnums[ng] - 1;		/* use s for buffer pointer */
    *s++ = rcchar[ng];			/* put the requisite : or !*/
    *s++ = ' ';				/* put the not-so-requisite space */
    for (i=1; i<=lastart; i++) {	/* for each article in newsgroup */
	if (s-mybuf > safelen) {	/* running out of room? */
	    safelen *= 2;
	    if (mybuf == buf) {		/* currently static? */
		*s = '\0';
		mybuf = safemalloc((MEM_SIZE)safelen + 16);
		strcpy(mybuf,buf);	/* so we must copy it */
		s = mybuf + (s-buf);
					/* fix the pointer, too */
	    }
	    else {			/* just grow in place, if possible */
		char *newbuf;

		newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16);
		s = newbuf + (s-mybuf);
		mybuf = newbuf;
	    }
	}
	/*NOSTRICT*/
	if (!was_read(i))		/* still unread? */
	    count++;			/* then count it */
	else {				/* article was read */
	    ART_NUM oldi;

	    sprintf(s,"%ld",(long)i);	/* put out the min of the range */
	    s += strlen(s);		/* keeping house */
	    oldi = i;			/* remember this spot */
	    /*NOSTRICT*/
	    do i++; while (i <= lastart && was_read(i));
					/* find 1st unread article or end */
	    i--;			/* backup to last read article */
	    if (i > oldi) {		/* range of more than 1? */
		sprintf(s,"-%ld,",(long)i);
					/* then it out as a range */
		s += strlen(s);		/* and housekeep */
	    }
	    else
		*s++ = ',';		/* otherwise, just a comma will do */
	}
    }
    if (*(s-1) == ',')			/* is there a final ','? */
	s--;				/* take it back */
    *s++ = '\0';			/* and terminate string */
#ifdef DEBUGGING
    if (debug & DEB_NEWSRC_LINE && !panic) {
	printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]);
	printf("%s\n",mybuf);
    }
#endif
    free(rcline[ng]);			/* return old rc line */
    if (mybuf = buf) {
	rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1);
					/* grab a new rc line */
	strcpy(rcline[ng], buf);	/* and load it */
    }
    else {
	mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
					/* be nice to the heap */
	rcline[ng] = mybuf;
    }
    *(rcline[ng] + rcnums[ng] - 1) = '\0';
    if (rcchar[ng] == NEGCHAR) {	/* did they unsubscribe? */
	printf(unsubto,ngname);
	toread[ng] = TR_UNSUB;		/* make line invisible */
    }
    else
	/*NOSTRICT*/
	toread[ng] = (ART_UNREAD)count;		/* remember how many unread there are */
}

/* mark an article unread, keeping track of toread[] */

void
onemore(artnum)
ART_NUM artnum;
{
    /*NOSTRICT*/
    if (ctl_read(artnum)) {
	/*NOSTRICT*/
	ctl_clear(artnum);
	++toread[ng];
    }
}

/* mark an article read, keeping track of toread[] */

void
oneless(artnum)
ART_NUM artnum;
{
    /*NOSTRICT*/
    if (!ctl_read(artnum)) {
	/*NOSTRICT*/
	ctl_set(artnum);
	if (toread[ng] > TR_NONE)
	    --toread[ng];
    }
}

/* mark an article as unread, making sure that firstart is properly handled */
/* cross-references are left as read in the other newsgroups */

void
unmark_as_read(artnum)
ART_NUM artnum;
{
    check_first(artnum);
    onemore(artnum);
#ifdef MCHASE
    if (!parse_maybe(artnum))
	chase_xrefs(artnum,FALSE);
#endif
}

#ifdef DELAYMARK
/* temporarily mark article as read.  When newsgroup is exited, articles */
/* will be marked as unread.  Called via M command */

void
delay_unmark(artnum)
ART_NUM artnum;
{
    if (!dmfp)
	dmfp = fopen(dmname,"w");
    oneless(artnum);			/* set the correct bit */
    dmcount++;
    fprintf(dmfp,"%ld\n",(long)artnum);
}
#endif

/* mark article as read.  If article is cross referenced to other */
/* newsgroups, mark them read there also. */

void
mark_as_read(artnum)
ART_NUM artnum;
{
    oneless(artnum);			/* set the correct bit */
    checkcount++;			/* get more worried about crashes */
    chase_xrefs(artnum,TRUE);
}

/* make sure we have bits set correctly down to firstart */

void
check_first(min)
ART_NUM min;
{
    register ART_NUM i = firstart;

    if (min < absfirst)
	min = absfirst;
    if (min < i) {
	for (i--; i>=min; i--)
	    /*NOSTRICT*/
	    ctl_set(i);		/* mark as read */
	firstart = min;
    }
}

/* bring back articles marked with M */

#ifdef DELAYMARK
void
yankback()
{
    register ART_NUM anum;

    if (dmfp) {			/* delayed unmarks pending? */
#ifdef VERBOSE
	printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
	    dmcount == 1 ? nullstr : "s");
#endif
	fclose(dmfp);
	if (dmfp = fopen(dmname,"r")) {
	    while (fgets(buf,sizeof buf,dmfp) != Nullch) {
		anum = (ART_NUM)atol(buf);
		/*NOSTRICT*/
		onemore(anum);             /* then unmark them */
#ifdef MCHASE
		chase_xrefs(anum,FALSE);
#endif
	    }
	    fclose(dmfp);
	    dmfp = Nullfp;
	    UNLINK(dmname);		/* and be tidy */
	}
    }
    dmcount = 0;
}
#endif
    
/* run down xref list and mark as read or unread */

int
chase_xrefs(artnum,markread)
ART_NUM artnum;
int markread;
{
#ifdef ASYNC_PARSE
    if (parse_maybe(artnum))		/* make sure we have right header */
	return -1;
#endif
    if (
#ifdef DEBUGGING
	debug & DEB_FEED_XREF ||
#endif
	htype[XREF_LINE].ht_minpos >= 0) {
					/* are there article# xrefs? */
	char *xref_buf, *curxref;
	register char *xartnum;
	register ART_NUM x;
	char tmpbuf[128];

#ifdef DEBUGGING
	if (htype[XREF_LINE].ht_minpos >= 0)
#endif
	    xref_buf = fetchlines(artnum,XREF_LINE);
					/* get xrefs list */
#ifdef DEBUGGING
	else {
	    xref_buf = safemalloc((MEM_SIZE)100);
	    printf("Give Xref: ");
	    gets(xref_buf);
	}
#endif
#ifdef DEBUGGING
	if (debug & DEB_XREF_MARKER)
	    printf("Xref: %s\n",xref_buf);
#endif
	curxref = cpytill(tmpbuf,xref_buf,' ');
	if (strEQ(tmpbuf,sitename))
	    curxref++;
	else {
#ifdef DEBUGGING
	    if (debug)
		printf("Xref not from this system--ignoring\n");
#endif
	    goto wild_goose;
	}
	while (*curxref) {
					/* for each newsgroup */
	    curxref = cpytill(tmpbuf,curxref,' ');
	    xartnum = index(tmpbuf,':');
	    if (!xartnum)		/* probably an old-style Xref */
		break;
	    *xartnum++ = '\0';
	    if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */
		x = atol(xartnum);
		if (x)
		    if (markread) {
			if (addartnum(x,tmpbuf))
			    goto wild_goose;
		    }
#ifdef MCHASE
		    else
			subartnum(x,tmpbuf);
#endif
	    }
	    while (*curxref && isspace(*curxref))
		curxref++;
	}
      wild_goose:
	free(xref_buf);
    }
    return 0;
}

int
initctl()
{
    char *mybuf = buf;			/* place to decode rc line */
    register char *s, *c, *h;
    register long i;
    
#ifdef DELAYMARK
    dmcount = 0;
#endif
    if ((lastart = getngsize(ng)) < 0)	/* this cannot happen (laugh here) */
	return -1;

    absfirst = getabsfirst(ng,lastart);	/* remember first existing article */
    if (!absfirst)			/* no articles at all? */
	absfirst = 1;			/* pretend there is one */
    /*NOSTRICT*/
    ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20);
    ctlarea = safemalloc(ctlsize);	/* allocate control area */

    /* now modify ctlarea to reflect what has already been read */

    for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ;
					/* find numbers in rc line */
    i = strlen(s);
    if (i >= LBUFLEN-2)			/* bigger than buf? */
	/*NOSTRICT*/
	mybuf = safemalloc((MEM_SIZE)(i+2));
    strcpy(mybuf,s);			/* make scratch copy of line */
    mybuf[i++] = ',';			/* put extra comma on the end */
    mybuf[i] = '\0';
    s = mybuf;				/* initialize the for loop below */
    if (strnEQ(s,"1-",2)) {		/* can we save some time here? */
	firstart = atol(s+2)+1;		/* ignore first range thusly */
	s=index(s,',') + 1;
    }
    else
	firstart = 1;			/* all the bits are valid for now */
    if (absfirst > firstart) {		/* do we know already? */
	toread[ng] -= (ART_UNREAD)(absfirst - firstart);
	firstart = absfirst;		/* no point calling getngmin again */
    }
    else if (artopen(firstart) == Nullfp) {
					/* first unread article missing? */
	i = getngmin(".",firstart);	/* see if expire has been busy */
	if (i) {			/* avoid a bunch of extra opens */
	    toread[ng] -= (ART_UNREAD)(i - firstart);
	    firstart = i;
	}
    }
#ifdef PENDING
#   ifdef CACHESUBJ
	subj_to_get = firstart;
#   endif
#endif
    for (i=OFFSET(firstart)/BITSPERBYTE; i<ctlsize; i++)
	ctlarea[i] = 0;			/* assume unread */
#ifdef DEBUGGING
    if (debug & DEB_CTLAREA_BITMAP) {
	printf("\n%s\n",mybuf);
	for (i=1; i <= lastart; i++)
	    /*NOSTRICT*/
	    if (! was_read(i))
		printf("%ld ",(long)i);
    }
#endif
    for ( ; (c = index(s,',')) != Nullch; s = ++c) {
					/* for each range */
	ART_NUM min, max;

	*c = '\0';			/* do not let index see past comma */
	if ((h = index(s,'-')) != Nullch) {	/* is there a -? */
	    min = atol(s);
	    max = atol(h+1);
	    if (min < firstart)		/* make sure range is in range */
		min = firstart;
	    if (max > lastart)
		max = lastart;
	    for (i=min; i<=max; i++)	/* for all articles in range */
		/*NOSTRICT*/
		ctl_set(i);		/* mark them read */
	}
	else if ((i = atol(s)) >= firstart && i <= lastart)
					/* is single number reasonable? */
	    /*NOSTRICT*/
	    ctl_set(i);			/* mark it read */
#ifdef DEBUGGING
	if (debug & DEB_CTLAREA_BITMAP) {
	    printf("\n%s\n",s);
	    for (i=1; i <= lastart; i++)
		/*NOSTRICT*/
		if (! was_read(i))
		    printf("%ld ",(long)i);
	}
#endif
    }
#ifdef DEBUGGING
    if (debug & DEB_CTLAREA_BITMAP) {
	fputs("\n(hit CR)",stdout);
	gets(cmd_buf);
    }
#endif
    if (mybuf != buf)
	free(mybuf);
    return 0;
}

void
grow_ctl()
{
    ART_NUM newlast;
    ART_NUM tmpfirst;
    MEM_SIZE newsize;
    register ART_NUM i;

    forcegrow = FALSE;
    newlast = getngsize(ng);
    if (newlast > lastart) {
	ART_NUM tmpart = art;
	/*NOSTRICT*/
	newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2);
	if (newsize > ctlsize) {
	    newsize += 20;
	    ctlarea = saferealloc(ctlarea,newsize);
	    ctlsize = newsize;
	}
	toread[ng] += (ART_UNREAD)(newlast-lastart);
	for (i=lastart+1; i<=newlast; i++)
	    /*NOSTRICT*/
	    ctl_clear(i);	/* these articles are unread */
#ifdef CACHESUBJ
	if (subj_list != Null(char**)) {
	    /*NOSTRICT*/
	    subj_list = (char**)saferealloc((char*)subj_list,
		  (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) );
	    for (i=lastart+1; i<=newlast; i++)
		subj_list[OFFSET(i)] = Nullch;
	}
#endif
	tmpfirst = lastart+1;
	lastart = newlast;
#ifdef KILLFILES
#ifdef VERBOSE
	IF(verbose)
	    sprintf(buf,
		"%ld more article%s arrived--looking for more to kill...\n\n",
		(long)(lastart - firstart + 1),
		(lastart > firstart ? "s have" : " has" ) );
	ELSE			/* my, my, how clever we are */
#endif
#ifdef TERSE
	    strcpy(buf, "More news--killing...\n\n");
#endif
	kill_unwanted(tmpfirst,buf,TRUE);
#endif
	art = tmpart;
    }
}

!STUFFY!FUNK!
echo Extracting makedist
cat >makedist <<'!STUFFY!FUNK!'
#!/bin/sh
# $Header: makedist,v 4.1 84/09/24 11:59:46 lwall Exp $
#
# $Log:	makedist,v $
# Revision 4.1  84/09/24  11:59:46  lwall
# Real baseline.
# 
# Revision 4.0  84/09/04  09:51:22  lwall
# Baseline for netwide release
# 

rm -f kit*.list
manifake
kitlists
manimake
makekit kit*.list
!STUFFY!FUNK!
echo ""
echo "End of kit 1 (of 8)"
cat /dev/null >kit1isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 2 (of 8).  If kit 2 is complete, the line"
echo '"'"End of kit 2 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting Configure
cat >Configure <<'!STUFFY!FUNK!'
#! /bin/sh
#
# If these # comments don't work, trim them.  Don't worry about the other
# shell scripts, Configure will trim # comments from them for you.
#
# $Header: Configure,v 4.1 84/09/24 11:19:19 lwall Exp $
#
# $Log:	Configure,v $
# Revision 4.1  84/09/24  11:19:19  lwall
# Real baseline.
# 
# Revision 4.0.1.7  84/09/22  16:53:46  lwall
# 2.10.2 inews moved.
# 
# Revision 4.0.1.6  84/09/18  15:51:08  lwall
# 2.10.2 tweaks.
# 
# Revision 4.0.1.5  84/09/14  17:31:45  lwall
# Compensate for greps that do not return status.
# 
# Revision 4.0.1.4  84/09/12  17:50:42  lwall
# Check for sh interpretation.
# Revised ndir checker.
# Qualified test now used only if in /bin, and test is not built in to sh.
# Various anti-brain-damage measures.
# 
# Revision 4.0.1.3  84/09/06  13:46:29  lwall
# Now figures out where manual pages go.
# 
# Revision 4.0.1.2  84/09/05  16:06:28  lwall
# Now determines how to invoke C preprocessor.
# 
# Revision 4.0.1.1  84/09/04  12:02:56  lwall
# Allow recmail on usg systems.
# Fixed $lib lookup.
# 
# Revision 4.0  84/09/04  09:48:13  lwall
# Baseline for netwide release
# 

# Yes, you may rip this off to use in other distribution packages.

echo "Beginning of configuration questions for rn kit."
echo ""

: sanity checks
PATH='.:/bin:/usr/bin:/usr/local/bin:/usr/ucb:/usr/local:/etc'
export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh $0; kill $$)

if test ! -t 0; then
    echo "Say 'sh Configure', not 'sh <Configure'"
    exit 1
fi

: some greps do not return status, grrr.
echo "grimblepritz" >grimble
if grep blurfldyick grimble >/dev/null 2>&1 ; then
    contains=contains
else
    if grep grimblepritz grimble >/dev/null 2>&1 ; then
	contains=grep
    else
	contains=contains
    fi
fi
rm grimble
: the following should work in any shell
case $contains in
contains*)
    echo ""
    echo "AGH!  Grep doesn't return a status.  Attempting remedial action."
    cat >contains <<'EOSS'
grep "$1" "$2" >.greptmp && cat .greptmp && test -s .greptmp
EOSS
chmod 755 contains
esac

: first determine how to suppress newline on echo command
echo "Checking echo to see how to suppress newlines..."
(echo "hi there\c" ; echo "") >.echotmp
if $contains c .echotmp >/dev/null 2>&1 ; then
    echo "...using -n."
    n='-n'
    c=''
else
    echo "...using \\\c."
    n=''
    c='\c'
fi
echo $n "Type carriage return to continue.  Your cursor should be here-->$c"
read ans
rm .echotmp

: general instructions
cat <<'EOH'

This installation shell script will examine your system and ask you questions
to determine how rn and its auxiliary files should be installed.  If you
get stuck on a question, you can interrupt and start over without fear of
getting messed up.  Many of the questions will have default answers in
square brackets--typing carriage return will give you the default.

On some of the questions which ask for file or directory names you are
allowed to use the ~name construct to specify the login directory belonging
to "name", even if you don't have a shell which knows about that.  Questions
where this is allowed will be marked "(~name ok)".

Much effort has been expended to ensure that this shell script will run
on any Unix system.  If despite that it blows up on you, your best bet is
to edit Configure and run it again.  (Trying to install rn without having run
Configure is well nigh impossible.)  Also, let me (lwall@sdcrdcf.UUCP) know
that I blew it.

This installation script affects things in two ways: 1) it does direct
variable substitutions on some of the files included in this kit, and
2) it builds a config.h file for inclusion in C programs.  You may edit
any of these files as the need arises after running this script.

EOH
echo $n "[Type carriage return to continue] $c"
read ans

: get list of predefined functions in a handy place
echo ""
if test -f /lib/libc.a; then
    echo "Your C library is in /lib/libc.a.  You're normal."
    libc=/lib/libc.a
else
    if test -f /usr/lib/libc.a; then
	echo "Your C library is in /usr/lib/libc.a, of all places."
	libc=/usr/lib/libc.a
    else
	cat <<'EOM'

I can't seem to find your C library.  I've looked for /lib/libc.a and
/usr/lib/libc.a, but neither of those are there.  What is the full name
EOM
	echo $n "of your C library? $c"
	read libc
    fi
fi
echo ""
echo $n "Extracting names from $libc for later perusal...$c"
if ar t $libc > libc.list; then
    echo "done"
else
    echo ""
    echo "The archiver doesn't think $libc is a reasonable library."
    exit 1
fi

: make some quick guesses about what we are up against
echo ""
echo $n "Hmm...  $c"
if $contains SIGTSTP /usr/include/signal.h >/dev/null 2>&1 ; then
    echo "Looks kind of like a BSD system, but we'll see..."
    echo exit 0 >bsd
    echo exit 1 >usg
    echo exit 1 >v7
else
    if $contains fcntl.o libc.list >/dev/null 2>&1 ; then
	echo "Looks kind of like a USG system, but we'll see..."
	echo exit 1 >bsd
	echo exit 0 >usg
	echo exit 1 >v7
    else
	echo "Looks kind of like a version 7 system, but we'll see..."
	echo exit 1 >bsd
	echo exit 1 >usg
	echo exit 0 >v7
    fi
fi
if $contains vmssystem.o libc.list >/dev/null 2>&1 ; then
    cat <<'EOI'
There is, however, a strange, musty smell in the air that reminds me of
something...hmm...yes...I've got it...there's a VMS nearby, or I'm a Blit.
EOI
    echo "exit 0" >eunice
    eunicefix=unixtovms
    eunice=define
: it so happens the Eunice I know will not run shell scripts in Unix format
else
    echo ""
    echo "Congratulations.  You aren't running Eunice."
    eunicefix=':'
    eunice=undef
    echo "exit 1" >eunice
fi
chmod 755 bsd usg v7 eunice
$eunicefix bsd usg v7 eunice

: see how we invoke the C preprocessor
echo ""
echo "Checking to see how your C preprocessor is invoked..."
cat <<'EOT' >testcpp.c
#define ABC abc
#define XYZ xyz
ABC+XYZ
EOT
echo 'Maybe "cc -E" will work...'
cc -E testcpp.c >testcpp.out 2>&1
if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then
    echo "Yup, it does."
    cpp='cc -E'
else
    echo 'Nope...maybe "cc -P" will work...'
    cc -P testcpp.c >testcpp.out 2>&1
    if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then
	echo "Yup, that does."
	cpp='cc -P'
    else
	echo 'Nope...maybe "/lib/cpp" will work...'
	/lib/cpp testcpp.c >testcpp.out 2>&1
	if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then
	    echo "Hooray, it works!  I was beginning to wonder."
	    cpp='/lib/cpp'
	else
	    echo "I can't find a C preprocessor.  Name one: $c"
	    read cpp
	    $cpp testcpp.c >testcpp.out 2>&1
	    if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then
		echo "OK, that will do."
	    else
		echo "Sorry, I can't get that to work.  Go find one."
		exit 1
	    fi
	fi
    fi
fi
rm -f testcpp.c testcpp.out

: check for pdp11
echo ""
if usg; then
    : pdp11 is already defined
else
    cat <<'EOT' >pdp11.c
#ifdef pdp11
exit 0
#else
exit 1
#endif
EOT
    $cpp pdp11.c | grep exit >pdp11
    chmod 755 pdp11
    $eunicefix pdp11
    rm pdp11.c
fi
if pdp11; then
    echo "This looks like a pdp11 to me."
else
    echo "This doesn't look like a pdp11 to me."
fi

: see if sh knows # comments
echo ""
echo "Checking your sh to see if it knows about # comments..."
if sh -c '#' >/dev/null 2>&1 ; then
    echo "Your sh handles # comments correctly."
    shsharp=true
    spitshell=cat
    echo ""
    echo "Okay, let's see if #! works on this system..."
    echo "#!/bin/date" > try
    $eunicefix try
    chmod 755 try
    try > today
    if test -s today; then
	echo "It does."
	sharpbang='#!'
    else
	echo "#! /bin/date" > try
	$eunicefix try
	chmod 755 try
	try > today
	if test -s today; then
	    echo "It does."
	    sharpbang='#! '
	else
	    echo "It doesn't."
	    sharpbang=': use '
	fi
    fi
else
    echo "Your sh doesn't grok # comments--I will strip them later on."
    shsharp=false
    echo "exec grep -v '^#'" >spitshell
    chmod 755 spitshell
    $eunicefix spitshell
    spitshell=`pwd`/spitshell
    echo "I presume that if # doesn't work, #! won't work either!"
    sharpbang=': use '
fi

: figure out how to guarantee sh startup
echo ""
echo "Checking out how to guarantee sh startup..."
startsh=$sharpbang'/bin/sh'
echo "Let's see if '$startsh' works..."
cat >try <<EOSS
$startsh
set abc
test "$?abc" != 1
EOSS

chmod 755 try
$eunicefix try
if try; then
    echo "Yup, it does."
else
    echo "Nope.  You may have to fix up the shell scripts to make sure sh runs them."
fi
rm -f try today

: find out where common programs are
echo ""
echo "Locating common programs..."
cat <<EOSC >loc
$startsh
for dir in /usr/ucb /bin /usr/bin /usr/local /usr/local/bin /etc /usr/lib
do
    if test -f \$dir/\$1; then
	echo \$dir/\$1
	echo \$1 is in \$dir > /dev/tty
	exit
    fi
done
echo \$1
echo "I don't know where \$1 is" >/dev/tty
EOSC
chmod 755 loc
$eunicefix loc
loclist="test expr sed echo cat rm mv cp tail tr mkdir sort uniq inews grep egrep"
for file in $loclist; do
    eval $file=`loc $file`
done
case $egrep in
egrep)
    $echo "Substituting grep for egrep."
    egrep=$grep
    ;;
esac
case $test in
test)
    echo "Hopefully test is built into your sh."
    ;;
/bin/test)
    echo ""
    echo $n 'Is your "test" built into sh? [n] (OK to guess) '"$c"
    read ans
    case $ans in
    y*) test=test ;;
    esac
    ;;
*)
    test=test
    ;;
esac

: decide how portable to be
$cat <<'EOH'

I can set things up so that your shell scripts and binaries are more portable,
at a noticable cost in performance.  Do you expect to run these scripts and
EOH
$echo $n "binaries on multiple machines? [n] $c"
read ans
case $ans in
    y*) portable=define
	for file in $loclist; do
	    eval $file=$file
	done
	;;
    *)  portable=undef ;;
esac

: set up shell script to do ~ expansion
cat >filexp <<EOSS
$startsh
: expand filename
case \$1 in
~/*|~)
    $echo \$1 | $sed "s/~/\${HOME-\$LOGDIR}/"
    ;;
~*)
    if $test -f /bin/csh; then
	/bin/csh -f -c "glob \$1"
	$echo ""
    else
	name=\`$expr x\$1 : '..\([^/]*\)'\`
	dir=\`$sed </etc/passwd -n -e "/^\${name}:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\).*"'\$'"/\1/" -e p -e q -e '}'\`
	if $test ! -d "\$dir"; then
	    me=\`basename \$0\`
	    $echo "\$me: can't locate home directory for: \$name" >&2
	    exit 1
	fi
	case \$1 in
	*/*)
	    $echo \$dir/\`$expr x\$1 : '..[^/]*/\(.*\)'\`
	    ;;
	*)
	    $echo \$dir
	    ;;
	esac
    fi
    ;;
*)
    $echo \$1
    ;;
esac
EOSS
chmod 755 filexp
$eunicefix filexp

: now get the site name
$echo ""
hostcmd=''
$echo "Figuring out site name..."
$echo 'Maybe "hostname" will work...'
if ans=`sh -c hostname 2>&1` ; then
    sitename=$ans
    hostcmd=hostname
else
    $echo 'No, maybe "uuname -l" will work...'
    if ans=`sh -c 'uuname -l' 2>&1` ; then
	sitename=$ans
	hostcmd='uuname -l'
    else
	$echo 'Strange.  Maybe "uname -n" will work...'
	if ans=`sh -c 'uname -n' 2>&1` ; then
	    sitename=$ans
	    hostcmd='uname -n'
	else
	    $echo 'Oh well, maybe I can mine it out of whoami.h...'
	    if ans=`sh -c $contains' sysname /usr/include/whoami.h' 2>&1` ; then
		sitename=`$echo "$ans" | $sed 's/^.*"\(.*\)"/\1/'`
		hostcmd="sed -n -e '"'/sysname/s/^.*\"\\(.*\\)\"/\1/{'"' -e p -e q -e '}' </usr/include/whoami.h"
	    else
		echo "Does this machine have an identity crisis or something?"
		sitename=''
	    fi
	fi
    fi
fi
: you do not want to know about this
set $sitename
sitename=$1

: translate upper to lower if necessary
case $sitename in
    *[A-Z]*)
	sitename=`$echo $sitename | tr [A-Z] [a-z]`
	$echo "(Normalizing case in your site name)"
	;;
esac

: verify guess
if $test "$sitename" ; then
    $echo 'Your site name appears to be "'$sitename'".'
    $echo $n "Is this correct? [y] $c"
    read ans
    case $ans in
      y*|'')  ;;
      *)      sitename='' ;;
    esac
fi

: bad guess or no guess
while $test "X$sitename" = X ; do
    $echo $n "Please type the (one word) name of your site: $c"
    read sitename
    case $hostcmd in
    sed*)
	$echo "(That doesn't agree with your whoami.h file, by the way.)"
	;;
    *)
	$echo "(That doesn't agree with your $hostcmd command, by the way.)"
	;;
    esac
    hostcmd=''
done

: get organizaton name
$cat << 'EOH'

Please type the name of your organization as you want it to appear on the
Organization line of outgoing articles.  (It's nice if this also specifies
your location.  Your city name is probably sufficient if well known.)
For example: "University of Southern North Dakota, Hoople"
EOH
$echo $n "Organization: $c"
read orgname

: get news administrator name
if $contains "^news:" /etc/passwd >/dev/null 2>&1 ; then
    dflt=news
else
    if $contains "^usenet:" /etc/passwd >/dev/null 2>&1 ; then
	dflt=news
    else
	if eunice; then
	    dflt=system
	else
	    dflt=root
	fi
    fi
fi
cat <<'EOM'

Many systems keep their news in a private directory, or have a non-superuser
in charge of administering news.  What is the login name (not directory) that
EOM
echo $n "is used for news administration? [$dflt] $c"
read newsadmin
case $newsadmin in
'') newsadmin=$dflt ;;
esac
case $newsadmin in
root) isadmin=undef ;;
*)    isadmin=define ;;
esac

: figure out news library
echo ""
lib=/usr/lib/news
libexp=$lib
while $test ! -d $libexp; do
    $echo ""
    $echo $n "$libexp not found--where is the news library? (~name ok) $c"
    read lib
    case $lib in
    ~*)
	libexp=`filexp $lib`
	echo "(That is $libexp on this particular system.)"
	case $portable in
	  undef) lib=$libexp ;;
	esac
	;;
    *)
	libexp=$lib
	;;
    esac
done
$echo "LIB = $lib"
if $test -f $libexp/inews; then
    echo "Aha!  Inews is really in $libexp!  Maybe this is 2.10.2..."
    case $inews in
    inews) 
	: null
	;;
    *)
	echo "(Make sure $inews isn't an old version.)"
	;;
    esac
    inews=$libexp/inews
fi

: find out how to find out full name
$echo ""
if bsd; then
    dflt=y
else
    dflt=n
fi
$echo "Does your /etc/passwd file keep full names in Berkeley format (name first"
$echo $n "thing after ':')? [$dflt] $c"
read ans
case $ans in
  '') ans=$dflt ;;
esac
case $ans in
  y*)
    passnam=define
    berknam=define
    usgnam=undef
    nametype=bsd
    ;;
  *)
    $echo ""
    if usg; then
	dflt=y
    else
	dflt=n
    fi
    $echo "Does your passwd file keep full names in USG format (name sandwiched"
    $echo $n "between a '-' and a '(')? [$dflt] $c"
    read ans
    case $ans in
      '') ans=$dflt ;;
    esac
    case $ans in
      n*)
	$echo "Full name will be taken from ~/.fullname"
	passnam=undef
	berknam=undef
	usgnam=undef
	nametype=other
	;;
      *)
	passnam=define
	berknam=undef
	usgnam=define
	nametype=usg
	;;
    esac
    ;;
esac

: see if we should throw a -i into the Makefile
$echo ""
if pdp11; then
    if usg; then
	ans=/usr/man/unix/man1/cc.1
    else
	ans=/usr/man/man1/cc.1
    fi
    if $contains '\-i' $ans >/dev/null 2>&1 ; then
	$echo $n "Your system appears to have separate I and D space.  Is this true? [y] $c"
	read ans
	case $ans in
	    n*|f*) iandd='' ;;
	    *)     iandd='-i' ;;
	esac
    else
	$echo "Your system appears to NOT have separate I and D space."
	$echo $n "Is this correct? [y] $c"
	read ans
	case $ans in
	    n*|f*) iandd='-i' ;;
	    *)     iandd='' ;;
	esac
    fi
else
    $echo "Not a pdp11--assuming no separate I and D."
fi

: index or strcpy
$echo ""
if $contains index.o libc.list >/dev/null 2>&1 ; then
    $echo "Your system appears to use index() and rindex() rather than strchr()"
    $echo $n "and strrchr().  Is this correct? [y] $c"
    read ans
    case $ans in
	n*|f*) strchr='define' ;;
	*)     strchr='undef' ;;
    esac
else
    $echo "Your system appears to use strchr() and strrchr() rather than index()"
    $echo $n "and rindex().  Is this correct? [y] $c"
    read ans
    case $ans in
	n*|f*) strchr=undef ;;
	*)     strchr=define ;;
    esac
fi

: determine how to determine when a file is a mailbox
$cat <<'EOM'

In saving articles, rn wants to differentiate between saving to mailbox
format files and normal files.  It does this by examining the first character
of the file in question.  On most systems the first line starts "From...",
so the first character is F.  On other systems there are magic cookies like
control codes between articles, so one of those would be first.  On your
system, if a file is in mailbox format, what is the first character of
EOM
echo $n "that file? [F] $c"
read mboxchar
case $mboxchar in
'') mboxchar=F ;;
esac
case $mboxchar in
'F') ;;
*)  cat <<'EOM'
You will need to edit the shell script mbox.saver to properly append an
article to a mailbox.  The arguments to the script are documented in
EOM
    case $shsharp in
    false)
	echo "comments in mbox.saver.std."
	;;
    true)
	echo "comments in the shell script itself."
	;;
    esac
esac

: where do we get termlib routines from
$echo ""
if $test -r /usr/lib/libtermlib.a || $test -r /usr/local/lib/libtermlib.a ; then
    termlib='-ltermlib'
    havetlib=define
    $echo "Termlib library found."
else
    if $test -d /usr/lib/terminfo || $test -d /etc/term; then
	termlib='-lcurses'
	havetlib=define
	$echo "Terminfo library found."
    else
	if $test -r /usr/lib/libtermcap.a || $test -r /usr/local/lib/libtermcap.a ; then
	    termlib='-ltermcap'
	    havetlib=define
	    $echo "Termcap library found."
	else
	    $echo $n "Your system appears to NOT have termlib-style routines.  Is this true? [y] $c"
	    read ans
	    case $ans in
		n*|f*) havetlib=define
		      $echo "Then where are the termlib-style routines kept (specify either -llibname"
		      $echo $n " or full pathname (~name ok))? $c"
		      read ans
		      termlib=`filexp $ans`
		      ;;
		*)    havetlib=undef
		      termlib=''
		      $echo "You will have to play around with term.c then."
		      ;;
	    esac
	    $echo ""
	fi
    fi
fi

: see if there is a whoami file
if $test -r /usr/include/whoami.h ; then
    whoami=define
    $echo "whoami.h found."
else
    whoami=undef
fi

: see if this is a termio system
if $test -r /usr/include/termio.h ; then
    termio=define
    $echo "termio.h found."
else
    if $test -r /usr/include/sgtty.h ; then
	termio=undef
	$echo "sgtty.h found."
    else
	termio=undef
	$echo "Neither termio.h nor sgtty.h found--you could have problems."
    fi
fi

: see if there is a vfork
if $contains vfork.o libc.list >/dev/null 2>&1 ; then
    $echo "vfork() found."
    novfork='undef'
else
    $echo "No vfork() found--will use fork() instead."
    novfork='define'
fi

: see if there is a getpw
if $contains getpw.o libc.list >/dev/null 2>&1 ; then
    $echo "getpw() found."
    getpwent='undef'
else
    $echo "No getpw() found--will use getpwent() instead."
    getpwent='define'
fi

: see how we will look up site name
douname=undef
gethostname=undef
phostname=undef
if $contains gethostname.o libc.list >/dev/null 2>&1 ; then
    $echo "gethostname() found."
    gethostname=define
else
    if $contains uname.o libc.list >/dev/null 2>&1 ; then
	$echo "uname() found."
	douname=define
    else
	case $hostcmd in
	  '') ;;
	  *)
	    $cat <<EOT

There is no gethostname() or uname() on this system.  You have two
possibilites at this point:

1)  You can have your site name ($sitename) compiled into rn, which lets rn
    start up faster, but makes your binaries non-portable, or
2)  you can have rn use a
	
	popen("$hostcmd","r")

    which will start slower but be more portable.

Option 1 will use whoami.h if you have one.  If you want option 2 but with
a different command, you can edit config.h after this shell script is done.

EOT
	    case $portable in
	    define) dflt=n ;;
	    *)      dflt=y ;;
	    esac
	    $echo $n "Do you want your site name compiled in? [$dflt] $c"
	    read ans
	    case $ans in
	      '') phostname=$portable ;;
	      n*) phostname=define ;;
	      *)  hostcmd='' ;;
	    esac
	    ;;
	esac
	case $hostcmd in
	  '')
	    case $whoami in
	      define)
		$echo 'No hostname function--using whoami.h.'
		;;
	      undef)
		$echo 'No hostname function--hardwiring "'$sitename'".'
		;;
	    esac
	    ;;
	esac
    fi
fi

: see if we need -ljobs and if we have sigset, etc.
if $test -r /usr/lib/libjobs.a || $test -r /usr/local/lib/libjobs.a ; then
    $echo "Jobs library found."
    normsig=undef
    jobslib='-ljobs'
else
    if bsd; then
	$echo "No jobs library found.  (I suppose this is 4.2...)"
    else
	$echo "No jobs library found.  (That's okay, we all have our faults.)"
    fi
    normsig=define
    jobslib=''
fi

: see if there are directory access routines out there
if $test -r /usr/lib/libndir.a || $test -r /usr/local/lib/libndir.a ; then
    $echo "Ndir library found."
    ndirlib='-lndir'
    libndir=define
    usendir=undef
else
    ndirlib=''
    libndir=undef
    if $contains readdir.o libc.list >/dev/null 2>&1 ; then
	$echo "No ndir library found, but you have readdir() so we'll hope this is 4.2bsd."
	usendir=undef
    else
	$echo "No ndir library found and no readdir() found--using ./ndir.c."
	usendir=define
    fi
fi

: locate spool directory
spool=/usr/spool/news
ans=$spool
while $test ! -d $ans; do
    $echo ""
    $echo $n "$ans not found--where is news spooled? (~name ok) $c"
    read spool
    case $spool in
    ~*)
	ans=`filexp $spool`
	echo "(That is $ans on this particular system.)"
	case $portable in
	  undef) spool=$ans ;;
	esac
	;;
    *)
	ans=$spool
	;;
    esac
done
$echo "SPOOL = $spool"

: locate active file
active=$lib/active
myactive=$libexp/active
while $test ! -f $myactive; do
    $echo ""
    $echo $n "$myactive not found--where is the active file? (~name ok) $c"
    read active
    case $active in
    ~*)
	myactive=`filexp $active`
	echo "(That is $myactive on this particular system.)"
	case $portable in
	  undef) active=$myactive ;;
	esac
	;;
    *)
	myactive=$active
	;;
    esac
    if $test -d $myactive ; then
	myactive=$myactive/active
	active=$active/active
    fi
done
$echo "ACTIVE = $active"

: check for 2.10.2
echo ""
if $contains ' [0-9][0-9]* [0-9]' $myactive >/dev/null 2>&1; then
    echo "Looks like you are running at least 2.10.2 news."
    mininact=define
else
    echo "It doesn't look like you are running 2.10.2 news yet.  Are you planning"
    echo $n "to install it in the near future? [y] $c"
    read ans
    case $ans in
    n*) mininact=undef ;;
    *)  mininact=define ;;
    esac
fi


: check for void type
$echo ""
$echo "Checking to see if your C compiler groks the void type..."
$cat >try.c <<'EOCP'
void main();
EOCP
if cc -c try.c >/dev/null 2>&1 ; then
    novoid='undef'
    $echo "Yup, it does."
else
    novoid='define'
    $echo "Nope, it doesn't (boo hiss).  I will substitute int."
fi
$rm try.*

: find out which shell people like to use most
ans='blurfl'
while $test ! -f "$ans" ; do
    case $ans in
      blurfl) ;;
      *) $echo "$ans does not appear to exist." ;;
    esac
    if $test -f /bin/ksh; then
	dflt='/bin/ksh'
    else
	if $test -f /bin/csh; then
	    dflt='/bin/csh'
	else
	    dflt='/bin/sh'
	fi
    fi
    $echo ""
    $echo "Give the full path name of the shell most people like to use on your"
    $echo $n "system: [$dflt] $c"
    read ans
    case $ans in
      '') ans=$dflt ;;
    esac
done
pref=$ans

: locate the preferred pager for this system
pager='blurfl'
while $test ! -f "$pager" ; do
    case $pager in
      blurfl)
	$echo ""
	$echo "(If your kernel does terminal paging then you may answer this with '/bin/cat'.)"
	;;
      /*) $echo "$pager does not appear to exist."
	$echo ""
        ;;
      *) $echo "Please give the full path name."
	$echo ""
        ;;
    esac
    $echo $n "What pager is used on your system? [/usr/ucb/more] $c"
    read pager
    case $pager in
      '') pager='/usr/ucb/more' ;;
    esac
done

: determine default editor
dflt=/usr/ucb/vi
defeditor='blurfl'
while $test ! -f "$defeditor" ; do
    case $defeditor in
      blurfl) ;;
      *) $echo "$defeditor does not appear to exist." ;;
    esac
    $echo ""
    $echo $n "What is the default editor on your system? [$dflt] $c"
    read defeditor
    case $defeditor in
      '') defeditor=$dflt ;;
    esac
done

: determine mailer for Rnmail to use
echo ""
if $test -f /usr/lib/sendmail; then
    mailer=/usr/lib/sendmail
else
    if usg && $test -f $libexp/recmail; then
	mailer=$libexp/recmail
    else
	mailer=/bin/mail
    fi
fi
echo "Mail sender is $mailer"

: determine where public executables go
if $test -d /usr/local/bin; then
    dflt=/usr/local/bin
else
    if $test -d /usr/local; then
	dflt=/usr/local
    else
	if $test -d /usr/bin; then
	    dflt=/usr/bin
	else
	    dflt=/bin
	fi
    fi
fi
rnbin='blurfl'
while $test ! -d "$rnbin" ; do
    case $rnbin in
      blurfl) ;;
      *) $echo "$rnbin does not appear to exist." ;;
    esac
    $echo ""
    $echo $n "Where do you want to put the public executables? [$dflt] $c"
    read rnbin
    rnbin=`filexp $rnbin`
    case $rnbin in
      '') rnbin=$dflt ;;
    esac
done

: determine where private executables go
dflt=$lib/rn
$echo ""
$echo "Rn has a number of auxiliary programs that need not be visible to the"
$echo "whole world.  Where do you want to put these private executables?"
$echo $n "[$dflt] (~name ok) $c"
read rnlib
case $rnlib in
  '') rnlib=$dflt ;;
esac
case $portable in
undef)
    rnlib=`filexp $rnlib`
    echo "RNLIB = $rnlib"
    ;;
esac
: must not allow self reference
case $rnlib in
  /*)
    filexp=$rnlib/filexp
    ;;
  *)
    filexp=`pwd`/filexp
    ;;
esac

: determine where manual pages go
if $test -d /usr/man/local/man1; then
    dflt=/usr/man/local/man1
else
    dflt=/usr/man/man1
fi
mansrc='blurfl'
while $test ! -d "$mansrc" ; do
    case $mansrc in
      blurfl) ;;
      *) $echo "$mansrc does not appear to exist." ;;
    esac
    $echo ""
    $echo $n "Where do the manual pages go? [$dflt] $c"
    read mansrc
    mansrc=`filexp $mansrc`
    case $mansrc in
      '') mansrc=$dflt ;;
    esac
done

: get the local distribution prefixes
if $test -f $libexp/sys ; then
    $sed <$libexp/sys -n -e "s/^$sitename://p" | \
    $sed -e "s/:.*//" -e "s/,/ /g" | tr ' ' '\012' | \
    $sed -e "/^to./d" -e "/^net$/d" -e "/^fa$/d" -e "/^mod$/d" > .distlist
fi

$cat <<'EOH'

Distribution groups are the things you use on the Distribution line to limit
where an article will go to.  You are likely to be a member of several
distribution groups, such as organization, city, state, province, country,
continent, etc.  For example, Los Angeles has the distribution prefix "la",
New Jersey has the prefix "nj", and Europe has the prefix "eur".

The categories you will be asked are: 

local organization	(Could be just one machine or a cluster or an office)
organization		att, dec, kgb, ...
city			la, ny, mosc, ...
state/province		ca, nj, bc, ...
country			usa, can, rok, whatever
continent		na (North America, not "Not Applicable"), eur, etc.

(If you don't have a distribution prefix in any of these categories then
just hit return.)

EOH
if $test -f .distlist; then
    distlist=`tr '\012' ' ' <.distlist`
    if $test "$distlist" ; then
	$echo "(These are the distributions in your sys file: $distlist)"
	$echo ""
    fi
fi
$echo $n "What is the distribution prefix for your local organization? $c"
read locpref
case $locpref in
  '') locpref=none ;;
esac
$echo $n "What is the distribution prefix for your organization? $c"
read orgpref
case $orgpref in
  '') orgpref=none ;;
esac
$echo $n "What is the distribution prefix for your city? $c"
read citypref
case $citypref in
  '') citypref=none ;;
esac
$echo $n "What is the distribution prefix for your state/province? $c"
read statepref
case $statepref in
  '') statepref=none ;;
esac
$echo $n "What is the distribution prefix for your country? $c"
read cntrypref
case $cntrypref in
  '') cntrypref=none ;;
esac
$echo $n "What is the distribution prefix for your continent? $c"
read contpref
case $contpref in
  '') contpref=none ;;
esac

$echo ""
$echo "If you have any other distribution groups you will need to edit Pnews"
$echo "and newsetup to add them."
$echo ""

: determine root id
rootid=`$sed </etc/passwd -e "/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
case $rootid in
  '') rootid=0 ;;
  *)  $echo "Root uid = $rootid" ;;
esac

: weed out incompatibilities
case $douname in
  define) whoami=undef ;;
esac

: preserve RCS keywords in files with variable substitution, grrr
Log='$Log'
Header='$Header'

: Warnings
if v7; then
    cat <<'EOM'

NOTE: the V7 compiler may ignore some #undefs that rn uses.  If so, you will
get messages about redefining EXT.  Ignore them.
EOM
fi

if pdp11; then
    cat <<'EOM'

NOTE: the PDP-11 does not have enough data space to do subject caching
reliably.  That feature will be disabled automatically.  Subject searching
will tend to be slow.
EOM
fi

$echo ""
$echo "End of configuration questions."
$echo ""

: create config.sh file
$echo ""
$echo "Creating config.sh..."
$spitshell <<EOT >config.sh
$startsh
# config.sh
# This file was produced by running the Configure script.

n="$n"
c="$c"
libc="$libc"
eunicefix="$eunicefix"
eunice="$eunice"
cpp="$cpp"
shsharp="$shsharp"
startsh="$startsh"
spitshell="$spitshell"
test="$test"
expr="$expr"
sed="$sed"
echo="$echo"
cat="$cat"
rm="$rm"
mv="$mv"
cp="$cp"
tail="$tail"
tr="$tr"
mkdir="$mkdir"
sort="$sort"
uniq="$uniq"
inews="$inews"
grep="$grep"
egrep="$egrep"
contains="$contains"
lib="$lib"
libexp="$libexp"
nametype="$nametype"
iandd="$iandd"
termlib="$termlib"
jobslib="$jobslib"
ndirlib="$ndirlib"
libndir="$libndir"
usendir="$usendir"
pager="$pager"
mailer="$mailer"
rnbin="$rnbin"
filexp="$filexp"
distlist="$distlist"
Log="$Log"
Header="$Header"
sitename="$sitename"
orgname="$orgname"
isadmin="$isadmin"
newsadmin="$newsadmin"
rnlib="$rnlib"
mansrc="$mansrc"
spool="$spool"
active="$active"
mininact="$mininact"
pref="$pref"
defeditor="$defeditor"
rootid="$rootid"
mboxchar="$mboxchar"
locpref="$locpref"
orgpref="$orgpref"
citypref="$citypref"
statepref="$statepref"
cntrypref="$cntrypref"
contpref="$contpref"
strchr="$strchr"
novoid="$novoid"
novfork="$novfork"
portable="$portable"
passnam="$passnam"
berknam="$berknam"
usgnam="$usgnam"
whoami="$whoami"
termio="$termio"
normsig="$normsig"
havetlib="$havetlib"
getpwent="$getpwent"
gethostname="$gethostname"
douname="$douname"
phostname="$phostname"
hostcmd="$hostcmd"
CONFIG=true
EOT

CONFIG=true

: now put these in environment for subsequent shell scripts
export n
export c
export libc
export eunicefix
export eunice
export cpp
export shsharp
export spitshell
export startsh
export test
export expr
export sed
export echo
export cat
export rm
export mv
export cp
export tail
export tr
export mkdir
export sort
export uniq
export inews
export grep
export egrep
export contains
export lib
export nametype
export iandd
export termlib
export jobslib
export ndirlib
export libndir
export usendir
export pager
export mailer
export rnbin
export filexp
export distlist
export Log
export Header
export sitename
export orgname
export isadmin
export newsadmin
export rnlib
export mansrc
export spool
export active
export mininact
export pref
export defeditor
export rootid
export mboxchar
export locpref
export orgpref
export citypref
export statepref
export cntrypref
export contpref
export strchr
export novoid
export novfork
export portable
export passnam
export berknam
export usgnam
export whoami
export termio
export normsig
export havetlib
export getpwent
export gethostname
export douname
export phostname
export hostcmd
export CONFIG

: create config.h file
$echo ""
$echo "Creating config.h..."
$cat <<EOT >config.h
/* config.h
 * This file was produced by running the Configure script.
 * Feel free to modify any of this as the need arises.
 */

/* name of the site.  May be overridden by gethostname, uname, etc. */
#define SITENAME "$sitename"

/* name of the organization, may be a file name */
#define ORGNAME "$orgname"

/* login name of news administrator, if any. */
#$isadmin NEWSADMIN "$newsadmin"

/* news library, may use only ~ and %l expansion */
#define LIB "$lib"

/* rn private library, may use ~ expansion, %x and %l */
#define RNLIB "$rnlib"

/* location of the news spool directory, may use ~ expansion, %x and %l */
#define SPOOL "$spool"

/* location of the active file, may use ~ expansion, %x and %l */
#define ACTIVE "$active"

/* default shell--ok to be a slow shell like csh */
#define PREFSHELL "$pref"

/* default editor */
#define DEFEDITOR "$defeditor"

/* root uid */
#define ROOTID $rootid

/* what is the first character of a mailbox? */
#define MBOXCHAR '$mboxchar'

/* how to cancel an article */
#define CANCEL "$inews -h <%h"

/* distribution groups */
#define LOCDIST "$locpref"
#define ORGDIST "$orgpref"
#define CITYDIST "$citypref"
#define STATEDIST "$statepref"
#define CNTRYDIST "$cntrypref"
#define CONTDIST "$contpref"

#$strchr	index strchr	/* cultural */
#$strchr	rindex strrchr	/*  differences? */
#$novoid	void int	/* is void to be avoided? */
#$novfork	vfork fork	/* is vfork too virtual? */
#$eunice	EUNICE		/* no linking? */
#$eunice	VMS		/* not currently used, here just in case */
#$usendir	USENDIR		/* include ndir.c? */
#$libndir	LIBNDIR		/* include /usr/include/ndir.h? */
#$mininact	MININACT	/* include 2.10.2 optimization? */
#$portable	PORTABLE	/* do we do extra lookups to start up? */
#$passnam	PASSNAMES	/* do names come from the passwd file? */
				/*  (undef to take name from ~/.fullname) */
#$berknam	BERKNAMES	/* if so, are they Berkeley format? */
				/* (that is, ":name,stuff:") */
#$usgnam	USGNAMES	/* or are they USG format? */
				/* (that is, ":stuff-name(stuff):") */
#$whoami	WHOAMI		/* should we include whoami.h? */
#$termio	TERMIO		/* is this a termio system? */
#$normsig	NORMSIG		/* use signal rather than sigset? */
#$havetlib	HAVETERMLIB	/* do we have termlib-style routines? */
#$getpwent	GETPWENT	/* need we include slow getpwent? */
#$gethostname	GETHOSTNAME	/* do we have a gethostname function? */
#$douname	DOUNAME		/* do we have a uname function? */
#$phostname	PHOSTNAME "$hostcmd"	/* how to get host name with popen */
EOT

$rm -f libc.list .distlist kit*isdone

echo ""
echo "Doing variable substitutions on various files..."
echo ""
set `$grep <MANIFEST '\.SH' | awk '{print $1}'`
for file in $*; do
    sh <$file
done

echo ""
echo "Remaking make dependencies..."
makedepend
!STUFFY!FUNK!
echo Extracting ng.c
cat >ng.c <<'!STUFFY!FUNK!'
/* $Header: ng.c,v 4.1 84/09/24 12:01:53 lwall Exp $
 *
 * $Log:	ng.c,v $
 * Revision 4.1  84/09/24  12:01:53  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.6  84/09/19  17:09:39  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.5  84/09/19  10:14:10  lwall
 * Maybe not clear() on End of newsgroup.
 * 
 * Revision 4.0.1.4  84/09/18  16:59:39  lwall
 * Defined s for = command.
 * 
 * Revision 4.0.1.3  84/09/12  17:43:05  lwall
 * Fixed overzealous cast.
 * 
 * Revision 4.0.1.2  84/09/10  15:16:39  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/06  17:03:10  lwall
 * Implemented newsgroup exit commands.
 * 
 * Revision 4.0  84/09/04  09:51:39  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "term.h"
#include "final.h"
#include "util.h"
#include "artsrch.h"
#include "cheat.h"
#include "help.h"
#include "kfile.h"
#include "rcstuff.h"
#include "head.h"
#include "artstate.h"
#include "bits.h"
#include "art.h"
#include "artio.h"
#include "ngstuff.h"
#include "intrp.h"
#include "respond.h"
#include "ngdata.h"
#include "backpage.h"
#include "rcln.h"
#include "last.h"
#include "INTERN.h"
#include "ng.h"
#include "artstate.h"			/* somebody has to do it */

/* art_switch() return values */

#define AS_NORM 0
#define AS_INP 1
#define AS_ASK 2
#define AS_CLEAN 3

ART_NUM recent_art = 0;		/* previous article # for '-' command */
ART_NUM curr_art = 0;                /* current article # */

void
ng_init()
{

#ifdef KILLFILES
    open_kfile(KF_GLOBAL);
#endif
}

/* do newsgroup on line ng with name ngname */

/* assumes that we are chdir'ed to SPOOL, and assures that that is
 * still true upon return, but chdirs to SPOOL/ngname in between
 *
 * If you can understand this routine, you understand most of the program.
 * The basic structure is:
 *	for each desired article
 *		for each desired page
 *			for each line on page
 *				if we need another line from file
 *					get it
 *					if it's a header line
 *						do special things
 *				for each column on page
 *					put out a character
 *				end loop
 *			end loop
 *		end loop
 *	end loop
 *
 *	(Actually, the pager is in another routine.)
 *
 * The chief problem is deciding what is meant by "desired".  Most of
 * the messiness of this routine is due to the fact that people want
 * to do unstructured things all the time.  I have used a few judicious
 * goto's where I thought it improved readability.  The rest of the messiness
 * arises from trying to be both space and time efficient.  Have fun.
 */

int
do_newsgroup(start_command)
char *start_command;			/* command to fake up first */
{
    register long i;			/* scratch */
    int skipstate;			/* how many unavailable articles */
					/*   have we skipped already? */
    
    char *whatnext = "%sWhat next? [%s]";

#ifdef ARTSEARCH
    srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
					/* did they say -S? */
#endif
    
    if (eaccess(ngdir,5)) {		/* directory read protected? */
	if (eaccess(ngdir,0)) {
#ifdef VERBOSE
	    IF(verbose)
		printf("\nNewsgroup %s does not have a spool directory!\n",
		    ngname);
	    ELSE
#endif
#ifdef TERSE
		printf("\nNo spool for %s!\n",ngname);
#endif
#ifdef CATCHUP
	    catch_up(ng);
#endif
	    toread[ng] = TR_NONE;
	}
	else {
#ifdef VERBOSE
	    IF(verbose)
		printf("\nNewsgroup %s is not currently accessible.\n",
		    ngname);
	    ELSE
#endif
#ifdef TERSE
		printf("\n%s not readable.\n",ngname);
#endif
	    toread[ng] = TR_NONE;	/* make this newsgroup invisible */
					/* (temporarily) */
	}
	return -1;
    }

    /* chdir to newsgroup subdirectory */

    if (chdir(ngdir)) {
	printf(nocd,ngdir);
	return -1;
    }

#ifdef CACHESUBJ
    subj_list = Null(char **);		/* no subject list till needed */
#endif
    
    /* initialize control bitmap */

    if (initctl())
	return -1;

    /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */
    
    in_ng = TRUE;			/* tell the world we are here */
    forcelast = TRUE;			/* if 0 unread, do not bomb out */
    art=firstart;
    
    /* remember what newsgroup we were in for sake of posterity */

    writelast();

    /* do they want a special top line? */

    firstline = getval("FIRSTLINE",Nullch);

    /* see if there are any special searches to do */

#ifdef KILLFILES
    open_kfile(KF_LOCAL);
#ifdef VERBOSE
    IF(verbose)
	kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE);
    ELSE
#endif
#ifdef TERSE
	kill_unwanted(firstart,"Killing...\n\n",TRUE);
#endif
#endif
    
    /* now read each unread article */

    rc_changed = doing_ng = TRUE;	/* enter the twilight zone */
    skipstate = 0;			/* we have not skipped anything (yet) */
    checkcount = 0;			/* do not checkpoint for a while */
    do_fseek = FALSE;			/* start 1st article at top */
    if (art > lastart)
	art=firstart;			/* init the for loop below */
    for (; art<=lastart+1; ) {		/* for each article */

	/* do we need to "grow" the newsgroup? */

	if (art > lastart || forcegrow)
	    grow_ctl();
	check_first(art);		/* make sure firstart is still 1st */
	if (start_command) {		/* fake up an initial command? */
	    prompt = whatnext;
	    strcpy(buf,start_command);
	    free(start_command);
	    start_command = Nullch;
	    art = lastart+1;
	    goto article_level;
	}
	if (art>lastart) {		/* are we off the end still? */
	    ART_NUM ucount = 0;		/* count of unread articles left */

	    for (i=firstart; i<=lastart; i++)
		/*NOSTRICT*/
		if (!(ctl_read(i)))
		    ucount++;		/* count the unread articles */
#ifdef DEBUGGING
	    /*NOSTRICT*/
	    if (debug && ((ART_NUM)toread[ng]) != ucount)
		printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount);
#endif
	    /*NOSTRICT*/
	    toread[ng] = (ART_UNREAD)ucount;	/* this is perhaps pointless */
	    art = lastart + 1;		/* keep bitmap references sane */
	    if (art != curr_art) {
		recent_art = curr_art;
					/* remember last article # (for '-') */
		curr_art = art;      /* remember this article # */
	    }
	    if (erase_screen)
		clear();			/* clear the screen */
	    else
		fputs("\n\n",stdout);
#ifdef VERBOSE
	    IF(verbose)
		printf("End of newsgroup %s.",ngname);
					/* print pseudo-article */
	    ELSE
#endif
#ifdef TERSE
		printf("End of %s",ngname);
#endif
	    if (ucount) {
		printf("  (%ld article%s still unread)",
		    (long)ucount,ucount==1?nullstr:"s");
	    }
	    else {
		if (!forcelast)
		    goto cleanup;	/* actually exit newsgroup */
	    }
	    prompt = whatnext;
#ifdef ARTSEARCH
	    srchahead = 0;		/* no more subject search mode */
#endif
	    fputs("\n\n",stdout);
	    skipstate = 0;		/* back to none skipped */
	}
	else if /*NOSTRICT*/ (!reread && was_read(art)) {
					/* has this article been read? */
	    art++;			/* then skip it */
	    continue;
	}
	else if /*NOSTRICT*/ (!reread && !was_read(art)
	    && artopen(art) == Nullfp) {	/* never read it, & cannot find it? */
	    if (errno != ENOENT) {	/* has it not been deleted? */
#ifdef VERBOSE
		IF(verbose)
		    printf("\n(Article %ld exists but is unreadable.)\n",
			(long)art);
		ELSE
#endif
#ifdef TERSE
		    printf("\n(%ld unreadable.)\n",(long)art);
#endif
		skipstate = 0;
		sleep(2);
	    }
	    switch(skipstate++) {
	    case 0:
		clear();
#ifdef VERBOSE
		IF(verbose)
		    fputs("Skipping unavailable article",stdout);
		ELSE
#endif
#ifdef TERSE
		    fputs("Skipping",stdout);
#endif
		for (i = just_a_sec/3; i; --i)
		    putchar(PC);
		fflush(stdout);
		sleep(1);
		break;
	    case 1:
		fputs("..",stdout);
		fflush(stdout);
		break;
	    default:
		putchar('.');
		fflush(stdout);
#define READDIR
#ifdef READDIR
		{			/* fast skip patch */
		    ART_NUM newart;
		    
		    if (! (newart=getngmin(".",art)))
			newart = lastart+1;
		    for (i=art; i<newart; i++)
			oneless(i);
		    art = newart - 1;
		}
#endif
		break;
	    }
	    oneless(art);		/* mark deleted as read */
	    art++;			/* try next article */
	    continue;
	}
	else {				/* we have a real live article */
	    skipstate = 0;		/* back to none skipped */
	    if (art != curr_art) {
		recent_art = curr_art;
					/* remember last article # (for '-') */
		curr_art = art;      /* remember this article # */
	    }
	    if (!do_fseek) {		/* starting at top of article? */
		artline = 0;		/* start at the beginning */
		topline = -1;		/* and remember top line of screen */
					/*  (line # within article file) */
	    }
	    clear();			/* clear screen */
	    artopen(art);		/* make sure article file is open */
	    if (artfp == Nullfp) {	/* could not find article? */
		printf("Article %ld of %s is not available.\n\n",
		    (long)art,ngname);
		prompt = whatnext;
#ifdef ARTSEARCH
		srchahead = 0;
#endif
	    }
	    else {			/* found it, so print it */
		switch (do_article()) {
		case DA_CLEAN:		/* quit newsgroup */
		    goto cleanup;
		case DA_TOEND:		/* do not mark as read */
		    goto reask_article; 
		case DA_RAISE:		/* reparse command at end of art */
		    goto article_level;
		case DA_NORM:		/* normal end of article */
		    break;
		}
	    }
	    mark_as_read(art);		 /* mark current article as read */
	    reread = FALSE;
	    do_hiding = TRUE;
#ifdef ROTATION
	    rotate = FALSE;
#endif
	}

/* if these gotos bother you, think of this as a little state machine */

reask_article:
#ifdef MAILCALL
	setmail();
#endif
	setdfltcmd();
	standout();			/* enter standout mode */
	printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */
	un_standout();			/* leave standout mode */
	putchar(' ');
	fflush(stdout);
reinp_article:
	eat_typeahead();
#ifdef PENDING
	look_ahead();			/* see what we can do in advance */
	if (!input_pending())
	    collect_subjects();		/* loads subject cache until */
					/* input is pending */
#endif
	getcmd(buf);
	if (errno || *buf == '\f') {
	    if (LINES < 100 && !int_count)
		*buf = '\f';		/* on CONT fake up refresh */
	    else {
		putchar('\n');		/* but only on a crt */
		goto reask_article;
	    }
	}
article_level:

	/* parse and process article level command */

	switch (art_switch()) {
	case AS_INP:			/* multichar command rubbed out */
	    goto reinp_article;
	case AS_ASK:			/* reprompt "End of article..." */
	    goto reask_article;
	case AS_CLEAN:			/* exit newsgroup */
	    goto cleanup;
	case AS_NORM:			/* display article art */
	    break;
	}
    }					/* end of article selection loop */
    
/* shut down newsgroup */

cleanup:
#ifdef KILLFILES
    kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
					/* do cleanup from KILL file, if any */
#endif
    in_ng = FALSE;			/* leave newsgroup state */
    if (artfp != Nullfp) {		/* article still open? */
	fclose(artfp);			/* close it */
	artfp = Nullfp;			/* and tell the world */
	openart = 0;
    }
    putchar('\n');
    yankback();				/* do a Y command */
    restore_ng();			/* reconstitute .newsrc line */
    doing_ng = FALSE;			/* tell sig_catcher to cool it */
    free(ctlarea);			/* return the control area */
#ifdef CACHESUBJ
    if (subj_list) {
	for (i=OFFSET(lastart); i>=0; --i)
	    if (subj_list[i])
		free(subj_list[i]);
	/*NOSTRICT*/
	free((char*)subj_list);
    }
#endif
    write_rc();				/* and update .newsrc */
    rc_changed = FALSE;			/* tell sig_catcher it is ok */
    if (chdir(spool)) {
	printf(nocd,spool);
	sig_catcher(0);
    }
#ifdef KILLFILES
    if (localkfp) {
	fclose(localkfp);
	localkfp = Nullfp;
    }
#endif
    return 0;
}					/* Whew! */

/* decide what to do at the end of an article */

int
art_switch()
{
    register ART_NUM i;
      
    setdef(buf,dfltcmd);
#ifdef VERIFY
    printcmd();
#endif
    switch (*buf) {
    case 'p':			/* find previous unread article */
	do {
	    if (art <= firstart)
		break;
	    art--;
	} while /*NOSTRICT*/ (was_read(art) || artopen(art) == Nullfp);
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case 'P':			/* goto previous article */
	if (art > absfirst)
	    art--;
	else {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\n\
There are no articles prior to this one.\n\
",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("\nNo previous articles\n",stdout);
#endif
	    return AS_ASK;
	}
	reread = TRUE;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case '-':
	if (recent_art)
	    art = recent_art;
	reread = TRUE;
#ifdef ARTSEARCH
	srchahead = -(srchahead != 0);
#endif
	return AS_NORM;
    case 'n':		/* find next unread article? */
	if (art > lastart) {
	    if (toread[ng])
		art = firstart;
	    else
		return AS_CLEAN;
	}
#ifdef ARTSEARCH
	else if (scanon && srchahead) {
	    *buf = Ctl('n');
	    goto normal_search;
	}
#endif
	else
	    art++;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case 'N':			/* goto next article */
	if (art > lastart)
	    art = absfirst;
	else
	    art++;
	if (art <= lastart)
	    reread = TRUE;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case '$':
	art = lastart+1;
	forcelast = TRUE;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case '1': case '2': case '3':	/* goto specified article */
    case '4': case '5': case '6':	/* or do something with a range */
    case '7': case '8': case '9': case '.':
	forcelast = TRUE;
	switch (numnum()) {
	case NN_INP:
	    return AS_INP;
	case NN_ASK:
	    return AS_ASK;
	case NN_REREAD:
	    reread = TRUE;
	    break;
	case NN_NORM:
	    /*NOSTRICT*/
	    if (was_read(art)) {
		art = firstart;
		pad(just_a_sec/3);
	    }
	    else
		return AS_ASK;
	    break;
	}
	return AS_NORM;
    case Ctl('k'):
	edit_kfile();
	return AS_ASK;
    case 'K':
    case 'k':
    case Ctl('n'): case Ctl('p'):
    case '/': case '?':
#ifdef ARTSEARCH
normal_search:
    {		/* search for article by pattern */
	char cmd = *buf;
	
	reread = TRUE;		/* assume this */
	switch (art_search(buf,TRUE)) {
	case SRCH_ERROR:
	    return AS_ASK;
	case SRCH_ABORT:
	    return AS_INP;
	case SRCH_INTR:
#ifdef VERBOSE
	    IF(verbose)
		printf("\n(Interrupted at article %ld)\n",(long)art);
	    ELSE
#endif
#ifdef TERSE
		printf("\n(Intr at %ld)\n",(long)art);
#endif
	    art = curr_art;
			    /* restore to current article */
	    return AS_ASK;
	case SRCH_DONE:
	    fputs("done\n",stdout);
	    pad(just_a_sec/3);	/* 1/3 second */
	    if (srchahead)
		art = firstart;
	    else
		art = curr_art;
	    reread = FALSE;
	    return AS_NORM;
	case SRCH_SUBJDONE:
#ifdef UNDEF
	    fputs("\n\n\n\nSubject not found.\n",stdout);
	    pad(just_a_sec/3);	/* 1/3 second */
#endif
	    art = firstart;
	    reread = FALSE;
	    return AS_NORM;
	case SRCH_NOTFOUND:
	    fputs("\n\n\n\nNot found.\n",stdout);
	    art = curr_art;  /* restore to current article */
	    return AS_ASK;
	case SRCH_FOUND:
	    if (cmd == Ctl('n') || cmd == Ctl('p'))
		oldsubject = TRUE;
	    break;
	}
	return AS_NORM;
    }
#else
    buf[1] = '\0';
    notincl(buf);
    return AS_ASK;
#endif
    case 'u':			/* unsubscribe from this newsgroup? */
	rcchar[ng] = NEGCHAR;
	return AS_CLEAN;
    case 'M':
#ifdef DELAYMARK
	if (art <= lastart) {
	    delay_unmark(art);
	    printf("\nArticle %ld will return.\n",(long)art);
	}
#else
	notincl("M");
#endif
	return AS_ASK;
    case 'm':
	if (art <= lastart) {
	    unmark_as_read(art);
	    printf("\nArticle %ld marked as still unread.\n",(long)art);
	}
	return AS_ASK;
    case 'c':			/* catch up */
      reask_catchup:
#ifdef VERBOSE
	IF(verbose)
	    in_char("\nDo you really want to mark everything as read? [yn] ");
	ELSE
#endif
#ifdef TERSE
	    in_char("\nReally? [ynh] ");
#endif
	putchar('\n');
	setdef(buf,"y");
#ifdef VERIFY
	printcmd();
#endif
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\
Type y or SP to mark all articles as read.\n\
Type n to leave articles marked as they are.\n\
",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("\
y or SP to mark all read.\n\
n to forget it.\n\
",stdout);
#endif
	    goto reask_catchup;
	}
	else if (*buf == 'n' || *buf == 'q') {
	    return AS_ASK;
	}
	else if (*buf != 'y') {
	    fputs(hforhelp,stdout);
	    goto reask_catchup;
	}
	for (i = firstart; i <= lastart; i++) {
	    /*NOSTRICT*/
	    ctl_set(i);		/* mark as read */
	}
#ifdef DELAYMARK
	if (dmfp)
	    yankback();
#endif
	art = lastart+1;
	return AS_NORM;
    case 'q':			/* go back up to newsgroup level? */
	return AS_CLEAN;
    case 'j':
	putchar('\n');
	if (art <= lastart)
	    mark_as_read(art);
	return AS_ASK;
    case 'h':			/* help? */
	help_art();
	return AS_ASK;
    case '&':
	if (switcheroo()) /* get rest of command */
	    return AS_INP;	/* if rubbed out, try something else */
	return AS_ASK;
    case '#':
#ifdef VERBOSE
	IF(verbose)
	    printf("\nThe last article is %ld.\n",(long)lastart);
	ELSE
#endif
#ifdef TERSE
	    printf("\n%ld\n",(long)lastart);
#endif
	return AS_ASK;
    case '=': {
	char tmpbuf[128];
#ifndef CACHESUBJ
	char *s;
#endif
	
	page_line = 1;
	putchar('\n');
#ifdef CACHESUBJ
	if (!subj_list)
	    fetchsubj(art,TRUE,FALSE);
#endif
	for (i=firstart; i<=lastart && !int_count; i++) {
	    /*NOSTRICT*/
	    if (!was_read(i) &&
	      (
#ifdef CACHESUBJ
	      subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)
#else
	      s = fetchsubj(i,FALSE,FALSE)
#endif
	      ) ) {
		sprintf(tmpbuf,"%5d ", i);
		safecpy(tmpbuf + 6,
#ifdef CACHESUBJ
		subj_list[OFFSET(i)],
#else
		s,
#endif
		120);
		if (print_lines(tmpbuf,NOMARKING))
		    break;
	    }
	}
	int_count = 0;
	return AS_ASK;
    }
    case '^':
	art = firstart;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
#if defined(CACHESUBJ) && defined(DEBUGGING)
    case 'D':
	printf("\nFirst article: %ld\n",(long)firstart);
	if (!subj_list)
	    fetchsubj(art,TRUE,FALSE);
	if (subj_list != Null(char **)) {
	    for (i=1; i<=lastart && !int_count; i++) {
		if (subj_list[OFFSET(i)])
		    /*NOSTRICT*/
		    printf("%5d %c %s\n",
			i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]);
	    }
	}
	int_count = 0;
	return AS_ASK;
#endif
    case 'v':
	if (art <= lastart) {
	    reread = TRUE;
	    do_hiding = FALSE;
	}
	return AS_NORM;
#ifdef ROTATION
    case Ctl('x'):
#endif
    case Ctl('r'):
#ifdef ROTATION
	rotate = (*buf==Ctl('x'));
#endif
	if (art <= lastart)
	    reread = TRUE;
	return AS_NORM;
#ifdef ROTATION
    case 'X':
	rotate = !rotate;
	/* FALL THROUGH */
#else
    case Ctl('x'):
    case 'x':
    case 'X':
	notincl("x");
	return AS_ASK;
#endif
    case 'l': case Ctl('l'):		/* refresh screen */
	if (art <= lastart) {
	    reread = TRUE;
	    clear();
	    do_fseek = TRUE;
	    artline = topline;
	    if (artline < 0)
		artline = 0;
	}
	return AS_NORM;
    case 'b': case Ctl('b'):		/* back up a page */
	if (art <= lastart) {
	    ART_LINE target;

	    reread = TRUE;
	    clear();
	    do_fseek = TRUE;
	    target = topline - (LINES - 2);
	    artline = topline;
	    do {
		artline--;
	    } while (artline >= 0 && artline > target &&
		vrdary(artline-1) >= 0);
	    topline = artline;
	    if (artline < 0)
		artline = 0;
	}
	return AS_NORM;
    case '!':			/* shell escape */
	if (escapade())
	    return AS_INP;
	return AS_ASK;
    case 'C': {
	cancel_article();
	return AS_ASK;
    }
    case 'R':
    case 'r': {			/* reply? */
	reply();
	return AS_ASK;
    }
    case 'F':
    case 'f': {			/* followup command */
	followup();
	forcegrow = TRUE;		/* recalculate lastart */
	return AS_ASK;
    }
    case '|':
    case 'w': case 'W':
    case 's': case 'S':		/* save command */
	if (save_article() == SAVE_ABORT)
	    return AS_INP;
	return AS_ASK;
#ifdef DELAYMARK
    case 'Y':				/* yank back M articles */
	yankback();
	art = firstart;			/* from the beginning */
	return AS_NORM;			/* pretend nothing happened */
#endif
    case '\n':
	fputs(badcr,stdout);
	return AS_ASK;
    default:
	printf("\n%s",hforhelp);
	return AS_ASK;
    }
}

#ifdef MAILCALL
/* see if there is any mail */

void
setmail()
{
    if (! (mailcount++)) {
	char *mailfile = filexp(getval("MAILFILE",MAILFILE));
	
	if (stat(mailfile,&filestat) < 0 || !filestat.st_size)
	    mailcall = nullstr;
	else
	    mailcall = "(Mail) ";
    }
    mailcount %= 10;			/* check every 10 articles */
}
#endif

void
setdfltcmd()
{
    if (toread[ng]) {
#ifdef ARTSEARCH
	if (srchahead)
	    dfltcmd = "^Nnpq";
	else
#endif
	    dfltcmd = "npq";
    }
    else {
	if (art > lastart)
	    dfltcmd = "qnp";
	else
	    dfltcmd = "npq";
    }
}

!STUFFY!FUNK!
echo Extracting inews.c.1.pat
cat >inews.c.1.pat <<'!STUFFY!FUNK!'
*** inews.c.old	Tue Sep  4 16:42:01 1984
--- inews.c	Tue Sep  4 16:38:10 1984
***************
*** 515,520
  	}
  	for (;;) {
  		sprintf(bfr, "%s/%ld", dirname(ngname), ngsize+1);
  		if (link(ARTICLE, bfr) == 0) break;
  		e = errno;	/* keep log from clobbering it */
  		fprintf(stderr, "Cannot install article as %s\n", bfr);

--- 515,525 -----
  	}
  	for (;;) {
  		sprintf(bfr, "%s/%ld", dirname(ngname), ngsize+1);
+ #ifdef LINKART
+ 		if (mylink(ARTICLE, bfr) == 0) break;
+ 				/* on first file inits ARTICLE, on subsequent */
+ 				/* files "links" to first article */
+ #else
  		if (link(ARTICLE, bfr) == 0) break;
  #endif
  		e = errno;	/* keep log from clobbering it */
***************
*** 516,521
  	for (;;) {
  		sprintf(bfr, "%s/%ld", dirname(ngname), ngsize+1);
  		if (link(ARTICLE, bfr) == 0) break;
  		e = errno;	/* keep log from clobbering it */
  		fprintf(stderr, "Cannot install article as %s\n", bfr);
  		log("Cannot install article as %s", bfr);

--- 521,527 -----
  				/* files "links" to first article */
  #else
  		if (link(ARTICLE, bfr) == 0) break;
+ #endif
  		e = errno;	/* keep log from clobbering it */
  		fprintf(stderr, "Cannot install article as %s\n", bfr);
  		log("Cannot install article as %s", bfr);
***************
*** 542,547
  		strcpy(firstbufname, bfr);
  	sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
  	addhist(bfr);
  	return TRUE;
  }
  

--- 548,554 -----
  		strcpy(firstbufname, bfr);
  	sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
  	addhist(bfr);
+ #ifndef DOXREFS
  	return TRUE;
  #else DOXREFS
  	return ngsize+1;
***************
*** 543,548
  	sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
  	addhist(bfr);
  	return TRUE;
  }
  
  /*

--- 550,558 -----
  	addhist(bfr);
  #ifndef DOXREFS
  	return TRUE;
+ #else DOXREFS
+ 	return ngsize+1;
+ #endif DOXREFS
  }
  
  /*
***************
*** 553,558
  	register char *ptr;
  	register FILE *tfp;
  	int badgroup = 0, goodgroup = 0;
  
  	/* Fill up the rest of header. */
  	if (mode != PROC) {

--- 563,572 -----
  	register char *ptr;
  	register FILE *tfp;
  	int badgroup = 0, goodgroup = 0;
+ #ifdef DOXREFS
+ 	register char *porig;
+ 	register char *nextxref = header.xref; 
+ #endif DOXREFS
  
  	/* Fill up the rest of header. */
  	if (mode != PROC) {
***************
*** 565,570
  	if (mode==PROC)
  		log("from %s relay %s", header.from, header.relayversion);
  
  	/* Write article to temp file. */
  	tfp = xfopen(mktemp(ARTICLE), "w");
  	lhwrite(&header, tfp);

--- 579,591 -----
  	if (mode==PROC)
  		log("from %s relay %s", header.from, header.relayversion);
  
+ #ifdef LINKART
+ 	*ARTICLE = '\0';	/* tell mylink() to snarf the name */
+ #else !LINKART
+ #ifdef DOXREFS
+ 	/* Open temp file for article, but link before writing */
+ 	tfp = xfopen(mktemp(ARTICLE), "w");
+ #else DOXREFS
  	/* Write article to temp file. */
  	tfp = xfopen(mktemp(ARTICLE), "w");
  	lhwrite(&header, tfp);
***************
*** 577,582
  	}
  	fclose(tfp);
  	fclose(infp);
  
  	if (is_ctl) {
  		control(&header);

--- 598,605 -----
  	}
  	fclose(tfp);
  	fclose(infp);
+ #endif DOXREFS
+ #endif LINKART
  
  	if (is_ctl) {
  		control(&header);
***************
*** 593,598
  			}
  		}
  	} else {
  		for (ptr = nbuf; *ptr;) {
  			if (*ptr == '-') {
  				while (*ptr++)

--- 616,622 -----
  			}
  		}
  	} else {
+ #ifndef DOXREFS
  		for (ptr = nbuf; *ptr;) {
  #else
  		sprintf(nextxref,"%s ",SYSNAME);
***************
*** 594,599
  		}
  	} else {
  		for (ptr = nbuf; *ptr;) {
  			if (*ptr == '-') {
  				while (*ptr++)
  					;

--- 618,642 -----
  	} else {
  #ifndef DOXREFS
  		for (ptr = nbuf; *ptr;) {
+ #else
+ 		sprintf(nextxref,"%s ",SYSNAME);
+ 		nextxref += strlen(nextxref);
+ 		for (ptr = nbuf, porig=header.nbuf;;) {
+ 			int namlen=strlen(ptr);
+ 
+ 			while (*porig && (strncmp(porig,ptr,namlen) ||
+ 				porig[namlen] != NGDELIM)) {
+ 				strcpy(nextxref,"0 ");
+ 				nextxref += 2;
+ 				while (*porig && *porig++ != NGDELIM)
+ 					;
+ 					/* skip unmatched newsgroup */
+ 			}
+ 			if (!*ptr)
+ 				break;
+ 			while (*porig && *porig++ != NGDELIM)
+ 				;	/* skip matching newsgroup */
+ #endif
  			if (*ptr == '-') {
  				while (*ptr++)
  					;
***************
*** 597,602
  			if (*ptr == '-') {
  				while (*ptr++)
  					;
  				continue;
  			}
  			strcpy(bfr, dirname(ptr));

--- 640,649 -----
  			if (*ptr == '-') {
  				while (*ptr++)
  					;
+ #ifdef DOXREFS
+ 				strcpy(nextxref,"0 ");
+ 				nextxref += 2;
+ #endif
  				continue;
  			}
  			strcpy(bfr, dirname(ptr));
***************
*** 610,615
  			}
  			else
  				goodgroup++;
  			if (*nbuf)
  				localize(ptr);
  			while (*ptr++)

--- 657,663 -----
  			}
  			else
  				goodgroup++;
+ #ifndef DOXREFS
  			if (*nbuf)
  				localize(ptr);
  #else DOXREFS
***************
*** 612,617
  				goodgroup++;
  			if (*nbuf)
  				localize(ptr);
  			while (*ptr++)
  				;
  		}

--- 660,673 -----
  #ifndef DOXREFS
  			if (*nbuf)
  				localize(ptr);
+ #else DOXREFS
+ 			if (*nbuf)
+ 				sprintf(nextxref,"%d ",localize(ptr));
+ 			else
+ 				strcat(nextxref,"0 ");
+ 			while (*nextxref)
+ 				nextxref++;
+ #endif DOXREFS
  			while (*ptr++)
  				;
  		}
***************
*** 616,621
  				;
  		}
  	}
  
  #ifdef NOFORWARD
  	if (*nbuf)

--- 672,683 -----
  				;
  		}
  	}
+ #ifdef DOXREFS
+ 	if (goodgroup < 2 || badgroup)
+ 	    header.xref[0] = '\0';
+ 	else
+ 	    *(nextxref-1) = '\0';
+ #endif
  
  #ifdef LINKART
  	tfp = xfopen(ARTICLE,"w");	/* open 1st article localized */
***************
*** 617,622
  		}
  	}
  
  #ifdef NOFORWARD
  	if (*nbuf)
  #endif

--- 679,703 -----
  	    *(nextxref-1) = '\0';
  #endif
  
+ #ifdef LINKART
+ 	tfp = xfopen(ARTICLE,"w");	/* open 1st article localized */
+ #endif
+ 
+ #if defined(LINKART) || defined(DOXREFS)
+ 	/* Now that xref is constructed, write article to temp file. */
+ 	/* (We ought to detect no room at this point and clean up.) */ 
+ 	lhwrite(&header, tfp);
+ 	while (fgets(bfr, BUFLEN, infp) != NULL) {
+ 		/*
+ 		if (!strncmp(bfr, "From ", 5))
+ 			putc('>', tfp);
+ 		*/
+ 		fputs(bfr, tfp);
+ 	}
+ 	fclose(tfp);
+ 	fclose(infp);
+ #endif LINKART || DOXREFS
+ 
  #ifdef NOFORWARD
  	if (*nbuf)
  #endif
***************
*** 861,863
  		mclose(fd);
  	}
  }

--- 942,966 -----
  		mclose(fd);
  	}
  }
+ 
+ #ifdef LINKART
+ mylink(tmpart,linkfrom)
+ char *tmpart, *linkfrom;
+ {
+     struct stat statbuf;
+ 
+     if (stat(linkfrom,&statbuf)==0)
+ 	return -1;
+     if (!*tmpart)
+ 	strcpy(tmpart,linkfrom);
+     else {
+ 	FILE *linkfp = fopen(linkfrom,"w");
+ 
+ 	if (!linkfp)
+ 	    return -1;
+ 	fprintf(linkfp,"%s\n",tmpart);
+ 	fclose(linkfp);
+     }
+     return 0;
+ }
+ #endif LINKART
!STUFFY!FUNK!
echo Extracting only.h
cat >only.h <<'!STUFFY!FUNK!'
/* $Header: only.h,v 4.1 84/09/24 12:04:15 lwall Exp $
 *
 * $Log:	only.h,v $
 * Revision 4.1  84/09/24  12:04:15  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:22:47  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:58  lwall
 * Baseline for netwide release
 * 
 */

#ifndef NBRA
#include "search.h"
#endif

#ifdef ONLY
    char *ngtodo[NGMAX];		/* restrictions in effect */
#   ifdef SPEEDOVERMEM
	COMPEX *compextodo[NGMAX];	/* restrictions in compiled form */
#   endif
#endif

int maxngtodo INIT(0);			/*  0 => no restrictions */
					/* >0 => # of entries in ngtodo */

void	only_init();
bool	inlist();	/* return TRUE if ngname is in command line list */
			/* or if there was no list */
void	setngtodo();
#ifdef ONLY
    void	end_only();
#endif
!STUFFY!FUNK!
echo ""
echo "End of kit 2 (of 8)"
cat /dev/null >kit2isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 3 (of 8).  If kit 3 is complete, the line"
echo '"'"End of kit 3 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting art.c
cat >art.c <<'!STUFFY!FUNK!'
/* $Header: art.c,v 4.1 84/09/24 11:40:12 lwall Exp $
 *
 * $Log:	art.c,v $
 * Revision 4.1  84/09/24  11:40:12  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.5  84/09/19  17:06:19  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.4  84/09/18  16:04:45  lwall
 * Fixed INNERSEARCH commands.
 * 
 * Revision 4.0.1.3  84/09/10  14:49:44  lwall
 * Delinted.
 * 
 * Revision 4.0.1.2  84/09/06  08:26:07  lwall
 * Include util.h to define instr.
 * 
 * Revision 4.0.1.1  84/09/05  17:07:19  lwall
 * Treat restart correctly.
 * 
 * Revision 4.0  84/09/04  09:49:21  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "ngstuff.h"
#include "head.h"
#include "cheat.h"
#include "help.h"
#include "search.h"
#include "artio.h"
#include "ng.h"
#include "bits.h"
#include "final.h"
#include "artstate.h"
#include "rcstuff.h"
#include "term.h"
#include "sw.h"
#include "util.h"
#include "backpage.h"
#include "intrp.h"
#include "INTERN.h"
#include "art.h"

/* page_switch() return values */

#define PS_NORM 0
#define PS_ASK 1
#define PS_RAISE 2
#define PS_TOEND 3

bool special = FALSE;		/* is next page special length? */
int slines = 0;			/* how long to make page when special */
ART_LINE highlight = -1;	/* next line to be highlighted */
char *restart = Nullch;		/* if nonzero, the place where last */
				/* line left off on line split */
char *blinebeg;			/* where in buffer current line began */
ART_POS alinebeg;		/* where in file current line began */

#ifdef INNERSEARCH
ART_POS innersearch = 0;	/* artpos of end of line we found */
				/* for 'g' command */
ART_LINE isrchline = 0;			/* last line to display */
bool hide_everything = FALSE;
				/* if set, do not write page now, */
				/* but refresh when done with page */
COMPEX gcompex;				/* in article search pattern */
#endif

bool firstpage;			/* is this the 1st page of article? */

void
art_init()
{
    ;
}

int
do_article()
{
    register char *s;
    ART_POS artsize;			/* size in bytes of article */
    char art_buf[LBUFLEN];		/* place for article lines */
    bool hide_this_line = FALSE;	/* hidden header line? */
    ART_LINE linenum;	/* line # on page, 1 origin */
#ifdef ULSMARTS
    bool under_lining = FALSE;
			    /* are we underlining a word? */
#endif
    register char *bufptr = art_buf;
			    /* pointer to input buffer */
    register int outpos;	/* column position of output */
    static char prompt_buf[64];		/* place to hold prompt */
    bool notesfiles = FALSE;		/* might there be notesfiles junk? */

#ifdef INNERSEARCH
    register int outputok;
#endif

    if (fstat(artfp->_file,&filestat))
			    /* get article file stats */
	return DA_CLEAN;
    artsize = filestat.st_size;
			    /* from that get article size */
    sprintf(prompt_buf,
	"%%sEnd of article %ld (of %ld)--what next? [%%s]",
	(long)art,(long)lastart);	/* format prompt string */
    prompt = prompt_buf;
    int_count = 0;		/* interrupt count is 0 */
    firstpage = (topline < 0);
    for (;;) {			/* for each page */
	assert(art == openart);
	if (do_fseek) {
	    artpos = vrdary(artline);
	    if (artpos < 0)
		artpos = -artpos;	/* labs(), anyone? */
	    fseek(artfp,artpos,0);
	    if (artpos < htype[PAST_HEADER].ht_minpos)
		in_header = SOME_LINE;
	    do_fseek = FALSE;
	    restart = Nullch;
	}
	if (firstpage) {
	    if (firstline) {
		interp(art_buf,firstline);
		fputs(art_buf,stdout);
	    }
	    else {
		ART_NUM i;

		printf("Article %ld",(long)art);
		i = (((ART_NUM)toread[ng]) - 1 + was_read(art));
#ifdef DELAYMARK
		if (i || dmcount) {
		    printf(" (%ld more",(long)i);
		    if (dmcount)
			printf(" + %ld Marked to return)",(long)dmcount);
		    putchar(')');
		}
#else
		if (i)
		    printf(" (%ld more)",(long)i);
#endif
		if (htype[NGS_LINE].ht_flags & HT_HIDE)
		    printf(" in %s", ngname);
		fputs(":\n",stdout);
	    }
	    start_header(art);
	    forcelast = FALSE;		/* we will have our day in court */
	    restart = Nullch;
	    artline = 0;		/* start counting lines */
	    artpos = 0;
	    vwtary(artline,artpos);	/* remember pos in file */
	}
	for (linenum=(firstpage?2:1);
	  in_header || (
#ifdef INNERSEARCH
	  innersearch ? innermore() :
#endif
	  linenum<(firstpage?initlines:(special?slines:LINES)) );
	  linenum++) {		/* for each line on page */
	    if (int_count) {	/* exit via interrupt? */
		putchar('\n');	/* get to left margin */
		int_count = 0;	/* reset interrupt count */
		return DA_NORM;	/* skip out of loops */
	    }
	    if (restart) {		/* did not finish last line? */
		bufptr = restart;	/* then start again here */
		restart = Nullch;	/* and reset the flag */
	    }
	    else {			/* not a restart */
		if (fgets(art_buf,LBUFLEN,artfp)==Nullch) {
					/* if all done */
		    return DA_NORM;	/* skip out of loops */
		}
		bufptr = art_buf;	/* so start at beginning */
		art_buf[LBUFLEN-1] = '\0';
					/* make sure string ends */
	    }
	    blinebeg = bufptr;	/* remember where we began */
	    alinebeg = artpos;	/* both in buffer and file */
	    if (in_header && bufptr == art_buf)
		hide_this_line =
		    parseline(art_buf,do_hiding,hide_this_line);
	    else if (notesfiles && do_hiding &&
	      bufptr == art_buf && *art_buf == '#' &&
	      isupper(art_buf[1]) && art_buf[2] == ':' ) {
		fgets(art_buf,sizeof(art_buf),artfp);
		if (index(art_buf,'!') != Nullch)
		    fgets(art_buf,sizeof(art_buf),artfp);
		htype[PAST_HEADER].ht_minpos = ftell(artfp);
					/* exclude notesfiles droppings */
		hide_this_line = TRUE;	/* and do not print either */
		notesfiles = FALSE;
	    }
	    if (in_header && htype[in_header].ht_flags & HT_MAGIC) {
		if (in_header == NGS_LINE) {
		    hide_this_line = (index(art_buf,',') == Nullch && do_hiding);
		}
		else if (in_header == EXPIR_LINE) {
		    if (!(htype[EXPIR_LINE].ht_flags & HT_HIDE))
		    hide_this_line = (strlen(art_buf) < 10 && do_hiding);
		}
	    }
	    if (in_header == SUBJ_LINE &&
		htype[SUBJ_LINE].ht_flags & HT_MAGIC) {
			    /* is this the subject? */
		int length;

		length = strlen(art_buf)-1;
		artline++;
		art_buf[length] = '\0';		/* wipe out newline */
#ifdef NOFIREWORKS
		no_ulfire();
#endif
		notesfiles =
		    (instr(&art_buf[length-10]," - (nf") != Nullch);
		if (oldsubject) {
		    length += 7;
		    fputs("(SAME) ",stdout);
		    oldsubject = FALSE;
		}
		if (length+UG > COLS) {		/* rarely true */
		    linenum++;
		    vwtary(artline,vrdary(artline-1)+COLS);
		    artline++;
		}
		s = art_buf + 8;
		*s++ = '\0';	/* make into 2 strings */
		fputs(art_buf,stdout);
				/* print up through : */
		if (!UG)
		    putchar(' ');
		underprint(s);	/* print subject underlined */
		putchar('\n');	/* and finish the line */
	    }
	    else if (hide_this_line) {	/* do not print line? */
		linenum--;		/* compensate for linenum++ */
		if (!in_header)
		    hide_this_line = FALSE;
	    }
	    else {			/* just a normal line */
		if (highlight==artline) {	/* this line to be highlit? */
		    if (marking == STANDOUT) {
#ifdef NOFIREWORKS
			if (erase_screen)
			    no_sofire();
#endif
			standout();
		    }
		    else {
#ifdef NOFIREWORKS
			if (erase_screen)
			    no_ulfire();
#endif
			underline();
		    }
		    if (*bufptr == '\n')
			putchar(' ');
		}
#ifdef INNERSEARCH
		outputok = !hide_everything;
					/* get it into register, hopefully */
#endif
		for (outpos = 0; outpos < COLS; ) {
				    /* while line has room */
		    if (*bufptr >= ' ') {	/* normal char? */
#ifdef ULSMARTS
			if (*bufptr == '_') {
			    if (bufptr[1] == '\b') {
				if (!under_lining && highlight!=artline
#ifdef INNERSEARCH
				    && outputok
#endif
				    ) {
				    under_lining++;
				    if (UG) {
					if (bufptr != buf &&
					  bufptr[-1] == ' ') {
					    outpos--;
					    backspace();
					}
				    }
				    underline();
				}
				bufptr += 2;
			    }
			}
			else {
			    if (under_lining) {
				under_lining = 0;
				un_underline();
				if (UG) {
				    if (*bufptr == ' ')
					goto skip_put;
				    outpos++;
				}
			    }
			}
#endif
#ifdef INNERSEARCH
			if (outputok)
#endif
			{
#ifdef ROTATION
			    if (rotate && !in_header
			      && isalpha(*bufptr)) {
				if ((*bufptr & 31) <= 13)
				    putchar(*bufptr+13);
				else
				    putchar(*bufptr-13);
			    }
			    else
#endif
				putchar(*bufptr);
			}
			if (*UC && ((highlight==artline && marking == 1)
#ifdef ULSMARTS
			    || under_lining
#endif
			    )) {
			    backspace();
			    underchar();
			}
		    skip_put:
			bufptr++;
			outpos++;
		    }
		    else if (*bufptr == '\n' || !*bufptr) {
						    /* newline? */
#ifdef ULSMARTS
			if (under_lining) {
			    under_lining = 0;
			    un_underline();
			}
#endif
#ifdef DEBUGGING
			if (debug & DEB_INNERSRCH && outpos < COLS - 6) {
			    standout();
			    printf("%4d",artline); 
			    un_standout();
			}
#endif
#ifdef INNERSEARCH
			if (outputok)
#endif
			    putchar('\n');
			restart = 0;
			outpos = 1000;	/* signal normal \n */
		    }
		    else if (*bufptr == '\t') {	/* tab? */
#ifdef INNERSEARCH
			if (outputok)
#endif
			    putchar(*bufptr);
			bufptr++;
			outpos += 8 - outpos % 8;
		    }
		    else if (*bufptr == '\f') {	/* form feed? */
#ifdef INNERSEARCH
			if (outputok)
#endif
			    fputs("^L",stdout);
			if (highlight != artline)
			    linenum = 32700;
			    /* how is that for a magic number? */
			bufptr++;
			outpos += 2;
		    }
		    else {		/* other control char */
#ifdef INNERSEARCH
			if (outputok)
#endif
			{
			    putchar('^');
			    if (highlight == artline && *UC && marking == 1) {
				backspace();
				underchar();
				putchar(*bufptr+64);
				backspace();
				underchar();
			    }
			    else
				putchar(*bufptr+64);
			}
			bufptr++;
			outpos += 2;
		    }
		    
		} /* end of column loop */

		if (outpos < 1000) {/* did line overflow? */
		    restart = bufptr;
				    /* restart here next time */
		    if (AM) {	/* automatic margins on tty? */
			if (!XN && *bufptr == '\n')
				    /* need we simulate XN? */
			    restart = 0;
				    /* skip the newline */
		    }
		    else {		/* cursor just hangs there */
#ifdef INNERSEARCH
			if (outputok)
#endif
			    putchar('\n');
				    /* so move it down ourselves */
			if (*bufptr == '\n')
			    restart = 0;
				    /* simulate XN if need be */
		    }
		}

		/* handle normal end of output line formalities */

		if (highlight == artline) {
					/* were we highlighting line? */
		    if (marking == STANDOUT)
			un_standout();
		    else
			un_underline();
		    highlight = -1;	/* no more we are */
		}
		artline++;	/* count the line just printed */
		if (artline - LINES + 1 > topline)
			    /* did we just scroll top line off? */
		    topline = artline - LINES + 1;
			    /* then recompute top line # */
	    }

	    /* determine actual position in file */

	    if (restart)	/* stranded somewhere in the buffer? */
		artpos += restart - blinebeg;
			    /* just calculate position */
	    else		/* no, ftell will do */
		artpos = ftell(artfp);
			    /* so do ftell */
	    vwtary(artline,artpos);	/* remember pos in file */
	} /* end of line loop */

#ifdef INNERSEARCH
	innersearch = 0;
	if (hide_everything) {
	    hide_everything = FALSE;
	    *buf = Ctl('l');
	    goto fake_command;
	}
#endif
	if (linenum >= 32700)/* did last line have formfeed? */
	    vwtary(artline-1,-vrdary(artline-1));
			    /* remember by negating pos in file */

	special = FALSE;	/* end of page, so reset page length */
	firstpage = FALSE;	/* and say it is not 1st time thru */

	/* extra loop bombout */

	if (artpos == artsize)	/* did we just now reach EOF? */
	    return DA_NORM;	/* avoid --MORE--(100%) */

/* not done with this article, so pretend we are a pager */

reask_pager:		    
	standout();		/* enter standout mode */
	printf("--MORE--(%ld%%)",(long)(artpos*100/artsize));
	un_standout();	/* leave standout mode */
	fflush(stdout);
/* reinp_pager:     			/* unused, commented for lint */
	eat_typeahead();
#ifdef DEBUGGING
	if (debug & DEB_CHECKPOINTING) {
	    printf("(%d %d %d)",checkcount,linenum,artline);
	    fflush(stdout);
	}
#endif
	if (checkcount >= docheckwhen &&
	  linenum == LINES &&
	  (artline > 40 || checkcount >= docheckwhen+10) ) {
			    /* while he is reading a whole page */
			    /* in an article he is interested in */
	    checkcount = 0;
	    checkpoint_rc();	/* update .newsrc */
	}
	collect_subjects();		/* loads subject cache until */
					/* input is pending */
	getcmd(buf);
	if (errno) {
	    if (LINES < 100 && !int_count)
		*buf = '\f';/* on CONT fake up refresh */
	    else {
		*buf = 'q';	/* on INTR or paper just quit */
	    }
	}
	putchar('\r');	/* put cursor at beginning of line */
	erase_eol();	/* and erase the prompt */
	fflush(stdout);

    fake_command:		/* used by innersearch */

	/* parse and process pager command */

	switch (page_switch()) {
	case PS_ASK:	/* reprompt "--MORE--..." */
	    goto reask_pager;
	case PS_RAISE:	/* reparse on article level */
	    return DA_RAISE;
	case PS_TOEND:	/* fast pager loop exit */
	    return DA_TOEND;
	case PS_NORM:	/* display more article */
	    break;
	}
    } /* end of page loop */
}

/* process pager commands */

int
page_switch()
{
    register char *s;
    
    switch (*buf) {
    case 'd':
    case Ctl('d'):	/* half page */
	special = TRUE;
	slines = LINES / 2 + 1;
	if (marking && *blinebeg != '\f') {
	    up_line();
	    highlight = --artline;
	    restart = blinebeg;
	    artpos = alinebeg;
	}
	return PS_NORM;
    case '!':			/* shell escape */
	escapade();
	return PS_ASK;
#ifdef INNERSEARCH
    case Ctl('g'):
	gline = 3;
	compile(&gcompex,"^Subject:",TRUE,TRUE);
	goto caseG;
    case 'g':		/* in-article search */
	if (!finish_command(FALSE))/* get rest of command */
	    return PS_ASK;
	s = buf+1;
	if (isspace(*s))
	    s++;
	if ((s = compile(&gcompex,s,TRUE,TRUE)) != Nullch) {
			    /* compile regular expression */
	    printf("\n%s\n",s);
	    return PS_ASK;
	}
	putchar('\r');	/* put cursor at beginning of line */
	erase_eol();	/* and erase the prompt */
	/* FALL THROUGH */
    caseG:
    case 'G': {
	/* ART_LINE lines_to_skip = 0; */
	ART_POS start_where;

	if (gline < 0 || gline > LINES-2)
	    gline = LINES-2;
#ifdef DEBUGGING
	if (debug & DEB_INNERSRCH)
	    printf("Start here? %d  >=? %d\n",topline + gline + 1,artline);
#endif
	if (topline+gline+1 >= artline)
	    start_where = artpos;
			/* in case we had a line wrap */
	else {
	    start_where = vrdary(topline+gline+1);
	    if (start_where < 0)
		start_where = -start_where;
	}
	if (start_where < htype[PAST_HEADER].ht_minpos)
	    start_where = htype[PAST_HEADER].ht_minpos;
	fseek(artfp,(long)start_where,0);
	innersearch = 0; /* assume not found */
	while (fgets(buf, sizeof buf, artfp) != Nullch) {
	    /* lines_to_skip++; 		NOT USED NOW */
#ifdef DEBUGGING
	    if (debug & DEB_INNERSRCH)
		printf("Test %s",buf);
#endif
	    if (execute(&gcompex,buf) != Nullch) {
		innersearch = ftell(artfp);
		break;
	    }
	}
	if (!innersearch) {
	    fseek(artfp,artpos,0);
	    fputs("(Not found)",stdout);
	    return PS_ASK;
	}
#ifdef DEBUGGING
	if (debug & DEB_INNERSRCH)
	    printf("On page? %ld <=? %ld\n",(long)innersearch,(long)artpos);
#endif
	if (innersearch <= artpos) {	/* already on page? */
	    if (innersearch < artpos) {
		artline = topline+1;
		while (vrdary(artline) < innersearch)
		    artline++;
	    }
	    highlight = artline - 1;
#ifdef DEBUGGING
	    if (debug & DEB_INNERSRCH)
		printf("@ %d\n",highlight);
#endif
	    topline = highlight - gline;
	    if (topline < -1)
		topline = -1;
	    *buf = '\f';		/* fake up a refresh */
	    innersearch = 0;
	    return page_switch();
	}
	else {				/* who knows how many lines it is? */
	    do_fseek = TRUE;
	    hide_everything = TRUE;
	}
	return PS_NORM;
    }
#else
    case 'g': case 'G': case Ctl('g'):
	notincl("g");
	return PS_ASK;
#endif
    case '\n':		/* one line */
	special = TRUE;
	slines = 2;
	return PS_NORM;
#ifdef ROTATION
    case 'X':
	rotate = !rotate;
	/* FALL THROUGH */
#endif
    case 'l':
    case '\f':		/* refresh screen */
#ifdef DEBUGGING
	if (debug & DEB_INNERSRCH) {
	    printf("Topline = %d",topline);
	    gets(buf);
	}
#endif
	clear();
	do_fseek = TRUE;
	artline = topline;
	if (artline < 0)
	    artline = 0;
	firstpage = (topline < 0);
	return PS_NORM;
    case 'b':
    case Ctl('b'): {	/* back up a page */
	ART_LINE target;

	clear();
	do_fseek = TRUE;	/* reposition article file */
	target = topline - (LINES - 2);
	artline = topline;
	do {
	    artline--;
	} while (artline >= 0 && artline > target &&
	    vrdary(artline-1) >= 0);
	topline = artline;
			/* remember top line of screen */
			/*  (line # within article file) */
	if (artline < 0)
	    artline = 0;
	firstpage = (topline < 0);
	return PS_NORM;
    }
    case 'h':		/* help */
	help_page();
	return PS_ASK;
    case '\177':
    case '\0':		/* treat del,break as 'n' */
	*buf = 'n';
	/* FALL THROUGH */
    case 'k':	case 'K':
    case 'n':	case 'N':	case Ctl('n'):
    case 's':	case 'S':
    case 'u':
    case 'w':	case 'W':
    case '|':
	mark_as_read(art);	/* mark article as read */
	/* FALL THROUGH */
    case '#':
    case '$':
    case '&':
    case '-':
    case '.':
    case '/':
    case '1': case '2': case '3': case '4': case '5':
    case '6': case '7': case '8': case '9':
    case '=':
    case '?':
    case 'c':	case 'C':	
    case 'f':	case 'F':	
    case 'j':
				case Ctl('k'):
    case 'm':	case 'M':	
    case 'p':	case 'P':	case Ctl('p'):	
    case 'r':	case 'R':	case Ctl('r'):
    case 'v':
		case 'Y':
#ifndef ROTATION
    case 'x':	case 'X':
#endif
    case Ctl('x'):
    case '^':

#ifdef ROTATION
	rotate = FALSE;
#endif
	reread = FALSE;
	do_hiding = TRUE;
	if (index("nNpP",*buf) == Nullch &&
	  index("wWsS!&|/?123456789.",*buf) != Nullch) {
	    setdfltcmd();
	    standout();		/* enter standout mode */
	    printf(prompt,mailcall,dfltcmd);
			    /* print prompt, whatever it is */
	    un_standout();	/* leave standout mode */
	    putchar(' ');
	    fflush(stdout);
	}
	return PS_RAISE;	/* and pretend we were at end */
#ifdef ROTATION
    case 'x':
	rotate = TRUE;
	/* FALL THROUGH */
#endif
    case 'y':
    case ' ':	/* continue current article */
	if (erase_screen) {	/* -e? */
	    clear();		/* clear screen */
	    if (*blinebeg != '\f') {
		restart = blinebeg;
		artline--;	 /* restart this line */
		artpos = alinebeg;
		if (marking)	/* and mark repeated line */
		    highlight = artline;
	    }
	    topline = artline;
			/* and remember top line of screen */
			/*  (line # within article file) */
	}
	else if (marking && *blinebeg != '\f') {
				/* are we marking repeats? */
	    up_line();		/* go up one line */
	    highlight = --artline;/* and get ready to highlight */
	    restart = blinebeg;	/*   the old line */
	    artpos = alinebeg;
	}
	return PS_NORM;
    case 'q':	/* quit this article? */
	do_hiding = TRUE;
	return PS_TOEND;
    default:
	fputs(hforhelp,stdout);
	return PS_ASK;
    }
}

#ifdef INNERSEARCH
bool
innermore()
{
    if (artpos < innersearch) {		/* not even on page yet? */
#ifdef DEBUGGING
	if (debug & DEB_INNERSRCH)
	    printf("Not on page %ld < %ld\n",(long)artpos,(long)innersearch);
#endif
	return TRUE;
    }
    if (artpos == innersearch) {	/* just got onto page? */
	isrchline = artline;		/* remember first line after */
	highlight = artline - 1;
#ifdef DEBUGGING
	if (debug & DEB_INNERSRCH)
	    printf("There it is %ld = %ld, %d @ %d\n",(long)artpos,(long)innersearch,hide_everything,highlight);
#endif
	if (hide_everything) {		/* forced refresh? */
	    topline = highlight - gline;
	    if (topline < -1)
		topline = -1;
	    return FALSE;		/* let refresh do it all */
	}
    }
#ifdef DEBUGGING
    if (debug & DEB_INNERSRCH)
	printf("Not far enough? %d <? %d + %d\n",artline,isrchline,gline);
#endif
    if (artline < isrchline + gline) {
	return TRUE;
    }
    return FALSE;
}
#endif
!STUFFY!FUNK!
echo Extracting common.h
cat >common.h <<'!STUFFY!FUNK!'
/* $Header: common.h,v 4.1 84/09/24 11:44:27 lwall Exp $
 * 
 * $Log:	common.h,v $
 * Revision 4.1  84/09/24  11:44:27  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.6  84/09/22  16:51:29  lwall
 * 2.10.2 inews moved.
 * 
 * Revision 4.0.1.5  84/09/13  12:11:19  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.4  84/09/12  17:47:14  lwall
 * Moved some includes here.
 * 
 * Revision 4.0.1.3  84/09/10  15:09:09  lwall
 * Delinted.
 * 
 * Revision 4.0.1.2  84/09/05  13:48:13  lwall
 * More LOCKART stuff.
 * 
 * Revision 4.0.1.1  84/09/04  15:10:52  lwall
 * LINKART option for poor Eunice sites.
 * 
 * Revision 4.0  84/09/04  09:50:08  lwall
 * Baseline for netwide release
 * 
 */

#include "config.h"	/* generated by installation script */
#ifdef WHOAMI
#    include <whoami.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

#ifndef isalnum
#   define isalnum(c) (isalpha(c) || isdigit(c))
#endif

#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>

#ifdef TERMIO
#   include <termio.h>
#else
#   include <sgtty.h>
#endif

#ifdef GETPWENT
#   include <pwd.h>
#endif

#define BITSPERBYTE 8
#define TCSIZE 256	/* capacity for termcap strings */
#define LBUFLEN 512	/* line buffer length */
			/* (don't worry, .newsrc lines can exceed this) */
#define CBUFLEN 256	/* command buffer length */
#ifdef pdp11
#   define MAXFILENAME 128
#else
#   define MAXFILENAME 512
#endif
#define LONGKEY 15	/* longest keyword: currently "posting-version" */
#define FINISHCMD 0177

/* some handy defs */

#define bool char
#define TRUE (1)
#define FALSE (0)
#define Null(t) ((t)0)
#define Nullch Null(char *)
#define Nullfp Null(FILE *)

#define Ctl(ch) (ch & 037)

#define strNE(s1,s2) (strcmp(s1,s2))
#define strEQ(s1,s2) (!strcmp(s1,s2))
#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))

/* Things we can figure out ourselves */

#ifdef SIGTSTP
#   define BERKELEY 	/* include job control signals? */
#endif

#ifdef SIGPROF
#   define BSD42		/* do we have Berkeley 4.2? */
#endif

#ifdef FIONREAD
#   define PENDING
#else
#   ifdef O_NDELAY
#	define PENDING
#   endif
#endif

#ifdef EUNICE
#   define LINKART		/* add 1 level of possible indirection */
#   define UNLINK(victim) while (!unlink(victim))
#else
#   define UNLINK(victim) unlink(victim)
#endif

/* Valid substitutions for strings marked with % comment are:
 *	%a	Current article number
 *	%A	Full name of current article (%P/%c/%a)
 *		(if LINKART defined, is the name of the real article)
 *	%b	Destination of a save command, a mailbox or command
 *	%B	The byte offset to the beginning of the article for saves
 *		with or without the header
 *	%c	Current newsgroup, directory form
 *	%C	Current newsgroup, dot form
 *	%d	%P/%c
 *	%D	Old Distribution: line
 *	%f	Old From: line or Reply-To: line
 *	%F	Newsgroups to followup to from Newsgroups: and Followup-To:
 *	%h	Name of header file to pass to mail or news poster
 *	%H	Host name (yours)
 *	%i	Old Message-I.D.: line, with <>
 *	%l	News administrator login name
 *	%L	Login name (yours)
 *	%M	Number of articles markd with M
 *	%n	Newsgroups from source article
 *	%N	Full name (yours)
 *	%o	Organization (yours)
 *	%O	Original working directory (where you ran rn from)
 *	%p	Your private news directory (-d switch)
 *	%P	Public news spool directory (SPOOLDIR)
 *	%r	Last reference (parent article id)
 *	%R	New references list
 *	%s	Subject, with all Re's and (nf)'s stripped off
 *	%S	Subject, with one Re stripped off
 *	%t	New To: line derived from From: and Reply-To (Internet always)
 *	%T	New To: line (Internet only if INTERNET is defined)
 *	%u	Number of unread articles
 *	%U	Number of unread articles disregarding current article
 *	%x	News library directory, usually /usr/lib/news
 *	%X	Rn library directory, usually %x/rn
 *	%~	Home directory
 *	%.	Directory containing . files
 *	%$	current process number
 *	%{name} Environment variable "name".  %{name-default} form allowed.
 *	%[name]	Header line beginning with "Name: ", without "Name: " 
 *	%(test_text=pattern?if_text:else_text)
 *		Substitute if_text if test_text matches pattern, otherwise
 *		substitute else_text.  Use != for negated match.
 *		% substitutions are done on test_text, if_text, and else_text.
 *		(Note: %() only works if CONDSUB defined.)
 *	%digit	Substitute the text matched by the nth bracket in the last
 *		pattern that had brackets.  %0 matches the last bracket
 *		matched, in case you had alternatives.
 *
 *	Put ^ in the middle to capitalize the first letter: %^C = Net.jokes
 *	Put ` in the middle to capitalize last component: %`c = net/Jokes
 *
 *	~ interpretation in filename expansion happens after % expansion, so
 *	you could put ~%{NEWSLOGNAME-news} and it will expand correctly.
 */

/* *** System Dependent Stuff *** */

/* NOTE: many of these are defined in the config.h file */

/* name of organization */
#ifndef ORGNAME
#   define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota"
#endif

#ifndef MBOXCHAR
#   define MBOXCHAR 'F'	/* how to recognize a mailbox by 1st char */
#endif

#ifndef ROOTID
#   define ROOTID 0        /* uid of superuser */
#endif

#ifdef NORMSIG
#   define sigset signal
#   define sigignore(sig) signal(sig,SIG_IGN)
#endif

#ifndef LOGDIRFIELD
#   define LOGDIRFIELD 6		/* Which field (origin 1) is the */
					/* login directory in /etc/passwd? */
					/* (If it is not kept in passwd, */
					/* but getpwnam() returns it, */
					/* define the symbol GETPWENT) */
#endif
#ifndef GCOSFIELD
#   define GCOSFIELD 5
#endif

#ifndef NEGCHAR
#   define NEGCHAR '!'
#endif

/* Space conservation section */

/* To save D space, cut down size of MAXRCLINE, NGMAX, VARYSIZE. */
#define MAXRCLINE 500	/* number of lines allowed in .newsrc */
			/* several parallel arrays affected. */
			/* (You can have more lines in the active file, */
			/* just not in the .newsrc) */
#define HASHSIZ 547	/* should be prime, and at least MAXRCLINE + 10% */
#define NGMAX 100	/* number of newsgroups allowed on command line */
			/* undefine ONLY symbol to disable "only" feature */
#define VARYSIZE 256	/* this makes a block 1024 bytes long in DECville */
			/* (used by virtual array routines) */

/* Undefine any of the following features to save both I and D space */
/* In general, earlier ones are easier to get along without */
/* Pdp11's without split I and D may have to undefine them all */
#define DEBUGGING	/* include debugging code */
#define SPEEDOVERMEM	/* use more memory to run faster */
#define WORDERASE	/* enable ^W to erase a word */
#define MAILCALL	/* check periodically for mail */
#define NOFIREWORKS	/* keep whole screen from flashing on certain */
			/* terminals such as older Televideos */
#define VERIFY		/* echo the command they just typed */
#define HASHNG		/* hash newsgroup lines for fast lookup-- */
			/* linear search used if not defined */
#define CONDSUB		/* allow %(cond?text:text) */
#define ULSMARTS	/* catch _^H in text and do underlining */
#define BAUDMOD		/* allow baudrate modifier on switches */
#define GETLOGIN	/* use getlogin() routine as backup to environment */
			/* variables USER or LOGNAME */
#define ORGFILE		/* if organization begins with /, look up in file */
#define TILDENAME	/* allow ~logname expansion */
#define SETENV		/* allow command line environment variable setting */
#define GETWD		/* use our getwd() instead of piped in pwd */
#define SETUIDGID	/* substitute eaccess() for access() so that rn */
			/* can run setuid or setgid */
			/* if not setuid or setgid, you don't need it */
#define MAKEDIR		/* use our makedir() instead of shell script */
#define MEMHELP		/* keep help messages in memory */
#define VERBOSE		/* compile in more informative messages */
#define TERSE		/* compile in shorter messages */
			/* (Note: both VERBOSE and TERSE can be defined; -t
			 * sets terse mode.  One or the other MUST be defined.
			 */
#ifndef pdp11
#   define CACHESUBJ	/* cache subject lines in memory */
			/* without this ^N still works but runs really slow */
			/* but you save lots and lots of D space */
#   define CACHEFIRST	/* keep absolute first article numbers in memory */
			/* cost: about 2k */
#endif
#define ROTATION	/* enable x, X and ^X commands to work */
#define DELBOGUS	/* ask if bogus newsgroups should be deleted */
#define RELOCATE	/* allow newsgroup rearranging */
#define ESCSUBS		/* escape substitutions in multi-character commands */
#define DELAYMARK	/* allow articles to be temporarily marked as read */
			/* until exit from current newsgroup or Y command */
#define MCHASE		/* unmark xrefed articles on m or M */
#define MUNGHEADER	/* allow alternate header formatting via */
			/* environment variable ALTHEADER (not impl) */
#define ASYNC_PARSE	/* allow parsing headers asyncronously to reading */
			/* used by MCHASE and MUNGHEADER */
#define FINDNEWNG	/* check for new newsgroups on startup */
#define FASTNEW		/* do optimizations on FINDNEWNG for faster startup */
			/* (this optimization can make occasional mistakes */
			/* if a group is removed and another group of the */
			/* same length is added, and if no softpointers are */
			/* affected by said change.) */
#define INNERSEARCH	/* search command 'g' with article */
#define CATCHUP		/* catchup command at newsgroup level */
#define NGSEARCH	/* newsgroup pattern matching */
#define ONLY		/* newsgroup restrictions by pattern */
#define KILLFILES	/* automatic article killer files */
#define ARTSEARCH	/* pattern searches among articles */
			/* /, ?, ^N, ^P, k, K */

/* some dependencies among options */

#ifndef ARTSEARCH
#   undef KILLFILES
#   undef INNERSEARCH
#   undef CACHESUBJ
#endif

#ifndef DELAYMARK
#   ifndef MCHASE
#	ifndef MUNGHEADER
#	    undef ASYNC_PARSE
#	endif
#   endif
#endif

#ifndef SETUIDGID
#   define eaccess access
#endif

#ifdef ONLY				/* idiot lint doesn't grok #if */
#   define NGSORONLY
#else
#   ifdef NGSEARCH
#	define NGSORONLY
#   endif
#endif

#ifdef VERBOSE
#   ifdef TERSE
#	define IF(c) if (c)
#	define ELSE else
#   else !TERSE
#	define IF(c)
#	define ELSE
#   endif
#else !VERBOSE
#   ifndef TERSE
#	define TERSE
#   endif
#   define IF(c) "IF" outside of VERBOSE???
#   define ELSE "ELSE" outside of VERBOSE???
#endif

#ifndef DEBUGGING
#   define NDEBUG
#endif
#include <assert.h>

#ifdef SPEEDOVERMEM
#   define OFFSET(x) (x)
#else
#   define OFFSET(x) ((x)-absfirst)
#endif

/* If you're strapped for space use the help messages in shell scripts */
/* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */
#ifdef MEMHELP  /* undef MEMHELP above to get them all as sh scripts */
#   undef NGHELP
#   undef ARTHELP
#   undef PAGERHELP
#   undef SUBSHELP
#else
#   ifndef NGHELP			/* % and ~ */
#	define NGHELP "%X/ng.help"
#   endif
#   ifndef ARTHELP			/* % and ~ */
#	define ARTHELP "%X/art.help"
#   endif
#   ifndef PAGERHELP		/* % and ~ */
#	define PAGERHELP "%X/pager.help"
#   endif
#   ifndef SUBSHELP		/* % and ~ */
#	define SUBSHELP "%X/subs.help"
#   endif
#endif

/* Additional ideas:
 *	Make the do_newsgroup() routine a separate process.
 *	Keep .newsrc on disk instead of in memory.
 *	Overlays, if you have them.
 *	Get a bigger machine.
 */

/* End of Space Conservation Section */

/* More System Dependencies */

/* news library */
#ifndef LIB		/* ~ and %l only ("~%l" is permissable) */
#   define LIB "/usr/lib/news"
#endif

/* path to private executables */
#ifndef RNLIB		/* ~, %x and %l only */
#   define RNLIB "%x/rn"
#endif

/* where to find news files */
#ifndef SPOOL			/* % and ~ */
#   define SPOOL "/usr/spool/news"
#endif

/* file containing list of active newsgroups and max article numbers */
#ifndef ACTIVE			/* % and ~ */
#   define ACTIVE "%x/active"
#endif

/* command to setup a new .newsrc */
#ifndef NEWSETUP		/* % and ~ */
#   define NEWSETUP "newsetup"
#endif

/* command to display a list of un-subscribed-to newsgroups */
#ifndef NEWSGROUPS		/* % and ~ */
#   define NEWSGROUPS "newsgroups"
#endif

/* preferred shell for use in doshell routine */
/*  ksh or sh would be okay here */
#ifndef PREFSHELL
#   define PREFSHELL "/bin/csh"
#endif

/* path to fastest starting shell */
#ifndef SH
#   define SH "/bin/sh"
#endif

/* path to default editor */
#ifndef DEFEDITOR
#   define DEFEDITOR "/usr/ucb/vi"
#endif

/* location of full name */
#ifndef FULLNAMEFILE
#   ifndef PASSNAMES
#	define FULLNAMEFILE "%./fullname"
#   endif
#endif

/* virtual array file name template */
#ifndef VARYNAME		/* % and ~ */
#   define VARYNAME "/tmp/rnvary.%$"
#endif

/* file to pass header to followup article poster */
#ifndef HEADNAME		/* % and ~ */
#   define HEADNAME "~/.rnhead"
/* or alternately #define HEADNAME "/tmp/rnhead.%$" */
#endif

#ifndef MAKEDIR
/* shell script to make n-deep subdirectories */
#   ifndef DIRMAKER		/* % and ~ */
#	define DIRMAKER "%X/makedir"
#   endif
#endif

/* location of newsrc file */
#ifndef RCNAME		/* % and ~ */
#   define RCNAME "%./.newsrc"
#endif

/* temporary newsrc file in case we crash while writing out */
#ifndef RCTNAME		/* % and ~ */
#   define RCTNAME "%./#.newsrc"
#endif

/* newsrc file at the beginning of this session */
#ifndef RCBNAME		/* % and ~ */
#   define RCBNAME "%./.oldnewsrc"
#endif

/* if existent, contains process number of current or crashed rn */
#ifndef LOCKNAME		/* % and ~ */
#   define LOCKNAME "%./.rnlock"
#endif

/* information from last invocation of rn */
#ifndef LASTNAME		/* % and ~ */
#   define LASTNAME "%./.rnlast"
#endif

/* file with soft pointers into the active file */
#ifndef SOFTNAME		/* % and ~ */
#   define SOFTNAME "%./.rnsoft"
#endif

/* list of article numbers to mark as unread later (see M and Y cmmands) */
#ifndef RNDELNAME		/* % and ~ */
#   define RNDELNAME "%./.rndelay"
#endif

/* a motd-like file for rn */
#ifndef NEWSNEWSNAME		/* % and ~ */
#   define NEWSNEWSNAME "%X/newsnews"
#endif

/* command to send a reply */
#ifndef MAILPOSTER		/* % and ~ */
#   define MAILPOSTER "Rnmail -h %h"
#endif

#ifndef MAILHEADER		/* % */
#   ifdef CONDSUB
#	define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
#   else
#	define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
#   endif
#endif

#ifndef YOUSAID			/* % */
#   define YOUSAID "In article %i you write:"
#endif

/* command to submit a followup article */
#ifndef NEWSPOSTER		/* % and ~ */
#   define NEWSPOSTER "Pnews -h %h"
#endif

#ifndef NEWSHEADER		/* % */
#   define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nReply-To: %L@%H.UUCP (%N)\nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: \n\n"
#endif

#ifndef ATTRIBUTION		/* % */
#   define ATTRIBUTION "In article %i %f writes:"
#endif

#ifndef PIPESAVER		/* % */
#   ifdef CONDSUB
#	define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b"
#   else
#	define PIPESAVER "tail +%Bc %A | %b"
#   endif
#endif

#ifndef NORMSAVER		/* % and ~ */
#   define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\""
#endif

#ifndef MBOXSAVER		/* % and ~ */
#   ifdef CONDSUB
#	define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From: %T %(%[posted]!=^$?%[posted]:%(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4))\""
					/* header munging with a vengeance */
#   else
#	define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From: %T %[posted]\""
#   endif
#endif

#ifdef MKDIRS

#   ifndef SAVEDIR			/* % and ~ */
#	define SAVEDIR "%p/%c"
#   endif
#   ifndef SAVENAME		/* % */
#	define SAVENAME "%a"
#   endif

#else

#   ifndef SAVEDIR			/* % and ~ */
#	define SAVEDIR "%p"
#   endif
#   ifndef SAVENAME		/* % */
#	define SAVENAME "%^C"
#   endif

#endif

#ifndef KILLGLOBAL		/* % and ~ */
#   define KILLGLOBAL "%p/KILL"
#endif

#ifndef KILLLOCAL		/* % and ~ */
#   define KILLLOCAL "%p/%c/KILL"
#endif

/* how to cancel an article */
#ifndef CANCEL
#   ifdef MININACT			/* 2.10.2 ? */
#	define CANCEL "%x/inews -h < %h"
#   else
#	define CANCEL "inews -h < %h"
#   endif
#endif

/* how to cancel an article, continued */
#ifndef CANCELHEADER
#   define CANCELHEADER "Newsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nReply-To: %L@%H.UUCP (%N)\nDistribution: %D\nOrganization: %o\n\n%i cancelled from rn.\n"
#endif

/* where to find the mail file */
#ifndef MAILFILE
#   define MAILFILE "/usr/spool/mail/%L"
#endif

/* some important types */

typedef int		NG_NUM;		/* newsgroup number */
typedef long		ART_NUM;	/* article number */
#ifdef pdp11
    typedef short	ART_UNREAD;	/* ordinarily this should be long */
					/* like ART_NUM, but assuming that */
					/* we stay less than 32767 articles */
					/* behind saves a lot of space. */
					/* NOTE: do not make unsigned. */
#else
    typedef long	ART_UNREAD;
#endif
typedef long		ART_POS;	/* char position in article file */
typedef int		ART_LINE;	/* line position in article file */
typedef short		ACT_POS;	/* char position in active file */
typedef unsigned int	MEM_SIZE;	/* for passing to malloc */

/* *** end of the machine dependent stuff *** */

/* GLOBAL THINGS */

/* file statistics area */

EXT struct stat filestat;

/* various things of type char */

char	*index();
char	*rindex();
char	*getenv();
char	*strcat();
char	*strcpy();
char	*sprintf();

EXT char buf[LBUFLEN+1];	/* general purpose line buffer */
EXT char cmd_buf[CBUFLEN];	/* buffer for formatting system commands */

EXT char *indstr INIT(">");	/* indent for old article embedded in followup */

EXT char *cwd INIT(Nullch);		/* current working directory */
EXT char *dfltcmd INIT(Nullch);	/* 1st char is default command */

/* switches */

#ifdef DEBUGGING
    EXT int debug INIT(0);				/* -D */
#   define DEB_INNERSRCH 32 
#   define DEB_FILEXP 64 
#   define DEB_HASH 128
#   define DEB_XREF_MARKER 256
#   define DEB_CTLAREA_BITMAP 512
#   define DEB_SOFT_POINTERS 1024
#   define DEB_NEWSRC_LINE 2048
#   define DEB_SEARCH_AHEAD 4096
#   define DEB_CHECKPOINTING 8192
#   define DEB_FEED_XREF 16384
#endif

#ifdef ARTSEARCH
    EXT int scanon INIT(0);				/* -S */
#endif

EXT bool mbox_always INIT(FALSE);			/* -M */
EXT bool norm_always INIT(FALSE);			/* -N */
EXT bool checkflag INIT(FALSE);			/* -c */
EXT bool suppress_cn INIT(FALSE);			/* -s */
EXT int countdown INIT(5);	/* how many lines to list before invoking -s */
EXT bool muck_up_clear INIT(FALSE);			/* -loco */
EXT bool erase_screen INIT(FALSE);			/* -e */
EXT bool findlast INIT(FALSE);			/* -r */
EXT bool typeahead INIT(FALSE);			/* -T */
#ifdef VERBOSE
#   ifdef TERSE
	EXT bool verbose INIT(TRUE);			/* +t */
#   endif
#endif
#ifdef VERIFY
    EXT bool verify INIT(FALSE);			/* -v */
#endif

#define NOMARKING 0
#define STANDOUT 1
#define UNDERLINE 2
EXT int marking INIT(NOMARKING);			/* -m */

EXT ART_LINE initlines INIT(0);		/* -i */

/* miscellania */

EXT bool in_ng INIT(FALSE);		/* current state of rn */

EXT FILE *tmpfp INIT(Nullfp);	/* scratch fp used for .rnlock, .rnlast, etc. */

EXT NG_NUM nextrcline INIT(0);	/* 1st unused slot in rcline array */
			/* startup to avoid checking twice in a row */

extern errno;

/* Factored strings */

EXT char nullstr[] INIT("");
EXT char sh[] INIT(SH);
EXT char defeditor[] INIT(DEFEDITOR);
EXT char hforhelp[] INIT("Type h for help.\n");
EXT char badcr[] INIT("\nUnnecessary CR ignored.\n");
EXT char readerr[] INIT("rn read error");
EXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n");

#ifdef VERBOSE
    EXT char nocd[] INIT("Can't chdir to directory %s\n");
#else
    EXT char nocd[] INIT("Can't find %s\n");
#endif

!STUFFY!FUNK!
echo Extracting rcstuff.c
cat >rcstuff.c <<'!STUFFY!FUNK!'
/* $Header: rcstuff.c,v 4.1 84/09/24 12:05:08 lwall Exp $
 *
 * $Log:	rcstuff.c,v $
 * Revision 4.1  84/09/24  12:05:08  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.7  84/09/22  17:01:33  lwall
 * Suppressed soft pointer message on -c.
 * 
 * Revision 4.0.1.6  84/09/18  16:39:18  lwall
 * Instructed relocate_newsgroup() about ngmax[].
 * 
 * Revision 4.0.1.5  84/09/13  12:05:28  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.4  84/09/10  15:24:22  lwall
 * Delinted.
 * 
 * Revision 4.0.1.3  84/09/10  08:30:24  lwall
 * Newsgroup relocation now disables starthere optimization.
 * 
 * Revision 4.0.1.2  84/09/06  13:13:51  lwall
 * Turned = into L.
 * 
 * Revision 4.0.1.1  84/09/05  09:31:26  lwall
 * Made 'm' command force soft pointer writing.
 * 
 * Revision 4.0  84/09/04  09:52:07  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "ngdata.h"
#include "term.h"
#include "final.h"
#include "rn.h"
#include "intrp.h"
#include "only.h"
#include "rcln.h"
#include "INTERN.h"
#include "rcstuff.h"

char *rcname INIT(Nullch);		/* path name of .newsrc file */
char *rctname INIT(Nullch);		/* path name of temp .newsrc file */
char *rcbname INIT(Nullch);		/* path name of backup .newsrc file */
char *softname INIT(Nullch);		/* path name of .rnsoft file */
FILE *rcfp INIT(Nullfp);			/* .newsrc file pointer */

#ifdef HASHNG
    short hashtbl[HASHSIZ];
#endif

bool
rcstuff_init()
{
    register NG_NUM newng;
    register char *s;
    register int i;
    register bool foundany = FALSE;
    char *some_buf;
    long length;

#ifdef HASHNG
    for (i=0; i<HASHSIZ; i++)
	hashtbl[i] = -1;
#endif

    /* make filenames */

    rcname = savestr(filexp(RCNAME));
    rctname = savestr(filexp(RCTNAME));
    rcbname = savestr(filexp(RCBNAME));
    softname = savestr(filexp(SOFTNAME));
    
    /* make sure the .newsrc file exists */

    newsrc_check();

    /* open .rnsoft file containing soft ptrs to active file */

    tmpfp = fopen(softname,"r");
    if (tmpfp == Nullfp)
	writesoft = TRUE;

    /* read in the .newsrc file */

    for (nextrcline = 0;
	(some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch;
	nextrcline++) {
					/* for each line in .newsrc */
	char tmpbuf[10];

	newng = nextrcline;		/* get it into a register */
	length = len_last_line_got;	/* side effect of get_a_line */
	if (length <= 1) {		/* only a newline??? */
	    nextrcline--;		/* compensate for loop increment */
	    continue;
	}
	if (newng >= MAXRCLINE) {	/* check for overflow */
	    fputs("Too many lines in .newsrc\n",stdout);
	    finalize(1);
	}
	if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch)
	    softptr[newng] = atoi(tmpbuf);
	else
	    softptr[newng] = 0;
	some_buf[--length] = '\0';	/* wipe out newline */
	if (some_buf == buf) {
	    rcline[newng] = savestr(some_buf);
					/* make a semipermanent copy */
	}
	else {
	    /*NOSTRICT*/
	    some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
	    rcline[newng] = some_buf;
	}
#ifdef NOTDEF
	if (strnEQ(some_buf,"to.",3)) {	/* is this a non-newsgroup? */
	    nextrcline--;		/* destroy this line */
	    continue;
	}
#endif
	if (*some_buf == ' ' ||
	  *some_buf == '\t' ||
	  strnEQ(some_buf,"options",7)) {		/* non-useful line? */
	    toread[newng] = TR_JUNK;
	    rcchar[newng] = ' ';
	    rcnums[newng] = 0;
	    continue;
	}
	for (s = rcline[newng]; *s && *s != ':' && *s != NEGCHAR; s++) ;
	if (!*s) {
	    rcline[newng] = saferealloc(rcline[newng],(MEM_SIZE)length+2);
	    s = rcline[newng] + length;
	    *s = ':';
	    *(s+1) = '\0';
	}
	rcchar[newng] = *s;		/* salt away the : or ! */
	rcnums[newng] = (char)(s - rcline[newng]); 
	rcnums[newng]++;		/* remember where it was */
	*s = '\0';			/* null terminate newsgroup name */
#ifdef HASHNG
	if (!checkflag)
	    sethash(newng);
#endif
	if (rcchar[newng] == NEGCHAR) {
	    toread[newng] = TR_UNSUB;
	    continue;
	}

	/* now find out how much there is to read */

	if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
	    toread[newng] = TR_NONE;	/* no need to calculate now */
	else
	    set_toread(newng);
#ifdef VERBOSE
	if (!checkflag && softmisses == 1) {
	    softmisses++;		/* lie a little */
	    fputs("(Revising soft pointers--be patient.)\n",stdout);
	}
#endif
	if (toread[newng] > TR_NONE) {	/* anything unread? */
	    if (!foundany) {
		starthere = newng;
		foundany = TRUE;	/* remember that fact*/
	    }
	    if (suppress_cn) {		/* if no listing desired */
		if (checkflag) {	/* if that is all they wanted */
		    finalize(1);	/* then bomb out */
		}
	    }
	    else {
#ifdef VERBOSE
		IF(verbose)
		    printf("Unread news in %-20s %5ld article%s\n",
			rcline[newng],(long)toread[newng],
			toread[newng]==TR_ONE ? nullstr : "s");
		ELSE
#endif
#ifdef TERSE
		    printf("%s: %ld article%s\n",
			rcline[newng],(long)toread[newng],
			toread[newng]==TR_ONE ? nullstr : "s");
#endif
		if (int_count) {
		    countdown = 1;
		    int_count = 0;
		}
		if (countdown) {
		    if (! --countdown) {
			fputs("etc.\n",stdout);
			if (checkflag)
			    finalize(1);
			suppress_cn = TRUE;
		    }
		}
	    }
	}
    }
    fclose(rcfp);			/* close .newsrc */
    if (tmpfp != Nullfp)
	fclose(tmpfp);			/* close .rnsoft */
    if (paranoid)
	cleanup_rc();

    if (checkflag) {			/* were we just checking? */
	finalize(foundany);		/* tell them what we found */
    }
#ifdef DEBUGGING
    if (debug & DEB_HASH) {
	page_line = 1;
	for (i=0; i<HASHSIZ; i++) {
	    sprintf(buf,"%d	%d",i,hashtbl[i]);
	    print_lines(buf,NOMARKING);
	}
    }
#endif

    return foundany;
}

/* try to find or add an explicitly specified newsgroup */
/* returns TRUE if found or added, FALSE if not. */
/* assumes that we are chdir'ed to SPOOL */

bool
get_ng(what,do_reloc)
char *what;
bool do_reloc;
{
    char *ntoforget;
    char promptbuf[128];

#ifdef VERBOSE
    IF(verbose)
	ntoforget = "Type n to forget about this newsgroup.\n";
    ELSE
#endif
#ifdef TERSE
	ntoforget = "n to forget it.\n";
#endif
    set_ngname(what);
    ng = find_ng(ngname);
    if (ng == nextrcline) {		/* not in .newsrc? */
	if (eaccess(ngdir,0)) {
#ifdef VERBOSE
	    IF(verbose)
		printf("\n\007Newsgroup %s does not exist!\n",ngname);
	    ELSE
#endif
#ifdef TERSE
		printf("\n\007No %s!\n",ngname);
#endif
	    sleep(2);
	    return FALSE;
	}
#ifdef VERBOSE
	IF(verbose)
	    sprintf(promptbuf,"\nNewsgroup %s not in .newsrc--add? [yn] ",ngname);
	ELSE
#endif
#ifdef TERSE
	    sprintf(promptbuf,"\nAdd %s? [yn] ",ngname);
#endif
reask_add:
	in_char(promptbuf);
	putchar('\n');
	setdef(buf,"y");
#ifdef VERIFY
	printcmd();
#endif
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		printf("Type y or SP to add %s to your .newsrc.\n", ngname);
	    ELSE
#endif
#ifdef TERSE
		fputs("y or SP to add\n",stdout);
#endif
	    fputs(ntoforget,stdout);
	    goto reask_add;
	}
	else if (*buf == 'n' || *buf == 'q') {
	    return FALSE;
	}
	else if (*buf == 'y') {
	    ng = add_newsgroup(ngname);
	    do_reloc = FALSE;
	}
	else {
	    fputs(hforhelp,stdout);
	    goto reask_add;
	}
    }
    else if (rcchar[ng] == NEGCHAR) {	/* unsubscribed? */
#ifdef VERBOSE
	IF(verbose)
	    sprintf(promptbuf,
"\nNewsgroup %s is currently unsubscribed to--resubscribe? [yn] ",ngname);
	ELSE
#endif
#ifdef TERSE
	    sprintf(promptbuf,"\n%s unsubscribed--resubscribe? [yn] ",ngname);
#endif
reask_unsub:
	in_char(promptbuf);
	putchar('\n');
	setdef(buf,"y");
#ifdef VERIFY
	printcmd();
#endif
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		printf("Type y or SP to resubscribe to %s.\n", ngname);
	    ELSE
#endif
#ifdef TERSE
		fputs("y or SP to resubscribe.\n",stdout);
#endif
	    fputs(ntoforget,stdout);
	    goto reask_unsub;
	}
	else if (*buf == 'n' || *buf == 'q') {
	    return FALSE;
	}
	else if (*buf == 'y') {
	    rcchar[ng] = ':';
	}
	else {
	    fputs(hforhelp,stdout);
	    goto reask_unsub;
	}
    }

    /* now calculate how many unread articles in newsgroup */

    set_toread(ng);
#ifdef RELOCATE
    if (do_reloc)
	ng = relocate_newsgroup(ng,-1);
#endif
    return toread[ng] >= TR_NONE;
}

/* add a newsgroup to the .newsrc file (eventually) */

NG_NUM
add_newsgroup(ngn)
char *ngn;
{
    register NG_NUM newng = nextrcline++;
					/* increment max rcline index */
    
    rcnums[newng] = strlen(ngn) + 1;
    rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 1));
    strcpy(rcline[newng],ngn);		/* and copy over the name */
    *(rcline[newng] + rcnums[newng]) = '\0';
    rcchar[newng] = ':';		/* call it subscribed */
    toread[newng] = TR_NONE;	/* just for prettiness */
#ifdef HASHNG
    sethash(newng);			/* so we can find it again */
#endif
#ifdef RELOCATE
    return relocate_newsgroup(newng,-1);
#else
    return newng;
#endif
}

#ifdef RELOCATE
NG_NUM
relocate_newsgroup(ngx,newng)
NG_NUM ngx;
NG_NUM newng;
{
    char *dflt = (ngx!=current_ng ? "$^.L" : "$^L");
    char *tmprcline;
    ART_UNREAD tmptoread;
    char tmprcchar;
    char tmprcnums;
    ACT_POS tmpsoftptr;
    register NG_NUM i;
#ifdef DEBUGGING
    ART_NUM tmpngmax;
#endif
    
    starthere = 0;                      /* Disable this optimization */
    writesoft = TRUE;			/* Update soft pointer file */
    if (ngx < nextrcline-1) {
#ifdef HASHNG
	for (i=0; i<HASHSIZ; i++) {
	    if (hashtbl[i] > ngx)
		--hashtbl[i];
	    else if (hashtbl[i] == ngx)
		hashtbl[i] = nextrcline-1;
	}
#endif
	tmprcline = rcline[ngx];
	tmptoread = toread[ngx];
	tmprcchar = rcchar[ngx];
	tmprcnums = rcnums[ngx];
	tmpsoftptr = softptr[ngx];
#ifdef DEBUGGING
	tmpngmax = ngmax[ngx];
#endif
	for (i=ngx+1; i<nextrcline; i++) {
	    rcline[i-1] = rcline[i];
	    toread[i-1] = toread[i];
	    rcchar[i-1] = rcchar[i];
	    rcnums[i-1] = rcnums[i];
	    softptr[i-1] = softptr[i];
#ifdef DEBUGGING
	    ngmax[i-1] = ngmax[i];
#endif
	}
	rcline[nextrcline-1] = tmprcline;
	toread[nextrcline-1] = tmptoread;
	rcchar[nextrcline-1] = tmprcchar;
	rcnums[nextrcline-1] = tmprcnums;
	softptr[nextrcline-1] = tmpsoftptr;
#ifdef DEBUGGING
	ngmax[nextrcline-1] = tmpngmax;
#endif
    }
    if (current_ng > ngx)
	current_ng--;
    if (newng < 0) {
      reask_reloc:
#ifdef VERBOSE
	IF(verbose)
	    printf("\nPut newsgroup where? [%s] ", dflt);
	ELSE
#endif
#ifdef TERSE
	    printf("\nPut where? [%s] ", dflt);
#endif
	fflush(stdout);
      reinp_reloc:
	eat_typeahead();
	getcmd(buf);
	if (errno || *buf == '\f') {
			    /* if return from stop signal */
	    goto reask_reloc;	/* give them a prompt again */
	}
	setdef(buf,dflt);
#ifdef VERIFY
	printcmd();
#endif
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose) {
		printf("\n\n\
Type ^ to put the newsgroup first (position 0).\n\
Type $ to put the newsgroup last (position %d).\n", nextrcline-1);
		printf("\
Type . to put it before the current newsgroup (position %d).\n", current_ng);
		printf("\
Type -newsgroup name to put it before that newsgroup.\n\
Type +newsgroup name to put it after that newsgroup.\n\
Type a number between 0 and %d to put it at that position.\n", nextrcline-1);
		printf("\
Type L for a listing of newsgroups and their positions.\n");
	    }
	    ELSE
#endif
#ifdef TERSE
	    {
		printf("\n\n\
^ to put newsgroup first (pos 0).\n\
$ to put last (pos %d).\n", nextrcline-1);
		printf("\
. to put before current newsgroup (pos %d).\n", current_ng);
		printf("\
-newsgroup to put before newsgroup.\n\
+newsgroup to put after.\n\
number in 0-%d to put at that pos.\n", nextrcline-1);
		printf("\
L for list of .newsrc.\n");
	    }
#endif
	    goto reask_reloc;
	}
	else if (*buf == 'L') {
	    putchar('\n');
	    list_newsgroups();
	    goto reask_reloc;
	}
	else if (isdigit(*buf)) {
	    if (!finish_command(TRUE))	/* get rest of command */
		goto reinp_reloc;
	    newng = atoi(buf);
	    if (newng < 0)
		newng = 0;
	    if (newng >= nextrcline)
		return nextrcline-1;
	}
	else if (*buf == '^') {
	    putchar('\n');
	    newng = 0;
	}
	else if (*buf == '$') {
	    putchar('\n');
	    return nextrcline-1;
	}
	else if (*buf == '.') {
	    putchar('\n');
	    newng = current_ng;
	}
	else if (*buf == '-' || *buf == '+') {
	    if (!finish_command(TRUE))	/* get rest of command */
		goto reinp_reloc;
	    newng = find_ng(buf+1);
	    if (newng == nextrcline) {
		fputs("Not found.",stdout);
		goto reask_reloc;
	    }
	    if (*buf == '+')
		newng++;
	}
	else {
	    printf("\n%s",hforhelp);
	    goto reask_reloc;
	}
    }
    if (newng < nextrcline-1) {
#ifdef HASHNG
	for (i=0; i<HASHSIZ; i++) {
	    if (hashtbl[i] == nextrcline-1)
		hashtbl[i] = newng;
	    else if (hashtbl[i] >= newng)
		++hashtbl[i];
	}
#endif
	tmprcline = rcline[nextrcline-1];
	tmptoread = toread[nextrcline-1];
	tmprcchar = rcchar[nextrcline-1];
	tmprcnums = rcnums[nextrcline-1];
	tmpsoftptr = softptr[nextrcline-1];
#ifdef DEBUGGING
	tmpngmax = ngmax[nextrcline-1];
#endif
	for (i=nextrcline-2; i>=newng; i--) {
	    rcline[i+1] = rcline[i];
	    toread[i+1] = toread[i];
	    rcchar[i+1] = rcchar[i];
	    rcnums[i+1] = rcnums[i];
	    softptr[i+1] = softptr[i];
#ifdef DEBUGGING
	    ngmax[i+1] = ngmax[i];
#endif
	}
	rcline[newng] = tmprcline;
	toread[newng] = tmptoread;
	rcchar[newng] = tmprcchar;
	rcnums[newng] = tmprcnums;
	softptr[newng] = tmpsoftptr;
#ifdef DEBUGGING
	ngmax[newng] = tmpngmax;
#endif
    }
    if (current_ng >= newng)
	current_ng++;
    return newng;
}
#endif

/* List out the newsrc with annotations */

void
list_newsgroups()
{
    register NG_NUM i;
    char tmpbuf[2048];
    static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"};

    putchar('\n');
    page_line = 1;
    print_lines("\
  #  Status  Newsgroup\n\
",STANDOUT);
    for (i=0; i<nextrcline && !int_count; i++) {
	if (toread[i] >= 0)
	    set_toread(i);
	*(rcline[i] + rcnums[i] - 1) = rcchar[i];
	if (toread[i] > 0)
	    sprintf(tmpbuf,"%3d %6d   ",i,toread[i]);
	else
	    sprintf(tmpbuf,"%3d %7s  ",i,status[-toread[i]]);
	safecpy(tmpbuf+13,rcline[i],2034);
	*(rcline[i] + rcnums[i] - 1) = '\0';
	if (print_lines(tmpbuf,NOMARKING))
	    break;
    }
    int_count = 0;
}

/* find a newsgroup in .newsrc */

NG_NUM
find_ng(ngnam)
char *ngnam;
{
    register NG_NUM ngnum;
#ifdef HASHNG
    register int hashix = hash(ngnam);
    register int incr = 1;

    while ((ngnum = hashtbl[hashix]) >= 0) {
	if (strEQ(rcline[ngnum], ngnam) && toread[ngnum] >= TR_UNSUB)
	    return ngnum;
	hashix = (hashix + incr) % HASHSIZ;
	incr += 2;			/* offsets from original are in n*2 */
    }
    return nextrcline;			/* = notfound */

#else /* just do linear search */

    for (ngnum = 0; ngnum < nextrcline; ngnum++) {
	if (strEQ(rcline[ngnum],ngnam))
	    break;
    }
    return ngnum;
#endif
}

void
cleanup_rc()
{
    register NG_NUM ngx;
    register NG_NUM bogosity = 0;

#ifdef VERBOSE
    IF(verbose)
	fputs("Checking out your .newsrc--hang on a second...\n",stdout);
    ELSE
#endif
#ifdef TERSE
	fputs("Checking .newsrc--hang on...\n",stdout);
#endif
    for (ngx = 0; ngx < nextrcline; ngx++) {
	if (toread[ngx] >= TR_UNSUB) {
	    set_toread(ngx);		/* this may reset newsgroup */
					/* or declare it bogus */
	}
	if (toread[ngx] == TR_BOGUS)
	    bogosity++;
    }
    for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--)
	bogosity--;			/* discount already moved ones */
    if (nextrcline > 5 && bogosity > nextrcline / 2) {
	fputs(
"It looks like the active file is messed up.  Contact your news administrator,\n\
",stdout);
	fputs(
"leave the \"bogus\" groups alone, and they may come back to normal.  Maybe.\n\
",stdout);
    }
#ifdef RELOCATE
    else if (bogosity) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("Moving bogus newsgroups to the end of your .newsrc.\n",
		stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("Moving boguses to the end.\n",stdout);
#endif
	for (; ngx >= 0; ngx--) {
	    if (toread[ngx] == TR_BOGUS)
		relocate_newsgroup(ngx,nextrcline-1);
	}
#ifdef DELBOGUS
reask_bogus:
	in_char("Delete bogus newsgroups? [ny] ");
	putchar('\n');
	setdef(buf,"n");
#ifdef VERIFY
	printcmd();
#endif
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\
Type y to delete bogus newsgroups.\n\
Type n or SP to leave them at the end in case they return.\n\
",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("y to delete, n to keep\n",stdout);
#endif
	    goto reask_bogus;
	}
	else if (*buf == 'n' || *buf == 'q')
	    ;
	else if (*buf == 'y') {
	    while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0)
		--nextrcline;		/* real tough, huh? */
	}
	else {
	    fputs(hforhelp,stdout);
	    goto reask_bogus;
	}
#endif
    }
#else
#ifdef VERBOSE
    IF(verbose)
	fputs("You should edit bogus newsgroups out of your .newsrc.\n",
	    stdout);
    ELSE
#endif
#ifdef TERSE
	fputs("Edit boguses from .newsrc.\n",stdout);
#endif
#endif
    paranoid = FALSE;
}

#ifdef HASHNG
/* make an entry in the hash table for the current newsgroup */

void
sethash(thisng)
NG_NUM thisng;
{
    register int hashix = hash(rcline[thisng]);
    register int incr = 1;
#ifdef DEBUGGING
    static int hashhits = 0, hashtries = 0;
#endif

#ifdef DEBUGGING
    hashtries++;
#endif
    while (hashtbl[hashix] >= 0) {
#ifdef DEBUGGING
	hashhits++;
	if (debug & DEB_HASH) {
	    printf("  Hash hits: %d / %d\n",hashhits, hashtries);
	}
	hashtries++;
#endif
	hashix = (hashix + incr) % HASHSIZ;
	incr += 2;			/* offsets from original are in n*2 */
    }
    hashtbl[hashix] = thisng;
}

short prime[] = {1,2,-3,-5,7,11,-13,-17,19,23,-29,-31,37,41,-43,-47,53,57,-59,
    -61,67,71,-73,-79,83,89,-97,-101,1,1,1,1,1,1,1,1,1,1,1,1};

int
hash(ngnam)
register char *ngnam;
{
    register int i = 0;
    register int ch;
    register int sum = 0;
#ifdef DEBUGGING
    char *ngn = ngnam;
#endif

    while (ch = *ngnam++) {
	sum += (ch + i) * prime[i];   /* gives ~ 10% hits at 25% full */
	i++;
    }
#ifdef DEBUGGING
    if (debug & DEB_HASH)
	printf("hash(%s) => %d => %d\n",ngn, sum, (sum<0?-sum:sum)%HASHSIZ);
#endif
    if (sum < 0)
	sum = -sum;
    return sum % HASHSIZ;
}

#endif

void
newsrc_check()
{
    rcfp = fopen(rcname,"r");		/* open it */
    if (rcfp == Nullfp) {			/* not there? */
#ifdef VERBOSE
	IF(verbose)
	    fputs("\
Trying to set up a .newsrc file--running newsetup...\n\n\
",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("Setting up .newsrc...\n",stdout);
#endif
	if (doshell(sh,filexp(NEWSETUP)) ||
	    (rcfp = fopen(rcname,"r")) == Nullfp) {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\
Can't create a .newsrc--you must do it yourself.\n\
",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("(Fatal)\n",stdout);
#endif
	    finalize(1);
	}
    }
    else {
	UNLINK(rcbname);		/* unlink backup file name */
	link(rcname,rcbname);		/* and backup current name */
    }
}

/* write out the (presumably) revised .newsrc */

void
write_rc()
{
    register NG_NUM tmpng;
    register char *delim;

    rcfp = fopen(rctname, "w");		/* open .newsrc */

    /* write out each line*/

    for (tmpng = 0; tmpng < nextrcline; tmpng++) {
	if (rcnums[tmpng]) {
	    delim = rcline[tmpng] + rcnums[tmpng] - 1;
	    *delim = rcchar[tmpng];
	}
	else
	    delim = Nullch;
#ifdef DEBUGGING
	if (debug & DEB_NEWSRC_LINE)
	    printf("%s\n",rcline[tmpng]);
#endif
	fprintf(rcfp,"%s\n",rcline[tmpng]);
	if (delim)
	    *delim = '\0';		/* might still need this line */
    }

    fclose(rcfp);			/* close .newsrc */
    UNLINK(rcname);
    link(rctname,rcname);
    UNLINK(rctname);

    if (writesoft) {
	tmpfp = fopen(filexp(softname), "w");	/* open .rnsoft */
	for (tmpng = 0; tmpng < nextrcline; tmpng++) {
	    fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]);
	}
	fclose(tmpfp);
    }
}

!STUFFY!FUNK!
echo Extracting art.help.SH
cat >art.help.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting art.help (with variable substitutions)"
$spitshell >art.help <<!GROK!THIS!
$startsh
# $Header: art.help.SH,v 4.1 84/09/24 11:41:43 lwall Exp $
# 
# $Log:	art.help.SH,v $
# Revision 4.1  84/09/24  11:41:43  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/12  15:19:06  lwall
# Housekeeping.
# 
# Revision 4.0  84/09/04  09:49:29  lwall
# Baseline for netwide release
# 
# 
$pager <<'EOT'
Article Selection commands:

n,SP	Scan forward for next unread article.
N	Go to next article.
^N	Scan forward for next unread article with same subject.
p,P,^P	Same as n,N,^N, only going backwards.
-	Go to previously displayed article.
number	Go to specified article.
range{,range} command{:command}
	Apply one or more commands to one or more ranges of articles.
	Ranges are of the form: number | number-number.  You may use . for
	the current article, and $ for the last article.
 	Valid commands are: j, m, M, s, S, and !.
/pattern/modifiers
	Scan forward for article containing pattern in the subject line.
	(Use ?pat? to scan backwards; append h to scan headers, a to scan
	entire articles, r to scan read articles, c to make case sensitive.
/pattern/modifiers:command{:command}
	Apply one or more commands to the set of articles matching pattern.
	Use a K modifier to save entire command to the KILL file for this
	newsgroup.  Commands m and M, if first, imply an r modifier.
 	Valid commands are: j, m, M, s, S, and !.
f,F	Submit a followup article (F = include this article).
r,R	Reply through net mail (R = include this article).
s ...	Save to file or pipe via sh.
S ...	Save via preferred shell.
w,W	Like s and S but save without the header.
| ...	Same as s|...
C	Cancel this article, if yours.
^R,v	Restart article (v=verbose).
^X	Restart article, rot13 mode.
c	Catch up (mark all articles as read).
^B	Back up one page.
^L	Refresh the screen.  You can get back to the pager with this.
X	Refresh screen in rot13 mode.
^	Go to first unread article.  Disables subject search mode.
$	Go to end of newsgroup.  Disables subject search mode.
#	Print last article number.
&	Print current values of command-line switches.
&switch {switch}
	Set or unset more switches.
j	Junk this article (mark it read).  Stays at end of article.
m	Mark article as still unread.
M	Mark article as still unread upon exiting newsgroup or Y command.
Y	Yank back articles marked temporarily read via M.
k	Mark current SUBJECT as read.
K	Mark current SUBJECT as read, and save command in KILL file.
=	List subjects of unread articles.
u	Unsubscribe to this newsgroup.
^K	Edit local KILL file (the one for this newsgroup).
q	Quit this newsgroup for now.
EOT

!GROK!THIS!
$eunicefix art.help
chmod 755 art.help
!STUFFY!FUNK!
echo ""
echo "End of kit 3 (of 8)"
cat /dev/null >kit3isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 4 (of 8).  If kit 4 is complete, the line"
echo '"'"End of kit 4 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting intrp.c
cat >intrp.c <<'!STUFFY!FUNK!'
/* $Header: intrp.c,v 4.1 84/09/24 11:57:13 lwall Exp $
 *
 * $Log:	intrp.c,v $
 * Revision 4.1  84/09/24  11:57:13  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.4  84/09/19  17:08:13  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.3  84/09/12  17:36:37  lwall
 * pwd.h now included by common.h.
 * 
 * Revision 4.0.1.2  84/09/10  15:13:49  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/04  15:14:39  lwall
 * LINKART option for Eunice sites.
 * 
 * Revision 4.0  84/09/04  09:50:48  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "search.h"
#include "head.h"
#include "rn.h"
#include "artsrch.h"
#include "ng.h"
#include "util.h"
#include "respond.h"
#include "rcstuff.h"
#include "bits.h"
#include "artio.h"
#include "INTERN.h"
#include "intrp.h"

char orgname[] = ORGNAME;

/* name of this site */
#ifdef GETHOSTNAME
    char *hostname;
#   undef SITENAME
#   define SITENAME hostname
#else !GETHOSTNAME
#   ifdef DOUNAME
#	include <sys/utsname.h>
	struct utsname uts;
#	undef SITENAME
#	define SITENAME uts.sysname
#   else !DOUNAME
#	ifdef PHOSTNAME
	    char *hostname;
#	    undef SITENAME
#	    define SITENAME hostname
#	else !PHOSTNAME
#	    ifdef WHOAMI
#		undef SITENAME
#		define SITENAME sysname
#	    endif WHOAMI
#	endif PHOSTNAME
#   endif DOUNAME
#endif GETHOSTNAME

#ifdef TILDENAME
static char *tildename = Nullch;
static char *tildedir = Nullch;
#endif

char *realname INIT(Nullch);	/* real name of sender from /etc/passwd */

char *dointerp();
char *getrealname();

void
intrp_init(tcbuf)
char *tcbuf;
{
    char *getlogin();

    lib = savestr(filexp(LIB));
    rnlib = savestr(filexp(RNLIB));
    spool = savestr(filexp(SPOOL));	/* usually /usr/spool/news */

    getwd(tcbuf);			/* find working directory name */
    origdir = savestr(tcbuf);		/* and remember it */
    
    /* get environmental stuff */

    /* get home directory */

    homedir = getenv("HOME");
    if (homedir == Nullch)
	homedir = getenv("LOGDIR");

    dotdir = getval("DOTDIR",homedir);

    /* get the real name of the person (%N) */

    strcpy(tcbuf,getrealname(getuid()));
    realname = savestr(tcbuf);

    /* get login name */

    logname = getenv("USER");
    if (logname == Nullch)
	logname = getenv("LOGNAME");
#ifdef GETLOGIN
    if (logname == Nullch)
	logname = savestr(getlogin());
#endif

    /* name of header file (%h) */

    headname = savestr(filexp(HEADNAME));

    /* name of this site (%H) */

#ifdef GETHOSTNAME
    gethostname(buf,sizeof buf);
    hostname = savestr(buf);
#else
#ifdef DOUNAME
    /* get sysname */
    uname(&uts);
#else
#ifdef PHOSTNAME
    {
	FILE *popen();
	FILE *pipefp = popen(PHOSTNAME,"r");

	fgets(buf,sizeof buf,tmpfp);
	buf[strlen(buf)-1] = '\0';	/* wipe out newline */
	hostname = savestr(buf);
	pclose(pipefp);
    }
#endif
#endif
#endif
    sitename = savestr(SITENAME);
}

/* expand filename via %, ~, and $ interpretation */
/* returns pointer to static area */
/* Note that there is a 1-deep cache of ~name interpretation */

char *
filexp(s)
register char *s;
{
    static char filename[MAXFILENAME];
    char scrbuf[MAXFILENAME];
    register char *d;

#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("< %s\n",s);
#endif
    interp(filename,s);			/* interpret any % escapes */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("%% %s\n",filename);
#endif
    s = filename;
    if (*s == '~') {	/* does destination start with ~? */
	if (!*(++s) || *s == '/') {
	    sprintf(scrbuf,"%s%s",homedir,s);
				/* swap $HOME for it */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("~ %s\n",scrbuf);
#endif
	    strcpy(filename,scrbuf);
	}
	else {
#ifdef TILDENAME
	    for (d=scrbuf; isalnum(*s); s++,d++)
		*d = *s;
	    *d = '\0';
	    if (tildedir && strEQ(tildename,scrbuf)) {
		strcpy(scrbuf,tildedir);
#ifdef DEBUGGING
		if (debug & DEB_FILEXP)
		    printf("r %s %s\n",tildename,tildedir);
#endif
	    }
	    else {
		if (tildename) {
		    free(tildename);
		    free(tildedir);
		    tildedir = Nullch;
		    tildename = savestr(scrbuf);
		}
#ifdef GETPWENT		/* getpwnam() is not the paragon of efficiency */
		{
		    struct passwd *getpwnam();
		    struct passwd *pwd = getpwnam(tildename);

		    sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
		    tildedir = savestr(pwd->pw_dir);
#ifdef NEWSADMIN
		    if (strEQ(newsadmin,tildename))
			newsuid = atoi(pwd->pw_uid);
#endif
		    strcpy(filename,scrbuf);
#ifdef GETPWENT
		    endpwent();
#endif
		}
#else			/* this will run faster, and is less D space */
		{	/* just be sure LOGDIRFIELD is correct */
		    FILE *pfp = fopen("/etc/passwd","r");
		    char tmpbuf[512];
		    int i;
		    
		    while (fgets(tmpbuf,512,pfp) != Nullch) {
			d = cpytill(scrbuf,tmpbuf,':');
#ifdef DEBUGGING
			if (debug & DEB_FILEXP)
			    printf("p %s\n",tmpbuf);
#endif
			if (strEQ(scrbuf,tildename)) {
#ifdef NEWSADMIN
			    if (strEQ(newsadmin,tildename))
				newsuid = atoi(index(d,':')+1);
#endif
			    for (i=LOGDIRFIELD-2; i; i--) {
				if (d)
				    d = index(d+1,':');
			    }
			    if (d) {
				cpytill(scrbuf,d+1,':');
				tildedir = savestr(scrbuf);
				strcat(scrbuf,s);
				strcpy(filename,scrbuf);
			    }
			    break;
			}
		    }
		    fclose(pfp);
		}
#endif
	    }
#else !TILDENAME
#ifdef VERBOSE
	    IF(verbose)
		fputs("~loginname not implemented.\n",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("~login not impl.\n",stdout);
#endif
#endif
	}
    }
    else if (*s == '$') {	/* starts with some env variable? */
	d = scrbuf;
	*d++ = '%';
	if (s[1] == '{')
	    strcpy(d,s+2);
	else {
	    *d++ = '{';
	    for (s++; isalnum(*s); s++) *d++ = *s;
				/* skip over token */
	    *d++ = '}';
	    strcpy(d,s);
	}
#ifdef DEBUGGING
	if (debug & DEB_FILEXP)
	    printf("$ %s\n",scrbuf);
#endif
	interp(filename,scrbuf);	/* this might do some extra '%'s but */
					/* that is how the Mercedes Benz */
    }
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("> %s\n",filename);
#endif
    return filename;
}

/* interpret interpolations */

char *
dointerp(dest,pattern,stoppers)
register char *dest;
register char *pattern;
char *stoppers;
{
    char *subj_buf = Nullch;
    char *ngs_buf = Nullch;
    char *refs_buf = Nullch;
    char *artid_buf = Nullch;
    char *reply_buf = Nullch;
    char *from_buf = Nullch;
    char *path_buf = Nullch;
    char *follow_buf = Nullch;
    char *dist_buf = Nullch;
    char *line_buf = Nullch;
    register char *s, *h;
    register int i;
    char scrbuf[512];
    bool upper = FALSE;
    bool lastcomp = FALSE;

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
	if (*pattern == '%') {
	    upper = FALSE;
	    lastcomp = FALSE;
	    for (s=Nullch; !s; ) {
		switch (*++pattern) {
		case '^':
		    upper = TRUE;
		    break;
		case '`':
		    lastcomp = TRUE;
		    break;
		case '/':
#ifdef ARTSRCH
		    s = scrbuf;
		    if (!index("/?g",pattern[-2]))
			*s++ = '/';
		    strcpy(s,lastpat);
		    s += strlen(s);
		    if (pattern[-2] != 'g') {
			if (index("/?",pattern[-2]))
			    *s++ = pattern[-2];
			else
			    *s++ = '/';
			if (art_howmuch == 1)
			    *s++ = 'h';
			else if (art_howmuch == 2)
			    *s++ = 'a';
			if (art_doread)
			    *s++ = 'r';
		    }
		    *s = '\0';
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case '{':
		    pattern = cpytill(scrbuf,pattern+1,'}');
		    if (s = index(scrbuf,'-'))
			*s++ = '\0';
		    else
			s = nullstr;
		    s = getval(scrbuf,s);
		    break;
		case '[':
		    pattern = cpytill(scrbuf,pattern+1,']');
		    i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
		    if (line_buf)
			free(line_buf);
		    s = line_buf = fetchlines(art,i);
		    break;
#ifdef CONDSUB
		case '(': {
		    COMPEX *oldbra_compex = bra_compex;
		    COMPEX cond_compex;
		    char rch;
		    bool matched;
		    
		    init_compex(&cond_compex);
		    pattern = dointerp(dest,pattern+1,"!=");
		    rch = *pattern;
		    if (rch == '!')
			pattern++;
		    if (*pattern != '=')
			goto getout;
		    pattern = cpytill(scrbuf,pattern+1,'?');
		    if (!*pattern)
			goto getout;
		    if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
			printf("%s: %s\n",scrbuf,s);
			pattern += strlen(pattern);
			goto getout;
		    }
		    matched = (execute(&cond_compex,dest) != Nullch);
		    if (cond_compex.nbra)	/* were there brackets? */
			bra_compex = &cond_compex;
		    if (matched==(rch == '=')) {
			pattern = dointerp(dest,pattern+1,":)");
			if (*pattern == ':')
			    pattern = dointerp(scrbuf,pattern+1,")");
		    }
		    else {
			pattern = dointerp(scrbuf,pattern+1,":)");
			if (*pattern == ':')
			    pattern++;
			pattern = dointerp(dest,pattern,")");
		    }
		    s = dest;
		    bra_compex = oldbra_compex;
		    free_compex(&cond_compex);
		    break;
		}
#endif
		case '~':
		    s = homedir;
		    break;
		case '.':
		    s = dotdir;
		    break;
		case '$':
		    s = scrbuf;
		    sprintf(s,"%d",getpid());
		    break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
#ifdef CONDSUB
		    s = getbracket(bra_compex,*pattern - '0');
#else
		    s = nullstr;
#endif
		    break;
		case 'a':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)art);
		    break;
		case 'A':
#ifdef LINKART
		    s = linkartname;	/* so Eunice people get right file */
#else
		    s = scrbuf;
		    sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
#endif
		    break;
		case 'b':
		    s = savedest;
		    break;
		case 'B':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)savefrom);
		    break;
		case 'c':
		    s = ngdir;
		    break;
		case 'C':
		    s = ngname;
		    break;
		case 'd':
		    s = scrbuf;
		    sprintf(s,"%s/%s",spool,ngdir);
		    break;
		case 'D':
		    s = dist_buf = fetchlines(art,DIST_LINE);
		    break;
		case 'f':			/* from line */
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
						/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    break;
		case 'F':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[FOLLOW_LINE].ht_minpos >= 0)
					/* is there a Followup-To line? */
			s = follow_buf = fetchlines(art,FOLLOW_LINE);
		    else {
			int off;
		
			s = ngs_buf = fetchlines(art,NGS_LINE);
			if (h = instr(s,"net.general")) {
			    off = h-s;
			    strncpy(scrbuf,s,off+4);
			    strcpy(scrbuf+off+4,"followup");
			    safecpy(scrbuf+off+12,h+11,sizeof(scrbuf));
			    s = scrbuf;
			}
		    }
		    break;
		case 'h':			/* header file name */
		    s = headname;
		    break;
		case 'H':			/* host name */
		    s = sitename;
		    break;
		case 'i':
		    if (!(s=artid_buf))
			s = artid_buf = fetchlines(art,ARTID_LINE);
		    if (*s != '<') {
			sprintf(scrbuf,"<%s>",artid_buf);
			s = scrbuf;
		    }
		    break;
		case 'l':			/* rn library */
#ifdef NEWSADMIN
		    s = newsadmin;
#else
		    s = "???";
#endif
		    break;
		case 'L':			/* login id */
		    s = logname;
		    break;
		case 'M':
#ifdef DELAYMARK
		    sprintf(scrbuf,"%ld",(long)dmcount);
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case 'n':			/* newsgroups */
		    s = ngs_buf = fetchlines(art,NGS_LINE);
		    break;
		case 'N':			/* full name */
		    s = getval("NAME",realname);
		    break;
		case 'o':			/* organization */
		    s = getval("ORGANIZATION",orgname);
#ifdef ORGFILE
		    if (*s == '/') {
			FILE *ofp = fopen(s,"r");

			if (ofp) {
			    fgets(scrbuf,sizeof scrbuf,ofp);
			    fclose(ofp);
			    s = scrbuf;
			    s[strlen(s)-1] = '\0';
			}
		    }
#endif
		    break;
		case 'O':
		    s = origdir;
		    break;
		case 'p':
		    s = cwd;
		    break;
		case 'P':
		    s = spool;
		    break;
		case 'r':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REFS_LINE].ht_minpos >= 0) {
			refs_buf = fetchlines(art,REFS_LINE);
			refscpy(scrbuf,refs_buf);
		    }
		    else
			*scrbuf = '\0';
		    s = rindex(scrbuf,'<');
		    break;
		case 'R':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REFS_LINE].ht_minpos >= 0) {
			refs_buf = fetchlines(art,REFS_LINE);
			refscpy(scrbuf,refs_buf);
		    }
		    else
			*scrbuf = '\0';
		    if (!artid_buf)
			artid_buf = fetchlines(art,ARTID_LINE);
		    if (artid_buf[0] == '<')
			safecat(scrbuf,artid_buf,sizeof(scrbuf));
		    else {
			char tmpbuf[64];
    
			sprintf(tmpbuf,"<%s>",artid_buf);
			safecat(scrbuf,tmpbuf,sizeof(scrbuf));
		    }
		    s = scrbuf;
		    break;
		case 's':
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE,TRUE);
						/* get subject handy */
		    while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    if (h = instr(s,"- (nf"))
			*h = '\0';
		    break;
		case 'S':
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE,TRUE);
						/* get subject handy */
		    if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    break;
		case 't':
		case 'T':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
					/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    if (*pattern == 'T') {
			if (htype[PATH_LINE].ht_minpos >= 0) {
					/* should we substitute path? */
			    s = path_buf = fetchlines(art,PATH_LINE);
			}
			i = strlen(sitename);
			if (strnEQ(sitename,s,i) && s[i] == '!')
			    s += i + 1;
		    }
		    if ((h=index(s,'(')) != Nullch)
						/* strip garbage from end */
			*(h-1) = '\0';
		    else if ((h=index(s,'<')) != Nullch) {
						/* or perhaps from beginning */
			s = h+1;
			if ((h=index(s,'>')) != Nullch)
			    *h = '\0';
		    }
		    break;
		case 'u':
		    sprintf(scrbuf,"%ld",(long)toread[ng]);
		    s = scrbuf;
		    break;
		case 'U':
		    sprintf(scrbuf,"%ld",
			(long)(((ART_NUM)toread[ng]) - 1 + was_read(art)));
		    s = scrbuf;
		    break;
		case 'x':			/* rn library */
		    s = lib;
		    break;
		case 'X':			/* rn library */
		    s = rnlib;
		    break;
		default:
		    *dest++ = *pattern;
		    s = nullstr;
		    break;
		}
	    }
	    if (!s)
		s = nullstr;
	    pattern++;
	    if (upper || lastcomp) {
		char *t;

		if (s != scrbuf) {
		    strcpy(scrbuf,s);
		    s = scrbuf;
		}
		if (upper || !(t=rindex(s,'/')))
		    t = s;
		while (*t && !isalpha(*t))
		    t++;
		if (islower(*t))
		    *t = toupper(*t);
	    }
	    if (s == dest) {
		while (*dest)
		    dest++;
	    }
	    else {
		while (*s)
		    *dest++ = *s++;
	    }
	}
	else if (*pattern == '\\') {
	    ++pattern;			/* skip backslash */
	    i = *pattern;		/* get char into a register */

	    /* this used to be a switch but the if may save space */
	    
	    if (i >= '0' && i <= '7') {
		i = 1;
		while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
		    i <<= 3;
		    i += *pattern++ - '0';
		}
		*dest++ = i & 0377;
		--pattern;
	    }
	    else if (i == 'b')
		*dest++ = '\b';
	    else if (i == 'f')
		*dest++ = '\f';
	    else if (i == 'n')
		*dest++ = '\n';
	    else if (i == 'r')
		*dest++ = '\r';
	    else if (i == 't')
		*dest++ = '\t';
	    else
		*dest++ = *pattern;
	    pattern++;
	}
	else
	    *dest++ = *pattern++;
    }
    *dest = '\0';
getout:
    if (subj_buf != Nullch)	/* return any checked out storage */
	free(subj_buf);
    if (ngs_buf != Nullch)
	free(ngs_buf);
    if (refs_buf != Nullch)
	free(refs_buf);
    if (artid_buf != Nullch)
	free(artid_buf);
    if (reply_buf != Nullch)
	free(reply_buf);
    if (from_buf != Nullch)
	free(from_buf);
    if (path_buf != Nullch)
	free(path_buf);
    if (follow_buf != Nullch)
	free(follow_buf);
    if (dist_buf != Nullch)
	free(dist_buf);
    if (line_buf != Nullch)
	free(line_buf);
    return pattern;			/* where we left off */
}

void
interp(dest,pattern)
char *dest;
char *pattern;
{
    dointerp(dest,pattern,Nullch);
}

/* copy a references line, normalizing as we go */

void
refscpy(dest,src)
register char *dest, *src;
{
    register char *dot, *at, *beg;
    char tmpbuf[64];
    
    while (*src) {
	if (*src != '<') {
	    *dest++ = '<';
	    at = dot = Nullch;
	    beg = src;
	    while (*src && *src != ' ' && *src != ',') {
		if (*src == '.')
		    dot = src;
		else if (*src == '@')
		    at = src;
		*dest++ = *src++;
	    }
	    if (dot && !at) {
		*dest = *dot++ = '\0';
		sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
		strcpy(dest,tmpbuf);
		dest = dest + strlen(dest);
	    }
	    *dest++ = '>';
	}
	else {
	    while (*src && (*dest++ = *src++) != '>') ;
	}
	while (*src == ' ' || *src == ',') src++;
	if (*src)
	    *dest++ = ' ';
    }
    *dest = '\0';
} 

/* get the person's real name from /etc/passwd */
/* (string is overwritten, so it must be copied) */

char *
getrealname(uid)
int uid;
{
    char *s, *c;

#ifdef PASSNAMES
#ifdef GETPWENT
    struct passwd *pwd = getpwuid(uid);
    
    s = pwd->pw_gcos;
#else
    char tmpbuf[512];
    int i;

    getpw(uid, tmpbuf);
    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
	if (s)
	    s = index(s,':')+1;
    }
    if (!s)
	return nullstr;
    cpytill(tmpbuf,s,':');
    s = tmpbuf;
#endif
#ifdef BERKNAMES
#ifdef BERKJUNK
    while (*s && !isalnum(*s) && *s != '&') s++;
#endif
    if ((c = index(s, ',')) != Nullch)
	*c = '\0';
    if ((c = index(s, ';')) != Nullch)
	*c = '\0';
    s = cpytill(buf,s,'&');
    if (*s == '&') {			/* whoever thought this one up was */
	strcat(buf,logname);		/* in the middle of the night */
	strcat(buf,s+1);		/* before the morning after */
	if (islower(*buf))
	    *buf = toupper(*buf);	/* gack and double gack */
    }
#else
    if ((c = index(s, '(')) != Nullch)
	*c = '\0';
    if ((c = index(s, '-')) != Nullch)
	s = c;
    strcpy(buf,tmpbuf);
#endif
#ifdef GETPWENT
    endpwent();
#endif
    return buf;				/* return something static */
#else
    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
	fgets(buf,sizeof buf,tmpfp);
	fclose(tmpfp);
	buf[strlen(buf)-1] = '\0';
	return buf;
    }
    return "PUT YOUR NAME HERE";
#endif
}

!STUFFY!FUNK!
echo Extracting search.c
cat >search.c <<'!STUFFY!FUNK!'
/* $Header: search.c,v 4.1 84/09/24 12:09:45 lwall Exp $
 *
 * $Log:	search.c,v $
 * Revision 4.1  84/09/24  12:09:45  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/12  17:34:48  lwall
 * Moved some includes to common.h.
 * 
 * Revision 4.0.1.2  84/09/10  15:29:52  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/05  10:38:46  lwall
 * Changed CEOF to CEND to avoid conflict with ttychars.h.
 * 
 * Revision 4.0  84/09/04  09:52:33  lwall
 * Baseline for netwide release
 * 
 */

/* string search routines */
 
/*		Copyright (c) 1981,1980 James Gosling		*/
 
/* Modified Aug. 12, 1981 by Tom London to include regular expressions
   as in ed.  RE stuff hacked over by jag to correct a few major problems,
   mainly dealing with searching within the buffer rather than copying
   each line to a separate array.  Newlines can now appear in RE's */

/* Ripped to shreds and glued back together to make a search package,
 * July 6, 1984, by Larry Wall. (If it doesn't work, it's probably my fault.)
 * Changes include:
 *	Buffer, window, and mlisp stuff gone.
 *	Translation tables reduced to 1 table.
 *	Expression buffer is now dynamically allocated.
 *	Character classes now implemented with a bitmap.
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "INTERN.h"
#include "search.h"

#ifndef BITSPERBYTE
#define BITSPERBYTE 8
#endif

#define BMAPSIZ (127 / BITSPERBYTE + 1)

/* meta characters in the "compiled" form of a regular expression */
#define	CBRA	2		/* \( -- begin bracket */
#define	CCHR	4		/* a vanilla character */
#define	CDOT	6		/* . -- match anything except a newline */
#define	CCL	8		/* [...] -- character class */
#define	NCCL	10		/* [^...] -- negated character class */
#define	CDOL	12		/* $ -- matches the end of a line */
#define	CEND	14		/* The end of the pattern */
#define	CKET	16		/* \) -- close bracket */
#define	CBACK	18		/* \N -- backreference to the Nth bracketed
				   string */
#define CIRC	20		/* ^ matches the beginning of a line */

#define WORD	32		/* matches word character \w */
#define NWORD	34		/* matches non-word characer \W */
#define WBOUND	36		/* matches word boundary \b */
#define NWBOUND	38		/* matches non-(word boundary) \B */
 
#define	STAR	01		/* * -- Kleene star, repeats the previous
				   REas many times as possible; the value
				   ORs with the other operator types */
 
#define ASCSIZ 0200
typedef char	TRANSTABLE[ASCSIZ];

static	TRANSTABLE trans = {
0000,0001,0002,0003,0004,0005,0006,0007,
0010,0011,0012,0013,0014,0015,0016,0017,
0020,0021,0022,0023,0024,0025,0026,0027,
0030,0031,0032,0033,0034,0035,0036,0037,
0040,0041,0042,0043,0044,0045,0046,0047,
0050,0051,0052,0053,0054,0055,0056,0057,
0060,0061,0062,0063,0064,0065,0066,0067,
0070,0071,0072,0073,0074,0075,0076,0077,
0100,0101,0102,0103,0104,0105,0106,0107,
0110,0111,0112,0113,0114,0115,0116,0117,
0120,0121,0122,0123,0124,0125,0126,0127,
0130,0131,0132,0133,0134,0135,0136,0137,
0140,0141,0142,0143,0144,0145,0146,0147,
0150,0151,0152,0153,0154,0155,0156,0157,
0160,0161,0162,0163,0164,0165,0166,0167,
0170,0171,0172,0173,0174,0175,0176,0177,
};
static bool folding = FALSE;

static int err;
static char *FirstCharacter;

void
search_init()
{
#ifdef UNDEF
    register int    i;
    
    for (i = 0; i < ASCSIZ; i++)
	trans[i] = i;
#else
    ;
#endif
}

void
init_compex(compex)
register COMPEX *compex;
{
    /* the following must start off zeroed */

    compex->eblen = 0;
    compex->brastr = Nullch;
}

void
free_compex(compex)
register COMPEX *compex;
{
    if (compex->eblen) {
	free(compex->expbuf);
	compex->eblen = 0;
    }
    if (compex->brastr) {
	free(compex->brastr);
	compex->brastr = Nullch;
    }
}

static char *gbr_str = Nullch;
static int gbr_siz = 0;

char *
getbracket(compex,n)
register COMPEX *compex;
int n;
{
    int length = compex->braelist[n] - compex->braslist[n];

    if (!compex->nbra || n > compex->nbra || !compex->braelist[n] || length<0)
	return nullstr;
    growstr(&gbr_str, &gbr_siz, length+1);
    safecpy(gbr_str, compex->braslist[n], length+1);
    return gbr_str;
}

void
case_fold(which)
int which;
{
    register int i;

    if (which != folding) {
	if (which) {
	    for (i = 'A'; i <= 'Z'; i++)
		trans[i] = tolower(i);
	}
	else {
	    for (i = 'A'; i <= 'Z'; i++)
		trans[i] = i;
	}
	folding = which;
    }
}

/* Compile the given regular expression into a [secret] internal format */

char *
compile (compex, strp, RE, fold)
register COMPEX *compex;
register char   *strp;
int RE;
int fold;
{
    register int c;
    register char  *ep;
    char   *lastep;
    char    bracket[NBRA],
	   *bracketp;
    char **alt = compex->alternatives;
    char *retmes = "Badly formed search string";
 
    case_fold(compex->do_folding = fold);
    if (!compex->eblen) {
	compex->expbuf = safemalloc(84);
	compex->eblen = 80;
    }
    ep = compex->expbuf;		/* point at expression buffer */
    *alt++ = ep;			/* first alternative starts here */
    bracketp = bracket;			/* first bracket goes here */
    if (*strp == 0) {			/* nothing to compile? */
	if (*ep == 0)			/* nothing there yet? */
	    return "Null search string";
	return Nullch;			/* just keep old expression */
    }
    compex->nbra = 0;			/* no brackets yet */
    lastep = 0;
    for (;;) {
	if (ep - compex->expbuf >= compex->eblen)
	    grow_eb(compex);
	c = *strp++;			/* fetch next char of pattern */
	if (c == 0) {			/* end of pattern? */
	    if (bracketp != bracket) {	/* balanced brackets? */
#ifdef VERBOSE
		retmes = "Unbalanced parens";
#endif
		goto cerror;
	    }
	    *ep++ = CEND;		/* terminate expression */
	    *alt++ = 0;			/* terminal alternative list */
	    /*
	    compex->eblen = ep - compex->expbuf + 1;
	    compex->expbuf = saferealloc(compex->expbuf,compex->eblen+4); */
	    return Nullch;		/* return success */
	}
	if (c != '*')
	    lastep = ep;
	if (!RE) {			/* just a normal search string? */
	    *ep++ = CCHR;		/* everything is a normal char */
	    *ep++ = c;
	}
	else				/* it is a regular expression */
	    switch (c) {
 
		case '\\':		/* meta something */
		    switch (c = *strp++) {
		    case '(':
			if (compex->nbra >= NBRA) {
#ifdef VERBOSE
			    retmes = "Too many parens";
#endif
			    goto cerror;
			}
			*bracketp++ = ++compex->nbra;
			*ep++ = CBRA;
			*ep++ = compex->nbra;
			break;
		    case '|':
			if (bracketp>bracket) {
#ifdef VERBOSE
			    retmes = "No \\| in parens";	/* Alas! */
#endif
			    goto cerror;
			}
			*ep++ = CEND;
			*alt++ = ep;
			break;
		    case ')':
			if (bracketp <= bracket) {
#ifdef VERBOSE
			    retmes = "Unmatched right paren";
#endif
			    goto cerror;
			}
			*ep++ = CKET;
			*ep++ = *--bracketp;
			break;
		    case 'w':
			*ep++ = WORD;
			break;
		    case 'W':
			*ep++ = NWORD;
			break;
		    case 'b':
			*ep++ = WBOUND;
			break;
		    case 'B':
			*ep++ = NWBOUND;
			break;
		    case '0': case '1': case '2': case '3': case '4':
		    case '5': case '6': case '7': case '8': case '9':
			*ep++ = CBACK;
			*ep++ = c - '0';
			break;
		    default:
			*ep++ = CCHR;
			if (c == '\0')
			    goto cerror;
			*ep++ = c;
			break;
		    }
		    break;
		case '.':
		    *ep++ = CDOT;
		    continue;
 
		case '*':
		    if (lastep == 0 || *lastep == CBRA || *lastep == CKET
			|| *lastep == CIRC
			|| (*lastep&STAR)|| *lastep>NWORD)
			goto defchar;
		    *lastep |= STAR;
		    continue;
 
		case '^':
		    if (ep != compex->expbuf && ep[-1] != CEND)
			goto defchar;
		    *ep++ = CIRC;
		    continue;
 
		case '$':
		    if (*strp != 0 && (*strp != '\\' || strp[1] != '|'))
			goto defchar;
		    *ep++ = CDOL;
		    continue;
 
		case '[': {		/* character class */
		    register int i;
		    
		    if (ep - compex->expbuf >= compex->eblen - BMAPSIZ)
			grow_eb(compex);	/* reserve bitmap */
		    for (i = BMAPSIZ; i; --i)
			ep[i] = 0;
		    
		    if ((c = *strp++) == '^') {
			c = *strp++;
			*ep++ = NCCL;	/* negated */
		    }
		    else
			*ep++ = CCL;	/* normal */
		    
		    i = 0;		/* remember oldchar */
		    do {
			if (c == '\0') {
#ifdef VERBOSE
			    retmes = "Missing ]";
#endif
			    goto cerror;
			}
			if (*strp == '-' && *(++strp))
			    i = *strp++;
			else
			    i = c;
			while (c <= i) {
			    ep[c / BITSPERBYTE] |= 1 << (c % BITSPERBYTE);
			    if (fold && isalpha(c))
				ep[(c ^ 32) / BITSPERBYTE] |=
				    1 << ((c ^ 32) % BITSPERBYTE);
					/* set the other bit too */
			    c++;
			}
		    } while ((c = *strp++) != ']');
		    ep += BMAPSIZ;
		    continue;
		}
 
	    defchar:
		default:
		    *ep++ = CCHR;
		    *ep++ = c;
	    }
    }
cerror:
    compex->expbuf[0] = 0;
    compex->nbra = 0;
    return retmes;
}

void
grow_eb(compex)
register COMPEX *compex;
{
    compex->eblen += 80;
    compex->expbuf = saferealloc(compex->expbuf, (MEM_SIZE)compex->eblen + 4);
}

char *
execute (compex, addr)
register COMPEX *compex;
char *addr;
{
    register char *p1 = addr;
    register char *trt = trans;
    register int c;
 
    if (addr == Nullch)
	return Nullch;
    if (compex->nbra) {			/* any brackets? */
	for (c = 0; c <= compex->nbra; c++)
	    compex->braslist[c] = compex->braelist[c] = Nullch;
	if (compex->brastr)
	    free(compex->brastr);
	compex->brastr = savestr(p1);	/* in case p1 is not static */
	p1 = compex->brastr;		/* ! */
    }
    case_fold(compex->do_folding);	/* make sure table is correct */
    FirstCharacter = p1;		/* for ^ tests */
    if (compex->expbuf[0] == CCHR && !compex->alternatives[1]) {
	c = trt[compex->expbuf[1]];	/* fast check for first character */
	do {
	    if (trt[*p1] == c && advance (compex, p1, compex->expbuf))
		return p1;
	    p1++;
	} while (*p1 && !err);
	return Nullch;
    }
    else {			/* regular algorithm */
	do {
	    register char **alt = compex->alternatives;
	    while (*alt) {
		if (advance (compex, p1, *alt++))
		    return p1;
	    }
	    p1++;
	} while (*p1 && !err);
	return Nullch;
    }
}
 
/* advance the match of the regular expression starting at ep along the
   string lp, simulates an NDFSA */
bool
advance (compex, lp, ep)
register COMPEX *compex;
register char *ep;
register char *lp;
{
    register char *curlp;
    register char *trt = trans;
    register int i;
 
    while ((*ep & STAR) || *lp || *ep == CIRC || *ep == CKET)
	switch (*ep++) {
 
	    case CCHR:
		if (trt[*ep++] != trt[*lp]) return FALSE;
		lp++;
		continue;
 
	    case CDOT:
		if (*lp == '\n') return FALSE;
		lp++;
		continue;
 
	    case CDOL:
		if (!*lp || *lp == '\n')
		    continue;
		return FALSE;
 
	    case CIRC:
		if (lp == FirstCharacter || lp[-1]=='\n')
		    continue;
		return FALSE;
 
	    case WORD:
		if (isalnum(*lp)) {
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case NWORD:
		if (!isalnum(*lp)) {
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case WBOUND:
		if ((lp == FirstCharacter || !isalnum(lp[-1])) !=
			(!*lp || !isalnum(*lp)) )
		    continue;
		return FALSE;
 
	    case NWBOUND:
		if ((lp == FirstCharacter || !isalnum(lp[-1])) ==
			(!*lp || !isalnum(*lp)))
		    continue;
		return FALSE;
 
	    case CEND:
		return TRUE;
 
	    case CCL:
		if (cclass (ep, *lp, 1)) {
		    ep += BMAPSIZ;
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case NCCL:
		if (cclass (ep, *lp, 0)) {
		    ep += BMAPSIZ;
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case CBRA:
		compex->braslist[*ep++] = lp;
		continue;
 
	    case CKET:
		i = *ep++;
		compex->braelist[i] = lp;
		compex->braelist[0] = lp;
		compex->braslist[0] = compex->braslist[i];
		continue;
 
	    case CBACK:
		if (compex->braelist[i = *ep++] == 0) {
		    fputs("bad braces\n",stdout);
		    err = TRUE;
		    return FALSE;
		}
		if (backref (compex, i, lp)) {
		    lp += compex->braelist[i] - compex->braslist[i];
		    continue;
		}
		return FALSE;
 
	    case CBACK | STAR:
		if (compex->braelist[i = *ep++] == 0) {
		    fputs("bad braces\n",stdout);
		    err = TRUE;
		    return FALSE;
		}
		curlp = lp;
		while (backref (compex, i, lp)) {
		    lp += compex->braelist[i] - compex->braslist[i];
		}
		while (lp >= curlp) {
		    if (advance (compex, lp, ep))
			return TRUE;
		    lp -= compex->braelist[i] - compex->braslist[i];
		}
		continue;
 
	    case CDOT | STAR:
		curlp = lp;
		while (*lp++ && lp[-1] != '\n');
		goto star;
 
	    case WORD | STAR:
		curlp = lp;
		while (*lp++ && isalnum(lp[-1]));
		goto star;
 
	    case NWORD | STAR:
		curlp = lp;
		while (*lp++ && !isalnum(lp[-1]));
		goto star;
 
	    case CCHR | STAR:
		curlp = lp;
		while (*lp++ && trt[lp[-1]] == trt[*ep]);
		ep++;
		goto star;
 
	    case CCL | STAR:
	    case NCCL | STAR:
		curlp = lp;
		while (*lp++ && cclass (ep, lp[-1], ep[-1] == (CCL | STAR)));
		ep += BMAPSIZ;
		goto star;
 
	star:
		do {
		    lp--;
		    if (advance (compex, lp, ep))
			return TRUE;
		} while (lp > curlp);
		return FALSE;
 
	    default:
		fputs("Badly compiled pattern\n",stdout);
		err = TRUE;
		return -1;
	}
	if (*ep == CEND || *ep == CDOL) {
	    return TRUE;
    }
    return FALSE;
}
 
bool
backref (compex, i, lp)
register COMPEX *compex;
register int i;
register char *lp;
{
    register char *bp;
 
    bp = compex->braslist[i];
    while (*lp && *bp == *lp) {
	bp++;
	lp++;
	if (bp >= compex->braelist[i])
	    return TRUE;
    }
    return FALSE;
}

bool
cclass (set, c, af)
register char  *set;
register int c;
{
    c &= 0177;
#if BITSPERBYTE == 8
    if (set[c >> 3] & 1 << (c & 7))
#else
    if (set[c / BITSPERBYTE] & 1 << (c % BITSPERBYTE))
#endif
	return af;
    return !af;
}
!STUFFY!FUNK!
echo Extracting term.c
cat >term.c <<'!STUFFY!FUNK!'
/* $Header: term.c,v 4.1 84/09/24 12:10:42 lwall Exp $
 *
 * $Log:	term.c,v $
 * Revision 4.1  84/09/24  12:10:42  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.4  84/09/24  10:27:07  lwall
 * print_lines() did not do fireworks suppression right.
 * 
 * Revision 4.0.1.3  84/09/19  17:12:36  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.2  84/09/12  17:22:14  lwall
 * ifdef usg -> ifdef TERMIO.
 * 
 * Revision 4.0.1.1  84/09/10  15:33:08  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:51  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "final.h"
#include "help.h"
#include "cheat.h"
#include "intrp.h"
#include "INTERN.h"
#include "term.h"

char ERASECH;		/* rubout character */
char KILLCH;		/* line delete character */
char tcarea[TCSIZE];	/* area for "compiled" termcap strings */

/* terminal initialization */

void
term_init(tcbuf)
char *tcbuf;		/* temp area for "uncompiled" termcap entry */
{
    char *tmpaddr;
    int status;

#ifndef read_tty
    /* do no delay reads on something that always gets closed on exit */

    devtty = open("/dev/tty",0);
    fcntl(devtty,F_SETFL,O_NDELAY);
#endif
    
    savetty();				/* remember current tty state */

#ifdef TERMIO
    ospeed = _tty.c_cflag & CBAUD;	/* for tputs() */
    ERASECH = _tty.c_cc[VERASE];	/* for finish_command() */
    KILLCH = _tty.c_cc[VKILL];		/* for finish_command() */
#else
    ospeed = _tty.sg_ospeed;		/* for tputs() */
    ERASECH = _tty.sg_erase;		/* for finish_command() */
    KILLCH = _tty.sg_kill;		/* for finish_command() */
#endif

    noecho();				/* turn off echo */
    crmode();				/* enter cbreak mode */

    /* get all that good termcap stuff */

#ifdef HAVETERMLIB
    status = tgetent(tcbuf,getenv("TERM"));	/* get termcap entry */
    if (status < 1) {
#ifdef VERBOSE
	printf("No termcap %s found.\n", status ? "file" : "entry");
#else
	fputs("Termcap botch\n",stdout)
#endif
	finalize(1);
    }
    tmpaddr = tcarea;			/* set up strange tgetstr pointer */
    tgetstr("pc",&tmpaddr);		/* get pad character */
    PC = *tcarea;			/* get it where tputs wants it */
    if (!tgetflag("bs")) {		/* is backspace not used? */
	BC = tmpaddr;			/* find out what is */
	tgetstr("bc",&tmpaddr);
    }
    else
	BC = "\010";			/* make a backspace handy */
    UP = tmpaddr;			/* move up a line */
    tgetstr("up",&tmpaddr);
    if (!*UP)				/* no UP string? */
	marking = 0;			/* disable any marking */
    if (muck_up_clear)			/* this is for weird HPs */
	CL = "\n\n\n\n";
    else {
	CL = tmpaddr;			/* get clear string */
	tgetstr("cl",&tmpaddr);
    }
    CE = tmpaddr;			/* clear to end of line string */
    tgetstr("ce",&tmpaddr);
    SO = tmpaddr;			/* begin standout */
    tgetstr("so",&tmpaddr);
    SE = tmpaddr;			/* end standout */
    tgetstr("se",&tmpaddr);
    if ((SG = tgetnum("sg"))<0)
	SG = 0;				/* blanks left by SG, SE */
    US = tmpaddr;			/* start underline */
    tgetstr("us",&tmpaddr);
    UE = tmpaddr;			/* end underline */
    tgetstr("ue",&tmpaddr);
    if ((UG = tgetnum("ug"))<0)
	UG = 0;				/* blanks left by US, UE */
    if (*US)
	UC = nullstr;			/* UC must not be NULL */
    else {
	UC = tmpaddr;			/* underline a character */
	tgetstr("uc",&tmpaddr);
    }
    if (!*US && !*UC) {			/* no underline mode? */
	US = SO;			/* substitute standout mode */
	UE = SE;
	UG = SG;
    }
    LINES = tgetnum("li");		/* lines per page */
    COLS = tgetnum("co");		/* columns on page */
    AM = tgetflag("am");		/* terminal wraps automatically? */
    XN = tgetflag("xn");		/* then eats next newline? */
#else
    ??????				/* Roll your own... */
#endif
    if (LINES > 0) {			/* is this a crt? */
	if (!initlines)			/* no -i? */
	    if (ospeed >= B9600)	/* whole page at >= 9600 baud */
		initlines = LINES;
	    else if (ospeed >= B4800)	/* 16 lines at 4800 */
		initlines = 16;
	    else			/* otherwise just header */
		initlines = 8;
    }
    else {				/* not a crt */
	LINES = 30000;			/* so don't page */
	CL = "\n\n";			/* put a couple of lines between */
	if (!initlines)			/* make initlines reasonable */
	    initlines = 8;
    }
    if (COLS <= 0)
	COLS = 80;
    just_a_sec =			/* 1 second of padding */
      ospeed >= B300 ? (
        ospeed >= B9600 ? 960 :
        ospeed == B4800 ? 480 :
        ospeed == B2400 ? 240 :
        ospeed == B1800 ? 180 :
        ospeed == B1200 ? 120 :
        ospeed == B600  ?  60 :
     /* ospeed == B300*/   30
     ) : (				/* avoid 'expression overflow' grrr */
        ospeed == B200  ?  20 :
        ospeed == B150  ?  15 :		/* do I really have to type the */
        ospeed == B134  ?  13 :		/* rest of this? */
        ospeed == B110  ?  11 :
        ospeed == B75   ?   8 :
        ospeed == B50   ?   5 :
     /* ospeed == B0 */   960		/* if we are running detached I */
     );					/*  don't want to know about it! */

    
}

/* routine to pass to tputs */

char
putchr(ch)
register char ch;
{
    putchar(ch);
}

/* input the 2nd and succeeding characters of a multi-character command */
/* returns TRUE if command finished, FALSE if they rubbed out first character */

bool
finish_command(donewline)
int donewline;
{
    register char *s;

    s = buf;
    if (s[1] != FINISHCMD)		/* someone faking up a command? */
	return TRUE;
    do {
      top:
	if (*s < ' ') {
	    putchar('^');
	    putchar(*s | 64);
	}
	else
	    putchar(*s);		/* echo previous character */
	s++;
re_read:
	fflush(stdout);
	getcmd(s);
	if (errno || *s == Ctl('l')) {
	    *s = Ctl('r');		/* force rewrite on CONT */
	}
	if (*s == '\033') {		/* substitution desired? */
#ifdef ESCSUBS
	    char tmpbuf[4], *cpybuf;

	    tmpbuf[0] = '%';
	    read_tty(&tmpbuf[1],1);
#ifdef RAWONLY
	    tmpbuf[1] &= 0177;
#endif
	    tmpbuf[2] = '\0';
	    if (tmpbuf[1] == 'h') {
		help_subs();
		*s = '\0';
		reprint();
		goto re_read;
	    }
	    else if (tmpbuf[1] == '\033') {
		*s = '\0';
		cpybuf = savestr(buf);
		interp(buf,cpybuf);
		free(cpybuf);
		s = buf + strlen(buf);
		reprint();
		goto re_read;
	    }
	    else {
		interp(s,tmpbuf);
		fputs(s,stdout);
		s += strlen(s);
	    }
	    goto re_read;
#else
	    notincl("^[");
	    *s = '\0';
	    reprint();
	    goto re_read;
#endif
	}
	else if (*s == ERASECH) {	/* they want to rubout a char? */
	    rubout();
	    s--;			/* discount the char rubbed out */
	    if (*s < ' ')
		rubout();
	    if (s == buf) {		/* entire string gone? */
		fflush(stdout);		/* return to single char command mode */
		return FALSE;
	    }
	    else
		goto re_read;
	}
	else if (*s == KILLCH) {	/* wipe out the whole line? */
	    while (s-- != buf) {	/* emulate that many ERASEs */
		rubout();
		if (*s < ' ')
		    rubout();
	    }
	    fflush(stdout);
	    return FALSE;		/* return to single char mode */
	}
#ifdef WORDERASE
	else if (*s == Ctl('w')) {	/* wipe out one word? */
	    *s-- = ' ';
	    while (!isspace(*s) || isspace(s[1])) {
		rubout();
		if (s-- == buf) {
		    fflush(stdout);
		    return FALSE;	/* return to single char mode */
		}
		if (*s < ' ')
		    rubout();
	    }
	    s++;
	    goto re_read;
	}
#endif
	else if (*s == Ctl('r')) {
	    *s = '\0';
	    reprint();
	    goto re_read;
	}
	else if (*s == Ctl('v')) {
	    putchar('^');
	    backspace();
	    getcmd(s);
	    goto top;
	}
    } while (*s != '\n');		/* till a newline (not echoed) */
    *s = '\0';				/* terminate the string nicely */
    if (donewline)
	putchar('\n');
    return TRUE;			/* say we succeeded */
}

/* discard any characters typed ahead */

void
eat_typeahead()
{
    /*NOSTRICT*/
    if (!typeahead) {
#ifdef PENDING
	while (input_pending())
	    read_tty(buf,sizeof(buf));
#else /* this is probably v7 */
	ioctl(_tty_ch,TIOCSETP,&_tty);
#endif
    }
}

#ifndef read_tty
/* read a character from the terminal, with hacks for O_NDELAY reads */

int
read_tty(addr,size)
char *addr;
int size;
{
    if (is_input) {
	*addr = pending_ch;
	is_input = FALSE;
	return 1;
    }
    else
	return read(0,addr,size);
}
#endif

/* print an underlined string, one way or another */

void
underprint(s)
register char *s;
{
    assert(UC);
    if (*UC) {		/* char by char underline? */
	while (*s) {
	    if (*s < ' ') {
		putchar('^');
		backspace();/* back up over it */
		underchar();/* and do the underline */
		putchar(*s+64);
		backspace();/* back up over it */
		underchar();/* and do the underline */
	    }
	    else {
		putchar(*s);
		backspace();/* back up over it */
		underchar();/* and do the underline */
	    }
	    s++;
	}
    }
    else {		/* start and stop underline */
	underline();	/* start underlining */
	while (*s) {
	    if (*s < ' ') {
		putchar('^');
		putchar(*s+64);
	    }
	    else
		putchar(*s);
	    s++;
	}
	un_underline();	/* stop underlining */
    }
}

/* keep screen from flashing strangely on magic cookie terminals */

#ifdef NOFIREWORKS
void
no_sofire()
{
    if (*UP && *SE) {		/* should we disable fireworks? */
	putchar('\n');
	un_standout();
	up_line();
	putchar('\r');
    }
}

void
no_ulfire()
{
    if (*UP && *US) {		/* should we disable fireworks? */
	putchar('\n');
	un_underline();
	up_line();
	putchar('\r');
    }
}
#endif

/* get a character into a buffer */

void
getcmd(whatbuf)
register char *whatbuf;
{
    int_count = 0;
    errno = 0;
    if (read_tty(whatbuf,1) < 0 && !errno)
	errno = EINTR;
    if (errno && errno != EINTR) {
	perror(readerr);
	sig_catcher(0);
    }
#ifdef RAWONLY
    *whatbuf &= 0177;
#endif
    if (whatbuf == buf)
	whatbuf[1] = FINISHCMD;		/* tell finish_command to work */
}

int
get_anything()
{
    char tmpbuf[2];

reask_anything:
    standout();
#ifdef VERBOSE
    IF(verbose)
	fputs("[Type space to continue] ",stdout);
    ELSE
#endif
#ifdef TERSE
	fputs("[MORE] ",stdout);
#endif
    un_standout();
    fflush(stdout);
    eat_typeahead();
    if (int_count) {
	return -1;
    }
    collect_subjects();			/* loads subject cache until */
					/* input is pending */
    getcmd(tmpbuf);
    if (errno || *tmpbuf == '\f') {
	putchar('\n');			/* if return from stop signal */
	goto reask_anything;		/* give them a prompt again */
    }
    if (*tmpbuf == 'h') {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\nType q to quit or space to continue.\n",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("\nq to quit, space to continue.\n",stdout);
#endif
	goto reask_anything;
    }
    else if (*tmpbuf != ' ') {
	putchar('\r');	/* put cursor at beginning of line */
	erase_eol();	/* and erase the prompt */
	return *tmpbuf;
    }
    if (erase_screen) 			/* -e? */
	clear();			/* clear screen */
    else {
	putchar('\r');	/* put cursor at beginning of line */
	erase_eol();	/* and erase the prompt */
    }
    return 0;
}

void
in_char(prompt)
char *prompt;
{
reask_in_char:
    fputs(prompt,stdout);
    fflush(stdout);
    eat_typeahead();
    getcmd(buf);
    if (errno || *buf == '\f') {
	putchar('\n');			/* if return from stop signal */
	goto reask_in_char;		/* give them a prompt again */
    }
}

int
print_lines(what_to_print,hilite)
char *what_to_print;
int hilite;
{
    register char *s;
    register int i;

    if (page_line < 0)			/* they do not want to see this? */
	return -1;
    for (s=what_to_print; *s; ) {
	if (page_line >= LINES || int_count) {
	    if (int_count || get_anything()) {
		page_line = -1;		/* disable further print_lines */
		return -1;
	    }
	    page_line = 2;
	}
	else
	    page_line++;
	if (hilite == STANDOUT) {
#ifdef NOFIREWORKS
	    if (erase_screen)
		no_sofire();
#endif
	    standout();
	}
	else if (hilite == UNDERLINE) {
#ifdef NOFIREWORKS
	    if (erase_screen)
		no_ulfire();
#endif
	    underline();
	}
	for (i=0; i<COLS; i++) {
	    if (!*s)
		break;
	    if (*s >= ' ')
		putchar(*s);
	    else if (*s == '\t') {
		putchar(*s);
		i = ((i+8) & ~7) - 1; 
	    }
	    else if (*s == '\n') {
		i = 32000;
	    }
	    else {
		i++;
		putchar('^');
		putchar(*s + 64);
	    }
	    s++;
	}
	if (i) {
	    if (hilite == STANDOUT)
		un_standout();
	    else if (hilite == UNDERLINE)
		un_underline();
	    if (AM && i == COLS)
		fflush(stdout);
	    else
		putchar('\n');
	}
    }
    return 0;
}

void
pad(num)
int num;
{
    register int i;

    for (i = num; i; --i)
	putchar(PC);
    fflush(stdout);
}

/* echo the command just typed */

#ifdef VERIFY
void
printcmd()
{
    if (verify && buf[1] == FINISHCMD) {
	if (*buf < ' ') {
	    putchar('^');
	    putchar(*buf | 64);
	    backspace();
	    backspace();
	}
	else {
	    putchar(*buf);
	    backspace();
	}
	fflush(stdout);
    }
}
#endif

void
rubout()
{
    backspace();			/* do the old backspace, */
    putchar(' ');			/*   space, */
    backspace();			/*     backspace trick */
}

void
reprint()
{
    register char *s;

    fputs("^R\n",stdout);
    for (s = buf; *s; s++) {
	if (*s < ' ') {
	    putchar('^');
	    putchar(*s | 64);
	}
	else
	    putchar(*s);
    }
}

!STUFFY!FUNK!
echo Extracting rcln.c
cat >rcln.c <<'!STUFFY!FUNK!'
/* $Header: rcln.c,v 4.1 84/09/24 12:04:36 lwall Exp $
 *
 * $Log:	rcln.c,v $
 * Revision 4.1  84/09/24  12:04:36  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/18  16:09:18  lwall
 * Fixed previous fix.
 * 
 * Revision 4.0.1.2  84/09/13  11:16:43  lwall
 * Check for out-of-bounds Xreffing.
 * 
 * Revision 4.0.1.1  84/09/10  15:23:02  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:01  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "rcstuff.h"
#include "ngdata.h"
#include "INTERN.h"
#include "rcln.h"

void
rcln_init()
{
    ;
}

#ifdef CATCHUP
void
catch_up(ngx)
NG_NUM ngx;
{
    char tmpbuf[128];
    
#ifdef VERBOSE
    IF(verbose)
	printf("\nMarking %s as all read.\n",rcline[ngx]);
    ELSE
#endif
#ifdef TERSE
	fputs("\nMarked read\n",stdout);
#endif
    sprintf(tmpbuf,"%s: 1-%ld", rcline[ngx],(long)getngsize(ngx));
    free(rcline[ngx]);
    rcline[ngx] = savestr(tmpbuf);
    *(rcline[ngx] + rcnums[ngx] - 1) = '\0';
    write_rc();
}
#endif

/* add an article number to a newsgroup, if it isn't already read */

int
addartnum(artnum,ngnam)
ART_NUM artnum;
char *ngnam;
{
    register NG_NUM ngnum = find_ng(ngnam);
    register char *s, *t, *maxt = Nullch;
    ART_NUM min = 0, max = -1, lastnum = 0;
    char *mbuf;
    bool morenum;

    if (!artnum)
	return 0;
    if (ngnum == nextrcline || !rcnums[ngnum])
					/* not found in newsrc? */
	return 0;
#ifdef CACHEFIRST
    if (!abs1st[ngnum])
#else
    if (!toread[ngnum])
#endif
					/* now is a good time to trim down */
	set_toread(ngnum);		/* the list due to expires if we */
					/* have not yet. */
#ifdef DEBUGGING
    if (artnum > ngmax[ngnum] + 10	/* allow for incoming articles */
       ) {
	printf("\nCorrupt Xref line!!!  %ld --> %s(1..%ld)\n",
	    artnum,ngnam,
	    ngmax[ngnum]);
	paranoid = TRUE;		/* paranoia reigns supreme */
	return -1;			/* hope this was the first newsgroup */
    }
#endif

    if (toread[ngnum] == TR_BOGUS)
	return 0;
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%ld->\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
    s = rcline[ngnum] + rcnums[ngnum];
    while (*s == ' ') s++;		/* skip spaces */
    t = s;
    while (isdigit(*s) && artnum >= (min = atol(s))) {
					/* while it might have been read */
	for (t = s; isdigit(*t); t++) ;	/* skip number */
	if (*t == '-') {		/* is it a range? */
	    t++;			/* skip to next number */
	    if (artnum <= (max = atol(t)))
		return 0;		/* it is in range => already read */
	    lastnum = max;		/* remember it */
	    maxt = t;			/* remember position in case we */
					/* want to overwrite the max */
	    while (isdigit(*t)) t++;	/* skip second number */
	}
	else {
	    if (artnum == min)		/* explicitly a read article? */
		return 0;
	    lastnum = min;		/* remember what the number was */
	    maxt = Nullch;		/* last one was not a range */
	}
	while (*t && !isdigit(*t)) t++;	/* skip comma and any spaces */
	s = t;
    }
    
    /* we have not read it, so insert the article number before s */
    
    morenum = isdigit(*s);		/* will it need a comma after? */
    *(rcline[ngnum] + rcnums[ngnum] - 1) = rcchar[ngnum];
    mbuf = safemalloc((MEM_SIZE)(strlen(s) + (s-rcline[ngnum]) + 8));
    strcpy(mbuf,rcline[ngnum]);		/* make new rc line */
    if (maxt && lastnum && artnum == lastnum+1)
    					/* can we just extend last range? */
	t = mbuf + (maxt-rcline[ngnum]);/* then overwrite previous max */
    else {
	t = mbuf + (t-rcline[ngnum]);	/* point t into new line instead */
	if (lastnum) {			/* have we parsed any line? */
	    if (!morenum)		/* are we adding to the tail? */
		*t++ = ',';		/* supply comma before */
	    if (!maxt && artnum == lastnum+1 && *(t-1) == ',')
					/* adjacent singletons? */
		*(t-1) = '-';		/* turn them into a range */
	}
    }
    if (morenum) {			/* is there more to life? */
	if (min == artnum+1) {		/* can we consolidate further? */
	    bool range_before = (*(t-1) == '-');
	    bool range_after;
	    char *nextmax;

	    for (nextmax = s; isdigit(*nextmax); nextmax++) ;
	    range_after = *nextmax++ == '-';
	    
	    if (range_before)
		*t = '\0';		/* artnum is redundant */
	    else
		sprintf(t,"%ld-",(long)artnum);/* artnum will be new min */
	    
	    if (range_after)
		s = nextmax;		/* *s is redundant */
	/*  else
		s = s */		/* *s is new max */
	}
	else
	    sprintf(t,"%ld,",(long)artnum);	/* put the number and comma */
    }
    else
	sprintf(t,"%ld",(long)artnum);	/* put the number there (wherever) */
    strcat(t,s);			/* copy remainder of line */
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%s\n",mbuf);
    }
#endif
    free(rcline[ngnum]);
    rcline[ngnum] = mbuf;		/* pull the switcheroo */
    *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0';
					/* wipe out : or ! */
    if (toread[ngnum] > TR_NONE)	/* lest we turn unsub into bogus */
	--toread[ngnum];
    return 0;
}

#ifdef MCHASE
/* delete an article number from a newsgroup, if it is there */

void
subartnum(artnum,ngnam)
register ART_NUM artnum;
char *ngnam;
{
    register NG_NUM ngnum = find_ng(ngnam);
    register char *s, *t;
    register ART_NUM min, max;
    char *mbuf;
    int curlen;

    if (!artnum)
	return;
    if (ngnum == nextrcline || !rcnums[ngnum])
	return;				/* not found in newsrc? */
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%ld<-\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
    s = rcline[ngnum] + rcnums[ngnum];
    while (*s == ' ') s++;		/* skip spaces */
    
    /* a little optimization, since it is almost always the last number */
    
    for (t=s; *t; t++) ;		/* find end of string */
    curlen = t-rcline[ngnum];
    for (t--; isdigit(*t); t--) ;	/* find previous delim */
    if (*t == ',' && atol(t+1) == artnum) {
	*t = '\0';
	if (toread[ngnum] >= TR_NONE)
	    ++toread[ngnum];
#ifdef DEBUGGING
	if (debug & DEB_XREF_MARKER)
	    printf("%s%c %s\n",rcline[ngnum],rcchar[ngnum],s);
#endif
	return;
    }

    /* not the last number, oh well, we may need the length anyway */

    while (isdigit(*s) && artnum >= (min = atol(s))) {
					/* while it might have been read */
	for (t = s; isdigit(*t); t++) ;	/* skip number */
	if (*t == '-') {		/* is it a range? */
	    t++;			/* skip to next number */
	    max = atol(t);
	    while (isdigit(*t)) t++;	/* skip second number */
	    if (artnum <= max) {
					/* it is in range => already read */
		if (artnum == min) {
		    min++;
		    artnum = 0;
		}
		else if (artnum == max) {
		    max--;
		    artnum = 0;
		}
		*(rcline[ngnum] + rcnums[ngnum] - 1) = rcchar[ngnum];
		mbuf = safemalloc((MEM_SIZE)(curlen + (artnum?15:2)));
		*s = '\0';
		strcpy(mbuf,rcline[ngnum]);	/* make new rc line */
		s = mbuf + (s-rcline[ngnum]);
					/* point s into mbuf now */
		if (artnum) {		/* split into two ranges? */
		    prange(s,min,artnum-1);
		    s += strlen(s);
		    *s++ = ',';
		    prange(s,artnum+1,max);
		}
		else			/* only one range */
		    prange(s,min,max);
		s += strlen(s);
		strcpy(s,t);		/* copy remainder over */
#ifdef DEBUGGING
		if (debug & DEB_XREF_MARKER) {
		    printf("%s\n",mbuf);
		}
#endif
		free(rcline[ngnum]);
		rcline[ngnum] = mbuf;	/* pull the switcheroo */
		*(rcline[ngnum] + rcnums[ngnum] - 1) = '\0';
					/* wipe out : or ! */
		if (toread[ngnum] >= TR_NONE)
		    ++toread[ngnum];
		return;
	    }
	}
	else {
	    if (artnum == min) {	/* explicitly a read article? */
		if (*t == ',')		/* pick a comma, any comma */
		    t++;
		else if (s[-1] == ',')
		    s--;
		else if (s[-2] == ',')	/* (in case of space) */
		    s -= 2;
		strcpy(s,t);		/* no need to realloc */
		if (toread[ngnum] >= TR_NONE)
		    ++toread[ngnum];
#ifdef DEBUGGING
		if (debug & DEB_XREF_MARKER) {
		    printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum],
		      rcline[ngnum] + rcnums[ngnum]);
		}
#endif
		return;
	    }
	}
	while (*t && !isdigit(*t)) t++;	/* skip comma and any spaces */
	s = t;
    }
}

void
prange(where,min,max)
char *where;
ART_NUM min,max;
{
    if (min == max)
	sprintf(where,"%ld",(long)min);
    else
	sprintf(where,"%ld-%ld",(long)min,(long)max);
}
#endif

/* calculate the number of unread articles for a newsgroup */

void
set_toread(ngnum)
register NG_NUM ngnum;
{
    register char *s, *c, *h;
    char tmpbuf[64], *mybuf = tmpbuf;
    char *nums;
    int length;
    ART_NUM ngsize = getngsize(ngnum);
    ART_NUM unread = ngsize;
    ART_NUM newmax;

#ifdef DEBUGGING
    ngmax[ngnum] = ngsize;		/* for checking out-of-range Xrefs */
#endif
    if (ngsize == TR_BOGUS) {
	printf("Warning!  Bogus newsgroup: %s\n",rcline[ngnum]);
	paranoid = TRUE;
	toread[ngnum] = TR_BOGUS;
	return;
    }
#ifdef CACHEFIRST
    if (!abs1st[ngnum])
#else
    if (!toread[ngnum])
#endif
    {
	sprintf(tmpbuf," 1-%ld",(long)ngsize);
	if (strNE(tmpbuf,rcline[ngnum]+rcnums[ngnum]))
	    checkexpired(ngnum,ngsize);	/* this might realloc rcline */
    }
    nums = rcline[ngnum]+rcnums[ngnum];
    length = strlen(nums);
    if (length >= 60)
	mybuf = safemalloc((MEM_SIZE)(length+5));
    strcpy(mybuf,nums);
    mybuf[length++] = ',';
    mybuf[length] = '\0';
    for (s = mybuf; isspace(*s); s++)
	    ;
    for ( ; (c = index(s,',')) != Nullch ; s = ++c) {
				    /* for each range */
	*c = '\0';			/* keep index from running off */
	if ((h = index(s,'-')) != Nullch)	/* find - in range, if any */
	    unread -= (newmax = atol(h+1)) - atol(s) + 1;
	else if (newmax = atol(s))
	    unread--;		/* recalculate length */
	if (newmax > ngsize) {	/* paranoia check */
	    unread = -1;
	    break;
	}
    }
    if (unread >= 0)		/* reasonable number? */
	toread[ngnum] = (ART_UNREAD)unread;
					/* remember how many are left */
    else {				/* SOMEONE RESET THE NEWSGROUP!!! */
	toread[ngnum] = (ART_UNREAD)ngsize;
					/* assume nothing carried over */
	printf("Warning!  Somebody reset %s--assuming nothing read.\n",
	    rcline[ngnum]);
	*(rcline[ngnum] + rcnums[ngnum]) = '\0';
	paranoid = TRUE;		/* enough to make a guy paranoid */
    }
    if (mybuf != tmpbuf)
	free(mybuf);
    if (rcchar[ngnum] == NEGCHAR)
	toread[ngnum] = TR_UNSUB;
}

/* make sure expired articles are marked as read */

void
checkexpired(ngnum,ngsize)
register NG_NUM ngnum;
ART_NUM ngsize;
{
    register ART_NUM a1st = getabsfirst(ngnum,ngsize);
    register char *s, *t;
    register ART_NUM num, lastnum = 0;
    char *mbuf, *newnum;

    if (a1st<=1)
	return;
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("1-%ld->\n%s%c%s\n",(long)(a1st-1),rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
    for (s = rcline[ngnum] + rcnums[ngnum]; isspace(*s); s++);
    while (*s && (num = atol(s)) <= a1st) {
	while (isdigit(*s)) s++;
	while (*s && !isdigit(*s)) s++;
	lastnum = num;
    }
    if (*s) {
	if (s[-1] == '-') {			/* landed in a range? */
	    if (lastnum != 1)
		sprintf(rcline[ngnum]+rcnums[ngnum]," 1-%s",s);
	    goto ret;
	}
    }
    /* s now points to what should follow first range */
    if (s - rcline[ngnum] > rcnums[ngnum] + 10) 
	mbuf = rcline[ngnum];
    else {
	mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + strlen(s) + 10));
	strcpy(mbuf,rcline[ngnum]);
    }
    newnum = t = mbuf+rcnums[ngnum];
    sprintf(t," 1-%ld",(long)(a1st - (lastnum != a1st)));
    if (*s) {
	t += strlen(t);
	*t++ = ',';
	strcpy(t,s);
    }
    if (mbuf == rcline[ngnum]) {
	rcline[ngnum] = saferealloc(rcline[ngnum],
	    (MEM_SIZE)(rcnums[ngnum] + strlen(newnum) + 1));
    }
    else {
	free(rcline[ngnum]);
	rcline[ngnum] = mbuf;
    }

ret:
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
}

!STUFFY!FUNK!
echo Extracting MANIFEST
cat >MANIFEST <<'!STUFFY!FUNK!'
After all the rn kits are run you should have the following files:

Filename	Kit Description
--------	--- -----------
Configure        2  A shell script that installs everything system dependent.
EXTERN.h         8  When included, makes other includes not belong to me.
HACKERSGUIDE     6  A brief guide to the contorted innards of rn.
INTERN.h         8  When included, makes other includes belong to me.
MANIFEST         4  This list of files.
Makefile.SH      6  The makefile.
Pnews.SH         5  A news posting shell script that knows about -h.
Pnews.man        7  Manual page for Pnews.
README           6  Installation instructions.
Rnmail.SH        6  A mailer that knows about -h.
Rnmail.man       7  Manual page for Rnmail.
Wishlist         8  What the next version wants in it.
addng.c          7  Routines for scanning the active file for new newsgroups.
addng.h          8  Public info regarding addng.c.
art.c            3  Routines to display an article.
art.h            8  Public info regarding art.c.
art.help.SH      3  Shell script for help at the article level.
artio.c          7  Reserved for the article abstract type, someday.
artio.h          8  Public info regarding artio.c.
artsrch.c        6  Routines for searching among articles.
artsrch.h        8  Public info regarding artsrch.c.
artstate.h       8  Info on the current state of the article.
backpage.c       5  Routines for paging backwards in articles.
backpage.h       8  Public info regarding backpage.c.
bits.c           1  Bitmap management functions.
bits.h           7  Public info regarding bits.c.
cheat.c          7  Routines to do lookahead of several types.
cheat.h          8  Public info regarding cheat.c.
common.h         3  Global info.
final.c          7  Finalization (exit) routines.
final.h          8  Public info regarding final.c.
head.c           6  Header parsing routines.
head.h           7  Public info regarding head.c.
header.c.pat     7  DOXREFS patch for header.c.
header.h.pat     8  DOXREFS patch for header.h.
help.c           6  Help routines.
help.h           8  Public info regarding help.c.
inews.c.1.pat    2  DOXREFS and LINKART patches for 2.10.1 news.
inews.c.2.pat    5  DOXREFS and LINKART patches for 2.10.2 news.
init.c           6  Initialization (startup) routines.
init.h           8  Public info regarding init.c.
intrp.c          4  Filename expansion and % interpretation routines.
intrp.h          8  Public info regarding intrp.c.
kfile.c          6  KILL file routines.
kfile.h          8  Public info regarding kfile.c.
kitleader        8  Shell script to produce front of kit.
kitlists.c       7  Knapsack packer.
kittrailer       8  Shell script to produce end of kit.
last.c           7  Routines for handling the .rnlast file.
last.h           8  Public info regarding last.c.
makedepend.SH    7  Shell script to generate make dependencies.
makedir.SH       7  Shell script to make nested subdirectories.
makedist         1  Shell script to make a distribution kit.
makekit          8  Shell script to make a kit file.
manifake         8  Shell script to make MANIFEST.new file.
manimake         8  Shell script to make MANIFEST file.
mbox.saver.SH    8  Shell script to save an article to a mailbox.
ndir.c           7  4.2 directory routine emulation.
ndir.h           7  Public info regarding ndir.c.
newsetup.SH      7  Shell script to create a .newsrc file.
newsetup.man     7  Manual page for newsetup.
newsgroups.SH    7  Shell script to list unsubscribed newsgroups.
newsgroups.man   7  Manual page for newsgroups.
newsnews.SH      7  A motd-like file that rn may print at startup.
ng.c             2  Routines to display a newsgroup.
ng.h             8  Public info regarding ng.c.
ng.help.SH       7  Shell script to do newsgroup selection help.
ngdata.c         7  General data fetching routines for a newsgroup.
ngdata.h         8  Public info regarding ngdata.c.
ngsrch.c         7  Routines to search among newsgroups.
ngsrch.h         8  Public info regarding ngsrch.c.
ngstuff.c        6  Support routines for ng.c.
ngstuff.h        8  Public info regarding ng.c.
norm.saver.SH    8  Shell script to save an article to a normal file.
only.c           7  Routines to perform newsgroup restriction.
only.h           2  Public info regarding only.c.
pager.help.SH    7  Shell script for help at the pager level.
rcln.c           4  Routines to mung a .newsrc line.
rcln.h           8  Public info regarding rcln.c.
rcstuff.c        3  Routines to mung the .newsrc file.
rcstuff.h        7  Public info regarding rcstuff.c.
respond.c        5  Various routines for doing things with articles.
respond.h        8  Public info regarding respond.c.
rn.c             5  Main program.
rn.h             8  Public info regarding rn.c.
rn.man           1  Manual pages for rn. PLEASE READ.
search.c         4  Regular expression processing ala emacs.
search.h         7  Public info regarding search.c.
subs.help.SH     7  Shell script for help for escape substitutions.
sw.c             5  Switch processing routines.
sw.h             8  Public info regarding switch.c.
term.c           4  Terminal interface routines.
term.h           6  Public info regarding term.c.
util.c           5  Utility routines.
util.h           8  Public info regarding util.c.
!STUFFY!FUNK!
echo ""
echo "End of kit 4 (of 8)"
cat /dev/null >kit4isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 5 (of 8).  If kit 5 is complete, the line"
echo '"'"End of kit 5 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting rn.c
cat >rn.c <<'!STUFFY!FUNK!'
/*  rn -- new readnews program
 *
 *  From: lwall@sdcrdcf.UUCP (Larry Wall)
 *  Organization: System Development Corporation, Santa Monica
 *
 *  begun:   01/14/83
 *	1.0: 04/08/83
 *      2.0: 09/01/83
 */

static char rnid[] = "@(#)$Header: rn.c,v 4.1 84/09/24 12:07:12 lwall Exp $";

/* $Log:	rn.c,v $
 * Revision 4.1  84/09/24  12:07:12  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:28:58  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:16  lwall
 * Baseline for netwide release
 * 
 * Revision 3.0  84/06/04  16:22:45  lwall
 * Baseline version for netwide release.
 * 
 */

#include "INTERN.h"
#include "common.h"
#include "rn.h"
#include "EXTERN.h"
#include "rcstuff.h"
#include "term.h"
#include "final.h"
#include "ngdata.h"
#include "util.h"
#include "only.h"
#include "ngsrch.h"
#include "help.h"
#include "last.h"
#include "init.h"
#include "intrp.h"
#include "rcln.h"
#include "sw.h"
#include "addng.h"
#include "INTERN.h"

void
rn_init()
{
    ;
}

void
main(argc,argv)
int argc;
char *argv[];
{
    bool foundany = initialize(argc,argv);
    register char *s;
    
    if (maxngtodo)
	starthere = 0;
    else if (!foundany) {		/* nothing to do? */
#ifdef VERBOSE
	if (verbose)
	    fputs("\
No unread news in subscribed-to newsgroups.  To subscribe to a new\n\
newsgroup use the g<newsgroup> command.\n\
",stdout);
#endif
	starthere = nextrcline;
    }

    /* loop through all unread news */

    {
	char promptbuf[80];
	bool special = FALSE;		/* temporarily allow newsgroup */
					/*   with no unread news? */
	bool retry;			/* cycle back to top of list? */
	NG_NUM recent_ng = 0;
	
	current_ng = 0;
	do {
	    retry = FALSE;
	    if (findlast) {
		findlast = FALSE;
		starthere = 0;
		if (*lastngname) {
		    if ((ng = find_ng(lastngname)) == nextrcline)
			ng = 0;
		    else {
			set_ngname(lastngname);
		    	set_toread(ng);
			if (toread[ng] <= TR_NONE)
			    ng = 0;
		    }
		}
	    }
	    else {
		ng = starthere;
		starthere = 0;
	    }
	    while (ng <= nextrcline) {	/* for each newsgroup */
		if (ng >= nextrcline) {	/* after the last newsgroup? */
		    ng = nextrcline;	/* force it to 1 after */
#ifdef ONLY
		    if (maxngtodo) {
			if (retry)
#ifdef VERBOSE
			    IF(verbose)
				printf("\nRestriction %s%s still in effect.\n",
				    ngtodo[0],
				    maxngtodo > 1 ? ", etc." : nullstr);
			    ELSE
#endif
#ifdef TERSE
				fputs("\n(\"Only\" mode.)\n",stdout);
#endif
			else {
#ifdef VERBOSE
			    IF(verbose)
				fputs("\nNo articles under restriction.",stdout);
			    ELSE
#endif
#ifdef TERSE
				fputs("\nNo \"only\" articles.",stdout);
#endif
			    end_only();	/* release the restriction */
			    retry = TRUE;
			}
		    }
#endif
		    dfltcmd = (retry ? "npq" : "qnp");
#ifdef VERBOSE
		    IF(verbose)
			sprintf(promptbuf,
			    "\n******** End of newsgroups--what next? [%s] ",
			    dfltcmd);
		    ELSE
#endif
#ifdef TERSE
			sprintf(promptbuf,
			    "\n**** End--next? [%s] ", dfltcmd);
#endif
		}
		else {
		    bool shoe_fits;	/* newsgroup matches restriction? */

		    if (toread[ng] >= TR_NONE) {	/* recalc toread? */
			set_ngname(rcline[ng]);
			if (shoe_fits = (special || inlist(ngname)))
			    set_toread(ng);
			if (paranoid) {
			    recent_ng = current_ng;
			    current_ng = ng;
			    cleanup_rc();
					/* this may move newsgroups around */
			    ng = current_ng;
			    set_ngname(rcline[ng]);
			}
		    }
		    if (toread[ng] < (maxngtodo||special ? TR_NONE : TR_ONE) || !shoe_fits) {
					/* unwanted newsgroup? */
			ng++;		/* then skip it */
			continue;
		    }
		    dfltcmd = "ynq";
#ifdef VERBOSE
		    IF(verbose)
			sprintf(promptbuf,
			    "\n******** %3ld unread article%s in %s--read now? [%s] ",
			    (long)toread[ng], (toread[ng]==TR_ONE ? nullstr : "s"),
			    ngname, dfltcmd);	/* format prompt string */
		    ELSE
#endif
#ifdef TERSE
			sprintf(promptbuf,
			    "\n**** %3ld in %s--read? [%s] ",
			    (long)toread[ng],
			    ngname,dfltcmd);	/* format prompt string */
#endif
		}
		special = FALSE;	/* go back to normal mode */
		if (ng != current_ng) {
		    recent_ng = current_ng;
					/* remember previous newsgroup */
		    current_ng = ng;	/* remember current newsgroup */
		}
    reask_newsgroup:
		fputs(promptbuf,stdout);/* print prompt */
		fflush(stdout);
    reinp_newsgroup:
		eat_typeahead();
		getcmd(buf);
		if (errno || *buf == '\f') {
		    putchar('\n');	/* if return from stop signal */
		    goto reask_newsgroup;	/* give them a prompt again */
		}
		setdef(buf,dfltcmd);
#ifdef VERIFY
		printcmd();
#endif
		switch (*buf) {
		case 'p':		/* find previous unread newsgroup */
		    do {
			if (ng <= 0)
			    break;
			ng--;
			if (toread[ng] == TR_NONE)
			    set_toread(ng);
		    } while (toread[ng] <= TR_NONE);
		    break;
		case 'P':		/* goto previous newsgroup */
		    do {
			if (ng <= 0)
			    break;
			ng--;
		    } while (toread[ng] < TR_NONE);
		    special = TRUE;	/* don't skip it if toread==0 */
		    break;
		case '-':
		    ng = recent_ng;	/* recall previous newsgroup */
		    special = TRUE;	/* don't skip it if toread==0 */
		    break;
		case 'q': case 'Q':	/* quit? */
		    putchar('\n');
		    ng = nextrcline+1;	/* satisfy */
		    retry = FALSE;	/*   loop conditions */
		    break;
		case '^':
		    putchar('\n');
		    ng = 0;
		    break;
		case 'n': case '+':	/* find next unread newsgroup */
		    if (ng == nextrcline) {
			putchar('\n');
			retry = TRUE;
		    }
		    else if (toread[ng] > TR_NONE)
			retry = TRUE;
		    ng++;
		    break;
		case 'N':		/* goto next newsgroup */
		    ng++;
		    special = TRUE;	/* and don't skip it if toread==0 */
		    break;
		case '1':		/* goto 1st newsgroup */
		    ng = 0;
		    special = TRUE;	/* and don't skip it if toread==0 */
		    break;
		case '$':
		    ng = nextrcline;	/* goto last newsgroup */
		    retry = TRUE;
		    break;
		case 'L':
		    list_newsgroups();
		    goto reask_newsgroup;
		case '/': case '?':	/* scan for newsgroup pattern */
#ifdef NGSEARCH
		    switch (ng_search(buf,TRUE)) {
		    case NGS_ABORT:
			goto reinp_newsgroup;
		    case NGS_INTR:
#ifdef VERBOSE
			IF(verbose)
			    fputs("\n(Interrupted)\n",stdout);
			ELSE
#endif
#ifdef TERSE
			    fputs("\n(Intr)\n",stdout);
#endif
			ng = current_ng;
			goto reask_newsgroup;
		    case NGS_FOUND:
			special = TRUE;	/* don't skip it if toread==0 */
			break;
		    case NGS_NOTFOUND:
#ifdef VERBOSE
			IF(verbose)
			    fputs("\n\nNot found--use g to add newsgroups\n",
				stdout);
			ELSE
#endif
#ifdef TERSE
			    fputs("\n\nNot found\n",stdout);
#endif
			goto reask_newsgroup;
		    }
#else
		    notincl("/");
#endif
		    break;
		case 'm':
#ifndef RELOCATE
		    notincl("m");
		    break;
#endif		    
		case 'g':	/* goto named newsgroup */
		    if (!finish_command(FALSE))
					/* if they didn't finish command */
			goto reinp_newsgroup;	/* go try something else */
		    for (s = buf+1; *s == ' '; s++);
					/* skip leading spaces */
		    if (!*s)
			strcpy(s,ngname);
#ifdef RELOCATE
		    if (!get_ng(s,*buf=='m'))	/* try to find newsgroup */
#else
		    if (!get_ng(s,FALSE))	/* try to find newsgroup */
#endif
			ng = current_ng;/* if not found, go nowhere */
		    special = TRUE;	/* don't skip it if toread==0 */
		    break;
#ifdef DEBUGGING
		case 'D':
		    printf("\nTries: %d Hits: %d\n",
			softtries,softtries-softmisses);
		    goto reask_newsgroup;
#endif
		case '!':		/* shell escape */
		    if (escapade())	 /* do command */
			goto reinp_newsgroup;
					/* if rubbed out, re input */
		    goto reask_newsgroup;
		case Ctl('k'):		/* edit global KILL file */
		    edit_kfile();
		    goto reask_newsgroup;
		case 'c':		/* catch up */
#ifdef CATCHUP
		    if (ng < nextrcline) {
			catch_up(ng);
		    }
		    else
			retry = TRUE;
		    ng++;
#else
		    notincl("c");
#endif
		    break;
		case 'u':		/* unsubscribe */
		    if (ng < nextrcline && toread[ng] >= TR_NONE) {
					/* unsubscribable? */
			printf(unsubto,rcline[ng]);
			rcchar[ng] = NEGCHAR;
					/* unsubscribe to (from?) it */
			toread[ng] = TR_UNSUB;
					/* and make line invisible */
			ng++;		/* do an automatic 'n' */
		    }
		    break;
		case 'h':		/* help */
		    help_ng();
		    goto reask_newsgroup;
		case 'a':
#ifndef FINDNEWNG
		    notincl("a");
		    goto reask_newsgroup;
#else
		    /* FALL THROUGH */
#endif
		case 'o':
#ifdef ONLY
		{
#ifdef FINDNEWNG
		    bool doscan = (*buf == 'a');
#endif

		    if (!finish_command(TRUE)) /* get rest of command */
			goto reinp_newsgroup;	/* if rubbed out, try something else */
		    end_only();
		    if (buf[1]) {
			bool minusd = instr(buf+1,"-d") != Nullch;

			sw_list(buf+1);
			if (minusd)
			    cwd_check();
			putchar('\n');
#ifdef FINDNEWNG
			if (doscan && maxngtodo)
			    scanactive();
#endif
		    }
		    ng = 0;		/* simulate ^ */
		    retry = FALSE;
		    break;
		}
#else
		    notincl("o");
		    goto reask_newsgroup;
#endif
		case '&':
		    if (switcheroo()) /* get rest of command */
			goto reinp_newsgroup;	/* if rubbed out, try something else */
		    goto reask_newsgroup;
		case 'l': {		/* list other newsgroups */
		    if (!finish_command(TRUE)) /* get rest of command */
			goto reinp_newsgroup;	/* if rubbed out, try something else */
		    for (s = buf+1; *s == ' '; s++);
		    			/* skip leading spaces */
		    sprintf(cmd_buf,"%s '%s'",filexp(NEWSGROUPS),s);
		    resetty();
		    if (doshell(sh,cmd_buf))
#ifdef VERBOSE
			IF(verbose)
			    fputs("    (Error from newsgroups program)\n",
				stdout);
			ELSE
#endif
#ifdef TERSE
			    fputs("(Error)\n",stdout);
#endif
		    noecho();
		    crmode();
		    goto reask_newsgroup;
		}
		case '.': case '=':
		case 'y': case 'Y': /* do normal thing */
		    if (ng >= nextrcline) {
			fputs("\nNot on a newsgroup.",stdout);
			goto reask_newsgroup;
		    }
		    if (*buf == '=')
			s = savestr("=");
		    else if (*buf == '.') {	/* start command? */
			if (!finish_command(FALSE)) /* get rest of command */
			    goto reinp_newsgroup;
			s = savestr(buf+1);
					/* do_newsgroup will free it */
		    }
		    else
			s = Nullch;
		    if (toread[ng])
			retry = TRUE;
		    do_newsgroup(s);
		    ng++;
		    break;
		case '\n':
		    fputs(badcr,stdout);
		    goto reask_newsgroup;
		case 'v':
		    printf("\n%s\n",rnid);
		    goto reask_newsgroup;
		default:
		    printf("\n%s",hforhelp);
		    goto reask_newsgroup;
		}
	    }
	} while (retry);
    }

    /* now write .newsrc back out */

    write_rc();

    finalize(0);			/* and exit */
}

/* set current newsgroup */

void
set_ngname(what)
char *what;
{
    int len = strlen(what)+1;

    growstr(&ngname,&ngnlen,len);
    strcpy(ngname,what);
    growstr(&ngdir,&ngdlen,len);
    strcpy(ngdir,getngdir(ngname));
}

static char *myngdir;
static int ngdirlen = 0;

char *
getngdir(ngnam)
char *ngnam;
{
    register char *s;

    growstr(&myngdir,&ngdirlen,strlen(ngnam)+1);
    strcpy(myngdir,ngnam);
    for (s = myngdir; *s; s++)
	if (*s == '.')
	    *s = '/';
    return myngdir;
}

!STUFFY!FUNK!
echo Extracting Pnews.SH
cat >Pnews.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting Pnews (with variable substitutions)"
$spitshell >Pnews <<!GROK!THIS!
$startsh
# $Header: Pnews.SH,v 4.1 84/09/24 11:37:46 lwall Exp $
#
# $Log:	Pnews.SH,v $
# Revision 4.1  84/09/24  11:37:46  lwall
# Real baseline.
# 
# Revision 4.0.1.2  84/09/12  15:17:24  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.1  84/09/06  14:20:30  lwall
# Fixed a substitution glitch.
# 
# Revision 4.0  84/09/04  09:48:50  lwall
# Baseline for netwide release
# 
#
# syntax: Pnews -h headerfile			or
#	  Pnews -h headerfile oldarticle	or
#         Pnews newsgroup title			or just
#         Pnews

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

# System dependencies

# your site name
sitename="$sitename"
# your organization name
orgname="$orgname"
# what pager you use--if you have kernal paging use cat
pager="$pager"
# how you derive full names, bsd, usg, or other
nametype="$nametype"
# default editor
defeditor="$defeditor"
# where recordings are kept
_NEWSLIB=/usr/lib/news
# how not to echo with newline
n="$n"
c="$c"

# You should also look at the distribution warnings below marked !DIST!
# to make sure any distribution regions you are a member of are included.
# The following are some prototypical distribution groups.  If you do not
# use them all set the unused ones to a non-null string such as 'none'.
loc="$locpref"
org="$orgpref"
city="$citypref"
state="$statepref"
cntry="$cntrypref"
cont="$contpref"

test=${test-test}
sed=${sed-sed}
echo=${echo-echo}
cat=${cat-cat}
egrep=${egrep-egrep}
grep=${grep-grep}
rm=${rm-rm}
tr=${tr-tr}
inews=${inews-inews}

!GROK!THIS!
$spitshell >>Pnews <<'!NO!SUBS!'
if $test -f ${DOTDIR-${HOME-$LOGDIR}}/.pnewsexpert; then
    expertise=expert
else
    $cat <<'EOM'
I see you've never used this version of Pnews before.  I will give you extra
help this first time through, but then you must remember what you learned.
If you don't understand any question, type h and a CR (carriage return) for
help.

If you've never posted an article to the net before, it is HIGHLY recommended
that you read the netiquette document found in net.announce.newusers so
that you'll know to avoid the commonest blunders.  To do that, interrupt
Pnews, and get to the top-level prompt of rn.  Say "g net.announce.newusers"
and you are on your way.

EOM
    expertise=beginner
fi

case $cntry in
  can) stpr=Province ;;
  *)   stpr=State ;;
esac

tmpart=/tmp/article$$

headerfile=""
case $# in
0) ;;
*)  case $1 in
    -h)
	headerfile="$2"
	shift
	shift
	case $# in
	0)
	    oldart=""
	    ;;
	*)
	    oldart="$1"
	    shift
	    ;;
	esac
	;;
    esac
    ;;
esac

case $headerfile in
'')
    case $# in
    0)
	ng=h
	while $test "$ng" = h ; do
	    $echo ""
	    $echo $n "Newsgroup(s): $c"
	    read ng
	    case $ng in
	    h)
		$cat <<'EOH'

Type the name of one or more newsgroups to which you wish to post an article.
If you want to post to multiple newsgroups, it is better to do them all at
once than to post to each newsgroup individually, which defeats the news
reading programs' strategies of eliminating duplicates.

Separate multiple newsgroup names with commas.
EOH
		;;
	    esac
	done
	;;
    *)
	ng=$1
	shift
	;;
    esac
    case $ng in
    *\ *)
	ng=`$echo "$ng" | $sed 's/[, ] */,/g'`
	;;
    esac

    dist=h
    while $test "$dist" = h ; do
	$egrep -v '[	 ]none$' <<EOM

Your local distribution prefixes are:
	Local organization:	$loc
	Organization:		$org
	City:			$city
	$stpr:  		$state
	Country:		$cntry
	Continent:		$cont
	Everywhere:		net,mod,fa

EOM
	$echo $n "Distribution (net): $c"
	read dist
	case $dist in
	h)
	    $cat <<'EOH'

The Distribution line may be used to limit the distribution of an article
to some subset of the systems that would receive the article based only on
the Newsgroups line.  For example, if you want to sell your car in net.auto,
and you live in New Jersey, you might want to put "nj" on the Distribution
line to avoid advertising in California, which has enough problems of its own.
The actual area designators to use depend on where you are, of course.
EOH
	    ;;
	''|$loc*|$org*|$city*|$state*|$cntry*|$cont*|fa*|mod*)
	    ;;
	net*)
	    dist=''
	    ;;
	*)  $echo "Unrecognized distribution prefix--type h for help."
	    dist=h
	    ;;
	esac
    done
    
    case $ng in
    *net.general*)
	follow=net.followup
	;;
    *)
	follow=""
	;;
    esac
    
    case $# in
    0)
	title=h
	while $test "$title" = h ; do
	    $echo ""
	    $echo $n "Title/Subject: $c"
	    read title
	    case $title in
	    h)
		$cat <<'EOH'

Type the title for your article.  Please make it as informative as possible
(within reason) so that people who aren't interested won't have to read the
article to find out they aren't interested.  This includes marking movie
spoilers as (spoiler), and rotated jokes as (rot 13).
EOH
	    ;;
	    esac
	done
	;;
    *)
	title="$*"
	;;
    esac

# now build a file with a header for them to edit

    set ${USER-${LOGNAME-`who am i`}}
    logname=$1
    case $logname in
    *!*) logname=`expr "$logname" : '!\(.*\)$'` ;;
    esac
    case ${NAME-$nametype} in
    bsd)
	fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
	case $fullname in
	'*&*') : GACK
	    lname=`$echo $logname | $tr 'a-z' 'A-Z'`
	    lname=`$echo lname $logname | $sed 's/^\(.\)[^ ]* ./\1/'`
	    fullname=`$echo $fullname | $sed 's/&/${lname}/'`
	    ;;
	esac
	;;
    usg)
	fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q" -e "}" -e "d"`
	;;
    *)
	fullname=${NAME-`$cat ${HOME-$LOGDIR}/.fullname`}
	;;
    esac

    orgname=${ORGANIZATION-$orgname}
    case $orgname in
    /*) orgname=`$cat $orgname` ;;
    esac

    $cat > $tmpart <<EOHeader
Newsgroups: $ng
Subject: $title
Expires: 
References: 
Sender: 
Reply-To: $logname@$sitename.UUCP ($fullname)
Followup-To: $follow
Distribution: $dist
Organization: $orgname
Keywords: 

EOHeader

    ;;
*)
    $cat < $headerfile  > $tmpart
    ;;
esac
    rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ; $echo saved in ${HOME-$LOGDIR}/dead.article ; $rm -f $tmpart; exit"
    trap "$rescue" 1
    trap "$rescue" 2

$echo ""
set `$sed < $tmpart -n -e '/^Distribution: /{' -e p -e q -e '}' -e '/^$/q'`
case $# in
0|1)
    set `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'`
    case $# in
    0|1)
	set "x net.whatever"
	;;
    esac
    ;;
esac
shift

#: play recorded message
#if $test -s ${_NEWSLIB}/recording ; then
#     ng=`$echo $1 | $sed "s/,.*//"`
#    _rec1=${_NEWSLIB}/`$sed -n "/^$ng/s/^.*	//p" ${_NEWSLIB}/recording`
#    _tmp=`$echo $ng |$sed "s/\..*//"`
#    _rec2=${_NEWSLIB}/`$cat -s ${_NEWSLIB}/recording|$grep ${_tmp}.all|$sed "s/^.*	//"`
#    if $test -f ${_rec1} ; then
#	$cat -s ${_rec1}
#    fi
#    if $test -f ${_rec2} ; then
#	$cat -s ${_rec2}
#    fi
#fi

# tell them what we think they are doing... !DIST!
case $1 in
net.*)
    $echo 'This program posts news to many hundreds of machines throughout the world.'
    ;;
$cont*)
    $echo 'This program posts news to many machines throughout the continent.'
    ;;
$cntry*)
    $echo 'This program posts news to many machines throughout the country.'
    ;;
$state*)
    $echo 'This program posts news to many machines throughout the state.'
    ;;
$city*)
    $echo 'This program posts news to many machines throughout the city.'
    ;;
$org*)
    $echo 'This program posts news to machines throughout the organization.'
    ;;
$loc*)
    $echo 'This program posts news to machines throughout the local organization.'
    ;;
*.*)
    $echo 'This program may post news to many machines.'
    ;;
*)
    $echo 'This program posts news to everyone on the machine.'
    ;;
esac
ans=""
while $test "$ans" = "" ; do
    $echo $n "Are you absolutely sure that you want to do this? [ny] $c"
    read ans
    case $ans in
    y*) ;;
    f*) suppressmess=y ;;
    h*) $cat <<'EOH'

Type n or CR to exit, y to post.

EOH
	ans="" ;;
    *) exit ;;
    esac
done

file=h
while $test "$file" = h ; do
    $echo ""
    $echo $n "Input file name?  [RETURN for new file]: $c"
    read file
    case $file in
    h)
	$cat <<'EOH'

If you have already produced the body of your article, type the filename
for it here.  If you just want to proceed directly to the editor, type a
RETURN.  In any event, you will be allowed to edit as many times as you
want before you send off the article.
EOH
	;;
    '')
	$echo "" >> $tmpart
	state=edit
	;;
    *)
	$cat $file >>$tmpart
	state=ask
	;;
    esac
done

$echo ""

while true ; do
    case $state in
    edit)
	case $expertise in
	beginner)
	    $cat </dev/null >${DOTDIR-${HOME-$LOGDIR}}/.pnewsexpert
	    $cat <<'EOMessage'
A temporary file has been created for you to edit.  Be sure to leave at
least one blank line between the header and the body of your message.
(And until a certain bug is fixed all over the net, don't start the body of
your message with any indentation, or it may get eaten.)

Within the header may be fields that you don't understand.  If you don't
understand a field (or even if you do), you can simply leave it blank, and
it will go away when the article is posted.

Type return to get the default editor, or type the name of your favorite
editor.

EOMessage
	    ;;
	esac
	tmp=h
	while $test "$tmp" = h ; do
	    $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c"
	    read tmp
	    case $tmp in
	    h)
		$cat <<'EOH'

Type a return to get the default editor, or type the name of the editor you
prefer.  The default editor depends on the VISUAL and EDITOR environment
variables.

EOH
		;;
	    '')
		;;
	    *)
		VISUAL=$tmp
		export VISUAL
		;;
	    esac
	done
	trap "" 2
	${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart
	trap "$rescue" 2
	state=ask
	;;
	
    ask)
	$echo ""
	$echo $n "Send, abort, edit, or list? $c"
	read ans
	
	case $ans in
	a*)
	    state=rescue
	    ;;
	e*)
	    state=edit
	    ;;
	l*)
	    $pager $tmpart
	    state=ask
	    ;;
	s*)
	    state=send
	    ;;
	h*)
	    $cat <<'EOH'

Type s to send the article, a to abort and append the article to dead.article,
e to edit the article again, or l to list the article.
EOH
	esac
	;;
    
    send)
	set `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'`
	case $# in
	2)
	    if $inews -h < $tmpart ; then
		state=cleanup
	    else
		state=rescue
	    fi
	    ;;
	*)
	    $echo ""
	    $echo "Malformed Newsgroups line."
	    $echo ""
	    sleep 1
	    state=edit
	    ;;
	esac
	;;
    rescue)
	$cat $tmpart >> ${HOME-$LOGDIR}/dead.article
	$echo "Article saved to ${HOME-$LOGDIR}/dead.article"
	state=cleanup
	;;
    cleanup)
	$rm -f $tmpart
	exit
	;;
    esac
done
!NO!SUBS!
$eunicefix Pnews
chmod 755 Pnews
!STUFFY!FUNK!
echo Extracting util.c
cat >util.c <<'!STUFFY!FUNK!'
/* $Header: util.c,v 4.1 84/09/24 12:11:16 lwall Exp $
 *
 * $Log:	util.c,v $
 * Revision 4.1  84/09/24  12:11:16  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.4  84/09/12  17:15:08  lwall
 * Ndir stuff.
 * 
 * Revision 4.0.1.3  84/09/10  15:34:14  lwall
 * Delinted.
 * 
 * Revision 4.0.1.2  84/09/06  09:19:14  lwall
 * Fixed bad cast.
 * 
 * Revision 4.0.1.1  84/09/06  08:08:36  lwall
 * Getwd now uses correct Null values.
 * 
 * Revision 4.0  84/09/04  09:53:03  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "final.h"
#include "ndir.h"
#include "INTERN.h"
#include "util.h"

void
util_init()
{
    ;
}
    
/* fork and exec a shell command */

int
doshell(shl,s)
char *s, *shl;
{
    int status, pid, w;
    register int (*istat)(), (*qstat)();
    int (*signal())();
    char *shell;

#ifdef SIGTSTP
    sigset(SIGTSTP,SIG_DFL);
    sigset(SIGCONT,SIG_DFL);
#endif
    if (shl != Nullch)
	shell = shl;
    else if ((shell = getenv("SHELL")) == Nullch || !*shell)
	shell = PREFSHELL;
    if ((pid = vfork()) == 0) {
	if (*s)
	    execl(shell, shell, "-c", s, 0);
	else
	    execl(shell, shell, 0);
	_exit(127);
    }
    istat = signal(SIGINT, SIG_IGN);
    qstat = signal(SIGQUIT, SIG_IGN);
    waiting = TRUE;
    while ((w = wait(&status)) != pid && w != -1)
	;
    if (w == -1)
	status = -1;
    waiting = FALSE;
    signal(SIGINT, istat);
    signal(SIGQUIT, qstat);
#ifdef SIGTSTP
    sigset(SIGTSTP,stop_catcher);
    sigset(SIGCONT,cont_catcher);
#endif
    return status;
}

static char nomem[] = "rn: out of memory!\n";

/* paranoid version of malloc */

char *
safemalloc(size)
MEM_SIZE size;
{
    char *ptr;
    char *malloc();

    ptr = malloc(size?size:1);	/* malloc(0) is NASTY on our system */
    if (ptr != Nullch)
	return ptr;
    else {
	fputs(nomem,stdout);
	sig_catcher(0);
    }
    /*NOTREACHED*/
}

/* paranoid version of realloc */

char *
saferealloc(where,size)
char *where;
MEM_SIZE size;
{
    char *ptr;
    char *realloc();

    ptr = realloc(where,size?size:1);	/* realloc(0) is NASTY on our system */
    if (ptr != Nullch)
	return ptr;
    else {
	fputs(nomem,stdout);
	sig_catcher(0);
    }
    /*NOTREACHED*/
}

/* safe version of string copy */

char *
safecpy(to,from,len)
char *to;
register char *from;
register int len;
{
    register char *dest = to;

    if (from != Nullch) 
	for (len--; len && (*dest++ = *from++); len--) ;
    *dest = '\0';
    return to;
}

/* safe version of string concatenate, with \n deletion and space padding */

char *
safecat(to,from,len)
char *to;
register char *from;
register int len;
{
    register char *dest = to;

    len--;				/* leave room for null */
    if (*dest) {
	while (len && *dest++) len--;
	if (len) {
	    len--;
	    *(dest-1) = ' ';
	}
    }
    if (from != Nullch)
	while (len && (*dest++ = *from++)) len--;
    if (len)
	dest--;
    if (*(dest-1) == '\n')
	dest--;
    *dest = '\0';
    return to;
}

/* copy a string up to some (non-backslashed) delimiter, if any */

char *
cpytill(to,from,delim)
register char *to, *from;
register int delim;
{
    for (; *from; from++,to++) {
	if (*from == '\\' && from[1] == delim)
	    from++;
	else if (*from == delim)
	    break;
	*to = *from;
    }
    *to = '\0';
    return from;
}

/* return ptr to little string in big string, NULL if not found */

char *
instr(big, little)
char *big, *little;

{
    register char *t, *s, *x;

    for (t = big; *t; t++) {
	for (x=t,s=little; *s; x++,s++) {
	    if (!*x)
		return Nullch;
	    if (*s != *x)
		break;
	}
	if (!*s)
	    return t;
    }
    return Nullch;
}

/* effective access */

#ifdef SETUIDGID
int
eaccess(filename, mode)
char *filename;
int mode;
{
    int protection, euid;
    
    mode &= 7;				/* remove extraneous garbage */
    if (stat(filename, &filestat) < 0)
	return -1;
    euid = geteuid();
    if (euid == ROOTID)
	return 0;
    protection = 7 & (filestat.st_mode >>
      (filestat.st_uid == euid ? 6 :
        (filestat.st_gid == getegid() ? 3 : 0)
      ));
    if ((mode & protection) == mode)
	return 0;
    errno = EACCES;
    return -1;
}
#endif

/*
 * Get working directory
 */

#ifdef GETWD
#define	dot	"."
#define	dotdot	".."

static	char	*name;

static	DIR	*dirp;
static	int	off;
static	struct	stat	d, dd;
static	struct	direct	*dir;

char *
getwd(np)
char *np;
{
	long rdev, rino;

	*np++ = '/';
	name = np;
	off = -1;
	stat("/", &d);
	rdev = d.st_dev;
	rino = d.st_ino;
	for (;;) {
		stat(dot, &d);
		if (d.st_ino==rino && d.st_dev==rdev)
			goto done;
		if ((dirp = opendir(dotdot)) == Null(DIR *))
			prexit("getwd: cannot open ..\n");
		stat(dotdot, &dd);
		chdir(dotdot);
		if(d.st_dev == dd.st_dev) {
			if(d.st_ino == dd.st_ino)
				goto done;
			do
				if ((dir = readdir(dirp)) == Null(struct direct *))
					prexit("getwd: read error in ..\n");
			while (dir->d_ino != d.st_ino);
		}
		else do {
				if ((dir = readdir(dirp)) == Null(struct direct *))
					prexit("getwd: read error in ..\n");
				stat(dir->d_name, &dd);
			} while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
		cat();
		closedir(dirp);
	}
done:
	name--;
	if (chdir(name) < 0) {
		printf("getwd: can't cd back to %s\n",name);
		sig_catcher(0);
	}
	return (name);
}

void
cat()
{
	register i, j;

	i = -1;
	while (dir->d_name[++i] != 0);
	if ((off+i+2) > 1024-1)
		return;
	for(j=off+1; j>=0; --j)
		name[j+i+1] = name[j];
	if (off >= 0)
		name[i] = '/';
	off=i+off+1;
	name[off] = 0;
	for(--i; i>=0; --i)
		name[i] = dir->d_name[i];
}

void
prexit(cp)
char *cp;
{
	write(2, cp, strlen(cp));
	sig_catcher(0);
}
#else
char *
getwd(np)			/* shorter but slower */
char *np;
{
    FILE *popen();
    FILE *pipefp = popen("/bin/pwd","r");

    fgets(np,512,pipefp);
    np[strlen(np)-1] = '\0';	/* wipe out newline */
    pclose(pipefp);
    return np;
}
#endif

/* just like fgets but will make bigger buffer as necessary */

char *
get_a_line(original_buffer,buffer_length,fp)
char *original_buffer;
register int buffer_length;
FILE *fp;
{
    register int bufix = 0;
    register int nextch;
    register char *some_buffer_or_other = original_buffer;

    do {
	if (bufix >= buffer_length) {
	    buffer_length *= 2;
	    if (some_buffer_or_other == original_buffer) {
					/* currently static? */
		some_buffer_or_other = safemalloc((MEM_SIZE)buffer_length+1);
		strncpy(some_buffer_or_other,original_buffer,buffer_length/2);
					/* so we must copy it */
	    }
	    else {			/* just grow in place, if possible */
		some_buffer_or_other = saferealloc(some_buffer_or_other,
		    (MEM_SIZE)buffer_length+1);
	    }
	}
	if ((nextch = getc(fp)) == EOF)
	    return Nullch;
	some_buffer_or_other[bufix++] = (char) nextch;
    } while (nextch && nextch != '\n');
    some_buffer_or_other[bufix] = '\0';
    len_last_line_got = bufix;
    return some_buffer_or_other;
}

/* copy a string to a safe spot */

char *
savestr(str)
char *str;
{
    register char *newaddr = safemalloc((MEM_SIZE)(strlen(str)+1));

    strcpy(newaddr,str);
    return newaddr;
}

int
makedir(dirname,nametype)
register char *dirname;
int nametype;
{
#ifdef MAKEDIR
    register char *end;
    register char *s;
    char tmpbuf[1024];
    register char *tbptr = tmpbuf+5;

    for (end = dirname; *end; end++) ;	/* find the end */
    if (nametype == MD_FILE) {		/* not to create last component? */
	for (--end; end != dirname && *end != '/'; --end) ;
	if (*end != '/')
	    return 0;			/* nothing to make */
	*end = '\0';			/* isolate file name */
    }
    strcpy(tmpbuf,"mkdir");

    s = end;
    for (;;) {
	if (stat(dirname,&filestat) >= 0) {
					/* does this much exist? */
	    *s = '/';			/* mark this as existing */
	    break;
	}
	s = rindex(dirname,'/');	/* shorten name */
	if (!s)				/* relative path! */
	    break;			/* hope they know what they are doing */
	*s = '\0';			/* mark as not existing */
    }
    
    for (s=dirname; s <= end; s++) {	/* this is grody but efficient */
	if (!*s) {			/* something to make? */
	    sprintf(tbptr," %s",dirname);
	    tbptr += strlen(tbptr);	/* make it, sort of */
	    *s = '/';			/* mark it made */
	}
    }
    if (nametype == MD_DIR)		/* don't need final slash unless */
	*end = '\0';			/*  a filename follows the dir name */

    return (tbptr==tmpbuf+5 ? 0 : doshell(sh,tmpbuf));
					/* exercise our faith */
#else
    sprintf(cmd_buf,"%s %s %d", filexp(DIRMAKER), dirname, nametype);
    return doshell(sh,cmd_buf);
#endif
}

#ifdef SETENV
static bool firstsetenv = TRUE;
extern char **environ;

void
setenv(nam,val)
char *nam, *val;
{
    register int i=envix(nam);		/* where does it go? */

    if (!environ[i]) {			/* does not exist yet */
	if (firstsetenv) {		/* need we copy environment? */
	    int j;
	    /*NOSTRICT*/
	    char **tmpenv = (char**)	/* point our wand at memory */
		safemalloc((MEM_SIZE) (i+2) * sizeof(char*));
    
	    firstsetenv = FALSE;
	    for (j=0; j<i; j++)		/* copy environment */
		tmpenv[j] = environ[j];
	    environ = tmpenv;		/* tell exec where it is now */
	}
	else
	    /*NOSTRICT*/
	    environ = (char**) saferealloc((char*) environ,
		(MEM_SIZE) (i+2) * sizeof(char*));
					/* just expand it a bit */
	environ[i+1] = Nullch;	/* make sure it's null terminated */
    }
    environ[i] = safemalloc((MEM_SIZE) strlen(nam) + strlen(val) + 2);
					/* this may or may not be in */
					/* the old environ structure */
    sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */
}

int
envix(nam)
char *nam;
{
    register int i, len = strlen(nam);

    for (i = 0; environ[i]; i++) {
	if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
	    break;			/* strnEQ must come first to avoid */
    }					/* potential SEGV's */
    return i;
}
#endif

void
notincl(feature)
char *feature;
{
    printf("\nNo room for feature \"%s\" on this machine.\n",feature);
}

char *
getval(nam,def)
char *nam,*def;
{
    char *val;

    if ((val = getenv(nam)) == Nullch || !*val)
	val = def;
    return val;
}

/* grow a static string to at least a certain length */

void
growstr(strptr,curlen,newlen)
char **strptr;
int *curlen;
int newlen;
{
    if (newlen > *curlen) {		/* need more room? */
	if (*curlen)
	    *strptr = saferealloc(*strptr,(MEM_SIZE)newlen);
	else
	    *strptr = safemalloc((MEM_SIZE)newlen);
	*curlen = newlen;
    }
}

void
setdef(buffer,dflt)
char *buffer,*dflt;
{
    if (*buffer == ' ') {
	if (*dflt == '^' && isupper(dflt[1]))
	    *buffer = Ctl(dflt[1]);
	else
	    *buffer = *dflt;
    }
}
!STUFFY!FUNK!
echo Extracting respond.c
cat >respond.c <<'!STUFFY!FUNK!'
/* $Header: respond.c,v 4.1 84/09/24 12:06:33 lwall Exp $
 *
 * $Log:	respond.c,v $
 * Revision 4.1  84/09/24  12:06:33  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/13  12:07:27  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.2  84/09/10  15:28:27  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/10  08:49:29  lwall
 * MBOXCHAR now matches against 1st non-space type character.
 * 
 * Revision 4.0  84/09/04  09:52:13  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "intrp.h"
#include "head.h"
#include "term.h"
#include "ng.h"
#include "util.h"
#include "rn.h"
#include "intrp.h"
#include "artio.h"
#include "final.h"
#include "INTERN.h"
#include "respond.h"

static char nullart[] = "\nNull article\n";

void
respond_init()
{
    ;
}

int
save_article()
{
    bool use_pref;
    register char *s, *c;
    char altbuf[256];
    int iter;
    bool interactive = (buf[1] == FINISHCMD);
    
    if (!finish_command(interactive))	/* get rest of command */
	return SAVE_ABORT;
    use_pref = isupper(*buf);
#ifdef ASYNC_PARSE
    parse_maybe(art);
#endif
    savefrom = (*buf=='w' || *buf=='W' ? htype[PAST_HEADER].ht_minpos : 0);
    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\n\
Saving null articles is not very productive!  :-)\n\
",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout);
#endif
	return SAVE_DONE;
    }
    if (chdir(cwd)) {
	printf(nocd,cwd);
	sig_catcher(0);
    }
    if (savedest)
	free(savedest);
    if ((s = index(buf,'|')) != Nullch) {
				/* is it a pipe command? */
	s++;			/* skip the | */
	while (*s == ' ') s++;
	safecpy(altbuf,filexp(s),sizeof altbuf);
	savedest = altbuf;
	interp(cmd_buf,getval("PIPESAVER",PIPESAVER));
				/* then set up for command */
	resetty();		/* restore tty state */
	if (use_pref)		/* use preferred shell? */
	    doshell(Nullch,cmd_buf);
				/* do command with it */
	else
	    doshell(sh,cmd_buf);	/* do command with sh */
	noecho();		/* and stop echoing */
	crmode();		/* and start cbreaking */
	savedest = savestr(savedest);
    }
    else {			/* normal save */
	bool there, mailbox;
	char *savename = getval("SAVENAME",SAVENAME);

	s = buf+1;		/* skip s or S */
	if (*s == '-') {	/* if they are confused, skip - also */
#ifdef VERBOSE
	    IF(verbose)
		fputs("Warning: '-' ignored.  This isn't readnews.\n",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("'-' ignored.\n",stdout);
#endif
	    s++;
	}
	for (; *s == ' '; s++);	/* skip spaces */
	safecpy(altbuf,filexp(s),sizeof altbuf);
	s = altbuf;
	if (! index(s,'/')) {
	    interp(buf,getval("SAVEDIR",SAVEDIR));
	    if (makedir(buf,MD_DIR))	/* ensure directory exists */
		strcpy(buf,cwd);
	    if (*s) {
		for (c = buf; *c; c++) ;
		*c++ = '/';
		strcpy(c,s);		/* add filename */
	    }
	    s = buf;
	}
	for (iter = 0;
	    (there = stat(s,&filestat) >= 0) &&
	    (filestat.st_mode & S_IFDIR);
	    iter++) {			/* is it a directory? */

	    c = (s+strlen(s));
	    *c++ = '/';			/* put a slash before filename */
	    interp(c, iter ? "News" : savename );
				/* generate a default name somehow or other */
	    if (index(c,'/')) {		/* yikes, a '/' in the filename */
		makedir(s,MD_FILE);
	    }
	}
	if (*s != '/') {		/* relative path? */
	    c = (s==buf ? altbuf : buf);
	    sprintf(c, "%s/%s", cwd, s);
	    s = c;			/* absolutize it */
	}
	s = savedest = savestr(s);	/* doesn't move any more */
					/* make it handy for %b */
	if (!there) {
	    if (mbox_always)
		mailbox = TRUE;
	    else if (norm_always)
		mailbox = FALSE;
	    else {
		char *dflt = (instr(savename,"%a") ? "nyq" : "ynq");
		
		sprintf(cmd_buf,
		"\nFile %s doesn't exist--\n	use mailbox format? [%s] ",
		  s,dflt);
	      reask_save:
		in_char(cmd_buf);
		putchar('\n');
		setdef(buf,dflt);
#ifdef VERIFY
		printcmd();
#endif
		if (*buf == 'h') {
#ifdef VERBOSE
		    IF(verbose)
			printf("\n\
Type y to create %s as a mailbox.\n\
Type n to create it as a normal file.\n\
Type q to abort the save.\n\
",s);
		    ELSE
#endif
#ifdef TERSE
			fputs("\n\
y to create mailbox.\n\
n to create normal file.\n\
q to abort.\n\
",stdout);
#endif
		    goto reask_save;
		}
		else if (*buf == 'n') {
		    mailbox = FALSE;
		}
		else if (*buf == 'y') {
		    mailbox = TRUE;
		}
		else if (*buf == 'q') {
		    goto s_bomb;
		}
		else {
		    fputs(hforhelp,stdout);
		    goto reask_save;
		}
	    }
	}
	else if (filestat.st_mode & S_IFCHR)
	    mailbox = FALSE;
	else {
	    int tmpfd;
	    
	    tmpfd = open(s,0);
	    if (tmpfd == -1)
		mailbox = FALSE;
	    else {
		read(tmpfd,buf,LBUFLEN);
		c = buf;
		if (!isspace(MBOXCHAR))
		    while (isspace(*c))
			c++;
		mailbox = (*c == MBOXCHAR);
		close(tmpfd);
	    }
	}

	safecpy(cmd_buf, filexp(mailbox ?
	    getval("MBOXSAVER",MBOXSAVER) :
	    getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf);
				/* format the command */
	resetty();		/* make terminal behave */
	if (doshell(use_pref?Nullch:SH,cmd_buf))
	    fputs("Not saved",stdout);
	else
	    printf("%s to %s %s",
	      there?"Appended":"Saved",
	      mailbox?"mailbox":"file",
	      s);
	if (interactive)
	    putchar('\n');
	noecho();		/* make terminal do what we want */
	crmode();
    }
s_bomb:
    if (chdir(spool) || chdir(ngdir)) {
	printf(nocd,ngdir);
	sig_catcher(0);
    }
    return SAVE_DONE;
}

int
cancel_article()
{
    char *artid_buf;
    char *ngs_buf;
    char *from_buf;
    char *reply_buf;
    int myuid = getuid();
    int r = -1;

    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\n\
Cancelling null articles is your idea of fun?  :-)\n\
",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout);
#endif
	return r;
    }
    reply_buf = fetchlines(art,REPLY_LINE);
    from_buf = fetchlines(art,FROM_LINE);
    artid_buf = fetchlines(art,ARTID_LINE);
    ngs_buf = fetchlines(art,NGS_LINE);
    if (!instr(from_buf,sitename) ||
	(!instr(from_buf,logname) &&
	 !instr(reply_buf,logname) &&
#ifdef NEWSADMIN
	 myuid != newsuid &&
#endif
	 myuid != ROOTID ) )
#ifdef VERBOSE
	    IF(verbose)
		fputs("You can't cancel someone else's article\n",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("Not your article\n",stdout);
#endif
    else {
	tmpfp = fopen(headname,"w");	/* open header file */
	interp(buf,getval("CANCELHEADER",CANCELHEADER));
	fputs(buf,tmpfp);
	fclose(tmpfp);
	r = doshell(sh,filexp(getval("CANCEL",CANCEL)));
    }
    free(artid_buf);
    free(ngs_buf);
    free(from_buf);
    free(reply_buf);
    return r;
}

void
reply()
{
    bool incl_body = (*buf == 'R');
    char *maildoer = savestr(filexp(getval("MAILPOSTER",MAILPOSTER)));

    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\nBut null articles are so dull!  :-)\n",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout);
#endif
	free(maildoer);
	return;
    }
    tmpfp = fopen(headname,"w");	/* open header file */
    interp(buf,getval("MAILHEADER",MAILHEADER));
    fputs(buf,tmpfp);
    if (!instr(maildoer,"%h"))
#ifdef VERBOSE
	IF(verbose)
	    printf("\n%s\n(Above lines saved in file %s)\n",buf,headname);
	ELSE
#endif
#ifdef TERSE
	    printf("\n%s\n(Header in %s)\n",buf,headname);
#endif
    if (incl_body) {
	interp(buf,getval("YOUSAID",YOUSAID));
	fprintf(tmpfp,"%s\n",buf);
#ifdef ASYNC_PARSE
	parse_maybe(art);
#endif
	fseek(artfp,htype[PAST_HEADER].ht_minpos,0);
	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
	    fprintf(tmpfp,"%s%s",indstr,buf);
	}
	fprintf(tmpfp,"\n");
    }
    fclose(tmpfp);
    interp(cmd_buf,maildoer);
    invoke(cmd_buf,origdir);
    UNLINK(headname);		/* kill the header file */
    free(maildoer);
}

void
followup()
{
    bool incl_body = (*buf == 'F');

    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\nNull articles give me indigestion!  :-)\n",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout);
#endif
	return;
    }
    tmpfp = fopen(headname,"w");
    interp(buf,getval("NEWSHEADER",NEWSHEADER));
    fprintf(tmpfp,"%s",buf);
    if (incl_body) {
#ifdef VERBOSE
	if (verbose)
	    fputs("\n\
(Be sure to double-check the attribution against the signature, and\n\
trim the quoted article down as much as possible.)\n\
",stdout);
#endif
	interp(buf,getval("ATTRIBUTION",ATTRIBUTION));
	fprintf(tmpfp,"%s\n",buf);
#ifdef ASYNC_PARSE
	parse_maybe(art);
#endif
	fseek(artfp,htype[PAST_HEADER].ht_minpos,0);
	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
	    fprintf(tmpfp,"%s%s",indstr,buf);
	}
	fprintf(tmpfp,"\n");
    }
    fclose(tmpfp);
    safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf);
    invoke(cmd_buf,origdir);
    UNLINK(headname);
}

void
invoke(cmd,dir)
char *cmd,*dir;
{
    if (chdir(dir)) {
	printf(nocd,dir);
	return;
    }
#ifdef VERBOSE
    IF(verbose)
	printf("\n(leaving cbreak mode; cwd=%s)\nInvoking command: %s\n\n",
	    dir,cmd);
    ELSE
#endif
#ifdef TERSE
	printf("\n(-cbreak; cwd=%s)\nInvoking: %s\n\n",dir,cmd);
#endif
    resetty();			/* make terminal well-behaved */
    doshell(sh,cmd);		/* do the command */
    noecho();			/* set no echo */
    crmode();			/* and cbreak mode */
#ifdef VERBOSE
    IF(verbose)
	fputs("\n(re-entering cbreak mode)\n",stdout);
    ELSE
#endif
#ifdef TERSE
	fputs("\n(+cbreak)\n",stdout);
#endif
    if (chdir(spool) || chdir(ngdir)) {
	printf(nocd,ngdir);
	sig_catcher(0);
    }
}

!STUFFY!FUNK!
echo Extracting sw.c
cat >sw.c <<'!STUFFY!FUNK!'
/* $Header: sw.c,v 4.1 84/09/24 12:10:21 lwall Exp $
 *
 * $Log:	sw.c,v $
 * Revision 4.1  84/09/24  12:10:21  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:31:49  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:46  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "head.h"
#include "only.h"
#include "term.h"
#include "ng.h"
#include "intrp.h"
#include "INTERN.h"
#include "sw.h"

void
sw_init(argc,argv,tcbuf)
int argc;
char *argv[];
char *tcbuf;
{
    register int i;

    safecpy(tcbuf,getenv("RNINIT"),1024);
    if (*tcbuf) {
	if (*tcbuf == '/') {
	    int initfd = open(tcbuf,0);
		
	    if (initfd >= 0) {
		fstat(initfd,&filestat);
		if (filestat.st_size > 1024)
		    tcbuf = saferealloc(tcbuf,(MEM_SIZE)filestat.st_size);
		if (filestat.st_size) {
		    read(initfd,tcbuf,(int)filestat.st_size);
		    tcbuf[filestat.st_size-1] = '\0';
					/* wipe out last newline */
		}
		else
		    *tcbuf = '\0';
		close(initfd);
	    }
	    else
		*tcbuf = '\0';
	}
	sw_list(tcbuf);
    }

    for (i = 1; i < argc; i++)
	decode_switch(argv[i]);
}

/* decode a list of space separated switches */

void
sw_list(swlist)
char *swlist;
{
    char *tmplist = safemalloc((MEM_SIZE) strlen(swlist) + 2);
					/* semi-automatic string */
    register char *p, inquote = 0;

    strcpy(tmplist,swlist);
    for (p=tmplist; isspace(*p); p++) ;	/* skip any initial spaces */
    while (*p) {			/* "String, or nothing" */
	if (!inquote && isspace(*p)) {	/* word delimiter? */
	    *p++ = '\0';		/* chop here */
	    while (isspace(*p))		/* these will be ignored later */
		p++;
	}
	else if (inquote == *p) {
	    strcpy(p,p+1);		/* delete trailing quote */
	    inquote = 0;		/* no longer quoting */
	}
	else if (!inquote && *p == '"' || *p == '\'') {
					/* OK, I know when I am not wanted */
	    inquote = *p;		/* remember single or double */
	    strcpy(p,p+1);		/* delete the quote */
	}				/* (crude, but effective) */
	else if (*p == '\\') {		/* quoted something? */
	    if (p[1] == '\n')		/* newline? */
		strcpy(p,p+2);		/* "I didn't see anything" */
	    else {
		strcpy(p,p+1);		/* delete the backwhack */
		p++;			/* leave the whatever alone */
	    }
	}
	else
	    p++;			/* normal char, leave it alone */
    }
    *++p = '\0';			/* put an extra null on the end */
    if (inquote)
	printf("Unmatched %c in switch\n",inquote);
    for (p = tmplist; *p; /* p += strlen(p)+1 */ ) {
	decode_switch(p);
	while (*p++) ;			/* point at null + 1 */
    }
    free(tmplist);			/* this oughta be in Ada */
}

/* decode a single switch */

void
decode_switch(s)
register char *s;
{
    while (isspace(*s))			/* ignore leading spaces */
	s++;
#ifdef DEBUGGING
    if (debug)
	printf("Switch: %s\n",s);
#endif
    if (*s != '-' && *s != '+') {	/* newsgroup pattern */
	setngtodo(s);
    }
    else {				/* normal switch */
	bool upordown = *s == '-' ? TRUE : FALSE;
	char tmpbuf[LBUFLEN];

	s++;
	switch (*s) {
#ifdef BAUDMOD
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	    if (upordown ? (atoi(s) <= just_a_sec*10)
	    		 : (atoi(s) >= just_a_sec*10) ) {
		while (isdigit(*s)) s++;
		decode_switch(s);
	    }
	    break;
#endif
	case '/':
#ifdef SETENV
	    setenv("SAVEDIR",  upordown ? "%p/%c" : "%p" );
	    setenv("SAVENAME", upordown ? "%a"    : "%^C");
#else
	    notincl("-/");
#endif
	    break;
	case 'c':
	    checkflag = upordown;
	    break;
	case 'C':
	    s++;
	    if (*s == '=') s++;
	    docheckwhen = atoi(s);
	    break;
	case 'd': {
	    s++;
	    if (*s == '=') s++;
	    if (cwd) {
		chdir(cwd);
		free(cwd);
	    }
	    cwd = savestr(s);
	    break;
	}
#ifdef DEBUGGING
	case 'D':
	    s++;
	    if (*s == '=') s++;
	    if (*s)
		if (upordown)
		    debug |= atoi(s);
		else
		    debug &= ~atoi(s);
	    else
		if (upordown)
		    debug |= 1;
		else
		    debug = 0;
	    break;
#endif
	case 'e':
	    erase_screen = upordown;
	    break;
	case 'E':
#ifdef SETENV
	    s++;
	    if (*s == '=')
		s++;
	    strcpy(tmpbuf,s);
	    s = index(tmpbuf,'=');
	    if (s) {
		*s++ = '\0';
		setenv(tmpbuf,s);
	    }
	    else
		setenv(tmpbuf,nullstr);
#else
	    notincl("-E");
#endif
	    break;
	case 'F':
	    s++;
	    indstr = savestr(s);
	    break;
#ifdef INNERSEARCH
	case 'g':
	    gline = atoi(s+1)-1;
	    break;
#endif
	case 'H':
	case 'h': {
	    register int len, i;
	    char *t;
	    int flag = (*s == 'h' ? HT_HIDE : HT_MAGIC);
	    
	    s++;
	    len = strlen(s);
	    for (t=s; *t; t++)
		if (isupper(*t))
		   *t = tolower(*t);
	    for (i=HEAD_FIRST; i<HEAD_LAST; i++)
		if (!len || strnEQ(s,htype[i].ht_name,len))
		    if (upordown)
			htype[i].ht_flags |= flag;
		    else
			htype[i].ht_flags &= ~flag;
	    break;
	}
	case 'i':
	    s++;
	    if (*s == '=') s++;
	    initlines = atoi(s);
	    break;
	case 'l':
	    muck_up_clear = upordown;
	    break;
	case 'M':
	    mbox_always = upordown;
	    break;
	case 'm':
	    s++;
	    if (*s == '=') s++;
	    if (!upordown)
		marking = NOMARKING;
	    else if (*s == 'u')
		marking = UNDERLINE;
	    else {
		marking = STANDOUT;
	    }
	    break;
	case 'N':
	    norm_always = upordown;
	    break;
#ifdef VERBOSE
	case 'n':
	    fputs("This isn't readnews.  Don't use -n.\n\n",stdout);
	    break;
#endif
	case 'r':
	    findlast = upordown;
	    break;
	case 's':
	    s++;
	    if (*s == '=') s++;
	    if (*s) {
		countdown = atoi(s);
		suppress_cn = FALSE;
	    }
	    else {
		if (!upordown)
		    countdown = 5;
		suppress_cn = upordown;
	    }
	    break;
	case 'S':
#ifdef ARTSEARCH
	    s++;
	    if (*s == '=') s++;
	    if (*s)
		scanon = atoi(s);
	    else
		scanon = upordown*3;
#else
	    notincl("-S");
#endif
	    break;
	case 't':
#ifdef VERBOSE
#ifdef TERSE
	    verbose = !upordown;
#else
	    notincl("+t");
#endif
#else
	    notincl("+t");
#endif
	    break;
	case 'T':
	    typeahead = upordown;
	    break;
	case 'v':
#ifdef VERIFY
	    verify = upordown;
#else
	    notincl("-v");
#endif
	    break;
	default:
#ifdef VERBOSE
	    IF(verbose)
		printf("\nIgnoring unrecognized switch: -%c\n", *s);
	    ELSE
#endif
#ifdef TERSE
		printf("\nIgnoring -%c\n", *s);
#endif
	    break;
	}
    }
}

/* print current switch values */

void
pr_switches()
{
    static char mp[2] = {'+','-'};
    register int i;
    
    fputs("\nCurrent switch settings:\n",stdout);
    printf("%c/ ", mp[strEQ(getval("SAVEDIR",SAVEDIR),"%d/%c")]);
    printf("%cc ", mp[checkflag]);
    printf("-C%d ", docheckwhen);
    printf("-d%s ", cwd);
#ifdef DEBUGGING
    if (debug)
	printf("-D%d ", debug);
#endif
    printf("%ce ", mp[erase_screen]);
    printf("-F\"%s\" ", indstr);
#ifdef INNERSEARCH
    printf("-g%d", gline);
#endif
    putchar('\n');
#ifdef VERBOSE
    if (verbose) {
	for (i=HEAD_FIRST; i<HEAD_LAST; i++)
	    printf("%ch%s%c",
		mp[htype[i].ht_flags & HT_HIDE], htype[i].ht_name,
		(! (i % 5) ? '\n' : ' ') );
    }
#endif
    printf("-i%d ", initlines);
    printf("%cl ", mp[muck_up_clear]);
    if (marking)
	printf("-m%c ",marking==UNDERLINE?'u':'s');
    else
	printf("+m ");
    printf("%cM ", mp[mbox_always]);
    printf("%cN ", mp[norm_always]);
    printf("%cr ", mp[findlast]);
    if (countdown)
	printf("-s%d ", countdown);
    else
	printf("%cs ", mp[suppress_cn]);
#ifdef ARTSEARCH
    if (scanon)
	printf("-S%d ",scanon);
    else
	printf("+S ");
#ifdef VERBOSE
#ifdef TERSE
    printf("%ct ", mp[!verbose]);
#endif
#endif
    printf("%cT ", mp[typeahead]);
#ifdef VERIFY
    printf("%cv ", mp[verify]);
#endif
#endif
    fputs("\n\n",stdout);
#ifdef ONLY
    if (maxngtodo) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("Current restriction:",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("Only:",stdout);
#endif
	for (i=0; i<maxngtodo; i++)
	    printf(" %s",ngtodo[i]);
	fputs("\n\n",stdout);
    }
#ifdef VERBOSE
    else if (verbose)
	fputs("No restriction.\n\n",stdout);
#endif
#endif
}

void
cwd_check()
{
    char tmpbuf[LBUFLEN];

    if (!cwd)
	cwd = savestr(filexp("~/News"));
    strcpy(tmpbuf,cwd);
    if (chdir(cwd)) {
	safecpy(tmpbuf,filexp(cwd),sizeof tmpbuf);
	if (makedir(tmpbuf,MD_DIR) < 0 || chdir(tmpbuf) < 0) {
	    interp(cmd_buf,"%~/News");
	    if (makedir(cmd_buf,MD_DIR) < 0)
		strcpy(tmpbuf,homedir);
	    else
		strcpy(tmpbuf,cmd_buf);
	    chdir(tmpbuf);
#ifdef VERBOSE
	    IF(verbose)
		printf("\
Cannot make directory %s--\n\
	articles will be saved to %s\n\
\n\
",cwd,tmpbuf);
	    ELSE
#endif
#ifdef TERSE
		printf("\
Can't make %s--\n\
	using %s\n\
\n\
",cwd,tmpbuf);
#endif
	}
    }
    free(cwd);
    getwd(tmpbuf);
    if (eaccess(tmpbuf,2)) {
#ifdef VERBOSE
	IF(verbose)
	    printf("\
Current directory %s is not writeable--\n\
	articles will be saved to home directory\n\n\
",tmpbuf);
	ELSE
#endif
#ifdef TERSE
	    printf("%s not writeable--using ~\n\n",tmpbuf);
#endif
	strcpy(tmpbuf,homedir);
    }
    cwd = savestr(tmpbuf);
}
!STUFFY!FUNK!
echo Extracting inews.c.2.pat
cat >inews.c.2.pat <<'!STUFFY!FUNK!'
*** inews.c.orig	Mon Sep 24 09:57:15 1984
--- inews.c	Mon Sep 24 10:17:39 1984
***************
*** 416,421
  /*
   *	Link ARTICLE into dir for ngname and update active file.
   */
  localize(ngname)
  char	*ngname;
  {

--- 416,424 -----
  /*
   *	Link ARTICLE into dir for ngname and update active file.
   */
+ #ifdef DOXREFS
+ long
+ #endif
  localize(ngname)
  char	*ngname;
  {
***************
*** 453,458
  			mknewsg(cp, ngname);
  
  		sprintf(bfr, "%s/%ld", cp, ngsize+1);
  #ifdef VMS
  		if ((f2 = creat(bfr, 0666)) >=0 ) {
  			f1 = open(article, 0);

--- 456,466 -----
  			mknewsg(cp, ngname);
  
  		sprintf(bfr, "%s/%ld", cp, ngsize+1);
+ #ifdef LINKART
+ 		if (mylink(ARTICLE, bfr) == 0) break;
+ 				/* on first file inits ARTICLE, on subsequent */
+ 				/* files "links" to first article */
+ #else !LINKART
  #ifdef VMS
  		if ((f2 = creat(bfr, 0666)) >=0 ) {
  			f1 = open(article, 0);
***************
*** 468,473
  		if (link(ARTICLE, bfr) == 0)
  			break;
  #endif !VMS
  		e = errno;	/* keep log from clobbering it */
  		logerr("Cannot install article as %s", bfr);
  		if (e != EEXIST) {

--- 476,482 -----
  		if (link(ARTICLE, bfr) == 0)
  			break;
  #endif !VMS
+ #endif !LINKART
  		e = errno;	/* keep log from clobbering it */
  		logerr("Cannot install article as %s", bfr);
  		if (e != EEXIST) {
***************
*** 494,499
  		strcpy(firstbufname, bfr);
  	sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
  	addhist(bfr);
  	return TRUE;
  }
  

--- 503,509 -----
  		strcpy(firstbufname, bfr);
  	sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
  	addhist(bfr);
+ #ifndef DOXREFS
  	return TRUE;
  #else DOXREFS
  	return ngsize+1;
***************
*** 495,500
  	sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
  	addhist(bfr);
  	return TRUE;
  }
  
  /*

--- 505,513 -----
  	addhist(bfr);
  #ifndef DOXREFS
  	return TRUE;
+ #else DOXREFS
+ 	return ngsize+1;
+ #endif DOXREFS
  }
  
  /*
***************
*** 507,512
  	char c;
  	struct srec srec;	/* struct for sys file lookup	*/
  	int is_invalid = FALSE;
  
  	/* Fill up the rest of header. */
  	if (mode != PROC) {

--- 520,529 -----
  	char c;
  	struct srec srec;	/* struct for sys file lookup	*/
  	int is_invalid = FALSE;
+ #ifdef DOXREFS
+ 	register char *nextxref = header.xref; 
+ 	int numxrefs = 0;
+ #endif DOXREFS
  
  	/* Fill up the rest of header. */
  	if (mode != PROC) {
***************
*** 527,532
  	if (!is_ctl && mode != CREATENG)
  		is_invalid = ngfcheck(mode == PROC);
  
  	/* Write article to temp file. */
  	tfp = xfopen(mktemp(ARTICLE), "w");
  	if ( (c=getc(infp)) == ' ' || c == '\t' ) {

--- 544,556 -----
  	if (!is_ctl && mode != CREATENG)
  		is_invalid = ngfcheck(mode == PROC);
  
+ #ifdef LINKART
+ 	*ARTICLE = '\0';	/* tell mylink() to snarf the name */
+ #else !LINKART
+ #ifdef DOXREFS
+ 	/* Open temp file for article, but link before writing */
+ 	tfp = xfopen(mktemp(ARTICLE), "w");
+ #else DOXREFS
  	/* Write article to temp file. */
  	tfp = xfopen(mktemp(ARTICLE), "w");
  	if ( (c=getc(infp)) == ' ' || c == '\t' ) {
***************
*** 545,550
  		putc('\n',tfp);
  	fclose(tfp);
  	fclose(infp);
  
  	if (is_invalid) {
  		logerr("No valid newsgroups found, moved to junk");

--- 569,576 -----
  		putc('\n',tfp);
  	fclose(tfp);
  	fclose(infp);
+ #endif DOXREFS
+ #endif LINKART
  
  	if (is_invalid) {
  		logerr("No valid newsgroups found, moved to junk");
***************
*** 550,555
  		logerr("No valid newsgroups found, moved to junk");
  		if (localize("junk"))
  			savehist(histline);
  		xxit(1);
  	}
  

--- 576,582 -----
  		logerr("No valid newsgroups found, moved to junk");
  		if (localize("junk"))
  			savehist(histline);
+ #ifndef LINKART
  		xxit(1);
  #endif
  	}
***************
*** 551,556
  		if (localize("junk"))
  			savehist(histline);
  		xxit(1);
  	}
  
  	if (time((time_t)0) > (cgtdate(header.subdate) + DFLTEXP) ){

--- 578,584 -----
  			savehist(histline);
  #ifndef LINKART
  		xxit(1);
+ #endif
  	}
  #ifdef LINKART
  	else
***************
*** 552,558
  			savehist(histline);
  		xxit(1);
  	}
! 
  	if (time((time_t)0) > (cgtdate(header.subdate) + DFLTEXP) ){
  		logerr("Article too old, moved to junk");
  		if (localize("junk"))

--- 580,588 -----
  		xxit(1);
  #endif
  	}
! #ifdef LINKART
! 	else
! #endif
  	if (time((time_t)0) > (cgtdate(header.subdate) + DFLTEXP) ){
  		logerr("Article too old, moved to junk");
  		if (localize("junk"))
***************
*** 557,562
  		logerr("Article too old, moved to junk");
  		if (localize("junk"))
  			savehist(histline);
  		xxit(1);
  	}
  

--- 587,593 -----
  		logerr("Article too old, moved to junk");
  		if (localize("junk"))
  			savehist(histline);
+ #ifndef LINKART
  		xxit(1);
  #endif
  	}
***************
*** 558,563
  		if (localize("junk"))
  			savehist(histline);
  		xxit(1);
  	}
  
  	if (is_ctl) {

--- 589,595 -----
  			savehist(histline);
  #ifndef LINKART
  		xxit(1);
+ #endif
  	}
  #ifdef LINKART
  	else
***************
*** 559,565
  			savehist(histline);
  		xxit(1);
  	}
! 
  	if (is_ctl) {
  		control(&header);
  		localize("control");

--- 591,599 -----
  		xxit(1);
  #endif
  	}
! #ifdef LINKART
! 	else
! #endif
  	if (is_ctl) {
  		control(&header);
  		localize("control");
***************
*** 566,571
  	} else {
  		if (s_find(&srec, FULLSYSNAME) == FALSE)
  			xerror("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE);
  		for (ptr = nbuf; *ptr;) {
  			if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL)
  				localize(ptr);

--- 600,609 -----
  	} else {
  		if (s_find(&srec, FULLSYSNAME) == FALSE)
  			xerror("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE);
+ #ifdef DOXREFS
+ 		sprintf(nextxref,"%s ",FULLSYSNAME);
+ 		nextxref += strlen(nextxref);
+ #endif
  		for (ptr = nbuf; *ptr;) {
  #ifndef DOXREFS
  			if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL)
***************
*** 567,572
  		if (s_find(&srec, FULLSYSNAME) == FALSE)
  			xerror("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE);
  		for (ptr = nbuf; *ptr;) {
  			if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL)
  				localize(ptr);
  			while (*ptr++)

--- 605,611 -----
  		nextxref += strlen(nextxref);
  #endif
  		for (ptr = nbuf; *ptr;) {
+ #ifndef DOXREFS
  			if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL)
  				localize(ptr);
  #else DOXREFS
***************
*** 569,574
  		for (ptr = nbuf; *ptr;) {
  			if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL)
  				localize(ptr);
  			while (*ptr++)
  				;
  		}

--- 608,622 -----
  #ifndef DOXREFS
  			if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL)
  				localize(ptr);
+ #else DOXREFS
+ 			if (ngmatch(ptr, srec.s_nbuf) ||
+ 			    index(ptr,'.') == NULL) {
+ 				sprintf(nextxref,"%s:%ld ",ptr,localize(ptr));
+ 				numxrefs++;
+ 				while (*nextxref)
+ 				       nextxref++;
+ 			}
+ #endif DOXREFS
  			while (*ptr++)
  				;
  		}
***************
*** 577,582
  			localize("junk");
  		}
  	}
  
  	broadcast();
  	savehist(histline);

--- 625,636 -----
  			localize("junk");
  		}
  	}
+ #ifdef DOXREFS
+ 	if (numxrefs >= 2)
+ 	    *(nextxref-1) = '\0';       /* wipe out the last space */
+ 	else
+ 	    header.xref[0] = '\0';      /* wipe out the whole thing */
+ #endif
  
  #ifdef LINKART
  	tfp = xfopen(ARTICLE,"w");	/* open 1st article localized */
***************
*** 578,583
  		}
  	}
  
  	broadcast();
  	savehist(histline);
  	xxit(0);

--- 632,662 -----
  	    header.xref[0] = '\0';      /* wipe out the whole thing */
  #endif
  
+ #ifdef LINKART
+ 	tfp = xfopen(ARTICLE,"w");	/* open 1st article localized */
+ #endif
+ 
+ #if defined(LINKART) || defined(DOXREFS)
+ 	/* Now that xref is constructed, write article to temp file. */
+ 	/* (We ought to detect no room at this point and clean up.) */ 
+ 	if ( (c=getc(infp)) == ' ' || c == '\t' ) {
+ 		header.intnumlines++;
+ 		sprintf(header.numlines,"%d",header.intnumlines);
+ 	}
+ 	lhwrite(&header, tfp);
+ 	/* Kludge to get around article truncation problem */
+ 	if (c == ' ' || c == '\t' )
+ 		putc('\n', tfp);
+ 	putc(c,tfp);
+ 	while (fgets(bfr, BUFLEN, infp) != NULL)
+ 		fputs(bfr, tfp);
+ 
+ 	if (bfr[strlen(bfr)-1] != '\n')
+ 		putc('\n',tfp);
+ 	fclose(tfp);
+ 	fclose(infp);
+ #endif LINKART || DOXREFS
+ 
  	broadcast();
  	savehist(histline);
  	xxit(0);
***************
*** 853,855
  	}
  	return(NULL);
  }

--- 932,956 -----
  	}
  	return(NULL);
  }
+ 
+ #ifdef LINKART
+ mylink(tmpart,linkfrom)
+ char *tmpart, *linkfrom;
+ {
+     struct stat statbuf;
+ 
+     if (stat(linkfrom,&statbuf)==0)
+ 	return -1;
+     if (!*tmpart)                       /* first article? */
+ 	strcpy(tmpart,linkfrom);        /* just remember name */
+     else {
+ 	FILE *linkfp = fopen(linkfrom,"w");
+ 
+ 	if (!linkfp)
+ 	    return -1;
+ 	fprintf(linkfp,"%s\n",tmpart);  /* do "symbolic link" */
+ 	fclose(linkfp);
+     }
+     return 0;
+ }
+ #endif LINKART
!STUFFY!FUNK!
echo Extracting backpage.c
cat >backpage.c <<'!STUFFY!FUNK!'
/* $Header: backpage.c,v 4.1 84/09/24 11:42:53 lwall Exp $
 *
 * $Log:	backpage.c,v $
 * Revision 4.1  84/09/24  11:42:53  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/18  16:44:22  lwall
 * Added some sanity checks.
 * 
 * Revision 4.0.1.2  84/09/13  12:01:37  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.1  84/09/10  15:06:04  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:46  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "intrp.h"
#include "INTERN.h"
#include "backpage.h"

ART_LINE maxindx = -1;

void
backpage_init()
{
    char *varyname;
    
    varyname = filexp(VARYNAME);
    close(creat(varyname,0600));
    varyfd = open(varyname,2);
    UNLINK(varyname);
    
}

/* virtual array read */

ART_POS
vrdary(indx)
ART_LINE indx;
{
    int subindx;
    long offset;

#ifdef DEBUGGING
    if (indx > maxindx) {
	printf("vrdary(%ld) > %ld\n",(long)indx, (long)maxindx);
	return 0;
    }
#endif
    if (indx < 0)
	return 0;
    subindx = indx % VARYSIZE;
    offset = (indx - subindx) * sizeof(varybuf[0]);
    if (offset != oldoffset) {
	if (oldoffset >= 0) {
	    lseek(varyfd,oldoffset,0);
	    write(varyfd, /*NOSTRICT*/(char *)varybuf,sizeof(varybuf));
	}
	lseek(varyfd,offset,0);
	/*NOSTRICT*/
	read(varyfd,(char *)varybuf,sizeof(varybuf));
	oldoffset = offset;
    }
    return varybuf[subindx];
}

/* write to virtual array */

void
vwtary(indx,newvalue)
ART_LINE indx;
ART_POS newvalue;
{
    int subindx;
    long offset;

#ifdef DEBUGGING
    if (indx < 0)
	printf("vwtary(%ld)\n",(long)indx);
    if (!indx)
	maxindx = 0;
    if (indx > maxindx) {
	if (indx != maxindx + 1)
	    printf("indx skipped %d-%d\n",maxindx+1,indx-1);
	maxindx = indx;
    }
#endif
    subindx = indx % VARYSIZE;
    offset = (indx - subindx) * sizeof(varybuf[0]);
    if (offset != oldoffset) {
	if (oldoffset >= 0) {
	    lseek(varyfd,oldoffset,0);
	    /*NOSTRICT*/
	    write(varyfd,(char *)varybuf,sizeof(varybuf));
	}
	lseek(varyfd,offset,0);
	/*NOSTRICT*/
	read(varyfd,(char *)varybuf,sizeof(varybuf));
	oldoffset = offset;
    }
    varybuf[subindx] = newvalue;
}

!STUFFY!FUNK!
echo ""
echo "End of kit 5 (of 8)"
cat /dev/null >kit5isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 6 (of 8).  If kit 6 is complete, the line"
echo '"'"End of kit 6 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting help.c
cat >help.c <<'!STUFFY!FUNK!'
/* $Header: help.c,v 4.1 84/09/24 11:48:20 lwall Exp $
 *
 * $Log:	help.c,v $
 * Revision 4.1  84/09/24  11:48:20  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:11:41  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:27  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "term.h"
#include "INTERN.h"
#include "help.h"

void
help_init()
{
    ;
}

void
help_page()
{
#ifdef PAGERHELP
    doshell(sh,filexp(PAGERHELP));
#else
    putchar('\n');
    page_line = 1;
    print_lines("\
Paging commands:\n\
",STANDOUT);
    print_lines("\n\
SP	Display the next page.\n\
x	Display the next page decrypted (rot13).\n\
d	Display half a page more.\n\
CR	Display one more line.\n\
^R,v,^X	Restart the current article (v=verbose header, ^X=rot13).\n\
",NOMARKING);
    print_lines("\
^B	Back up one page.\n\
^L,X	Refresh the screen (X=rot13).\n\
g pat	Go to (search forward within article for) pattern.\n\
G	Search again for current pattern within article.\n\
^G	Search for next line beginning with \"Subject:\".\n\
q	Quit the pager, go to end of article.  Leave article read or unread.\n\
j	Junk this article (mark it read).  Goes to end of article.\n\
\n\
",NOMARKING);
    print_lines("\
The following commands skip the rest of the current article, then behave\n\
just as if typed to the 'What next?' prompt at the end of the article:\n\
",STANDOUT);
    print_lines("\n\
n	Scan forward for next unread article.\n\
N	Go to next article.\n\
^N	Scan forward for next unread article with same title.\n\
p,P,^P	Same as n,N,^N, only going backwards.\n\
-	Go to previously displayed article.\n\
\n\
",NOMARKING);
    print_lines("\
The following commands also take you to the end of the article.\n\
Type h at end of article for a description of these commands:\n\
",STANDOUT);
    print_lines("\
	# $ & / = ? c C f F k K ^K m M number r R ^R s S u v w W Y ^ |\n\
\n\
(To return to the middle of the article after one of these commands, type ^L.)\n\
",NOMARKING);
#endif
}

void
help_art()
{
#ifdef ARTHELP
    doshell(sh,filexp(ARTHELP));
#else
    putchar('\n');
    page_line = 1;
    print_lines("\
Article Selection commands:\n\
",STANDOUT);
    print_lines("\n\
n,SP	Scan forward for next unread article.\n\
N	Go to next article.\n\
^N	Scan forward for next unread article with same subject.\n\
p,P,^P	Same as n,N,^N, only going backwards.\n\
-	Go to previously displayed article.\n\
",NOMARKING);
    print_lines("\
number	Go to specified article.\n\
range{,range} command{:command}\n\
	Apply one or more commands to one or more ranges of articles.\n\
	Ranges are of the form: number | number-number.  You may use . for\n\
	the current article, and $ for the last article.\n\
 	Valid commands are: j, m, M, s, S, and !.\n\
",NOMARKING);
    print_lines("\
/pattern/modifiers\n\
	Scan forward for article containing pattern in the subject line.\n\
	(Use ?pat? to scan backwards; append h to scan headers, a to scan\n\
	entire articles, r to scan read articles, c to make case sensitive.\n\
/pattern/modifiers:command{:command}\n\
	Apply one or more commands to the set of articles matching pattern.\n\
	Use a K modifier to save entire command to the KILL file for this\n\
	newsgroup.  Commands m and M, if first, imply an r modifier.\n\
 	Valid commands are: j, m, M, s, S, and !.\n\
",NOMARKING);
    print_lines("\
f,F	Submit a followup article (F = include this article).\n\
r,R	Reply through net mail (R = include this article).\n\
s ...	Save to file or pipe via sh.\n\
S ...	Save via preferred shell.\n\
w,W	Like s and S but save without the header.\n\
| ...	Same as s|...\n\
C	Cancel this article, if yours.\n\
",NOMARKING);
    print_lines("\
^R,v	Restart article (v=verbose).\n\
^X	Restart article, rot13 mode.\n\
c	Catch up (mark all articles as read).\n\
^B	Back up one page.\n\
^L	Refresh the screen.  You can get back to the pager with this.\n\
X	Refresh screen in rot13 mode.\n\
",NOMARKING);
    print_lines("\
^	Go to first unread article.  Disables subject search mode.\n\
$	Go to end of newsgroup.  Disables subject search mode.\n\
",NOMARKING);
    print_lines("\#       Print last article number.\n\
&	Print current values of command-line switches.\n\
&switch {switch}\n\
	Set or unset more switches.\n\
j	Junk this article (mark it read).  Stays at end of article.\n\
m	Mark article as still unread.\n\
M	Mark article as still unread upon exiting newsgroup or Y command.\n\
",NOMARKING);
    print_lines("\
Y	Yank back articles marked temporarily read via M.\n\
k	Mark current SUBJECT as read.\n\
K	Mark current SUBJECT as read, and save command in KILL file.\n\
=	List subjects of unread articles.\n\
u	Unsubscribe to this newsgroup.\n\
^K	Edit local KILL file (the one for this newsgroup).\n\
q	Quit this newsgroup for now.\n\
",NOMARKING);
#endif
}

void
help_ng()
{
#ifdef NGHELP
    doshell(sh,filexp(NGHELP));
#else
    putchar('\n');
    page_line = 1;
    print_lines("\
Newsgroup Selection commands:\n\
",STANDOUT);
    if (ng != nextrcline)
	print_lines("\
\n\
y,SP	Do this newsgroup now.\n\
.cmd	Do this newsgroup, executing cmd as first command.\n\
=	Equivalent to .=<carriage return>.\n\
u	Unsubscribe from this newsgroup.\n\
c	Catch up (mark this newsgroup all read).\n\
",NOMARKING);
    print_lines("\
\n\
n	Go to the next newsgroup with unread news.\n\
N	Go to the next newsgroup.\n\
p	Go to the previous newsgroup with unread news.\n\
P	Go to the previous newsgroup.\n\
",NOMARKING);
    print_lines("\
-	Go to the previously displayed newsgroup.\n\
1	Go to the first newsgroup.\n\
^	Go to the first newsgroup with unread news.\n\
$	Go to the last newsgroup.\n\
",NOMARKING);
    print_lines("\
g name	Go to the named newsgroup.  Subscribe to new newsgroups this way too.\n\
/pat	Search forward for newsgroup matching pattern.\n\
?pat	Search backward for newsgroup matching pattern.\n\
	(Use * and ? style patterns.  Append r to include read newsgroups.)\n\
",NOMARKING);
    print_lines("\
l pat	List unsubscribed newsgroups containing pattern.\n\
m name	Move named newsgroup elsewhere (no name moves current newsgroup).\n\
o pat	Only display newsgroups matching pattern.  Omit pat to unrestrict.\n\
a pat	Like o, but also scans for unsubscribed newsgroups matching pattern.\n\
L	List current .newsrc.\n\
",NOMARKING);
    print_lines("\
&	Print current command-line switch settings.\n\
&switch {switch}\n\
	Set (or unset) more command-line switches.\n\
!cmd	Shell escape.\n\
",NOMARKING);
    print_lines("\
q	Quit rn.\n\
^K	Edit the global KILL file.  Use commands like /pattern/j to suppress\n\
	pattern in every newsgroup.\n\
v	Print version.\n\
",NOMARKING);
#endif
}

#ifdef ESCSUBS
void
help_subs()
{
#ifdef SUBSHELP
    doshell(sh,filexp(SUBSHELP));
#else
    putchar('\n');
    page_line = 1;
    print_lines("\
Valid substitutions are:\n\
",STANDOUT);
    print_lines("\
\n\
a	Current article number\n\
A	Full name of current article (%P/%c/%a)\n\
b	Destination of last save command, often a mailbox\n\
B	Bytes to ignore at beginning of last saved article\n\
",NOMARKING);
    print_lines("\
c	Current newsgroup, directory form\n\
C	Current newsgroup, dot form\n\
d	Full name of newsgroup directory (%P/%c)\n\
D	Distribution line from current article\
",NOMARKING);
    print_lines("\
f	Who the current article is from\n\
F	Newsgroups to followup to (from Newsgroups and Followup-To)\n\
h	(This help message)\n\
H	Host name (yours)\n\
i	Message-I.D. line from current article, with <>\n\
",NOMARKING);
    print_lines("\
l	News administrator's login name, if any\n\
L	Login name (yours)\n\
M	Number of article marked with M\n\
n	Newsgroups from current article\n\
N	Full name (yours)\n\
",NOMARKING);
    print_lines("\
o	Organization (yours)\n\
O	Original working directory (where you ran rn from)\n\
p	Your private news directory (from -d)\n\
P	Public news spool directory\n\
",NOMARKING);
    print_lines("\
r	Last reference (parent article id)\n\
R	References list for followup article\n\
s	Subject, with all Re's and (nf)'s stripped off\n\
S	Subject, with one Re stripped off\
",NOMARKING);
    print_lines("\
t	New To line derived from From and Reply-To (Internet format)\n\
T	New To line derived from Path\n\
u	Number of unread articles\n\
U	Number of unread articles not counting current article\n\
x	News library directory\n\
X	Rn library directory\n\
",NOMARKING);
    print_lines("\
~	Your home directory\n\
.	Directory containing . files\n\
$	Current process number\n\
/	Last search string\n\
ESC	Run preceding command through % interpretation\n\
",NOMARKING);
#endif
}
#endif

!STUFFY!FUNK!
echo Extracting artsrch.c
cat >artsrch.c <<'!STUFFY!FUNK!'
/* $Header: artsrch.c,v 4.1 84/09/24 11:42:18 lwall Exp $
 *
 * $Log:	artsrch.c,v $
 * Revision 4.1  84/09/24  11:42:18  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:05:28  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:35  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "search.h"
#include "term.h"
#include "util.h"
#include "intrp.h"
#include "bits.h"
#include "kfile.h"
#include "head.h"
#include "final.h"
#include "cheat.h"
#include "ng.h"
#include "artio.h"
#include "INTERN.h"
#include "artsrch.h"

void
artsrch_init()
{
#ifdef ARTSEARCH
#ifdef ZEROGLOB
    init_compex(&sub_compex);
    init_compex(&art_compex);
#endif
#endif
}

/* search for an article containing some pattern */

#ifdef ARTSEARCH
int
art_search(patbuf,get_cmd)
char *patbuf;				/* if patbuf != buf, get_cmd must */
int get_cmd;				/*   be set to FALSE!!! */
{
    char *pattern;			/* unparsed pattern */
    register char cmdchr = *patbuf;	/* what kind of search? */
    register char *s;
    bool backward = cmdchr == '?' || cmdchr == Ctl('p');
					/* direction of search */
    COMPEX *compex;			/* which compiled expression */
    char *cmdlst = Nullch;		/* list of commands to do */
    int normal_return = SRCH_NOTFOUND;	/* assume no commands */
    bool saltaway = FALSE;		/* store in KILL file? */
    char howmuch;			/* search just the subjects */
    bool doread;			/* search read articles? */
    bool foldcase = TRUE;		/* fold upper and lower case? */

    int_count = 0;
    if (cmdchr == '/' || cmdchr == '?') {	/* normal search? */
	if (get_cmd && buf == patbuf)
	    if (!finish_command(FALSE))	/* get rest of command */
		return SRCH_ABORT;
	compex = &art_compex;
	if (patbuf[1]) {
	    howmuch = 0;
	    doread = FALSE;
	}
	else {
	    howmuch = art_howmuch;
	    doread = art_doread;
	}
	s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */
	pattern = buf;
	if (*pattern) {
	    if (*lastpat)
		free(lastpat);
	    lastpat = savestr(pattern);
	}
	if (*s) {			/* modifiers or commands? */
	    for (s++; *s && index("Kharc",*s); s++) {
		if (*s == 'h')		/* scan header */
		    howmuch = 1;
		else if (*s == 'a')	/* scan article */
		    howmuch = 2;
		else if (*s == 'r')	/* scan read articles */
		    doread = TRUE;
		else if (*s == 'K')	/* put into KILL file */
		    saltaway = TRUE;
		else if (*s == 'c')	/* make search case sensitive */
		    foldcase = FALSE;
	    }
	}
	while (isspace(*s) || *s == ':')
	    s++;
	if (*s) {
	    if (*s == 'm' || *s == 'M')
		doread = TRUE;
	    if (*s == 'k')		/* grandfather clause */
		*s = 'j';
	    cmdlst = savestr(s);
	    normal_return = SRCH_DONE;
	}
	art_howmuch = howmuch;
	art_doread = doread;
	if (srchahead)
	    srchahead = -1;
    }
    else {
	register char *h;

	howmuch = 0;			/* just search subjects */
	doread = (cmdchr == Ctl('p'));
	if (cmdchr == Ctl('n'))
	    normal_return = SRCH_SUBJDONE;
	compex = &sub_compex;
	pattern = patbuf+1;
	strcpy(pattern,": *");
	h = pattern + strlen(pattern);
	interp(h,"%s");		/* fetch current subject */
	if (cmdchr == 'K') {
	    saltaway = TRUE;
	    cmdchr = 'k';
	}
	if (cmdchr == 'k') {
	    normal_return = SRCH_DONE;
	    cmdlst = savestr("j");
	    mark_as_read(art);		/* this article has this subject */
	    if (!*h) {
#ifdef VERBOSE
		IF(verbose)
		    fputs("\nCannot delete null subject.\n",stdout);
		ELSE
#endif
#ifdef TERSE
		    fputs("\nNull subject.\n",stdout);
#endif
		return SRCH_ABORT;
	    }
#ifdef VERBOSE
	    else if (verbose)
		printf("\nMarking subject \"%s\" as read.\n",h);
#endif
	}
	else if (!srchahead)
	    srchahead = -1;
	h[24] = '\0';		/* compensate for notesfiles */
	while (*h) {
	    if (index("\\[.^*$'\"",*h) != Nullch)
		*h++ = '.';
	    else
		h++;
	}
#ifdef DEBUGGING
	if (debug) {
	    printf("\npattern = %s\n",pattern);
	}
#endif
    }
    if ((s = compile(compex,pattern,TRUE,foldcase)) != Nullch) {
					/* compile regular expression */
	printf("\n%s\n",s);
	return SRCH_ABORT;
    }
#ifdef KILLFILES
    if (saltaway) {
	char saltbuf[LBUFLEN];

	s = saltbuf;
	sprintf(s,"/%s/",pattern);
	s += strlen(s);
	if (doread)
	    *s++ = 'r';
	if (howmuch==1)
	    *s++ = 'h';
	else if (howmuch==2)
	    *s++ = 'a';
	*s++ = ':';
	if (!cmdlst)
	    cmdlst = savestr("j");
	safecpy(s,cmdlst,LBUFLEN-(s-saltbuf));
	kf_append(saltbuf);
    }
#endif
    if (cmdlst && index(cmdlst,'='))
	normal_return = SRCH_ERROR;	/* listing subjects is an error? */
    if (get_cmd) {
	fputs("\nSearching...\n",stdout);
					/* give them something to read */
    }
    if (!backward && art > lastart)
	art = (doread ? absfirst : firstart) - 1;
    if (srchahead > 0) {
	if (!backward)
	    art = srchahead - 1;
	srchahead = -1;
    }
    assert(!cmdlst || *cmdlst);
    for (;;) {
	if (int_count) {
	    int_count = 0;
	    if (cmdlst)
		free(cmdlst);
	    return SRCH_INTR;
	}
	if (backward ?
		(--art < absfirst || (!doread && art < firstart)) :
		(++art > lastart)
	  ) {			/* out of articles? */
	    if (cmdlst)
		free(cmdlst);
	    return normal_return;
	}
	/*NOSTRICT*/
	if (doread || !was_read(art)) {
	    if (wanted(compex,art,howmuch)) {
				    /* does the shoe fit? */
		if (cmdlst) {
		    if (perform(cmdlst,TRUE)) {
			if (cmdlst)
			    free(cmdlst);
			return SRCH_INTR;
		    }
		}
		else {
		    if (cmdlst)
			free(cmdlst);
		    return SRCH_FOUND;
		}
	    }
	    else if (!cmdlst && ! (art%50)) {
		printf("...%ld",(long)art);
		fflush(stdout);
	    }
	}
    }
}

/* determine if article fits pattern */
/* returns TRUE if it exists and fits pattern, FALSE otherwise */

bool
wanted(compex, artnum, scope)
COMPEX *compex;
ART_NUM artnum;
char scope;
{
    if (!scope) {
	char subj_buf[266];
	
	strcpy(subj_buf, "Subject: ");
	strncpy(subj_buf+9,fetchsubj(artnum,FALSE,FALSE),256);
#ifdef DEBUGGING
	if (debug & DEB_SEARCH_AHEAD)
	    printf("%s\n",subj_buf);
#endif
	return execute(compex,subj_buf) != Nullch;
    }
#ifdef CACHESUBJ
    else
	fetchsubj(artnum,FALSE,FALSE);/* might as well get subject handy */
#endif
    
    if (artopen(artnum) == Nullfp)	/* ensure that article is open */
	return FALSE;			/* if not, return NO MATCH */
    scope--;
    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
					/* for each line of article */
	if (!scope && index(buf,':') == Nullch && *buf != ' ' && *buf != '\t')
					/* if headers only and out of header */
	    return FALSE;		/* say no go */
	if (execute(compex,buf) != Nullch) {
					/* does pattern matcher match? */
	    return TRUE;		/* say Eureka */
	}
    }
    return FALSE;			/* out of article, so no match */
}
#endif

!STUFFY!FUNK!
echo Extracting init.c
cat >init.c <<'!STUFFY!FUNK!'
/* $Header: init.c,v 4.1 84/09/24 11:56:37 lwall Exp $
 *
 * $Log:	init.c,v $
 * Revision 4.1  84/09/24  11:56:37  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/12  17:36:04  lwall
 * pwd.h now included by common.h.
 * 
 * Revision 4.0.1.1  84/09/10  15:12:37  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:42  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "final.h"
#include "term.h"
#include "last.h"
#include "rn.h"
#include "rcstuff.h"
#include "ngdata.h"
#include "util.h"
#include "only.h"
#include "intrp.h"
#include "addng.h"
#include "sw.h"
#include "art.h"
#include "artsrch.h"
#include "artio.h"
#include "backpage.h"
#include "bits.h"
#include "cheat.h"
#include "head.h"
#include "help.h"
#include "kfile.h"
#include "ngsrch.h"
#include "ngstuff.h"
#include "rcln.h"
#include "respond.h"
#include "ng.h"
#include "INTERN.h"
#include "init.h"

bool
initialize(argc,argv)
int argc;
char *argv[];
{
    char *tcbuf;
    register bool foundany = FALSE;
    long time();

    tcbuf = safemalloc(1024);		/* make temp buffer for termcap and */
					/* other initialization stuff */
    
    /* decode switches */

    sw_init(argc,argv,tcbuf);               /* must not do % interps! */
					/* (but may mung environment) */

    /* start up file expansion and the % interpreter */

    intrp_init(tcbuf);
    
    /* now make sure we have a current working directory */

    cwd_check();
    
    /* now that we know where to save things, cd to news directory */

    if (chdir(spool)) {
	printf(nocd,spool);
	finalize(1);
    }

    /* init signals, status flags and terminal stuff */

    final_init();
    term_init(tcbuf);
    
    /* get info on last rn run, if any */

    last_init(tcbuf);

    free(tcbuf);			/* recover 1024 bytes */

    /* make sure we are the right version */

    version_check();

    /* make sure we are the sole possessors of .newsrc */

    lock_check();

    /* check for news news */

    newsnews_check();

    /* open active file, etc. */

    ngdata_init();

    /* now read in the .newsrc file */

    foundany = rcstuff_init();

    /* it looks like we will actually read something, so init everything */

    addng_init();
    art_init();
    artio_init();
    artsrch_init();
    backpage_init();
    bits_init();
    cheat_init();
/*  final_init();	already done */
    head_init();
    help_init();
/*  intrp_init();      already done */
    kfile_init();
/*  last_init();	already done */
    ng_init();
/*  ngdata_init();	already done */
    ngsrch_init();
    ngstuff_init();
    only_init();
    rcln_init();
/*  rcstuff_init();	already done */
    respond_init();
    rn_init();
    search_init();
/*  sw_init();      already done */
/*  term_init();	already done */
    util_init();

#ifdef FINDNEWNG
    fstat(actfp->_file,&filestat);	/* did active file grow? */
    if (filestat.st_size != lastactsiz) {
	long actsiz = filestat.st_size;	/* remember new size */
	NG_NUM oldnext = nextrcline;	/* remember # lines in newsrc */
#ifdef FASTNEW
	bool munged = writesoft || !lastactsiz;
					/* bad soft ptrs -> edited active */
#else
	bool munged = TRUE;		/* just assume .newsrc munged */
#endif

#ifdef VERBOSE
	IF(verbose)
	    fputs("\nChecking active list for new newsgroups...\n",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("\nNew newsgroups:\n",stdout);
#endif
#ifdef FASTNEW
	if (!munged) {			/* maybe just do tail of file? */
	    fseek(actfp,lastactsiz-1,0);
	    fgets(buf,LBUFLEN,actfp);
	    munged = (*buf != '\n');
	    if (!munged)
		munged = newlist(munged,FALSE);
	}
#endif
	if (munged) {			/* must we scan entire file? */
	    fseek(actfp,0L,0);		/* rewind active file */
	    newlist(munged,FALSE);      /* sure hope they use hashing... */
	}
	lastactsiz = actsiz;		/* remember for .rnlast */
	if (nextrcline != oldnext) {	/* did we add any new groups? */
	    foundany = TRUE;		/* let main() know */
	    starthere = 0;              /* and start ng scan from the top */
	}
    }
#endif
    time(&lasttime);			/* remember when we inited-- */
					/* ends up back in .rnlast */
    writelast();                       /* in fact, put it there now */
    
#ifdef FINDNEWNG
# ifdef ONLY
    if (maxngtodo)			/* patterns on command line? */
	foundany |= scanactive();
# endif
#endif

    return foundany;
}

/* make sure there is no rn out there already */

void
lock_check()
{
    lockname = savestr(filexp(LOCKNAME));
    if (!checkflag) {
	tmpfp = fopen(lockname,"r");
	if (tmpfp != Nullfp) {
	    int processnum;

	    fgets(buf,LBUFLEN,tmpfp);
	    fclose(tmpfp);
	    processnum = atoi(buf);
#ifdef VERBOSE
	    IF(verbose)
		printf("You seem to have left an rn running, process %d.\n",
		    processnum);
	    ELSE
#endif
#ifdef TERSE
		printf("Rn left running, #%d.\n", processnum);
#endif
	    if (kill(processnum, SIGEMT)) {
				    /* does process not exist? */
				    /* (rn ignores SIGEMT) */
		sleep(2);
#ifdef VERBOSE
		IF(verbose)
		    fputs("\n\
That process does not seem to exist anymore.  The count of read articles\n\
may be incorrect in the last newsgroup accessed by that other (defunct)\n\
process.\n\n",stdout);
		ELSE
#endif
#ifdef TERSE
		    fputs("\nProcess crashed.\n",stdout);
#endif
		if (*lastngname) {
#ifdef VERBOSE
		    IF(verbose)
			printf("(The last newsgroup accessed was %s.)\n\n",
			lastngname);
		    ELSE
#endif
#ifdef TERSE
			printf("(In %s.)\n\n",lastngname);
#endif
		}
		get_anything();
		putchar('\n');
	    }
	    else {
#ifdef VERBOSE
		IF(verbose)
		    fputs("\n\
You may not have two copies of rn running simultaneously.  Goodbye.\n\
",stdout);
		ELSE
#endif
#ifdef TERSE
		    fputs("\nCan't start another.\n",stdout);
#endif
		finalize(0);
	    }
	}
	tmpfp = fopen(lockname,"w");
	fprintf(tmpfp,"%d\n",getpid());
	fclose(tmpfp);
    }
}

void
newsnews_check()
{
    char *newsnewsname = filexp(NEWSNEWSNAME);

    if (!checkflag && (tmpfp = fopen(newsnewsname,"r")) != Nullfp) {
	fstat(tmpfp->_file,&filestat);
	if (filestat.st_mtime > lasttime) {
	    while (fgets(buf,sizeof(buf),tmpfp) != Nullch)
		fputs(buf,stdout);
	    get_anything();
	    putchar('\n');
	}
	fclose(tmpfp);
    }
}

void
version_check()
{
    set_ngname("net.announce");
    if (access(ngdir,0)) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("Can't find net.announce.  Wrong news version?\n",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("Wrong version?\n",stdout);
#endif
	finalize(1);
    }
}
!STUFFY!FUNK!
echo Extracting head.c
cat >head.c <<'!STUFFY!FUNK!'
/* $Header: head.c,v 4.1 84/09/24 11:47:10 lwall Exp $
 *
 * $Log:	head.c,v $
 * Revision 4.1  84/09/24  11:47:10  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:11:08  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:17  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "artio.h"
#include "bits.h"
#include "util.h"
#include "INTERN.h"
#include "head.h"

bool first_one;		/* is this the 1st occurance of this header line? */

static char htypeix[26] =
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void
head_init()
{
    register int i;

    for (i=HEAD_FIRST+1; i<HEAD_LAST; i++)
	htypeix[*htype[i].ht_name - 'a'] = i;
}

int
set_line_type(bufptr,colon)
char *bufptr;
register char *colon;
{
    char lc[LONGKEY+3];
    register char *t, *f;
    register int i, len;

    for (t=lc,f=bufptr; f<colon; f++, t++) {
	if (isspace(*f))
	/* guard against space before : */
	    break;
	*t = isupper(*f) ? tolower(*f) : *f;
    }
    *t = '\0';
    f = lc;				/* get lc into register */
    len = t - f;

    /* now scan the headtype table, backwards so we don't have to supply an
     * extra terminating value, using first letter as index, and length as
     * optimization to avoid calling subroutine strEQ unnecessarily.  Hauls.
     */
    
    if (islower(*f)) {
	for (i = htypeix[*f - 'a']; *htype[i].ht_name == *f; --i) {
	    if (len == htype[i].ht_length && strEQ(f, htype[i].ht_name)) {
		return i;
	    }
	}
    }
    return SOME_LINE;
}

void
start_header(artnum)
ART_NUM artnum;
{
    register int i;

    for (i=0; i<HEAD_LAST; i++) {
	htype[i].ht_minpos = -1;
	htype[i].ht_maxpos = 0;
    }
    in_header = SOME_LINE;
    first_one = FALSE;
#ifdef ASYNC_PARSE
    parsed_art = artnum;
#endif
}

bool
parseline(art_buf,newhide,oldhide)
char *art_buf;
int newhide, oldhide;
{
    if (*art_buf == ' ' || *art_buf == '\t')
					/* header continuation line? */
	return oldhide;
    else {				/* maybe another header line */
	char *s;

	if (first_one) {		/* did we just pass 1st occurance? */
	    first_one = FALSE;
	    htype[in_header].ht_maxpos = artpos;
					/* remember where line left off */
	}
	s = index(art_buf,':');
	if (s == Nullch || s-art_buf > LONGKEY+2) {
			    /* is it the end of the header? */
	    htype[PAST_HEADER].ht_minpos =
		(*art_buf == '\n') ? ftell(artfp) : artpos;
			    /* remember where body starts */
	    in_header = PAST_HEADER;
	}
	else {	/* it is a new header line */
	    in_header = set_line_type(art_buf,s);
	    first_one = (htype[in_header].ht_minpos < 0);
	    if (htype[in_header].ht_minpos < 0)
		htype[in_header].ht_minpos = artpos;
	    if (htype[in_header].ht_flags & HT_HIDE)
		return newhide;
	}
    }
    return FALSE;			/* don't hide this line */
}

#ifdef ASYNC_PARSE
int
parse_maybe(artnum)
ART_NUM artnum;
{
    char tmpbuf[LBUFLEN];

    if (parsed_art == artnum)
	return 0;
    /* no maybe about it now */
    if (artopen(artnum) == Nullfp) {
	return -1;
    }
    start_header(artnum);
    while (in_header) {
	artpos = ftell(artfp);
	if (fgets(tmpbuf,LBUFLEN,artfp) == Nullch)
	    break;
	parseline(tmpbuf,FALSE,FALSE);
    }
    in_header = PAST_HEADER;
    return 0;
}
#endif

/* get the subject line for an article */

char *
fetchsubj(artnum,current_subject,copy)
ART_NUM artnum;				/* article to get subject from */
bool current_subject;			/* is it in a parsed header? */
bool copy;				/* do you want it savestr()ed? */
{
    char *s = Nullch, *t;

#ifdef CACHESUBJ
    if (!subj_list) {
	register ART_NUM i;
	
	/*NOSTRICT*/
	subj_list =
	    (char**)safemalloc((MEM_SIZE)((OFFSET(lastart)+2)*sizeof(char *)));
	for (i=0; i<=OFFSET(lastart); i++)
	    subj_list[i] = Nullch;
    }
    if (!artnum || artnum > lastart)
	s = nullstr;
    else
	s = subj_list[OFFSET(artnum)];
#endif
    if (s == Nullch) {
	if (current_subject) {
	    s = fetchlines(artnum,SUBJ_LINE);
#ifdef CACHESUBJ
	    subj_list[OFFSET(artnum)] = s;
#endif
	}
	else {
	    s = safemalloc((MEM_SIZE)256);
	    *s = '\0';
	    if (artopen(artnum) != Nullfp) {
		do {
		    if (fgets(s,256,artfp) == Nullch)
			strcpy(s, "Title: \n");
		} while (strnNE(s,"Title:",6) && strnNE(s,"Subject:",8));
		s[strlen(s)-1] = '\0';
		t = index(s,':')+1;
		while (*t == ' ') t++;
		strcpy(s, t);
	    }
	    s = saferealloc(s, (MEM_SIZE)strlen(s)+1);
#ifdef CACHESUBJ
	    subj_list[OFFSET(artnum)] = s;
#endif 
	}
    }
#ifdef CACHESUBJ
    if (copy) {
	t = savestr(s);
	return t;
    }
    else
	return s;
#else
    if (copy)
	return s;
    else {
	safecpy(cmd_buf,s,CBUFLEN);	/* hope this is okay--we're */
	return cmd_buf;			/* really scraping for space here */
    }
#endif
}

/* get header lines from an article */

char *
fetchlines(artnum,which_line)
ART_NUM artnum;				/* article to get line from */
int which_line;				/* type of line desired */
{
    char *newbuf, *t, tmp_buf[LBUFLEN];
    register ART_POS curpos;
    int size;
    register ART_POS firstpos;
    register ART_POS lastpos;
    
#ifdef ASYNC_PARSE
    if (parse_maybe(artnum))
	artnum = 0;
#endif
    firstpos = htype[which_line].ht_minpos;
    lastpos = htype[which_line].ht_maxpos;
    if (!artnum || firstpos < 0 || artopen(artnum) == Nullfp) {
	newbuf = safemalloc((unsigned int)1);
	*newbuf = '\0';
	return newbuf;
    }
    /*NOSTRICT*/
    size = lastpos - firstpos + 1;
#ifdef DEBUGGING
    if (debug && (size < 1 || size > 1000)) {
	printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos);
	gets(tmp_buf);
    }
#endif
    newbuf = safemalloc((unsigned int)size);
    *newbuf = '\0';
    fseek(artfp,firstpos,0);
    for (curpos = firstpos; curpos < lastpos; curpos = ftell(artfp)) {
	if (fgets(tmp_buf,LBUFLEN,artfp) == Nullch)
	    break;
	if (*tmp_buf == ' ' || *tmp_buf == '\t')
	    t = tmp_buf;
	else
	    t = index(tmp_buf,':')+1;
	if (t == Nullch)
	    break;
	else {
	    while (*t == ' ' || *t == '\t') t++;
	    safecat(newbuf,t,size);
	}
    }
    return newbuf;
}

!STUFFY!FUNK!
echo Extracting ngstuff.c
cat >ngstuff.c <<'!STUFFY!FUNK!'
/* $Header: ngstuff.c,v 4.1 84/09/24 12:03:20 lwall Exp $
 *
 * $Log:	ngstuff.c,v $
 * Revision 4.1  84/09/24  12:03:20  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/22  17:03:54  lwall
 * escapade() was not chdiring back.
 * 
 * Revision 4.0.1.2  84/09/19  17:12:10  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.1  84/09/10  15:21:21  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:51  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "term.h"
#include "util.h"
#include "ng.h"
#include "bits.h"
#include "intrp.h"
#include "cheat.h"
#include "head.h"
#include "final.h"
#include "sw.h"
#include "INTERN.h"
#include "ngstuff.h"

void
ngstuff_init()
{
    ;
}

/* do a shell escape */

int
escapade()
{
    register char *s;
    bool interactive = (buf[1] == FINISHCMD);
    bool docd;
    char whereiam[256];

    if (!finish_command(interactive))	/* get remainder of command */
	return -1;
    s = buf+1;
    docd = *s != '!';
    if (!docd) {
	s++;
    }
    else {
	getwd(whereiam);
	if (chdir(cwd)) {
	    printf(nocd,cwd);
	    sig_catcher(0);
	}
    }
    while (*s == ' ') s++;
					/* skip leading spaces */
    interp(cmd_buf,s);			/* interpret any % escapes */
    resetty();				/* make sure tty is friendly */
    doshell(Nullch,cmd_buf);	/* invoke the shell */
    noecho();				/* and make terminal */
    crmode();				/*   unfriendly again */
    if (docd) {
	if (chdir(whereiam)) {
	    printf(nocd,whereiam);
	    sig_catcher(0);
	}
    }
#ifdef MAILCALL;
    mailcount = 0;			/* force recheck */
#endif
    return 0;
}

/* process & command */

int
switcheroo()
{
    if (!finish_command(TRUE)) /* get rest of command */
	return -1;	/* if rubbed out, try something else */
    if (!buf[1])
	pr_switches();
    else {
	bool docd = (instr(buf,"-d") != Nullch);
 	char whereami[256];
 
	if (docd)
	    getwd(whereami);
	sw_list(buf+1);
	if (docd) {
	    cwd_check();
	    if (chdir(whereami)) {		/* -d does chdirs */
		printf(nocd,whereami);
		sig_catcher(0);
	    }
	}
    }
    return 0;
}

/* process range commands */

int
numnum()
{
    ART_NUM min, max;
    char *cmdlst = Nullch;
    register char *s, *c;
    ART_NUM oldart = art;
    char tmpbuf[LBUFLEN];
    bool justone = TRUE;		/* assume only one article */

    if (!finish_command(TRUE))	/* get rest of command */
	return NN_INP;
#ifdef ARTSRCH
    if (srchahead)
	srchahead = -1;
#endif
    for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++)
	if (!isdigit(*s))
	    justone = FALSE;
    if (*s) {
	cmdlst = savestr(s);
	justone = FALSE;
    }
    else if (!justone)
	cmdlst = savestr("m");
    *s++ = ',';
    *s = '\0';
    safecpy(tmpbuf,buf,LBUFLEN);
    for (s = tmpbuf; c = index(s,','); s = ++c) {
	*c = '\0';
	if (*s == '.')
	    min = oldart;
	else
	    min = atol(s);
	if (min<absfirst) {		/* make sure it is reasonable */
	    min = absfirst;
	    printf("(First article is %ld)\n",(long)absfirst);
	    pad(just_a_sec/3);
	}
	if ((s=index(s,'-')) != Nullch) {
	    s++;
	    if (*s == '$')
		max = lastart;
	    else if (*s == '.')
		max = oldart;
	    else
		max = atol(s);
	}
	else
	    max = min;
	if (max>lastart) {
	    max = lastart;
	    if (min > max)
		min = max;
	    printf("(Last article is %ld)\n",(long)lastart);
	    pad(just_a_sec/3);
	}
	if (max < min) {
	    fputs("\nBad range\n",stdout);
	    if (cmdlst)
		free(cmdlst);
	    return NN_ASK;
	}
	if (justone) {
	    art = min;
	    return NN_REREAD;
	}
	check_first(min);
	for (art=min; art<=max; art++) {
	    if (perform(cmdlst,TRUE)) {
#ifdef VERBOSE
		IF(verbose)
		    printf("\n(Interrupted at article %ld)\n",(long)art);
		ELSE
#endif
#ifdef TERSE
		    printf("\n(Intr at %ld)\n",(long)art);
#endif
		if (cmdlst)
		    free(cmdlst);
		return NN_ASK;
	    }
	}
    }
    art = oldart;
    if (cmdlst)
	free(cmdlst);
    return NN_NORM;
}

int
perform(cmdlst,toplevel)
register char *cmdlst;
int toplevel;
{
    register int ch;
    
    if (toplevel) {
	printf("%-6ld",art);
	fflush(stdout);
    }
    for (; ch = *cmdlst; cmdlst++) {
	if (isspace(ch) || ch == ':')
	    continue;
	if (ch == 'j') {
	    mark_as_read(art);
#ifdef VERBOSE
		IF(verbose)
		    fputs("\tJunked",stdout);
#endif
	}
	else if (ch == 'm') {
	    unmark_as_read(art);
#ifdef VERBOSE
		IF(verbose)
		    fputs("\tMarked unread",stdout);
#endif
	}
	else if (ch == 'M') {
#ifdef DELAYMARK
	    delay_unmark(art);
#ifdef VERBOSE
		IF(verbose)
		    fputs("\tWill return",stdout);
#endif
#else
	    notincl("M");
	    return -1;
#endif
	}
	else if (ch == '=') {
	    printf("\t%s",fetchsubj(art,FALSE,FALSE));
#ifdef VERBOSE
	    IF(verbose)
		;
	    ELSE
#endif
		putchar('\n');		/* ghad! */
	}
	else if (ch == 'C') {
#ifdef ASYNC_PARSE
	    printf("\t%sancelled",(cancel_article() ? "Not c" : "C"));
#else
	    notincl("C");
	    return -1;
#endif
	}
	else if (ch == '%') {
#ifdef ASYNC_PARSE
	    char tmpbuf[512];

	    cmdlst = dointerp(tmpbuf,cmdlst,":");
	    if (*cmdlst != ':')
		--cmdlst;
	    if (perform(tmpbuf,FALSE))
		return -1;
#else
	    notincl("%");
	    return -1;
#endif
	}
	else if (index("!&sSwW|",ch)) {
	    cmdlst = cpytill(buf,cmdlst,':') - 1;
	    /* we now have the command in buf */
	    if (ch == '!') {
		escapade();
#ifdef VERBOSE
		IF(verbose)
		    fputs("\tShell escaped",stdout);
#endif
	    }
	    else if (ch == '&') {
		switcheroo();
#ifdef VERBOSE
		IF(verbose)
		    fputs("\tSwitched",stdout);
#endif
	    }
	    else {
		putchar('\t');
		save_article();
	    }
	}
	else {
	    printf("\t???%s\n",cmdlst);
	    return -1;
	}
#ifdef VERBOSE
	fflush(stdout);
#endif
    }
    if (toplevel) {
#ifdef VERBOSE
	IF(verbose)
	    putchar('\n');
	ELSE
#endif
#ifdef TERSE
	    putchar(' ');
#endif
    }
    return 0;
}

!STUFFY!FUNK!
echo Extracting kfile.c
cat >kfile.c <<'!STUFFY!FUNK!'
/* $Header: kfile.c,v 4.1 84/09/24 11:58:18 lwall Exp $
 *
 * $Log:	kfile.c,v $
 * Revision 4.1  84/09/24  11:58:18  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.6  84/09/24  10:42:22  lwall
 * mention() did not terminate highlighting correctly.
 * 
 * Revision 4.0.1.5  84/09/14  08:54:53  lwall
 * kf_append did not append a newline.
 * 
 * Revision 4.0.1.4  84/09/13  12:04:57  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.3  84/09/10  15:15:58  lwall
 * Delinted.
 * 
 * Revision 4.0.1.2  84/09/06  17:02:30  lwall
 * Implemented newsgroup exit commands.
 * 
 * Revision 4.0.1.1  84/09/04  12:15:59  lwall
 * Allow switch setting (via &) in KILL files.
 * 
 * Revision 4.0  84/09/04  09:50:56  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "term.h"
#include "util.h"
#include "artsrch.h"
#include "ng.h"
#include "bits.h"
#include "intrp.h"
#include "ngstuff.h"
#include "INTERN.h"
#include "kfile.h"

static bool exitcmds = FALSE;

void
kfile_init()
{
    ;
}

#ifndef KILLFILES
int
edit_kfile()
{
    notincl("^K");
    return -1;
}

#else KILLFILES

char killglobal[] = KILLGLOBAL;
char killlocal[] = KILLLOCAL;

void
mention(str)
char *str;
{
#ifdef NOFIREWORKS
    no_sofire();
#endif
    standout();
    fputs(str,stdout);
    un_standout();
    putchar('\n');
    fflush(stdout);
}

int
do_kfile(kfp,entering)
FILE *kfp;
int entering;
{
    art = lastart+1;
    fseek(kfp,0L,0);			/* rewind file */
    while (fgets(buf,LBUFLEN,kfp) != Nullch) {
	buf[strlen(buf)-1] = '\0';
	if (strnEQ(buf,"THRU",4)) {
	    firstart = atol(buf+4)+1;
	    continue;
	}
	if (*buf == 'X') {		/* exit command? */
	    if (entering) {
		exitcmds = TRUE;
		continue;
	    }
	    strcpy(buf,buf+1);
	}
	else {
	    if (!entering)
		continue;
	}
	if (*buf == '&') {
	    mention(buf);
	    switcheroo();
	}
	else if (*buf == '/' && firstart <= lastart) {
	    mention(buf);
	    switch (art_search(buf,FALSE)) {
	    case SRCH_ABORT:
		continue;
	    case SRCH_INTR:
#ifdef VERBOSE
		IF(verbose)
		    printf("\n(Interrupted at article %ld)\n",(long)art);
		ELSE
#endif
#ifdef TERSE
		    printf("\n(Intr at %ld)\n",(long)art);
#endif
		return -1;
	    case SRCH_DONE:
		break;
	    case SRCH_SUBJDONE:
		fputs("\tsubject not found (???)\n",stdout);
		break;
	    case SRCH_NOTFOUND:
		fputs("\tnot found\n",stdout);
		break;
	    case SRCH_FOUND:
		fputs("\tfound\n",stdout);
	    }
	}
    }
    return 0;
}

void
kill_unwanted(starting,message,entering)
ART_NUM starting;
char *message;
int entering;
{
    bool intr = FALSE;			/* did we get an interrupt? */
    ART_NUM oldfirst;

    if (localkfp || globkfp) {
	if (!entering && !exitcmds)
	    return;
	exitcmds = FALSE;
	oldfirst = firstart;
	firstart = starting;
	clear();
	if (message)
	    fputs(message,stdout);
	if (localkfp)
	    intr = do_kfile(localkfp,entering);
	if (globkfp && !intr)
	    intr = do_kfile(globkfp,entering);
	if (entering && localkfp && !intr)
	    setthru(lastart);
	putchar('\n');
	if (entering)
	    get_anything();
	forcelast = FALSE;
	firstart = oldfirst;
    }
}

void
setthru(thru)
ART_NUM thru;
{
    FILE *newkfp;

    fseek(localkfp,0L,0);		/* rewind current file */
    strcpy(buf,filexp(getval("KILLLOCAL",killlocal)));
    UNLINK(buf);			/* to prevent file reuse */
    if (newkfp = fopen(buf,"w")) {
	fprintf(newkfp,"THRU %ld\n",(long)thru);
	while (fgets(buf,LBUFLEN,localkfp) != Nullch) {
	    if (strnEQ(buf,"THRU",4))
		continue;
	    fputs(buf,newkfp);
	}
	fclose(newkfp);
	open_kfile(KF_LOCAL);		/* and reopen local file */
    }
}

/* edit KILL file for newsgroup */

int
edit_kfile()
{
    int r = -1;

    if (in_ng)
	strcpy(buf,filexp(getval("KILLLOCAL",killlocal)));
    else
	strcpy(buf,filexp(getval("KILLGLOBAL",killglobal)));
    if ((r = makedir(buf,MD_FILE)) >= 0) {
	sprintf(cmd_buf,"%s %s",
	    filexp(getval("VISUAL",getval("EDITOR",defeditor))),buf);
	printf("\nEditing %s KILL file:\n%s\n",
	    (in_ng?"local":"global"),cmd_buf);
	resetty();			/* make sure tty is friendly */
	r = doshell(sh,cmd_buf);/* invoke the shell */
	noecho();			/* and make terminal */
	crmode();			/*   unfriendly again */
	open_kfile(in_ng);
    }
    else
	printf("Can't make %s\n",buf);
    return r;
}

void
open_kfile(local)
int local;
{
    char *kname = filexp(local ?
	getval("KILLLOCAL",killlocal) :
	getval("KILLGLOBAL",killglobal)
	);
    
    stat(kname,&filestat);
    if (!filestat.st_size)		/* nothing in the file? */
	UNLINK(kname);			/* delete the file */
    if (local) {
	if (localkfp)
	    fclose(localkfp);
	localkfp = fopen(kname,"r");
    }
    else {
	if (globkfp)
	    fclose(globkfp);
	globkfp = fopen(kname,"r");
    }
}

void
kf_append(cmd)
char *cmd;
{
    strcpy(cmd_buf,filexp(getval("KILLLOCAL",killlocal)));
    if (makedir(cmd_buf,MD_FILE) >= 0) {
#ifdef VERBOSE
	IF(verbose)
	    printf("\nDepositing command in %s...",cmd_buf);
	ELSE
#endif
#ifdef TERSE
	    printf("\n--> %s...",cmd_buf);
#endif
	fflush(stdout);
	sleep(2);
	if ((tmpfp = fopen(cmd_buf,"a")) != Nullfp) {
	    fseek(tmpfp,0L,2);		/* get to EOF for sure */
	    fprintf(tmpfp,"%s\n",cmd);
	    fclose(tmpfp);
	    fputs("done\n",stdout);
	}
	else
	    fputs("ERROR\n",stdout);
    }
}
#endif KILLFILES
!STUFFY!FUNK!
echo Extracting Rnmail.SH
cat >Rnmail.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting Rnmail (with variable substitutions)"
$spitshell >Rnmail <<!GROK!THIS!
#! /bin/sh
# $Header: Rnmail.SH,v 4.1 84/09/24 11:38:51 lwall Exp $
# 
# $Log:	Rnmail.SH,v $
# Revision 4.1  84/09/24  11:38:51  lwall
# Real baseline.
# 
# Revision 4.0.1.2  84/09/12  15:18:54  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.1  84/09/04  10:56:18  lwall
# Added possibility of using /usr/lib/news/recmail.
# 
# Revision 4.0  84/09/04  09:49:08  lwall
# Baseline for netwide release
# 
#
# syntax: Rnmail -h headerfile [oldart]		or
#         Rnmail destination-list 		or just
#         Rnmail

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

# System dependencies

mailer="${mailer-/bin/mail}"
# if you change this to something that does signatures, take out signature code

# your site name
sitename="$sitename"
# your organization name
orgname="$orgname"
# what pager you use--if you have kernal paging use cat
pager="$pager"
# how you derive full names, bsd, usg, or other
nametype="$nametype"
# default editor
defeditor="$defeditor"
# how not to do a newline with echo
n="$n"
c="$c"

test=${test-test}
sed=${sed-sed}
echo=${echo-echo}
cat=${cat-cat}
grep=${grep-grep}
rm=${rm-rm}

!GROK!THIS!
$spitshell >>Rnmail <<'!NO!SUBS!'
tmpart=/tmp/rnmail$$
dotdir=${DOTDIR-${HOME-$LOGDIR}}

headerfile=""
case $# in
0) ;;
*)  case $1 in
    -h)
	headerfile="$2"
	case $# in
	3) oldart=$3 ;;
	esac
	;;
    esac
    ;;
esac

case $headerfile in
'')
    case $# in
    0)
	to=h
	while $test "$to" = h ; do
	    $echo ""
	    $echo -n "To: "
	    read to
	    case $to in
	    h)
		$cat <<'EOH'

Type the net address of those people that you wish the message sent to.
Note that you will be asked later for additional addresses of people to
whom the message is not addressed but you wish to get copies.

Separate multiple addresses with spaces.

EOH
		;;
	    esac
	done
	;;
    *)
	to="$*"
	;;
    esac
    to=`$echo "$to" | $sed 's/  */ /g'`

    title=h
    while $test "$title" = h ; do
	$echo ""
	$echo -n "Title/Subject: "
	read title
	case $title in
	h)
	    $cat <<'EOH'

Type the title for your message.  
EOH
	    ;;
	esac
    done

# now build a file with a header for them to edit
    
    orgname=${ORGANIZATION-$orgname}
    case $orgname in
    /*) orgname=`$cat $orgname` ;;
    esac

    $cat > $tmpart <<EOHeader
To: $to
Subject: $title
Organization: $orgname
Cc:
Bcc:

EOHeader

    ;;
*)
    $cat < $headerfile  > $tmpart
    ;;
esac


file=h
while $test "$file" = h ; do
    $echo ""
    $echo -n "Input file name?  [RETURN for new file]: "
    read file
    case $file in
    h)
	$cat <<'EOH'

If you have already produced the body of your message, type the filename
for it here.  If you just want to proceed directly to the editor, type a
RETURN.  In any event, you will be allowed to edit as many times as you
want before you send off the message.
EOH
	;;
    '')
	$echo "" >> $tmpart
	state=edit
	;;
    *)
	$cat $file >>$tmpart
	state=ask
	;;
    esac
done

$echo ""

while true ; do
    case $state in
    edit)
	rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.letter ; $echo saved in ${HOME-$LOGDIR}/dead.letter ; $rm -f $tmpart; exit"
	trap "$rescue" 1
	trap "" 2
	tmp=h
	while $test "$tmp" = h ; do
	    $echo -n "Editor [${VISUAL-${EDITOR-$defeditor}}]: "
	    read tmp
	    case $tmp in
	    h)
		$cat <<'EOH'

Type a return to get the default editor, or type the name of the editor you
prefer.  The default editor depends on the VISUAL and EDITOR environment
variables.

EOH
		;;
	    '')
		;;
	    *)
		VISUAL=$tmp
		export VISUAL
		;;
	    esac
	done
	${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart
	trap "$rescue" 2
	state=ask
	;;
	
    ask)
	$echo ""
	$echo -n "Send, abort, edit, or list? "
	read ans
	
	case $ans in
	a*)
	    state=rescue
	    ;;
	e*)
	    state=edit
	    ;;
	l*)
	    $pager $tmpart
	    state=ask
	    ;;
	s*)
	    state=send
	    ;;
	h*)
	    $cat <<'EOH'

Type s to send the message, a to abort and append the message to dead.letter,
e to edit the message again, or l to list the message.
EOH
	esac
	;;
    
    send)
	if $test -f $dotdir/.signature; then
	    echo $n "Append .signature file? [y] $c"
	    read ans
	    case $ans in
	    ''|y*) cat $dotdir/.signature >> $tmpart
	    esac
	fi
	case $mailer in
	*sendmail)
	    $mailer -t <$tmpart
	    ;;
# but recmail does not know about Bcc, alas
	*recmail)
	    $mailer <$tmpart
	    ;;
	*)
	    set `$sed <$tmpart -n -e '/^To:/{' -e 's/To: *//p' -e q -e '}'`
	    set "$@" `$sed <$tmpart -n -e '/^Cc:/{' -e 's/Cc: *//p' -e q -e '}'`
	    set "$@" `$sed <$tmpart -n -e '/^Bcc:/{' -e 's/Bcc: *//p' -e q -e '}'`
	    $grep -v "^Bcc:"  <$tmpart | $mailer "$@"
	    ;;
	esac
	case $? in
	0)
	    state=cleanup
	    ;;
	*)
	    state=rescue
	    ;;
	esac
	;;
    rescue)
	$cat $tmpart >> ${HOME-$LOGDIR}/dead.letter
	$echo "Message saved to ${HOME-$LOGDIR}/dead.letter"
	state=cleanup
	;;
    cleanup)
	$rm -f $tmpart
	exit
	;;
    esac
done
!NO!SUBS!
$eunicefix Rnmail
chmod 755 Rnmail
!STUFFY!FUNK!
echo Extracting README
cat >README <<'!STUFFY!FUNK!'
			Rn Kit, Version 4.1

		    Copyright (c) 1984, Larry Wall

You may copy the rn kit as long as you aren't trying to make money off it.
--------------------------------------------------------------------------
(Note: Berkeleyphiles and -phobes:  The 4.1 above is unrelated to 4.1bsd.)

Please read all the directions below before you proceed any further, and
then follow them carefully.  Failure to do so may void your warranty. :-)

After you have unpacked your kit, you should have all the files listed
in MANIFEST.

Installation

1)  Run Configure.  This will figure out various things about your system.
    Some things Configure will figure out for itself, other things it will
    ask you about.  It will then procede to make config.h, config.sh, Makefile,
    and a bunch of shell scripts.  It will also do a make depend for you.
    You might possibly have to trim # comments from the front of Configure
    if your sh doesn't handle them, but all other # comments will be taken
    care of.

2)  Glance through config.h and common.h to make sure system dependencies
    are correct.  Most of them should have been taken care of by running
    the Configure script.

    If you have any additional changes to make to the C definitions, they
    can be done in the Makefile, in config.h, or in common.h.  If you have
    strange mailboxes on your system you should modify mbox.saver to correctly
    append an article to a mailbox or folder.

    If you are on a machine with limited address space, you may have to
    remove some of the special functions of rn to make it fit.  This is
    easily done by undefining symbols in the System Dependencies section
    of common.h.  You should run "make depend" again to be safe.

3)  make

    This will attempt to make rn in the current directory.

4)  make install

    This will put rn, newsetup, newsgroups, Pnews, and Rnmail into a public
    directory (normally /usr/local/bin), and put a number of files into the
    private rn library (normally /usr/lib/news/rn).  It will also try to put
    the man pages in a reasonable place.

5)  Read the manual entry before running rn.  It's quite different from
    readnews.

6)  Install the Xref patch to header.h, header.c, and inews.c.  These patches
    are found in header.h.pat, header.c.pat, and inews.c.?.pat, where ? is
    either 1 for news 2.10.1 (or earlier) or 2 for 2.10.2.  The purpose
    of the Xref patch is to put an Xref: line in the header of articles
    posted to more than one newsgroup.  Rn uses this line to keep from showing
    such postings more than once.  Other than that rn will work without
    this patch, so if you just want to try out rn you can delay putting in
    the patch.

    When you put in the patch and recompile inews, don't forget to define
    DOXREFS in the makefile for inews.  Do NOT define LINKART unless you
    are a Eunice site and really want to do that.  In fact, if you are not
    a Eunice site you needn't install the LINKART part of the patch.

    NOTE: the Makefile that comes with some of the older news systems does not
    have all the dependencies quite right.  In particular, ifuncs.c may
    not recompile when you change header.h.  If this happens when you install
    the Xref patch, inews will start dumping core.  Make sure both inews.o
    and ifuncs.o depend on header.h in the Makefile.

    Eunice users: the inews.c patch contains both the Xref patch and a LINKART
    patch to put a form of "symbolic link" between articles posted to
    multiple newsgroups.  What it does is to put the article into the first
    newsgroup on the Newsgroups line, and in subsequent newsgroups it just
    puts a little file containing the name of the article in the first
    newsgroup.  Rn (when compiled with the LINKART option) is clever about
    these pseudo-articles, and ends up opening the right one.  YOU WILL NOT
    be able to use readnews or vnews on your system without modification,
    though.  If you do this, be sure to define both DOXREFS and LINKART in
    the makefile for inews.c.  If you are using the option in inews that
    copies instead of linking, you will want to rip that out.

    NOTE: if you transmit articles to other systems using xfernews with the
    U flag, Xref's can leak out of your system, as can Date-Received's.  This
    may make neighboring sites unhappy unless they also have the Xref patch
    installed.  For now, either don't use the U flag, or fix the inews/xfernews
    interaction.

7)  Try rn, and play with some of the switches.  You may want to make -/
    default on your system.  This is done in common.h.  You may want to modify
    which header lines are displayed by default--this is done in head.h.

8)  IMPORTANT!  Help save the world!  Communicate any problems and
    suggested patches to me, lwall@sdcrdcf.UUCP (Larry Wall), so we can
    keep the world in sync.  If you have a problem, there's someone else
    out there who either has had or will have the same problem.

9)  If you are going to hack on rn, please read the HACKERSGUIDE first.
!STUFFY!FUNK!
echo Extracting term.h
cat >term.h <<'!STUFFY!FUNK!'
/* $Header: term.h,v 4.1 84/09/24 12:11:06 lwall Exp $
 *
 * $Log:	term.h,v $
 * Revision 4.1  84/09/24  12:11:06  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/12  17:21:34  lwall
 * Little TERMIO bug.
 * 
 * Revision 4.0.1.1  84/09/10  15:34:07  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:57  lwall
 * Baseline for netwide release
 * 
 */

#ifdef PENDING
#ifdef FIONREAD	/* must have FIONREAD or O_NDELAY for input_pending() */
#define read_tty(addr,size) read(0,addr,size)
#define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount)
EXT long iocount INIT(0);

#else

#include <fcntl.h>
EXT int devtty INIT(0);
EXT bool is_input INIT(FALSE);
EXT char pending_ch INIT(0);
#define input_pending() (is_input?TRUE:(is_input=read(devtty,&pending_ch,1)))
#endif
#else
#define read_tty(addr,size) read(0,addr,size)
#define input_pending() (FALSE)
#endif

/* stuff wanted by terminal mode diddling routines */

#ifdef TERMIO
EXT struct termio _tty, _oldtty;
#else
EXT struct sgttyb _tty;
EXT int _res_flg INIT(0);
#endif

EXT int _tty_ch INIT(2);
EXT bool bizarre INIT(FALSE);			/* do we need to restore terminal? */

/* terminal mode diddling routines */

#ifdef TERMIO

#define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty))
#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty))
#define echo()	 ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty))
#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty))
#define nl()	 ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
#define nonl()	 ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
#define	savetty() ((bizarre=1),ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty))
#define	resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty))

#else

#define raw()	 ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty))
#define noraw()	 ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty))
#define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty))
#define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty))
#define echo()	 ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty))
#define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty))
#define nl()	 ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty))
#define nonl()	 ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty))
#define	savetty() ((bizarre=1),gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags)
#define	resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty))
#endif

#ifdef TIOCSTI
#define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */
#else
#define forceme(c)
#endif

/* termcap stuff */

/*
 * NOTE: if you don't have termlib you'll either have to define these strings
 *    and the tputs routine, or you'll have to redefine the macros below
 */

#ifdef HAVETERMLIB
EXT char *BC INIT(Nullch);		/* backspace character */
EXT char *UP INIT(Nullch);		/* move cursor up one line */
EXT char *CL INIT(Nullch);		/* home and clear screen */
EXT char *CE INIT(Nullch);		/* clear to end of line */
EXT char *SO INIT(Nullch);		/* begin standout mode */
EXT char *SE INIT(Nullch);		/* end standout mode */
EXT int SG INIT(0);		/* blanks left by SO and SE */
EXT char *US INIT(Nullch);		/* start underline mode */
EXT char *UE INIT(Nullch);		/* end underline mode */
EXT char *UC INIT(Nullch);		/* underline a character, if that's how it's done */
EXT int UG INIT(0);		/* blanks left by US and UE */
EXT bool AM INIT(FALSE);		/* does terminal have automatic margins? */
EXT bool XN INIT(FALSE);		/* does it eat 1st newline after automatic wrap? */
EXT char PC INIT(0);		/* pad character for use by tputs() */
EXT short ospeed INIT(0);	/* terminal output speed, for use by tputs() */
EXT int LINES INIT(0), COLS INIT(0);	/* size of screen */
EXT int just_a_sec INIT(960);			/* 1 sec at current baud rate */
					/* (number of nulls) */

/* define a few handy macros */

#define backspace() tputs(BC,0,putchr)
#define clear() tputs(CL,LINES,putchr)
#define erase_eol() tputs(CE,1,putchr)
#define underline() tputs(US,1,putchr)
#define un_underline() tputs(UE,1,putchr)
#define underchar() tputs(UC,0,putchr)
#define standout() tputs(SO,1,putchr)
#define un_standout() tputs(SE,1,putchr)
#define up_line() tputs(UP,1,putchr)
#else
  ????????		/* up to you */
#endif

EXT int page_line INIT(1);	/* line number for paging in print_line (origin 1) */

void	term_init();
char	putchr();	/* routine for tputs to call */
bool	finish_command();
void	eat_typeahead();
#ifndef read_tty
    int		read_tty();
#endif
void	underprint();
#ifdef NOFIREWORKS
    void	no_sofire();
    void	no_ulfire();
#endif
void	getcmd();
int	get_anything();
void	in_char();
int	print_lines();
void	pad();
void	printcmd();
void	rubout();
void	reprint();
!STUFFY!FUNK!
echo Extracting HACKERSGUIDE
cat >HACKERSGUIDE <<'!STUFFY!FUNK!'
Hacking Notes

If you aren't interested in mucking with the innards of rn, don't read this.

In the interests of both space and time optimization, things are done inside
rn that don't always conform to the highest ideals of programming.  To the
extent I felt it was practical, I've tried to conform to good programming
practice, but you must realize that my goal was to make a better mousetrap,
so certain conscious tradeoffs were made in the design of rn right from the
start.  In particular, if you want to hack on rn (and I wouldn't blame you,
it's fun), beware of the following:
  
  * buf and cmd_buf are reused all over the place.  11-squishing is a good
    term for it.  No, I'm on a Vax now, but I've been there.

  * The article header is parsed on the fly, while it is being displayed.
    In fact, practically everything is done on the fly within the article
    display loop, and there are plenty of state variables.  The header
    is never explicitly stored in memory; rather, pointers are kept into
    the file.  The information required to backup pages is not stored in
    memory, except for 1 buffer's worth.  The information required to do
    the delayed mark as unread (M) is not stored in memory either.

  * Lots of contortions are gone through to avoid using static memory, or
    allocating unnecessary memory, or losing track of allocated memory,
    while at the same time allowing .newsrc lines and header lines to be
    ANY length up to the amount of memory you have.  Rn spends a great deal
    of effort being lazy.  Do not use a static buffer when you can use
    growstr().

  * Lots of contortions are gone through to try to do things when people
    aren't waiting, or have only been waiting a very short time.  Guessing
    the next article to be opened and opening it, searching ahead for the
    next article with the same subject, delaying the look up of the number
    of articles in a newsgroup, writing the rest of the page while the
    reader is examining the header, cacheing up subjects while the user
    is reading, checkpointing the .newsrc only while the reader is in the
    middle of an interesting article, are some of the strategies employed.
  
  * There are plenty of goto's.  Most of them involve going back to reprompt,
    to reask for input, or to just plain do the unstructured things people
    want to do when they are glaring at a terminal.  If they bother you
    too much, just think of rn as a big state machine.  If they don't bother
    you at all, I don't want you hacking on rn.

  * Put all includes at the front of the file, before the first function,
    or makedepend will not work right.  I could relax this, but makedepend
    would take about 5 times longer to run.

In general then, feel free to hack on rn.  Just don't broadcast untested
patches to the net.  Remember that there are people with limited address
spaces and limited cpu cycles.  If you add a wonderful new feature and
want to publish a patch, put #ifdef's around it so that people who don't
want it or can't afford it can work around it.  THIS MEANS YOU.  We don't
need 57 varieties of mutually incompatible and incomprehensible rn floating
about the net.  Consider telling me about your patch so that I can consider
including it in the standard version.  A COMPLETE PATCH TAKES INTO ACCOUNT
SYSTEM DEPENDENCIES AS DETERMINED BY THE CONFIGURE SCRIPT.

* Don't use ints where rn uses typedefs, in particular, for article numbers.
* Don't use %d anywhere that someone might need a %ld.  (Just because YOU
    typedefed it as an int doesn't mean someone else won't need a long.)
* Don't use %D, that's archaic.
* Declare the type of every function.  Use void, even if your C compiler
    doesn't.
* Follow the style that rn already uses!  This is my pet peeve.  Well, one of
    them, anyway.  I follow other people's strange styles when modifying
    their programs, so I'd be much obliged if you did likewise.
* Use lint.
* Use RCS.  Start a new branch, like 4.1.[2-9].  (I will use 4.1.1 myself.)
* Be structured wherever it doesn't interfere with practicality.
* Long live paranoid programming.  The rest of the program is out to get you.
    The world is out to destroy the program, not to mention the .newsrc.
    And then there's always bitrot...
* Stop reading this lugubrious trash and start thinking for yourself.
* Thank you and good night.
!STUFFY!FUNK!
echo Extracting Makefile.SH
cat >Makefile.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting Makefile (with variable substitutions)"
cat >Makefile <<!GROK!THIS!
# $Header: Makefile.SH,v 4.1 84/09/24 11:37:35 lwall Exp $
#
# $Log:	Makefile.SH,v $
# Revision 4.1  84/09/24  11:37:35  lwall
# Real baseline.
# 
# Revision 4.0.1.4  84/09/12  16:57:01  lwall
# Added ndir.
# 
# Revision 4.0.1.3  84/09/12  15:16:38  lwall
# Check for sh on install.
# 
# Revision 4.0.1.2  84/09/06  14:01:35  lwall
# Made not install newsnews over existent one.
# 
# Revision 4.0.1.1  84/09/06  13:15:19  lwall
# General cleanup.
# 
# Revision 4.0  84/09/04  09:48:44  lwall
# Baseline for netwide release
# 

rnbin = $rnbin
rnlib = $rnlib
mansrc = $mansrc
CFLAGS = $iandd -O
LDFLAGS = $iandd

libs = $ndirlib $termlib $jobslib
!GROK!THIS!
cat >>Makefile <<'!NO!SUBS!'

public = rn newsetup newsgroups Pnews Rnmail
private = norm.saver mbox.saver ng.help art.help pager.help subs.help makedir filexp

h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h
h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h
h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
h4 = respond.h rn.h search.h sw.h term.h util.h

h = $(h1) $(h2) $(h3) $(h4)

c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c
c2 = final.c head.c help.c init.c intrp.c kfile.c last.c ndir.c ng.c
c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c
c4 = respond.c rn.c search.c sw.c term.c util.c

c = $(c1) $(c2) $(c3) $(c4)

obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o ndir.o ng.o
obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
obj4 = respond.o rn.o search.o sw.o term.o util.o

obj = $(obj1) $(obj2) $(obj3) $(obj4)

lintflags = -phbvxac


.c.o:
	$(CC) -c $(CFLAGS) $*.c

rn: $(obj)
	$(CC) $(LDFLAGS) $(obj) $(libs) -o rn

# if a .h file depends on another .h file...
$(h):
	touch $@

install: rn
# won't work with csh
	export PATH || exit 1
	- mv $(rnbin)/rn $(rnbin)/rn.old
	- if test `pwd` != $(rnbin); then cp $(public) $(rnbin); fi
	cd $(rnbin); chmod 755 $(public)
	chmod 755 makedir
	- makedir `filexp $(rnlib)`
	- if test `pwd` != `filexp $(rnlib)`; then cp $(private) `filexp $(rnlib)`; fi
	cd `filexp $(rnlib)`; chmod 755 $(private)
	- if test ! -f `filexp $(rnlib)/newsnews`; then cp newsnews `filexp $(rnlib)`; fi
	- if test `pwd` != $(mansrc); then cp $(manpages) $(mansrc); fi

# The following lint has practically everything turned on.  Unfortunately,
# you have to wade through a lot of mumbo jumbo that can't be suppressed.
# If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message
# for that spot.

lint:
	lint $(lintflags) $(defs) $(c) > rn.fuzz

depend:
	makedepend

# AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE
$(obj):
	@ echo "You haven't done a "'"make depend" yet!'; exit 1
!NO!SUBS!
$eunicefix Makefile
!STUFFY!FUNK!
echo ""
echo "End of kit 6 (of 8)"
cat /dev/null >kit6isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 7 (of 8).  If kit 7 is complete, the line"
echo '"'"End of kit 7 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting final.c
cat >final.c <<'!STUFFY!FUNK!'
/* $Header: final.c,v 4.1 84/09/24 11:46:35 lwall Exp $
 *
 * $Log:	final.c,v $
 * Revision 4.1  84/09/24  11:46:35  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/19  11:23:00  lwall
 * SIGTTOU not ignored til sig_catcher().
 * 
 * Revision 4.0.1.2  84/09/13  12:04:45  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.1  84/09/10  15:10:50  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:14  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "term.h"
#include "ng.h"
#include "init.h"
#include "bits.h"
#include "last.h"
#include "rcstuff.h"
#include "INTERN.h"
#include "final.h"

void
final_init()
{
#ifdef SIGTSTP
    sigset(SIGTSTP, stop_catcher);	/* job control signals */
    sigset(SIGCONT, cont_catcher);	/* job control signals */
#endif

    sigset(SIGINT, int_catcher);	/* always catch interrupts */
    sigset(SIGHUP, sig_catcher);	/* and hangups */
    sigignore(SIGEMT);

    sigset(SIGILL, sig_catcher);
    sigset(SIGTRAP, sig_catcher);
    sigset(SIGFPE, sig_catcher);
    sigset(SIGBUS, sig_catcher);
    sigset(SIGSEGV, sig_catcher);
    sigset(SIGSYS, sig_catcher);
    sigset(SIGTERM, sig_catcher);
#ifdef SIGXCPU
    sigset(SIGXCPU, sig_catcher);
#endif
#ifdef SIGXFSZ
    sigset(SIGXFSZ, sig_catcher);
#endif
}

void					/* very much void */
finalize(status)
int status;
{
    if (bizarre)
	resetty();
    UNLINK(lockname);
    if (status < 0) {
	chdir("/usr/tmp");
	sigset(SIGILL,SIG_DFL);
	abort();
    }
    exit(status);
}

/* come here on interrupt */

void
int_catcher()
{
    sigset(SIGINT,int_catcher);
#ifdef DEBUGGING
    if (debug)
	write(2,"int_catcher\n",12);
#endif
    if (!waiting) {
	if (int_count) {		/* was there already an interrupt? */
	    write(2,"\nBye-bye.\n",10);
	    sig_catcher(0);		/* emulate the other signals */
	}
	int_count++;
    }
}

/* come here on signal other than interrupt, stop, or cont */

void
sig_catcher(signo)
{
#ifdef VERBOSE
    static char *signame[] = {
	"???",
	"HUP",
	"INT",
	"QUIT",
	"ILL",
	"TRAP",
	"IOT",
	"EMT",
	"FPE",
	"KILL",
	"BUS",
	"SEGV",
	"SYS",
	"PIPE",
	"ALRM",
	"TERM",
	"???"
#ifdef SIGTSTP
	,"STOP",
	"TSTP",
	"CONT",
	"CHLD",
	"TTIN",
	"TTOU",
	"TINT",
	"XCPU",
	"XFSZ"
#ifdef SIGPROF
	,"VTALARM",
	"PROF"
#endif
#endif
	};
#endif

#ifdef SIGTTOU
    sigignore(SIGTTOU);
#endif
#ifdef DEBUGGING
    if (debug) {
	printf("\nSIG%s--.newsrc not restored in debug\n",signame[signo]);
	finalize(-1);
    }
#endif
    panic = TRUE;			/* disable terminal I/O */
    if (doing_ng) {			/* need we reconstitute rc line? */
	yankback();
	restore_ng();			/* then do so (hope this works) */
    }
    doing_ng = FALSE;
    if (rc_changed)			/* need we write .newsrc out? */
	write_rc();			/* then do so */
    rc_changed = FALSE;
    if (signo && signo != SIGHUP)
#ifdef VERBOSE
	IF(verbose)
	    printf("\nCaught a SIG%s--.newsrc restored\n",signame[signo]);
	ELSE
#endif
#ifdef TERSE
	    printf("\nSignal %d--bye bye\n",signo);
#endif
    switch (signo) {
    case SIGBUS:
    case SIGILL:
    case SIGSEGV:
	finalize(-signo);
    }
    finalize(1);				/* and blow up */
}

#ifdef SIGTSTP
/* come here on stop signal */

void
stop_catcher()
{
    if (!waiting) {
	checkpoint_rc();		/* good chance of crash while stopped */
	resetty();			/* this is the point of all this */
#ifdef DEBUGGING
	if (debug)
	    write(2,"stop_catcher\n",13);
#endif
	sigset(SIGTSTP,SIG_DFL);	/* enable stop */
#ifdef BSD42
	sigsetmask(sigblock(0) & ~(1 << (SIGTSTP-1)));
#endif
	kill(0,SIGTSTP);		/* and do the stop */
    }
    sigset(SIGTSTP,stop_catcher);	/* unenable the stop */
}

/* come here on cont signal */

void
cont_catcher()
{
    sigset(SIGCONT,cont_catcher);
#ifdef MAILCALL;
    mailcount = 0;			/* force recheck */
#endif
    if (!panic) {
	if (!waiting) {
#ifdef DEBUGGING
	    if (debug)
		write(2,"cont_catcher\n",13);
#endif
	    noecho();			/* set no echo */
	    crmode();			/* set cbreak mode */
	    forceme("\f");		/* cause a refresh */
					/* (defined only if TIOCSTI defined) */
	}
    }
}
#endif

!STUFFY!FUNK!
echo Extracting ngdata.c
cat >ngdata.c <<'!STUFFY!FUNK!'
/* $Header: ngdata.c,v 4.1 84/09/24 12:02:49 lwall Exp $
 *
 * $Log:	ngdata.c,v $
 * Revision 4.1  84/09/24  12:02:49  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/12  17:16:13  lwall
 * Ndir stuff.
 * 
 * Revision 4.0.1.2  84/09/10  15:20:07  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/06  08:10:18  lwall
 * Getngmin now uses correct Null for directory stuff.
 * 
 * Revision 4.0  84/09/04  09:51:45  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "ndir.h"
#include "rcstuff.h"
#include "rn.h"
#include "intrp.h"
#include "final.h"
#include "rcln.h"
#include "INTERN.h"
#include "ngdata.h"

void
ngdata_init()
{
/* The following is only for systems that do not zero globals properly */
#ifdef ZEROGLOB
# ifdef CACHEFIRST
    for (i=0; i<MAXRCLINE; i++)
	abs1st[i] = 0;
# endif
#endif	/* ZEROGLOB */

    /* open the active file */

    actfp = fopen(filexp(ACTIVE),"r");
    if (actfp == Nullfp) {
	fputs("Can't find active file.\n",stdout);
	finalize(1);
    }
}

/* find the maximum article number of a newsgroup */

ART_NUM
getngsize(num)
register NG_NUM num;
{
    register int len;
    register char *nam;
    char tmpbuf[80];

    nam = rcline[num];
    len = rcnums[num] - 1;
    softtries++;
    fseek(actfp,1024L,1);	/* hopefully this forces a reread */
#ifdef DEBUGGING
    if (debug & DEB_SOFT_POINTERS)
	printf("Softptr = %ld\n",(long)softptr[num]);
#endif
    if (fseek(actfp,(long)softptr[num],0) < 0 ||
      fgets(tmpbuf,80,actfp) == Nullch ||
      tmpbuf[len] != ' ' ||
      strnNE(tmpbuf,nam,len)) {
#ifdef DEBUGGING
	if (debug & DEB_SOFT_POINTERS)
	    printf("Missed, looking for %s in %sLen = %d\n",nam,tmpbuf,len);
#endif
	softmisses++;
	fseek(actfp,0L,0);
	softptr[num] = 0;
	while (fgets(tmpbuf,80,actfp) != Nullch) {
	    if (tmpbuf[len] != ' ' || strnNE(tmpbuf,nam,len)) {
		/*NOSTRICT*/
		softptr[num] = (ACT_POS) ftell(actfp);
		continue;
	    }
	    writesoft = TRUE;		/* force update on .rnsoft */
	    goto foundit;
	}
	if (rcchar[num] == ':')		/* unsubscribe quietly */
	    rcchar[num] = NEGCHAR;
	return TR_BOGUS;		/* well, not so quietly, actually */
    }
foundit:
#ifdef DEBUGGING
    if (debug & DEB_SOFT_POINTERS) {
	printf("Should be %ld\n",(long)softptr[num]);
    }
#endif
#ifdef MININACT
    {
	char *s;
	int tmp;

	for (s=tmpbuf+len+1; isdigit(*s); s++) ;
	if (tmp = atol(s))
#ifdef CACHEFIRST
	    abs1st[num] = tmp;
#else
	    abs1st = tmp;
#endif
    }
#endif
    return atol(tmpbuf+len+1);
}

/* determine the absolutely first existing article number */

ART_NUM
getabsfirst(ngnum,ngsize)
register NG_NUM ngnum;
ART_NUM ngsize;
{
    register ART_NUM a1st;
    char dirname[MAXFILENAME];

#ifdef CACHEFIRST
    if (a1st = abs1st[ngnum])
	return a1st;
#endif
#ifdef MININACT
    getngsize(ngnum);
# ifdef CACHEFIRST
    return abs1st[ngnum];
# else
    return abs1st;
# endif
#else
    sprintf(dirname,"%s/%s",spool,getngdir(rcline[ngnum]));
    a1st = getngmin(dirname,0L);
    if (!a1st)				/* nothing there at all? */
	a1st = ngsize+1;		/* aim them at end of newsgroup */
# ifdef CACHEFIRST
    abs1st[ngnum] = a1st;
# endif
    return a1st;
#endif MININACT
}

/* scan a directory for minimum article number greater than floor */

ART_NUM
getngmin(dirname,floor)
char *dirname;
ART_NUM floor;
{
    register DIR *dirp;
    register struct direct *dp;
    register ART_NUM min = 1000000;
    register ART_NUM maybe;
    register char *p;
    char tmpbuf[128];
    
    dirp = opendir(dirname);
    if (!dirp)
	return 0;
    while ((dp = readdir(dirp)) != Null(struct direct *)) {
	if ((maybe = atoi(dp->d_name)) < min && maybe > floor) {
	    for (p = dp->d_name; *p; p++)
		if (!isdigit(*p))
		    goto nope;
	    if (*dirname == '.' && !dirname[1])
		stat(dp->d_name, &filestat);
	    else {
		sprintf(tmpbuf,"%s/%s",dirname,dp->d_name);
		stat(tmpbuf, &filestat);
	    }
	    if (! (filestat.st_mode & S_IFDIR))
		min = maybe;
	}
      nope:
	;
    }
    closedir(dirp);
    return min==1000000 ? 0 : min;
}

!STUFFY!FUNK!
echo Extracting newsetup.SH
cat >newsetup.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting newsetup (with variable substitutions)"
$spitshell >newsetup <<!GROK!THIS!
$startsh
# $Header: newsetup.SH,v 4.1 84/09/24 12:01:10 lwall Exp $
# 
# $Log:	newsetup.SH,v $
# Revision 4.1  84/09/24  12:01:10  lwall
# Real baseline.
# 
# Revision 4.0.1.2  84/09/12  15:21:47  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.1  84/09/05  10:36:13  lwall
# Now removes n.tmp file.
# 
# Revision 4.0  84/09/04  09:51:30  lwall
# Baseline for netwide release
# 

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

: syntax: newsetup

: System dependencies
: You will want to change the definitions below to reflect the distribution
: areas around you.  If you have more areas than this you will need to modify
: the sed below.

locorg="$locpref"
organization="$orgpref"
city="$citypref"
state="$statepref"
cntry="$cntrypref"
cont="$contpref"
active="${active-/usr/lib/news/active}"

dotdir="\${DOTDIR-\${HOME-\$LOGDIR}}"
$echo "Creating .newsrc in \$dotdir to used by news programs."

case \$active in
~*) active=\`$filexp \$active\` ;;
esac

: NOTE: SED WILL NOT TAKE MORE THAN 10 WFILES, SO BEWARE

$sort <\$active | $sed >/tmp/n.tmp\$\$	\\
	-e 's/^\([^ ]*\) .*\$/\1:/'	\\
	-e '/^control:/{'		\\
	-e "  w /tmp/n.test\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e '/^junk:/{'			\\
	-e "  w /tmp/n.test\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e '/test:/{'			\\
	-e "  w /tmp/n.test\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^net\./{"			\\
	-e "  w /tmp/n.net\$\$"		\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^mod\./{"			\\
	-e "  w /tmp/n.mod\$\$"		\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^\$locorg\./{"		\\
	-e "  w /tmp/n.\$locorg\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^\$organization\./{"	\\
	-e "  w /tmp/n.\$organization\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^\$city\./{"		\\
	-e "  w /tmp/n.\$city\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^\$state\./{" 		\\
	-e "  w /tmp/n.\$state\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^fa\./{"			\\
	-e "  w /tmp/n.fa\$\$"		\\
	-e '  d'			\\
	-e '}'

$sed </tmp/n.tmp\$\$ >/tmp/n.local\$\$	\\
	-e "/^\$cntry\./{" 		\\
	-e "  w /tmp/n.\$cntry\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^\$cont\./{" 		\\
	-e "  w /tmp/n.\$cont\$\$"	\\
	-e '  d'			\\
	-e '}'				\\
	-e "/^to\./{"			\\
	-e "  w /tmp/n.to\$\$"		\\
	-e '  d'			\\
	-e '}'				\\
	-e "/\./{"			\\
	-e "  w /tmp/n.misc\$\$"	\\
	-e '  d'			\\
	-e '}'


if $test -s \$dotdir/.newsrc ; then
    $echo "Saving your current .newsrc as .oldnewsrc..."
    $mv -f \$dotdir/.newsrc \$dotdir/.oldnewsrc
fi

: newsrc order determined here
$cat \\
    /tmp/n.local\$\$ \\
    /tmp/n.\$locorg\$\$ \\
    /tmp/n.\$organization\$\$ \\
    /tmp/n.\$city\$\$ \\
    /tmp/n.\$state\$\$ \\
    /tmp/n.\$cntry\$\$ \\
    /tmp/n.\$cont\$\$ \\
    /tmp/n.mod\$\$ \\
    /tmp/n.net\$\$ \\
    /tmp/n.fa\$\$ \\
    /tmp/n.misc\$\$ \\
    /tmp/n.test\$\$ \\
| $uniq >\$dotdir/.newsrc

$rm -f	/tmp/n.to\$\$ \\
	/tmp/n.tmp\$\$ \\
	/tmp/n.local\$\$ \\
	/tmp/n.\$locorg\$\$ \\
	/tmp/n.\$organization\$\$ \\
	/tmp/n.\$city\$\$ \\
	/tmp/n.\$state\$\$ \\
	/tmp/n.\$cntry\$\$ \\
	/tmp/n.\$cont\$\$ \\
	/tmp/n.mod\$\$ \\
	/tmp/n.net\$\$ \\
	/tmp/n.fa\$\$ \\
	/tmp/n.misc\$\$ \\
	/tmp/n.test\$\$

$cat <<'EOH'
Done.

If you have never used the news system before, you may find the articles
in net.announce.newuser to be helpful.  There is also a manual entry for rn.

To get rid of newsgroups you aren't interested in, use the 'u' command.
Type h for help at any time while running rn.
EOH
!GROK!THIS!
$eunicefix newsetup
chmod 755 newsetup
!STUFFY!FUNK!
echo Extracting head.h
cat >head.h <<'!STUFFY!FUNK!'
/* $Header: head.h,v 4.1 84/09/24 11:47:47 lwall Exp $
 *
 * $Log:	head.h,v $
 * Revision 4.1  84/09/24  11:47:47  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:11:28  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:20  lwall
 * Baseline for netwide release
 * 
 */

#define HEAD_FIRST 1

/* types of header lines (if only C really believed in enums)
 * (These must stay in alphabetic order at least in the first letter.
 * Within each letter it helps to arrange in increasing likelihood.)
 */

#define PAST_HEADER	0	/* body */
#define SOME_LINE	1	/* unrecognized */
#define ARTID_LINE	2	/* article-i.d. */
#define DIST_LINE	3	/* distribution */
#define DATE_LINE	4	/* date */
#define RECEIVED_LINE	5	/* date-received */
#define EXPIR_LINE	6	/* expires */
#define FOLLOW_LINE	7	/* followup-to */
#define FROM_LINE	8	/* from */
#define KEYW_LINE	9	/* keywords */
#define LINES_LINE	10	/* lines */
#define MESSID_LINE	11	/* message-id */
#define NFFR_LINE	12	/* nf-from */
#define NFID_LINE	13	/* nf-id */
#define NGS_LINE	14	/* newsgroups */
#define ORG_LINE	15	/* organization */
#define PATH_LINE	16	/* path */
#define POSTED_LINE	17	/* posted */
#define PVER_LINE	18	/* posting-version */
#define REPLY_LINE	19	/* reply-to */
#define REFS_LINE	20	/* references */
#define RVER_LINE	21	/* relay-version */
#define SENDER_LINE	22	/* sender */
#define SUMRY_LINE	23	/* summary */
#define SUBJ_LINE	24	/* subject */
#define XREF_LINE	25	/* xref */

#define HEAD_LAST	26	/* one more than the last one above */

struct headtype {
    char *ht_name;		/* header line identifier */
#ifdef pdp11
    short ht_minpos;
    short ht_maxpos;
#else
    ART_POS ht_minpos;		/* pointer to beginning of line in article */
    ART_POS ht_maxpos;		/* pointer to end of line in article */
#endif
    char ht_length;		/* could make these into nybbles but */
    char ht_flags;		/* it wouldn't save space normally */
};				/* due to alignment considerations */

#define HT_HIDE 1	/* -h on this line */
#define HT_MAGIC 2	/* do any special processing on this line */

/* This array must stay in the same order as the list above */

#ifndef DOINIT
EXT struct headtype htype[HEAD_LAST];
#else
struct headtype htype[HEAD_LAST] = {
 /* name             minpos   maxpos  length   flag */
    {"BODY",		0,	0,	4,	0		},
    {"unrecognized",	0,	0,	12,	0		},
    {"article-i.d.",	0,	0,	12,	HT_HIDE		},
    {"distribution",	0,	0,	12,	0		},
    {"date",		0,	0,	4,	0		},
    {"date-received",	0,	0,	13,	0		},
    {"expires",		0,	0,	7,	HT_HIDE|HT_MAGIC},
    {"followup-to",	0,	0,	11,	0		},
    {"from",		0,	0,	4,	0		},
    {"keywords",	0,	0,	8,	0		},
    {"lines",		0,	0,	5,	0		},
    {"message-id",	0,	0,	10,	0		},
    {"nf-from",		0,	0,	7,	HT_HIDE		},
    {"nf-id",		0,	0,	5,	HT_HIDE		},
    {"newsgroups",	0,	0,	10,	HT_MAGIC|HT_HIDE},
    {"organization",	0,	0,	12,	0		},
    {"path",		0,	0,	4,	HT_HIDE		},
    {"posted",		0,	0,	6,	HT_HIDE		},
    {"posting-version",	0,	0,	15,	HT_HIDE		},
    {"reply-to",	0,	0,	8,	0		},
    {"references",	0,	0,	10,	0		},
    {"relay-version",	0,	0,	13,	HT_HIDE		},
    {"sender",		0,	0,	6,	0		},
    {"summary",		0,	0,	7,	0		},
    {"subject",		0,	0,	7,	HT_MAGIC	},
    {"xref",		0,	0,	4,	HT_HIDE		}
};
#endif

#ifdef ASYNC_PARSE
EXT ART_NUM parsed_art INIT(0);
#endif

EXT char in_header INIT(0);		/* are we decoding the header? */

#ifdef CACHESUBJ
    EXT char **subj_list INIT(Null(char **));
#endif

void	head_init();
int	set_line_type();
void	start_header();
bool    parseline();
#ifdef ASYNC_PARSE
    int		parse_maybe();
#endif
char	*fetchsubj();
char	*fetchlines();
!STUFFY!FUNK!
echo Extracting cheat.c
cat >cheat.c <<'!STUFFY!FUNK!'
/* $Header: cheat.c,v 4.1 84/09/24 11:44:08 lwall Exp $
 *
 * $Log:	cheat.c,v $
 * Revision 4.1  84/09/24  11:44:08  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/10  15:08:12  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/05  11:35:12  lwall
 * Fixed ifdefs so makedepend would not complain.
 * 
 * Revision 4.0  84/09/04  09:49:59  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "intrp.h"
#include "search.h"
#include "ng.h"
#include "bits.h"
#include "artio.h"
#include "term.h"
#include "artsrch.h"
#include "head.h"
#include "INTERN.h"
#include "cheat.h"

/* see what we can do while they are reading */

#ifdef PENDING
#   ifdef ARTSEARCH
	COMPEX srchcompex;		/* compiled regex for searchahead */
#   endif
#endif

void
cheat_init()
{
    ;
}

#ifdef PENDING
void
look_ahead()
{
#ifdef ARTSEARCH
    register char *h, *s;

#ifdef DEBUGGING
    if (debug && srchahead) {
	printf("(%ld)",(long)srchahead);
	fflush(stdout);
    }
#endif
    if (srchahead && srchahead < art) {	/* in ^N mode? */
	char *pattern;

	pattern = buf+1;
	strcpy(pattern,": *");
	h = pattern + strlen(pattern);
	interp(h,"%s");
	h[24] = '\0';		/* compensate for notesfiles */
	while (*h) {
	    if (index("\\[.^*$'\"",*h) != Nullch)
		*h++ = '.';
	    else
		h++;
	}
#ifdef DEBUGGING
	if (debug & DEB_SEARCH_AHEAD) {
	    fputs("(hit CR)",stdout);
	    fflush(stdout);
	    gets(buf+128);
	    printf("\npattern = %s\n",pattern);
	}
#endif
	if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) {
				    /* compile regular expression */
	    printf("\n%s\n",s);
	    srchahead = 0;
	}
	if (srchahead) {
	    srchahead = art;
	    for (;;) {
		srchahead++;	/* go forward one article */
		if (srchahead > lastart) { /* out of articles? */
#ifdef DEBUGGING
		    if (debug)
			fputs("(not found)",stdout);
#endif
		    break;
		}
		/*NOSTRICT*/
		if (!was_read(srchahead) &&
		    wanted(&srchcompex,srchahead,0)) {
				    /* does the shoe fit? */
#ifdef DEBUGGING
		    if (debug)
			printf("(%ld)",(long)srchahead);
#endif
		    artopen(srchahead);
		    break;
		}
		/*NOSTRICT*/
		if (input_pending())
		    break;
	    }
	    fflush(stdout);
	}
    }
    else
#endif
    {
	if (art+1 <= lastart)/* how about a pre-fetch? */
	    artopen(art+1);	/* look for the next article */
    }
}
#endif

/* see what else we can do while they are reading */

void
collect_subjects()
{
#ifdef PENDING
# ifdef CACHESUBJ
    ART_NUM oldart = openart;
    ART_POS oldartpos;

    if (!in_ng || !srchahead)
	return;
    if (oldart)			/* remember where we were in art */
	oldartpos = ftell(artfp);
    if (srchahead >= subj_to_get)
	subj_to_get = srchahead+1;
    while (!input_pending() && subj_to_get <= lastart)
	fetchsubj(subj_to_get++,FALSE,FALSE);
    if (oldart) {
	artopen(oldart);
	fseek(artfp,oldartpos,0);	/* do not screw the pager */
    }
# endif
#endif
}

!STUFFY!FUNK!
echo Extracting ngsrch.c
cat >ngsrch.c <<'!STUFFY!FUNK!'
/* $Header: ngsrch.c,v 4.1 84/09/24 12:03:06 lwall Exp $
 *
 * $Log:	ngsrch.c,v $
 * Revision 4.1  84/09/24  12:03:06  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:20:42  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:48  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rcstuff.h"
#include "final.h"
#include "search.h"
#include "rn.h"
#include "util.h"
#include "term.h"
#include "rcln.h"
#include "INTERN.h"
#include "ngsrch.h"

#ifdef NGSORONLY
    COMPEX ngcompex;
#endif

void
ngsrch_init()
{
#ifdef ZEROGLOB
    init_compex(&ngcompex);
#endif	/* ZEROGLOB */
    ;
}

#ifdef NGSEARCH
int
ng_search(patbuf,get_cmd)
char *patbuf;				/* if patbuf != buf, get_cmd must */
int get_cmd;				/*   be set to FALSE!!! */
{
    char *pattern;			/* unparsed pattern */
    register char cmdchr = *patbuf;	/* what kind of search? */
    register char *s;
    bool backward = cmdchr == '?';	/* direction of search */

    int_count = 0;
    if (get_cmd && buf == patbuf)
	if (!finish_command(FALSE))		/* get rest of command */
	    return NGS_ABORT;
    for (pattern = patbuf+1; *pattern == ' '; pattern++) ;
    if (*pattern) {
	ng_doread = FALSE;
    }
    s = rindex(pattern,cmdchr);
    if (s != Nullch && *(s-1) != '\\') {
	*s++ = '\0';
	if (index(s,'r') != Nullch)
	    ng_doread = TRUE;
    }
    if ((s = ng_comp(&ngcompex,pattern,TRUE,TRUE)) != Nullch) {
					/* compile regular expression */
	printf("\n%s\n",s);
	return NGS_ABORT;
    }
    fputs("\nSearching...",stdout);	/* give them something to read */
    fflush(stdout);
    for (;;) {
	if (int_count) {
	    int_count = 0;
	    return NGS_INTR;
	}
	if (backward) {
	    if (ng > 0)
		--ng;
	    else
		ng = nextrcline;
	}
	else {
	    if (ng >= nextrcline)
		ng = 0;
	    else
		++ng;
	}
	if (ng == current_ng)
	    return NGS_NOTFOUND;
	if (ng == nextrcline || toread[ng] < TR_NONE || !ng_wanted())
	    continue;
	if (toread[ng] == TR_NONE)
	    set_toread(ng);
	
	if (toread[ng] > TR_NONE)
	    return NGS_FOUND;
	else if (toread[ng] == TR_NONE)
	    if (ng_doread)
		return NGS_FOUND;
	    else
		printf("\n[0 unread in %s--skipping]",rcline[ng]);
    }
}

bool
ng_wanted()
{
    return execute(&ngcompex,rcline[ng]) != Nullch;
}
#endif

#ifdef NGSORONLY
char *
ng_comp(compex,pattern,RE,fold)
COMPEX *compex;
char *pattern;
bool RE;
bool fold;
{
    char ng_pattern[128];
    register char *s = pattern, *d = ng_pattern;

    if (!*s)
	return Nullch;			/* reuse old pattern */
    for (; *s; s++) {
	if (*s == '.') {
	    *d++ = '\\';
	    *d++ = *s;
	}
	else if (*s == '?') {
	    *d++ = '.';
	}
	else if (*s == '*') {
	    *d++ = '.';
	    *d++ = *s;
	}
	else if (strnEQ(s,"all",3)) {
	    *d++ = '.';
	    *d++ = '*';
	    s += 2;
	}
	else
	    *d++ = *s;
    }
    *d = '\0';
    return compile(compex,ng_pattern,RE,fold);
}
#endif

!STUFFY!FUNK!
echo Extracting addng.c
cat >addng.c <<'!STUFFY!FUNK!'
/* $Header: addng.c,v 4.1 84/09/24 11:39:46 lwall Exp $
 *
 * $Log:	addng.c,v $
 * Revision 4.1  84/09/24  11:39:46  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/13  12:00:52  lwall
 * UNLINK for Eunice.
 * 
 * Revision 4.0.1.1  84/09/10  14:48:35  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:17  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "ngdata.h"
#include "last.h"
#include "util.h"
#include "intrp.h"
#include "only.h"
#include "rcstuff.h"
#include "INTERN.h"
#include "addng.h"

void
addng_init()
{
    ;
}

#ifdef FINDNEWNG
/* generate a list of new newsgroups from active file */

bool
newlist(munged,checkinlist)
bool munged;				/* are we scanning the whole file? */
bool checkinlist;
{
    char *tmpname;
    register char *s;
    long birthof();

    tmpname = savestr(filexp("/tmp/rnew.%$"));
    tmpfp = fopen(tmpname,"w");
    while (fgets(buf,LBUFLEN,actfp) != Nullch) {
	if (s = index(buf,' ')) {
	    *s++ = '\0';
	    if (strnEQ(buf,"to.",3))
		continue;
	    if (find_ng(buf) == nextrcline &&
		    (checkinlist ?
			(inlist(buf)) :
			(birthof(buf,(ART_NUM)atol(s)) > lasttime)
		    )
		) {
					/* if not in .newsrc and younger */
					/* than the last time we checked */
		fprintf(tmpfp,"%s\n",buf);
					/* then remember said newsgroup */
	    }
#ifdef FASTNEW
	    else {			/* not really a new group */
		if (!munged) {		/* did we assume not munged? */
		    fclose(tmpfp);	/* then go back, knowing that */
		    UNLINK(tmpname);
		    free(tmpname);
		    return TRUE;	/* active file was indeed munged */
		}
	    }
#endif
	}
#ifdef DEBUGGING
	else
	    printf("Bad active record: %s\n",buf);
#endif
    }

    /* we have successfully generated the list */

    fclose(tmpfp);
    tmpfp = fopen(tmpname,"r");
    UNLINK(tmpname);			/* be nice to the world */
    while (fgets(buf,LBUFLEN,tmpfp) != Nullch) {
	buf[strlen(buf)-1] = '\0';
	get_ng(buf,TRUE);		/* add newsgroup, maybe */
    }
    fclose(tmpfp);			/* be nice to ourselves */
    free(tmpname);
    return FALSE;			/* do not call us again */
}

/* return creation time of newsgroup */

long
birthof(ngnam,ngsize)
char *ngnam;
ART_NUM ngsize;
{
    char tst[128];
 
    sprintf(tst, ngsize ? "%s/%s/1" : "%s/%s" ,spool,getngdir(ngnam));
    if (stat(tst,&filestat) < 0)
	return 0L;			/* not there, assume ancient */
    else
	return filestat.st_mtime;
}

bool
scanactive()
{
    NG_NUM oldnext = nextrcline;	/* remember # lines in newsrc */

    fseek(actfp,0L,0);
    newlist(TRUE,TRUE);
    if (nextrcline != oldnext) {	/* did we add any new groups? */
	return TRUE;
    }
    return FALSE;
}

#endif

!STUFFY!FUNK!
echo Extracting Pnews.man
cat >Pnews.man <<'!STUFFY!FUNK!'
''' $Header: Pnews.man,v 4.1 84/09/24 11:38:14 lwall Exp $
''' 
''' $Log:	Pnews.man,v $
''' Revision 4.1  84/09/24  11:38:14  lwall
''' Real baseline.
''' 
''' Revision 4.0  84/09/04  09:48:58  lwall
''' Baseline for netwide release
''' 
''' 
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
'''     Set up \*(-- to give an unbreakable dash;
'''     string Tr holds user defined translation string.
'''     Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" "
.ds R" "
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH PNEWS 1 LOCAL
.SH NAME
Pnews - a program for posting news articles
.SH SYNOPSIS
.B Pnews newsgroup title
.br
  or
.br
.B Pnews -h headerfile [oldarticle]
.br
  or
.br
.B Pnews
.SH DESCRIPTION
Pnews is a friendly interface for posting news articles.
It will ask several questions, then allow you to enter your article,
and then post it using the inews(1) program.
If you type h and a carriage return at any point,
.I Pnews
will tell you what it wants to know.
.PP
The -h form is used when invoked from
.IR rn .
If your editor can edit multiple files, and you want the article to which
you are replying to show up as an alternate file, define the environment
variable MAILPOSTER as \*(L"Rnmail -h %h %a\*(R".
You can also modify the the MAILHEADER environment variable to change the
header file that
.I rn
passes to Rnmail.
.SH ENVIRONMENT
.IP DOTDIR 8
Where to find your dot files, if they aren't in your home directory.
.Sp
Default: $HOME
.IP EDITOR 8
The editor you want to use, if VISUAL is undefined.
.Sp
Default: whatever your news administrator installed, usually vi.
.IP HOME 8
Your home directory.
.Sp
Default: $LOGDIR
.IP LOGDIR 8
Your home directory if HOME is undefined.
.IP LOGNAME 8
Your login name, if USER is undefined.
.Sp
Default: value of \*(L"whoami\*(R".
.IP NAME 8
Your full name.
.Sp
Default: name from /etc/passwd, or ~/.fullname.
.IP ORGANIZATION 8
Either the name of your organization, or the name of a file containing the
name of your organization.
.Sp
Default: whatever your news administrator chose.
.IP USER 8
Your login name.
.Sp
Default: $LOGNAME
.IP VISUAL 8
The editor you want to use.
.Sp
Default: $EDITOR
.SH FILES
/tmp/article$$
.br
~/dead.article
.SH SEE ALSO
rn(1), Rnmail(1), inews(1)
.SH DIAGNOSTICS
.SH BUGS
Not the speediest program in the world, but maybe that's a blessing to the
net.
!STUFFY!FUNK!
echo Extracting Rnmail.man
cat >Rnmail.man <<'!STUFFY!FUNK!'
''' $Header: Rnmail.man,v 4.1 84/09/24 11:39:16 lwall Exp $
''' 
''' $Log:	Rnmail.man,v $
''' Revision 4.1  84/09/24  11:39:16  lwall
''' Real baseline.
''' 
''' Revision 4.0  84/09/04  09:49:12  lwall
''' Baseline for netwide release
''' 
''' 
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
'''     Set up \*(-- to give an unbreakable dash;
'''     string Tr holds user defined translation string.
'''     Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" "
.ds R" "
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH RNMAIL 1 LOCAL
.SH NAME
Rnmail - a program for replying via mail
.SH SYNOPSIS
.B Rnmail destination_list
.br
  or
.br
.B Rnmail -h headerfile [oldarticle]
.br
  or
.br
.B Rnmail
.SH DESCRIPTION
Rnmail is a friendly interface for mailing replies to news articles.
It will ask several questions, then allow you to enter your letter,
and then mail it off.
If you type h and a carriage return at any point,
.I Rnmail
will tell you what it wants to know.
.PP
The -h form is used when invoked from
.IR rn .
If your editor can edit multiple files, and you want the article to which
you are replying to show up as an alternate file, define the environment
variable MAILPOSTER as \*(L"Rnmail -h %h %a\*(R".
You can also modify the the MAILHEADER environment variable to change the
header file that
.I rn
passes to Rnmail.
.SH ENVIRONMENT
.IP EDITOR 8
The editor you want to use, if VISUAL is undefined.
.Sp
Default: whatever your news administrator installed, usually vi.
.IP HOME 8
Your home directory.
.Sp
Default: $LOGDIR
.IP LOGDIR 8
Your home directory if HOME is undefined.
.IP LOGNAME 8
Your login name, if USER is undefined.
.Sp
Default: value of \*(L"whoami\*(R".
.IP ORGANIZATION 8
Either the name of your organization, or the name of a file containing the
name of your organization.
.Sp
Default: whatever your news administrator chose.
.IP USER 8
Your login name.
.Sp
Default: $LOGNAME
.IP VISUAL 8
The editor you want to use.
.Sp
Default: $EDITOR
.SH FILES
/tmp/rnmail$$
.br
~/dead.letter
.SH SEE ALSO
rn(1), Pnews(1), mail(1)
.SH DIAGNOSTICS
.SH BUGS
Uses /bin/mail in the absence of sendmail.
!STUFFY!FUNK!
echo Extracting only.c
cat >only.c <<'!STUFFY!FUNK!'
/* $Header: only.c,v 4.1 84/09/24 12:04:06 lwall Exp $
 *
 * $Log:	only.c,v $
 * Revision 4.1  84/09/24  12:04:06  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:22:17  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:56  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "search.h"
#include "util.h"
#include "final.h"
#include "ngsrch.h"
#include "INTERN.h"
#include "only.h"

void
only_init()
{
    ;
}

void
setngtodo(pat)
char *pat;
{
    char *s;

#ifdef ONLY
    if (!*pat)
	return;
    if (maxngtodo < NGMAX) {
	ngtodo[maxngtodo] = savestr(pat);
#ifdef SPEEDOVERMEM
	compextodo[maxngtodo] = (COMPEX*)safemalloc(sizeof(COMPEX));
	init_compex(compextodo[maxngtodo]);
	compile(compextodo[maxngtodo],pat,TRUE,TRUE);
	if ((s = ng_comp(compextodo[maxngtodo],pat,TRUE,TRUE)) != Nullch) {
					    /* compile regular expression */
	    printf("\n%s\n",s);
	    finalize(1);
	}
#endif
	maxngtodo++;
    }
#else
    notincl("o");
#endif
}

/* if command line list is non-null, is this newsgroup wanted? */

bool
inlist(ngnam)
char *ngnam;
{
#ifdef ONLY
    register int i;
#ifdef SPEEDOVERMEM

    if (maxngtodo == 0)
	return TRUE;
    for (i=0; i<maxngtodo; i++) {
	if (execute(compextodo[i],ngnam))
	    return TRUE;
    }
    return FALSE;
#else
    COMPEX ilcompex;
    char *s;

    if (maxngtodo == 0)
	return TRUE;
    init_compex(&ilcompex);
    for (i=0; i<maxngtodo; i++) {
	if ((s = ng_comp(&ilcompex,ngtodo[i],TRUE,TRUE)) != Nullch) {
					    /* compile regular expression */
	    printf("\n%s\n",s);
	    finalize(1);
	}
	
	if (execute(&ilcompex,ngnam) != Nullch) {
	    free_compex(&ilcompex);
	    return TRUE;
	}
    }
    free_compex(&ilcompex);
    return FALSE;
#endif
#else
    return TRUE;
#endif
}

#ifdef ONLY
void
end_only()
{
    if (maxngtodo) {			/* did they specify newsgroup(s) */
	int whicharg;

#ifdef VERBOSE
	IF(verbose)
	    printf("\nRestriction %s%s removed.\n",ngtodo[0],
		maxngtodo > 1 ? ", etc." : nullstr);
	ELSE
#endif
#ifdef TERSE
	    fputs("\nExiting \"only\".\n",stdout);
#endif
	for (whicharg = 0; whicharg < maxngtodo; whicharg++) {
	    free(ngtodo[whicharg]);
#ifdef SPEEDOVERMEM
	    free_compex(compextodo[whicharg]);
	    free((char*)compextodo[whicharg]);
#endif
	}
	maxngtodo = 0;
    }
}
#endif
!STUFFY!FUNK!
echo Extracting makedepend.SH
cat >makedepend.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting makedepend (with variable substitutions)"
$spitshell >makedepend <<!GROK!THIS!
$startsh
# $Header: makedepend.SH,v 4.1 84/09/24 11:59:28 lwall Exp $
#
# $Log:	makedepend.SH,v $
# Revision 4.1  84/09/24  11:59:28  lwall
# Real baseline.
# 
# Revision 4.0.1.4  84/09/14  15:55:49  lwall
# Made less interrupt sensitive.
# 
# Revision 4.0.1.3  84/09/12  15:20:16  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.2  84/09/05  17:02:52  lwall
# More efficient.
# 
# Revision 4.0.1.1  84/09/05  16:02:25  lwall
# Now makes use of cpp as determined by Configure.
# 
# Revision 4.0  84/09/04  09:51:17  lwall
# Baseline for netwide release
# 

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

$cat /dev/null >.deptmp
$echo "(Note: this is going to take a while.)"
$rm -f X*.c
for file in *.c; do
    filebase=\`basename \$file .c\`
    $echo "Finding dependencies for \$filebase.o."
    $sed -n <\$file >X\$file \\
	-e "/^\${filebase}_init(/q" \\
	-e '/^#/{' \\
	-e 's|/\*.*$||' \\
	-e p \\
	-e '}'
    $cpp X\$file | $sed  \\
	-e '/^# *[0-9]/!d' \\
	-e 's/^.*"\(.*\)".*\$/'\$filebase'.o: \1/' \\
	-e 's|: \./|: |' \\
	-e 's|: X|: |' | \\
	$uniq | $sort | $uniq >> .deptmp
done

$sed <Makefile >Makefile.new -e '1,/^# AUTOMATICALLY/!d'

if $test -s .deptmp; then
    echo "Updating Makefile..."
    echo "# If this runs make out of memory, delete /usr/include lines." >>Makefile.new
    $cat .deptmp >>Makefile.new
else
    $echo "You don't seem to have a proper C preprocessor.  Using grep instead."
    $egrep '^#include ' *.c *.h >.deptmp
    echo "Updating Makefile..."
    <.deptmp $sed -n 's|c:#include "\(.*\)".*\$\$|o: \1|p' >> Makefile.new
    <.deptmp $sed -n 's|c:#include <\(.*\)>.*\$\$|o: /usr/include/\1|p' >> Makefile.new
    <.deptmp $sed -n 's|h:#include "\(.*\)".*\$\$|h: \1|p' >> Makefile.new
    <.deptmp $sed -n 's|h:#include <\(.*\)>.*\$\$|h: /usr/include/\1|p' >> Makefile.new
fi
$mv Makefile Makefile.old
$mv Makefile.new Makefile
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> Makefile
rm .deptmp X*.c

!GROK!THIS!
$eunicefix makedepend
chmod 755 makedepend
!STUFFY!FUNK!
echo Extracting ndir.c
cat >ndir.c <<'!STUFFY!FUNK!'
/* $Header: ndir.c,v 4.1 84/09/24 12:01:00 lwall Exp $
 *
 * $Log:	ndir.c,v $
 * Revision 4.1  84/09/24  12:01:00  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/14  11:36:30  lwall
 * Include ndir.h even when USENDIR not defined.
 * 
 * Revision 4.0.1.1  84/09/12  17:08:32  lwall
 * Rn-ized.
 * 
 * Revision 4.0  84/09/12  17:07:44  lwall
 * *** empty log message ***
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "INTERN.h"
#include "ndir.h"

#ifdef USENDIR
/*
 * support for Berkeley directory reading routine on a V7 file system
 */

/*
 * open a directory.
 */
DIR *
opendir(name)
char *name;
{
	register DIR *dirp;
	register int fd;

	if ((fd = open(name, 0)) == -1)
		return NULL;
	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
		close (fd);
		return NULL;
	}
	dirp->dd_fd = fd;
	dirp->dd_loc = 0;
	return dirp;
}

/*
 * read an old stlye directory entry and present it as a new one
 */
#define	ODIRSIZ	14

struct	olddirect {
	short	od_ino;
	char	od_name[ODIRSIZ];
};

/*
 * get next entry in a directory.
 */
struct direct *
readdir(dirp)
register DIR *dirp;
{
	register struct olddirect *dp;
	static struct direct dir;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->od_ino == 0)
			continue;
		dir.d_ino = dp->od_ino;
		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen = DIRSIZ(&dir);
		return (&dir);
	}
}

/*
 * close a directory.
 */
void
closedir(dirp)
register DIR *dirp;
{
	close(dirp->dd_fd);
	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	free(dirp);
}
#endif USENDIR
!STUFFY!FUNK!
echo Extracting subs.help.SH
cat >subs.help.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting subs.help (with variable substitutions)"
$spitshell >subs.help <<!GROK!THIS!
$startsh
# $Header: subs.help.SH,v 4.1 84/09/24 12:10:16 lwall Exp $
# 
# $Log:	subs.help.SH,v $
# Revision 4.1  84/09/24  12:10:16  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/12  15:26:07  lwall
# Housekeeping.
# 
# Revision 4.0  84/09/04  09:52:44  lwall
# Baseline for netwide release
# 

$pager <<'EOT'
Valid substitutions are:

a	Current article number
A	Full name of current article (%P/%c/%a)
b	Destination of last save command, often a mailbox
B	Bytes to ignore at beginning of last saved article
c	Current newsgroup, directory form
C	Current newsgroup, dot form
d	Full name of newsgroup directory (%P/%c)
D	Distribution line from current article\
f	Who the current article is from
F	Newsgroups to followup to (from Newsgroups and Followup-To)
h	(This help message)
H	Host name (yours)
i	Message-I.D. line from current article, with <>
l	News administrator's login name, if any
L	Login name (yours)
M	Number of article marked with M
n	Newsgroups from current article
N	Full name (yours)
o	Organization (yours)
O	Original working directory (where you ran rn from)
p	Your private news directory (from -d)
P	Public news spool directory
r	Last reference (parent article id)
R	References list for followup article
s	Subject, with all Re's and (nf)'s stripped off
S	Subject, with one Re stripped off\
t	New To line derived from From and Reply-To (Internet format)
T	New To line derived from Path
u	Number of unread articles
U	Number of unread articles not counting current article
x	News library directory
X	Rn library directory
~	Your home directory
.	Directory containing . files
$	Current process number
/	Last search string
ESC	Run preceding command through % interpretation
EOT
!GROK!THIS!
$eunicefix subs.help
chmod 755 subs.help
!STUFFY!FUNK!
echo Extracting ng.help.SH
cat >ng.help.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting ng.help (with variable substitutions)"
$spitshell >ng.help <<!GROK!THIS!
$startsh
# $Header: ng.help.SH,v 4.1 84/09/24 12:02:44 lwall Exp $
# 
# $Log:	ng.help.SH,v $
# Revision 4.1  84/09/24  12:02:44  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/12  15:23:47  lwall
# Housekeeping.
# 
# Revision 4.0  84/09/04  09:51:43  lwall
# Baseline for netwide release
# 

$pager <<'EOT'
Newsgroup Selection commands:

y,SP	Do this newsgroup now.
.cmd	Do this newsgroup, executing cmd as first command.
=	Equivalent to .=<carriage return>.
u	Unsubscribe from this newsgroup.
c	Catch up (mark this newsgroup all read).

n	Go to the next newsgroup with unread news.
N	Go to the next newsgroup.
p	Go to the previous newsgroup with unread news.
P	Go to the previous newsgroup.
-	Go to the previously displayed newsgroup.
1	Go to the first newsgroup.
^	Go to the first newsgroup with unread news.
$	Go to the last newsgroup.
g name	Go to the named newsgroup.  Subscribe to new newsgroups this way too.
/pat	Search forward for newsgroup matching pattern.
?pat	Search backward for newsgroup matching pattern.
	(Use * and ? style patterns.  Append r to include read newsgroups.)
l pat	List unsubscribed newsgroups containing pattern.
m name	Move named newsgroup elsewhere (no name moves current newsgroup).
o pat	Only display newsgroups matching pattern.  Omit pat to unrestrict.
a pat	Like o, but also scans for unsubscribed newsgroups matching pattern.
L	List current .newsrc.
&	Print current command-line switch settings.
&switch {switch}
	Set (or unset) more command-line switches.
!cmd	Shell escape.
q	Quit rn.
^K	Edit the global KILL file.  Use commands like /pattern/j to suppress
	pattern in every newsgroup.
v	Print version.
EOT
!GROK!THIS!
$eunicefix ng.help
chmod 755 ng.help
!STUFFY!FUNK!
echo Extracting header.c.pat
cat >header.c.pat <<'!STUFFY!FUNK!'
NOTE: the preceding context may be different under 2.10.2, but you should
be able to figure it out anyway.

*** header.old.c	Fri Apr 27 11:30:49 1984
--- header.c	Mon Feb 27 10:44:03 1984
***************
*** 107,112
  #define NUMLINES	19
  #define KEYWORDS	20
  #define APPROVED	21
  #define OTHER		99
  
  char *malloc();

--- 107,115 -----
  #define NUMLINES	19
  #define KEYWORDS	20
  #define APPROVED	21
+ #ifdef DOXREFS
+ #define XREF		98
+ #endif DOXREFS
  #define OTHER		99
  
  char *malloc();
***************
*** 201,206
  				seenrelay = 1;
  			}
  			break;
  		case OTHER:
  			if (unreccnt < NUNREC) {
  				hp->unrec[unreccnt] = malloc(strlen(bfr) + 1);

--- 204,214 -----
  				seenrelay = 1;
  			}
  			break;
+ #ifdef DOXREFS
+ 		case XREF:
+ 			getfield(hp->xref);
+ 			break;
+ #endif DOXREFS
  		case OTHER:
  			if (unreccnt < NUNREC) {
  				hp->unrec[unreccnt] = malloc(strlen(bfr) + 1);
***************
*** 398,403
  		return KEYWORDS;
  	if (its("Approved: "))
  		return APPROVED;
  	return OTHER;
  }
  

--- 406,415 -----
  		return KEYWORDS;
  	if (its("Approved: "))
  		return APPROVED;
+ #ifdef DOXREFS
+ 	if (its("Xref: "))
+ 		return XREF;
+ #endif DOXREFS
  	return OTHER;
  }
  
***************
*** 507,512
  		fprintf(fp, "Keywords: %s\n", hp->keywords);
  	if (*hp->approved)
  		fprintf(fp, "Approved: %s\n", hp->approved);
  	for (iu = 0; iu < NUNREC; iu++) {
  		if (hp->unrec[iu])
  			fprintf(fp, "%s", &hp->unrec[iu][0]);

--- 519,528 -----
  		fprintf(fp, "Keywords: %s\n", hp->keywords);
  	if (*hp->approved)
  		fprintf(fp, "Approved: %s\n", hp->approved);
+ #ifdef DOXREFS
+ 	if (wr == 1 && *hp->xref)
+ 		fprintf(fp, "Xref: %s\n", hp->xref);
+ #endif DOXREFS
  	for (iu = 0; iu < NUNREC; iu++) {
  		if (hp->unrec[iu])
  			fprintf(fp, "%s", &hp->unrec[iu][0]);
!STUFFY!FUNK!
echo Extracting artio.c
cat >artio.c <<'!STUFFY!FUNK!'
/* $Header: artio.c,v 4.1 84/09/24 11:41:57 lwall Exp $
 *
 * $Log:	artio.c,v $
 * Revision 4.1  84/09/24  11:41:57  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/10  15:05:12  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/04  15:14:02  lwall
 * LINKART option for Eunice sites.
 * 
 * Revision 4.0  84/09/04  09:49:31  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "INTERN.h"
#include "artio.h"

void
artio_init()
{
    ;
}

/* open an article, unless it's already open */

FILE *
artopen(artnum)
ART_NUM artnum;
{
    char artname[8];			/* filename of current article */

    if (openart == artnum) {		/* this article is already open? */
	fseek(artfp,0L,0);		/* just get to the beginning */
	return artfp;			/* and say we succeeded */
    }
    if (artfp != Nullfp) {		/* it was somebody else? */
	fclose(artfp);			/* put them out of their misery */
	openart = 0;			/* and remember them no more */
    }
    sprintf(artname,"%ld",(long)artnum);
					/* produce the name of the article */
    if (artfp = fopen(artname,"r"))	/* if we can open it */
	openart = artnum;		/* remember what we did here */
#ifdef LINKART
    {
	char tmpbuf[256];
	char *s;

	if (fstat(artfp->_file,&filestat))
	    return artfp;
	if (filestat.st_size < (sizeof tmpbuf)) {
	    fgets(tmpbuf,(sizeof tmpbuf),artfp);
	    if (*tmpbuf == '/') {	/* is a "link" to another article */
		fclose(artfp);
		if (s=index(tmpbuf,'\n'))
		    *s = '\0';
		if (!(artfp = fopen(tmpbuf,"r")))
		    openart = 0;
		else {
		    if (*linkartname)
			free(linkartname);
		    linkartname = savestr(tmpbuf);
		}
	    }
	    else
		fseek(artfp,0L,0);		/* get back to the beginning */
	}
    }
#endif
    return artfp;			/* and return either fp or NULL */
}

!STUFFY!FUNK!
echo Extracting newsgroups.man
cat >newsgroups.man <<'!STUFFY!FUNK!'
''' $Header: newsgroups.man,v 4.1 84/09/24 12:01:37 lwall Exp $
''' 
''' $Log:	newsgroups.man,v $
''' Revision 4.1  84/09/24  12:01:37  lwall
''' Real baseline.
''' 
''' Revision 4.0  84/09/04  09:51:36  lwall
''' Baseline for netwide release
''' 
''' 
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
'''     Set up \*(-- to give an unbreakable dash;
'''     string Tr holds user defined translation string.
'''     Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" "
.ds R" "
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH NEWSGROUPS 1 LOCAL
.SH NAME
newsgroups - a program to list unsubscribed newsgroups.
.SH SYNOPSIS
.B newsgroups pattern flag
.SH DESCRIPTION
The
.I newsgroups
program compares your .newsrc file with the file of active newsgroups,
and prints a list of unsubscribed newsgroups matching pattern.
If the second argument \*(L"flag\*(R" is present, only newsgroups not
found in your .newsrc are listed, and the display is not paged.
If the second argument is missing, the display is paged, and an additional
list of unsubscribed newsgroups occurring in your .newsrc is printed.
.SH ENVIRONMENT
.IP DOTDIR 8
Where to find your .newsrc, if not in your home directory.
.Sp
Default: $HOME
.IP HOME 8
Your home directory.
.Sp
Default: $LOGDIR
.IP LOGDIR 8
Your home directory if HOME is undefined.
.SH FILES
/usr/lib/news/active or a reasonable facsimile
.br
${DOTDIR-{$HOME-$LOGDIR}}/.newsrc
.SH SEE ALSO
rn(1), newsrc(5)
.SH DIAGNOSTICS
.SH BUGS
The flag argument is a kludge.
!STUFFY!FUNK!
echo Extracting newsgroups.SH
cat >newsgroups.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting newsgroups (with variable substitutions)"
$spitshell >newsgroups <<!GROK!THIS!
$startsh
# $Header: newsgroups.SH,v 4.1 84/09/24 12:01:31 lwall Exp $
# 
# $Log:	newsgroups.SH,v $
# Revision 4.1  84/09/24  12:01:31  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/12  15:22:25  lwall
# Check for sh interpretation.
# 
# Revision 4.0  84/09/04  09:51:34  lwall
# Baseline for netwide release
# 

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

: syntax: newsgroups [pattern] [pipeflag]

: System Dependencies

: You might want to change pager to a "make column" program if you have one.
: On the other hand, if your kernel does paging, cat would probably do.
pager="${pager-/usr/ucb/more}"
active="${active-/usr/lib/news/active}"

: End of system dependencies, hopefully

case \$active in
~*) active=\`$filexp \$active\` ;;
esac

if $test \$# -ge 2 ; then
    pager=$cat
else
    $echo "Completely unsubscribed newsgroups:"
fi

: Throwing .newsrc into the pot twice is a lovely hack to prevent
: bogus newsgroups from showing up as unsubscribed.

$cat \$HOME/.newsrc \$HOME/.newsrc \$active | \\
$sed	-e '/^options/d' \\
	-e '/^[	 ]/d' \\
	-e '/^control/d' \\
	-e '/^to\./d' \\
	-e 's/^\([^ !:]*\)[ !:].*\$/\1/' \\
	-e "/.*\$1/p" \\
	-e 'd' | \\
$sort | $uniq -u | \$pager
if $test \$# -ge 2 ; then
    exit
fi
$echo $n "[Type return to continue] $c"
read tmp
$echo ""
$echo "Unsubscribed but mentioned in .newsrc:"
$sed < \$HOME/.newsrc \\
	-e "/\$1.*!/"'s/^\([^!]*\)!.*\$/\1/p' \\
	-e 'd' | \\
$sort | \$pager
!GROK!THIS!
$eunicefix newsgroups
chmod 755 newsgroups
!STUFFY!FUNK!
echo Extracting pager.help.SH
cat >pager.help.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting pager.help (with variable substitutions)"
$spitshell >pager.help <<!GROK!THIS!
$startsh
# $Header: pager.help.SH,v 4.1 84/09/24 12:04:24 lwall Exp $
#
# $Log:	pager.help.SH,v $
# Revision 4.1  84/09/24  12:04:24  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/12  15:25:26  lwall
# Housekeeping.
# 
# Revision 4.0  84/09/04  09:51:59  lwall
# Baseline for netwide release
# 

$pager <<'EOT'
Paging commands:

SP	Display the next page.
x	Display the next page decrypted (rot13).
d	Display half a page more.
CR	Display one more line.
^R,v,^X	Restart the current article (v=verbose header, ^X=rot13).
^B	Back up one page.
^L,X	Refresh the screen (X=rot13).
g pat	Go to (search forward within article for) pattern.
G	Search again for current pattern within article.
^G	Search for next line beginning with \"Subject:\".
q	Quit the pager, go to end of article.  Leave article read or unread.
j	Junk this article (mark it read).  Goes to end of article.

The following commands skip the rest of the current article, then behave
just as if typed to the 'What next?' prompt at the end of the article:

n	Scan forward for next unread article.
N	Go to next article.
^N	Scan forward for next unread article with same title.
p,P,^P	Same as n,N,^N, only going backwards.
-	Go to previously displayed article.

The following commands also take you to the end of the article.
Type h at end of article for a description of these commands:
	# $ & / = ? c C f F k K ^K m M number r R ^R s S u v w W Y ^ |

(To return to the middle of the article after one of these commands, type ^L.)
EOT
!GROK!THIS!
$eunicefix pager.help
chmod 755 pager.help
!STUFFY!FUNK!
echo Extracting newsetup.man
cat >newsetup.man <<'!STUFFY!FUNK!'
''' $Header: newsetup.man,v 4.1 84/09/24 12:01:16 lwall Exp $
''' 
''' $Log:	newsetup.man,v $
''' Revision 4.1  84/09/24  12:01:16  lwall
''' Real baseline.
''' 
''' Revision 4.0  84/09/04  09:51:32  lwall
''' Baseline for netwide release
''' 
''' 
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
'''     Set up \*(-- to give an unbreakable dash;
'''     string Tr holds user defined translation string.
'''     Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" "
.ds R" "
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH NEWSETUP 1 LOCAL
.SH NAME
newsetup - a program to set up a .newsrc file
.SH SYNOPSIS
.B newsetup
.SH DESCRIPTION
The
.I newsetup
program creates a new .newsrc file containing all of the currently active
newsgroups.
It tries to put them in a reasonable order, i.e. local newsgroups earlier,
but you'll probably want to change the ordering anyway (if you use
.IR rn )
in order to put interesting newsgroups first.
If you already have a .newsrc, it will be backed up with the name
\*(L".oldnewsrc\*(R".
.SH ENVIRONMENT
.IP DOTDIR 8
Where to put your .newsrc, if not in your home directory.
.Sp
Default: $HOME
.IP HOME 8
Your home directory.
.Sp
Default: $LOGDIR
.IP LOGDIR 8
Your home directory if HOME is undefined.
.SH FILES
/usr/lib/news/active or a reasonable facsimile
.br
${DOTDIR-{$HOME-$LOGDIR}}/.newsrc
.SH SEE ALSO
rn(1), newsrc(5)
.SH DIAGNOSTICS
.SH BUGS
!STUFFY!FUNK!
echo Extracting bits.h
cat >bits.h <<'!STUFFY!FUNK!'
/* $Header: bits.h,v 4.1 84/09/24 11:43:58 lwall Exp $
 *
 * $Log:	bits.h,v $
 * Revision 4.1  84/09/24  11:43:58  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:08:05  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:56  lwall
 * Baseline for netwide release
 * 
 */

EXT char *ctlarea INIT(Nullch);	/* one bit for each article in current newsgroup */
			/* with the following interpretation: */
			/*	0 => unread  */
			/*	1 => read    */

/* if subscripting is faster than shifting on your machine, define this */
#undef USESUBSCRIPT
#ifdef USESUBSCRIPT
EXT char powerof2[] INIT({1,2,4,8,16,32,64,128});
#define pow2(x) powerof2[x]
#else
#define pow2(x) (1 << (x))
#endif

#define ctl_set(a) (ctlarea[(OFFSET(a)) / BITSPERBYTE] |= pow2((OFFSET(a)) % BITSPERBYTE))
#define ctl_clear(a) (ctlarea[(OFFSET(a)) / BITSPERBYTE] &= ~pow2((OFFSET(a)) % BITSPERBYTE))
#define ctl_read(a) ((ctlarea[(OFFSET(a)) / BITSPERBYTE] & pow2((OFFSET(a)) % BITSPERBYTE)) != 0)

#define was_read(a) ((a)<firstart || ctl_read(a))

EXT ART_NUM absfirst INIT(0);	/* 1st real article in current newsgroup */
EXT ART_NUM firstart INIT(0);	/* minimum unread article number in newsgroup */
EXT ART_NUM lastart INIT(0);	/* maximum article number in newsgroup */

#ifdef DELAYMARK
EXT FILE *dmfp INIT(Nullfp);
EXT char *dmname INIT(Nullch);
EXT int dmcount INIT(0);
#endif

void	bits_init();
void	checkpoint_rc();
void	restore_ng();
void	onemore();
void	oneless();
void	unmark_as_read();
void	delay_unmark();
void	mark_as_read();
void	check_first();
#ifdef DELAYMARK
    void	yankback();
#endif
int	chase_xrefs();
int	init_ctl();
void	grow_ctl();
!STUFFY!FUNK!
echo Extracting ndir.h
cat >ndir.h <<'!STUFFY!FUNK!'
/* $Header: ndir.h,v 4.1 84/09/24 12:01:06 lwall Exp $
 *
 * $Log:	ndir.h,v $
 * Revision 4.1  84/09/24  12:01:06  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/12  17:09:03  lwall
 * Rn-ized.
 * 
 * Revision 4.0  84/09/12  17:07:28  lwall
 * *** empty log message ***
 * 
 */

#ifdef LIBNDIR
#   include <ndir.h>
#else
#   ifndef USENDIR
#	include <sys/dir.h>
#   else

#ifndef DEV_BSIZE
#define	DEV_BSIZE	512
#endif
#define DIRBLKSIZ	DEV_BSIZE
#define	MAXNAMLEN	255

struct	direct {
	long	d_ino;			/* inode number of entry */
	short	d_reclen;		/* length of this record */
	short	d_namlen;		/* length of string in d_name */
	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
};

/*
 * The DIRSIZ macro gives the minimum record length which will hold
 * the directory entry.  This requires the amount of space in struct direct
 * without the d_name field, plus enough space for the name with a terminating
 * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
 */
#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))

/*
 * Definitions for library routines operating on directories.
 */
typedef struct _dirdesc {
	int	dd_fd;
	long	dd_loc;
	long	dd_size;
	char	dd_buf[DIRBLKSIZ];
} DIR;
#ifndef NULL
#define NULL 0
#endif
extern	DIR *opendir();
extern	struct direct *readdir();
extern	long telldir();
extern	void seekdir();
#define rewinddir(dirp)	seekdir((dirp), (long)0)
extern	void closedir();

#   endif
#endif
!STUFFY!FUNK!
echo Extracting rcstuff.h
cat >rcstuff.h <<'!STUFFY!FUNK!'
/* $Header: rcstuff.h,v 4.1 84/09/24 12:06:21 lwall Exp $
 *
 * $Log:	rcstuff.h,v $
 * Revision 4.1  84/09/24  12:06:21  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:28:24  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:11  lwall
 * Baseline for netwide release
 * 
 */

EXT char *rcline[MAXRCLINE];/* pointers to lines of .newsrc */
EXT ART_UNREAD toread[MAXRCLINE];
			/* number of articles to be read in newsgroup */
			/* <0 => invalid or unsubscribed newsgroup */
#define TR_ONE ((ART_UNREAD) 1)
#define TR_NONE ((ART_UNREAD) 0)
#define TR_UNSUB ((ART_UNREAD) -1)
			/* keep this one as -1, some tests use >= TR_UNSUB */
#define TR_BOGUS ((ART_UNREAD) -2)
#define TR_JUNK ((ART_UNREAD) -3)

EXT char rcchar[MAXRCLINE]; /* holds the character : or ! while spot is \0 */
EXT char rcnums[MAXRCLINE]; /* offset from rcline to numbers on line */
EXT ACT_POS softptr[MAXRCLINE];
			/* likely ptr to active file entry for newsgroup */
EXT bool paranoid INIT(FALSE);	/* did we detect some inconsistency in .newsrc? */

bool	rcstuff_init();
bool	get_ng();	/* return TRUE if newsgroup can be found or added */
NG_NUM	add_newsgroup();
#ifdef RELOCATE
    NG_NUM	relocate_newsgroup();	/* move newsgroup around */
#endif
void	list_newsgroups();
NG_NUM	find_ng();	/* return index of newsgroup */
void	cleanup_rc();
void	sethash();
int	hash();
void	newsrc_check();
void	write_rc();
!STUFFY!FUNK!
echo Extracting newsnews.SH
cat >newsnews.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting newsnews (with variable substitutions)"
cat >newsnews <<!GROK!THIS!
			*** NEWS NEWS ***

Welcome to rn.  There are more options to rn than you want to think about, so
we won't list them here.  If you want to find out about them, read the manual
page(s).  There are some important things to remember, though:

  * Rn is not a modified readnews.  Don't expect the commands to be identical.
  * Rn runs in cbreak mode.  This means you don't have to type carriage return
    on single character commands.  (There are some multi-character commands.)
  * At ANY prompt in rn, you may type 'h' for help.  There are many different
    help menus, depending on where you are in rn.  Typing <esc>h in the
    middle of a multi-character command will list escape substitutions.
  * Typing a space to any prompt means to do the normal thing.  You could
    spend all day reading news and never hit anything but the space bar.

This particular message comes from $rnlib/newsnews.  You will only
see it once.  You news administrator should feel free to substitute his or
her own message whenever something new happens to rn, and then the file
will again be displayed, just once for each person.

Larry Wall	sdcrdcf!lwall
!GROK!THIS!
$eunicefix newsnews
!STUFFY!FUNK!
echo Extracting makedir.SH
cat >makedir.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting makedir (with variable substitutions)"
$spitshell >makedir <<!GROK!THIS!
$startsh
# $Header: makedir.SH,v 4.1 84/09/24 11:59:35 lwall Exp $
# 
# $Log:	makedir.SH,v $
# Revision 4.1  84/09/24  11:59:35  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/12  15:20:48  lwall
# Check for sh interpretation.
# 
# Revision 4.0  84/09/04  09:51:21  lwall
# Baseline for netwide release
# 

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

case \$# in
  0)
    $echo "makedir pathname filenameflag"
    exit 1
    ;;
esac

: guarantee one slash before 1st component
case \$1 in
  /*) ;;
  *)  set ./\$1 \$2 ;;
esac

: strip last component if it is to be a filename
case X\$2 in
  X1) set \`$echo \$1 | $sed 's:\(.*\)/[^/]*\$:\1:'\` ;;
  *)  set \$1 ;;
esac

: return reasonable status if nothing to be created
if $test -d "\$1" ; then
    exit 0
fi

list=''
while true ; do
    case \$1 in
    */*)
	list="\$1 \$list"
	set \`echo \$1 | $sed 's:\(.*\)/:\1 :'\`
	;;
    *)
	break
	;;
    esac
done

set \$list

for dir do
    $mkdir \$dir >/dev/null 2>&1
done
!GROK!THIS!
$eunicefix makedir
chmod 755 makedir
!STUFFY!FUNK!
echo Extracting search.h
cat >search.h <<'!STUFFY!FUNK!'
/* $Header: search.h,v 4.1 84/09/24 12:10:12 lwall Exp $
 *
 * $Log:	search.h,v $
 * Revision 4.1  84/09/24  12:10:12  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:31:38  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:42  lwall
 * Baseline for netwide release
 * 
 */

#ifndef NBRA
#define	NBRA	10		/* the maximum number of meta-brackets in an
				   RE -- \( \) */
#define NALTS	10		/* the maximum number of \|'s */
 
typedef struct {	
    char *expbuf;		/* The compiled search string */
    int eblen;			/* Length of above buffer */
    char *alternatives[NALTS];	/* The list of \| seperated alternatives */
    char *braslist[NBRA];	/* RE meta-bracket start list */
    char *braelist[NBRA];	/* RE meta-bracket end list */
    char *brastr;		/* saved match string after execute() */
    char nbra;			/* The number of meta-brackets int the most
				   recenlty compiled RE */
    bool do_folding;		/* fold upper and lower case? */
} COMPEX;

void	search_init();
void	init_compex();
void	free_compex();
char	*getbracket();
void	case_fold();
char	*compile(); 
void	grow_eb();
char	*execute(); 
bool	advance();
bool	backref(); 
bool	cclass(); 
#endif
!STUFFY!FUNK!
echo Extracting kitlists.c
cat >kitlists.c <<'!STUFFY!FUNK!'
/* $Header: kitlists.c,v 4.1 84/09/24 11:58:44 lwall Exp $
 *
 * $Log:	kitlists.c,v $
 * Revision 4.1  84/09/24  11:58:44  lwall
 * Real baseline.
 * 
 * Revision 4.0  84/09/04  09:51:03  lwall
 * Baseline for netwide release
 * 
 */

#include <stdio.h>

#define MAXKIT 100
#define MAXKITSIZE 63000
#define KITOVERHEAD 700
#define FILEOVERHEAD 80

long tot[MAXKIT];
FILE *outfp[MAXKIT];		/* of course, not this many file descriptors */

main(argc,argv)
int argc;
char **argv;
{
    FILE *inp, *popen();
    char buf[1024], filnam[128];
    char *index();
    register char *s;
    register int i, newtot;
    
    sprintf(buf,"\
ls -l `awk '{print $1}' <%s'` | awk '{print $8 \" \" $4}' | sort +1nr\
", argc > 1 ? argv[1] : "MANIFEST.new");
    inp = popen(buf,"r");

    while (fgets(buf,1024,inp) != Nullch) {
	s = index(buf,' ');
	*s++ = '\0';
	for (i=1;
	  (newtot = tot[i] + atol(s) + FILEOVERHEAD) > MAXKITSIZE-KITOVERHEAD;
	  i++) 
	    ;
	if (!tot[i]) {
	    sprintf(filnam,"kit%d.list",i);
	    outfp[i] = fopen(filnam,"w");
	}
	tot[i] = newtot;
	printf("Adding %s to kit %d giving %d bytes\n",buf,i,newtot);
	fprintf(outfp[i],"%s\n",buf);
    }
}
!STUFFY!FUNK!
echo Extracting last.c
cat >last.c <<'!STUFFY!FUNK!'
/* $Header: last.c,v 4.1 84/09/24 11:59:07 lwall Exp $
 *
 * $Log:	last.c,v $
 * Revision 4.1  84/09/24  11:59:07  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:16:19  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:11  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "util.h"
#include "intrp.h"
#include "INTERN.h"
#include "last.h"

char *lastname = Nullch;	/* path name of .rnlast file */

void
last_init(tcbuf)
char *tcbuf;
{
    lastname = savestr(filexp(LASTNAME));
    if ((tmpfp = fopen(lastname,"r")) != Nullfp) {
	fgets(tcbuf,1024,tmpfp);
	tcbuf[strlen(tcbuf)-1] = '\0';
	lastngname = savestr(tcbuf);
	fgets(tcbuf,1024,tmpfp);
	lasttime = atoi(tcbuf);
	fgets(tcbuf,1024,tmpfp);
	lastactsiz = atoi(tcbuf);
	fclose(tmpfp);
    }
    else {
	lastngname = nullstr;
	lasttime = 0;
	lastactsiz = 0;
    }
}

/* put out certain values for next run of rn */

void
writelast()
{
    if ((tmpfp = fopen(lastname,"w")) != Nullfp) {
	fprintf(tmpfp,"%s\n%ld\n%ld\n",ngname,(long)lasttime,(long)lastactsiz);
	fclose(tmpfp);
    }
}
!STUFFY!FUNK!
echo ""
echo "End of kit 7 (of 8)"
cat /dev/null >kit7isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit

lwall@sdcrdcf.UUCP (Larry Wall) (09/25/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 8 (of 8).  If kit 8 is complete, the line"
echo '"'"End of kit 8 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting mbox.saver.SH
cat >mbox.saver.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting mbox.saver (with variable substitutions)"
$spitshell >mbox.saver <<!GROK!THIS!
$startsh
# $Header: mbox.saver.SH,v 4.1 84/09/24 12:00:56 lwall Exp $
# 
# $Log:	mbox.saver.SH,v $
# Revision 4.1  84/09/24  12:00:56  lwall
# Real baseline.
# 
# Revision 4.0.1.2  84/09/12  15:21:16  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.1  84/09/04  15:09:34  lwall
# Fixed for Eunice "symbolic" links.
# 
# Revision 4.0  84/09/04  09:51:29  lwall
# Baseline for netwide release
# 
# 
#	Arguments:
#	1 Full name of article (%A)
#	2 Public news spool directory (%P)
#	3 Directory of current newsgroup (%c)
#	4 Article number (%a)
#	5 Where in article to start (%B)
#	6 Newsgroup name (%C)
#	7 Save destination (%b)
#	8 First line of message, normally From...
#
export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

( $echo "\$8"
  $echo "Article \$4 of \$6:"
  $tail +\$5c \$1 | $sed "s/^From/>From/"
  $echo ""
  $echo "" ) >> \$7
!GROK!THIS!
$eunicefix mbox.saver
chmod 755 mbox.saver
!STUFFY!FUNK!
echo Extracting norm.saver.SH
cat >norm.saver.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting norm.saver (with variable substitutions)"
$spitshell >norm.saver <<!GROK!THIS!
$startsh
# $Header: norm.saver.SH,v 4.1 84/09/24 12:03:58 lwall Exp $
# 
# $Log:	norm.saver.SH,v $
# Revision 4.1  84/09/24  12:03:58  lwall
# Real baseline.
# 
# Revision 4.0.1.3  84/09/12  15:24:45  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.2  84/09/06  11:33:39  lwall
# Fixed last fix.
# 
# Revision 4.0.1.1  84/09/04  15:10:13  lwall
# Fixed for Eunice "symbolic" links.
# 
# Revision 4.0  84/09/04  09:51:55  lwall
# Baseline for netwide release
# 
# 
#	Arguments:
#	1 Full name of article (%A)
#	2 Public news spool directory (%P)
#	3 Directory of current newsgroup (%c)
#	4 Article number (%a)
#	5 Where in article to start (%B)
#	6 Newsgroup name (%C)
#	7 Save destination (%b)
#
export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

( $echo "Article \$4 of \$6:"
  $tail +\$5c \$1
  $echo ""
  $echo "" ) >> \$7
!GROK!THIS!
$eunicefix norm.saver
chmod 755 norm.saver
!STUFFY!FUNK!
echo Extracting artsrch.h
cat >artsrch.h <<'!STUFFY!FUNK!'
/* $Header: artsrch.h,v 4.1 84/09/24 11:42:37 lwall Exp $
 *
 * $Log:	artsrch.h,v $
 * Revision 4.1  84/09/24  11:42:37  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:05:40  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:37  lwall
 * Baseline for netwide release
 * 
 */

#ifndef NBRA
#include "search.h"
#endif

#ifdef ARTSEARCH

#define SRCH_ABORT 0
#define SRCH_INTR 1
#define SRCH_FOUND 2
#define SRCH_NOTFOUND 3
#define SRCH_DONE 4
#define SRCH_SUBJDONE 5
#define SRCH_ERROR 6
#endif

EXT char *lastpat INIT(nullstr);	/* last search pattern */
#ifdef ARTSEARCH
    EXT COMPEX sub_compex;		/* last compiled subject search */
    EXT COMPEX art_compex;		/* last compiled normal search */
#   ifdef CONDSUB
	EXT COMPEX *bra_compex INIT(&art_compex);
					/* current compex with brackets */
#   endif
    EXT char art_howmuch;		/* search just the subjects */
    EXT bool art_doread;		/* search read articles? */
#endif

void	artsrch_init();
#ifdef ARTSEARCH
    int		art_search();
    bool	wanted();	/* return TRUE if current article matches pattern */
#endif
!STUFFY!FUNK!
echo Extracting ng.h
cat >ng.h <<'!STUFFY!FUNK!'
/* $Header: ng.h,v 4.1 84/09/24 12:02:37 lwall Exp $
 *
 * $Log:	ng.h,v $
 * Revision 4.1  84/09/24  12:02:37  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:19:40  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:42  lwall
 * Baseline for netwide release
 * 
 */

EXT ART_NUM art INIT(0);	/* current or prospective article # */

EXT int checkcount INIT(0);	/* how many articles have we read */
			/*   in the current newsgroup since */
			/*   the last checkpoint? */
EXT int docheckwhen INIT(20);	/* how often to do checkpoint */

#ifdef MAILCALL
EXT int mailcount INIT(0);			/* check for mail when 0 mod 10 */
#endif
EXT char *mailcall INIT(nullstr);

EXT bool forcelast INIT(FALSE);			/* ought we show "End of newsgroup"? */
EXT bool forcegrow INIT(FALSE);		/* do we want to recalculate size */
				    /* of newsgroup, e.g. after posting? */

void    ng_init();
int	do_newsgroup();
int	art_switch();
#ifdef MAILCALL
    void	setmail();
#endif
void	setdfltcmd();
!STUFFY!FUNK!
echo Extracting intrp.h
cat >intrp.h <<'!STUFFY!FUNK!'
/* $Header: intrp.h,v 4.1 84/09/24 11:58:13 lwall Exp $
 *
 * $Log:	intrp.h,v $
 * Revision 4.1  84/09/24  11:58:13  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/10  15:15:55  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/06  08:21:04  lwall
 * Swiped sitename from respond.h.
 * 
 * Revision 4.0  84/09/04  09:50:54  lwall
 * Baseline for netwide release
 * 
 */

EXT char *lib INIT(Nullch);		/* news library */
EXT char *rnlib INIT(Nullch);		/* private news program library */
EXT char *origdir INIT(Nullch);		/* cwd when rn invoked */
EXT char *homedir INIT(Nullch);		/* login directory */
EXT char *dotdir INIT(Nullch);		/* where . files go */
EXT char *logname INIT(Nullch);		/* login id */
EXT char *sitename INIT(Nullch);	/* host name */

#ifdef NEWSADMIN
    EXT char newsadmin[] INIT(NEWSADMIN);/* news administrator */
    EXT int newsuid INIT(0);
#endif

void    intrp_init();
char	*filexp();
char	*dointerp();
void	interp();
void	refscpy();
char	*getrealname();
!STUFFY!FUNK!
echo Extracting util.h
cat >util.h <<'!STUFFY!FUNK!'
/* $Header: util.h,v 4.1 84/09/24 12:11:33 lwall Exp $
 *
 * $Log:	util.h,v $
 * Revision 4.1  84/09/24  12:11:33  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:35:28  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:53:08  lwall
 * Baseline for netwide release
 * 
 */

EXT bool waiting INIT(FALSE);		/* are we waiting for subprocess (in doshell)? */
EXT int len_last_line_got INIT(0);
			/* strlen of some_buf after */
			/*  some_buf = get_a_line(bufptr,buffersize,fp) */

/* is the string for makedir a directory name or a filename? */

#define MD_DIR 0
#define MD_FILE 1

void	util_init();
int	doshell();
char	*safemalloc();
char	*saferealloc();
char	*safecpy();
char	*safecat();
char	*cpytill();
char	*instr();
#ifdef SETUIDGID
    int		eaccess();
#endif
char	*getwd();
void	cat();
void	prexit();
char	*get_a_line();
char	*savestr();
int	makedir();
void	setenv();
int	envix();
void	notincl();
char	*getval();
void	growstr();
void	setdef();
!STUFFY!FUNK!
echo Extracting rn.h
cat >rn.h <<'!STUFFY!FUNK!'
/* $Header: rn.h,v 4.1 84/09/24 12:07:44 lwall Exp $
 *
 * $Log:	rn.h,v $
 * Revision 4.1  84/09/24  12:07:44  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:29:46  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:20  lwall
 * Baseline for netwide release
 * 
 */

EXT char *ngname INIT(Nullch);		/* name of current newsgroup */
EXT int ngnlen INIT(0);			/* current malloced size of ngname */
EXT char *ngdir INIT(Nullch);		/* same thing in directory name form */
EXT int ngdlen INIT(0);			/* current malloced size of ngdir */

EXT NG_NUM ng INIT(0);		/* current newsgroup index into rcline and toread */
EXT NG_NUM current_ng INIT(0);	/* stable current newsgroup so we can ditz with ng */
EXT NG_NUM starthere INIT(0);   /* set to the first newsgroup with unread news on startup */
EXT char *spool INIT(Nullch);		/* public news spool directory */

void	rn_init();
void	main();
void	set_ngname();
char	*getngdir();
!STUFFY!FUNK!
echo Extracting kittrailer
cat >kittrailer <<'!STUFFY!FUNK!'
#! /bin/sh
# $Header: kittrailer,v 4.1 84/09/24 11:58:57 lwall Exp $
#
# $Log:	kittrailer,v $
# Revision 4.1  84/09/24  11:58:57  lwall
# Real baseline.
# 
# Revision 4.0.1.2  84/09/07  08:31:06  lwall
# Added exit at end in case of .signature.
# 
# Revision 4.0.1.1  84/09/05  09:28:36  lwall
# Made use sh instead of csh.
# 
# Revision 4.0  84/09/04  09:51:08  lwall
# Baseline for netwide release
# 

rangelist=`range 1 $3`
cat >>$1 <<EOT
echo ""
echo "End of kit $2 (of $3)"
cat /dev/null >kit${2}isdone
config=true
for iskit in $rangelist; do
    if test -f kit\${iskit}isdone; then
	echo "You have run kit \${iskit}."
    else
	echo "You still need to run kit \${iskit}."
	config=false
    fi
done
case \$config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit
EOT
!STUFFY!FUNK!
echo Extracting makekit
cat >makekit <<'!STUFFY!FUNK!'
#!/bin/sh
# $Header: makekit,v 4.1 84/09/24 11:59:59 lwall Exp $
#
# $Log:	makekit,v $
# Revision 4.1  84/09/24  11:59:59  lwall
# Real baseline.
# 
# Revision 4.0.1.1  84/09/07  08:38:16  lwall
# Uses sed if file contains an orphan '.'.
# 
# Revision 4.0  84/09/04  09:51:24  lwall
# Baseline for netwide release
# 

numkits=$#
for kitlist in $*; do
    kit=`basename $kitlist .list`
    kitnum=`expr "$kit" : 'kit\([0-9][0-9]*\)'`
    echo "*** Making $kit ***"
    kitleader "$kit" "$kitnum" "$numkits"
    for file in `/bin/cat $kitlist`; do
	echo $file
	echo "echo Extracting $file" >> $kit
	if egrep '^\.$' $file; then
	    echo "sed >$file <<'!STUFFY!FUNK!' -e 's/X//'" >> $kit
	    sed <$file >>$kit -e 's/^/X/'
	else
	    echo "cat >$file <<'!STUFFY!FUNK!'" >> $kit
	    /bin/cat $file >> $kit
	fi
	echo "!STUFFY!FUNK!" >> $kit
    done
    kittrailer "$kit" "$kitnum" "$numkits"
done
!STUFFY!FUNK!
echo Extracting artstate.h
cat >artstate.h <<'!STUFFY!FUNK!'
/* $Header: artstate.h,v 4.1 84/09/24 11:42:44 lwall Exp $
 *
 * $Log:	artstate.h,v $
 * Revision 4.1  84/09/24  11:42:44  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:05:48  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:43  lwall
 * Baseline for netwide release
 * 
 */

EXT bool reread INIT(FALSE);		/* consider current art temporarily */
				    /* unread? */
EXT bool do_fseek INIT(FALSE);	/* should we back up in article file? */

EXT bool oldsubject INIT(FALSE);	/* not 1st art in subject thread */
EXT ART_LINE topline INIT(-1);		/* top line of current screen */
EXT bool do_hiding INIT(TRUE);		/* hide header lines with -h? */
#ifdef ROTATION
EXT bool rotate INIT(FALSE);		/* has rotation been requested? */
#endif
EXT char *prompt;			/* pointer to current prompt */

EXT char *firstline INIT(Nullch);			/* special first line? */
!STUFFY!FUNK!
echo Extracting artio.h
cat >artio.h <<'!STUFFY!FUNK!'
/* $Header: artio.h,v 4.1 84/09/24 11:42:11 lwall Exp $
 *
 * $Log:	artio.h,v $
 * Revision 4.1  84/09/24  11:42:11  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/10  15:05:21  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/04  15:14:25  lwall
 * LINKART option for Eunice sites.
 * 
 * Revision 4.0  84/09/04  09:49:33  lwall
 * Baseline for netwide release
 * 
 */

EXT ART_POS artpos INIT(0);	/* byte position in article file */

EXT ART_LINE artline INIT(0);		/* current line number in article file */
EXT FILE *artfp INIT(Nullfp);		/* current article file pointer */
EXT ART_NUM openart INIT(0);		/* what is the currently open article number? */
#ifdef LINKART
    EXT char *linkartname INIT(nullstr);/* real name of article for Eunice */
#endif

void	artio_init();
FILE	*artopen();			/* open an article unless already opened */
!STUFFY!FUNK!
echo Extracting final.h
cat >final.h <<'!STUFFY!FUNK!'
/* $Header: final.h,v 4.1 84/09/24 11:47:00 lwall Exp $
 * 
 * $Log:	final.h,v $
 * Revision 4.1  84/09/24  11:47:00  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:11:00  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:16  lwall
 * Baseline for netwide release
 * 
 */

/* cleanup status for fast exits */

EXT bool panic INIT(FALSE);		/* we got hung up or something-- */
					/*  so leave tty alone */
EXT bool rc_changed INIT(FALSE);	/* need we rewrite .newsrc? */
EXT bool doing_ng INIT(FALSE);		/* do we need to reconstitute */
					/* current rc line? */

EXT char int_count INIT(0);		/* how many interrupts we've had */

/* signal catching routines */

void	int_catcher();
void	sig_catcher();
#ifdef SIGTSTP
    void	stop_catcher();
    void	cont_catcher();
#endif

void	final_init();
void	finalize();
!STUFFY!FUNK!
echo Extracting kitleader
cat >kitleader <<'!STUFFY!FUNK!'
#! /bin/sh
# $Header: kitleader,v 4.1 84/09/24 11:58:35 lwall Exp $
#
# $Log:	kitleader,v $
# Revision 4.1  84/09/24  11:58:35  lwall
# Real baseline.
# 
# Revision 4.0.1.3  84/09/12  15:39:33  lwall
# Check for sh interpretation.
# 
# Revision 4.0.1.2  84/09/07  09:06:02  lwall
# Anti-clunch check.
# 
# Revision 4.0.1.1  84/09/05  09:28:02  lwall
# Made use sh instead of csh.
# 
# Revision 4.0  84/09/04  09:50:59  lwall
# Baseline for netwide release
# 

cat >$1 <<EOT
#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru $3 
# through sh.  When all $3 kits have been run, read README.

echo "This is rn kit $2 (of $3).  If kit $2 is complete, the line"
echo '"'"End of kit $2 (of $3)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill \$\$)
EOT
!STUFFY!FUNK!
echo Extracting Wishlist
cat >Wishlist <<'!STUFFY!FUNK!'
Generalized article set manipulation, with interface to subject listing.
Separation of .newsrc functions and newsgroup functions to separate processes
	communicating via pipes (to make fit on non-separate-I-and-D pdp11,
	or unreasonable facsimiles).
Personal archive perusal.
%`command`, %"prompt".
Merge Pnews and postnews.  (Add -h to postnews, mostly.)
Virtual article abstract type to allow personalized header munging via % subs.
Vnews duplicate suppression algorithm for sites that can't do Xref patch.
Parent command.
Dynamic allocation of stuff currently restricted by MAXRCLINE.
	(And pull parallel arrays into array of structs).
Buffer overflow checking on % subs.
Allow CR optionally.
Macros?
Keymaps?
Faster!!!
Smaller!!!
More general!!!
!STUFFY!FUNK!
echo Extracting manimake
cat >manimake <<'!STUFFY!FUNK!'
#!/bin/sh
# $Header: manimake,v 4.1 84/09/24 12:00:48 lwall Exp $
#
# $Log:	manimake,v $
# Revision 4.1  84/09/24  12:00:48  lwall
# Real baseline.
# 
# Revision 4.0  84/09/04  09:51:28  lwall
# Baseline for netwide release
# 

: make MANIFEST and MANIFEST.pre say the same thing
if test -f MANIFEST.new; then
    cat <<'EOH' > MANIFEST
After all the rn kits are run you should have the following files:

Filename	Kit Description
--------	--- -----------
EOH
    sort MANIFEST.new >.mani
    grep . kit*.list | sed 's/^kit\(.*\)\.list:\$*\(.*\)$/\2 |\1|/' | \
	sort | \
	join -a1 - .mani | \
	awk -F'|' '{printf "%-16s%2s %s\n",$1,$2,$3}' | \
	unexpand >> MANIFEST
    rm .mani
else
    echo "You don't have a MANIFEST.new file.  Run manifake."
fi
!STUFFY!FUNK!
echo Extracting ngdata.h
cat >ngdata.h <<'!STUFFY!FUNK!'
/* $Header: ngdata.h,v 4.1 84/09/24 12:03:00 lwall Exp $
 *
 * $Log:	ngdata.h,v $
 * Revision 4.1  84/09/24  12:03:00  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:20:30  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:46  lwall
 * Baseline for netwide release
 * 
 */

EXT FILE *actfp INIT(Nullfp);	/* the active file */
EXT bool writesoft INIT(FALSE);	/* rewrite the soft pointer file? */
EXT int softtries INIT(0), softmisses INIT(0);

#ifdef CACHEFIRST
    EXT ART_NUM abs1st[MAXRCLINE];	/* 1st real article in newsgroup */
#else
# ifdef MININACT
    EXT ART_NUM abs1st INIT(0);
# endif
#endif


void	ngdata_init();
ART_NUM	getngsize();
ART_NUM	getabsfirst();
ART_NUM	getngmin();
!STUFFY!FUNK!
echo Extracting respond.h
cat >respond.h <<'!STUFFY!FUNK!'
/* $Header: respond.h,v 4.1 84/09/24 12:07:03 lwall Exp $
 *
 * $Log:	respond.h,v $
 * Revision 4.1  84/09/24  12:07:03  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/10  15:28:54  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/06  08:21:24  lwall
 * Gave sitename to interp.h.
 * 
 * Revision 4.0  84/09/04  09:52:15  lwall
 * Baseline for netwide release
 * 
 */

EXT char *savedest INIT(Nullch);	/* value of %b */
EXT ART_POS savefrom INIT(0);		/* value of %B */
EXT char *headname INIT(Nullch);

#define SAVE_ABORT 0
#define SAVE_DONE 1

void	respond_init();
int	save_article();
int	cancel_article();
void	reply();
void	followup();
void	invoke();
!STUFFY!FUNK!
echo Extracting header.h.pat
cat >header.h.pat <<'!STUFFY!FUNK!'
NOTE: the preceding context may be different under 2.10.2, but you should
be able to figure it out anyway.

*** header.old.h	Fri Apr 27 11:30:49 1984
--- header.h	Thu May 10 15:19:55 1984
***************
*** 34,38
  	int	intnumlines;		/* Integer version	*/
  	char	keywords[BUFLEN];	/* Keywords:		*/
  	char	approved[BUFLEN];	/* Approved:		*/
  	char	*unrec[NUNREC];		/* unrecognized lines	*/
  };

--- 34,41 -----
  	int	intnumlines;		/* Integer version	*/
  	char	keywords[BUFLEN];	/* Keywords:		*/
  	char	approved[BUFLEN];	/* Approved:		*/
+ #ifdef DOXREFS
+ 	char	xref[BUFLEN];		/* Xref:		*/
+ #endif
  	char	*unrec[NUNREC];		/* unrecognized lines	*/
  };
!STUFFY!FUNK!
echo Extracting kfile.h
cat >kfile.h <<'!STUFFY!FUNK!'
/* $Header: kfile.h,v 4.1 84/09/24 11:58:29 lwall Exp $
 *
 * $Log:	kfile.h,v $
 * Revision 4.1  84/09/24  11:58:29  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:16:14  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:57  lwall
 * Baseline for netwide release
 * 
 */

#define KF_GLOBAL 0
#define KF_LOCAL 1

#ifdef KILLFILES
EXT FILE *globkfp INIT(Nullfp);		/* global article killer file */
EXT FILE *localkfp INIT(Nullfp);	/* local (for this newsgroup) */
					/*  article killer file */
#endif

void	kfile_init();
int	do_kfile();
void	kill_unwanted();
int	edit_kfile();
void	open_kfile();
void    kf_append();
void	setthru();

!STUFFY!FUNK!
echo Extracting backpage.h
cat >backpage.h <<'!STUFFY!FUNK!'
/* $Header: backpage.h,v 4.1 84/09/24 11:43:08 lwall Exp $
 *
 * $Log:	backpage.h,v $
 * Revision 4.1  84/09/24  11:43:08  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:06:36  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:50  lwall
 * Baseline for netwide release
 * 
 */

/* things for doing the 'back page' command */

EXT int varyfd INIT(0);			/* virtual array file for storing  */
					/* file offsets */
EXT ART_POS varybuf[VARYSIZE];		/* current window onto virtual array */

EXT long oldoffset INIT(-1);		/* offset to block currently in window */

void	backpage_init();
ART_POS	vrdary();
void	vwtary();
!STUFFY!FUNK!
echo Extracting rcln.h
cat >rcln.h <<'!STUFFY!FUNK!'
/* $Header: rcln.h,v 4.1 84/09/24 12:04:59 lwall Exp $
 *
 * $Log:	rcln.h,v $
 * Revision 4.1  84/09/24  12:04:59  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.2  84/09/13  11:19:19  lwall
 * addartnum() now returns int.
 * 
 * Revision 4.0.1.1  84/09/10  15:24:05  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:05  lwall
 * Baseline for netwide release
 * 
 */

#ifdef DEBUGGING
ART_NUM ngmax[MAXRCLINE];
#endif

void    rcln_init();
#ifdef CATCHUP
    void	catch_up();
#endif
int	addartnum();
#ifdef MCHASE
    void	subartnum();
#endif
void	prange();
void	set_toread();
void	checkexpired();
!STUFFY!FUNK!
echo Extracting ngsrch.h
cat >ngsrch.h <<'!STUFFY!FUNK!'
/* $Header: ngsrch.h,v 4.1 84/09/24 12:03:13 lwall Exp $
 *
 * $Log:	ngsrch.h,v $
 * Revision 4.1  84/09/24  12:03:13  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:21:01  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:49  lwall
 * Baseline for netwide release
 * 
 */

#ifdef NGSEARCH
#define NGS_ABORT 0
#define NGS_FOUND 1
#define NGS_INTR 2
#define NGS_NOTFOUND 3

EXT bool ng_doread INIT(FALSE);		/* search read newsgroups? */
#endif

void	ngsrch_init();
#ifdef NGSEARCH 
    int		ng_search();
    bool	ng_wanted();
#endif
#ifdef NGSORONLY
    char	*ng_comp();
#endif
!STUFFY!FUNK!
echo Extracting cheat.h
cat >cheat.h <<'!STUFFY!FUNK!'
/* $Header: cheat.h,v 4.1 84/09/24 11:44:21 lwall Exp $
 *
 * $Log:	cheat.h,v $
 * Revision 4.1  84/09/24  11:44:21  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:08:49  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:07  lwall
 * Baseline for netwide release
 * 
 */

#ifdef ARTSEARCH
EXT ART_NUM srchahead INIT(0); 	/* are we in subject scan mode? */
				/* (if so, contains art # found or -1) */
#endif

#ifdef PENDING
#   ifdef CACHESUBJ
	EXT ART_NUM subj_to_get;
#   endif
#endif

void	cheat_init();
void	look_ahead();
void	collect_subjects();
!STUFFY!FUNK!
echo Extracting last.h
cat >last.h <<'!STUFFY!FUNK!'
/* $Header: last.h,v 4.1 84/09/24 11:59:17 lwall Exp $
 *
 * $Log:	last.h,v $
 * Revision 4.1  84/09/24  11:59:17  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:16:28  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:14  lwall
 * Baseline for netwide release
 * 
 */

EXT char *lastngname INIT(Nullch);	/* last newsgroup read, from .rnlast file */
EXT long lasttime INIT(0);	/* time last rn was started up */
EXT long lastactsiz INIT(0);	/* size of active file when rn last started up */

void	last_init();
void    writelast();
!STUFFY!FUNK!
echo Extracting manifake
cat >manifake <<'!STUFFY!FUNK!'
#!/bin/sh
# $Header: manifake,v 4.1 84/09/24 12:00:12 lwall Exp $
#
# $Log:	manifake,v $
# Revision 4.1  84/09/24  12:00:12  lwall
# Real baseline.
# 
# Revision 4.0  84/09/04  09:51:25  lwall
# Baseline for netwide release
# 

: make MANIFEST and MANIFEST.new say the same thing
if test ! -f MANIFEST.new; then
    if test -f MANIFEST; then
	sed <MANIFEST >MANIFEST.new \
	    -e '1,/---/d' \
	    -e 's/\([ 	][ 	]*\)[0-9]*  */\1/'
    else
	echo "Make a MANIFEST.new file, with names and descriptions."
    fi
fi
!STUFFY!FUNK!
echo Extracting art.h
cat >art.h <<'!STUFFY!FUNK!'
/* $Header: art.h,v 4.1 84/09/24 11:41:29 lwall Exp $
 *
 * $Log:	art.h,v $
 * Revision 4.1  84/09/24  11:41:29  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:04:54  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:27  lwall
 * Baseline for netwide release
 * 
 */

/* do_article() return values */

#define DA_NORM 0
#define DA_RAISE 1
#define DA_CLEAN 2
#define DA_TOEND 3

void	art_init();
int	do_article();
int	page_switch();
bool	innermore();
!STUFFY!FUNK!
echo Extracting ngstuff.h
cat >ngstuff.h <<'!STUFFY!FUNK!'
/* $Header: ngstuff.h,v 4.1 84/09/24 12:03:43 lwall Exp $
 *
 * $Log:	ngstuff.h,v $
 * Revision 4.1  84/09/24  12:03:43  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:22:05  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:51:53  lwall
 * Baseline for netwide release
 * 
 */

#define NN_NORM 0
#define NN_INP 1
#define NN_REREAD 2
#define NN_ASK 3

void	ngstuff_init();
int	escapade();
int	switcheroo();
int	numnum();
int	perform();
!STUFFY!FUNK!
echo Extracting sw.h
cat >sw.h <<'!STUFFY!FUNK!'
/* $Header: sw.h,v 4.1 84/09/24 12:10:35 lwall Exp $
 *
 * $Log:	sw.h,v $
 * Revision 4.1  84/09/24  12:10:35  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:33:02  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:50  lwall
 * Baseline for netwide release
 * 
 */

#ifdef INNERSEARCH
EXT int gline INIT(0);
#endif

void    sw_init();
void    sw_list();
void	decode_switch();
void	pr_switches();
void	cwd_check();
!STUFFY!FUNK!
echo Extracting init.h
cat >init.h <<'!STUFFY!FUNK!'
/* $Header: init.h,v 4.1 84/09/24 11:56:56 lwall Exp $
 *
 * $Log:	init.h,v $
 * Revision 4.1  84/09/24  11:56:56  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:13:28  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:47  lwall
 * Baseline for netwide release
 * 
 */

EXT char *lockname INIT(nullstr);

bool	initialize();
void	lock_check();
void	newsnews_check();
void	version_check();
!STUFFY!FUNK!
echo Extracting help.h
cat >help.h <<'!STUFFY!FUNK!'
/* $Header: help.h,v 4.1 84/09/24 11:48:51 lwall Exp $
 *
 * $Log:	help.h,v $
 * Revision 4.1  84/09/24  11:48:51  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  15:12:29  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:50:30  lwall
 * Baseline for netwide release
 * 
 */

void	help_init();
void	help_ng();
void	help_art();
void	help_page();
#ifdef ESCSUBS
    void	help_subs();
#endif
!STUFFY!FUNK!
echo Extracting addng.h
cat >addng.h <<'!STUFFY!FUNK!'
/* $Header: addng.h,v 4.1 84/09/24 11:39:57 lwall Exp $
 *
 * $Log:	addng.h,v $
 * Revision 4.1  84/09/24  11:39:57  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  14:57:35  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:49:20  lwall
 * Baseline for netwide release
 * 
 */

void	addng_init();
#ifdef FINDNEWNG
    bool	newlist();
    long	birthof();
    bool	scanactive();
#endif
!STUFFY!FUNK!
echo Extracting EXTERN.h
cat >EXTERN.h <<'!STUFFY!FUNK!'
/* $Header: EXTERN.h,v 4.1 84/09/24 11:36:47 lwall Exp $
 *
 * $Log:	EXTERN.h,v $
 * Revision 4.1  84/09/24  11:36:47  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  14:57:16  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:48:26  lwall
 * Baseline for netwide release
 * 
 */

#undef EXT
#define EXT extern

#undef INIT
#define INIT(x)

#undef DOINIT
!STUFFY!FUNK!
echo Extracting INTERN.h
cat >INTERN.h <<'!STUFFY!FUNK!'
/* $Header: INTERN.h,v 4.1 84/09/24 11:37:08 lwall Exp $
 *
 * $Log:	INTERN.h,v $
 * Revision 4.1  84/09/24  11:37:08  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.1  84/09/10  14:57:30  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:48:33  lwall
 * Baseline for netwide release
 * 
 */

#undef EXT
#define EXT

#undef INIT
#define INIT(x) = x

#define DOINIT
!STUFFY!FUNK!
echo ""
echo "End of kit 8 (of 8)"
cat /dev/null >kit8isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit