Article 2706 of comp.sources.unix:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!bloom-beacon.mit.edu!news.kei.com!news.mathworks.com!newsfeed.internetmci.com!in1.uu.net!news1.digital.com!vixie!vixie!not-for-mail
From: davison@borland.com (Wayne Davison)
Newsgroups: comp.sources.unix
Subject: v29i050: trn-3.6 - threaded newsreader based on RN, V3.6, Part13/14
Date: 29 Oct 1995 01:33:05 -0800
Organization: Vixie Enterprises
Lines: 2952
Sender: vixie@vix.com
Approved: vixie@gw.home.vix.com
Message-ID: <46vhoh$t5h@gw.home.vix.com>
References: <1.814959141.29825@gw.home.vix.com>
NNTP-Posting-Host: gw.home.vix.com

Submitted-By: davison@borland.com (Wayne Davison)
Posting-Number: Volume 29, Issue 50
Archive-Name: trn-3.6/part13

#!/bin/sh
# This is `part13' (part 13 of a multipart archive).
# Do not concatenate these parts, unpack them in order with `/bin/sh'.
# File `trn-3.6/trn.1' is being continued...
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if test ! -r _sharseq.tmp; then
  echo 'Please unpack part 1 first!'
  exit 1
fi
shar_sequence=`cat _sharseq.tmp`
if test "$shar_sequence" != 13; then
  echo "Please unpack part $shar_sequence next!"
  exit 1
fi
if test ! -f _sharnew.tmp; then
  echo 'x - still skipping trn-3.6/trn.1'
else
  echo 'x - continuing file trn-3.6/trn.1'
  sed 's/^X//' << 'SHAR_EOF' >> 'trn-3.6/trn.1' &&
