From: CSRG Date: Fri, 4 Jun 1993 12:17:18 +0000 (-0800) Subject: BSD 4_4 development X-Git-Tag: BSD-4_4~354 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/0dbe5c9b19762df95e3ddc78bcab0df2d2e75853 BSD 4_4 development 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 --- diff --git a/usr/src/contrib/news/trn3/Pnews.SH b/usr/src/contrib/news/trn3/Pnews.SH new file mode 100755 index 0000000000..90ce994e6b --- /dev/null +++ b/usr/src/contrib/news/trn3/Pnews.SH @@ -0,0 +1,770 @@ +case $CONFIG in + '') . ./config.sh ;; +esac +echo "Extracting Pnews (with variable substitutions)" +$spitshell >Pnews <>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 $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$' < (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 /dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"` + fi + if $test "$fullname" = ""; then + fullname=`$sed $tmpart < 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 index 0000000000..4b8693ea7a --- /dev/null +++ b/usr/src/contrib/news/trn3/art.c @@ -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 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 index 0000000000..45f3679e8a --- /dev/null +++ b/usr/src/contrib/news/trn3/artcheck.c @@ -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
+*/ + +#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
\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 index 0000000000..3fd69eae8b --- /dev/null +++ b/usr/src/contrib/news/trn3/help.c @@ -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 .+).\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 index 0000000000..0fd5defe3e --- /dev/null +++ b/usr/src/contrib/news/trn3/intrp.c @@ -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 +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 index 0000000000..f3140c9bdf --- /dev/null +++ b/usr/src/contrib/news/trn3/ngdata.c @@ -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 index 0000000000..800ae45c74 --- /dev/null +++ b/usr/src/contrib/news/trn3/patchlevel.h @@ -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 index 0000000000..55c2692b6f --- /dev/null +++ b/usr/src/contrib/news/trn3/rt-process.c @@ -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 index 0000000000..cd3fa20bb1 --- /dev/null +++ b/usr/src/contrib/news/trn3/sw.c @@ -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= '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