BSD 4_4 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 4 Jun 1993 12:17:18 +0000 (04:17 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 4 Jun 1993 12:17:18 +0000 (04:17 -0800)
Work on file usr/src/contrib/news/trn3/patchlevel.h
Work on file usr/src/contrib/news/trn3/Pnews.SH
Work on file usr/src/contrib/news/trn3/art.c
Work on file usr/src/contrib/news/trn3/intrp.c
Work on file usr/src/contrib/news/trn3/help.c
Work on file usr/src/contrib/news/trn3/artcheck.c
Work on file usr/src/contrib/news/trn3/sw.c
Work on file usr/src/contrib/news/trn3/rt-process.c
Work on file usr/src/contrib/news/trn3/ngdata.c

Synthesized-from: CSRG/cd3/4.4

usr/src/contrib/news/trn3/Pnews.SH [new file with mode: 0755]
usr/src/contrib/news/trn3/art.c [new file with mode: 0644]
usr/src/contrib/news/trn3/artcheck.c [new file with mode: 0644]
usr/src/contrib/news/trn3/help.c [new file with mode: 0644]
usr/src/contrib/news/trn3/intrp.c [new file with mode: 0644]
usr/src/contrib/news/trn3/ngdata.c [new file with mode: 0644]
usr/src/contrib/news/trn3/patchlevel.h [new file with mode: 0644]
usr/src/contrib/news/trn3/rt-process.c [new file with mode: 0644]
usr/src/contrib/news/trn3/sw.c [new file with mode: 0644]

diff --git a/usr/src/contrib/news/trn3/Pnews.SH b/usr/src/contrib/news/trn3/Pnews.SH
new file mode 100755 (executable)
index 0000000..90ce994
--- /dev/null
@@ -0,0 +1,770 @@
+case $CONFIG in
+    '') . ./config.sh ;;
+esac
+echo "Extracting Pnews (with variable substitutions)"
+$spitshell >Pnews <<!GROK!THIS!
+$startsh
+# $Id: Pnews.SH,v 3.0 1992/02/23 21:25:39 davison Trn $
+# 
+# This software is Copyright 1991 by Stan Barber. 
+#
+# Permission is hereby granted to copy, reproduce, redistribute or otherwise
+# use this software as long as: there is no monetary profit gained
+# specifically from the use or reproduction of this software, it is not
+# sold, rented, traded or otherwise marketed, and this copyright notice is
+# included prominently in any copy made. 
+#
+# The author make no claims as to the fitness or correctness of this software
+# for any use whatsoever, and it is provided as is. Any use of this software
+# is at the user's own risk. 
+#
+# syntax: Pnews -h headerfile                  or
+#        Pnews -h headerfile oldarticle        or
+#         Pnews newsgroup title                        or just
+#         Pnews
+
+export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)
+
+# System dependencies
+
+mailer="${mailer-/bin/mail}"
+# if you change this to something that does signatures, take out signature code
+
+case $d_portable in
+define)
+    # where recordings, distributions and moderators are kept
+    lib=\`$filexp $newslib\`
+    # where important rn things are kept
+    rnlib=\`$filexp $privlib\`
+    ;;
+undef)
+    # where recordings, distributions and moderators are kept
+    lib="$newslib"
+    # where important rn things are kept
+    rnlib="$privlib"
+    ;;
+esac
+
+# your organization name
+orgname="$orgname"
+# what pager you use--if you have kernal paging use cat
+pager="\${PAGER-$pager}"
+# how you derive full names, bsd, usg, or other
+nametype="$nametype"
+# default editor
+defeditor="$defeditor"
+# how not to echo with newline
+n="$n"
+c="$c"
+
+# You should also look at the distribution warnings below marked !DIST!
+# to make sure any distribution regions you are a member of are included.
+# The following are some prototypical distribution groups.  If you do not
+# use them all set the unused ones to a non-null string such as 'none'.
+loc="$locdist"
+org="$orgdist"
+multistate="$multistatedist"
+city="$citydist"
+state="$statedist"
+cntry="$cntrydist"
+cont="$contdist"
+
+test=${test-test}
+sed=${sed-sed}
+echo=${echo-echo}
+cat=${cat-cat}
+egrep=${egrep-egrep}
+grep=${grep-grep}
+tr=${tr-tr}
+inews=${inewsloc-inews}
+nidump=${nidump}
+ypmatch=${ypmatch}
+
+!GROK!THIS!
+case "$ignoreorg" in
+define) $spitshell >>Pnews <<'!NO!SUBS!'
+orgname=${NEWSORG-$orgname}
+!NO!SUBS!
+       ;;
+*)     $spitshell >>Pnews <<'!NO!SUBS!'
+orgname=${NEWSORG-${ORGANIZATION-$orgname}}
+!NO!SUBS!
+       ;;
+esac
+$spitshell >>Pnews <<'!NO!SUBS!'
+dotdir=${DOTDIR-${HOME-$LOGDIR}}
+tmpart=$dotdir/.article
+artcheck=$rnlib/artcheck
+speller=$rnlib/Speller
+
+if $test -f $dotdir/.pnewsexpert; then
+    expertise=expert
+else
+    $cat <<'EOM'
+I see you've never used this version of Pnews before.  I will give you extra
+help this first time through, but then you must remember what you learned.
+If you don't understand any question, type h and a CR (carriage return) for
+help.
+
+If you've never posted an article to the net before, it is HIGHLY recommended
+that you read the netiquette document found in news.announce.newusers so
+that you'll know to avoid the commonest blunders.  To do that, interrupt
+Pnews, get to the top-level prompt of [t]rn, and use the command
+"g news.announce.newusers" to go to that group.
+
+EOM
+    expertise=beginner
+fi
+
+case $cntry in
+  can) Stpr=Province ; stpr=province ;;
+  *)   Stpr=State ; stpr=state ;;
+esac
+
+case $multistate in
+  pnw) multistpr="Pacific NorthWest" ;;
+  *)   multistpr="Multi-State Area" ;;
+esac
+
+headerfile=""
+case $# in
+0) ;;
+*)  case $1 in
+    -h)
+       headerfile="$2"
+       shift
+       shift
+       case $# in
+       0)
+           oldart=""
+           ;;
+       *)
+           oldart="$1"
+           shift
+           ;;
+       esac
+       ;;
+    esac
+    ;;
+esac
+
+case $headerfile in
+'')
+    . $rnlib/Pnews.header
+    ;;
+*)
+    $cat < $headerfile  > $tmpart
+    ;;
+esac
+    rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ; $echo Article appended to ${HOME-$LOGDIR}/dead.article ; exit"
+    trap "$rescue" 1
+    trap "$rescue" 2
+
+$echo ""
+
+# extract the newsgroups list and distribution
+hdr_newsgroups=`$sed -n -e '/^Newsgroups:/{' -e 's///' -e 's/,/ /g' -e p -e q -e '}' $tmpart`
+hdr_distribution=`$sed -n -e '/^Distribution:/{' -e 's///' -e p -e q -e '}' $tmpart`
+
+# check for "poster" magic cookie.  Allow erroneous user@site too.
+flag=0
+for ng in $hdr_newsgroups ; do
+    case "$ng" in
+       poster) flag=1 ;;
+       *@*) flag=1 ;;
+       *)      ;;
+    esac
+done
+case $flag in
+1)
+    $echo " "
+    $echo "The original author has requested that messages be sent back via"
+    $echo "mail rather than posting to news.  Do you want to jump out of this"
+    $echo $n "and mail your reply instead? [yn] $c"
+    read ans
+    case $ans in
+       n*) ;;
+       *)  exit ;;
+    esac
+    $echo " "
+    $echo "OK, but you will have to edit the 'Newsgroups:' line in the message."
+    ;;
+esac
+  
+# play recorded message
+if $test -s ${lib}/recording ; then
+     for ng in $hdr_newsgroups ; do
+       _rec1=${lib}/`$sed -n "/^$ng/s/^.*      //p" ${lib}/recording`
+       _tmp=`$echo $ng |$sed "s/\..*//"`
+       _rec2=${lib}/`$cat -s ${lib}/recording|$grep ${_tmp}.all|$sed "s/^.*    //"`
+       if $test -f ${_rec1} ; then
+           $cat -s ${_rec1}
+       fi
+       if $test -f ${_rec2} ; then
+           $cat -s ${_rec2}
+       fi
+    done
+fi
+
+# determine the distribution of this message
+set X $hdr_distribution
+shift
+if $test $# -gt 0 ; then
+    dist=$1.whatever
+else
+    set X $hdr_newsgroups
+    shift
+    if $test $# -gt 0 ; then
+       dist=$1.whatever
+    else
+       dist=misc.whatever
+    fi
+fi
+case $dist in
+*.*)
+    ;;
+*)
+    dist=$dist.whatever
+    ;;
+esac
+
+# tell them what we think they are doing... !DIST!
+case $dist in
+world.*|comp.*|news.*|sci.*|rec.*|misc.*|soc.*|talk.*|alt.*|'')
+    $cat <<'EOM'
+This program posts news to thousands of machines throughout the entire
+civilized world.  Your message will cost the net hundreds if not thousands of
+dollars to send everywhere.  Please be sure you know what you are doing.
+
+EOM
+    ;;
+vmsnet.*)
+    $echo 'This program posts news to many machines.'
+    ;;
+bit.*)
+    $echo 'This program posts news to many machines on BITNET.'
+    ;;
+ddn.*)
+    $echo 'This program posts news to many machines throughout the internet.'
+    ;;
+$cont.*)
+    $echo 'This program posts news to many machines throughout the continent.'
+    ;;
+$cntry.*)
+    $echo 'This program posts news to many machines throughout the country.'
+    ;;
+$multistate.*)
+    $echo "This program posts news to many machines throughout the ${multistpr}."
+    ;;
+$state.*)
+    $echo "This program posts news to many machines throughout the ${stpr}."
+    ;;
+$city.*)
+    $echo 'This program posts news to many machines throughout the city.'
+    ;;
+$org.*)
+    $echo 'This program posts news to machines throughout the organization.'
+    ;;
+$loc.*)
+    $echo 'This program posts news to machines throughout the local organization.'
+    ;;
+*.*)
+    $echo 'This program may post news to many machines.'
+    ;;
+to.*)
+    $echo 'This program may post news to a particular machine.'
+    ;;
+*)
+    $echo 'This program posts news to everyone on the machine.'
+    ;;
+esac
+ans=""
+while $test "$ans" = "" ; do
+    $echo $n "Are you absolutely sure that you want to do this? [ny] $c"
+    read ans
+    case $ans in
+    y*) ;;
+    f*) ;;
+    h*) $cat <<'EOH'
+
+Type n or CR to exit, y to post.
+
+EOH
+       ans="" ;;
+    *) exit ;;
+    esac
+done
+
+file=h
+while $test "$file" = h ; do
+    $echo ""
+    $echo $n "Prepared file to include [none]: $c"
+    read file
+    case $file in
+    h)
+       $cat <<'EOH'
+
+If you have already produced the body of your article, type the filename
+for it here.  If you just want to proceed directly to the editor, type a
+RETURN.  In any event, you will be allowed to edit as many times as you
+want before you send off the article.
+EOH
+       ;;
+    '')
+       $echo "" >> $tmpart
+       state=edit
+       ;;
+    *)
+       $cat $file >>$tmpart
+       state=check
+       ;;
+    esac
+done
+
+$echo ""
+
+while true ; do
+    case $state in
+    edit)
+       case $expertise in
+       beginner)
+           $cat </dev/null >$dotdir/.pnewsexpert
+           $cat <<'EOMessage'
+A temporary file has been created for you to edit.  Be sure to leave at
+least one blank line between the header and the body of your message.
+(And until a certain bug is fixed all over the net, don't start the body of
+your message with any indentation, or it may get eaten.)
+
+Within the header may be fields that you don't understand.  If you don't
+understand a field (or even if you do), you can simply leave it blank, and
+it will go away when the article is posted.
+
+Type return to get the default editor, or type the name of your favorite
+editor.
+
+EOMessage
+           ;;
+       esac
+       case "${VISUAL-${EDITOR-}}" in
+       '')
+           tmp=h
+           ;;
+       *)
+           tmp=''
+           ;;
+       esac
+       while $test "$tmp" = h ; do
+           $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c"
+           read tmp
+           case $tmp in
+           h)
+               $cat <<'EOH'
+
+Type a return to get the default editor, or type the name of the editor you
+prefer.  The default editor depends on the VISUAL and EDITOR environment
+variables.
+
+EOH
+               ;;
+           '')
+               ;;
+           *)
+               VISUAL=$tmp
+               export VISUAL
+               ;;
+           esac
+       done
+       trap : 2
+       ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart
+       trap "$rescue" 2
+       state=check
+       ;;
+       
+    check)
+       # warn about long lines, malformed headers, misspelled newsgroups
+       $artcheck $tmpart 79 $lib/newsgroups
+       state=ask
+       ;;
+
+    ask)
+       $echo ""
+       $echo $n "Check spelling, Send, Abort, Edit, or List? $c"
+       read ans
+       
+       case "$ans" in
+       a*)
+           state=rescue
+           ;;
+       e*)
+           set $ans
+           case $# in
+           2)  VISUAL="$2" ;;
+           esac
+           state=edit
+           ;;
+       l*)
+           $pager $tmpart
+           state=ask
+           ;;
+       s*)
+           state=send
+           ;;
+       c*)
+           $speller $tmpart
+           state=ask
+           ;;
+       h*)
+           $cat <<'EOH'
+
+Type c to check the article's spelling, s to send the article, a to abort
+and append the article to dead.article, e to edit the article again, or l
+to list the article with your pager.
+
+To invoke an alternate editor, type 'e editor'.
+EOH
+       esac
+       ;;
+    
+    send)
+       set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'`
+       shift
+       case $# in
+       2)
+           state=cleanup
+           if $test -f $lib/moderators; then
+               tryinews=no
+               shift
+               case "$1" in
+               *,*) set `$echo $1 | tr ',' ' '`;;
+               esac
+               for newsgroup in $*; do
+# the following screwy sed should prevent Eunice from hanging on no match
+                   moderator=`$sed <$lib/moderators \
+                   -e "/^$newsgroup[   ]/!s/.*//" \
+                   -e "s/^$newsgroup[  ]//"`
+                   case ${moderator}X in
+                   X)  tryinews=yes
+                       ;;
+                   *)
+                       $echo Mailing to moderator $moderator
+                       case "$sign" in
+                       n*) ;;
+                       *)
+                           if $test -f $dotdir/.signature; then
+                               $echo $n "Append .signature file? [y] $c"
+                               read ans
+                               case $ans in
+                               ''|y*)
+                                   $echo "-- " >> $tmpart
+                                   $cat $dotdir/.signature >> $tmpart
+                                   ;;
+                               esac
+                           fi
+                           sign=no
+                           ;;
+                       esac
+                       case "$mailer" in
+                       *recmail)
+                           $echo To: $moderator | $cat - $tmpart | $mailer
+                           ;;
+                       *)
+                           $mailer $moderator < $tmpart
+                           ;;
+                       esac
+                       case $? in
+                       0) ;;
+                       *)
+                           $echo Unable to mail to moderator $moderator
+                           state=rescue
+                           ;;
+                       esac
+                       ;;
+                   esac
+               done
+           else
+               tryinews=yes
+           fi
+           case "$tryinews" in
+           yes)
+               if $sed '1,/^[   ]*$/{/^[A-Z][-A-Za-z0-9]*:[     ]*$/d;
+                       /^X-ORIGINAL-NEWSGROUPS:.*$/d;
+                       /^[Cc][Cc]:/d;}' $tmpart | $inews -h ; then
+                   : null
+               else
+                   state=rescue
+               fi
+               cc=`$sed -n '1,/^[       ]*$/{/^[Cc][Cc]:[       ][^     ]/p;}' $tmpart|
+                $sed 's/^[Cc][Cc]:[     ][      ]*//'`
+               if $test "$cc " != " " ; then
+                   set -- $cc
+                   case "$mailer" in
+                   *recmail)
+                       $echo To: $cc | $cat - $tmpart | $mailer
+                       ;;
+                   *)
+                       set -- `echo $cc | sed 's/,/ /g'`
+                       $mailer $@ < $tmpart
+                       ;;
+                   esac
+               fi
+               ;;
+           esac
+           ;;
+       *)
+           $echo ""
+           $echo "Malformed Newsgroups line."
+           $echo ""
+           sleep 1
+           state=edit
+           ;;
+       esac
+       ;;
+    rescue)
+       if $test -s $tmpart; then
+               $cat $tmpart >> ${HOME-$LOGDIR}/dead.article
+               $echo "Article appended to ${HOME-$LOGDIR}/dead.article"
+               $echo "A copy may be temporarily found in $tmpart"
+       else
+               $echo "Null article discarded."
+       fi
+       exit
+       ;;
+    cleanup)
+       case "${AUTHORCOPY-none}" in
+       none)
+           ;;
+       *)
+           set X ${USER-${LOGNAME-`who am i`}} unknown
+           shift
+           $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $AUTHORCOPY "From $1 `LANG= date`"
+           if $test $? -eq 0 ; then
+               $echo "Article appended to $AUTHORCOPY"
+           else
+               $echo "Cannot append to $AUTHORCOPY"
+           fi
+           ;;
+       esac
+       exit
+       ;;
+    esac
+done
+!NO!SUBS!
+$eunicefix Pnews
+chmod 755 Pnews
+$spitshell >Pnews.header <<'!NO!SUBS!'
+case $# in
+0)
+    ng=h
+    while $test "$ng" = h ; do
+       $echo ""
+       $echo $n "Newsgroup(s): $c"
+       read ng
+       case $ng in
+       h)
+           $cat <<'EOH'
+
+Type the name of one or more newsgroups to which you wish to post an article.
+If you want to post to multiple newsgroups, it is better to do them all at
+once than to post to each newsgroup individually, which defeats the news
+reading programs' strategies of eliminating duplicates.
+
+Separate multiple newsgroup names with commas.
+EOH
+           ;;
+       esac
+    done
+    ;;
+*)
+    ng=$1
+    shift
+    ;;
+esac
+case $ng in
+*\ *)
+    ng=`$echo "$ng" | $sed 's/[, ] */,/g'`
+    ;;
+esac
+case $ng in
+ddn.*)
+    defdist=inet
+    dist=h
+    ;;
+*.*)
+    defdist=''
+    dist=h
+    ;;
+*)
+    defdist=''
+    dist=''
+    ;;
+esac
+
+while $test "$dist" = h ; do
+    if $test -f $lib/distributions; then
+       $echo " "
+       $echo "Your local distribution prefixes are:"
+       $cat $lib/distributions
+       $echo " "
+    else
+       $egrep -v '[     ]none$' <<EOM
+
+Your local distribution prefixes are:
+    Local organization:        $loc
+    Organization:      $org
+    City:              $city
+    $Stpr:             $state
+    $multistpr:        $multistate
+    Country:           $cntry
+    Continent:         $cont
+    Everywhere:                <null> (not "world")
+
+EOM
+    fi
+    $echo $n "Distribution ($defdist): $c"
+    read dist
+    case $dist in
+    '') dist="$defdist" ;;
+    esac
+    case "$dist" in
+    h)
+       $cat <<'EOH'
+
+The Distribution line may be used to limit the distribution of an article
+to some subset of the systems that would receive the article based only on
+the Newsgroups line.  For example, if you want to sell your car in talk.auto,
+and you live in New Jersey, you might want to put "nj" on the Distribution
+line to avoid advertising in California, which has enough problems of its own.
+The actual area designators to use depend on where you are, of course.
+EOH
+       ;;
+    world*|comp*|news*|sci*|rec*|misc*|soc*|talk*|alt*)
+       dist=''
+       ;;
+    ''|$loc|$org|$city|$state|$multistate|$cntry|$cont|$defdist)
+       ;;
+    *)  
+       if $test -f $lib/distributions && \
+         $egrep "^$dist[       ]" $lib/distributions >$tmpart && \
+         $test -s $tmpart; then
+           : null
+       else
+           $echo "Unrecognized distribution prefix--type h for help, CR to use anyway."
+           defdist=$dist
+           dist=h
+       fi
+       ;;
+    esac
+done
+
+follow=""
+
+# LCP 16-Oct-91 Subject line is required.  Make it a little more
+# difficult to omit.  Added "while : ; do", ... "done", and "if"
+# at end of while loop.
+while :
+do
+  case $# in
+  0)
+    title=h
+    while $test "$title" = h ; do
+       $echo ""
+       $echo $n "Title/Subject: $c"
+       read title
+       case $title in
+       h)
+           $cat <<'EOH'
+
+Type the title for your article.  Please make it as informative as possible
+(within reason) so that people who aren't interested won't have to read the
+article to find out they aren't interested.  This includes marking movie
+spoilers as (spoiler), and rotated jokes as (rot 13).
+EOH
+       ;;
+       esac
+    done
+    ;;
+  *)
+    title="$*"
+    # LCP 16-Oct-91 Added "set" and "shift".  Must insure $# is 0
+    # in case the title is all white space and we make another
+    # pass thru this loop.
+    set X
+    shift
+    ;;
+  esac
+  if expr "X$title" : "^X[    ]*$" > /dev/null 2>&1
+  then
+    $cat <<'EOH'
+
+Articles without a "Subject:" line will not be accepted by the News
+system.  Please give a Title/Subject line for your article.
+EOH
+  else
+     break
+  fi
+done
+
+
+# now build a file with a header for them to edit
+
+set X ${USER-${LOGNAME-`who am i`}}
+shift
+logname=$1
+case $logname in
+*!*) logname=`expr "$logname" : '!\(.*\)$'` ;;
+esac
+case ${NAME-$nametype} in
+bsd)
+       if $test "$ypmatch" != ""; then
+               fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/"`
+       elif $test "$nidump" != ""; then
+               fullname=`$nidump passwd / | $sed -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
+       fi
+     if $test "$fullname" = ""; then
+               fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
+       fi
+    case $fullname in
+    *'&'*) : GACK
+       lname=`$echo $logname | $tr 'a-z' 'A-Z'`
+       lname=`$echo $lname $logname | $sed 's/^\(.\)[^ ]* ./\1/'`
+       fullname=`$echo "$fullname" | $sed "s/&/${lname}/"`
+       ;;
+    esac
+    ;;
+usg)
+       if $test "$ypmatch" != ""; then
+               fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"`
+       fi
+     if $test "$fullname" = ""; then
+    fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q" -e "}" -e "d"`
+       fi
+    ;;
+*)
+    fullname=${NAME-`$cat $dotdir/.fullname`}
+    ;;
+esac
+
+case $orgname in
+/*) orgname=`$cat $orgname` ;;
+esac
+
+$sed -e '/^Reply-To: $/d' > $tmpart <<EOHeader
+Newsgroups: $ng
+Subject: $title
+Summary: 
+Reply-To: $REPLYTO
+Followup-To: $follow
+Distribution: $dist
+Organization: $orgname
+Keywords: 
+Cc: 
+
+EOHeader
+
+!NO!SUBS!
+case "$d_nntp" in
+define) sed < Pnews.header -e '/^#NORMAL/d' > Pnews.h.new ;;
+*)  sed < Pnews.header -e '/^#NORMAL/s/^#NORMAL//' > Pnews.h.new ;;
+esac
+mv Pnews.h.new Pnews.header
+$eunicefix Pnews.header
diff --git a/usr/src/contrib/news/trn3/art.c b/usr/src/contrib/news/trn3/art.c
new file mode 100644 (file)
index 0000000..4b8693e
--- /dev/null
@@ -0,0 +1,1046 @@
+/* $Id: art.c,v 3.0 1992/02/01 03:09:32 davison Trn $
+ */
+/* This software is Copyright 1991 by Stan Barber. 
+ *
+ * Permission is hereby granted to copy, reproduce, redistribute or otherwise
+ * use this software as long as: there is no monetary profit gained
+ * specifically from the use or reproduction of this software, it is not
+ * sold, rented, traded or otherwise marketed, and this copyright notice is
+ * included prominently in any copy made. 
+ *
+ * The author make no claims as to the fitness or correctness of this software
+ * for any use whatsoever, and it is provided as is. Any use of this software
+ * is at the user's own risk. 
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "trn.h"
+#include "ngstuff.h"
+#include "ngdata.h"
+#include "cache.h"
+#include "bits.h"
+#include "head.h"
+#include "help.h"
+#include "search.h"
+#include "artio.h"
+#include "ng.h"
+#include "final.h"
+#include "artstate.h"
+#include "rcstuff.h"
+#include "term.h"
+#include "sw.h"
+#include "util.h"
+#include "backpage.h"
+#include "intrp.h"
+#include "rthread.h"
+#include "rt-select.h"
+#include "rt-util.h"
+#include "rt-wumpus.h"
+#include "INTERN.h"
+#include "art.h"
+
+/* page_switch() return values */
+
+#define PS_NORM 0
+#define PS_ASK 1
+#define PS_RAISE 2
+#define PS_TOEND 3
+
+bool special = FALSE;          /* is next page special length? */
+int slines = 0;                        /* how long to make page when special */
+ART_LINE highlight = -1;       /* next line to be highlighted */
+char *restart = Nullch;                /* if nonzero, the place where last */
+                               /* line left off on line split */
+char *blinebeg;                        /* where in buffer current line began */
+ART_POS alinebeg;              /* where in file current line began */
+
+#ifdef INNERSEARCH
+ART_POS innersearch = 0;       /* artpos of end of line we found */
+                               /* for 'g' command */
+ART_LINE isrchline = 0;                        /* last line to display */
+bool hide_everything = FALSE;
+                               /* if set, do not write page now, */
+                               /* but refresh when done with page */
+COMPEX gcompex;                                /* in article search pattern */
+#endif
+
+bool firstpage;                        /* is this the 1st page of article? */
+
+char art_buf[LBUFLEN];         /* place for article lines */
+
+void
+art_init()
+{
+    ;
+}
+
+#ifdef METAMAIL
+#define VERY_LONG_STRING        200
+int
+display_mime()
+{
+    if (!getenv("NOMETAMAIL")) {
+       int code;
+
+       interp(cmd_buf,(sizeof cmd_buf),getval("MIMESHOW",MIMESHOW));
+       termlib_reset();
+       resetty();
+       code = doshell(SH,cmd_buf);
+       noecho();
+       crmode();
+       termlib_init();
+       return code;
+    }
+    return 1;
+}
+#endif
+
+
+int
+do_article()
+{
+    register char *s;
+    ART_POS artsize;                   /* size in bytes of article */
+    bool hide_this_line = FALSE;       /* hidden header line? */
+    ART_LINE linenum;  /* line # on page, 1 origin */
+#ifdef ULSMARTS
+    bool under_lining = FALSE;         /* are we underlining a word? */
+#endif
+    register char *bufptr = art_buf;   /* pointer to input buffer */
+    register int outpos;               /* column position of output */
+    static char prompt_buf[64];                /* place to hold prompt */
+    bool notesfiles = FALSE;           /* might there be notesfiles junk? */
+    char oldmode = mode;
+    char *ctime();
+#ifdef METAMAIL
+    bool tried_display_mime = FALSE;
+#endif
+#ifdef INNERSEARCH
+    register int outputok;
+#endif
+
+#ifdef METAMAIL
+    mime_article = FALSE;
+#endif
+
+    if (fstat(fileno(artfp),&filestat)) /* get article file stats */
+       return DA_CLEAN;
+    if ((filestat.st_mode & S_IFMT) != S_IFREG)
+       return DA_NORM;
+    artsize = filestat.st_size;                /* from that get article size */
+    sprintf(prompt_buf,
+       "%%sEnd of article %ld (of %ld) -- what next? [%%s]",
+       (long)art,(long)lastart);       /* format prompt string */
+    prompt = prompt_buf;
+    int_count = 0;             /* interrupt count is 0 */
+    if ((firstpage = (topline < 0)) != 0) {
+       parseheader(art);
+       fseek(artfp,htype[PAST_HEADER].ht_minpos,0);
+    }
+    for (;;) {                 /* for each page */
+       if (ThreadedGroup && max_tree_lines)
+           init_tree();        /* init tree display */
+       assert(art == openart);
+       if (do_fseek) {
+           parseheader(art);           /* make sure header is ours */
+           artpos = vrdary(artline);
+           if (artpos < 0)
+               artpos = -artpos;       /* labs(), anyone? */
+           if (firstpage)
+               artpos = (ART_POS)0;
+           if (artpos < htype[PAST_HEADER].ht_minpos) {
+               in_header = SOME_LINE;
+               fseek(artfp,htype[PAST_HEADER].ht_minpos,0);
+           } else
+               fseek(artfp,artpos,0);
+           do_fseek = FALSE;
+           restart = Nullch;
+       }
+       linenum = 1;
+       if (firstpage) {
+           if (firstline) {
+               interp(art_buf, (sizeof art_buf), firstline);
+               linenum += tree_puts(art_buf,linenum+topline,0);
+               artopen(art);           /* rewind article in case interp */
+                                       /* forced a header parse */
+           } else 
+            {
+               ART_NUM i;
+               int selected, unseen;
+
+               selected = (curr_artp->flags & AF_SEL);
+               unseen = !was_read(art);
+               sprintf(art_buf,"%s%s #%ld",ngname,moderated,(long)art);
+               if (selected_only) {
+                   i = selected_count - (unseen && selected);
+                   sprintf(art_buf+strlen(art_buf)," (%ld + %ld more)",
+                           (long)i,(long)toread[ng] - selected_count
+                                       - (!selected && unseen));
+               }
+               else if ((i = (ART_NUM)(toread[ng] - unseen)) != 0
+                      || (!ThreadedGroup && dmcount))
+                   sprintf(art_buf+strlen(art_buf)," (%ld more)",(long)i);
+               if (!ThreadedGroup && dmcount)
+                   sprintf(art_buf+strlen(art_buf)-1," + %ld Marked to return)",
+                           (long)dmcount);
+               linenum += tree_puts(art_buf,linenum+topline,0);
+           }
+           start_header(art);
+           forcelast = FALSE;          /* we will have our day in court */
+           restart = Nullch;
+           artline = 0;                /* start counting lines */
+           artpos = 0;
+           vwtary(artline,artpos);     /* remember pos in file */
+       }
+       for (;                          /* linenum already set */
+         in_header || (
+#ifdef INNERSEARCH
+         innersearch ? innermore() :
+#endif
+         linenum<(firstpage?initlines:(special?slines:LINES)) );
+         linenum++) {          /* for each line on page */
+           if (int_count) {    /* exit via interrupt? */
+               putchar('\n') FLUSH;    /* get to left margin */
+               int_count = 0;  /* reset interrupt count */
+               mode = oldmode;
+               special = FALSE;
+               return DA_NORM; /* skip out of loops */
+           }
+           if (restart) {              /* did not finish last line? */
+               bufptr = restart;       /* then start again here */
+               restart = Nullch;       /* and reset the flag */
+           }
+           else if (in_header && headbuf[artpos] != '\0') {
+               bufptr = index(headbuf+artpos,'\n') + 1;
+               bcopy(headbuf+artpos,art_buf,bufptr-headbuf-artpos);
+               art_buf[bufptr-headbuf-artpos] = '\0';
+               bufptr = art_buf;
+           } else {
+               if (fgets(art_buf,LBUFLEN,artfp)==Nullch) {
+                                       /* if all done */
+                   mode = oldmode;
+                   special = FALSE;
+                   return DA_NORM;     /* skip out of loops */
+               }
+               bufptr = art_buf;       /* so start at beginning */
+               art_buf[LBUFLEN-1] = '\0';
+                                       /* make sure string ends */
+           }
+           blinebeg = bufptr;  /* remember where we began */
+           alinebeg = artpos;  /* both in buffer and file */
+           if (in_header && bufptr == art_buf) {
+               hide_this_line =
+                   parseline(art_buf,do_hiding,hide_this_line);
+               if (!in_header) {
+                   linenum += finish_tree(linenum+topline);
+                   end_header();
+               }
+           } else if (notesfiles && do_hiding &&
+             bufptr == art_buf && *art_buf == '#' &&
+             isupper(art_buf[1]) && art_buf[2] == ':' ) {
+               fgets(art_buf,sizeof(art_buf),artfp);
+               if (index(art_buf,'!') != Nullch)
+                   fgets(art_buf,sizeof(art_buf),artfp);
+               htype[PAST_HEADER].ht_minpos = ftell(artfp);
+                                       /* exclude notesfiles droppings */
+               hide_this_line = TRUE;  /* and do not print either */
+               notesfiles = FALSE;
+           }
+#ifdef CUSTOMLINES
+           if (hideline && bufptr == art_buf &&
+                 execute(&hide_compex,art_buf) )
+               hide_this_line = TRUE;
+#endif
+           if (in_header && htype[in_header].ht_flags & HT_MAGIC) {
+               if (in_header == NGS_LINE) {
+                   if ((s = index(art_buf,'\n')) != Nullch)
+                       *s = '\0';
+                   hide_this_line = (index(art_buf,',') == Nullch)
+                       && strEQ(art_buf+12, ngname);
+                   if (s != Nullch)
+                       *s = '\n';
+               }
+               else if (in_header == EXPIR_LINE) {
+                   if (!(htype[EXPIR_LINE].ht_flags & HT_HIDE))
+                       hide_this_line = (strlen(art_buf) < 10);
+               }
+               else if (in_header == FROM_LINE) {
+                   if (do_hiding && (s = extract_name(art_buf+6)) != Nullch)
+                       strcpy(art_buf+6,s);
+               }
+               else if (in_header == DATE_LINE) {
+                   if (do_hiding && curr_artp->date != -1)
+                       strftime(art_buf+6, sizeof(art_buf)-6,
+                                getval("LOCALTIMEFMT", LOCALTIMEFMT),
+                                localtime(&curr_artp->date));
+               }
+#ifdef METAMAIL
+               else if (in_header == CONTENT_LINE) {
+                   mime_article = nontext(art_buf+14);
+               }
+#endif
+           }
+           if (in_header == SUBJ_LINE &&
+               htype[SUBJ_LINE].ht_flags & HT_MAGIC) {
+                           /* is this the subject? */
+               int length;
+
+               length = strlen(art_buf)-1;
+               artline++;
+               art_buf[length] = '\0';         /* wipe out newline */
+#ifdef NOFIREWORKS
+               no_ulfire();
+#endif
+               notesfiles =
+                   (instr(&art_buf[length-10]," - (nf", TRUE) != Nullch);
+               /* tree_puts(, ,1) underlines subject */
+               linenum += tree_puts(art_buf,linenum+topline,1)-1;
+           }
+           else if (hide_this_line && do_hiding) {
+                                       /* do not print line? */
+               linenum--;              /* compensate for linenum++ */
+               if (!in_header)
+                   hide_this_line = FALSE;
+           }
+           else if (in_header) {
+               artline++;
+               linenum += tree_puts(art_buf,linenum+topline,0)-1;
+           }
+           else {                      /* just a normal line */
+#ifdef METAMAIL
+               if (mime_article && !tried_display_mime) {
+                   if (display_mime() == 0)
+                       return DA_NORM;
+                   else
+                       tried_display_mime = TRUE;
+               }
+#endif
+               if (highlight==artline) {       /* this line to be highlit? */
+                   if (marking == STANDOUT) {
+#ifdef NOFIREWORKS
+                       if (erase_screen)
+                           no_sofire();
+#endif
+                       standout();
+                   }
+                   else {
+#ifdef NOFIREWORKS
+                       if (erase_screen)
+                           no_ulfire();
+#endif
+                       underline();
+                   }
+                   if (*bufptr == '\n')
+                       putchar(' ');
+               }
+#ifdef INNERSEARCH
+               outputok = !hide_everything;
+                                       /* get it into register, hopefully */
+#endif
+#ifdef CLEAREOL
+#ifdef INNERSEARCH
+               if (outputok)
+#endif
+               maybe_eol();    
+#endif /* CLEAREOL */
+#ifdef CUSTOMLINES
+               if (pagestop && bufptr == art_buf && 
+                 execute(&page_compex,art_buf) )
+                   linenum = 32700;
+#endif
+               for (outpos = 0; outpos < COLS; ) {
+                                   /* while line has room */
+                   if (*(unsigned char *)bufptr >= ' ') { /* normal char? */
+#ifdef ULSMARTS
+                       if (*bufptr == '_') {
+                           if (bufptr[1] == '\b') {
+                               if (!under_lining && highlight!=artline
+#ifdef INNERSEARCH
+                                   && outputok
+#endif
+                                   ) {
+                                   under_lining++;
+                                   if (UG) {
+                                       if (bufptr != buf &&
+                                         bufptr[-1] == ' ') {
+                                           outpos--;
+                                           backspace();
+                                       }
+                                   }
+                                   underline();
+                               }
+                               bufptr += 2;
+                           }
+                       }
+                       else {
+                           if (under_lining) {
+                               under_lining = 0;
+                               un_underline();
+                               if (UG) {
+                                   if (*bufptr == ' ')
+                                       goto skip_put;
+                                   outpos++;
+                               }
+                           }
+                       }
+#endif
+#ifdef INNERSEARCH
+                       if (outputok)
+#endif
+                       {
+#ifdef ROTATION
+                           if (rotate && !in_header
+                             && isalpha(*bufptr)) {
+                               if ((*bufptr & 31) <= 13)
+                                   putchar(*bufptr+13);
+                               else
+                                   putchar(*bufptr-13);
+                           }
+                           else
+#endif
+                               putchar(*bufptr);
+                       }
+                       if (*UC && ((highlight==artline && marking == 1)
+#ifdef ULSMARTS
+                           || under_lining
+#endif
+                           )) {
+                           backspace();
+                           underchar();
+                       }
+                   skip_put:
+                       bufptr++;
+                       outpos++;
+                   }
+                   else if (*bufptr == '\n' || !*bufptr) {
+                                                   /* newline? */
+#ifdef ULSMARTS
+                       if (under_lining) {
+                           under_lining = 0;
+                           un_underline();
+                       }
+#endif
+#ifdef DEBUG
+                       if (debug & DEB_INNERSRCH && outpos < COLS - 6) {
+                           standout();
+                           printf("%4d",artline); 
+                           un_standout();
+                       }
+#endif
+#ifdef INNERSEARCH
+                       if (outputok)
+#endif
+                           putchar('\n') FLUSH;
+                       restart = 0;
+                       outpos = 1000;  /* signal normal \n */
+                   }
+                   else if (*bufptr == '\t') { /* tab? */
+                       int incpos =  8 - outpos % 8;
+#ifdef INNERSEARCH
+                       if (outputok)
+#endif
+                           if (GT)
+                               putchar(*bufptr);
+                           else
+                               while (incpos--) putchar(' ');
+                       bufptr++;
+                       outpos += 8 - outpos % 8;
+                   }
+                   else if (*bufptr == '\f') { /* form feed? */
+#ifdef INNERSEARCH
+                       if (outputok)
+#endif
+                           fputs("^L",stdout);
+                       if (bufptr == blinebeg && highlight != artline)
+                           linenum = 32700;
+                           /* how is that for a magic number? */
+                       bufptr++;
+                       outpos += 2;
+                   }
+                   else {              /* other control char */
+#ifdef INNERSEARCH
+                       if (outputok)
+#endif
+                       {
+                           if (dont_filter_control)
+                               putchar(*bufptr);
+                           else {
+                               putchar('^');
+                               if (highlight == artline && *UC && marking == 1) {
+                                   backspace();
+                                   underchar();
+                                   putchar(*bufptr+64);
+                                   backspace();
+                                   underchar();
+                               }
+                               else
+                                   putchar(*bufptr+64);
+                           }
+                       }
+                       bufptr++;
+                       outpos += 2;
+                   }
+                   
+               } /* end of column loop */
+
+               if (outpos < 1000) {/* did line overflow? */
+                   restart = bufptr;
+                                   /* restart here next time */
+                   if (!AM || XN) {/* no automatic margins on tty? */
+#ifdef INNERSEARCH                 /* then move it down ourselves */
+                       if (outputok)
+#endif
+                           putchar('\n') FLUSH;
+                   }
+                   if (*bufptr == '\n')    /* skip the newline */
+                       restart = 0;
+               }
+
+               /* handle normal end of output line formalities */
+
+               if (highlight == artline) {
+                                       /* were we highlighting line? */
+                   if (marking == STANDOUT)
+                       un_standout();
+                   else
+                       un_underline();
+                   highlight = -1;     /* no more we are */
+               }
+               artline++;      /* count the line just printed */
+               if (artline - LINES + 1 > topline)
+                           /* did we just scroll top line off? */
+                   topline = artline - LINES + 1;
+                           /* then recompute top line # */
+           }
+
+           /* determine actual position in file */
+
+           if (restart)        /* stranded somewhere in the buffer? */
+               artpos += restart - blinebeg;
+                           /* just calculate position */
+           else if (in_header)
+               artpos = index(headbuf+artpos, '\n') - headbuf + 1;
+           else                /* no, ftell will do */
+               artpos = ftell(artfp);
+           vwtary(artline,artpos);     /* remember pos in file */
+       } /* end of line loop */
+
+#ifdef INNERSEARCH
+       innersearch = 0;
+       if (hide_everything) {
+           hide_everything = FALSE;
+           *buf = Ctl('l');
+           goto fake_command;
+       }
+#endif
+       if (linenum >= 32700)/* did last line have formfeed? */
+           vwtary(artline-1,-vrdary(artline-1));
+                           /* remember by negating pos in file */
+
+       special = FALSE;        /* end of page, so reset page length */
+       firstpage = FALSE;      /* and say it is not 1st time thru */
+
+       /* extra loop bombout */
+
+       if (artpos == artsize) {/* did we just now reach EOF? */
+           mode = oldmode;
+           return DA_NORM;     /* avoid --MORE--(100%) */
+       }
+
+/* not done with this article, so pretend we are a pager */
+
+reask_pager:               
+       unflush_output();       /* disable any ^O in effect */
+       standout();             /* enter standout mode */
+       printf("--MORE--(%ld%%)",(long)(artpos*100/artsize));
+       un_standout();  /* leave standout mode */
+#ifdef CLEAREOL
+       maybe_eol();
+#endif
+       fflush(stdout);
+/* reinp_pager:                        /* unused, commented for lint */
+       eat_typeahead();
+#ifdef DEBUG
+       if (debug & DEB_CHECKPOINTING) {
+           printf("(%d %d %d)",checkcount,linenum,artline);
+           fflush(stdout);
+       }
+#endif
+       if (checkcount >= docheckwhen &&
+         linenum == LINES &&
+         (artline > 40 || checkcount >= docheckwhen+10) ) {
+                           /* while he is reading a whole page */
+                           /* in an article he is interested in */
+           checkcount = 0;
+           checkpoint_rc();    /* update .newsrc */
+       }
+       cache_until_key();
+       mode = 'p';
+       getcmd(buf);
+       if (errno) {
+           if (LINES < 100 && !int_count)
+               *buf = '\f';/* on CONT fake up refresh */
+           else {
+               *buf = 'q';     /* on INTR or paper just quit */
+           }
+       }
+       carriage_return();
+#ifndef CLEAREOL
+       erase_eol();    /* and erase the prompt */
+#else
+       if (erase_screen && can_home_clear)     
+           clear_rest();
+       else
+           erase_eol();        /* and erase the prompt */
+#endif /* CLEAREOL */
+       carriage_return();      /* Resets kernel's tab column counter to 0 */
+       fflush(stdout);
+
+    fake_command:              /* used by innersearch */
+       output_chase_phrase = TRUE;
+
+       /* parse and process pager command */
+
+       switch (page_switch()) {
+       case PS_ASK:    /* reprompt "--MORE--..." */
+           goto reask_pager;
+       case PS_RAISE:  /* reparse on article level */
+           mode = oldmode;
+           return DA_RAISE;
+       case PS_TOEND:  /* fast pager loop exit */
+           mode = oldmode;
+           return DA_TOEND;
+       case PS_NORM:   /* display more article */
+           break;
+       }
+    } /* end of page loop */
+}
+
+/* process pager commands */
+
+int
+page_switch()
+{
+    register char *s;
+    
+    switch (*buf) {
+    case 'd':
+    case Ctl('d'):     /* half page */
+       special = TRUE;
+       slines = LINES / 2 + 1;
+       if (marking && *blinebeg != '\f'
+#ifdef CUSTOMLINES
+         && (!pagestop || blinebeg != art_buf ||
+             !execute(&page_compex,blinebeg))
+#endif
+         ) {
+           up_line();
+           highlight = --artline;
+           restart = blinebeg;
+           artpos = alinebeg;
+       }
+       return PS_NORM;
+    case '!':                  /* shell escape */
+       escapade();
+       return PS_ASK;
+#ifdef INNERSEARCH
+    case Ctl('i'):
+       gline = 3;
+       sprintf(cmd_buf,"^[^%c]",*blinebeg);
+       compile(&gcompex,cmd_buf,TRUE,TRUE);
+       goto caseG;
+    case Ctl('g'):
+       gline = 3;
+       compile(&gcompex,"^Subject:",TRUE,TRUE);
+       goto caseG;
+    case 'g':          /* in-article search */
+       if (!finish_command(FALSE))/* get rest of command */
+           return PS_ASK;
+       s = buf+1;
+       if (isspace(*s))
+           s++;
+       if ((s = compile(&gcompex,s,TRUE,TRUE)) != Nullch) {
+                           /* compile regular expression */
+           printf("\n%s\n",s) FLUSH;
+           return PS_ASK;
+       }
+       carriage_return();
+       erase_eol();    /* erase the prompt */
+       carriage_return();      /* Resets kernel's tab column counter to 0 */
+       /* FALL THROUGH */
+    caseG:
+    case 'G': {
+       /* ART_LINE lines_to_skip = 0; */
+       ART_POS start_where;
+
+       if (gline < 0 || gline > LINES-2)
+           gline = LINES-2;
+#ifdef DEBUG
+       if (debug & DEB_INNERSRCH)
+           printf("Start here? %d  >=? %d\n",topline + gline + 1,artline)
+             FLUSH;
+#endif
+       if (*buf == Ctl('i') || topline+gline+1 >= artline)
+           start_where = artpos;
+                       /* in case we had a line wrap */
+       else {
+           start_where = vrdary(topline+gline+1);
+           if (start_where < 0)
+               start_where = -start_where;
+       }
+       if (start_where < htype[PAST_HEADER].ht_minpos)
+           start_where = htype[PAST_HEADER].ht_minpos;
+       fseek(artfp,(long)start_where,0);
+       innersearch = 0; /* assume not found */
+       while (fgets(buf, sizeof buf, artfp) != Nullch) {
+           /* lines_to_skip++;                 NOT USED NOW */
+#ifdef DEBUG
+           if (debug & DEB_INNERSRCH)
+               printf("Test %s",buf) FLUSH;
+#endif
+           if (execute(&gcompex,buf) != Nullch) {
+               innersearch = ftell(artfp);
+               break;
+           }
+       }
+       if (!innersearch) {
+           fseek(artfp,artpos,0);
+           fputs("(Not found)",stdout) FLUSH;
+           return PS_ASK;
+       }
+#ifdef DEBUG
+       if (debug & DEB_INNERSRCH)
+           printf("On page? %ld <=? %ld\n",(long)innersearch,(long)artpos)
+             FLUSH;
+#endif
+       if (innersearch <= artpos) {    /* already on page? */
+           if (innersearch < artpos) {
+               artline = topline+1;
+               while (vrdary(artline) < innersearch)
+                   artline++;
+           }
+           highlight = artline - 1;
+#ifdef DEBUG
+           if (debug & DEB_INNERSRCH)
+               printf("@ %d\n",highlight) FLUSH;
+#endif
+           topline = highlight - gline;
+           if (topline < -1)
+               topline = -1;
+           *buf = '\f';                /* fake up a refresh */
+           innersearch = 0;
+           return page_switch();
+       }
+       else {                          /* who knows how many lines it is? */
+           do_fseek = TRUE;
+           hide_everything = TRUE;
+       }
+       return PS_NORM;
+    }
+#else
+    case 'g': case 'G': case Ctl('g'):
+       notincl("g");
+       return PS_ASK;
+#endif
+    case '\n':         /* one line */
+       special = TRUE;
+       slines = 2;
+       return PS_NORM;
+#ifdef ROTATION
+    case 'X':
+       rotate = !rotate;
+       /* FALL THROUGH */
+#endif
+    case 'l':
+    case '\f':         /* refresh screen */
+#ifdef DEBUG
+       if (debug & DEB_INNERSRCH) {
+           printf("Topline = %d",topline) FLUSH;
+           gets(buf);
+       }
+#endif
+       clear();
+       carriage_return();      /* Resets kernel's tab column counter to 0 */
+       do_fseek = TRUE;
+       artline = topline;
+       if (artline < 0)
+           artline = 0;
+       firstpage = (topline < 0);
+       return PS_NORM;
+    case 'b':
+    case '\b':
+    case Ctl('b'): {   /* back up a page */
+       ART_LINE target;
+
+#ifndef CLEAREOL
+       clear();
+#else
+       if (can_home_clear)     /* if we can home do it */
+           home_cursor();
+       else
+           clear();
+
+#endif /* CLEAREOL */
+       carriage_return();      /* Resets kernel's tab column counter to 0 */
+       do_fseek = TRUE;        /* reposition article file */
+       target = topline - (LINES - 2);
+       artline = topline;
+       if (artline >= 0) do {
+           artline--;
+       } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
+       topline = artline;
+                       /* remember top line of screen */
+                       /*  (line # within article file) */
+       if (artline < 0)
+           artline = 0;
+       firstpage = (topline < 0);
+       return PS_NORM;
+    }
+    case 'h': {                /* help */
+       int cmd;
+
+       if ((cmd = help_page()) > 0)
+           pushchar(cmd);
+       return PS_ASK;
+    }
+    case 't':          /* output thread data */
+       page_line = 1;
+       entire_tree(curr_artp);
+       return PS_ASK;
+    case '_':
+       if (!finish_dblchar())
+           return PS_ASK;
+       switch (buf[1] & 0177) {
+       default:
+           goto leave_pager;
+       }
+       break;
+    case '\177':
+    case '\0':         /* treat del,break as 'n' */
+       *buf = 'n';
+       /* FALL THROUGH */
+    case 'k':  case 'K':
+    case 'T':  case 'J':
+    case 'n':  case 'N':       case Ctl('n'):
+    case 's':  case 'S':
+    case 'e':
+    case 'u':
+    case 'w':  case 'W':
+    case '|':
+       mark_as_read();         /* mark article as read */
+       /* FALL THROUGH */
+    case 'U':  case ',':
+    case '<':  case '>':
+    case '[':  case ']':
+    case '{':  case '}':
+    case '(':   case ')':
+    case '+':   case ':':
+    case '#':
+    case '$':
+    case '&':
+    case '-':
+    case '.':
+    case '/':
+    case '1': case '2': case '3': case '4': case '5':
+    case '6': case '7': case '8': case '9':
+    case '=':
+    case '?':
+    case 'c':  case 'C':       
+#ifdef DEBUG
+    case 'D':
+#endif
+    case 'E':
+    case 'f':  case 'F':       case Ctl('f'):
+    case 'j':
+                               case Ctl('k'):
+    case 'm':  case 'M':       
+    case 'p':  case 'P':       case Ctl('p'):  
+               case 'Q':
+    case 'r':  case 'R':       case Ctl('r'):
+    case 'v':
+               case 'Y':
+#ifndef ROTATION
+    case 'x':  case 'X':
+#endif
+    case Ctl('x'):
+    case 'z':  case 'Z':
+    case '^':
+leave_pager:
+#ifdef ROTATION
+       rotate = FALSE;
+#endif
+       reread = FALSE;
+       do_hiding = TRUE;
+       if (index("nNpP\016\020",*buf) == Nullch &&
+         index("wWsSe:!&|/?123456789.",*buf) != Nullch) {
+           setdfltcmd();
+           standout();         /* enter standout mode */
+           printf(prompt,mailcall,dfltcmd);
+                           /* print prompt, whatever it is */
+           un_standout();      /* leave standout mode */
+           putchar(' ');
+           fflush(stdout);
+       }
+       return PS_RAISE;        /* and pretend we were at end */
+#ifdef ROTATION
+    case 'x':
+       rotate = TRUE;
+       /* FALL THROUGH */
+#endif
+    case 'y':
+    case Ctl('v'):
+                                       /* Leaving it undocumented in case */
+                                       /* I want to steal the key--LAW */
+    case ' ':  /* continue current article */
+       if (erase_screen) {     /* -e? */
+#ifndef CLEAREOL
+           clear();            /* clear screen */
+#else
+           if (can_home_clear) /* if we can home do it */
+               home_cursor();
+           else
+               clear();        /* else clear screen */
+
+#endif /* CLEAREOL */
+           carriage_return();  /* Resets kernel's tab column counter to 0 */
+           fflush(stdout);
+
+           if (*blinebeg != '\f'
+#ifdef CUSTOMLINES
+             && (!pagestop || blinebeg != art_buf ||
+                 !execute(&page_compex,blinebeg))
+#endif
+             ) {
+               restart = blinebeg;
+               artline--;       /* restart this line */
+               artpos = alinebeg;
+               if (marking)    /* and mark repeated line */
+                   highlight = artline;
+           }
+           topline = artline;
+                       /* and remember top line of screen */
+                       /*  (line # within article file) */
+       }
+       else if (marking && *blinebeg != '\f'
+#ifdef CUSTOMLINES
+         && (!pagestop || blinebeg != art_buf ||
+             !execute(&page_compex,blinebeg))
+#endif
+         ) {
+                               /* are we marking repeats? */
+           up_line();          /* go up one line */
+           highlight = --artline;/* and get ready to highlight */
+           restart = blinebeg; /*   the old line */
+           artpos = alinebeg;
+       }
+       return PS_NORM;
+    case 'q':  /* quit this article? */
+       do_hiding = TRUE;
+       return PS_TOEND;
+    default:
+       fputs(hforhelp,stdout) FLUSH;
+       settle_down();
+       return PS_ASK;
+    }
+}
+
+#ifdef INNERSEARCH
+bool
+innermore()
+{
+    if (artpos < innersearch) {                /* not even on page yet? */
+#ifdef DEBUG
+       if (debug & DEB_INNERSRCH)
+           printf("Not on page %ld < %ld\n",(long)artpos,(long)innersearch)
+             FLUSH;
+#endif
+       return TRUE;
+    }
+    if (artpos == innersearch) {       /* just got onto page? */
+       isrchline = artline;            /* remember first line after */
+       highlight = artline - 1;
+#ifdef DEBUG
+       if (debug & DEB_INNERSRCH)
+           printf("There it is %ld = %ld, %d @ %d\n",(long)artpos,
+               (long)innersearch,hide_everything,highlight) FLUSH;
+#endif
+       if (hide_everything) {          /* forced refresh? */
+           topline = highlight - gline;
+           if (topline < -1)
+               topline = -1;
+           return FALSE;               /* let refresh do it all */
+       }
+    }
+#ifdef DEBUG
+    if (debug & DEB_INNERSRCH)
+       printf("Not far enough? %d <? %d + %d\n",artline,isrchline,gline)
+         FLUSH;
+#endif
+    if (artline < isrchline + gline) {
+       return TRUE;
+    }
+    return FALSE;
+}
+#endif
+
+#ifdef METAMAIL
+int
+nontext(content_type)
+char *content_type;
+{
+    char *t;
+
+    if (content_type[0] == '\n')
+       return 0;
+    while (content_type && isspace(*content_type))
+       content_type++;
+    t = index(content_type, ';');
+    if (!t)
+       t = index(content_type, '\n');
+    if (t)
+       *t-- = '\0';
+    while (t && *t && t > content_type && isspace(*t))
+       *t-- = '\0';
+    if (notplain(content_type))
+       return 1;
+    return 0;
+}
+
+int
+notplain(s)
+char *s;
+{
+    char *t;
+    if (!s)
+       return 1;
+    while (*s && isspace(*s))
+       s++;
+    for (t=s; *t; ++t) {
+       if (isupper(*t))
+           *t = tolower(*t);
+    }
+    while (t > s && isspace(*--t)) ;
+    if (((t-s) == 3) && !strncmp(s, "text", 4))
+       return 0;
+    if (strncmp(s, "text/plain", 10))
+       return 1;
+    t = index(s, ';');
+    while (t) {
+       t++;
+       while (*t && isspace(*t)) t++;
+       if (!strncmp(t, "charset", 7)) {
+           s = index(t, '=');
+           if (s) {
+               s++;
+               while (*s && isspace(*s)) s++;
+               if (!strncmp(s, "us-ascii", 8))
+                   return 0;
+           }
+           return(1);
+       }
+       t = index(t, ';');
+    }
+    return 0;  /* no charset, was text/plain */
+}
+#endif
diff --git a/usr/src/contrib/news/trn3/artcheck.c b/usr/src/contrib/news/trn3/artcheck.c
new file mode 100644 (file)
index 0000000..45f3679
--- /dev/null
@@ -0,0 +1,138 @@
+/* $Id: artcheck.c,v 3.0 1992/02/01 03:09:32 davison Trn $
+*/
+
+/* A program to check an article's validity and print warnings if problems
+** are found.
+**
+** Usage: artcheck <article> <maxLineLen> <newsgroupsFile>
+*/
+
+#include "EXTERN.h"
+#include "common.h"
+
+#define MAXNGS 100
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+    FILE *fp, *fp2;
+    char buff[LBUFLEN], *cp, *cp2;
+    char *ngptrs[MAXNGS];
+    int nglens[MAXNGS];
+    int i, col, max_col_len, line_num = 0, ngcnt = 0;
+    int found_newsgroups = 0;
+
+    if (argc != 4 || !(max_col_len = atoi(argv[2]))) {
+       fprintf(stderr, "\
+Usage: artcheck <article> <maxLineLen> <newsgroupsFile>\n");
+       exit(1);
+    }
+
+    if ((fp = fopen(argv[1], "r")) == NULL) {
+       fprintf(stderr, "artcheck: unable to open article `%s'.\n", argv[1]);
+       exit(1);
+    }
+
+    /* Check the header for proper format and report on the newsgroups */
+    while (fgets(buff, LBUFLEN, fp)) {
+       line_num++;
+       buff[strlen(buff)-1] = '\0';
+       if (!*buff)
+           break;
+       if (*buff == ' ' || *buff == '\t')
+           continue;
+       if (!(cp = index(buff, ':'))) {
+           printf("\nERROR: line %d is an invalid header line:\n%s\n",
+                  line_num, buff);
+           break;
+       }
+       if (cp[1] != ' ') {
+           printf("\n\
+ERROR: header on line %d does not have a space after the colon:\n%s\n",
+                  line_num, buff);
+       }
+       if (cp - buff == 10 && strnEQ(buff, "Newsgroups", 10)) {
+           found_newsgroups = 1;
+           for (cp = buff + 11; *cp == ' '; cp++)
+               ;
+           if (index(cp, ' ')) {
+               printf("\n\
+ERROR: the \"Newsgroups:\" line has spaces in it that MUST be removed. The\n\
+only allowable space is the one separating the colon (:) from the contents.\n\
+Use a comma (,) to separate multiple newsgroup names.\n");
+               continue;
+           }
+           while (*cp) {
+               if (!(cp2 = index(cp, ',')))
+                   cp2 = cp + strlen(cp);
+               else
+                   *cp2++ = '\0';
+               if (ngcnt < MAXNGS) {
+                   nglens[ngcnt] = strlen(cp);
+                   ngptrs[ngcnt] = malloc(nglens[ngcnt]+1);
+                   if (!ngptrs[ngcnt]) {
+                       fprintf(stderr,"Out of memory.\n");
+                       exit(1);
+                   }
+                   strcpy(ngptrs[ngcnt], cp);
+                   ngcnt++;
+               }
+               cp = cp2;
+           }
+           if (!ngcnt) {
+               printf("\n\
+ERROR: the \"Newsgroups:\" line lists no newsgroups.\n");
+               continue;
+           }
+       }
+    }
+    if (!found_newsgroups) {
+       printf("\nERROR: the \"Newsgroups:\" line is missing from the header.\n");
+    }
+
+    /* Check the body of the article for long lines */
+    while (fgets(buff, LBUFLEN, fp)) {
+       line_num++;
+       buff[strlen(buff)-1] = '\0';
+       col = 0;
+       for (cp = buff; *cp; cp++) {
+           if (*cp == '\t')
+               col += 8 - (col%8);
+           else
+               col++;
+       }
+       if (col > max_col_len) {
+           printf("\n\
+Warning: posting exceeds %d columns.  Line %d is the first long one:\n%s\n",
+                  max_col_len, line_num, buff);
+           break;
+       }
+    }
+    if (ngcnt) {
+       /* Print a note about each newsgroup */
+       printf("\nYour article's newsgroup%s:\n", ngcnt == 1? "" : "s");
+       if ((fp2 = fopen(argv[3], "r")) != NULL) {
+           while (fgets(buff, LBUFLEN, fp2)) {
+               for (i = 0; i < ngcnt; i++) {
+                   if (ngptrs[i]) {
+                       if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ')
+                         && strnEQ(ngptrs[i], buff, nglens[i])) {
+                           printf("%s", buff);
+                           free(ngptrs[i]);
+                           ngptrs[i] = 0;
+                       }
+                   }
+               }
+           }
+       }
+       for (i = 0; i < ngcnt; i++) {
+           if (ngptrs[i]) {
+               printf("%s\t[no description available]\n", ngptrs[i]);
+               free(ngptrs[i]);
+           }
+       }
+    }
+    return 0;
+}
diff --git a/usr/src/contrib/news/trn3/help.c b/usr/src/contrib/news/trn3/help.c
new file mode 100644 (file)
index 0000000..3fd69ea
--- /dev/null
@@ -0,0 +1,469 @@
+/* $Id: help.c,v 3.0 1992/02/01 03:09:32 davison Trn $
+ */
+/* This software is Copyright 1991 by Stan Barber. 
+ *
+ * Permission is hereby granted to copy, reproduce, redistribute or otherwise
+ * use this software as long as: there is no monetary profit gained
+ * specifically from the use or reproduction of this software, it is not
+ * sold, rented, traded or otherwise marketed, and this copyright notice is
+ * included prominently in any copy made. 
+ *
+ * The author make no claims as to the fitness or correctness of this software
+ * for any use whatsoever, and it is provided as is. Any use of this software
+ * is at the user's own risk. 
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "trn.h"
+#include "term.h"
+#include "INTERN.h"
+#include "help.h"
+
+void
+help_init()
+{
+    ;
+}
+
+int
+help_page()
+{
+    int cmd;
+
+#ifdef PAGERHELP
+    doshell(sh,filexp(PAGERHELP));
+#else
+    page_init();
+    if ((cmd = print_lines("\
+Paging commands:\n\
+",STANDOUT)) ||
+    (cmd = print_lines("\n\
+SP     Display the next page.\n\
+x      Display the next page decrypted (rot13).\n\
+d      Display half a page more.\n\
+CR     Display one more line.\n\
+^R,v,^X        Restart the current article (v=verbose header, ^X=rot13).\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+b      Back up one page.\n\
+^L,X   Refresh the screen (X=rot13).\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+t      Display the entire article tree and all its subjects.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+g pat  Go to (search forward within article for) pattern.\n\
+G      Search again for current pattern within article.\n\
+^G     Search for next line beginning with \"Subject:\".\n\
+TAB    Search for next line beginning with a different character.\n\
+q      Quit the pager, go to end of article.  Leave article read or unread.\n\
+j      Junk this article (mark it read).  Goes to end of article.\n\
+\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+The following commands skip the rest of the current article, then behave\n\
+just as if typed to the 'What next?' prompt at the end of the article:\n\
+",STANDOUT)) ||
+    (cmd = print_lines("\n\
+n      Scan forward for next unread article.\n\
+N      Go to next article.\n\
+^N     Scan forward for next unread article with same title.\n\
+p,P,^P Same as n,N,^N, only going backwards.\n\
+-      Go to previously displayed article.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+<, >   Browse the previous/next selected thread.  If no threads are selected,\n\
+       all threads that had unread news upon entry to the group are considered\n\
+       selected for browsing.  Entering an empty group browses all threads.\n\
+[, ]   Go to article's parent/child (try left-/right-arrow also).\n\
+(, )   Go to article's previous/next sibling (try up-/down-arrow also).\n\
+{, }   Go to tree's root/leaf.\n\
+\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+The following commands also take you to the end of the article.\n\
+Type h at end of article for a description of these commands:\n\
+",STANDOUT)) ||
+    (cmd = print_lines("\
+       # $ & / = ? c C f F k K ^K J , m M number e r R ^R s S u U v w W Y ^ |\n\
+\n\
+(To return to the middle of the article after one of these commands, type ^L.)\n\
+",NOMARKING)) )
+       return cmd;
+#endif
+    return 0;
+}
+
+int
+help_art()
+{
+    int cmd;
+#ifdef ARTHELP
+    doshell(sh,filexp(ARTHELP));
+#else
+    page_init();
+    if ((cmd = print_lines("\
+Article Selection commands:\n\
+",STANDOUT)) ||
+    (cmd = print_lines("\n\
+n,SP   Find next unread article (follows discussion-tree in threaded groups).\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+N      Go to next article.\n\
+^N     Scan forward for next unread article with same subject in date order.\n\
+p,P,^P Same as n,N,^N, only going backwards.\n\
+_N,_P  Go to the next/previous article numerically.\n\
+-      Go to previously displayed article.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+<, >   Browse the previous/next selected thread.  If no threads are selected,\n\
+       all threads that had unread news upon entry to the group are considered\n\
+       selected for browsing.  Entering an empty group browses all threads.\n\
+[, ]   Go to article's parent/child (try left-/right-arrow also).\n\
+(, )   Go to article's previous/next sibling (try up-/down-arrow also).\n\
+{, }   Go to tree's root/leaf.\n\
+t      Display the entire article tree and all its subjects.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+number Go to specified article.\n\
+range{,range}:command{:command}\n\
+       Apply one or more commands to one or more ranges of articles.\n\
+       Ranges are of the form: number | number-number.  You may use . for\n\
+       the current article, and $ for the last article.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+       Valid commands are: e, j, m, M, s, S, t, T, |, +, ++, -, and --.\n\
+:cmd   Perform a command on all the selected articles.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+/pattern/modifiers\n\
+       Scan forward for article containing pattern in the subject line.\n\
+       (Use ?pat? to scan backwards; append f to scan from lines, h to scan\n\
+       whole headers, a to scan entire articles, r to scan read articles, c\n\
+       to make case-sensitive.)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+/pattern/modifiers:command{:command}\n\
+       Apply one or more commands to the set of articles matching pattern.\n\
+       Use a K modifier to save entire command to the KILL file for this\n\
+       newsgroup.  Commands m and M, if first, imply an r modifier.\n\
+       Valid commands are the same as for the range command.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+f,F    Submit a followup article (F = include this article).\n\
+r,R    Reply through net mail (R = include this article).\n\
+e dir{|command}\n\
+       Extract to directory using /bin/sh, uudecode, unship, or command.\n\
+s ...  Save to file or pipe via sh.\n\
+S ...  Save via preferred shell.\n\
+w,W    Like s and S but save without the header.\n\
+| ...  Same as s|...\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+C      Cancel this article, if yours.\n\
+^R,v   Restart article (v=verbose).\n\
+^X     Restart article, rot13 mode.\n\
+c      Catch up (mark all articles as read).\n\
+b      Back up one page.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+^L     Refresh the screen.  You can get back to the pager with this.\n\
+X      Refresh screen in rot13 mode.\n\
+^      Go to first unread article.  Disables subject search mode.\n\
+$      Go to end of newsgroup.  Disables subject search mode.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("#       Print last article number.\n\
+&      Print current values of command-line switches.\n\
+&switch {switch}\n\
+       Set or unset more switches.\n\
+&&     Print current macro definitions.\n\
+&&def  Define a new macro.\n\
+j      Junk this article (mark it read).  Stays at end of article.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+m      Mark article as still unread.\n\
+M      Mark article as read but to-return on group exit or Y command.\n\
+Y      Yank back articles marked as to-return via the M command.\n\
+k      Kill current subject (mark articles as read).\n\
+,      Mark current article and its replies as read.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+J      Junk entire thread (mark all subjects as read in this thread).\n\
+A      Add current subject to memorized commands (selection or killing).\n\
+T      Add current (sub)thread to memorized commands (selection or killing).\n\
+K      Mark current subject as read, and save command in KILL file.\n\
+^K     Edit local KILL file (the one for this newsgroup).\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+=      List subjects of unread articles.\n\
++      Start the selector in whatever mode it was last in.\n\
+_a     Start the article selector.\n\
+_s     Start the subject selector.\n\
+_t     Start the thread selector.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+_T     Start the thread selector if threaded, else the subject selector.\n\
+U      Unread some news -- prompts for thread, subthread, all, or select.\n\
+u      Unsubscribe from this newsgroup.\n\
+q      Quit this newsgroup for now.\n\
+Q      Quit newsgroup, staying at current newsgroup.\n\
+",NOMARKING)) )
+       return cmd;
+#endif
+    return 0;
+}
+
+int
+help_ng()
+{
+    int cmd;
+#ifdef NGHELP
+    doshell(sh,filexp(NGHELP));
+#else
+    page_init();
+    if (cmd = print_lines("\
+Newsgroup Selection commands:\n\
+",STANDOUT) )
+       return cmd;
+    if (ng != nextrcline) {
+       if ((cmd = print_lines("\
+\n\
+y      Do this newsgroup now.\n\
+SP     Do this newsgroup, executing the default command listed in []'s.\n\
+.cmd   Do this newsgroup, executing cmd as first command.\n\
++      Enter this newsgroup through the selector (like typing .+<CR>).\n\
+=      Start this newsgroup, but list subjects before reading articles.\n\
+U      Enter this newsgroup by way of the \"Set unread?\" prompt.\n\
+u      Unsubscribe from this newsgroup.\n\
+",NOMARKING)) )
+           return cmd;
+    }
+    if ((cmd = print_lines("\
+t      Toggle the newsgroup between threaded and unthreaded reading.\n\
+c      Catch up (mark all articles as read).\n\
+A      Abandon read/unread changes to this newsgroup since you started trn.\n\
+n      Go to the next newsgroup with unread news.\n\
+N      Go to the next newsgroup.\n\
+p      Go to the previous newsgroup with unread news.\n\
+P      Go to the previous newsgroup.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+-      Go to the previously displayed newsgroup.\n\
+1      Go to the first newsgroup.\n\
+^      Go to the first newsgroup with unread news.\n\
+$      Go to the last newsgroup.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+g name Go to the named newsgroup.  Subscribe to new newsgroups this way too.\n\
+/pat   Search forward for newsgroup matching pattern.\n\
+?pat   Search backward for newsgroup matching pattern.\n\
+       (Use * and ? style patterns.  Append r to include read newsgroups.)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+l pat  List unsubscribed newsgroups containing pattern.\n\
+m name Move named newsgroup elsewhere (no name moves current newsgroup).\n\
+o pat  Only display newsgroups matching pattern.  Omit pat to unrestrict.\n\
+a pat  Like o, but also scans for unsubscribed newsgroups matching pattern.\n\
+L      List current .newsrc.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+&      Print current command-line switch settings.\n\
+&switch {switch}\n\
+       Set (or unset) more command-line switches.\n\
+&&     Print current macro definitions.\n\
+&&def  Define a new macro.\n\
+!cmd   Shell escape.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+q      Quit trn.\n\
+x      Quit, restoring .newsrc to its state at startup of trn.\n\
+^K     Edit the global KILL file.  Use commands like /pattern/j to suppress\n\
+       pattern in every newsgroup.\n\
+v      Print version and the address for reporting bugs.\n\
+",NOMARKING)) )
+       return cmd;
+#endif
+    if (cmd = get_anything())
+       return cmd;
+    show_macros();
+    return 0;
+}
+
+#ifdef ESCSUBS
+int
+help_subs()
+{
+    int cmd;
+#ifdef SUBSHELP
+    doshell(sh,filexp(SUBSHELP));
+#else
+    page_init();
+    if ((cmd = print_lines("\
+Valid substitutions are:\n\
+",STANDOUT)) ||
+    (cmd = print_lines("\
+\n\
+a      Current article number\n\
+A      Full name of current article (%P/%c/%a)\n\
+b      Destination of last save command, often a mailbox\n\
+B      Bytes to ignore at beginning of last saved article\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+c      Current newsgroup, directory form\n\
+C      Current newsgroup, dot form\n\
+d      Full name of newsgroup directory (%P/%c)\n\
+D      Distribution line from current article\n\
+e      The last command executed to extract data from an article\n\
+E      The last extraction directory\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+f      Who the current article is from\n\
+F      Newsgroups to followup to (from Newsgroups and Followup-To)\n\
+h      (This help message)\n\
+H      Host name (yours)\n\
+i      Message-I.D. line from current article, with <>\n\
+I      Reference indicator mark (see -F switch)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+l      News administrator's login name, if any\n\
+L      Login name (yours)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+m      Current mode, first letter of (init,newsgroup,thread,article,pager,\n\
+               unread,Add,Catchup,Delete-bogus,Mailbox,Resubscribe)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+M      Number of article marked with M\n\
+n      Newsgroups from current article\n\
+N      Full name (yours)\n\
+o      Organization (yours)\n\
+O      Original working directory (where you ran trn from)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+p      Your private news directory (from -d)\n\
+P      Public news spool directory\n\
+r      Last reference (parent article id)\n\
+R      References list for followup article\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+s      Subject, with all Re's and (nf)'s stripped off\n\
+S      Subject, with one Re stripped off\n\
+t      New To line derived from From and Reply-To (Internet format)\n\
+T      New To line derived from Path\n\
+u      Number of unread articles\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+U      Number of unread articles not counting the current article (when\n\
+       threads are selected, the count only reflects selected articles)\n\
+v      The number of extra (unselected) articles, not counting the current\n\
+       one if it is unselected\n\
+W      Where thread files are saved\n\
+x      News library directory\n\
+X      Trn library directory\n\
+z      Length of current article in bytes\n\
+Z      Number of selected threads\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+~      Your home directory\n\
+.      Directory containing the \"dot\" files, such as .newsrc\n\
+#      A counter in multi-article saves\n\
+$      Current process number\n\
+/      Last search string\n\
+ESC    Run preceding command through % interpretation\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+Put ^ in the middle to capitalize the first letter: %^C = Rec.humor\n\
+Put _ in the middle to capitalize the last component: %_c = rec/Humor\n\
+Put \\ in the middle to quote regexp and % characters in the resulting string\n\
+Put :FMT in the middle to format the result printf-style:  %:-30.30t\n\
+",NOMARKING)) )
+       return cmd;
+#endif
+    return 0;
+}
+#endif
+
+int
+help_select()
+{
+    int cmd;
+
+    page_init();
+    if ((cmd = print_lines("\
+Selection commands:\n\
+",STANDOUT)) ||
+    (cmd = print_lines("\n\
+a-z,0-9        Select/deselect the indicated item by its letter or number.  Many of\n\
+       the alpha letters are ommitted for the following commands.\n\
+SP     Perform the default command (usually > or Z).\n\
+CR     Start reading.  Selects the current item if nothing is selected.\n\
+Z,TAB  Start reading.  If nothing is selected, read all unread articles.\n\
+.      Toggle the current item's selection.\n\
+*      Same as '.' except that it affects all items with the same subject.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+#      Read the current item only, temporarily ignoring all other selections.\n\
+k, ',' Mark the current item as killed.\n\
+m, \\  Unmark the current item.\n\
+-      Set a range, as in d - f.  Repeats the last marking action.\n\
+@      Toggle the selection of all visible items.\n\
+M      Mark the current item's article(s) as to-return and kill the item.\n\
+Y      Yank back and select articles marked to return via M.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+E      Toggle exclusion of non-selected items from the selection list.\n\
+n, ]   Move down to the next item (try down-arrow also).\n\
+p, [   Move up to the previous item (try up-arrow also).\n\
+<, >   Go to previous/next page (try left-/right-arrow also).\n\
+^, $   Go to first/last page.\n\
+S      Set what the selector displays:  threads, subjects, or articles.\n\
+       If the group is unthreaded, choosing threads will thread it.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+=      Toggle between the article and thread/subject selector.\n\
+O      Set the selector's order.  A separate default is kept for the\n\
+       article and subject/thread selector.\n\
+R      Reverse the current sort order.\n\
+L      Switch the display between a short style without authors and a\n\
+       medium or long style with authors.\n\
+U      Switch between selecting unread/read articles.\n\
+X      Mark all unselected articles as read and start reading.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+D      Mark unselected articles on the current page as read.  Start\n\
+       reading if articles were selected, else go to next page.\n\
+J      Junk all selected articles (mark them as read).\n\
+c      Catch up -- marks ALL articles as read without chasing xrefs.\n\
+A      Add current subject to memorized commands (selection or killing).\n\
+T      Add current thread to memorized commands (selection or killing).\n\
+^K     Edit local KILL file (the one for this newsgroup).\n\
+N      Leave this group as-is and go on to the next one.\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+P      Leave this group as-is and go on to the previous one.\n\
+:cmd   Perform a command on all the selected articles.\n\
+/pattern/modifiers\n\
+       Scan all articles for a subject containing pattern.\n\
+       (Append f to scan the from line, h to scan whole headers, a to scan\n\
+       entire articles, c to make it case-sensitive, r to scan read articles\n\
+       (assumed when you are selecting read articles to set unread.)\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+/pattern/modifiers:command{:command}\n\
+       Apply one or more commands to the set of articles matching pattern.\n\
+       Use a K modifier to save entire command to the KILL file for this\n\
+       newsgroup.  Commands m and M, if first, imply an r modifier.\n\
+       Valid commands are: e, E, j, m, M, s, S, t, T, !, =, ',' and the\n\
+       article/thread (de)selection commands: +/++ (-/--).\n\
+",NOMARKING)) ||
+    (cmd = print_lines("\
+&      View or set command line switches.\n\
+&&     View or set macro definitions.\n\
+!cmd   Escape to a subshell.\n\
+q      Quit the selector.\n\
+Q      Quit group and return to news group selection prompt for this group.\n\
+",NOMARKING)) )
+       return cmd;
+    return 0;
+}
diff --git a/usr/src/contrib/news/trn3/intrp.c b/usr/src/contrib/news/trn3/intrp.c
new file mode 100644 (file)
index 0000000..0fd5def
--- /dev/null
@@ -0,0 +1,1202 @@
+/* $Id: intrp.c,v 3.0 1992/02/01 03:09:32 davison Trn $
+ */
+/* This software is Copyright 1991 by Stan Barber. 
+ *
+ * Permission is hereby granted to copy, reproduce, redistribute or otherwise
+ * use this software as long as: there is no monetary profit gained
+ * specifically from the use or reproduction of this software, it is not
+ * sold, rented, traded or otherwise marketed, and this copyright notice is
+ * included prominently in any copy made. 
+ *
+ * The author make no claims as to the fitness or correctness of this software
+ * for any use whatsoever, and it is provided as is. Any use of this software
+ * is at the user's own risk. 
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "search.h"
+#include "cache.h"
+#include "bits.h"
+#include "head.h"
+#include "trn.h"
+#include "artsrch.h"
+#include "ng.h"
+#include "ngdata.h"
+#include "respond.h"
+#include "rcstuff.h"
+#include "artio.h"
+#include "init.h"
+#include "term.h"
+#include "final.h"
+#include "rthread.h"
+#include "rt-select.h"
+#include "nntp.h"
+#include "INTERN.h"
+#include "intrp.h"
+
+static char * regexp_specials = "^$.*[\\/?%";
+
+char orgname[] = ORGNAME;
+
+#ifdef HAS_UNAME
+#include <sys/utsname.h>
+struct utsname utsn;
+#endif
+
+#ifdef TILDENAME
+static char *tildename = Nullch;
+static char *tildedir = Nullch;
+#endif
+
+#ifdef CONDSUB
+char *skipinterp _((char *,char *));
+#endif
+
+static void abort_interp _((void));
+
+void
+intrp_init(tcbuf)
+char *tcbuf;
+{
+    char *getlogin();
+    
+    /* get environmental stuff */
+
+#ifdef NEWS_ADMIN
+    {
+#ifdef HAS_GETPWENT
+       struct passwd *getpwnam();
+       struct passwd *pwd = getpwnam(NEWS_ADMIN);
+
+       if (pwd != NULL)
+           newsuid = pwd->pw_uid;
+#else
+#ifdef TILDENAME
+       char tildenews[2+sizeof NEWS_ADMIN];
+       strcpy(tildenews, "~");
+       strcat(tildenews, NEWS_ADMIN);
+       (void) filexp(tildenews);
+#else
+       ??? "Define either HAS_GETPWENT or TILDENAME to get NEWS_ADMIN"
+#endif  /* TILDENAME */
+#endif /* HAS_GETPWENT */
+    }
+#endif /* NEWS_ADMIN */
+    /* get home directory */
+
+    homedir = getenv("HOME");
+    if (homedir == Nullch)
+       homedir = getenv("LOGDIR");
+
+    dotdir = getval("DOTDIR",homedir);
+
+    /* get login name */
+
+    logname = getenv("USER");
+    if (logname == Nullch)
+       logname = getenv("LOGNAME");
+#ifdef GETLOGIN
+    if (logname == Nullch)
+       logname = savestr(getlogin());
+#endif
+
+    spool = savestr(filexp(NEWSSPOOL));        /* usually /usr/spool/news */
+    threaddir = filexp(THREAD_DIR);
+    if (strEQ(threaddir,spool))
+       threaddir = spool;
+    else
+       threaddir = savestr(threaddir);
+    overviewdir = filexp(OVERVIEW_DIR);
+    if (strEQ(overviewdir,spool))
+       overviewdir = spool;
+    else
+       overviewdir = savestr(overviewdir);
+
+#ifdef NEWS_ADMIN
+    /* if this is the news admin than load his UID into newsuid */
+
+    if ( strEQ(logname,NEWS_ADMIN) )
+       newsuid = getuid();
+#endif
+
+    if (checkflag)                     /* that getwd below takes ~1/3 sec. */
+       return;                         /* and we do not need it for -c */
+    getwd(tcbuf);                      /* find working directory name */
+    origdir = savestr(tcbuf);          /* and remember it */
+
+    /* get the real name of the person (%N) */
+    /* Must be done after logname is read in because BERKNAMES uses that */
+
+    strcpy(tcbuf,getrealname((long)getuid()));
+    realname = savestr(tcbuf);
+
+    /* name of header file (%h) */
+
+    headname = savestr(filexp(HEADNAME));
+
+    /* host name that goes in postings (%H) */
+
+    phostname = PHOSTNAME;
+    if (*phostname == '/') {
+       if ((tmpfp = fopen(phostname,"r")) == NULL) {
+           printf("Warning: Couldn't open %s to determine hostname!\n",
+                  phostname); 
+           sig_catcher(0);
+       }
+       fgets(buf, sizeof(buf), tmpfp);
+       if (buf[strlen(buf)-1] == '\n')
+           buf[strlen(buf)-1] = 0;
+       fclose(tmpfp);
+    }
+    else {
+#ifdef HAS_GETHOSTNAME
+       gethostname(buf,sizeof buf);
+#else
+# ifdef HAS_UNAME
+       /* get sysname */
+       uname(&utsn);
+       strcpy(buf,utsn.nodename);
+# else
+#  ifdef PHOSTCMD
+       {
+       FILE *popen();
+       FILE *pipefp = popen(PHOSTCMD,"r");
+       
+       if (pipefp == Nullfp) {
+           printf("Can't find hostname\n");
+           sig_catcher(0);
+       }
+       fgets(buf,sizeof buf,pipefp);
+       buf[strlen(buf)-1] = '\0';      /* wipe out newline */
+       pclose(pipefp);
+       }
+#  else
+       *buf = '\0';
+#  endif /* PHOSTCMD */
+# endif /* HAS_UNAME */
+#endif /* HAS_GETHOSTNAME */
+       if (*buf) {
+           char *cp = index(buf,'.');
+           if (cp)
+               *cp = '\0';
+           cp = index(phostname,'.');
+           if (cp)
+               strcat(buf,cp);
+           phostname = savestr(buf);
+       }
+    }
+}
+
+/* expand filename via %, ~, and $ interpretation */
+/* returns pointer to static area */
+/* Note that there is a 1-deep cache of ~name interpretation */
+
+char *
+filexp(s)
+register char *s;
+{
+    static char filename[CBUFLEN];
+    char scrbuf[CBUFLEN];
+    register char *d;
+
+#ifdef DEBUG
+    if (debug & DEB_FILEXP)
+       printf("< %s\n",s) FLUSH;
+#endif
+    interp(filename, (sizeof filename), s);    
+                                       /* interpret any % escapes */
+#ifdef DEBUG
+    if (debug & DEB_FILEXP)
+       printf("%% %s\n",filename) FLUSH;
+#endif
+    s = filename;
+    if (*s == '~') {   /* does destination start with ~? */
+       if (!*(++s) || *s == '/') {
+           sprintf(scrbuf,"%s%s",homedir,s);
+                               /* swap $HOME for it */
+#ifdef DEBUG
+    if (debug & DEB_FILEXP)
+       printf("~ %s\n",scrbuf) FLUSH;
+#endif
+           strcpy(filename,scrbuf);
+       }
+       else {
+#ifdef TILDENAME
+           for (d=scrbuf; isalnum(*s); s++,d++)
+               *d = *s;
+           *d = '\0';
+           if (tildedir && strEQ(tildename,scrbuf)) {
+               strcpy(scrbuf,tildedir);
+               strcat(scrbuf, s);
+               strcpy(filename, scrbuf);
+#ifdef DEBUG
+               if (debug & DEB_FILEXP)
+                   printf("r %s %s\n",tildename,tildedir) FLUSH;
+#endif
+           }
+           else {
+               if (tildename) {
+                   free(tildename);
+                   free(tildedir);
+               }
+               tildedir = Nullch;
+               tildename = savestr(scrbuf);
+#ifdef HAS_GETPWENT    /* getpwnam() is not the paragon of efficiency */
+               {
+#ifdef notdef
+                   struct passwd *getpwnam _((char*));
+#endif
+                   struct passwd *pwd = getpwnam(tildename);
+                   if ( pwd == NULL){
+                       printf("%s is an unknown user. Using default.\n",tildename) FLUSH;
+                       return(Nullch);
+                   }
+                   sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
+                   tildedir = savestr(pwd->pw_dir);
+                   strcpy(filename,scrbuf);
+                   endpwent();
+               }
+#else                  /* this will run faster, and is less D space */
+               {       /* just be sure LOGDIRFIELD is correct */
+                   FILE *pfp = fopen("/etc/passwd","r");
+                   char tmpbuf[512];
+                   int i;
+                   
+                   if (pfp == Nullfp) {
+                       printf(cantopen,"passwd") FLUSH;
+                       sig_catcher(0);
+                   }
+                   while (fgets(tmpbuf,512,pfp) != Nullch) {
+                       d = cpytill(scrbuf,tmpbuf,':');
+#ifdef DEBUG
+                       if (debug & DEB_FILEXP)
+                           printf("p %s\n",tmpbuf) FLUSH;
+#endif
+                       if (strEQ(scrbuf,tildename)) {
+                           for (i=LOGDIRFIELD-2; i; i--) {
+                               if (d)
+                                   d = index(d+1,':');
+                           }
+                           if (d) {
+                               cpytill(scrbuf,d+1,':');
+                               tildedir = savestr(scrbuf);
+                               strcat(scrbuf,s);
+                               strcpy(filename,scrbuf);
+                           }
+                           break;
+                       }
+                   }
+                   fclose(pfp);
+               }
+#endif
+           }
+#else /* !TILDENAME */
+#ifdef VERBOSE
+           IF(verbose)
+               fputs("~loginname not implemented.\n",stdout) FLUSH;
+           ELSE
+#endif
+#ifdef TERSE
+               fputs("~login not impl.\n",stdout) FLUSH;
+#endif
+#endif
+       }
+    }
+    else if (*s == '$') {      /* starts with some env variable? */
+       d = scrbuf;
+       *d++ = '%';
+       if (s[1] == '{')
+           strcpy(d,s+2);
+       else {
+           *d++ = '{';
+           for (s++; isalnum(*s); s++) *d++ = *s;
+                               /* skip over token */
+           *d++ = '}';
+           strcpy(d,s);
+       }
+#ifdef DEBUG
+       if (debug & DEB_FILEXP)
+           printf("$ %s\n",scrbuf) FLUSH;
+#endif
+       interp(filename, (sizeof filename), scrbuf);
+                                       /* this might do some extra '%'s but */
+                                       /* that is how the Mercedes Benz */
+    }
+#ifdef DEBUG
+    if (debug & DEB_FILEXP)
+       printf("> %s\n",filename) FLUSH;
+#endif
+    return filename;
+}
+
+#ifdef CONDSUB
+/* skip interpolations */
+
+char *
+skipinterp(pattern,stoppers)
+register char *pattern;
+char *stoppers;
+{
+
+    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
+#ifdef DEBUG
+       if (debug & DEB_INTRP)
+           printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
+#endif
+       if (*pattern == '%' && pattern[1]) {
+           switch (*++pattern) {
+           case '{':
+               for (pattern++; *pattern && *pattern != '}'; pattern++)
+                   if (*pattern == '\\')
+                       pattern++;
+               break;
+           case '[':
+               for (pattern++; *pattern && *pattern != ']'; pattern++)
+                   if (*pattern == '\\')
+                       pattern++;
+               break;
+#ifdef CONDSUB
+           case '(': {
+               pattern = skipinterp(pattern+1,"!=");
+               if (!*pattern)
+                   goto getout;
+               for (pattern++; *pattern && *pattern != '?'; pattern++)
+                   if (*pattern == '\\')
+                       pattern++;
+               if (!*pattern)
+                   goto getout;
+               pattern = skipinterp(pattern+1,":)");
+               if (*pattern == ':')
+                   pattern = skipinterp(pattern+1,")");
+               break;
+           }
+#endif
+#ifdef BACKTICK
+           case '`': {
+               pattern = skipinterp(pattern+1,"`");
+               break;
+           }
+#endif
+#ifdef PROMPTTTY
+           case '"':
+               pattern = skipinterp(pattern+1,"\"");
+               break;
+#endif
+           default:
+               break;
+           }
+           pattern++;
+       }
+       else {
+           if (*pattern == '^' && pattern[1])
+               pattern += 2;
+           else if (*pattern == '\\' && pattern[1])
+               pattern += 2;
+           else
+               pattern++;
+       }
+    }
+getout:
+    return pattern;                    /* where we left off */
+}
+#endif
+
+/* interpret interpolations */
+
+char *
+dointerp(dest,destsize,pattern,stoppers)
+register char *dest;
+register int destsize;
+register char *pattern;
+char *stoppers;
+{
+    char *subj_buf = Nullch;
+    char *ngs_buf = Nullch;
+    char *refs_buf = Nullch;
+    char *artid_buf = Nullch;
+    char *reply_buf = Nullch;
+    char *from_buf = Nullch;
+    char *path_buf = Nullch;
+    char *follow_buf = Nullch;
+    char *dist_buf = Nullch;
+    char *line_buf = Nullch;
+    register char *s, *h;
+    register int i;
+    char scrbuf[512];
+    char spfbuf[512];
+    bool upper = FALSE;
+    bool lastcomp = FALSE;
+    bool re_quote = FALSE;
+    bool proc_sprintf = FALSE;
+    int metabit = 0;
+
+    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
+#ifdef DEBUG
+       if (debug & DEB_INTRP)
+           printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
+#endif
+       if (*pattern == '%' && pattern[1]) {
+           upper = FALSE;
+           lastcomp = FALSE;
+           re_quote = FALSE;
+           proc_sprintf = FALSE;
+           for (s=Nullch; !s; ) {
+               switch (*++pattern) {
+               case '^':
+                   upper = TRUE;
+                   break;
+               case '_':
+                   lastcomp = TRUE;
+                   break;
+               case '\\':
+                   re_quote = TRUE;
+                   break;
+               case ':':
+                   proc_sprintf = TRUE;
+                   h = spfbuf;
+                   *h++ = '%';
+                   pattern++;  /* Skip over ':' */
+                   while (*pattern
+                    && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) {
+                       *h++ = *pattern++;
+                   }
+                   *h++ = 's';
+                   *h++ = '\0';
+                   pattern--;
+                   break;
+               case '/':
+#ifdef ARTSRCH
+                   s = scrbuf;
+                   if (!index("/?g",pattern[-2]))
+                       *s++ = '/';
+                   strcpy(s,lastpat);
+                   s += strlen(s);
+                   if (pattern[-2] != 'g') {
+                       if (index("/?",pattern[-2]))
+                           *s++ = pattern[-2];
+                       else
+                           *s++ = '/';
+                       if (art_howmuch == 1)
+                           *s++ = 'h';
+                       else if (art_howmuch == 2)
+                           *s++ = 'a';
+                       if (art_doread)
+                           *s++ = 'r';
+                   }
+                   *s = '\0';
+                   s = scrbuf;
+#else
+                   s = nullstr;
+#endif
+                   break;
+               case '{':
+                   pattern = cpytill(scrbuf,pattern+1,'}');
+                   if (s = index(scrbuf,'-'))
+                       *s++ = '\0';
+                   else
+                       s = nullstr;
+                   s = getval(scrbuf,s);
+                   break;
+               case '[':
+                   pattern = cpytill(scrbuf,pattern+1,']');
+                   i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
+                   if (line_buf)
+                       free(line_buf);
+                   s = line_buf = fetchlines(art,i);
+                   break;
+#ifdef CONDSUB
+               case '(': {
+                   COMPEX *oldbra_compex = bra_compex;
+                   COMPEX cond_compex;
+                   char rch;
+                   bool matched;
+                   
+                   init_compex(&cond_compex);
+                   pattern = dointerp(dest,destsize,pattern+1,"!=");
+                   rch = *pattern;
+                   if (rch == '!')
+                       pattern++;
+                   if (*pattern != '=')
+                       goto getout;
+                   pattern = cpytill(scrbuf,pattern+1,'?');
+                   if (!*pattern)
+                       goto getout;
+                   if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
+                       printf("%s: %s\n",scrbuf,s) FLUSH;
+                       pattern += strlen(pattern);
+                       goto getout;
+                   }
+                   matched = (execute(&cond_compex,dest) != Nullch);
+                   if (cond_compex.nbra)       /* were there brackets? */
+                       bra_compex = &cond_compex;
+                   if (matched==(rch == '=')) {
+                       pattern = dointerp(dest,destsize,pattern+1,":)");
+                       if (*pattern == ':')
+                           pattern = skipinterp(pattern+1,")");
+                   }
+                   else {
+                       pattern = skipinterp(pattern+1,":)");
+                       if (*pattern == ':')
+                           pattern++;
+                       pattern = dointerp(dest,destsize,pattern,")");
+                   }
+                   s = dest;
+                   bra_compex = oldbra_compex;
+                   free_compex(&cond_compex);
+                   break;
+               }
+#endif
+#ifdef BACKTICK
+               case '`': {
+                   FILE *pipefp, *popen();
+
+                   pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
+                   pipefp = popen(scrbuf,"r");
+                   if (pipefp != Nullfp) {
+                       int len;
+
+                       len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
+                           pipefp);
+                       scrbuf[len] = '\0';
+                       pclose(pipefp);
+                   }
+                   else {
+                       printf("\nCan't run %s\n",scrbuf);
+                       *scrbuf = '\0';
+                   }
+                   for (s=scrbuf; *s; s++) {
+                       if (*s == '\n') {
+                           if (s[1])
+                               *s = ' ';
+                           else
+                               *s = '\0';
+                       }
+                   }
+                   s = scrbuf;
+                   break;
+               }
+#endif
+#ifdef PROMPTTTY
+               case '"':
+                   pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
+                   fputs(scrbuf,stdout) FLUSH;
+                   resetty();
+                   gets(scrbuf);
+                   noecho();
+                   crmode();
+                   s = scrbuf;
+                   break;
+#endif
+               case '~':
+                   s = homedir;
+                   break;
+               case '.':
+                   s = dotdir;
+                   break;
+               case '$':
+                   s = scrbuf;
+                   sprintf(s,"%ld",our_pid);
+                   break;
+               case '#':
+                   s = scrbuf;
+                   sprintf(s,"%d",perform_cnt);
+                   break;
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+#ifdef CONDSUB
+                   s = getbracket(bra_compex,*pattern - '0');
+#else
+                   s = nullstr;
+#endif
+                   break;
+               case 'a':
+                   s = scrbuf;
+                   sprintf(s,"%ld",(long)art);
+                   break;
+               case 'A':
+#ifdef LINKART
+                   s = linkartname;    /* so Eunice people get right file */
+#else
+                   s = scrbuf;
+#ifdef USE_NNTP
+                   sprintf(s,"%s/%s",spool,nntp_artname());
+#else
+                   sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
+#endif
+#endif
+                   break;
+               case 'b':
+                   s = savedest;
+                   break;
+               case 'B':
+                   s = scrbuf;
+                   sprintf(s,"%ld",(long)savefrom);
+                   break;
+               case 'c':
+                   s = ngdir;
+                   break;
+               case 'C':
+                   s = ngname;
+                   break;
+               case 'd':
+                   s = scrbuf;
+                   sprintf(s,"%s/%s",spool,ngdir);
+                   break;
+               case 'D':
+                   s = dist_buf = fetchlines(art,DIST_LINE);
+                   break;
+               case 'e':
+                   s = (extractprog ? extractprog : "-");
+                   break;
+               case 'E':
+                   s = extractdest;
+                   break;
+               case 'f':                       /* from line */
+                   parseheader(art);
+                   if (htype[REPLY_LINE].ht_minpos >= 0) {
+                                               /* was there a reply line? */
+                       if (!(s=reply_buf))
+                           s = reply_buf = fetchlines(art,REPLY_LINE);
+                   }
+                   else if (!(s = from_buf))
+                       s = from_buf = fetchlines(art,FROM_LINE);
+                   break;
+               case 'F':
+                   parseheader(art);
+                   if (htype[FOLLOW_LINE].ht_minpos >= 0)
+                                       /* is there a Followup-To line? */
+                       s = follow_buf = fetchlines(art,FOLLOW_LINE);
+                   else 
+                       s = ngs_buf = fetchlines(art,NGS_LINE);
+                   break;
+               case 'h':                       /* header file name */
+                   s = headname;
+                   break;
+               case 'H':                       /* host name in postings */
+                   s = phostname;
+                   break;
+               case 'i':
+                   if (!(s=artid_buf))
+                       s = artid_buf = fetchlines(art,MESSID_LINE);
+                   if (*s && *s != '<') {
+                       sprintf(scrbuf,"<%s>",artid_buf);
+                       s = scrbuf;
+                   }
+                   break;
+               case 'I':                       /* ref article indicator */
+                   s = scrbuf;
+                   sprintf(scrbuf,"'%s'",indstr);
+                   break;
+               case 'l':                       /* rn library */
+#ifdef NEWS_ADMIN
+                   s = newsadmin;
+#else
+                   s = "???";
+#endif
+                   break;
+               case 'L':                       /* login id */
+                   s = logname;
+                   break;
+               case 'm':               /* current mode */
+                   s = scrbuf;
+                   *s = mode;
+                   s[1] = '\0';
+                   break;
+               case 'M':
+                   sprintf(scrbuf,"%ld",(long)dmcount);
+                   s = scrbuf;
+                   break;
+               case 'n':                       /* newsgroups */
+                   s = ngs_buf = fetchlines(art,NGS_LINE);
+                   break;
+               case 'N':                       /* full name */
+                   s = getval("NAME",realname);
+                   break;
+               case 'o':                       /* organization */
+#ifdef IGNOREORG
+                   s = getval("NEWSORG",orgname); 
+#else
+                   s = getenv("NEWSORG");
+                   if (s == Nullch) 
+                       s = getval("ORGANIZATION",orgname); 
+#endif
+#ifdef ORGFILE
+                   if (*s == '/') {
+                       FILE *ofp = fopen(s,"r");
+
+                       if (ofp) {
+                           fgets(scrbuf,sizeof scrbuf,ofp);
+                           fclose(ofp);
+                           s = scrbuf+strlen(scrbuf)-1;
+                           if (*s == '\n')
+                               *s = '\0';
+                           s = scrbuf;
+                       }
+                   }
+#endif
+                   break;
+               case 'O':
+                   s = origdir;
+                   break;
+               case 'p':
+                   s = cwd;
+                   break;
+               case 'P':
+                   s = spool;
+                   break;
+               case 'r':
+                   parseheader(art);
+                   if (htype[REFS_LINE].ht_minpos >= 0) {
+                       refs_buf = fetchlines(art,REFS_LINE);
+                       refscpy(scrbuf,(sizeof scrbuf),refs_buf);
+                   }
+                   else
+                       *scrbuf = '\0';
+                   s = rindex(scrbuf,'<');
+                   break;
+               case 'R':
+                   parseheader(art);
+                   if (htype[REFS_LINE].ht_minpos >= 0) {
+                       refs_buf = fetchlines(art,REFS_LINE);
+                       refscpy(scrbuf,(sizeof scrbuf),refs_buf);
+                       /* no more than 3 prior references PLUS the
+                       ** root article allowed, including the one
+                       ** concatenated below */
+                       if ((s = rindex(scrbuf,'<')) > scrbuf) {
+                           *s = '\0';
+                           h = rindex(scrbuf,'<');
+                           *s = '<';
+                           if (h > scrbuf) {
+                               s = index(scrbuf+1,'<');
+                               if (s < h)
+                                   strcpy(s,h);
+                           }
+                       }
+                   }
+                   else
+                       *scrbuf = '\0';
+                   if (!artid_buf)
+                       artid_buf = fetchlines(art,MESSID_LINE);
+                   if (artid_buf[0] == '<')
+                       safecat(scrbuf,artid_buf,sizeof(scrbuf));
+                   else if (artid_buf[0]) {
+                       char tmpbuf[64];
+    
+                       sprintf(tmpbuf,"<%s>",artid_buf);
+                       safecat(scrbuf,tmpbuf,sizeof(scrbuf));
+                   }
+                   s = scrbuf;
+                   break;
+               case 's':
+                   if (!(s=subj_buf))
+                       s = subj_buf = fetchsubj(art,TRUE);
+                                               /* get subject handy */
+                   while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
+                                               /* skip extra Re: */
+                       s += 3;
+                       if (*s == ' ')
+                           s++;
+                   }
+                   if (h = instr(s,"- (nf", TRUE))
+                       *h = '\0';
+                   break;
+               case 'S':
+                   if (!(s=subj_buf))
+                       s = subj_buf = fetchsubj(art,TRUE);
+                                               /* get subject handy */
+                   if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
+                                               /* skip extra Re: */
+                       s += 3;
+                       if (*s == ' ')
+                           s++;
+                   }
+                   break;
+               case 't':
+               case 'T':
+                   parseheader(art);
+                   if (htype[REPLY_LINE].ht_minpos >= 0) {
+                                       /* was there a reply line? */
+                       if (!(s=reply_buf))
+                           s = reply_buf = fetchlines(art,REPLY_LINE);
+                   }
+                   else if (!(s = from_buf))
+                       s = from_buf = fetchlines(art,FROM_LINE);
+                   else
+                       s = "noname";
+                   if (*pattern == 'T') {
+                       if (htype[PATH_LINE].ht_minpos >= 0) {
+                                       /* should we substitute path? */
+                           s = path_buf = fetchlines(art,PATH_LINE);
+                       }
+                       i = strlen(phostname);
+                       if (strnEQ(phostname,s,i) && s[i] == '!')
+                           s += i + 1;
+                   }
+                   if ((h=index(s,'(')) != Nullch)
+                                               /* strip garbage from end */
+                       *(h-1) = '\0';
+                   else if ((h=index(s,'<')) != Nullch) {
+                                               /* or perhaps from beginning */
+                       s = h+1;
+                       if ((h=index(s,'>')) != Nullch)
+                           *h = '\0';
+                   }
+                   break;
+               case 'u':
+                   sprintf(scrbuf,"%ld",(long)toread[ng]);
+                   s = scrbuf;
+                   break;
+               case 'U': {
+                   int unseen;
+
+                   unseen = (art <= lastart) && !was_read(art);
+                   if (selected_only) {
+                       int selected;
+
+                       selected = (curr_artp->flags & AF_SEL);
+                       sprintf(scrbuf,"%ld",
+                               (long)selected_count - (selected && unseen));
+                   }
+                   else
+                       sprintf(scrbuf,"%ld",(long)toread[ng] - unseen);
+                   s = scrbuf;
+                   break;
+               }
+               case 'v': {
+                   int selected, unseen;
+
+                   selected = (curr_artp->flags & AF_SEL);
+                   unseen = (art <= lastart) && !was_read(art);
+                   sprintf(scrbuf,"%ld",(long)toread[ng] - selected_count
+                                        - (!selected && unseen));
+                   s = scrbuf;
+                   break;
+               }
+               case 'W':
+                   s = threaddir;
+                   break;
+               case 'x':                       /* news library */
+                   s = lib;
+                   break;
+               case 'X':                       /* rn library */
+                   s = rnlib;
+                   break;
+               case 'z':
+#ifdef LINKART
+                   s = linkartname;    /* so Eunice people get right file */
+#else
+                   s = scrbuf;
+                   sprintf(s,"%ld",(long)art);
+#endif
+                   if (stat(s,&filestat) < 0)
+                       filestat.st_size = 0L;
+                   sprintf(scrbuf,"%5ld",(long)filestat.st_size);
+                   s = scrbuf;
+                   break;
+               case 'Z':
+                   sprintf(scrbuf,"%ld",(long)selected_count);
+                   s = scrbuf;
+                   break;
+               default:
+                   if (--destsize <= 0)
+                       abort_interp();
+                   *dest++ = *pattern | metabit;
+                   s = nullstr;
+                   break;
+               }
+           }
+           if (!s)
+               s = nullstr;
+           if (proc_sprintf) {
+               sprintf(scrbuf,spfbuf,s);
+               s = scrbuf;
+           }
+           pattern++;
+           if (upper || lastcomp) {
+               char *t;
+
+               if (s != scrbuf) {
+                   safecpy(scrbuf,s,(sizeof scrbuf));
+                   s = scrbuf;
+               }
+               if (upper || !(t=rindex(s,'/')))
+                   t = s;
+               while (*t && !isalpha(*t))
+                   t++;
+               if (islower(*t))
+                   *t = toupper(*t);
+           }
+           /* Do we have room left? */
+           i = strlen(s);
+           if (destsize <= i)
+               abort_interp();
+           destsize -= i;      /* adjust the size now. */
+
+           /* A maze of twisty little conditions, all alike... */
+           if (metabit) {
+               /* set meta bit while copying. */
+               i = metabit;            /* maybe get into register */
+               if (s == dest) {
+                   while (*dest)
+                       *dest++ |= i;
+               } else {
+                   while (*s)
+                       *dest++ = *s++ | i;
+               }
+           } else if (re_quote) {
+               /* put a backslash before regexp specials while copying. */
+               if (s == dest) {
+                   /* copy out so we can copy in. */
+                   safecpy(scrbuf, s, sizeof scrbuf);
+                   s = scrbuf;
+                   if (i > sizeof scrbuf)      /* we truncated, ack! */
+                       destsize += i - sizeof scrbuf;
+               }
+               while (*s) {
+                   if (index(regexp_specials, *s)) {
+                       if (--destsize <= 0)
+                           abort_interp();
+                       *dest++ = '\\';
+                   }
+                   *dest++ = *s++;
+               }
+           } else {
+               /* straight copy. */
+               if (s == dest) {
+                   dest += i;
+               } else {
+                   while (*s)
+                       *dest++ = *s++;
+               }
+           }
+       }
+       else {
+           if (--destsize <= 0)
+               abort_interp();
+           if (*pattern == '^' && pattern[1]) {
+               ++pattern;                      /* skip uparrow */
+               i = *pattern;           /* get char into a register */
+               if (i == '?')
+                   *dest++ = '\177' | metabit;
+               else if (i == '(') {
+                   metabit = 0200;
+                   destsize++;
+               }
+               else if (i == ')') {
+                   metabit = 0;
+                   destsize++;
+               }
+               else
+                   *dest++ = (i & 037) | metabit;
+               pattern++;
+           }
+           else if (*pattern == '\\' && pattern[1]) {
+               ++pattern;                      /* skip backslash */
+               i = *pattern;           /* get char into a register */
+    
+               /* this used to be a switch but the if may save space */
+               
+               if (i >= '0' && i <= '7') {
+                   i = 1;
+                   while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
+                       i <<= 3;
+                       i += *pattern++ - '0';
+                   }
+                   *dest++ = (i & 0377) | metabit;
+                   --pattern;
+               }
+               else if (i == 'b')
+                   *dest++ = '\b' | metabit;
+               else if (i == 'f')
+                   *dest++ = '\f' | metabit;
+               else if (i == 'n')
+                   *dest++ = '\n' | metabit;
+               else if (i == 'r')
+                   *dest++ = '\r' | metabit;
+               else if (i == 't')
+                   *dest++ = '\t' | metabit;
+               else
+                   *dest++ = i | metabit;
+               pattern++;
+           }
+           else
+               *dest++ = *pattern++ | metabit;
+       }
+    }
+    *dest = '\0';
+getout:
+    if (subj_buf != Nullch)    /* return any checked out storage */
+       free(subj_buf);
+    if (ngs_buf != Nullch)
+       free(ngs_buf);
+    if (refs_buf != Nullch)
+       free(refs_buf);
+    if (artid_buf != Nullch)
+       free(artid_buf);
+    if (reply_buf != Nullch)
+       free(reply_buf);
+    if (from_buf != Nullch)
+       free(from_buf);
+    if (path_buf != Nullch)
+       free(path_buf);
+    if (follow_buf != Nullch)
+       free(follow_buf);
+    if (dist_buf != Nullch)
+       free(dist_buf);
+    if (line_buf != Nullch)
+       free(line_buf);
+    return pattern;                    /* where we left off */
+}
+
+void
+interp(dest,destsize,pattern)
+char *dest;
+int destsize;
+char *pattern;
+{
+    dointerp(dest,destsize,pattern,Nullch);
+#ifdef DEBUG
+    if (debug & DEB_FILEXP)
+       fputs(dest,stdout);
+#endif
+}
+
+/* copy a references line, normalizing as we go */
+
+void
+refscpy(dest,destsize,src)
+register char *dest, *src;
+register int destsize;
+{
+    register char *dot, *at, *beg;
+    char tmpbuf[64];
+    
+    while (*src) {
+       if (*src != '<') {
+           if (--destsize <= 0)
+               break;
+           *dest++ = '<';
+           at = dot = Nullch;
+           beg = src;
+           while (*src && *src != ' ' && *src != ',') {
+               if (*src == '.')
+                   dot = src;
+               else if (*src == '@')
+                   at = src;
+               if (--destsize <= 0)
+                   break;
+               *dest++ = *src++;
+           }
+           if (destsize <= 0)
+               break;
+           if (dot && !at) {
+               int len;
+
+               *dest = *dot++ = '\0';
+               sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
+               len = strlen(tmpbuf);
+               if (destsize > len) {
+                   strcpy(dest,tmpbuf);
+                   dest = dest + len;
+                   destsize -= len;
+               }
+           }
+           if (--destsize <= 0)
+               break;
+           *dest++ = '>';
+       }
+       else {
+           while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
+           if (destsize <= 0)
+               break;
+       }
+       while (*src == ' ' || *src == '\t' || *src == '\n' || *src == ',')
+           src++;
+       if (*src && --destsize > 0)
+           *dest++ = ' ';
+    }
+    *dest = '\0';
+} 
+
+/* get the person's real name from /etc/passwd */
+/* (string is overwritten, so it must be copied) */
+
+char *
+getrealname(uid)
+long uid;
+{
+    char *s, *c;
+
+#ifdef PASSNAMES
+#ifdef HAS_GETPWENT
+#ifdef notdef
+    struct passwd *getpwuid _((uid_t));
+#endif
+    struct passwd *pwd = getpwuid(uid);
+
+    if (!pwd)
+       return nullstr;
+
+    s = pwd->pw_gecos;
+#else
+    char tmpbuf[512];
+    int i;
+
+    getpw(uid, tmpbuf);
+    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
+       if (s)
+           s = index(s,':')+1;
+    }
+    if (!s)
+       return nullstr;
+    cpytill(tmpbuf,s,':');
+    s = tmpbuf;
+#endif
+#ifdef BERKNAMES
+#ifdef BERKJUNK
+    while (*s && !isalnum(*s) && *s != '&') s++;
+#endif
+    if ((c = index(s, ',')) != Nullch)
+       *c = '\0';
+    if ((c = index(s, ';')) != Nullch)
+       *c = '\0';
+    s = cpytill(buf,s,'&');
+    if (*s == '&') {                   /* whoever thought this one up was */
+       c = buf + strlen(buf);          /* in the middle of the night */
+       strcat(c,logname);              /* before the morning after */
+       strcat(c,s+1);
+       if (islower(*c))
+           *c = toupper(*c);           /* gack and double gack */
+    }
+#else
+    if ((c = index(s, '(')) != Nullch)
+       *c = '\0';
+    if ((c = index(s, '-')) != Nullch)
+       s = c;
+    strcpy(buf,s);
+#endif
+#ifdef HAS_GETPWENT
+    endpwent();
+#endif
+    return buf;                                /* return something static */
+#else
+    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
+       fgets(buf,sizeof buf,tmpfp);
+       fclose(tmpfp);
+       buf[strlen(buf)-1] = '\0';
+       return buf;
+    }
+    return "PUT YOUR NAME HERE";
+#endif
+}
+
+static void
+abort_interp()
+{
+    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
+    sig_catcher(0);
+}
+
+
diff --git a/usr/src/contrib/news/trn3/ngdata.c b/usr/src/contrib/news/trn3/ngdata.c
new file mode 100644 (file)
index 0000000..f3140c9
--- /dev/null
@@ -0,0 +1,316 @@
+/* $Id: ngdata.c,v 3.0 1992/02/01 03:09:32 davison Trn $
+ */
+/* This software is Copyright 1991 by Stan Barber. 
+ *
+ * Permission is hereby granted to copy, reproduce, redistribute or otherwise
+ * use this software as long as: there is no monetary profit gained
+ * specifically from the use or reproduction of this software, it is not
+ * sold, rented, traded or otherwise marketed, and this copyright notice is
+ * included prominently in any copy made. 
+ *
+ * The author make no claims as to the fitness or correctness of this software
+ * for any use whatsoever, and it is provided as is. Any use of this software
+ * is at the user's own risk. 
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "rcstuff.h"
+#include "trn.h"
+#include "cache.h"
+#include "bits.h"
+#include "head.h"
+#include "rthread.h"
+#include "rt-select.h"
+#include "ng.h"
+#include "intrp.h"
+#include "kfile.h"
+#include "final.h"
+#include "rcln.h"
+#include "term.h"
+#include "util.h"
+#include "nntp.h"
+#include "ndir.h"
+#include "INTERN.h"
+#include "ngdata.h"
+
+/* open the active file */
+
+void
+ngdata_init()
+{
+#ifdef USE_NNTP
+    char *cp;
+
+    nntp_command("LIST");      /* tell server we want the active file */
+    if (nntp_check(TRUE) != NNTP_CLASS_OK) { /* and then see if that's ok */
+       printf("Can't get active file from server: \n%s\n", ser_line);
+       finalize(1);
+    }
+    time(&lastactfetch);
+
+    cp = filexp("%P/rrnact.%$");       /* make a temporary name */
+    strcpy(active_name, cp);
+    actfp = fopen(active_name, "w+");  /* and get ready */
+    if (actfp == Nullfp) {
+       printf(cantopen,active_name) FLUSH;
+       finalize(1);
+    }
+
+    activeitems = 0;
+    while (1) {
+       nntp_gets(ser_line, sizeof ser_line);
+       if (ser_line[0] == '.')         /* while there's another line */
+           break;                      /* get it and write it to */
+       activeitems++;
+       fputs(ser_line, actfp);
+       putc('\n', actfp);
+    }
+
+    if (ferror(actfp)) {
+       printf("Error writing to active file %s.\n", active_name) FLUSH;
+       finalize(1);
+    }
+#else /* !USE_NNTP */
+    ngdatansrv_init();
+#endif
+    if (fseek(actfp,0L,0) == -1) {     /* just get to the beginning */
+       printf("Error seeking in active file.\n") FLUSH;
+       finalize(1);
+    }
+    return;
+}
+
+bool
+access_ng()
+{
+#ifdef USE_NNTP
+    long unread, first, last;
+
+    if (!nntp_group(ngname)) {
+       toread[ng] = TR_BOGUS;
+       return FALSE;
+    }
+    if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */
+       return FALSE;
+    (void) sscanf(ser_line,"%*d%ld%ld%ld",&unread,&first,&last);
+
+    /* NNTP mangles the high/low values when no articles are present. */
+    if (!unread)
+       absfirst = lastart+1;
+    else {
+       absfirst = (ART_NUM)first;
+       if (last > lastart)
+           lastart = (ART_NUM)last;
+    }
+    ngmax[ng] = lastart;       /* ensure getngsize() knows the new maximum */
+    first = abs1st[ng];
+    abs1st[ng] = absfirst;
+    if (absfirst > first)
+       checkexpired(ng);
+#else /* !USE_NNTP */
+
+    if (eaccess(ngdir,5)) {            /* directory read protected? */
+       if (eaccess(ngdir,0)) {
+# ifdef VERBOSE
+           IF(verbose)
+               printf("\nNewsgroup %s does not have a spool directory!\n",
+                   ngname) FLUSH;
+           ELSE
+# endif
+# ifdef TERSE
+               printf("\nNo spool for %s!\n",ngname) FLUSH;
+# endif
+# ifdef CATCHUP
+           catch_up(ng);
+# endif
+       } else {
+# ifdef VERBOSE
+           IF(verbose)
+               printf("\nNewsgroup %s is not currently accessible.\n",
+                   ngname) FLUSH;
+           ELSE
+# endif
+# ifdef TERSE
+               printf("\n%s not readable.\n",ngname) FLUSH;
+# endif
+       }
+       toread[ng] = TR_NONE; /* make this newsgroup temporarily invisible */
+       return FALSE;
+    }
+
+    /* chdir to newsgroup subdirectory */
+
+    if (chdir(ngdir)) {
+       printf(nocd,ngdir) FLUSH;
+       return FALSE;
+    }
+    if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */
+       return FALSE;
+    absfirst = abs1st[ng];
+    setfoundbits();                    /* might reset absfirst */
+#endif /* !USE_NNTP */
+
+    dmcount = 0;
+
+    build_cache();
+    return TRUE;
+}
+
+void
+grow_ng(newlast)
+ART_NUM newlast;
+{
+    ART_NUM tmpfirst;
+
+    forcegrow = FALSE;
+    if (newlast > lastart) {
+       ART_NUM tmpart = art;
+       toread[ng] += (ART_UNREAD)(newlast-lastart);
+       grow_cache(newlast);
+       tmpfirst = lastart+1;
+       lastart = newlast;
+       thread_grow();
+#ifdef KILLFILES
+#ifdef VERBOSE
+       IF(verbose)
+           sprintf(buf,
+               "%ld more article%s arrived -- processing memorized commands...\n\n",
+               (long)(lastart - tmpfirst + 1),
+               (lastart > tmpfirst ? "s have" : " has" ) );
+       ELSE                    /* my, my, how clever we are */
+#endif
+#ifdef TERSE
+           strcpy(buf, "More news -- auto-processing...\n\n");
+#endif
+       if (has_normal_kills)
+           kill_unwanted(tmpfirst,buf,TRUE);
+#endif
+       art = tmpart;
+    }
+}
+
+void
+ng_skip()
+{
+#ifndef USE_NNTP                       /* never read it & cannot find it? */
+    if (errno != ENOENT) {     /* has it not been deleted? */
+       clear();
+# ifdef VERBOSE
+       IF(verbose)
+           printf("\n(Article %ld exists but is unreadable.)\n",(long)art)
+               FLUSH;
+       ELSE
+# endif
+# ifdef TERSE
+           printf("\n(%ld unreadable.)\n",(long)art) FLUSH;
+# endif
+       if (novice_delays) {
+           pad(just_a_sec);
+           sleep(2);
+       }
+    }
+    inc_art(selected_only,FALSE);      /* try next article */
+
+#else /* USE_NNTP */
+    ART_NUM artnum;
+
+    clear();
+# ifdef VERBOSE
+    IF(verbose)
+       fputs("Skipping unavailable article\n",stdout);
+    ELSE
+# endif /* VERBOSE */
+# ifdef TERSE
+       fputs("Skipping\n",stdout);
+# endif /* TERSE */
+    if (novice_delays) {
+       pad(just_a_sec/3);
+       sleep(1);
+    }
+    art++;
+    artp++;
+    do {
+       /* tries to grab PREFETCH_SIZE XHDRS, flagging missing articles */
+       (void) fetchsubj(art, FALSE);
+       artnum = art+PREFETCH_SIZE-1;
+       if (artnum > lastart)
+           artnum = lastart;
+       while (art <= artnum) {
+           if (!(artp->flags & AF_MISSING))
+               return;
+           art++;
+           artp++;
+       }
+    } while (art <= lastart);
+#endif /* USE_NNTP */
+}
+
+/* find the maximum article number of a newsgroup */
+
+ART_NUM
+getngsize(num)
+register NG_NUM num;
+{
+    register int len;
+    register char *nam;
+    char tmpbuf[LBUFLEN];
+    ART_POS oldsoft;
+    long last, first;
+    char ch;
+
+    nam = rcline[num];
+    len = rcnums[num] - 1;
+    softtries++;
+#ifdef DEBUG
+    if (debug & DEB_SOFT_POINTERS)
+       printf("Softptr = %ld\n",(long)softptr[num]) FLUSH;
+#endif
+    oldsoft = softptr[num];
+#ifndef USE_NNTP
+    fseek(actfp,100000L,1);    /* hopefully this forces a reread */
+#endif
+    if ((softptr[num] = findact(tmpbuf, nam, len, (long)oldsoft)) >= 0) {
+       if (softptr[num] != oldsoft) {
+           softmisses++;
+           writesoft = TRUE;
+       }
+    }
+    else {
+       softptr[num] = 0;
+       if (RCCHAR(rcchar[num]) == ':')
+           rcchar[num] = NEGCHAR;
+       return TR_BOGUS;                /* well, not so quietly, actually */
+    }
+       
+#ifdef DEBUG
+    if (debug & DEB_SOFT_POINTERS) {
+       printf("Should be %ld\n",(long)softptr[num]) FLUSH;
+    }
+#endif
+#ifndef ANCIENT_NEWS
+    sscanf(tmpbuf+len+1, "%ld %ld %c", &last, &first, &ch);
+#else
+    sscanf(tmpbuf+len+1, "%ld %c", &last, &ch);
+    first = 1;
+#endif
+    if (!abs1st[num])
+       abs1st[num] = (ART_NUM)first;
+    if (!in_ng) {
+       switch (ch) {
+       case 'n': moderated = getval("NOPOSTRING"," (no posting)"); break;
+       case 'm': moderated = getval("MODSTRING", " (moderated)"); break;
+       /* This shouldn't even occur.  What are we doing in a non-existent
+          group?  Disallow it. */
+       case 'x': return TR_BOGUS;
+       /* what should be done about refiled groups?  rn shouldn't even
+          be in them (ie, if sci.aquaria is refiled to rec.aquaria, then
+          get the news there) */
+       case '=': return TR_BOGUS;
+       default: moderated = nullstr;
+       }
+    }
+    if (last < ngmax[num])
+       return ngmax[num];
+    return ngmax[num] = (ART_NUM)last;
+}
diff --git a/usr/src/contrib/news/trn3/patchlevel.h b/usr/src/contrib/news/trn3/patchlevel.h
new file mode 100644 (file)
index 0000000..800ae45
--- /dev/null
@@ -0,0 +1 @@
+#define PATCHLEVEL "Version: 3.0 "
diff --git a/usr/src/contrib/news/trn3/rt-process.c b/usr/src/contrib/news/trn3/rt-process.c
new file mode 100644 (file)
index 0000000..55c2692
--- /dev/null
@@ -0,0 +1,488 @@
+/* $Id: rt-process.c,v 3.0 1992/12/14 00:14:13 davison Trn $
+*/
+
+#include "EXTERN.h"
+#include "common.h"
+#include "intrp.h"
+#include "trn.h"
+#include "cache.h"
+#include "bits.h"
+#include "ng.h"
+#include "ngdata.h"
+#include "rcln.h"
+#include "util.h"
+#include "kfile.h"
+#include "hash.h"
+#include "rthread.h"
+#include "rt-select.h"
+
+extern HASHTABLE *msgid_hash;
+
+static char *valid_message_id _((char*, char*));
+static void merge_threads _((SUBJECT*, SUBJECT*));
+static void link_child _((ARTICLE*));
+static void unlink_child _((ARTICLE*));
+
+/* This depends on art being set to the current article number.
+*/
+ARTICLE *
+allocate_article(artnum)
+ART_NUM artnum;
+{
+    register ARTICLE *article;
+
+    /* create an new article */
+    if (artnum >= absfirst) {
+       if (artnum > lastart)
+           grow_cache(artnum);
+       article = article_ptr(artnum);
+    } else {
+       article = (ARTICLE *)safemalloc(sizeof (ARTICLE));
+       bzero((char*)article, sizeof (ARTICLE));
+       article->flags |= AF_READ|AF_MISSING|AF_FAKE|AF_TMPMEM;
+    }
+    return article;
+}
+
+void
+fix_msgid(msgid)
+char *msgid;
+{
+    register char *cp;
+
+    if ((cp = index(msgid, '@')) != Nullch) {
+       while (*++cp) {
+           if (isupper(*cp)) {
+               *cp = tolower(*cp);     /* lower-case domain portion */
+           }
+       }
+    }
+}
+
+int
+msgid_cmp(key, keylen, data)
+char *key;
+int keylen;
+HASHDATUM data;
+{
+    ARTICLE *article = (data.dat_ptr? (ARTICLE*)data.dat_ptr
+                                   : article_ptr(data.dat_len));
+    /* We already know that the lengths are equal, just compare the strings */
+    return bcmp(key, article->msgid, keylen);
+}
+
+SUBJECT *fake_had_subj; /* the fake-turned-real article had this subject */
+
+bool
+valid_article(article)
+ARTICLE *article;
+{
+    ARTICLE *ap, *fake_ap;
+    char *msgid = article->msgid;
+    HASHDATUM data;
+
+    if (msgid) {
+       fix_msgid(msgid);
+       data = hashfetch(msgid_hash, msgid, strlen(msgid));
+       if ((fake_ap = (ARTICLE*)data.dat_ptr) == Nullart) {
+           if (!data.dat_len) {
+               data.dat_len = article_num(article);
+               hashstorelast(data);
+               fake_had_subj = Nullsubj;
+               return TRUE;
+           }
+           if (data.dat_len == article_num(article)) {
+               fake_had_subj = Nullsubj;
+               return TRUE;
+           }
+       }
+
+       /* Whenever we replace a fake art with a real one, it's a lot of work
+       ** cleaning up the references.  Fortunately, this is not often. */
+       if (fake_ap) {
+           article->parent = fake_ap->parent;
+           article->child1 = fake_ap->child1;
+           article->sibling = fake_ap->sibling;
+           fake_had_subj = fake_ap->subj;
+           if (fake_ap->flags & AF_AUTOFLAGS) {
+               article->flags |= fake_ap->flags & AF_AUTOFLAGS;
+               save_ids = TRUE;
+           }
+           if (curr_artp == fake_ap) {
+               curr_artp = article;
+               curr_art = article_num(article);
+           }
+           if (recent_artp == fake_ap) {
+               recent_artp = article;
+               recent_art = article_num(article);
+           }
+           if ((ap = article->parent) != Nullart) {
+               if (ap->child1 == fake_ap)
+                   ap->child1 = article;
+               else {
+                   ap = ap->child1;
+                   goto sibling_search;
+               }
+           } else if (fake_had_subj) {
+               register SUBJECT *sp = fake_had_subj;
+               if ((ap = sp->thread) == fake_ap) {
+                   do {
+                       sp->thread = article;
+                       sp = sp->thread_link;
+                   } while (sp != fake_had_subj);
+               } else {
+                 sibling_search:
+                   while (ap->sibling) {
+                       if (ap->sibling == fake_ap) {
+                           ap->sibling = article;
+                           break;
+                       }
+                       ap = ap->sibling;
+                   }
+               }
+#if 1
+               for (ap = fake_had_subj->articles; ap; ap = ap->subj_next) {
+                   assert(ap != fake_ap);
+               }
+#endif
+           }
+           for (ap = article->child1; ap; ap = ap->sibling)
+               ap->parent = article;
+           clear_article(fake_ap);
+           free((char*)fake_ap);
+           data.dat_ptr = Nullch;
+           data.dat_len = article_num(article);
+           hashstorelast(data);
+           return TRUE;
+       }
+    }
+    /* Forget about the duplicate message-id or bogus article. */
+    uncache_article(article,TRUE);
+    return FALSE;
+}
+
+/* Take a message-id and see if we already know about it.  If so, return
+** the article, otherwise create a fake one.
+*/
+ARTICLE *
+get_article(msgid)
+char *msgid;
+{
+    register ARTICLE *article;
+    HASHDATUM data;
+
+    fix_msgid(msgid);
+
+    data = hashfetch(msgid_hash, msgid, strlen(msgid));
+    if (!(article = (ARTICLE *)data.dat_ptr)) {
+       if (data.dat_len)
+           article = article_ptr(data.dat_len);
+       else {
+           article = allocate_article(0);
+           data.dat_ptr = (char*)article;
+           article->msgid = savestr(msgid);
+           hashstorelast(data);
+       }
+    }
+    return article;
+}
+
+/* Take all the data we've accumulated about the article and shove it into
+** the article tree at the best place we can deduce.
+*/
+void
+thread_article(article)
+ARTICLE *article;
+{
+    register ARTICLE *ap, *last;
+    register char *cp, *end;
+    ARTICLE *kill_ap = ((article->flags & AF_AUTOKILL)? article : Nullart);
+    int select_this_art = (article->subj->flags & SF_AUTOSELECT)
+       || (article->flags & AF_AUTOSELECTALL)? AF_AUTOSELECTALL
+       : (article->flags & AF_AUTOSELECT);
+
+    /* We're definitely not a fake anymore */
+    article->flags = (article->flags & ~AF_FAKE) | AF_THREADED;
+
+    /* If the article was already part of an existing thread, unlink it
+    ** to try to put it in the best possible spot.
+    */
+    if (fake_had_subj) {
+       if (fake_had_subj->thread != article->subj->thread) {
+           fake_had_subj->flags &= ~SF_THREAD;
+           merge_threads(fake_had_subj, article->subj);
+       }
+       /* Check for a real or shared-fake parent */
+       ap = article->parent;
+       while (ap && (ap->flags&AF_FAKE) == AF_FAKE && !ap->child1->sibling)
+           ap = ap->parent;
+       unlink_child(article);
+       if (ap) {                       /* do we have decent parents? */
+           /* Yes: assume that our references are ok, and just reorder us
+           ** with our siblings by date.
+           */
+           link_child(article);
+           /* Freshen the date & subject in any faked parent articles. */
+           for (ap = article->parent;
+                ap && (ap->flags&AF_FAKE)==AF_FAKE && article->date < ap->date;
+                ap = ap->parent)
+           {
+               ap->date = article->date;
+               ap->subj = article->subj;
+               unlink_child(ap);
+               link_child(ap);
+           }
+           goto exit;
+       }
+       /* We'll assume that this article has as good or better references
+       ** than the child that faked us initially.  Free the fake reference-
+       ** chain and process our references as usual.
+       */
+       for (ap = article->parent; ap; ap = last) {
+           unlink_child(ap);
+           last = ap->parent;
+           ap->date = 0;
+           ap->subj = 0;
+           ap->parent = 0;
+           /* don't free it until group exit since we probably re-use it */
+       }
+       article->parent = Nullart;              /* neaten up */
+       article->sibling = Nullart;
+    }
+
+    /* If we have references, process them from the right end one at a time
+    ** until we either run into somebody, or we run out of references.
+    */
+    if (*references) {
+       last = article;
+       ap = Nullart;
+       end = references + strlen(references) - 1;
+       while ((cp = rindex(references, '<')) != Nullch) {
+           while (end >= cp && ((unsigned char)*end <= ' ' || *end == ',')) {
+               end--;
+           }
+           end[1] = '\0';
+           /* Quit parsing references if this one is garbage. */
+           if (!(end = valid_message_id(cp, end)))
+               break;
+           /* Dump all domains that end in '.', such as "..." & "1@DEL." */
+           if (end[-1] == '.')
+               break;
+           ap = get_article(cp);
+           *cp = '\0';
+           select_this_art |= ap->flags & (AF_AUTOSELECT|AF_AUTOSELECTALL);
+           if (ap->flags & AF_AUTOKILL)
+               kill_ap = ap;
+
+           /* Check for duplicates on the reference line.  Brand-new data has
+           ** no date.  Data we just allocated earlier on this line has a
+           ** date but no subj.  Special-case the article itself, since it
+           ** does have a subj.
+           */
+           if ((ap->date && !ap->subj) || ap == article) {
+               if ((ap = last) == article)
+                   ap = Nullart;
+               continue;
+           }
+           last->parent = ap;
+           link_child(last);
+           if (ap->subj)
+               break;
+
+           ap->date = article->date;
+           last = ap;
+           end = cp-1;
+       }
+       if (!ap)
+           goto no_references;
+
+       /* Check if we ran into anybody that was already linked.  If so, we
+       ** just use their thread.
+       */
+       if (ap->subj) {
+           /* See if this article spans the gap between what we thought
+           ** were two different threads.
+           */
+           if (article->subj->thread != ap->subj->thread)
+               merge_threads(ap->subj, article->subj);
+       } else {
+           /* We didn't find anybody we knew, so either create a new thread
+           ** or use the article's thread if it was previously faked.
+           */
+           ap->subj = article->subj;
+           link_child(ap);
+       }
+       /* Set the subj of faked articles we created as references. */
+       for (ap = article->parent; ap && !ap->subj; ap = ap->parent)
+           ap->subj = article->subj;
+
+       /* Make sure we didn't circularly link to a child article(!), by
+       ** ensuring that we run off the top before we run into ourself.
+       */
+       while (ap && ap->parent != article)
+           ap = ap->parent;
+       if (ap) {
+           /* Ugh.  Someone's tweaked reference line with an incorrect
+           ** article-order arrived first, and one of our children is
+           ** really one of our ancestors. Cut off the bogus child branch
+           ** right where we are and link it to the thread.
+           */
+           unlink_child(ap);
+           ap->parent = Nullart;
+           link_child(ap);
+       }
+    } else {
+      no_references:
+       /* The article has no references.  Either turn it into a new thread
+       ** or re-attach the fleshed-out article to its old thread.
+       */
+       link_child(article);
+    }
+exit:
+    if (!(article->flags & AF_CACHED))
+       cache_article(article);
+    if (select_this_art & AF_AUTOSELECTALL) {
+       if (sel_mode == SM_THREAD)
+           select_thread(article->subj->thread, AF_AUTOSELECTALL);
+       else
+           select_subject(article->subj, AF_AUTOSELECTALL);
+    } else if (select_this_art)
+       select_subthread(article, AF_AUTOSELECT);
+    if (kill_ap)
+       kill_subthread(kill_ap, KF_ALL|KF_KILLFILE);
+}
+
+/* Check if the string we've found looks like a valid message-id reference.
+*/
+static char *
+valid_message_id(start, end)
+register char *start, *end;
+{
+    char *mid;
+
+    if (start == end)
+       return 0;
+
+    if (*end != '>') {
+       /* Compensate for space cadets who include the header in their
+       ** subsitution of all '>'s into another citation character.
+       */
+       if (*end == '<' || *end == '-' || *end == '!' || *end == '%'
+        || *end == ')' || *end == '|' || *end == ':' || *end == '}'
+        || *end == '*' || *end == '+' || *end == '#' || *end == ']'
+        || *end == '@' || *end == '$') {
+           *end = '>';
+       }
+    } else if (end[-1] == '>') {
+       *(end--) = '\0';
+    }
+    /* Id must be "<...@...>" */
+    if (*start != '<' || *end != '>' || (mid = index(start, '@')) == Nullch
+     || mid == start+1 || mid+1 == end) {
+       return 0;
+    }
+    return end;
+}
+
+/* Remove an article from its parent/siblings.  Leave parent pointer intact.
+*/
+static void
+unlink_child(child)
+register ARTICLE *child;
+{
+    register ARTICLE *last;
+
+    if (!(last = child->parent)) {
+       register SUBJECT *sp = child->subj;
+       if ((last = sp->thread) == child) {
+           do {
+               sp->thread = child->sibling;
+               sp = sp->thread_link;
+           } while (sp != child->subj);
+       } else
+           goto sibling_search;
+    } else {
+       if (last->child1 == child)
+           last->child1 = child->sibling;
+       else {
+           last = last->child1;
+         sibling_search:
+           while (last->sibling != child)
+               last = last->sibling;
+           last->sibling = child->sibling;
+       }
+    }
+}
+
+/* Link an article to its parent article.  If its parent pointer is zero,
+** link it to its thread.  Sorts siblings by date.
+*/
+static void
+link_child(child)
+register ARTICLE *child;
+{
+    register ARTICLE *ap;
+
+    if (!(ap = child->parent)) {
+       register SUBJECT *sp = child->subj;
+       ap = sp->thread;
+       if (!ap || child->date < ap->date) {
+           do {
+               sp->thread = child;
+               sp = sp->thread_link;
+           } while (sp != child->subj);
+           child->sibling = ap;
+       } else
+           goto sibling_search;
+    } else {
+       ap = ap->child1;
+       if (!ap || child->date < ap->date) {
+           child->sibling = ap;
+           child->parent->child1 = child;
+       } else {
+         sibling_search:
+           while (ap->sibling && ap->sibling->date <= child->date)
+               ap = ap->sibling;
+           child->sibling = ap->sibling;
+           ap->sibling = child;
+       }
+    }
+}
+
+/* Merge all of s2's thread into s1's thread.
+*/
+static void
+merge_threads(s1, s2)
+SUBJECT *s1, *s2;
+{
+    register SUBJECT *sp;
+    register ARTICLE *t1, *t2;
+    int visit_flag;
+
+    t1 = s1->thread;
+    t2 = s2->thread;
+    t1->subj->flags &= ~SF_THREAD;
+    if (sel_mode == SM_THREAD)
+       visit_flag = (t1->subj->flags | (t2? t2->subj->flags : 0)) & SF_VISIT;
+    else
+       visit_flag = 0;
+    /* Change all of t2's thread pointers to a common lead article */
+    sp = s2;
+    do {
+       sp->thread = t1;
+       sp->flags &= ~SF_THREAD;
+       sp = sp->thread_link;
+    } while (sp != s2);
+
+    /* Join the two circular lists together */
+    sp = s2->thread_link;
+    s2->thread_link = s1->thread_link;
+    s1->thread_link = sp;
+
+    /* Link each article that was attached to t2 to t1. */
+    for (t1 = t2; t1; t1 = t2) {
+       t2 = t2->sibling;
+       link_child(t1);      /* parent is null, thread is newly set */
+    }
+    s1->thread->subj->flags |= SF_THREAD | visit_flag;
+}
diff --git a/usr/src/contrib/news/trn3/sw.c b/usr/src/contrib/news/trn3/sw.c
new file mode 100644 (file)
index 0000000..cd3fa20
--- /dev/null
@@ -0,0 +1,647 @@
+/* $Id: sw.c,v 3.0 1992/02/01 03:09:32 davison Trn $
+ */
+/* This software is Copyright 1991 by Stan Barber. 
+ *
+ * Permission is hereby granted to copy, reproduce, redistribute or otherwise
+ * use this software as long as: there is no monetary profit gained
+ * specifically from the use or reproduction of this software, it is not
+ * sold, rented, traded or otherwise marketed, and this copyright notice is
+ * included prominently in any copy made. 
+ *
+ * The author make no claims as to the fitness or correctness of this software
+ * for any use whatsoever, and it is provided as is. Any use of this software
+ * is at the user's own risk. 
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "cache.h"
+#include "head.h"
+#include "only.h"
+#include "term.h"
+#include "ng.h"
+#include "intrp.h"
+#include "rt-page.h"
+#include "rt-util.h"
+#include "INTERN.h"
+#include "sw.h"
+
+void
+sw_init(argc,argv,tcbufptr)
+int argc;
+char *argv[];
+char **tcbufptr;
+{
+    register int i;
+
+    if (argc >= 2 && strEQ(argv[1],"-c"))
+       checkflag=TRUE;                 /* so we can optimize for -c */
+    interp(*tcbufptr,1024,GLOBINIT);
+    sw_file(tcbufptr,FALSE);
+    if (!use_threads || !*safecpy(*tcbufptr,getenv("TRNINIT"),1024))
+       safecpy(*tcbufptr,getenv("RNINIT"),1024);
+    if (**tcbufptr) {
+       if (**tcbufptr == '/') {
+           sw_file(tcbufptr,TRUE);
+       }
+       else
+           sw_list(*tcbufptr);
+    }
+
+    for (i = 1; i < argc; i++)
+       decode_switch(argv[i]);
+}
+
+void
+sw_file(tcbufptr,bleat)
+char **tcbufptr;
+bool_int bleat;
+{
+    int initfd = open(*tcbufptr,0);
+       
+    if (initfd >= 0) {
+       fstat(initfd,&filestat);
+       if (filestat.st_size > 1024)
+           *tcbufptr = saferealloc(*tcbufptr,(MEM_SIZE)filestat.st_size);
+       if (filestat.st_size) {
+           read(initfd,*tcbufptr,(int)filestat.st_size);
+           (*tcbufptr)[filestat.st_size-1] = '\0';
+                               /* wipe out last newline */
+           sw_list(*tcbufptr);
+       }
+       else
+           **tcbufptr = '\0';
+       close(initfd);
+    }
+    else {
+       if (bleat)
+           printf(cantopen,*tcbufptr) FLUSH;
+       **tcbufptr = '\0';
+    }
+}
+
+/* decode a list of space separated switches */
+
+void
+sw_list(swlist)
+char *swlist;
+{
+    char *tmplist = safemalloc((MEM_SIZE) strlen(swlist) + 2);
+                                       /* semi-automatic string */
+    register char *s, *p, inquote = 0;
+
+    strcpy(tmplist,swlist);
+    p = tmplist;
+    for (s = p;;) {
+       while (isspace(*s)) s++;        /* skip any initial spaces */
+       if (*s != '#') {
+           if (s != p)
+               strcpy(p, s);
+           break;
+       }
+       while (*s && *s++ != '\n') ;    /* skip comments */
+    }
+    while (*p) {                       /* "String, or nothing" */
+       if (!inquote && isspace(*p)) {  /* word delimiter? */
+           *p++ = '\0';                /* chop here */
+           for (s = p;;) {
+               while (isspace(*s)) s++;
+               if (*s != '#') {
+                   if (s != p)
+                       strcpy(p, s);
+                   break;
+               }
+               while (*s && *s++ != '\n') ;
+           }
+       }
+       else if (inquote == *p) {
+           strcpy(p,p+1);              /* delete trailing quote */
+           inquote = 0;                /* no longer quoting */
+       }
+       else if (!inquote && (*p == '"' || *p == '\'')) {
+                                       /* OK, I know when I am not wanted */
+           inquote = *p;               /* remember single or double */
+           strcpy(p,p+1);              /* delete the quote */
+       }                               /* (crude, but effective) */
+       else if (*p == '\\') {          /* quoted something? */
+           if (p[1] == '\n')           /* newline? */
+               strcpy(p,p+2);          /* "I didn't see anything" */
+           else {
+               strcpy(p,p+1);          /* delete the backwhack */
+               p++;                    /* leave the whatever alone */
+           }
+       }
+       else
+           p++;                        /* normal char, leave it alone */
+    }
+    *++p = '\0';                       /* put an extra null on the end */
+    if (inquote)
+       printf("Unmatched %c in switch\n",inquote) FLUSH;
+    for (p = tmplist; *p; /* p += strlen(p)+1 */ ) {
+       decode_switch(p);
+       while (*p++) ;                  /* point at null + 1 */
+    }
+    free(tmplist);                     /* this oughta be in Ada */
+}
+
+/* decode a single switch */
+
+void
+decode_switch(s)
+register char *s;
+{
+    while (isspace(*s))                        /* ignore leading spaces */
+       s++;
+#ifdef DEBUG
+    if (debug)
+       printf("Switch: %s\n",s) FLUSH;
+#endif
+    if (*s != '-' && *s != '+') {      /* newsgroup pattern */
+       setngtodo(s);
+    }
+    else {                             /* normal switch */
+       bool upordown = *s == '-' ? TRUE : FALSE;
+       char tmpbuf[LBUFLEN];
+
+       s++;
+       switch (*s) {
+#ifdef TERMMOD
+       case '=': {
+           char *beg = s+1;
+
+           while (*s && *s != '-' && *s != '+') s++;
+           cpytill(tmpbuf,beg,*s);
+           if (upordown ? strEQ(getenv("TERM"),tmpbuf)
+                        : strNE(getenv("TERM"),tmpbuf) ) {
+               decode_switch(s);
+           }
+           break;
+       }
+#endif
+#ifdef BAUDMOD
+       case '0': case '1': case '2': case '3': case '4':
+       case '5': case '6': case '7': case '8': case '9':
+           if (upordown ? (just_a_sec*10 <= atoi(s))
+                        : (just_a_sec*10 >= atoi(s)) ) {
+               while (isdigit(*s)) s++;
+               decode_switch(s);
+           }
+           break;
+#endif
+       case '/':
+           if (checkflag)
+               break;
+#ifdef SETENV
+           setenv("SAVEDIR",  upordown ? "%p/%c" : "%p" );
+           setenv("SAVENAME", upordown ? "%a"    : "%^C");
+#else
+           notincl("-/");
+#endif
+           break;
+       case 'a':
+           thread_always = upordown;
+           break;
+       case 'A':
+           auto_arrow_macros = upordown;
+           break;
+       case 'b':
+           breadth_first = upordown;
+           break;
+       case 'B':
+           bkgnd_spinner = upordown;
+           break;
+       case 'c':
+           checkflag = upordown;
+           break;
+       case 'C':
+           s++;
+           if (*s == '=') s++;
+           docheckwhen = atoi(s);
+           break;
+       case 'd': {
+           if (checkflag)
+               break;
+           s++;
+           if (*s == '=') s++;
+           if (cwd) {
+               chdir(cwd);
+               free(cwd);
+           }
+           cwd = savestr(s);
+           break;
+       }
+#ifdef DEBUG
+       case 'D':
+           s++;
+           if (*s == '=') s++;
+           if (*s)
+               if (upordown)
+                   debug |= atoi(s);
+               else
+                   debug &= ~atoi(s);
+           else
+               if (upordown)
+                   debug |= 1;
+               else
+                   debug = 0;
+           break;
+#endif
+       case 'e':
+           erase_screen = upordown;
+           break;
+       case 'E':
+#ifdef SETENV
+           s++;
+           if (*s == '=')
+               s++;
+           strcpy(tmpbuf,s);
+           s = index(tmpbuf,'=');
+           if (s) {
+               *s++ = '\0';
+               setenv(tmpbuf,s);
+           }
+           else
+               setenv(tmpbuf,nullstr);
+#else
+           notincl("-E");
+#endif
+           break;
+       case 'f':
+           novice_delays = !upordown;
+           break;
+       case 'F':
+           s++;
+           indstr = savestr(s);
+           break;
+#ifdef INNERSEARCH
+       case 'g':
+           gline = atoi(s+1)-1;
+           break;
+#endif
+#ifdef EDIT_DISTANCE
+       case 'G':
+           fuzzyGet = upordown;
+           break;
+#endif        
+       case 'H':
+       case 'h': {
+           register int len, i;
+           char *t;
+           int flag = (*s == 'h' ? HT_HIDE : HT_MAGIC);
+           
+           if (checkflag)
+               break;
+           s++;
+           len = strlen(s);
+           for (t=s; *t; t++)
+               if (isupper(*t))
+                  *t = tolower(*t);
+           for (i=HEAD_FIRST; i<HEAD_LAST; i++)
+               if (!len || strnEQ(s,htype[i].ht_name,len))
+                   if (upordown)
+                       htype[i].ht_flags |= flag;
+                   else
+                       htype[i].ht_flags &= ~flag;
+           break;
+       }
+       case 'i':
+           s++;
+           if (*s == '=') s++;
+           initlines = atoi(s);
+           initlines_specified = TRUE;
+           break;
+       case 'I':
+           append_unsub = upordown;
+           break;
+       case 'j':
+           dont_filter_control = TRUE;
+           break;
+       case 'l':
+           muck_up_clear = upordown;
+           break;
+       case 'L':
+#ifdef CLEAREOL
+           can_home_clear = upordown;
+#else
+           notincl("-L");
+#endif
+           break;
+       case 'M':
+           mbox_always = upordown;
+           break;
+       case 'm':
+           s++;
+           if (*s == '=') s++;
+           if (!upordown)
+               marking = NOMARKING;
+           else if (*s == 'u')
+               marking = UNDERLINE;
+           else {
+               marking = STANDOUT;
+           }
+           break;
+       case 'N':
+           norm_always = upordown;
+           break;
+#ifdef VERBOSE
+       case 'n':
+           fputs("This isn't readnews.  Don't use -n.\n\n",stdout) FLUSH;
+           break;
+#endif
+       case 'o':
+           s++;
+           if (*s == '=') s++;
+           if (*s <= '9' && *s >= '0') {
+               olden_days = atoi(s);
+               do {
+                   s++;
+               } while (*s <= '9' && *s >= '0');
+           } else
+               olden_days = upordown;
+           break;
+       case 'O':
+           s++;
+           if (*s == '=') s++;
+           if (!*s)
+               break;
+           if (!set_sel_mode(*s)
+            || (*++s && !set_sel_sort(*s))) {
+#ifdef VERBOSE
+               IF(verbose)
+                   printf("\nIgnoring unrecognized -O option: %c\n", *s) FLUSH;
+               ELSE
+#endif
+#ifdef TERSE
+                   printf("\nIgnoring -O with %c\n", *s) FLUSH;
+#endif
+               break;
+           }
+           break;
+       case 'p':
+           auto_select_postings = upordown;
+           break;
+       case 'r':
+           findlast = upordown;
+           break;
+       case 's':
+           s++;
+           if (*s == '=') s++;
+           if (*s) {
+               countdown = atoi(s);
+               suppress_cn = FALSE;
+           }
+           else {
+               if (!upordown)
+                   countdown = 5;
+               suppress_cn = upordown;
+           }
+           break;
+       case 'S':
+#ifdef ARTSEARCH
+           s++;
+           if (*s == '=') s++;
+           if (*s)
+               scanon = atoi(s);
+           else
+               scanon = upordown*3;
+#else
+           notincl("-S");
+#endif
+           break;
+       case 't':
+#ifdef VERBOSE
+#ifdef TERSE
+           verbose = !upordown;
+           if (!verbose)
+               novice_delays = FALSE;
+#else
+           notincl("+t");
+#endif
+#else
+           notincl("+t");
+#endif
+           break;
+       case 'T':
+           typeahead = upordown;
+           break;
+       case 'u':
+           unbroken_subjects = upordown;
+           break;
+       case 'v':
+#ifdef VERIFY
+           verify = upordown;
+#else
+           notincl("-v");
+#endif
+           break;
+       case 'x':
+           s++;
+           if (*s == '=') s++;
+           if (*s <= '9' && *s >= '0') {
+               if ((max_tree_lines = atoi(s)) > 11)
+                   max_tree_lines = 11;
+               do {
+                   s++;
+               } while (*s <= '9' && *s >= '0');
+           } else
+               max_tree_lines = 6;
+           if (*s)
+               strncpy(select_order, s, 3);
+           use_threads = upordown;
+           break;
+       case 'X':
+           s++;
+           if (*s == '=') s++;
+           if (*s <= '9' && *s >= '0') {
+               select_on = atoi(s);
+               do {
+                   s++;
+               } while (*s <= '9' && *s >= '0');
+           } else
+               select_on = upordown;
+           if (*s)
+               end_select = *s++;
+           if (*s)
+               page_select = *s;
+           break;
+       /*
+        * People want a way to avoid checking for new newsgroups on startup.
+        */
+       case 'q':
+               quickstart = upordown;
+               break;
+       default:
+#ifdef VERBOSE
+           IF(verbose)
+               printf("\nIgnoring unrecognized switch: -%c\n", *s) FLUSH;
+           ELSE
+#endif
+#ifdef TERSE
+               printf("\nIgnoring -%c\n", *s) FLUSH;
+#endif
+           break;
+       }
+    }
+}
+
+/* print current switch values */
+
+void
+pr_switches()
+{
+    static char mp[2] = {'+','-'};
+    register int i;
+    
+    fputs("\nCurrent switch settings:\n",stdout);
+    printf("%c/ ", mp[strEQ(getval("SAVEDIR",SAVEDIR),"%p/%c")]);
+    printf("%ca ", mp[thread_always]);
+    printf("%cA ", mp[auto_arrow_macros]);
+    printf("%cb ", mp[breadth_first]);
+    printf("%cB ", mp[bkgnd_spinner]);
+    printf("%cc ", mp[checkflag]);
+    printf("-C%d ", docheckwhen);
+    printf("-d%s ", cwd);
+#ifdef DEBUG
+    if (debug)
+       printf("-D%d ", debug);
+#endif
+    printf("%ce ", mp[erase_screen]);
+    printf("%cf ", mp[!novice_delays]);
+    printf("-F\"%s\" ", indstr);
+#ifdef INNERSEARCH
+    printf("-g%d ", gline);
+#endif
+    printf("%cG", mp[fuzzyGet]);
+    putchar('\n');
+#ifdef VERBOSE
+    if (verbose) {
+       for (i=HEAD_FIRST; i<HEAD_LAST; i++)
+           printf("%ch%s%c",
+               mp[htype[i].ht_flags & HT_HIDE], htype[i].ht_name,
+               (! (i % 5) ? '\n' : ' ') );
+       putchar('\n');
+    }
+#endif
+    printf("-i%d ", initlines);
+    printf("%cI ", mp[append_unsub]);
+    printf("%cj ", mp[dont_filter_control]);
+    printf("%cl ", mp[muck_up_clear]);
+#ifdef CLEAREOL
+    printf("%cL ", mp[can_home_clear]);
+#endif /* CLEAREOL */
+    if (marking)
+       printf("-m%c ",marking==UNDERLINE?'u':'s');
+    else
+       printf("+m ");
+    printf("%cM ", mp[mbox_always]);
+    printf("%cN ", mp[norm_always]);
+    if (olden_days)
+       printf("-o%d ", olden_days);
+    else
+       printf("+o ");
+    printf("%cp ", mp[auto_select_postings]);
+    printf("%cr ", mp[findlast]);
+    if (countdown)
+       printf("-s%d ", countdown);
+    else
+       printf("%cs ", mp[suppress_cn]);
+#ifdef ARTSEARCH
+    if (scanon)
+       printf("-S%d ",scanon);
+    else
+       printf("+S ");
+#endif
+#ifdef VERBOSE
+#ifdef TERSE
+    printf("%ct ", mp[!verbose]);
+#endif
+#endif
+    printf("%cT ", mp[typeahead]);
+    printf("%cu ", mp[unbroken_subjects]);
+#ifdef VERIFY
+    printf("%cv ", mp[verify]);
+#endif
+    if (use_threads)
+       printf("-x%d%s ",max_tree_lines,select_order);
+    else
+       printf("+x ");
+    if (select_on)
+       printf("-X%d%c%c ",select_on,end_select,page_select);
+    else
+       printf("+X ");
+    fputs("\n\n",stdout) FLUSH;
+#ifdef ONLY
+    if (maxngtodo) {
+#ifdef VERBOSE
+       IF(verbose)
+           fputs("Current restriction:",stdout);
+       ELSE
+#endif
+#ifdef TERSE
+           fputs("Only:",stdout);
+#endif
+       for (i=0; i<maxngtodo; i++)
+           printf(" %s",ngtodo[i]);
+       fputs("\n\n",stdout) FLUSH;
+    }
+#ifdef VERBOSE
+    else if (verbose)
+       fputs("No restriction.\n\n",stdout) FLUSH;
+#endif
+#endif
+}
+
+void
+cwd_check()
+{
+    char tmpbuf[LBUFLEN];
+
+    if (!cwd)
+       cwd = savestr(filexp("~/News"));
+    strcpy(tmpbuf,cwd);
+    if (chdir(cwd)) {
+       safecpy(tmpbuf,filexp(cwd),sizeof tmpbuf);
+       if (makedir(tmpbuf,MD_DIR) < 0 || chdir(tmpbuf) < 0) {
+           interp(cmd_buf, (sizeof cmd_buf), "%~/News");
+           if (makedir(cmd_buf,MD_DIR) < 0)
+               strcpy(tmpbuf,homedir);
+           else
+               strcpy(tmpbuf,cmd_buf);
+           chdir(tmpbuf);
+#ifdef VERBOSE
+           IF(verbose)
+               printf("\
+Cannot make directory %s--\n\
+       articles will be saved to %s\n\
+\n\
+",cwd,tmpbuf) FLUSH;
+           ELSE
+#endif
+#ifdef TERSE
+               printf("\
+Can't make %s--\n\
+       using %s\n\
+\n\
+",cwd,tmpbuf) FLUSH;
+#endif
+       }
+    }
+    free(cwd);
+    getwd(tmpbuf);
+    if (eaccess(tmpbuf,2)) {
+#ifdef VERBOSE
+       IF(verbose)
+           printf("\
+Current directory %s is not writeable--\n\
+       articles will be saved to home directory\n\n\
+",tmpbuf) FLUSH;
+       ELSE
+#endif
+#ifdef TERSE
+           printf("%s not writeable--using ~\n\n",tmpbuf) FLUSH;
+#endif
+       strcpy(tmpbuf,homedir);
+    }
+    cwd = savestr(tmpbuf);
+}