X.I .newsrc
Xsorted into order of interest,
X.B \-s5
Xwill tell you the 5 most interesting newsgroups that have unread news.
XThis is also a nice feature to use in your
X.I .login
Xfile, since it not only tells you whether there is unread news, but also how
Ximportant the unread news is, without having to wade through the entire
Xlist of unread newsgroups.
XIf no 
X.B \-s
Xswitch is given 
X.B \-s5
Xis assumed, so just putting \*(L"rn \-c\*(R"
Xinto your
X\&.login file is fine.
X.TP 5
X.B \-S<number>
Xcauses
X.I trn
Xto enter subject search mode (^N) automatically whenever an unthreaded
Xnewsgroup is
Xstarted up with <number> unread articles or more.
XAdditionally, it causes any \*(L'n\*(R' typed while in subject search mode
Xto be interpreted as \*(L'^N\*(R' instead.
X(To get back out of subject search mode, the best command is probably
X\&\*(L'^\*(R'.)
XIf <number> is omitted, 3 is assumed.
X.TP 5
X.B \-t
Xputs
X.I trn
Xinto terse mode.
XThis is more cryptic but useful for low baud rates.
X(Note that your system administrator may have compiled
X.I trn
Xwith either verbose or terse messages only to save memory.)
XYou may wish to use the baud-rate switch modifier below to enable terse mode
Xonly at lower baud rates.
X.TP 5
X.B \-T
Xallows you to type ahead of trn.
XOrdinarily trn will eat typeahead to prevent your autorepeating space bar from
Xdoing a very frustrating thing when you accidentally hold it down.
XIf you don't have a repeating space bar, or you are working at low baud
Xrate, you can set this switch to prevent this behavior.
XYou may wish to use the baud-rate switch modifier below to disable typeahead
Xonly at lower baud rates.
X.TP 5
X.B \-u
Xsets the unbroken-subject-line mode in the selector, which simply truncates
Xsubjects that are too long instead of dumping the middle portion prior to
Xthe last two words of the subject.
X.TP 5
X.B \-U
Xtells trn to not write the .newsrc file out after visiting each group.
XWhile this is \*(L"unsafe\*(R" it can be faster if you have a really
Xhuge .newsrc.
X.TP 5
X.B \-v
Xsets verification mode for commands.
XWhen set, the command being executed is displayed to give some feedback that
Xthe key has actually been typed.
XUseful when the system is heavily loaded and you give a command that takes
Xa while to start up.
X.TP 5
X.B \-V
Xwill output trn's version number and quit.
X.TP 5
X.B \-x{<number>}{<list>}
XEnable the extended (threaded) features of
X.I trn
Xbeyond the
X.I rn
Xcompatibility mode
X(this may be the default on your system, use +x if you yearn for the good
Xol' days).
XThe <number> is the maximum number of article-tree lines (from 0 to 11)
Xyou want displayed in your header.
XUse the <list> to choose which thread selector styles you like
X(\*(L's\*(R'hort, \*(L'm\*(R'edium, or \*(L'l\*(R'ong), and in what order
Xthey are selected with the \*(L'L\*(R' command.
XFor example, use
X.B \-xms
Xto start with the medium display mode and only switch between it and
Xthe short mode.
XYou can omit either or both of the parameters, in which case a default of
X.B \-x6lms
Xis assumed.
X.TP 5
X.B \-X{<number>}{<commands>}
XIf you like using the selector, you'll probably want to use this
Xoption to make the selector command (+) the default when a newsgroup
Xis started up with at least <number> unread articles.
X(Your installer may have chosen to make -X1 the default on your system.)
XIt is also used to select which commands you want to be the defaults while
Xusing the thread selector.
XFor example,
X.B \-X2XD
Xwill make the thread selector the default command for entering a newsgroup
Xwith at least 2 unread articles, and set the default command for the LAST
Xpage of the thread selector to be the
X.B X
Xcommand and the default command for all other pages to be the
X.B D
Xcommand.
XEither or both parameters can be omitted, as well as the second default
Xcommand (e.\|g.
X.B \-XX
Xwould change the default newsgroup entry to use the selector and the default
Xcommand for the last page of the selector to be \*(L'X\*(R').
XThe default is
X.B \-X1Z>
Xif just
X.B \-X
Xis specified.
XTo set the default selector commands without having \*(L'+\*(R' be the
Xdefault entry into a newsgroup, specify a high number, like 9999.
X.TP 5
X.B \-z
Xsets the minimum number of minutes that must elapse before the active file
Xis refetched to look for new articles.
XA value of 0 or using +z turns this off.
X.TP 5
X.B \-Z
Xis used to select what style of database you want trn to access.
XUse \-Zt for thread files, \-Zo for overview files, and +Z for none.
XThe default is whatever your newsadmin compiled into trn, and can
Xbe \-Zot to try to access either one.
X.TP 5
X.B \-/
Xsets SAVEDIR to \*(L"%p/%c\*(R" and SAVENAME to \*(L"%a\*(R", which means
Xthat by default articles are saved in a subdirectory of your private news
Xdirectory corresponding to the name of the the current newsgroup, with the
Xfilename being the article number.
X.B +/
Xsets SAVEDIR to \*(L"%p\*(R" and SAVENAME to \*(L"%^C\*(R", which by
Xdefault saves articles directly to your private news directory, with the
Xfilename being the name of the current newsgroup, first letter capitalized.
X(Either
X.B +/
Xor
X.B \-/
Xmay be default on your system, depending on the feelings of your news
Xadministrator when he, she or it installed
X.IR trn .)
XYou may, of course, explicitly set SAVEDIR and SAVENAME to other
Xvalues \*(-- see discussion in the environment section.
X.PP
XAny switch may be selectively applied according to the current baud-rate.
XSimply prefix the switch with +speed to apply the switch at that speed or
Xgreater, and \%\-speed to apply the switch at that speed or less.
XExamples: \%\-1200\-hposted suppresses the Posted line at 1200 baud or less;
X\%+9600\-m enables marking at 9600 baud or more.
XYou can apply the modifier recursively to itself also: \%+300\-1200\-t sets
Xterse mode from 300 to 1200 baud.
X.PP
XSimilarly, switches may be selected based on terminal type:
X.Sp
X	\-=vt100+T		set +T on vt100
X.br
X	\-=tvi920\-ETERM=mytvi	get a special termcap entry
X.br
X	\-=tvi920\-ERNMACRO=%./.rnmac.tvi
X.br
X				set up special key-mappings
X.br
X	+=paper\-v		set verify mode if not hardcopy
X.PP
XSome switch arguments, such as environment variable values, may require
Xspaces in them.
XSuch spaces should be quoted via ", ', or \e in the conventional fashion,
Xeven when passed via TRNINIT or the & command.
X.Sh "Regular Expressions"
XThe patterns used in article searching are regular expressions such as
Xthose used by
X.IR ed (1).
XIn addition, \ew matches an alphanumeric character and \eW a non-alphanumeric.
XWord boundaries may be matched by \eb, and non-boundaries by \eB.
XThe bracketing construct \e(\ ...\ \e) may also be used, and \edigit matches
Xthe digit'th substring, where digit can range from 1 to 9.
X\e0 matches whatever the last bracket match matched.
XUp to 10 alternatives may given in a pattern, separated by \e|, with the
Xcaveat that \e(\ ...\ \e|\ ...\ \e) is illegal.
X.Sh "Characterset conversions"
X.I trn
Xcan use character set conversions when displaying articles. This helps
Xusers in non-English-speaking countries to display special characters
Xon 7-bit displays.
X.I trn
Xassumes that articles use the ISO-8859-1 character set and converts
Xthe special characters (e.g., \*(L"umlauts\*(R") to a string of ASCII
Xcharacters. 
XCurrently the following conversions are supported (see the 
X.I \-Q
Xoption):
X.TP 5
X.B p
XPlain. No change. This is the default.
X.TP 5
X.B a
XISO->ASCII. Special characters are mapped to ASCII, e.g. the umlaut-o
Xcharacter becomes oe.
X.TP 5
X.B m
XISO->ASCII monospaced. Special characters are mapped to exactly one
Xsimilar-looking ASCII character, e.g. umlaut-o becomes o. Used where
Xcorrect spacing is more important than accuracy.
X.TP 5
X.B t
XTeX->ISO. Assuming your display can handle the ISO-8859-1 charset, 
X.I trn
Xtransforms umlauts in the TeX notation, which is commonly used in
XGermany, to real ISO characters, e.g. "a becomes umlaut-a.
X.PP
XThe selected conversion, if different from 
X.BR p ,
Xwill be displayed in the article level and pager prompt. The
Xconversion is also used when including original articles in a reply or
Xfollowup. It is not used when saving articles to files.
X.Sh "Interpretation and Interpolation"
XMany of the strings that
X.I trn
Xhandles are subject to interpretations of several types.
XUnder filename expansion, an initial \*(L"~/\*(R" is translated to the name
Xof your home directory, and \*(L"~name\*(R" is translated to the login
Xdirectory for the user specified.
XFilename expansion will also expand an initial environment variable, and
Xalso does the backslash, caret and percent expansion mentioned below.
X.PP
XAll interpreted strings go through backslash, caret and percent
Xinterpretation.
XThe backslash escapes are the normal ones (such as \en, \et, \e033, etc.).
XThe caret escapes indicate control codes (such as ^i, ^l, etc.).
XIf you wish to pass through a backslash or a caret it must be escaped with
Xa backslash.
XThe special percent escapes are similar to printf percent escapes.
XThese cause the substitution of various run-time values into the string.
XThe following are currently recognized:
X.Ip %a 8
XCurrent article number.
X.Ip %A 8
XFull name of current article (%P/%c/%a).
X.Ip %b 8
XDestination of last save command, often a mailbox.
X.Ip %B 8
XThe byte offset to the beginning of the part of the article to be saved,
Xset by the save command.
XThe \*(L's\*(R' and \*(L'S\*(R' commands set it to 0, and the \*(L'w\*(R'
Xand \*(L'W\*(R' commands set it to the byte offset of the body of the article.
X.Ip %c 8
XCurrent newsgroup, directory form.
X.Ip %C 8
XCurrent newsgroup, dot form.
X.Ip %d 8
XFull name of newsgroup directory (%P/%c).
X.Ip %D 8
X\*(L"Distribution:\*(R" line from the current article.
X.Ip %e 8
XThe last command executed to extract data from an article.
X.Ip %E 8
XThe last directory where an extracted file went.
X.Ip %f 8
X\*(L"From:\*(R" line from the current article, or the \*(L"Reply-To:\*(R"
Xline if there is one.
XThis differs from %t in that comments (such as the full name) are not
Xstripped out with %f.
X.Ip %F 8
X\*(L"Newsgroups:\*(R" line for a new article, constructed from
X\*(L"Newsgroups:\*(R" and \*(L"Followup-To:\*(R" lines of current article.
X.Ip %h 8
XName of the header file to pass to the mail or news poster,
Xcontaining all the information that the poster program needs in the
Xform of a message header.
XIt may also contain a copy of the current article.
XThe format of the header file is controlled by the MAILHEADER and NEWSHEADER
Xenvironment variables.
X.Ip %H 8
XHost name (your machine's name).
X.Ip %i 8
X\*(L"Message-I.D.:\*(R" line from the current article, with <> guaranteed.
X.Ip %I 8
XThe reference indication mark (see the
X.B \-F
Xswitch.)
X.Ip %l 8
XThe news administrator's login name, if any.
X.Ip %L 8
XLogin name (yours).
X.Ip %m 8
XThe current mode of
X.I trn,
Xfor use in conditional macros.
X.Sp
X.nf
X	i	Initializing.
X	n	Newsgroup-selection level.
X	f	end (Finis) of newsgroup-selection level.
X	t	the Thread/subject/article selector.
X	a	Article level (What next?).
X	e	End of the article level.
X	p	Pager level (MORE prompt).
X	u	Set-unread prompt.
X	d	selector moDe prompt.
X	o	selector Order prompt.
X	m	Memorize thread command prompt.
X	r	memoRize subject command prompt.
X	k	processing memorized (KILL file) commands.
X	A	Add this newsgroup?
X	B	aBandon confirmation.
X	C	Catchup confirmation.
X	D	Delete bogus newsgroups?
X	F	Is follow-up a new topic?
X	M	Use mailbox format?
X	R	Resubscribe to this newsgroup?
X.fi
X.Sp
XNote that yes/no questions are all upper-case modes.
XIf, for example, you wanted to disallow defaults on all yes/no questions,
Xyou could define the following macro:
X.Sp
X.nf
X\e040	%(%m=[A-Z]?h: )
X.fi
X.Ip %M 8
XThe number of articles marked to return via the \*(L'M\*(R' command.
XIf the same article is Marked multiple times, \*(L"%M\*(R" counts it
Xmultiple times in the current implementation.
X.Ip %n 8
X\*(L"Newsgroups:\*(R" line from the current article.
X.Ip %N 8
XFull name (yours).
X.Ip %o 8
XOrganization (yours).
X.Ip %O 8
XOriginal working directory (where you ran trn from).
X.Ip %p 8
XYour private news directory, normally ~/News.
X.Ip %P 8
XPublic news spool directory, normally /usr/spool/news on systems that don't use NNTP.
X.Ip %q 8
XThe value of the last \*(L"quoted\*(R" input string (see the %" interp).
X.Ip %r 8
XLast reference on references line of current article (parent article id).
X.Ip %R 8
XReferences list for a new article, constructed from the references and article
XID of the current article.
X.Ip %s 8
XSubject, with all Re's and (nf)'s stripped off.
X.Ip %S 8
XSubject, with one \*(L"Re:\*(R" stripped off.
X.Ip %t 8
X\*(L"To:\*(R" line derived from the \*(L"From:\*(R" and \*(L"Reply-To:\*(R"
Xlines of the current article.
XThis always returns an Internet format address.
X.Ip %T 8
X\*(L"To:\*(R" line derived from the \*(L"Path:\*(R" line of the
Xcurrent article to produce a uucp path.
X.Ip %u 8
XThe number of unread articles in the current newsgroup.
X.Ip %U 8
XThe number of unread articles in the current newsgroup, not counting the
Xthe current article.
XWhen threads are selected, this count reflects only selected articles.
X.Ip %v 8
XThe number of unselected articles, not counting the current article
Xif it is unselected.
X.Ip %w 8
XThe directory where mthreads keeps its tmp files.
X.Ip %W 8
XThe directory where thread files are placed.
X.Ip %x 8
XThe news library directory.
X.Ip %X 8
XThe trn library directory.
X.Ip %z 8
XThe length of the current article in bytes.
X.Ip %Z 8
XThe number of selected threads.
X.Ip %~ 8
XYour home directory.
X.Ip %. 8
XThe directory containing your dot files, which is your home directory unless
Xthe environment variable DOTDIR is defined when trn is invoked.
X.Ip %# 8
XThe current count for a multi-file save, starting with 1.
XThis value is incremented by one for each file saved or extracted within a
Xsingle command.
X.Ip %$ 8
XCurrent process number.
X.Ip %/ 8
XLast search string.
X.Ip %? 8
XA space unless the current interp string is > 79 characters, at which point
Xit turns into a newline.
X.Ip %% 8
XA percent sign.
X.Ip "%{name} or %{name\-default}" 8
XThe environment variable \*(L"name\*(R".
X.Ip %[name] 8
XThe value of header line \*(L"Name:\*(R" from the current article.
XThe \*(L"Name:\ \*(R" is not included.
XFor example \*(L"%D\*(R" and \*(L"%[distribution]\*(R" are equivalent.
XThe name must be spelled out in full.
X.Ip %`command` 8
XInserts the output of the command, with any embedded newlines translated
Xto space.
X.Ip %""prompt"" 8
XPrints prompt on the terminal, then inputs one string, and inserts it.
X.Ip "%(test_text=pattern?then_text:else_text)" 8
XIf
X.I test_text
Xmatches
X.IR pattern ,
Xhas the value
X.IR then_text ,
Xotherwise
X.IR else_text .
XThe \*(L":else_text\*(R" is optional, and if absent, interpolates the null string.
XThe = may be replaced with != to negate the test.
XTo quote any of the meta-characters
X(\*(L'=\*(R', \*(L'?\*(R', \*(L':\*(R', or \*(L')\*(R'),
Xprecede with a backslash.
X.Ip %digit 8
XThe digits 1 through 9 interpolate the string matched by the nth bracket
Xin the last pattern match that had brackets.
XIf the last pattern had alternatives, you may not know the number of the
Xbracket you want \*(-- %0 will give you the last bracket matched.
X.PP
XModifiers: to capitalize the first letter, insert \*(L'^\*(R':
X\*(L"%^C\*(R" produces something like \*(L"Rec.humor\*(R".
XInserting \*(L'_\*(R' causes the first letter following the last
X\&\*(L'/\*(R' to be capitalized: \*(L"%_c\*(R" produces \*(L"rec/Humor\*(R".
X.PP
XInserting \*(L'\\\*(R' will insert a backslash before any characters that
Xwould be magic in a regular expression, including \*(L'%\*(R':
X\*(L"%\\C\*(R" produces \*(L"rec\\.humor\*(R".
X.PP
XInserting \*(L"'\*(R" will insert a backslash before any single-quotes in
Xthe result, suitable for enclosing in single-quotes and sending to a shell:
X\*(L"'%'s'\*(R" might produce \*(L"'I'\\''m a subject'\*(R".
X.PP
XInserting \*(L"''\*(R" will insert a backslash before any double-quotes in
Xthe result, suitable for enclosing in double-quotes and sending to a shell.
X.PP
XInserting \*(L">\*(R" will strip out just the address portion
Xof an address string such as the From line.
X.PP
XInserting \*(L")\*(R" will strip out just the comment (real name) portion
Xof an address string such as the From line.
X.PP
XInserting \*(L":FMT\*(R" will format the result according to the printf-style
XFMT string: \*(L"%:-50.50s\*(R" left-justifies the subject into a 50
Xcharacter field.
X.SH ENVIRONMENT
XThe following environment variables are paid attention to by
X.IR trn .
XIn general the default values assumed for these variables by
X.I trn
Xare reasonable, so if you are using
X.I trn
Xfor the first time, you can safely ignore this section.
XNote that the defaults below may not correspond precisely to the defaults
Xon your system.
XTo find the actual defaults you would need to look in config.h and common.h
Xin the trn source directory, and the file INIT in the trn library directory.
X.PP
XThose variables marked (%) are subject to % interpolation, and those marked
X(~) are subject to both % interpolation and ~ interpretation.
X.Ip "ATTRIBUTION (%)" 8
XGives the format of the attribution line in front of the quoted article
Xincluded by an F command.
X.Sp
XDefault: In article %i,%?%)f <%>f> wrote:
X.Ip "AUTOSUBSCRIBE" 8
XWhen
X.I trn
Xis checking for new newsgroups and finds one
Xmatching one of the patterns in AUTOSUBSCRIBE, the new group is
Xautomatically added to the end of the .newsrc, subscribed.
XNewsgroups not matching this or AUTOUNSUBSCRIBE, below, are offered
Xto the user.
X.Sp
XAUTOSUBSCRIBE is a comma separated list of newsgroup patterns ala
X\&\*(L'o\*(R', \*(L'/\*(R', etc.
XIt can also include \*(L"but not\*(R" entries preceded by \*(L'!\*(R'.
X\*(L"a,b,!c,d\*(R" is read as \*(L"matching a or b, unless it also
Xmatches c; matching d regardless\*(R".
XAnother way to look at it is \*(L"(((a or b) and not c) or d)\*(R".
XTo automatically subscribe to all local
Xgroups but be choosy about non-local groups, one might say \*(L"*,!*.*\*(R".
X.Sp
XDefault: (none)
X.Ip "AUTOUNSUBSCRIBE" 8
XAUTOUNSUBSCRIBE is very similar to AUTOSUBSCRIBE, above, but
Xnew newsgroups matching it are automatically added to the end
Xof the .newsrc file, unsubscribed.
XIf a newsgroup matches AUTOSUBSCRIBE, AUTOUNSUBSCRIBE is not consulted.
X.Sp
XDefault: (none)
X.Ip "CANCEL (~)" 8
XThe shell command used to cancel an article.
X.Sp
XDefault: inews \-h < %h
X.Ip "CANCELHEADER (%)" 8 13v
XThe format of the file to pass to the CANCEL command in order to cancel
Xan article.
X.Sp
XDefault:
X.br
XNewsgroups: %n
X.br
XSubject: cmsg cancel %i
X.br
XReferences: %R
X.br
XReply-To: %L@%H (%N)
X.br
XDistribution: %D
X.br
XOrganization: %o
X.sp 1
X%i cancelled from trn.
X.Ip DOTDIR 8
XWhere to find your dot files, if they aren't in your home directory.
XCan be interpolated using \*(L"%.\*(R".
X.Sp
XDefault: $HOME
X.Ip "EDITOR (~)" 8
XThe name of your editor, if VISUAL is undefined.
X.Sp
XDefault: whatever your news administrator compiled in, usually vi.
X.Ip "EXSAVER (%)" 8
XThe shell command to execute in order to extract data to either /bin/sh
Xor a user-specified command.
X.Sp
XDefault: tail +%Bc %A | %e
X.Ip "FIRSTLINE (%)" 8
XControls the format of the line displayed at the top of an article.
XWarning: this may go away.
X.Sp
XThe default (ignoring the Marked to return display in unthreaded
Xgroups) is approximately:
X.Sp
X%C #%a%(%Z=^0$?%(%U!=^0$? (%U more\e)): (%U + %v more\e))
X.Ip "FORWARDHEADER (%)" 8
XThe format of the header file for forwarding messages.
XSee also FORWARDPOSTER.
X.Sp
XDefault:
X.Sp
XTo: 
X.br
XSubject: %(%i=^$?:[subject] (fwd\e\e)
X.br
X%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}
X.br
X)Newsgroups: %n
X.br
XIn-Reply-To: %i)
X.br
X%(%[references]=^$?:References: %[references]
X.br
X)Organization: %o
X.br
XCc: 
X.br
XBcc: \en\en
X.Ip "FORWARDPOSTER (~)" 8
XThe shell command to be used by the forward command (^F)
Xin order to allow you to edit and deliver the file.
X.I trn
Xwill not itself call upon an editor for replies \*(-- this
Xis a function of the program referenced by FORWARDPOSTER.
XSee also FORWARDHEADER and MAILPOSTER.
X.Sp
XDefault: QUOTECHARS=%I Rnmail \-h %h
X.Ip HIDELINE 8
XIf defined, contains a regular expression which matches article lines to
Xbe hidden, in order, for instance, to suppress quoted material.
XA recommended string for this purpose is \*(L"^>...\*(R", which \fIdoesn't\fR
Xhide lines with only \*(L'>\*(R', to give some indication that quoted
Xmaterial is being skipped.
XIf you want to hide more than one pattern, you can use \*(L"\||\|\*(R" to
Xseparate the alternatives.
XYou can view the hidden lines by restarting the article with the \*(L'v\*(R'
Xcommand.
X.Sp
XThere is some overhead involved in matching each line of the article against
Xa regular expression.
XYou might wish to use a baud-rate modifier to enable this feature only at
Xlow baud rates.
X.Sp
XDefault: undefined
X.Ip HOME 8
XYour home directory.
XAffects ~ interpretation, and the location of your
Xdot files if DOTDIR is not defined.
X.Sp
XDefault: $LOGDIR
X.Ip "KILLGLOBAL (~)" 8
XWhere to find the KILL file to apply to every newsgroup.
XSee the \*(L'^K\*(R' command at the newsgroup-selection level.
X.Sp
XDefault: %p/KILL
X.Ip "KILLLOCAL (~)" 8
XWhere to find the KILL file for the current newsgroup.
XSee the commands \*(L'K\*(R' and \*(L'^K\*(R' at the article selection level,
Xand the search modifier \*(L'K\*(R'.
X.Sp
XDefault: %p/%c/KILL
X.Ip LOGDIR 8
XYour home directory if HOME is undefined.
XAffects ~ interpretation, and the location of your
Xdot files if DOTDIR is not defined.
X.Sp
XDefault: none.
X.Sp
XExplanation: you must have either $HOME or $LOGDIR.
X.Ip LOGNAME 8
XYour login name, if USER is undefined.
XMay be interpolated using \*(L"%L\*(R".
X.Sp
XDefault: value of getlogin().
X.Ip LOCALTIMEFMT 8
XThe format used by strftime() to print the local time.
XThe Date line is only displayed in local time if the group is threaded
X(see the \-H option for more information on Date).
X.Sp
XDefault: %a %b %e %X %Z %Y
X.Sp
Xwhich is the same format as the
X.IR date (1)
Xcommand.
X.Ip "MAILCALL (~)" 8
XWhat to say when there is new mail.
X.Sp
XDefault: (Mail)
X.Ip "MAILFILE (~)" 8
XWhere to check for mail.
X.Sp
XDefault: /usr/spool/mail/%L
X.Ip "MAILHEADER (%)" 8
XThe format of the header file for replies.
XSee also MAILPOSTER.
X.Sp
XDefault:
X.Sp
XTo: %t
X.br
XSubject: %(%i=^$?:Re: %S
X.br
X%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}
X.br
X)Newsgroups: %n
X.br
XIn-Reply-To: %i)
X.br
X%(%[references]=^$?:References: %[references]
X.br
X)Organization: %o
X.br
XCc: 
X.br
XBcc: \en\en
X.Ip "MAILPOSTER (~)" 8
XThe shell command to be used by the reply commands (r and R)
Xin order to allow you to enter and deliver the response.
X.I trn
Xwill not itself call upon an editor for replies \*(-- this
Xis a function of the program referenced by MAILPOSTER.
XSee also MAILHEADER.
X.Sp
XDefault: QUOTECHARS=%I Rnmail \-h %h
X.Ip "MBOXSAVER (~)" 8
XThe shell command to save an article in mailbox format.
X.Sp
XDefault: %X/mbox.saver %A %P %c %a %B %C "%b" \e
X.br
X"From %t %`date`"
X.Sp
XExplanation: the first seven arguments are the same as for NORMSAVER.
XThe eighth argument to the shell script is the new From line
Xfor the article, including the posting date,
Xderived either directly from the Posted: line, or not-so-directly from
Xthe Date: line.
XHeader munging at its finest.
X.Ip MODSTRING 8
XThe string to insert in the group summary line, which heads each article,
Xfor a moderated group.
XSee also NOPOSTRING.
X.Sp
XDefault: " (moderated)"
X.Ip NAME 8
XYour full name.
XMay be interpolated using \*(L"%N\*(R".
X.Sp
XDefault: name from /etc/passwd, or ~/.fullname.
X.Ip "NEWSHEADER (%)" 8 16v
XThe format of the header file for follow-ups.
XSee also NEWSPOSTER.
X.Sp
XDefault:
X.Sp
X%(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n
X.br
X))Newsgroups: %(%F=^$?%C:%F)
X.br
XSubject: %(%S=^$?%"\en\enSubject: ":Re: %S)
X.br
XSummary:
X.br
XExpires: 
X.br
X%(%R=^$?:References: %R
X.br
X)Sender: 
X.br
XFollowup-To: 
X.br
X%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}
X.br
X)Distribution: %(%i=^$?%"Distribution: ":%D)
X.br
XOrganization: %o
X.br
XKeywords: %[keywords]
X.br
XCc: \en\en
X.Ip NEWSORG 8
XEither the name of your organization, or the name of a file containing the
Xname of your organization.
X(For use at sites where the ORGANIZATION environmental variable is already
Xin use.
XNEWSORG will override ORGANIZATION if both are present.)
XMay be interpolated using \*(L"%o\*(R".
X.Sp
XDefault: whatever your news administrator compiled in.
X.Ip "NEWSPOSTER (~)" 8
XThe shell command to be used by the follow-up commands (f and F)
Xin order to allow you to enter and post a follow-up news article.
X.I trn
Xwill not itself call upon an editor for follow-ups \*(-- this
Xis a function of the program called by
X.IR trn .
XSee also NEWSHEADER.
X.Sp
XDefault: QUOTECHARS=%I Pnews \-h %h
X.Ip NNTPSERVER 8
XThe hostname of your NNTPSERVER.
X[This does not apply unless you are running the NNTP version of trn.]
X.Sp
XDefault: the hostname listed in the server file, usually
X/usr/local/lib/rn/server.
X.Ip NOPOSTRING 8
XThe string to insert in the group summary line, which heads each article,
Xfor a group to which local posting is not allowed.
XSee also MODSTRING.
X.Sp
XDefault: " (no posting)"
X.Ip "NORMSAVER (~)" 8
XThe shell command to save an article in the normal (non-mailbox) format.
X.Sp
XDefault: %X/norm.saver %A %P %c %a %B %C "%b"
X.Ip ORGANIZATION 8
XEither the name of your organization, or the name of a file containing the
Xname of your organization.
X(If NEWSORG is set, it will override ORGANIZATION.)
XMay be interpolated using \*(L"%o\*(R".
X.Sp
XDefault: whatever your news administrator compiled in.
X.Ip PAGESTOP 8
XIf defined, contains a regular expression which matches article lines to
Xbe treated as form-feeds.
XThere are at least two things you might want to do with this.
XTo cause page breaks between articles in a digest, you might define it
Xas \*(L"^--------\*(R".
XTo force a page break before a signature, you could define it
Xas \*(L"^-- $\*(R".
X(Then, when you see \*(L"--\*(R" at the bottom of the page, you can skip
Xthe signature if you so desire by typing \*(L'n\*(R' instead of space.)
XTo do both, you could use \*(L"^--\*(R".
XIf you want to break on more than one pattern, you can use \*(L"\||\|\*(R" to
Xseparate the alternatives.
X.Sp
XThere is some overhead involved in matching each line of the article against
Xa regular expression.
XYou might wish to use a baud-rate modifier to enable this feature only at
Xlow baud rates.
X.Sp
XDefault: undefined
X.Ip "PIPESAVER (%)" 8
XThe shell command to execute in order to accomplish a save to a pipe
X(\*(L"s\ |\ command\*(R" or \*(L"w\ |\ command\*(R").
XThe command typed by the user is substituted in as %b.
X.Sp
XDefault: %(%B=^0$?<%A:tail +%Bc %A |) %b
X.Sp
XExplanation: if %B is 0, the command is \*(L"<%A %b\*(R", otherwise
Xthe command is \*(L"tail +%Bc %A | %b\*(R".
X.Ip REPLYTO 8
XThe value of the \*(L"Reply-To:\*(R" header, if needed.
X.Sp Default: \*(L" \*(R".
X.Ip RNINIT 8
XThis variable is used when initializing trn in rn-compatibility mode
X(see the \-x switch) or when the TRNINIT variable isn't defined.
XSee the TRNINIT variable for a description.
X.Ip "RNMACRO (~)" 8
XThe name of the file containing macros and key mappings when running trn
Xas rn.
XSee also the TRNMACRO variable and the CUSTOM MACROS section.
X.Sp
XDefault: %./.rnmac
X.Ip "SAVEDIR (~)" 8
XThe name of the directory to save to, if the save command does not specify
Xa directory name.
X.Sp
XDefault:
X.br
X   If
X.B \-/
Xis set: %p/%c
X.br
X   If
X.B +/
Xis set: %p
X.Ip "SAVENAME (%)" 8
XThe name of the file to save to, if the save command contains only a
Xdirectory name.
X.Sp
XDefault:
X.br
X   If
X.B \-/
Xis set: %a
X.br
X   If
X.B +/
Xis set: %^C
X.Ip "SELECTCHARS" 8
XThe characters used by the thread selector to select the associated thread
Xof discussion.
XYou can specify up to 64 visible characters, including upper- and lower-case
Xletters, numbers, and many punctuation characters.
XSelection characters override command characters in the selector, but are
Xnot excluded from macro expansion, so be careful.
X.br
XDefault: abdefgijlorstuvwxyz1234567890BCFGHIKMVW
X.br
X(You'll notice various characters are omitted to allow them to be typed
Xas commands in the selector.)
X.Ip SHELL 8
XThe name of your preferred shell.
XIt will be used by the \*(L'!\*(R', \*(L'S\*(R' and \*(L'W\*(R' commands.
X.Sp
XDefault: whatever your news administrator compiled in.
X.Ip "SUBJLINE (%)" 8
XControls the format of the lines displayed by the \*(L'=\*(R' command at
Xthe article selection level.
X.Sp
XDefault: %s
X.Ip "SUPERSEDEHEADER (%)" 8 16v
XThe format of the header file for a supersede article.
X.Sp
XDefault:
X.Sp
XFrom: %L@%H (%N)
X.br
XNewsgroups: %n
X.br
XSubject: %S
X.br
XDistribution: %D
X.br
XOrganization: %o
X.br
XSupersedes: %i
X.Ip TERM 8
XDetermines which termcap entry to use, unless TERMCAP contains the entry.
X.Ip TERMCAP 8
XHolds either the name of your termcap file, or a termcap entry.
X.Sp
XDefault: /etc/termcap, normally.
X.Ip TRNINIT 8
XDefault values for switches may be passed to
X.I trn
Xby placing them in the TRNINIT variable (or RNINIT if you're starting
Xtrn in rn-compatibility mode).
XAny switch that is set in this way may be overruled 
Xon the command line, or via the \*(L'&\*(R' command from within
X.IR trn .
XBinary-valued switches that are set with \*(L"\-switch\*(R" may be unset
Xusing \*(L"+switch\*(R".
X.Sp
XIf TRNINIT begins with a \*(L'/\*(R' it is assumed to be the name of a file
Xcontaining switches.
XYou can put comments in this file by preceding them with a \*(L'#\*(R'
Xas long as this is the first character on a line or it follows some
Xwhite-space (which delimits the switches in the file).
XIf you want to set many environment variables but don't want to keep
Xthem all in your environment, or if the use of any of these variables
Xconflicts with other programs, you can use this feature along with the
X.B \-E
Xswitch to set the environment variables upon startup.
X.Sp
XDefault: \*(L" \*(R".
X.Ip "TRNMACRO (~)" 8
XThe name of the file containing macros and key mappings.
XIf the file is not found, the RNMACRO variable is used to look for your
Xrn macros.
XFor information on what to put into this file, see the CUSTOM MACROS section.
X.Sp
XDefault: %./.trnmac
X.Ip "UNSHAR (~)" 8
XThe shell command to execute in order to accomplish the unshar'ing of a
Xshell archive.
X.Sp
XDefault: /bin/sh
X.Ip USER 8
XYour login name.
XMay be interpolated using \*(L"%L\*(R".
X.Sp
XDefault: $LOGNAME
X.Ip "VISUAL (~)" 8
XThe name of your editor.
X.Sp
XDefault: $EDITOR
X.Ip XTERMMOUSE 8
XIf you set this variable to \*(L'y\*(R' (yes), trn will enable the use of the
Xxterm mouse in the selector if you are using an xterm.
XIf you set it to \*(L'a\*(R' (always), trn will assume you have an xterm.
XOnce enabled left-clicking on an item selects it while middle-clicking an
Xitem will move to that item.
XIf you click the top (header) line of the selector it moves up a page.
XIf you click the bottom (footer) line of the selector it executes the
Xdefault command for the page (left click) or goes down a page (middle
Xclick).
XYou can also use the right mouse button to move up or down a page by
Xclicking in the upper-half or lower-half of the screen, respectively.
X.Ip "YOUSAID (%)" 8
XGives the format of the attribution line in front of the quoted article
Xincluded by an R command.
X.Sp
XDefault: In article %i you write:
X.SH "AUTOMATIC MACROS"
XOn startup
X.I trn
Xattempts to build a set of macros that map your keypad arrow keys to
Xuseful functions.
XThese default actions are mentioned in the prior description of each level's
Xcommands.
XIf you don't like this (or trn gets it wrong), you can disable the automatic
Xmacros by using the
X.B \-A
Xoption.
X.SH "CUSTOM MACROS"
XWhen
X.I trn
Xstarts up it looks for a file containing macro definitions (see environment
Xvariables TRNMACRO and RNMACRO).
XAny sequence of commands may be bound to any sequence of keys, so you
Xcould re-map your entire keyboard if you desire.
XBlank lines or lines beginning with # in the macro file are considered
Xcomments; otherwise
X.I trn
Xlooks for two fields separated by white space.
XThe first field gives the sequence of keystrokes that trigger the macro,
Xand the second field gives the sequence of commands to execute.
XBoth fields are subject to % interpolation, which will also translate
Xbackslash and caret sequences.
X(The keystroke field is interpreted at startup time, but the command field
Xis interpreted at macro execution time so that you may refer to % values
Xin a macro.)
XFor example, if you want to reverse the roles of carriage return and
Xspace in
X.I trn
X.Sp
X^J	\e040
X.br
X^M	\e040
X.br
X\e040	^J
X.Sp
Xwill do just that.
XBy default, all characters in the command field are interpreted as the
Xcanonical
X.I trn
Xcharacters, i.\|e. no macro expansion is done.
XOtherwise the above pair of macros would cause an infinite loop.
XTo force macro expansion in the command field, enclose the
Xmacro call with ^( ... ^) thusly:
X.Sp
X@s	|mysavescript
X.br
X@w	w^(@s^)
X.Sp
XYou can use the %() conditional construct to construct macros that work
Xdifferently under different circumstances.
XIn particular, the current mode (%m) of
X.I trn
Xcould be used to make a command that only works at a particular level.
XThis is particularly vital for the selector which uses most of
Xthe lower-case letters to select the associated item in its display.
XFor example,
X.Sp
Xa	%(%m=t?a:s art.hold\en)
X.Sp
Xwill return the original letter (a) in the selector, and the command
X\*(L"s art.hold\en\*(R" everywhere else.
X.Sp
X%(%{TERM}=vt100?^[[O)	/^J
X.Sp
Xwill do the binding only if the terminal type is vt100,
Xthough if you have many of these it would be better to have separate
Xfiles for each terminal.
X.Sp
XIf you want to bind a macro to a function key that puts a common garbage character
Xafter the sequence (such as the carriage return on the end of Televideo 920
Xfunction sequences), DO NOT put the carriage return
Xinto all the sequences or you will waste a CONSIDERABLE amount of internal
Xstorage.
XInstead of \*(L"^AF^M\*(R", put \*(L"^AF+1\*(R", which indicates to
X.I trn
Xthat it should gobble up one character after the F.
X.SH "WHAT'S NEW"
XHere's a quick run-down of
X.IR trn 's
Xfeatures and commands aimed at the knowledgeable
X.I rn
Xor
X.I trn
Xuser.
X.PP
XThe addition of true reference-line threading is one of the biggest
Ximprovements over rn.
XThis threading allows you to read a discussion in reply order with
Xan article's replies being attached to the article that inspired them.
XThreads will encompass multiple subjects whenever a reply to an article
Xin the thread arrives with a different subject.
XThis is usually done to better indicate the topic in the reply
Xwhen it diverges from the original subject.
X.PP
XAnother big improvement is the selector, which is bound
Xto the \*(L'+\*(R' key.
XThe selector displays a list of threads, subjects, or individual articles
Xto allow you to select the topics that interest you by typing their
Xassociated letter.
XThe difference between the thread and the subject selector is that the
Xsubject selector displays all subjects with a separate selection letter,
Xeven those tied together via their references.
XThis can be quite useful if you select some threads and desire to weed
Xout some extraneous discussions: you could switch the selector into
Xexclusive mode (\*(L'E\*(R' shows only selected threads) and then into
Xsubject mode (\*(L'Ss\*(R') to separate the threads into their component
Xsubjects and deselect or kill the subjects you don't care about.
XYou don't have to go to all this trouble using the selector if you prefer
Xto just hit the \*(L'k\*(R' key when you start reading a subject you're not
Xinterested in.
XThe selector can also switch between showing unread articles and
Xarticles that have already been read, allowing you to selectively re-read
Xdiscussions (this is the \*(L'U\*(R' command in the selector).
X.PP
XAnother threaded addition is the article-tree display in the
Xupper-right corner of the header.
XLooking at the tree gives you a feel for how the articles you are
Xreading relate to each other, allowing you to see at a glance when
Xthere are lots of replies and decide if you want to junk an uninteresting
Xset of replies or perhaps tough it out.
X.PP
XThe header display has also been modified to hide a few more lines by default
X(e.\|g. References), but, as always, you can override these with \-h.
XThere is also some more \*(L"magic\*(R" in the header: the From header can
Xbe trimmed to be just the comment portion (if available), and the Date
Xheader is displayed in local time (by default).
XUse \-H and +H to turn header magic on and off.
X.PP
XOnce you begin reading articles, use the regular movement commands (n, N,
Xp, P, etc.) as you normally would.
XYou'll find that these commands track the reply order shown in the tree
Xdisplay.
XThen try using ^N and ^P, which follow a subject in the order the articles
Xwere posted.
XFinally, check out the [, ], (, ), {, and } commands to move around in the
Xarticle tree a bit more directly.
XThe first four commands should also be bound to your keypad's arrow keys,
Xmaking them easier to type.
XFor example, typing \*(L'[\*(R' (left) takes you to your parent article,
Xeven if it was already read, which is very useful for tracking down the
Xcited portion of the article in its original context.
X.PP
XThere are additional kill commands for the entire thread (J) and the
Xcurrent article and all its replies (,).
X.PP
XThe KILL files have been extended and the commands inside them are now
Xreferred to memorized commands, since they are often used for selection
Xrather than killing of articles.
XThere are new, easier ways to add memorized commands using the \*(L'A\*(R'dd
Xand \*(L'T\*(R'hread commands.
XThe \*(L'A\*(R' command is subject-oriented, while the \*(L'T\*(R' command
Xis article-oriented (meaning they affect a specific set of articles rather
Xthan any article that happens to have a matching subject).
XThey both prompt you for what kind of command you want to add,
Xmaking both auto-killing and auto-selecting just as easy.
X.PP
XThere is also an easy way to skip around among the various threads with
Xthe < and > commands.
XUse them if you want to skip a set of article and read them later instead
Xof junking them.
X.PP
XNote: your news administrator has the option of turning thread processing
Xoff for individual groups, and thus it is possible for some groups to not
Xhave any pre-processed thread information available for use.
XWhen
X.I trn
Xencounters such a group, it generates the thread information on the fly
Xwhile entering the group.
XFor really large groups (or really slow systems), this can take an
Xappreciable amount of time.
XIf you can't talk your news administrator into pre-threading the group,
Xyou can turn off the threading on a group-by-group basis using
Xthe \*(L't\*(R' command at the newsgroup-selection level.
XGroups turned off in this way are read in the
X.I rn
Xstyle \*(-- articles arranged in arrival order unless you specify the
X\-S option, which reads the articles in date order by subject.
X.PP
XTake note of the \*(L"e dir\*(R" command, which is used to extract a shell
Xarchive or uuencoded file into the specified directory.
XIt is even possible to extract other data formats if you specify the
Xappropriate filter command (e.\|g. \*(L"e dir|cmd\*(R".
X.PP
XAlso, if you plan to use macro definitions, it is good to keep in mind
Xthat the selector uses most of the lower-case letters for
Xselection, and thus it is a good idea to explicitly set the mode(s) in
Xwhich a macro applies.
XFor example, if you want to press \*(L'f\*(R' from the article pager/selector
Xto forward the current article to the user \*(L"smith\*(R", you could define:
X.Sp
X.nf
X	f	%(%m=[pa]?|mail smith\en:f)
X.fi
X.Sp
XThis checks the current mode (%m) and if it is \*(L'p\*(R' or \*(L'a\*(R'
Xit expands it to the string \*(L"|mail smith\en\*(R", otherwise it returns
Xthe letter \*(L'f\*(R'.
XIn some cases, you may simply wish to exclude the selector from a
Xmacro with the conditional \*(L"%m!=t\*(R".
X.PP
XFinally, you'll probably want to use the new options,
X.B \-x
Xand
X.B \-X
Xto ensure that all the newest features are available for use.
XThese options might be on by default, depending on how your administrator
Xdecided to install
X.IR trn .
X.SH AUTHORS
XRn was created by Larry Wall <lwall@jpl-devvax.jpl.nasa.gov>
X.br
Xand is now under the direction of Stan Barber <sob@bcm.tmc.edu>.
X.br
XThreaded version by Wayne Davison <davison@borland.com>
X.br
X(Mail all bug reports for trn to Wayne.)
X.br
XRegular expression routines are borrowed from emacs, by James Gosling.
X.br
XHashing routines are modified versions from Geoffrey Collyer.
X.SH FILES
X.Ip "%./.newsrc" 1.25i
Xstatus of your news reading
X.Ip "%./.oldnewsrc" 1.25i
Xbackup copy of your
X.I .newsrc
Xfrom start of session
X.Ip "%./.rnlock" 1.25i
Xlock file so you don't screw up your
X.I .newsrc
X.Ip "%./.rnlast" 1.25i
Xinfo from last run of trn
X.Ip "%./.rnsoft" 1.25i
Xsoft pointers into /usr/lib/news/active to speed startup, synchronous with
X.I .newsrc
X.Ip "%./.rnhead" 1.25i
Xtemporary header file to pass to a mailer or news poster
X.Ip "%./.[t]rnmac" 1.25i
Xmacro and keymap definitions
X.Ip "%p" 1.25i
Xyour news save directory, usually ~/News
X.Ip "%x/active" 1.25i
Xthe list of active newsgroups, usually /usr/lib/news/active on systems that don't use NNTP
X.Ip "%P" 1.25i
Xthe public news spool directory, usually /usr/spool/news on systems that don't use NNTP
X.Ip "%X/INIT" 1.25i
Xsystem-wide default switches
X.SH SEE ALSO
Xnewsrc(5), more(1), readnews(1), Pnews(1), Rnmail(1)
X.SH DIAGNOSTICS
XGenerally self-documenting, as they say.
X.SH BUGS
XThe
X.B \-h
Xswitch can only hide header lines that
X.I trn
Xknows about.
X.PP
XThe \*(L'\-\*(R' command doesn't cross newsgroup boundaries, and only undoes
Xthe last article selection.
X.PP
XIf you edit your
X.I .newsrc
Xwhile
X.I trn
Xis running,
X.I trn
Xwill happily wipe out your changes when it decides to
Xwrite out the
X.I .newsrc
Xfile.
X.PP
XMarking of duplicate articles as read in cross-referenced newsgroups will
Xnot work unless the Xref patch is installed in inews.
X.PP
XIf you get carried away with % or escape substitutions, you can overflow
Xbuffers.
SHAR_EOF
  echo 'File trn-3.6/trn.1 is complete' &&
  $shar_touch -am 1118220194 'trn-3.6/trn.1' &&
  chmod 0644 'trn-3.6/trn.1' ||
  echo 'restore of trn-3.6/trn.1 failed'
  shar_count="`wc -c < 'trn-3.6/trn.1'`"
  test 102473 -eq "$shar_count" ||
    echo "trn-3.6/trn.1: original size 102473, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/nntpinit.c ==============
if test -f 'trn-3.6/nntpinit.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/nntpinit.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/nntpinit.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/nntpinit.c' &&
X/* $Id: nntpinit.c,v 3.0 1991/11/22 04:12:21 davison Trn $
X*/
X/* This software is Copyright 1992 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction or this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#undef	DECNET    /* If you want decnet support */
X#undef	EXCELAN   /* Excelan EXOS 205 support */
X#undef	NONETDB	  /* Define if you're missing netdb.h */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "nntpclient.h"
X
X#ifdef USE_NNTP
X
X#include <sys/socket.h>
X#include <netinet/in.h>
X#ifdef NONETDB
X# define IPPORT_NNTP	((unsigned short) 119)
X#else
X# include <netdb.h>
X#endif /* !EXCELAN */
X
X#ifdef EXCELAN
Xint connect _((int, struct sockaddr *));
Xunsigned short htons _((unsigned short));
Xunsigned long rhost _((char **));
Xint rresvport p((int));
Xint socket _((int, struct sockproto *, struct sockaddr_in *, int));
X#endif /* EXCELAN */
X
X#ifdef DECNET
X#include <netdnet/dn.h>
X#include <netdnet/dnetdb.h>
X#endif /* DECNET */
X
Xunsigned long inet_addr _((char *x));
Xint get_tcp_socket _((char *machine));
X
Xint
Xserver_init(server)
Xchar *server;
X{
X    int sockt_rd, sockt_wr;
X#ifdef DECNET
X    char *cp;
X
X    cp = index(server, ':');
X
X    if (cp && cp[1] == ':') {
X	*cp = '\0';
X	sockt_rd = get_dnet_socket(server);
X    } else
X	sockt_rd = get_tcp_socket(server);
X#else /* !DECNET */
X    sockt_rd = get_tcp_socket(server);
X#endif
X
X    if (sockt_rd < 0)
X	return -1;
X    sockt_wr = dup(sockt_rd);
X
X    /* Now we'll make file pointers (i.e., buffered I/O) out of
X    ** the socket file descriptor.  Note that we can't just
X    ** open a fp for reading and writing -- we have to open
X    ** up two separate fp's, one for reading, one for writing. */
X    if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) {
X	perror("server_init: fdopen #1");
X	return -1;
X    }
X    if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) {
X	perror("server_init: fdopen #2");
X	ser_rd_fp = NULL;
X	return -1;
X    }
X
X    /* Now get the server's signon message */
X    nntp_check(FALSE);
X
X    if (*ser_line == NNTP_CLASS_OK) {
X	char line2[NNTP_STRLEN];
X	/* Send an XMODE READER command to see if this is a compliant
X	** nntpd we're talking to.  If not, try MODE READER just in
X	** case we're talking to innd.  If OK, use the new reply. */
X	nntp_command("XMODE READER");
X	nntp_gets(line2, sizeof line2);
X	if (atoi(line2) != NNTP_BAD_COMMAND_VAL)
X	    CompliantServer = 1;
X	else {
X	    nntp_command("MODE READER");
X	    nntp_gets(line2, sizeof line2);
X	}
X#if defined(DEBUG) && defined(FLUSH)
X	if (debug & DEB_NNTP)
X	    printf("<%s\n", line2) FLUSH;
X#endif
X	if (atoi(line2) != NNTP_BAD_COMMAND_VAL)
X	    strcpy(ser_line, line2);
X    }
X    return atoi(ser_line);
X}
X
Xint
Xget_tcp_socket(server)
Xchar *server;
X{
X    int portno;	
X    int s;
X    struct sockaddr_in sin;
X#ifdef __hpux
X    int socksize = 0;
X    int socksizelen = sizeof socksize;
X#endif
X#ifdef NONETDB
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = AF_INET;
X#else
X    struct servent *getservbyname(), *sp;
X    struct hostent *gethostbyname(), *hp;
X#ifdef h_addr
X    int x = 0;
X    register char **cp;
X    static char *alist[1];
X#endif /* h_addr */
X    static struct hostent def;
X    static struct in_addr defaddr;
X    static char namebuf[ 256 ];
X
X    if ((sp = getservbyname("nntp", "tcp")) ==  NULL) {
X	fprintf(stderr, "nntp/tcp: Unknown service.\n");
X	return -1;
X    }
X    portno = sp->s_port;
X    /* If not a raw ip address, try nameserver */
X    if (!isdigit(*server) || (defaddr.s_addr = inet_addr(server)) == -1)
X	hp = gethostbyname(server);
X    else {
X	/* Raw ip address, fake  */
X	(void) strcpy(namebuf, server);
X	def.h_name = namebuf;
X#ifdef h_addr
X	def.h_addr_list = alist;
X#endif
X	def.h_addr = (char *)&defaddr;
X	def.h_length = sizeof(struct in_addr);
X	def.h_addrtype = AF_INET;
X	def.h_aliases = 0;
X	hp = &def;
X    }
X    if (hp == NULL) {
X	fprintf(stderr, "%s: Unknown host.\n", server);
X	return -1;
X    }
X
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = hp->h_addrtype;
X    sin.sin_port = portno;
X#endif /* !NONETDB */
X
X    /* The following is kinda gross.  The name server under 4.3
X    ** returns a list of addresses, each of which should be tried
X    ** in turn if the previous one fails.  However, 4.2 hostent
X    ** structure doesn't have this list of addresses.
X    ** Under 4.3, h_addr is a #define to h_addr_list[0].
X    ** We use this to figure out whether to include the NS specific
X    ** code... */
X#ifdef h_addr
X    /* get a socket and initiate connection -- use multiple addresses */
X    for (cp = hp->h_addr_list; cp && *cp; cp++) {
X	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
X	if (s < 0) {
X	    perror("socket");
X	    return -1;
X	}
X        bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
X		
X	if (x < 0)
X	    fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr));
X	x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
X	if (x == 0)
X	    break;
X        fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr));
X	perror("");
X	(void) close(s);
X    }
X    if (x < 0) {
X	fprintf(stderr, "giving up...\n");
X	return -1;
X    }
X#else /* no name server */
X#ifdef EXCELAN
X    s = socket(SOCK_STREAM, (struct sockproto *)NULL, &sin, SO_KEEPALIVE);
X    if (s < 0) {
X	/* Get the socket */
X	perror("socket");
X	return -1;
X    }
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons(IPPORT_NNTP);
X
X    /* set up addr for the connect */
X    if ((sin.sin_addr.s_addr = rhost(&server)) == -1) {
X	fprintf(stderr, "%s: Unknown host.\n", server);
X	return -1;
X    }
X
X    /* And then connect */
X    if (connect(s, (struct sockaddr *)&sin) < 0) {
X	perror("connect");
X	(void) close(s);
X	return -1;
X    }
X#else /* not EXCELAN */
X    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X	perror("socket");
X	return -1;
X    }
X
X    /* And then connect */
X
X    bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
X    if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
X	perror("connect");
X	(void) close(s);
X	return -1;
X    }
X
X#endif /* !EXCELAN */
X#endif /* !h_addr */
X#ifdef __hpux	/* recommended by raj@cup.hp.com */
X#define	HPSOCKSIZE 0x8000
X    getsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, (caddr_t)&socksizelen);
X    if (socksize < HPSOCKSIZE) {
X	socksize = HPSOCKSIZE;
X	setsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, sizeof(socksize));
X    }
X    socksize = 0;
X    socksizelen = sizeof(socksize);
X    getsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, (caddr_t)&socksizelen);
X    if (socksize < HPSOCKSIZE) {
X	socksize = HPSOCKSIZE;
X	setsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, sizeof(socksize));
X    }
X#endif
X    return s;
X}
X
X#ifdef DECNET
Xint
Xget_dnet_socket(server)
Xchar *server;
X{
X    int s, area, node;
X    struct sockaddr_dn sdn;
X    struct nodeent *getnodebyname(), *np;
X
X    bzero((char *) &sdn, sizeof(sdn));
X
X    switch (s = sscanf(server, "%d%*[.]%d", &area, &node)) {
X    case 1: 
X	node = area;
X	area = 0;
X    case 2: 
X	node += area*1024;
X	sdn.sdn_add.a_len = 2;
X	sdn.sdn_family = AF_DECnet;
X	sdn.sdn_add.a_addr[0] = node % 256;
X	sdn.sdn_add.a_addr[1] = node / 256;
X	break;
X    default:
X	if ((np = getnodebyname(server)) == NULL) {
X	    fprintf(stderr, "%s: Unknown host.\n", server);
X	    return -1;
X	} else {
X	    bcopy(np->n_addr, (char *) sdn.sdn_add.a_addr, np->n_length);
X	    sdn.sdn_add.a_len = np->n_length;
X	    sdn.sdn_family = np->n_addrtype;
X	}
X	break;
X    }
X    sdn.sdn_objnum = 0;
X    sdn.sdn_flags = 0;
X    sdn.sdn_objnamel = strlen("NNTP");
X    bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel);
X
X    if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) {
X	nerror("socket");
X	return -1;
X    }
X
X    /* And then connect */
X    if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) {
X	nerror("connect");
X	close(s);
X	return -1;
X    }
X    return s;
X}
X#endif /* DECNET */
X
X#ifdef EXCELAN
X/*
X * inet_addr for EXCELAN (which does not have it!)
X *
X */
Xunsigned long
Xinet_addr(cp)
Xregister char   *cp;
X{
X	unsigned long val, base, n;
X	register char c;
X	unsigned long octet[4], *octetptr = octet;
X#ifndef htonl
X	extern  unsigned long   htonl();
X#endif  /* htonl */
Xagain:
X	/*
X	 * Collect number up to ``.''.
X	 * Values are specified as for C:
X	 * 0x=hex, 0=octal, other=decimal.
X	 */
X	val = 0; base = 10;
X	if (*cp == '0')
X		base = 8, cp++;
X	if (*cp == 'x' || *cp == 'X')
X		base = 16, cp++;
X	while (c = *cp) {
X		if (isdigit(c)) {
X			val = (val * base) + (c - '0');
X			cp++;
X			continue;
X		}
X		if (base == 16 && isxdigit(c)) {
X			val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
X			cp++;
X			continue;
X		}
X		break;
X	}
X	if (*cp == '.') {
X		/*
X		 * Internet format:
X		 *      a.b.c.d
X		 *      a.b.c   (with c treated as 16-bits)
X		 *      a.b     (with b treated as 24 bits)
X		 */
X		if (octetptr >= octet + 4)
X			return (-1);
X		*octetptr++ = val, cp++;
X		goto again;
X	}
X	/*
X	 * Check for trailing characters.
X	 */
X	if (*cp && !isspace(*cp))
X		return (-1);
X	*octetptr++ = val;
X	/*
X	 * Concoct the address according to
X	 * the number of octet specified.
X	 */
X	n = octetptr - octet;
X	switch (n) {
X
X	case 1:                         /* a -- 32 bits */
X		val = octet[0];
X		break;
X
X	case 2:                         /* a.b -- 8.24 bits */
X		val = (octet[0] << 24) | (octet[1] & 0xffffff);
X		break;
X
X	case 3:                         /* a.b.c -- 8.8.16 bits */
X		val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) |
X			(octet[2] & 0xffff);
X		break;
X
X	case 4:                         /* a.b.c.d -- 8.8.8.8 bits */
X		val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) |
X		      ((octet[2] & 0xff) << 8) | (octet[3] & 0xff);
X		break;
X
X	default:
X		return (-1);
X	}
X	val = htonl(val);
X	return (val);
X}
X#endif /* EXCELAN */
X
X#endif /* USE_NNTP */
SHAR_EOF
  $shar_touch -am 1118220194 'trn-3.6/nntpinit.c' &&
  chmod 0644 'trn-3.6/nntpinit.c' ||
  echo 'restore of trn-3.6/nntpinit.c failed'
  shar_count="`wc -c < 'trn-3.6/nntpinit.c'`"
  test 10045 -eq "$shar_count" ||
    echo "trn-3.6/nntpinit.c: original size 10045, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-process.h ==============
if test -f 'trn-3.6/rt-process.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-process.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-process.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-process.h' &&
X/* $Id: rt-process.h,v 3.0 1992/12/14 00:14:15 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
Xint msgid_cmp _((char*,int,HASHDATUM));
XARTICLE *allocate_article _((ART_NUM));
Xbool valid_article _((ARTICLE*));
XARTICLE *get_article _((char*));
Xvoid thread_article _((ARTICLE*));
Xvoid merge_threads _((SUBJECT*, SUBJECT*));
SHAR_EOF
  $shar_touch -am 0411202494 'trn-3.6/rt-process.h' &&
  chmod 0644 'trn-3.6/rt-process.h' ||
  echo 'restore of trn-3.6/rt-process.h failed'
  shar_count="`wc -c < 'trn-3.6/rt-process.h'`"
  test 484 -eq "$shar_count" ||
    echo "trn-3.6/rt-process.h: original size 484, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-util.h ==============
if test -f 'trn-3.6/rt-util.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-util.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-util.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-util.h' &&
X/* $Id: rt-util.h,v 3.0 1992/12/14 00:14:15 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
Xchar *extract_name _((char*));
Xchar *compress_from _((ARTICLE*,int));
Xchar *compress_name _((char*,int));
Xchar *compress_subj _((ARTICLE*,int));
Xchar *get_subject_start _((char*));
X#ifndef HAS_STRCASECMP
Xint strcasecmp _((char*,char*));
Xint strncasecmp _((char*,char*,int));
X#endif
X
XEXT char spin_char INIT(' ');	/* char to put back when we're done spinning */
X
X#define SPIN_OFF	0
X#define SPIN_POP	1
X#define SPIN_FOREGROUND	2
X#define SPIN_BACKGROUND 3
X
Xvoid setspin _((int));
Xvoid spin _((int));
Xbool inbackground _((void));
SHAR_EOF
  $shar_touch -am 1004153894 'trn-3.6/rt-util.h' &&
  chmod 0644 'trn-3.6/rt-util.h' ||
  echo 'restore of trn-3.6/rt-util.h failed'
  shar_count="`wc -c < 'trn-3.6/rt-util.h'`"
  test 780 -eq "$shar_count" ||
    echo "trn-3.6/rt-util.h: original size 780, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-wumpus.h ==============
if test -f 'trn-3.6/rt-wumpus.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-wumpus.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-wumpus.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-wumpus.h' &&
X/* $Id: rt-wumpus.h,v 3.0 1992/12/14 00:14:15 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
Xvoid init_tree _((void));
Xvoid entire_tree _((ARTICLE*));
Xint tree_puts _((char*,ART_LINE,int));
Xint finish_tree _((ART_LINE));
X
X#ifdef DOINIT
X
Xstatic void find_depth _((ARTICLE*,int));
Xstatic void cache_tree _((ARTICLE*,int,char*));
Xstatic void display_tree _((ARTICLE*,char*));
Xstatic char letter _((ARTICLE*));
Xstatic int check_page_line _((void));
X
X#endif
SHAR_EOF
  $shar_touch -am 0711223693 'trn-3.6/rt-wumpus.h' &&
  chmod 0644 'trn-3.6/rt-wumpus.h' ||
  echo 'restore of trn-3.6/rt-wumpus.h failed'
  shar_count="`wc -c < 'trn-3.6/rt-wumpus.h'`"
  test 617 -eq "$shar_count" ||
    echo "trn-3.6/rt-wumpus.h: original size 617, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/edit_dist.c ==============
if test -f 'trn-3.6/edit_dist.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/edit_dist.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/edit_dist.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/edit_dist.c' &&
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"		/* Declare MEM_SIZE */
X#include "util.h"		/* Declare safemalloc() */
X
X#ifdef EDIT_DISTANCE
X
X/* edit_dist -- returns the minimum edit distance between two strings
X
X	Program by:  Mark Maimone   CMU Computer Science   13 Nov 89
X	Last Modified:  28 Jan 90
X
X   If the input strings have length n and m, the algorithm runs in time
X   O(nm) and space O(min(m,n)).
X
XHISTORY
X   13 Nov 89 (mwm) Created edit_dist() and set_costs().
X
X   28 Jan 90 (mwm) Added view_costs().  Should verify that THRESHOLD
X   computations will work even when THRESHOLD is not a multiple of
X   sizeof(int).
X
X   17 May 93 (mwm) Improved performance when used with trn's newsgroup
X   processing; assume all costs are 1, and you can terminate when a
X   threshold is exceeded.
X*/
X
X
X#define	TRN_SPEEDUP		/* Use a less-general version of the
X				   routine, one that's better for trn.
X				   All change costs are 1, and it's okay
X				   to terminate if the edit distance is
X				   known to exceed MIN_DIST */
X
X#define THRESHOLD 4000		/* worry about allocating more memory only
X				   when this # of bytes is exceeded */
X#define STRLENTHRESHOLD ((int) ((THRESHOLD / sizeof (int) - 3) / 2))
X
X#define SAFE_ASSIGN(x,y) (((x) != NULL) ? (*(x) = (y)) : (y))
X
X#define swap_int(x,y)  (_iswap = (x), (x) = (y), (y) = _iswap)
X#define swap_char(x,y) (_cswap = (x), (x) = (y), (y) = _cswap)
X#define min3(x,y,z) (_mx = (x), _my = (y), _mz = (z), (_mx < _my ? (_mx < _mz ? _mx : _mz) : (_mz < _my) ? _mz : _my))
X#define min2(x,y) (_mx = (x), _my = (y), (_mx < _my ? _mx : _my))
X
X
Xstatic int insert_cost = 1;
Xstatic int delete_cost = 1;
Xstatic int change_cost = 1;
Xstatic int swap_cost   = 1;
X
Xstatic int _iswap;			/* swap_int temp variable */
Xstatic char *_cswap;			/* swap_char temp variable */
Xstatic int _mx, _my, _mz;		/* min2, min3 temp variables */
X
X
X
Xvoid
Xview_costs(ins, del, ch, swap)
Xint *ins, *del, *ch, *swap;
X{
X    SAFE_ASSIGN(ins, insert_cost);
X    SAFE_ASSIGN(del, delete_cost);
X    SAFE_ASSIGN(ch, change_cost);
X    SAFE_ASSIGN(swap, swap_cost);
X} /* view_costs */
X
Xvoid
Xset_costs(ins, del, ch, swap)
Xint ins, del, ch, swap;
X{
X    insert_cost = ins;
X    delete_cost = del;
X    change_cost = ch;
X    swap_cost   = swap;
X} /* set_costs */
X
X
X/* edit_distn -- returns the edit distance between two strings, or -1 on
X   failure */
X
Xint
Xedit_distn(from, from_len, to, to_len)
Xchar *from, *to;
Xregister int from_len, to_len;
X{
X#ifndef TRN_SPEEDUP
X    register int ins, del, ch;	  	/* local copies of edit costs */
X#endif
X    register int row, col, index;	/* dynamic programming counters */
X    register int radix;			/* radix for modular indexing */
X#ifdef TRN_SPEEDUP
X    register int low;
X#endif
X    int *buffer;			/* pointer to storage for one row
X					   of the d.p. array */
X    static int store[THRESHOLD / sizeof (int)];
X					/* a small amount of static
X					   storage, to be used when the
X					   input strings are small enough */
X
X/* Handle trivial cases when one string is empty */
X
X    if (from == NULL || !from_len)
X	if (to == NULL || !to_len)
X	    return 0;
X	else
X	    return to_len * insert_cost;
X    else if (to == NULL || !to_len)
X	return from_len * delete_cost;
X
X/* Initialize registers */
X
X    radix = 2 * from_len + 3;
X#ifdef TRN_SPEEDUP
X#define ins 1
X#define del 1
X#define ch 1
X#define swap_cost 1
X#else
X    ins  = insert_cost;
X    del  = delete_cost;
X    ch   = change_cost;
X#endif
X
X/* Make   from   short enough to fit in the static storage, if it's at all
X   possible */
X
X    if (from_len > to_len && from_len > STRLENTHRESHOLD) {
X	swap_int(from_len, to_len);
X	swap_char(from, to);
X#ifndef TRN_SPEEDUP
X	swap_int(ins, del);
X#endif
X    } /* if from_len > to_len */
X
X/* Allocate the array storage (from the heap if necessary) */
X
X    if (from_len <= STRLENTHRESHOLD)
X	buffer = store;
X    else
X	buffer = (int *) safemalloc((MEM_SIZE) radix * sizeof (int));
X
X/* Here's where the fun begins.  We will find the minimum edit distance
X   using dynamic programming.  We only need to store two rows of the matrix
X   at a time, since we always progress down the matrix.  For example,
X   given the strings "one" and "two", and insert, delete and change costs
X   equal to 1:
X
X	   _  o  n  e
X	_  0  1  2  3
X	t  1  1  2  3
X	w  2  2  2  3
X	o  3  2  3  3
X
X   The dynamic programming recursion is defined as follows:
X
X	ar(x,0) := x * insert_cost
X	ar(0,y) := y * delete_cost
X	ar(x,y) := min(a(x - 1, y - 1) + (from[x] == to[y] ? 0 : change),
X		       a(x - 1, y) + insert_cost,
X		       a(x, y - 1) + delete_cost,
X		       a(x - 2, y - 2) + (from[x] == to[y-1] &&
X					  from[x-1] == to[y] ? swap_cost :
X					  infinity))
X
X   Since this only looks at most two rows and three columns back, we need
X   only store the values for the two preceeding rows.  In this
X   implementation, we do not explicitly store the zero column, so only 2 *
X   from_len + 2   words are needed.  However, in the implementation of the
X   swap_cost   check, the current matrix value is used as a buffer; we
X   can't overwrite the earlier value until the   swap_cost   check has
X   been performed.  So we use   2 * from_len + 3   elements in the buffer.
X*/
X
X#define ar(x,y,index) (((x) == 0) ? (y) * del : (((y) == 0) ? (x) * ins : \
X	buffer[mod(index)]))
X#define NW(x,y)	  ar(x, y, index + from_len + 2)
X#define N(x,y)	  ar(x, y, index + from_len + 3)
X#define W(x,y)	  ar(x, y, index + radix - 1)
X#define NNWW(x,y) ar(x, y, index + 1)
X#define mod(x) ((x) % radix)
X
X    index = 0;
X
X#ifdef DEBUG_EDITDIST
X    printf("      ");
X    for (col = 0; col < from_len; col++)
X	printf(" %c ", from[col]);
X    printf("\n   ");
X
X    for (col = 0; col <= from_len; col++)
X	printf("%2d ", col * del);
X#endif
X
X/* Row 0 is handled implicitly; its value at a given column is   col*del.
X   The loop below computes the values for Row 1.  At this point we know the
X   strings are nonempty.  We also don't need to consider swap costs in row
X   1.
X
X   COMMENT:  the indicies   row and col   below point into the STRING, so
X   the corresponding MATRIX indicies are   row+1 and col+1.
X*/
X
X    buffer[index++] = min2(ins + del, (from[0] == to[0] ? 0 : ch));
X#ifdef TRN_SPEEDUP
X    low = buffer[mod(index + radix - 1)];
X#endif
X
X#ifdef DEBUG_EDITDIST
X    printf("\n %c %2d %2d ", to[0], ins, buffer[index - 1]);
X#endif
X
X    for (col = 1; col < from_len; col++) {
X	buffer[index] = min3(
X		col * del + ((from[col] == to[0]) ? 0 : ch),
X		(col + 1) * del + ins,
X		buffer[index - 1] + del);
X#ifdef TRN_SPEEDUP
X	if (buffer[index] < low)
X	    low = buffer[index];
X#endif
X	index++;
X
X#ifdef DEBUG_EDITDIST
X	printf("%2d ", buffer[index - 1]);
X#endif
X
X    } /* for col = 1 */
X
X#ifdef DEBUG_EDITDIST
X    printf("\n %c %2d ", to[1], 2 * ins);
X#endif
X
X/* Now handle the rest of the matrix */
X
X    for (row = 1; row < to_len; row++) {
X	for (col = 0; col < from_len; col++) {
X	    buffer[index] = min3(
X		    NW(row, col) + ((from[col] == to[row]) ? 0 : ch),
X		    N(row, col + 1) + ins,
X		    W(row + 1, col) + del);
X	    if (from[col] == to[row - 1] && col > 0 &&
X		    from[col - 1] == to[row])		    
X		buffer[index] = min2(buffer[index],
X			NNWW(row - 1, col - 1) + swap_cost);
X
X#ifdef DEBUG_EDITDIST
X	    printf("%2d ", buffer[index]);
X#endif
X#ifdef TRN_SPEEDUP
X	    if (buffer[index] < low || col == 0)
X		low = buffer[index];
X#endif
X
X	    index = mod(index + 1);
X	} /* for col = 1 */
X#ifdef DEBUG_EDITDIST
X	if (row < to_len - 1)
X	    printf("\n %c %2d ", to[row+1], (row + 2) * ins);
X	else
X	    printf("\n");
X#endif
X#ifdef TRN_SPEEDUP
X	if (low > MIN_DIST)
X	    break;
X#endif
X    } /* for row = 1 */
X
X    row = buffer[mod(index + radix - 1)];
X    if (buffer != store)
X	free((char *) buffer);
X    return row;
X} /* edit_distn */
X
X#endif /* EDIT_DISTANCE */
SHAR_EOF
  $shar_touch -am 0711223593 'trn-3.6/edit_dist.c' &&
  chmod 0644 'trn-3.6/edit_dist.c' ||
  echo 'restore of trn-3.6/edit_dist.c failed'
  shar_count="`wc -c < 'trn-3.6/edit_dist.c'`"
  test 7885 -eq "$shar_count" ||
    echo "trn-3.6/edit_dist.c: original size 7885, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-page.h ==============
if test -f 'trn-3.6/rt-page.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-page.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-page.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-page.h' &&
X/* $Id: rt-page.h,v 3.0 1992/12/14 00:14:12 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#define PRESERVE_PAGE     0
X#define FILL_LAST_PAGE    1
X
Xbool set_sel_mode _((char_int));
Xbool set_sel_sort _((char_int));
Xvoid set_selector _((int,int));
Xvoid init_pages _((bool_int));
Xbool first_page _((void));
Xbool last_page _((void));
Xbool next_page _((void));
Xbool prev_page _((void));
Xvoid display_page _((void));
Xvoid update_page _((void));
Xvoid output_sel _((int));
X
X/* Stuff internal to rt-select.c */
X
X#ifdef DOINIT
X
Xstatic int count_subject_lines _((SUBJECT*, int*));
Xstatic int count_thread_lines _((SUBJECT*, int*));
Xstatic void display_article _((ARTICLE*, char_int, int));
Xstatic void display_subject _((SUBJECT*, char_int, int));
X
X#endif
SHAR_EOF
  $shar_touch -am 0424175594 'trn-3.6/rt-page.h' &&
  chmod 0644 'trn-3.6/rt-page.h' ||
  echo 'restore of trn-3.6/rt-page.h failed'
  shar_count="`wc -c < 'trn-3.6/rt-page.h'`"
  test 909 -eq "$shar_count" ||
    echo "trn-3.6/rt-page.h: original size 909, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/parsedate.y ==============
if test -f 'trn-3.6/parsedate.y' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/parsedate.y (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/parsedate.y (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/parsedate.y' &&
X%{
X/* $Revision: 1.12 $
X**
X**  Originally written by Steven M. Bellovin <smb@research.att.com> while
X**  at the University of North Carolina at Chapel Hill.  Later tweaked by
X**  a couple of people on Usenet.  Completely overhauled by Rich $alz
X**  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
X**  Further revised (removed obsolete constructs and cleaned up timezone
X**  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
X**  helped in September, 1992.
X**
X**  This grammar has six shift/reduce conflicts.
X**
X**  This code is in the public domain and has no copyright.
X*/
X/* SUPPRESS 530 *//* Empty body for statement */
X/* SUPPRESS 593 on yyerrlab *//* Label was not used */
X/* SUPPRESS 593 on yynewstate *//* Label was not used */
X/* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include "config.h"
X#include <time.h>
X
X#define yyparse		date_parse
X#define yylex		date_lex
X#define yyerror		date_error
X
X
X    /* See the LeapYears table in Convert. */
X#define EPOCH		1970
X#define END_OF_TIME	2038
X    /* Constants for general time calculations. */
X#define DST_OFFSET	1
X#define SECSPERDAY	(24L * 60L * 60L)
X    /* Readability for TABLE stuff. */
X#define HOUR(x)		(x * 60)
X
X#define LPAREN		'('
X#define RPAREN		')'
X#define IS7BIT(x)	((unsigned int)(x) < 0200)
X
X#define SIZEOF(array)	((int)(sizeof array / sizeof array[0]))
X#define ENDOF(array)	(&array[SIZEOF(array)])
X
X
X/*
X**  An entry in the lexical lookup table.
X*/
Xtypedef struct _TABLE {
X    char	*name;
X    int		type;
X    time_t	value;
X} TABLE;
X
X/*
X**  Daylight-savings mode:  on, off, or not yet known.
X*/
Xtypedef enum _DSTMODE {
X    DSTon, DSToff, DSTmaybe
X} DSTMODE;
X
X/*
X**  Meridian:  am, pm, or 24-hour style.
X*/
Xtypedef enum _MERIDIAN {
X    MERam, MERpm, MER24
X} MERIDIAN;
X
X
X/*
X**  Global variables.  We could get rid of most of them by using a yacc
X**  union, but this is more efficient.  (This routine predates the
X**  yacc %union construct.)
X*/
Xstatic char	*yyInput;
Xstatic DSTMODE	yyDSTmode;
Xstatic int	yyHaveDate;
Xstatic int	yyHaveRel;
Xstatic int	yyHaveTime;
Xstatic time_t	yyTimezone;
Xstatic time_t	yyDay;
Xstatic time_t	yyHour;
Xstatic time_t	yyMinutes;
Xstatic time_t	yyMonth;
Xstatic time_t	yySeconds;
Xstatic time_t	yyYear;
Xstatic MERIDIAN	yyMeridian;
Xstatic time_t	yyRelMonth;
Xstatic time_t	yyRelSeconds;
X
X
Xextern struct tm	*localtime();
X
Xstatic void		date_error();
X%}
X
X%union {
X    time_t		Number;
X    enum _MERIDIAN	Meridian;
X}
X
X%token	tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
X%token	tUNUMBER tZONE
X
X%type	<Number>	tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
X%type	<Number>	tSNUMBER tUNUMBER tZONE numzone zone
X%type	<Meridian>	tMERIDIAN o_merid
X
X%%
X
Xspec	: /* NULL */
X	| spec item
X	;
X
Xitem	: time {
X	    yyHaveTime++;
X#ifdef lint
X	    /* I am compulsive about lint natterings... */
X	    if (yyHaveTime == -1) {
X		YYERROR;
X	    }
X#endif /* lint */
X	}
X	| time zone {
X	    yyHaveTime++;
X	    yyTimezone = $2;
X	}
X	| date {
X	    yyHaveDate++;
X	}
X	| rel {
X	    yyHaveRel = 1;
X	}
X	;
X
Xtime	: tUNUMBER o_merid {
X	    if ($1 < 100) {
X		yyHour = $1;
X		yyMinutes = 0;
X	    }
X	    else {
X		yyHour = $1 / 100;
X		yyMinutes = $1 % 100;
X	    }
X	    yySeconds = 0;
X	    yyMeridian = $2;
X	}
X	| tUNUMBER ':' tUNUMBER o_merid {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yySeconds = 0;
X	    yyMeridian = $4;
X	}
X	| tUNUMBER ':' tUNUMBER numzone {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yyTimezone = $4;
X	    yyMeridian = MER24;
X	    yyDSTmode = DSToff;
X	}
X	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yySeconds = $5;
X	    yyMeridian = $6;
X	}
X	| tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
X	    yyHour = $1;
X	    yyMinutes = $3;
X	    yySeconds = $5;
X	    yyTimezone = $6;
X	    yyMeridian = MER24;
X	    yyDSTmode = DSToff;
X	}
X	;
X
Xzone	: tZONE {
X	    $$ = $1;
X	    yyDSTmode = DSToff;
X	}
X	| tDAYZONE {
X	    $$ = $1;
X	    yyDSTmode = DSTon;
X	}
X	| tZONE numzone {
X	    /* Only allow "GMT+300" and "GMT-0800" */
X	    if ($1 != 0) {
X		YYABORT;
X	    }
X	    $$ = $2;
X	    yyDSTmode = DSToff;
X	}
X	| numzone {
X	    $$ = $1;
X	    yyDSTmode = DSToff;
X	}
X	;
X
Xnumzone	: tSNUMBER {
X	    int		i;
X
X	    /* Unix and GMT and numeric timezones -- a little confusing. */
X	    if ($1 < 0) {
X		/* Don't work with negative modulus. */
X		$1 = -$1;
X		if ($1 > 9999 || (i = $1 % 100) >= 60) {
X		    YYABORT;
X		}
X		$$ = ($1 / 100) * 60 + i;
X	    }
X	    else {
X		if ($1 > 9999 || (i = $1 % 100) >= 60) {
X		    YYABORT;
X		}
X		$$ = -(($1 / 100) * 60 + i);
X	    }
X	}
X	;
X
Xdate	: tUNUMBER '/' tUNUMBER {
X	    yyMonth = $1;
X	    yyDay = $3;
X	}
X	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
X	    if ($1 > 100) {
X		yyYear = $1;
X		yyMonth = $3;
X		yyDay = $5;
X	    }
X	    else {
X		yyMonth = $1;
X		yyDay = $3;
X		yyYear = $5;
X	    }
X	}
X	| tMONTH tUNUMBER {
X	    yyMonth = $1;
X	    yyDay = $2;
X	}
X	| tMONTH tUNUMBER ',' tUNUMBER {
X	    yyMonth = $1;
X	    yyDay = $2;
X	    yyYear = $4;
X	}
X	| tUNUMBER tMONTH {
X	    yyDay = $1;
X	    yyMonth = $2;
X	}
X	| tUNUMBER tMONTH tUNUMBER {
X	    yyDay = $1;
X	    yyMonth = $2;
X	    yyYear = $3;
X	}
X	| tDAY ',' tUNUMBER tMONTH tUNUMBER {
X	    yyDay = $3;
X	    yyMonth = $4;
X	    yyYear = $5;
X	}
X	;
X
Xrel	: tSNUMBER tSEC_UNIT {
X	    yyRelSeconds += $1 * $2;
X	}
X	| tUNUMBER tSEC_UNIT {
X	    yyRelSeconds += $1 * $2;
X	}
X	| tSNUMBER tMONTH_UNIT {
X	    yyRelMonth += $1 * $2;
X	}
X	| tUNUMBER tMONTH_UNIT {
X	    yyRelMonth += $1 * $2;
X	}
X	;
X
Xo_merid	: /* NULL */ {
X	    $$ = MER24;
X	}
X	| tMERIDIAN {
X	    $$ = $1;
X	}
X	;
X
X%%
X
X/* Month and day table. */
Xstatic TABLE	MonthDayTable[] = {
X    { "january",	tMONTH,  1 },
X    { "february",	tMONTH,  2 },
X    { "march",		tMONTH,  3 },
X    { "april",		tMONTH,  4 },
X    { "may",		tMONTH,  5 },
X    { "june",		tMONTH,  6 },
X    { "july",		tMONTH,  7 },
X    { "august",		tMONTH,  8 },
X    { "september",	tMONTH,  9 },
X    { "october",	tMONTH, 10 },
X    { "november",	tMONTH, 11 },
X    { "december",	tMONTH, 12 },
X	/* The value of the day isn't used... */
X    { "sunday",		tDAY, 0 },
X    { "monday",		tDAY, 0 },
X    { "tuesday",	tDAY, 0 },
X    { "wednesday",	tDAY, 0 },
X    { "thursday",	tDAY, 0 },
X    { "friday",		tDAY, 0 },
X    { "saturday",	tDAY, 0 },
X};
X
X/* Time units table. */
Xstatic TABLE	UnitsTable[] = {
X    { "year",		tMONTH_UNIT,	12 },
X    { "month",		tMONTH_UNIT,	1 },
X    { "week",		tSEC_UNIT,	7L * 24 * 60 * 60 },
X    { "day",		tSEC_UNIT,	1L * 24 * 60 * 60 },
X    { "hour",		tSEC_UNIT,	60 * 60 },
X    { "minute",		tSEC_UNIT,	60 },
X    { "min",		tSEC_UNIT,	60 },
X    { "second",		tSEC_UNIT,	1 },
X    { "sec",		tSEC_UNIT,	1 },
X};
X
X/* Timezone table. */
Xstatic TABLE	TimezoneTable[] = {
X    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
X    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
X    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
X    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
X    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
X    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
X    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
X    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
X    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
X    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
X    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
X    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
X    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
X    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
X    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
X    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
X    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
X    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
X    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
X    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
X    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
X    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
X    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
X    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
X    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
X    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
X    { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
X    { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
X    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
X    { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
X    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
X    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
X    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
X    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
X    { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
X    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
X    { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
X    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
X    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
X    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
X    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
X    { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
X    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
X    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
X    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
X    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
X    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
X    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
X
X    /* For completeness we include the following entries. */
X#if 0
X
X    /* Duplicate names.  Either they conflict with a zone listed above
X     * (which is either more likely to be seen or just been in circulation
X     * longer), or they conflict with another zone in this section and
X     * we could not reasonably choose one over the other. */
X    { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
X    { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
X    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
X    { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
X    { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
X    { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
X    { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
X    { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
X    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
X    { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
X    { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
X    { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
X    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
X    { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
X    { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
X    { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
X    { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
X    { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
X    { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
X    { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
X    { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
X    { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
X    { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
X
X    /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
X    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
X    { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
X    { "at",	tZONE,     HOUR( 2) },	/* Azores */
X    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
X    { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
X    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
X    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
X    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
X    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
X    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
X    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
X    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
X    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
X    { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
X    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
X    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
X    { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
X    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
X    { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
X    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
X    { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
X    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
X    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
X    { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
X    { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
X    { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
X    { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
X#endif /* 0 */
X};
X
X
X/* ARGSUSED */
Xstatic void
Xdate_error(s)
X    char	*s;
X{
X    /* NOTREACHED */
X}
X
X
Xstatic time_t
XToSeconds(Hours, Minutes, Seconds, Meridian)
X    time_t	Hours;
X    time_t	Minutes;
X    time_t	Seconds;
X    MERIDIAN	Meridian;
X{
X    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
X	return -1;
X    if (Meridian == MER24) {
X	if (Hours < 0 || Hours > 23)
X	    return -1;
X    }
X    else {
X	if (Hours < 1 || Hours > 12)
X	    return -1;
X	if (Hours == 12)
X	    Hours = 0;
X	if (Meridian == MERpm)
X	    Hours += 12;
X    }
X    return (Hours * 60L + Minutes) * 60L + Seconds;
X}
X
X
Xstatic time_t
XConvert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst)
X    time_t	Month;
X    time_t	Day;
X    time_t	Year;
X    time_t	Hours;
X    time_t	Minutes;
X    time_t	Seconds;
X    MERIDIAN	Meridian;
X    DSTMODE	dst;
X{
X    static int	DaysNormal[13] = {
X	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X    };
X    static int	DaysLeap[13] = {
X	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X    };
X    static int	LeapYears[] = {
X	1972, 1976, 1980, 1984, 1988, 1992, 1996,
X	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
X    };
X    register int	*yp;
X    register int	*mp;
X    register time_t	Julian;
X    register int	i;
X    time_t		tod;
X
X    if (Year < 0)
X	Year = -Year;
X    if (Year < 100)
X	Year += 1900;
X    if (Year < EPOCH)
X	Year += 100;
X    for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
X	if (Year == *yp) {
X	    mp = DaysLeap;
X	    break;
X	}
X    if (Year < EPOCH || Year > END_OF_TIME
X     || Month < 1 || Month > 12
X     /* NOSTRICT *//* conversion from long may lose accuracy */
X     || Day < 1 || Day > mp[(int)Month])
X	return -1;
X
X    Julian = Day - 1 + (Year - EPOCH) * 365;
X    for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
X	if (Year <= *yp)
X	    break;
X    for (i = 1; i < Month; i++)
X	Julian += *++mp;
X    Julian *= SECSPERDAY;
X    Julian += yyTimezone * 60L;
X    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
X	return -1;
X    Julian += tod;
X    tod = Julian;
X    if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
X	Julian -= DST_OFFSET * 60L * 60L;
X    return Julian;
X}
X
X
Xstatic time_t
XDSTcorrect(Start, Future)
X    time_t	Start;
X    time_t	Future;
X{
X    time_t	StartDay;
X    time_t	FutureDay;
X
X    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
X    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
X    return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L;
X}
X
X
Xstatic time_t
XRelativeMonth(Start, RelMonth)
X    time_t	Start;
X    time_t	RelMonth;
X{
X    struct tm	*tm;
X    time_t	Month;
X    time_t	Year;
X
X    tm = localtime(&Start);
X    Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
X    Year = Month / 12;
X    Month = Month % 12 + 1;
X    return DSTcorrect(Start,
X	    Convert(Month, (time_t)tm->tm_mday, Year,
X		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
X		MER24, DSTmaybe));
X}
X
X
Xstatic int
XLookupWord(buff, length)
X    char		*buff;
X    register int	length;
X{
X    register char	*p;
X    register char	*q;
X    register TABLE	*tp;
X    register int	c;
X
X    p = buff;
X    c = p[0];
X
X    /* See if we have an abbreviation for a month. */
X    if (length == 3 || (length == 4 && p[3] == '.'))
X	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
X	    q = tp->name;
X	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X	}
X    else
X	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
X	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X
X    /* Try for a timezone. */
X    for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
X	if (c == tp->name[0] && p[1] == tp->name[1]
X	 && strcmp(p, tp->name) == 0) {
X	    yylval.Number = tp->value;
X	    return tp->type;
X	}
X
X    /* Try the units table. */
X    for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
X	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
X	    yylval.Number = tp->value;
X	    return tp->type;
X	}
X
X    /* Strip off any plural and try the units table again. */
X    if (--length > 0 && p[length] == 's') {
X	p[length] = '\0';
X	for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
X	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
X		p[length] = 's';
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X	p[length] = 's';
X    }
X    length++;
X
X    /* Drop out any periods. */
X    for (p = buff, q = (char*)buff; *q; q++)
X	if (*q != '.')
X	    *p++ = *q;
X    *p = '\0';
X
X    /* Try the meridians. */
X    if (buff[1] == 'm' && buff[2] == '\0') {
X	if (buff[0] == 'a') {
X	    yylval.Meridian = MERam;
X	    return tMERIDIAN;
X	}
X	if (buff[0] == 'p') {
X	    yylval.Meridian = MERpm;
X	    return tMERIDIAN;
X	}
X    }
X
X    /* If we saw any periods, try the timezones again. */
X    if (p - buff != length) {
X	c = buff[0];
X	for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
X	    if (c == tp->name[0] && p[1] == tp->name[1]
X	    && strcmp(p, tp->name) == 0) {
X		yylval.Number = tp->value;
X		return tp->type;
X	    }
X    }
X
X    /* Unknown word -- assume GMT timezone. */
X    yylval.Number = 0;
X    return tZONE;
X}
X
X
Xint
Xdate_lex()
X{
X    register char	c;
X    register char	*p;
X    char		buff[20];
X    register int	sign;
X    register int	i;
X    register int	nesting;
X
X    for ( ; ; ) {
X	/* Get first character after the whitespace. */
X	for ( ; ; ) {
X	    while (isspace(*yyInput))
X		yyInput++;
X	    c = *yyInput;
X
X	    /* Ignore RFC 822 comments, typically time zone names. */
X	    if (c != LPAREN)
X		break;
X	    for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
X		if (c == LPAREN)
X		    nesting++;
X		else if (!IS7BIT(c) || c == '\0' || c == '\r'
X		     || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
X		    /* Lexical error: bad comment. */
X		    return '?';
X	    yyInput++;
X	}
X
X	/* A number? */
X	if (isdigit(c) || c == '-' || c == '+') {
X	    if (c == '-' || c == '+') {
X		sign = c == '-' ? -1 : 1;
X		yyInput++;
X		if (!isdigit(*yyInput))
X		    /* Skip the plus or minus sign. */
X		    continue;
X	    }
X	    else
X		sign = 0;
X	    for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); )
X		i = 10 * i + c - '0';
X	    yyInput--;
X	    yylval.Number = sign < 0 ? -i : i;
X	    return sign ? tSNUMBER : tUNUMBER;
X	}
X
X	/* A word? */
X	if (isalpha(c)) {
X	    for (p = buff; (c = *yyInput++) == '.' || isalpha(c); )
X		if (p < &buff[sizeof buff - 1])
X		    *p++ = isupper(c) ? tolower(c) : c;
X	    *p = '\0';
X	    yyInput--;
X	    return LookupWord(buff, p - buff);
X	}
X
X	return *yyInput++;
X    }
X}
X
X
Xtime_t
Xparsedate(p)
X    char		*p;
X{
X    extern int		date_parse();
X    time_t		Start;
X
X    yyInput = p;
X
X    yyYear = 0;
X    yyMonth = 0;
X    yyDay = 0;
X    yyTimezone = 0;
X    yyDSTmode = DSTmaybe;
X    yyHour = 0;
X    yyMinutes = 0;
X    yySeconds = 0;
X    yyMeridian = MER24;
X    yyRelSeconds = 0;
X    yyRelMonth = 0;
X    yyHaveDate = 0;
X    yyHaveRel = 0;
X    yyHaveTime = 0;
X
X    if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
X	return -1;
X
X    if (yyHaveDate || yyHaveTime) {
X	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
X		    yyMeridian, yyDSTmode);
X	if (Start < 0)
X	    return -1;
X    }
X    else
X	return -1;
X
X    Start += yyRelSeconds;
X    if (yyRelMonth)
X	Start += RelativeMonth(Start, yyRelMonth);
X
X    /* Have to do *something* with a legitimate -1 so it's distinguishable
X     * from the error return value.  (Alternately could set errno on error.) */
X    return Start == -1 ? 0 : Start;
X}
X
X
X#ifdef TEST
X
X#if YYDEBUG
Xextern int	yydebug;
X#endif /* YYDEBUG */
X
X/* ARGSUSED */
Xint
Xmain(ac, av)
X    int		ac;
X    char	*av[];
X{
X    char	buff[128];
X    time_t	d;
X
X#if YYDEBUG
X    yydebug = 1;
X#endif /* YYDEBUG */
X
X    (void)printf("Enter date, or blank line to exit.\n\t> ");
X    for ( ; ; ) {
X	(void)printf("\t> ");
SHAR_EOF
  : || echo 'restore of trn-3.6/parsedate.y failed'
fi
echo 'End of archive part 13'
echo 'File trn-3.6/parsedate.y is continued in part 14'
echo 14 > _sharseq.tmp
exit 0


