trn-4.0-test77/Configure0000755000000000000000000043332511437640112013671 0ustar rootroot#! /bin/sh # # If these # comments don't work, trim them. Don't worry about any other # shell scripts, Configure will trim # comments from them for you. # # (If you are trying to port this package to a machine without sh, # I would suggest you have a look at the prototypical config_h.SH file # and edit it to reflect your system. Some packages may include samples # of config.h for certain machines, so you might look for one of those.) # # Yes, you may rip this off to use in other distribution packages. This # script belongs to the public domain and cannot be copyrighted. # # (Note: this Configure script was generated automatically. Rather than # working with this copy of Configure, you may wish to get metaconfig. # The dist-3.0 package (which contains metaconfig) was posted in # comp.sources.misc and is available on CPAN under authors/id/RAM so # you may fetch it yourself from your nearest archive site.) # # $Id: Head.U,v 3.0.1.9 1997/02/28 15:02:09 ram Exp $ # # Generated on Sun Aug 10 11:58:23 PDT 2003 [metaconfig 3.0 PL70] # (with additional metaconfig patches by trn-workers@lists.sourceforge.net) cat >/tmp/c1$$ </tmp/c2$$ </dev/null` test "$me" || me=$0 ;; esac : Proper separator for the PATH environment variable p_=: : On OS/2 this directory should exist if this is not floppy only system :-] if test -d c:/. ; then if test -n "$OS2_SHELL"; then p_=\; PATH=`cmd /c "echo %PATH%" | tr '\\\\' / ` OS2_SHELL=`cmd /c "echo %OS2_SHELL%" | tr '\\\\' / | tr '[A-Z]' '[a-z]'` elif test -n "$DJGPP"; then p_=\; fi fi : Proper PATH setting paths='/bin /usr/bin /usr/local/bin /usr/ucb /usr/local /usr/lbin' paths="$paths /opt/bin /opt/local/bin /opt/local /opt/lbin" paths="$paths /usr/5bin /etc /usr/gnu/bin /usr/new /usr/new/bin /usr/nbin" paths="$paths /opt/gnu/bin /opt/new /opt/new/bin /opt/nbin" paths="$paths /sys5.3/bin /sys5.3/usr/bin /bsd4.3/bin /bsd4.3/usr/ucb" paths="$paths /bsd4.3/usr/bin /usr/bsd /bsd43/bin /usr/ccs/bin" paths="$paths /etc /usr/lib /usr/ucblib /lib /usr/ccs/lib" paths="$paths /sbin /usr/sbin /usr/libexec" for p in $paths do case "$p_$PATH$p_" in *$p_$p$p_*) ;; *) test -d $p && PATH=$PATH$p_$p ;; esac done PATH=.$p_$PATH export PATH : shall we be using ksh? inksh='' needksh='' avoidksh='' newsh=/bin/ksh changesh='' if (PATH=.; alias -x) >/dev/null 2>&1; then inksh=true fi if test -f /hp-ux -a -f /bin/ksh; then needksh='to avoid sh bug in "here document" expansion' fi if test -d /usr/lpp -a -f /usr/bin/bsh -a -f /usr/bin/uname; then if test X`/usr/bin/uname -v` = X4; then avoidksh="to avoid AIX 4's /bin/sh" newsh=/usr/bin/bsh fi fi case "$inksh/$needksh" in /[a-z]*) ENV='' changesh=true reason="$needksh" ;; esac case "$inksh/$avoidksh" in true/[a-z]*) changesh=true reason="$avoidksh" ;; esac case "$inksh/$needksh-$avoidksh-" in true/--) cat <&2 $me: Fatal Error: I can't find a Bourne Shell anywhere. Usually it's in /bin/sh. How did you even get this far? Please contact me (Trn Workers) at trn-workers@lists.sourceforge.net and we'll try to straighten this all out. EOM exit 1 ;; esac : see if sh knows # comments if `$sh -c '#' >/dev/null 2>&1`; then shsharp=true spitshell=cat xcat=/bin/cat test -f $xcat || xcat=/usr/bin/cat echo "#!$xcat" >try $eunicefix try chmod +x try ./try > today if test -s today; then sharpbang='#!' else echo "#! $xcat" > try $eunicefix try chmod +x try ./try > today if test -s today; then sharpbang='#! ' else sharpbang=': use ' fi fi else echo " " echo "Your $sh doesn't grok # comments--I will strip them later on." shsharp=false cd .. echo "exec grep -v '^[ ]*#'" >spitshell chmod +x spitshell $eunicefix spitshell spitshell=`pwd`/spitshell cd UU echo "I presume that if # doesn't work, #! won't work either!" sharpbang=': use ' fi rm -f try today : figure out how to guarantee sh startup case "$startsh" in '') startsh=${sharpbang}${sh} ;; *) esac cat >try <options.awk <<'EOF' BEGIN { optstr = "dD:eEf:hKOrsSU:V"; # getopt-style specification len = length(optstr); for (i = 1; i <= len; i++) { c = substr(optstr, i, 1); if (i < len) a = substr(optstr, i + 1, 1); else a = ""; if (a == ":") { arg[c] = 1; i++; } opt[c] = 1; } } { expect = 0; str = $0; if (substr(str, 1, 1) != "-") { printf("'%s'\n", str); next; } len = length($0); for (i = 2; i <= len; i++) { c = substr(str, i, 1); if (!opt[c]) { printf("-%s\n", substr(str, i)); next; } printf("-%s\n", c); if (arg[c]) { if (i < len) printf("'%s'\n", substr(str, i + 1)); else expect = 1; next; } } } END { if (expect) print "?"; } EOF : process the command line options set X `for arg in "$@"; do echo "X$arg"; done | sed -e s/X// | awk -f options.awk` eval "set $*" shift rm -f options.awk : set up default values fastread='' reuseval=false config_sh='' alldone='' error='' silent='' extractsh='' override='' knowitall='' rm -f optdef.sh cat >optdef.sh <&2 error=true fi cd UU shift;; -h) shift; error=true;; -r) shift; reuseval=true;; -s) shift; silent=true; realsilent=true;; -E) shift; alldone=exit;; -K) shift; knowitall=true;; -O) shift; override=true;; -S) shift; silent=true; extractsh=true;; -D) shift case "$1" in *=) echo "$me: use '-U symbol=', not '-D symbol='." >&2 echo "$me: ignoring -D $1" >&2 ;; *=*) echo "$1" | \ sed -e "s/'/'\"'\"'/g" -e "s/=\(.*\)/='\1'/" >> optdef.sh;; *) echo "$1='define'" >> optdef.sh;; esac shift ;; -U) shift case "$1" in *=) echo "$1" >> optdef.sh;; *=*) echo "$me: use '-D symbol=val', not '-U symbol=val'." >&2 echo "$me: ignoring -U $1" >&2 ;; *) echo "$1='undef'" >> optdef.sh;; esac shift ;; -V) echo "$me generated by metaconfig 3.0 PL70." >&2 exit 0;; --) break;; -*) echo "$me: unknown option $1" >&2; shift; error=true;; *) break;; esac done case "$error" in true) cat >&2 <&1 case "$silent" in true) exec 1>/dev/null;; esac : run the defines and the undefines, if any, but leave the file out there... touch optdef.sh . ./optdef.sh : set package name package=trn first=`echo $package | sed -e 's/^\(.\).*/\1/'` last=`echo $package | sed -e 's/^.\(.*\)/\1/'` case "`echo AbyZ | tr '[:lower:]' '[:upper:]' 2>/dev/null`" in ABYZ) spackage=`echo $first | tr '[:lower:]' '[:upper:]'`$last;; *) spackage=`echo $first | tr '[a-z]' '[A-Z]'`$last;; esac : Some greps do not return status, grrr. echo "grimblepritz" >grimble if grep blurfldyick grimble >/dev/null 2>&1 ; then contains=contains elif grep grimblepritz grimble >/dev/null 2>&1 ; then contains=grep else contains=contains fi rm -f grimble : the following should work in any shell case "$contains" in contains*) echo " " echo "AGH! Grep doesn't return a status. Attempting remedial action." cat >contains <<'EOSS' grep "$1" "$2" >.greptmp && cat .greptmp && test -s .greptmp EOSS chmod +x contains esac : Find the path to the source tree case "$src" in '') case "$0" in */*) src=`echo $0 | sed -e 's%/[^/][^/]*$%%'`;; *) src='.';; esac;; esac case "$src" in '') src=/ rsrc=/ ;; /*) rsrc="$src";; *) rsrc="../$src";; esac if test -f $rsrc/Configure && \ $contains "^package=$package$" $rsrc/Configure >/dev/null 2>&1 then : found it, so we are ok. else rsrc='' for src in . .. ../.. ../../.. ../../../..; do if test -f ../$src/Configure && \ $contains "^package=$package$" ../$src/Configure >/dev/null 2>&1 then rsrc=../$src break fi done fi case "$rsrc" in '') cat <&4 Sorry, I can't seem to locate the source dir for $package. Please start Configure with an explicit path -- i.e. /some/path/Configure. EOM exit 1 ;; ../.) rsrc='..';; *) echo " " echo "Sources for $package found in \"$src\"." >&4 ;; esac : script used to extract .SH files with variable substitutions cat >extract <<'EOS' CONFIG=true echo "Doing variable substitutions on .SH files..." set makedir.SH `awk '$1 ~ /.SH$/ {print $1}' $src/MANIFEST | egrep -v 'newsnews|makedir'` for file in $*; do . $src/$file done EOS : extract files and exit if asked to do so case "$extractsh" in true) case "$realsilent" in true) ;; *) exec 1>&4;; esac case "$config_sh" in '') config_sh='config.sh';; esac echo " " echo "Fetching answers from $config_sh..." cd .. . $config_sh test "$override" && . ./optdef.sh echo " " . UU/extract rm -rf UU echo "Done." exit 0 ;; esac : Eunice requires " " instead of "", can you believe it echo " " : Here we go... echo "Beginning of configuration questions for $package." trap 'echo " "; test -d ../UU && rm -rf X $rmlist; exit 1' 1 2 3 15 : first determine how to suppress newline on echo command echo " " echo "Checking echo to see how to suppress newlines..." (echo "hi there\c" ; echo " ") >.echotmp if $contains c .echotmp >/dev/null 2>&1 ; then echo "...using -n." n='-n' c='' else cat <<'EOM' ...using \c EOM n='' c='\c' fi echo $n "The star should be here-->$c" echo '*' rm -f .echotmp : Now test for existence of everything in MANIFEST echo " " if test -f $rsrc/MANIFEST; then echo "First let's make sure your kit is complete. Checking..." >&4 awk '$1 !~ /PACK[A-Z]+/ {print $1}' $rsrc/MANIFEST | split -l50 rm -f missing tmppwd=`pwd` for filelist in x??; do (cd $rsrc; ls `cat $tmppwd/$filelist` >/dev/null 2>>$tmppwd/missing) done if test -s missing; then cat missing >&4 cat >&4 <<'EOM' THIS PACKAGE SEEMS TO BE INCOMPLETE. You have the option of continuing the configuration process, despite the distinct possibility that your kit is damaged, by typing 'y'es. If you do, don't blame me if something goes wrong. I advise you to type 'n'o and contact the author (trn-workers@lists.sourceforge.net). EOM echo $n "Continue? [n] $c" >&4 read ans case "$ans" in y*) echo "Continuing..." >&4 rm -f missing ;; *) echo "ABORTING..." >&4 kill $$ ;; esac else echo "Looks good..." fi else echo "There is no MANIFEST file. I hope your kit is complete !" fi rm -f missing x?? echo " " : Find the appropriate value for a newline for tr if test -n "$DJGPP"; then trnl='\012' fi if test X"$trnl" = X; then case "`echo foo|tr '\n' x 2>/dev/null`" in foox) trnl='\n' ;; esac fi if test X"$trnl" = X; then case "`echo foo|tr '\012' x 2>/dev/null`" in foox) trnl='\012' ;; esac fi if test X"$trnl" = X; then cat <&2 $me: Fatal Error: cannot figure out how to translate newlines with 'tr'. EOM exit 1 fi : compute the number of columns on the terminal for proper question formatting case "$COLUMNS" in '') COLUMNS='80';; esac : set up the echo used in my read myecho="case \"\$xxxm\" in '') echo $n \"\$rp $c\" >&4;; *) case \"\$rp\" in '') echo $n \"[\$xxxm] $c\";; *) if test \`echo \"\$rp [\$xxxm] \" | wc -c\` -ge $COLUMNS; then echo \"\$rp\" >&4 echo $n \"[\$xxxm] $c\" >&4 else echo $n \"\$rp [\$xxxm] $c\" >&4 fi ;; esac;; esac" : now set up to do reads with possible shell escape and default assignment cat <myread $startsh xxxm=\$dflt $myecho ans='!' case "\$fastread" in yes) case "\$dflt" in '') ;; *) ans=''; case "\$silent-\$rp" in true-) ;; *) echo " " >&4;; esac;; esac;; *) case "\$silent" in true) case "\$rp" in '') ans='';; esac;; esac;; esac while expr "X\$ans" : "X!" >/dev/null; do read answ set x \$xxxm shift aok=''; eval "ans=\\"\$answ\\"" && aok=y case "\$answ" in "!") sh 1>&4 echo " " $myecho ;; !*) set x \`expr "X\$ans" : "X!\(.*\)\$"\` shift sh 1>&4 -c "\$*" echo " " $myecho ;; "\$ans") case "\$ans" in \\&*) set x \`expr "X\$ans" : "X&\(.*\)\$"\` shift case "\$1" in -d) fastread=yes echo "(OK, I'll run with -d after this question.)" >&4 ;; -*) echo "*** Sorry, \$1 not supported yet." >&4 ;; esac $myecho ans=! ;; esac;; *) case "\$aok" in y) echo "*** Substitution done -- please confirm." xxxm="\$ans" ans=\`echo $n "\$ans$c" | tr '$trnl' ' '\` xxxm="\$ans" ans=! ;; *) echo "*** Error -- try again." ans=! ;; esac $myecho ;; esac case "\$ans\$xxxm\$nostick" in '') ans=! $myecho ;; esac done case "\$ans" in '') ans="\$xxxm";; esac EOSC : create .config dir to save info across Configure sessions test -d ../.config || mkdir ../.config cat >../.config/README </dev/null` case "$user" in '') user=`whoami 2>&1`;; esac if $contains "^$user\$" ../.config/instruct >/dev/null 2>&1; then firsttime=false echo " " rp='Would you like to see the instructions?' dflt=n . ./myread case "$ans" in [yY]*) ;; *) needman=false;; esac fi if $needman; then cat <>../.config/instruct;; esac fi : find out where common programs are echo " " echo "Locating common programs..." >&4 cat <loc $startsh case \$# in 0) exit 1;; esac thing=\$1 shift dflt=\$1 shift for dir in \$*; do case "\$thing" in .) if test -d \$dir/\$thing; then echo \$dir exit 0 fi ;; *) for thisthing in \$dir/\$thing; do : just loop through to pick last item done if test -f \$thisthing; then echo \$thisthing exit 0 elif test -f \$dir/\$thing.exe; then if test -n "$DJGPP"; then echo \$dir/\$thing.exe else : on Eunice apparently echo \$dir/\$thing fi exit 0 fi ;; esac done echo \$dflt exit 1 EOSC chmod +x loc $eunicefix loc loclist=" awk cat comm cp echo expr grep mkdir mv rm sed sleep sort tail touch tr uniq " trylist=" Mcc ar basename bash bison byacc cpp csh date diff ed egrep inews ispell ksh less ln mail more nm nroff perl pg pgp rmail sendmail smail spell test uname uuname vi who " pth=`echo $PATH | sed -e "s/$p_/ /g"` pth="$pth /lib /usr/lib" for file in $loclist; do eval xxx=\$$file case "$xxx" in /*|?:[\\/]*) if test -f "$xxx"; then : ok else echo "WARNING: no $xxx -- ignoring your setting for $file." >&4 xxx=`./loc $file $file $pth` fi ;; '') xxx=`./loc $file $file $pth`;; *) xxx=`./loc $xxx $xxx $pth`;; esac eval $file=$xxx eval _$file=$xxx case "$xxx" in /*) echo $file is in $xxx. ;; ?:[\\/]*) echo $file is in $xxx. ;; *) echo "I don't know where '$file' is, and my life depends on it." >&4 echo "Go find a public domain implementation or fix your PATH setting!" >&4 exit 1 ;; esac done echo " " echo "Don't worry if any of the following aren't found..." say=offhand for file in $trylist; do eval xxx=\$$file case "$xxx" in /*|?:[\\/]*) if test -f "$xxx"; then : ok else echo "WARNING: no $xxx -- ignoring your setting for $file." >&4 xxx=`./loc $file $file $pth` fi ;; '') xxx=`./loc $file $file $pth`;; *) xxx=`./loc $xxx $xxx $pth`;; esac eval $file=$xxx eval _$file=$xxx case "$xxx" in /*) echo $file is in $xxx. ;; ?:[\\/]*) echo $file is in $xxx. ;; *) echo "I don't see $file out there, $say." say=either ;; esac done case "$egrep" in egrep) echo "Substituting grep for egrep." egrep=$grep ;; esac case "$ln" in ln) echo "Substituting cp for ln." ln=$cp ;; esac case "$test" in test) echo "Hopefully test is built into your sh." ;; *) if `sh -c "PATH= test true" >/dev/null 2>&1`; then echo "Using the test built into your sh." test=test _test=test fi ;; esac case "$echo" in echo) echo "Hopefully echo is built into your sh." ;; '') ;; *) echo " " echo "Checking compatibility between $echo and builtin echo (if any)..." >&4 $echo $n "hi there$c" >foo1 echo $n "hi there$c" >foo2 if cmp foo1 foo2 >/dev/null 2>&1; then echo "They are compatible. In fact, they may be identical." else case "$n" in '-n') n='' c='\c';; *) n='-n' c='';; esac cat <$c" $echo "*" fi $rm -f foo1 foo2 ;; esac : determine whether symbolic links are supported echo " " $touch blurfl if $ln -s blurfl sym > /dev/null 2>&1 ; then echo "Symbolic links are supported." >&4 lns="$ln -s" else echo "Symbolic links are NOT supported." >&4 lns="$ln" fi $rm -f blurfl sym : see whether [:lower:] and [:upper:] are supported character classes echo " " case "`echo AbyZ | $tr '[:lower:]' '[:upper:]' 2>/dev/null`" in ABYZ) echo "Good, your tr supports [:lower:] and [:upper:] to convert case." >&4 up='[:upper:]' low='[:lower:]' ;; *) # There is a discontinuity in EBCDIC between 'I' and 'J' # (0xc9 and 0xd1), therefore that is a nice testing point. if test "X$up" = X -o "X$low" = X; then case "`echo IJ | tr '[I-J]' '[i-j]' 2>/dev/null`" in ij) up='[A-Z]' low='[a-z]' ;; esac fi if test "X$up" = X -o "X$low" = X; then case "`echo IJ | tr I-J i-j 2>/dev/null`" in ij) up='A-Z' low='a-z' ;; esac fi if test "X$up" = X -o "X$low" = X; then case "`echo IJ | od -x 2>/dev/null`" in *C9D1*|*c9d1*) echo "Hey, this might be EBCDIC." >&4 if test "X$up" = X -o "X$low" = X; then case "`echo IJ | tr '[A-IJ-RS-Z]' '[a-ij-rs-z]' 2>/dev/null`" in ij) up='[A-IJ-RS-Z]' low='[a-ij-rs-z]' ;; esac fi if test "X$up" = X -o "X$low" = X; then case "`echo IJ | tr A-IJ-RS-Z a-ij-rs-z 2>/dev/null`" in ij) up='A-IJ-RS-Z' low='a-ij-rs-z' ;; esac fi ;; esac fi esac case "`echo IJ | tr \"$up\" \"$low\" 2>/dev/null`" in ij) echo "Using $up and $low to convert case." >&4 ;; *) echo "I don't know how to translate letters from upper to lower case." >&4 echo "Your tr is not acting any way I know of." >&4 exit 1 ;; esac : set up the translation script tr, must be called with ./tr of course cat >tr </dev/null || hostname) 2>&1` # tr '[A-Z]' '[a-z]' would not work in EBCDIC # because the A-Z/a-z are not consecutive. myuname=`echo $myuname | $sed -e 's/^[^=]*=//' -e 's/\///g' | \ ./tr '[A-Z]' '[a-z]' | tr $trnl ' '` newmyuname="$myuname" dflt=n case "$knowitall" in '') if test -f ../config.sh; then if $contains myuname= ../config.sh >/dev/null 2>&1; then eval "`grep myuname= ../config.sh`" fi if test "X$myuname" = "X$newmyuname"; then dflt=y fi fi ;; *) dflt=y;; esac : Get old answers from old config file if Configure was run on the : same system, otherwise use the hints. hint=default cd .. if test -f config.sh; then echo " " rp="I see a config.sh file. Shall I use it to set the defaults?" . UU/myread case "$ans" in n*|N*) echo "OK, I'll ignore it."; mv config.sh config.sh.old;; *) echo "Fetching default answers from your old config.sh file..." >&4 tmp_n="$n" tmp_c="$c" tmp_sh="$sh" . ./config.sh cp config.sh UU n="$tmp_n" c="$tmp_c" : Older versions did not always set $sh. Catch re-use of such : an old config.sh. case "$sh" in '') sh="$tmp_sh" ;; esac hint=previous ;; esac fi if test ! -f config.sh; then $cat <&4 dflt='' : Half the following guesses are probably wrong... If you have better : tests or hints, please send them to trn-workers@lists.sourceforge.net : The metaconfig authors would also appreciate a copy... $test -f /irix && osname=irix $test -f /xenix && osname=sco_xenix $test -f /dynix && osname=dynix $test -f /dnix && osname=dnix $test -f /lynx.os && osname=lynxos $test -f /unicos && osname=unicos && osvers=`$uname -r` $test -f /unicosmk.ar && osname=unicosmk && osvers=`$uname -r` $test -f /bin/mips && /bin/mips && osname=mips $test -d /NextApps && set X `hostinfo | grep 'NeXT Mach.*:' | \ $sed -e 's/://' -e 's/\./_/'` && osname=next && osvers=$4 $test -d /usr/apollo/bin && osname=apollo $test -f /etc/saf/_sactab && osname=svr4 $test -d /usr/include/minix && osname=minix if $test -d /MachTen; then osname=machten if $test -x /sbin/version; then osvers=`/sbin/version | $awk '{print $2}' | $sed -e 's/[A-Za-z]$//'` elif $test -x /usr/etc/version; then osvers=`/usr/etc/version | $awk '{print $2}' | $sed -e 's/[A-Za-z]$//'` else osvers="$2.$3" fi fi if $test -f $uname; then set X $myuname shift case "$5" in fps*) osname=fps ;; mips*) case "$4" in umips) osname=umips ;; *) osname=mips ;; esac;; [23]100) osname=mips ;; next*) osname=next ;; i386*) if $test -f /etc/kconfig; then osname=isc if test "$lns" = "ln -s"; then osvers=4 elif $contains _SYSV3 /usr/include/stdio.h > /dev/null 2>&1 ; then osvers=3 elif $contains _POSIX_SOURCE /usr/include/stdio.h > /dev/null 2>&1 ; then osvers=2 fi fi ;; pc*) if test -n "$DJGPP"; then osname=dos osvers=djgpp fi ;; esac case "$1" in aix) osname=aix tmp=`( (oslevel) 2>/dev/null || echo "not found") 2>&1` case "$tmp" in 'not found') osvers="$4"."$3" ;; '<3240'|'<>3240') osvers=3.2.0 ;; '=3240'|'>3240'|'<3250'|'<>3250') osvers=3.2.4 ;; '=3250'|'>3250') osvers=3.2.5 ;; *) osvers=$tmp;; esac ;; *dc.osx) osname=dcosx osvers="$3" ;; dnix) osname=dnix osvers="$3" ;; domainos) osname=apollo osvers="$3" ;; dgux) osname=dgux osvers="$3" ;; dynixptx*) osname=dynixptx osvers="$3" ;; freebsd) osname=freebsd osvers="$3" ;; genix) osname=genix ;; hp*) osname=hpux osvers=`echo "$3" | $sed 's,.*\.\([0-9]*\.[0-9]*\),\1,'` ;; irix*) osname=irix case "$3" in 4*) osvers=4 ;; 5*) osvers=5 ;; *) osvers="$3" ;; esac ;; linux) osname=linux case "$3" in 1*) osvers=1 ;; *) osvers="$3" ;; esac ;; MiNT) osname=mint ;; netbsd*) osname=netbsd osvers="$3" ;; news-os) osvers="$3" case "$3" in 4*) osname=newsos4 ;; *) osname=newsos ;; esac ;; bsd386) osname=bsd386 osvers=`$uname -r` ;; powerux | power_ux | powermax_os | powermaxos | \ powerunix | power_unix) osname=powerux osvers="$3" ;; next*) osname=next ;; solaris) osname=solaris case "$3" in 5*) osvers=`echo $3 | $sed 's/^5/2/g'` ;; *) osvers="$3" ;; esac ;; sunos) osname=sunos case "$3" in 5*) osname=solaris osvers=`echo $3 | $sed 's/^5/2/g'` ;; *) osvers="$3" ;; esac ;; titanos) osname=titanos case "$3" in 1*) osvers=1 ;; 2*) osvers=2 ;; 3*) osvers=3 ;; 4*) osvers=4 ;; *) osvers="$3" ;; esac ;; ultrix) osname=ultrix osvers="$3" ;; osf1|mls+) case "$5" in alpha) osname=dec_osf1 osvers=`echo "$3" | sed 's/^[xvt]//'` ;; hp*) osname=hp_osf1 ;; mips) osname=mips_osf1 ;; esac ;; uts) osname=uts osvers="$3" ;; qnx) osname=qnx osvers="$4" ;; $2) case "$osname" in *isc*) ;; *freebsd*) ;; svr*) : svr4.x or possibly later case "svr$3" in ${osname}*) osname=svr$3 osvers=$4 ;; esac case "$osname" in svr4.0) : Check for ESIX if test -f /stand/boot ; then eval `grep '^INITPROG=[a-z/0-9]*$' /stand/boot` if test -n "$INITPROG" -a -f "$INITPROG"; then isesix=`strings -a $INITPROG|grep 'ESIX SYSTEM V/386 Release 4.0'` if test -n "$isesix"; then osname=esix4 fi fi fi ;; esac ;; *) if test -f /etc/systemid; then osname=sco set `echo $3 | $sed 's/\./ /g'` $4 if $test -f $src/hints/sco_$1_$2_$3.sh; then osvers=$1.$2.$3 elif $test -f $src/hints/sco_$1_$2.sh; then osvers=$1.$2 elif $test -f $src/hints/sco_$1.sh; then osvers=$1 fi else case "$osname" in '') : Still unknown. Probably a generic Sys V. osname="sysv" osvers="$3" ;; esac fi ;; esac ;; *) case "$osname" in '') : Still unknown. Probably a generic BSD. osname="$1" osvers="$3" ;; esac ;; esac else if test -f /vmunix -a -f $src/hints/news_os.sh; then (what /vmunix | UU/tr '[A-Z]' '[a-z]') > UU/kernel.what 2>&1 if $contains news-os UU/kernel.what >/dev/null 2>&1; then osname=news_os fi $rm -f UU/kernel.what elif test -d c:/.; then set X $myuname osname=os2 osvers="$5" fi fi : Now look for a hint file osname_osvers, unless one has been : specified already. case "$hintfile" in ''|' ') file=`echo "${osname}_${osvers}" | $sed -e 's@\.@_@g' -e 's@_$@@'` : Also try without trailing minor version numbers. xfile=`echo $file | $sed -e 's@_[^_]*$@@'` xxfile=`echo $xfile | $sed -e 's@_[^_]*$@@'` xxxfile=`echo $xxfile | $sed -e 's@_[^_]*$@@'` xxxxfile=`echo $xxxfile | $sed -e 's@_[^_]*$@@'` case "$file" in '') dflt=none ;; *) case "$osvers" in '') dflt=$file ;; *) if $test -f $src/hints/$file.sh ; then dflt=$file elif $test -f $src/hints/$xfile.sh ; then dflt=$xfile elif $test -f $src/hints/$xxfile.sh ; then dflt=$xxfile elif $test -f $src/hints/$xxxfile.sh ; then dflt=$xxxfile elif $test -f $src/hints/$xxxxfile.sh ; then dflt=$xxxxfile elif $test -f "$src/hints/${osname}.sh" ; then dflt="${osname}" else dflt=none fi ;; esac ;; esac if $test -f Policy.sh ; then case "$dflt" in *Policy*) ;; none) dflt="Policy" ;; *) dflt="Policy $dflt" ;; esac fi ;; *) dflt=`echo $hintfile | $sed 's/\.sh$//'` ;; esac if $test -f Policy.sh ; then $cat <> UU/config.sh elif $test -f $src/hints/$file.sh; then . $src/hints/$file.sh $cat $src/hints/$file.sh >> UU/config.sh elif $test X$tans = X -o X$tans = Xnone ; then : nothing else : Give one chance to correct a possible typo. echo "$file.sh does not exist" dflt=$file rp="hint to use instead?" . UU/myread for file in $ans; do if $test -f "$src/hints/$file.sh"; then . $src/hints/$file.sh $cat $src/hints/$file.sh >> UU/config.sh elif $test X$ans = X -o X$ans = Xnone ; then : nothing else echo "$file.sh does not exist -- ignored." fi done fi done hint=recommended : Remember our hint file for later. if $test -f "$src/hints/$file.sh" ; then hintfile="$file" else hintfile='' fi fi cd UU ;; *) echo " " echo "Fetching default answers from $config_sh..." >&4 tmp_n="$n" tmp_c="$c" cd .. cp $config_sh config.sh 2>/dev/null chmod +w config.sh . ./config.sh cd UU cp ../config.sh . n="$tmp_n" c="$tmp_c" hint=previous ;; esac test "$override" && . ./optdef.sh myuname="$newmyuname" : Restore computed paths for file in $loclist $trylist; do eval $file="\$_$file" done cat << EOM Configure uses the operating system name and version to set some defaults. The default value is probably right if the name rings a bell. Otherwise, since spelling matters for me, either accept the default or answer "none" to leave it blank. EOM case "$osname" in ''|' ') case "$hintfile" in ''|' '|none) dflt=none ;; *) dflt=`echo $hintfile | $sed -e 's/\.sh$//' -e 's/_.*$//'` ;; esac ;; *) dflt="$osname" ;; esac rp="Operating system name?" . ./myread case "$ans" in none) osname='' ;; *) osname=`echo "$ans" | $sed -e 's/[ ][ ]*/_/g' | ./tr '[A-Z]' '[a-z]'`;; esac : who configured the system cf_time=`LC_ALL=C; export LC_ALL; $date 2>&1` cf_by=`(logname) 2>/dev/null` case "$cf_by" in "") cf_by=`(whoami) 2>/dev/null` case "$cf_by" in "") cf_by=unknown ;; esac ;; esac : set up shell script to do ~ expansion (CONFIG=true ; . $rsrc/filexp.SH) >/dev/null : check if NNTP is to be used case "$d_nntp" in "$undef") dflt="local";; *) case "$d_local" in "$undef") dflt="nntp";; *) dflt="both";; esac esac $cat < .falseactive case "$d_local" in "$define") case "$servername" in '') dflt="local";; *) dflt="$servername";; esac $cat <getfile $startsh EOS cat <<'EOSC' >>getfile tilde='' fullpath='' already='' skip='' none_ok='' exp_file='' nopath_ok='' orig_rp="$rp" orig_dflt="$dflt" case "$fn" in *\(*) expr $fn : '.*(\(.*\)).*' | tr ',' $trnl >getfile.ok fn=`echo $fn | sed 's/(.*)//'` ;; esac case "$fn" in *:*) loc_file=`expr $fn : '.*:\(.*\)'` fn=`expr $fn : '\(.*\):.*'` ;; esac case "$fn" in *~*) tilde=true;; esac case "$fn" in */*) fullpath=true;; esac case "$fn" in *+*) skip=true;; esac case "$fn" in *n*) none_ok=true;; esac case "$fn" in *e*) exp_file=true;; esac case "$fn" in *p*) nopath_ok=true;; esac case "$fn" in *f*) type='File';; *d*) type='Directory';; *l*) type='Locate';; esac what="$type" case "$what" in Locate) what='File';; esac case "$exp_file" in '') case "$d_portable" in "$define") ;; *) exp_file=true;; esac ;; esac cd .. while test "$type"; do redo='' rp="$orig_rp" dflt="$orig_dflt" case "$tilde" in true) rp="$rp (~name ok)";; esac . UU/myread if test -f UU/getfile.ok && \ $contains "^$ans\$" UU/getfile.ok >/dev/null 2>&1 then value="$ans" ansexp="$ans" break fi case "$ans" in none) value='' ansexp='' case "$none_ok" in true) type='';; esac ;; *) case "$tilde" in '') value="$ans" ansexp="$ans";; *) value=`UU/filexp $ans` case $? in 0) if test "$ans" != "$value"; then echo "(That expands to $value on this system.)" fi ;; *) value="$ans";; esac ansexp="$value" case "$exp_file" in '') value="$ans";; esac ;; esac case "$fullpath" in true) case "$ansexp" in /*) value="$ansexp" ;; *) redo=true case "$already" in true) echo "I shall only accept a full path name, as in /bin/ls." >&4 echo "Use a ! shell escape if you wish to check pathnames." >&4 ;; *) echo "Please give a full path name, starting with slash." >&4 case "$tilde" in true) echo "Note that using ~name is ok provided it expands well." >&4 already=true ;; esac esac ;; esac ;; esac case "$redo" in '') case "$type" in File) if test -f "$ansexp"; then type='' elif test -r "$ansexp" || (test -h "$ansexp") >/dev/null 2>&1 then echo "($value is not a plain file, but that's ok.)" type='' fi ;; Directory) if test -d "$ansexp"; then type='' fi ;; Locate) if test -d "$ansexp"; then echo "(Looking for $loc_file in directory $value.)" value="$value/$loc_file" ansexp="$ansexp/$loc_file" fi if test -f "$ansexp"; then type='' fi case "$nopath_ok" in true) case "$value" in */*) ;; *) echo "Assuming $value will be in people's path." type='' ;; esac ;; esac ;; esac case "$skip" in true) type=''; esac case "$type" in '') ;; *) if test "$fastread" = yes; then dflt=y else dflt=n fi rp="$what $value doesn't exist. Use that name anyway?" . UU/myread dflt='' case "$ans" in y*) type='';; *) echo " ";; esac ;; esac ;; esac ;; esac done cd UU ans="$value" rp="$orig_rp" dflt="$orig_dflt" rm -f getfile.ok EOSC : determine root of directory hierarchy where package will be installed. case "$prefix" in '') dflt=`./loc . /usr/local /usr/local /local /opt /usr` ;; *) dflt="$prefix" ;; esac $cat <&4 case "$inews" in inews) : null;; *) $echo "(There's a public version too: $inews.)" >&4;; esac inews="$newslibexp/inews" fi ;; *) if test "X$newslib" = X -o ! -d "$newslib"; then newslib='/tmp' fi newslibexp=`./filexp $newslib` ;; esac case "$servername" in local) if test "X$groupdesc" = X -o "X$groupdesc" = "X$oldnewslib/newsgroups"; then dflt="$newslib/newsgroups" else dflt="$groupdesc" fi $cat <&4 echo " " >&4 $cat >&4 $newslibexp/distributions echo " " >&4 fi echo "Use 'none' for any distributions you don't have." echo " " case "$locdist" in '') dflt="none";; *) dflt="$locdist";; esac rp="What is the distribution code for your local organization?" . ./myread locdist="$ans" case "$orgdist" in '') dflt="none";; *) dflt="$orgdist";; esac rp="What is the distribution code for your organization?" . ./myread orgdist="$ans" case "$citydist" in '') dflt="none";; *) dflt="$citydist";; esac rp="What is the distribution code for your city?" . ./myread citydist="$ans" case "$statedist" in '') dflt="none";; *) dflt="$statedist";; esac rp="What is the distribution code for your state/province?" . ./myread statedist="$ans" case "$multistatedist" in '') dflt="none";; *) dflt="$multistatedist";; esac rp="What is the distribution code for your multi-state region?" . ./myread multistatedist="$ans" case "$cntrydist" in '') dflt="none";; *) dflt="$cntrydist";; esac rp="What is the distribution code for your country?" . ./myread cntrydist="$ans" case "$contdist" in '') dflt="none";; *) dflt="$contdist";; esac rp="What is the distribution code for your continent?" . ./myread contdist="$ans" $cat <<'EOM' If you have any other distribution groups you will need to edit Pnews to add them. EOM : make some quick guesses about what we are up against echo " " $echo $n "Hmm... $c" echo exit 1 >bsd echo exit 1 >usg echo exit 1 >v7 echo exit 1 >osf1 echo exit 1 >eunice echo exit 1 >xenix echo exit 1 >venix echo exit 1 >os2 d_bsd="$undef" $cat /usr/include/signal.h /usr/include/sys/signal.h >foo 2>/dev/null if test -f /osf_boot || $contains 'OSF/1' /usr/include/ctype.h >/dev/null 2>&1 then echo "Looks kind of like an OSF/1 system, but we'll see..." echo exit 0 >osf1 elif test `echo abc | tr a-z A-Z` = Abc ; then xxx=`./loc addbib blurfl $pth` if $test -f $xxx; then echo "Looks kind of like a USG system with BSD features, but we'll see..." echo exit 0 >bsd echo exit 0 >usg else if $contains SIGTSTP foo >/dev/null 2>&1 ; then echo "Looks kind of like an extended USG system, but we'll see..." else echo "Looks kind of like a USG system, but we'll see..." fi echo exit 0 >usg fi elif $contains SIGTSTP foo >/dev/null 2>&1 ; then echo "Looks kind of like a BSD system, but we'll see..." d_bsd="$define" echo exit 0 >bsd else echo "Looks kind of like a Version 7 system, but we'll see..." echo exit 0 >v7 fi case "$eunicefix" in *unixtovms*) $cat <<'EOI' There is, however, a strange, musty smell in the air that reminds me of something...hmm...yes...I've got it...there's a VMS nearby, or I'm a Blit. EOI echo exit 0 >eunice d_eunice="$define" : it so happens the Eunice I know will not run shell scripts in Unix format ;; *) echo " " echo "Congratulations. You aren't running Eunice." d_eunice="$undef" ;; esac : Detect OS2. The p_ variable is set above in the Head.U unit. case "$p_" in :) ;; *) $cat <<'EOI' I have the feeling something is not exactly right, however...don't tell me... lemme think...does HAL ring a bell?...no, of course, you're only running OS/2! EOI echo exit 0 >os2 ;; esac if test -f /xenix; then echo "Actually, this looks more like a XENIX system..." echo exit 0 >xenix d_xenix="$define" else echo " " echo "It's not Xenix..." d_xenix="$undef" fi chmod +x xenix $eunicefix xenix if test -f /venix; then echo "Actually, this looks more like a VENIX system..." echo exit 0 >venix else echo " " if ./xenix; then : null else echo "Nor is it Venix..." fi fi chmod +x bsd usg v7 osf1 eunice xenix venix os2 $eunicefix bsd usg v7 osf1 eunice xenix venix os2 $rm -f foo : determine where manual pages are on this system echo " " case "$sysman" in '') syspath='/usr/man/man1 /usr/man/mann /usr/man/manl /usr/man/local/man1' syspath="$syspath /usr/man/u_man/man1 /usr/share/man/man1" syspath="$syspath /usr/catman/u_man/man1 /usr/man/l_man/man1" syspath="$syspath /usr/local/man/u_man/man1 /usr/local/man/l_man/man1" syspath="$syspath /usr/man/man.L /local/man/man1 /usr/local/man/man1" sysman=`./loc . /usr/man/man1 $syspath` ;; esac if $test -d "$sysman"; then echo "System manual is in $sysman." >&4 else echo "Could not find manual pages in source form." >&4 fi : see if we need a special compiler echo " " if ./usg; then case "$cc" in '') case "$Mcc" in /*) dflt='Mcc';; *) case "$large" in -M*) dflt='cc';; *) if $contains '\-M' $sysman/cc.1 >/dev/null 2>&1 ; then if $contains '\-M' $sysman/cpp.1 >/dev/null 2>&1; then dflt='cc' else dflt='cc -M' fi else dflt='cc' fi;; esac;; esac;; *) dflt="$cc";; esac case "$dflt" in *M*) $cat <<'EOM' On some older systems the default C compiler will not resolve multiple global references that happen to have the same name. On some such systems the "Mcc" command may be used to force these to be resolved. On other systems a "cc -M" command is required. (Note that the -M flag on other systems indicates a memory model to use!) If you have the Gnu C compiler, you might wish to use that instead. EOM ;; esac rp="Use which C compiler?" . ./myread cc="$ans" else case "$cc" in '') dflt=cc;; *) dflt="$cc";; esac rp="Use which C compiler?" . ./myread cc="$ans" fi : Look for a hint-file generated 'call-back-unit'. Now that the : user has specified the compiler, we may need to set or change some : other defaults. if $test -f cc.cbu; then . ./cc.cbu fi echo " " echo "Checking for GNU cc in disguise and/or its version number..." >&4 $cat >gccvers.c < int main() { #ifdef __GNUC__ #ifdef __VERSION__ printf("%s\n", __VERSION__); #else printf("%s\n", "1"); #endif #endif exit(0); } EOM if $cc -o gccvers gccvers.c >/dev/null 2>&1; then gccversion=`./gccvers` case "$gccversion" in '') echo "You are not using GNU cc." ;; *) echo "You are using GNU cc $gccversion." ;; esac else echo " " echo "*** WHOA THERE!!! ***" >&4 echo " Your C compiler \"$cc\" doesn't seem to be working!" >&4 case "$knowitall" in '') echo " You'd better start hunting for one and let me know about it." >&4 exit 1 ;; esac fi $rm -f gccvers* case "$gccversion" in 1*) cpp=`./loc gcc-cpp $cpp $pth` ;; esac : What should the include directory be ? echo " " $echo $n "Hmm... $c" dflt='/usr/include' incpath='' mips_type='' if $test -f /bin/mips && /bin/mips; then echo "Looks like a MIPS system..." $cat >usr.c <<'EOCP' #ifdef SYSTYPE_BSD43 /bsd43 #endif EOCP if $cc -E usr.c > usr.out && $contains / usr.out >/dev/null 2>&1; then dflt='/bsd43/usr/include' incpath='/bsd43' mips_type='BSD 4.3' else mips_type='System V' fi $rm -f usr.c usr.out echo "and you're compiling with the $mips_type compiler and libraries." xxx_prompt=y echo "exit 0" >mips else echo "Doesn't look like a MIPS system." xxx_prompt=n echo "exit 1" >mips fi chmod +x mips $eunicefix mips case "$usrinc" in '') ;; *) dflt="$usrinc";; esac case "$xxx_prompt" in y) fn=d/ echo " " rp='Where are the include files you want to use?' . ./getfile usrinc="$ans" ;; *) usrinc="$dflt" ;; esac : now get the hostname that appears in news articles $echo " " $echo "Figuring out local hostname..." cont=true $echo 'Maybe "hostname" will work...' if ans=`sh -c hostname 2>&1` ; then thishost=$ans phostcmd=hostname cont='' fi if $test "$cont"; then if xenix; then $echo 'Oh, dear. Maybe "/etc/systemid" is the key...' if ans=`cat /etc/systemid 2>&1` ; then thishost=$ans phostcmd='cat /etc/systemid' $echo "Whadyaknow. Xenix always was a bit strange..." cont='' fi elif $test -r /etc/systemid; then $echo "(What is a non-Xenix system doing with /etc/systemid?)" fi fi if $test "$cont"; then $echo 'No, maybe "uuname -l" will work...' if ans=`sh -c 'uuname -l' 2>&1` ; then thishost=$ans phostcmd='uuname -l' else $echo 'Strange. Maybe "uname -n" will work...' if ans=`sh -c 'uname -n' 2>&1` ; then thishost=$ans phostcmd='uname -n' else $echo 'Oh well, maybe I can mine it out of whoami.h...' if ans=`sh -c $contains' sysname $usrinc/whoami.h' 2>&1` ; then thishost=`$echo "$ans" | $sed 's/^.*"\(.*\)"/\1/'` phostcmd="sed -n -e '"'/sysname/s/^.*\"\\(.*\\)\"/\1/{'"' -e p -e q -e '}' <$usrinc/whoami.h" else phostcmd='' case "$phost" in '') $echo "Does this machine have an identity crisis or something?" ;; *) $echo "Well, you said $phost before..." thishost=$phost ;; esac fi fi fi fi : you do not want to know about this set $thishost thishost=$1 echo "Your local hostname was computed to be '$thishost'." : translate upper to lower if necessary case "$thishost" in *[A-Z]*) thishost=`$echo $thishost | ./tr '[A-Z]' '[a-z]'` ;; esac case "$phost" in '') dflt="$thishost" case "$dflt" in *.*) ;; *) dflt="$dflt.UUCP";; esac ;; *) dflt="$phost";; esac if test "$d_inews" = "$define"; then $cat <&4 cat <<'EOT' >testcpp.c #define ABC abc #define XYZ xyz ABC.XYZ EOT cd .. echo 'cat >.$$.c; '"$cc"' -E ${1+"$@"} .$$.c; rm .$$.c' >cppstdin chmod 755 cppstdin wrapper=`pwd`/cppstdin ok='false' cd UU if $test "X$cppstdin" != "X" && \ $cppstdin $cppminus testcpp.out 2>&1 && \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 then echo "You used to use $cppstdin $cppminus so we'll use that again." case "$cpprun" in '') echo "But let's see if we can live without a wrapper..." ;; *) if $cpprun $cpplast testcpp.out 2>&1 && \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 then echo "(And we'll use $cpprun $cpplast to preprocess directly.)" ok='true' else echo "(However, $cpprun $cpplast does not work, let's see...)" fi ;; esac else case "$cppstdin" in '') ;; *) echo "Good old $cppstdin $cppminus does not seem to be of any help..." ;; esac fi if $ok; then : nothing elif echo 'Maybe "'"$cc"' -E" will work...'; \ $cc -E testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yup, it does." x_cpp="$cc -E" x_minus=''; elif echo 'Nope...maybe "'"$cc"' -E -" will work...'; \ $cc -E - testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yup, it does." x_cpp="$cc -E" x_minus='-'; elif echo 'Nope...maybe "'"$cc"' -P" will work...'; \ $cc -P testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yipee, that works!" x_cpp="$cc -P" x_minus=''; elif echo 'Nope...maybe "'"$cc"' -P -" will work...'; \ $cc -P - testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "At long last!" x_cpp="$cc -P" x_minus='-'; elif echo 'No such luck, maybe "'$cpp'" will work...'; \ $cpp testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "It works!" x_cpp="$cpp" x_minus=''; elif echo 'Nixed again...maybe "'$cpp' -" will work...'; \ $cpp - testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Hooray, it works! I was beginning to wonder." x_cpp="$cpp" x_minus='-'; elif echo 'Uh-uh. Time to get fancy. Trying a wrapper...'; \ $wrapper testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then x_cpp="$wrapper" x_minus='' echo "Eureka!" else dflt='' rp="No dice. I can't find a C preprocessor. Name one:" . ./myread x_cpp="$ans" x_minus='' $x_cpp testcpp.out 2>&1 if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "OK, that will do." >&4 else echo "Sorry, I can't get that to work. Go find one and rerun Configure." >&4 exit 1 fi fi case "$ok" in false) cppstdin="$x_cpp" cppminus="$x_minus" cpprun="$x_cpp" cpplast="$x_minus" set X $x_cpp shift case "$1" in "$cpp") echo "Perhaps can we force $cc -E using a wrapper..." if $wrapper testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 then echo "Yup, we can." cppstdin="$wrapper" cppminus=''; else echo "Nope, we'll have to live without it..." fi ;; esac case "$cpprun" in "$wrapper") cpprun='' cpplast='' ;; esac ;; esac case "$cppstdin" in "$wrapper") ;; *) $rm -f $wrapper;; esac $rm -f testcpp.c testcpp.out : Set private lib path case "$plibpth" in '') if ./mips; then plibpth="$incpath/usr/lib /usr/local/lib /usr/ccs/lib" fi;; esac case "$libpth" in ' ') dlist='';; '') dlist="$loclibpth $plibpth $glibpth";; *) dlist="$libpth";; esac : Now check and see which directories actually exist, avoiding duplicates libpth='' for xxx in $dlist do if $test -d $xxx; then case " $libpth " in *" $xxx "*) ;; *) libpth="$libpth $xxx";; esac fi done $cat <<'EOM' Some systems have incompatible or broken versions of libraries. Among the directories listed in the question below, please remove any you know not to be holding relevant libraries, and add any that are needed. Say "none" for none. EOM case "$libpth" in '') dflt='none';; *) set X $libpth shift dflt=${1+"$@"} ;; esac rp="Directories to use for library searches?" . ./myread case "$ans" in none) libpth=' ';; *) libpth="$ans";; esac : compute shared library extension case "$so" in '') if xxx=`./loc libc.sl X $libpth`; $test -f "$xxx"; then dflt='sl' else dflt='so' fi ;; *) dflt="$so";; esac $cat <&4 case "$libs" in ' '|'') dflt='';; *) dflt="$libs";; esac case "$libswanted" in '') libswanted='c_s';; esac for thislib in $libswanted; do if xxx=`./loc lib$thislib.$so.[0-9]'*' X $libpth`; $test -f "$xxx"; then echo "Found -l$thislib (shared)." case " $dflt " in *"-l$thislib "*);; *) dflt="$dflt -l$thislib";; esac elif xxx=`./loc lib$thislib.$so X $libpth` ; $test -f "$xxx"; then echo "Found -l$thislib (shared)." case " $dflt " in *"-l$thislib "*);; *) dflt="$dflt -l$thislib";; esac elif xxx=`./loc lib$thislib$_a X $libpth`; $test -f "$xxx"; then echo "Found -l$thislib." case " $dflt " in *"-l$thislib "*);; *) dflt="$dflt -l$thislib";; esac elif xxx=`./loc $thislib$_a X $libpth`; $test -f "$xxx"; then echo "Found -l$thislib." case " $dflt " in *"-l$thislib "*);; *) dflt="$dflt -l$thislib";; esac elif xxx=`./loc lib${thislib}_s$_a X $libpth`; $test -f "$xxx"; then echo "Found -l${thislib}_s." case " $dflt " in *"-l$thislib "*);; *) dflt="$dflt -l${thislib}_s";; esac elif xxx=`./loc Slib$thislib$_a X $xlibpth`; $test -f "$xxx"; then echo "Found -l$thislib." case " $dflt " in *"-l$thislib "*);; *) dflt="$dflt -l$thislib";; esac else echo "No -l$thislib." fi done set X $dflt shift dflt="$*" case "$libs" in '') dflt="$dflt";; *) dflt="$libs";; esac case "$dflt" in ' '|'') dflt='none';; esac $cat </dev/null 2>&1 then dflt="$dflt -posix" fi ;; esac ;; esac case "$mips_type" in *BSD*|'') inclwanted="$locincpth $usrinc";; *) inclwanted="$locincpth $inclwanted $usrinc/bsd";; esac for thisincl in $inclwanted; do if $test -d $thisincl; then if $test x$thisincl != x$usrinc; then case "$dflt" in *$thisincl*);; *) dflt="$dflt -I$thisincl";; esac fi fi done inctest='if $contains $2 $usrinc/$1 >/dev/null 2>&1; then xxx=true; elif $contains $2 $usrinc/sys/$1 >/dev/null 2>&1; then xxx=true; else xxx=false; fi; if $xxx; then case "$dflt" in *$2*);; *) dflt="$dflt -D$2";; esac; fi' if ./osf1; then set signal.h __LANGUAGE_C__; eval $inctest else set signal.h LANGUAGE_C; eval $inctest fi case "$hint" in none|recommended) dflt="$ccflags $dflt" ;; *) dflt="$ccflags";; esac case "$dflt" in ''|' ') dflt=none;; esac $cat <&4 set X $cppflags shift cppflags='' $cat >cpp.c <<'EOM' #define BLURFL foo BLURFL xx LFRULB EOM previous='' for flag in $* do case "$flag" in -*) ftry="$flag";; *) ftry="$previous $flag";; esac if $cppstdin -DLFRULB=bar $cppflags $ftry $cppminus cpp1.out 2>/dev/null && \ $cpprun -DLFRULB=bar $cppflags $ftry $cpplast cpp2.out 2>/dev/null && \ $contains 'foo.*xx.*bar' cpp1.out >/dev/null 2>&1 && \ $contains 'foo.*xx.*bar' cpp2.out >/dev/null 2>&1 then cppflags="$cppflags $ftry" previous='' else previous="$flag" fi done set X $cppflags shift cppflags=${1+"$@"} case "$cppflags" in *-*) echo "They appear to be: $cppflags";; esac $rm -f cpp.c cpp?.out ;; esac : flags used in final linking phase case "$ldflags" in '') if ./venix; then dflt='-i -z' else dflt='' fi case "$ccflags" in *-posix*) dflt="$dflt -posix" ;; esac ;; *) dflt="$ldflags";; esac : Try to guess additional flags to pick up local libraries. for thislibdir in $libpth; do case " $loclibpth " in *" $thislibdir "*) case "$dflt " in *"-L$thislibdir "*) ;; *) dflt="$dflt -L$thislibdir" ;; esac ;; esac done case "$dflt" in '') dflt='none' ;; esac $cat <&4 $cat > try.c <<'EOF' #include main() { printf("Ok\n"); exit(0); } EOF set X $cc $optimize $ccflags -o try $ldflags try.c $libs shift $cat >try.msg <<'EOM' I've tried to compile and run the following simple program: EOM $cat try.c $cat >> try.msg <>try.msg 2>&1; then if sh -c './try' >>try.msg 2>&1; then xxx=`./try` case "$xxx" in "Ok") dflt=n ;; *) echo 'The program compiled OK, but produced no output.' >> try.msg case " $libs " in *" -lsfio "*) cat >> try.msg <<'EOQS' If $libs contains -lsfio, and sfio is mis-configured, then it sometimes (apparently) runs and exits with a 0 status, but with no output! It may have to do with sfio's use of _exit vs. exit. EOQS rp="You have a big problem. Shall I abort Configure" dflt=y ;; esac ;; esac else echo "The program compiled OK, but exited with status $?." >>try.msg rp="You have a problem. Shall I abort Configure" dflt=y fi else echo "I can't compile the test program." >>try.msg rp="You have a BIG problem. Shall I abort Configure" dflt=y fi case "$dflt" in y) $cat try.msg >&4 case "$knowitall" in '') echo "(The supplied flags or libraries might be incorrect.)" ;; *) dflt=n;; esac echo " " . ./myread case "$ans" in n*|N*) ;; *) echo "Ok. Stopping Configure." >&4 exit 1 ;; esac ;; n) echo "OK, that should do.";; esac $rm -f try try.* core : set up the script used to warn in case of inconsistency cat <whoa $startsh EOS cat <<'EOSC' >>whoa dflt=y echo " " echo "*** WHOA THERE!!! ***" >&4 echo " The $hint value for \$$var on this machine was \"$was\"!" >&4 rp=" Keep the $hint value?" . ./myread case "$ans" in y) td=$was; tu=$was;; esac EOSC : function used to set $1 to $val setvar='var=$1; eval "was=\$$1"; td=$define; tu=$undef; case "$val$was" in $define$undef) . ./whoa; eval "$var=\$td";; $undef$define) . ./whoa; eval "$var=\$tu";; *) eval "$var=$val";; esac' : Look for GNU-cc style attribute checking echo " " echo "Checking whether your compiler can handle __attribute__ ..." >&4 $cat >attrib.c <<'EOCP' #include void croak (char* pat,...) __attribute__((format(printf,1,2),noreturn)); EOCP if $cc $ccflags -c attrib.c >attrib.out 2>&1 ; then if $contains 'warning' attrib.out >/dev/null 2>&1; then echo "Your C compiler doesn't fully support __attribute__." val="$undef" else echo "Your C compiler supports __attribute__." val="$define" fi else echo "Your C compiler doesn't seem to understand __attribute__ at all." val="$undef" fi set d_attribut eval $setvar $rm -f attrib* : find out how to find out full name case "$d_berknames" in "$define") dflt=y;; "$undef") dflt=n;; *) if ./bsd; then dflt=y elif ./xenix; then dflt=y else dflt=n fi ;; esac $cat <<'EOM' Does your /etc/passwd file keep full names in Berkeley/V7 format (name first thing after ':' in GCOS field)? In that case, a typical entry in the password file looks like this: guest:**paswword**:10:100:Mister Guest User:/usr/users:/bin/sh ^^^^^^^^^^^^^^^^^ EOM rp="Berkeley/V7 format for full name in /etc/passwd?" . ./myread case "$ans" in y*) d_passnames="$define" d_berknames="$define" d_usgnames="$undef" nametype=bsd ;; *) case "$d_usgnames" in "$define") dflt=y;; "$undef") dflt=n;; *) if ./usg; then dflt=y else dflt=n fi ;; esac $cat <<'EOM' Does your passwd file keep full names in USG format (name sandwiched between a '-' and a '(')? In that case, a typical entry in the password file looks like this: guest:**paswword**:10:100:000-Mister Guest User(000):/usr/users:/bin/sh ^^^^^^^^^^^^^^^^^ EOM rp="USG format for full name in /etc/passwd?" . ./myread case "$ans" in n*) echo "Full name will be taken from ~/.fullname" d_passnames="$undef" d_berknames="$undef" d_usgnames="$undef" nametype=other ;; *) d_passnames="$define" d_berknames="$undef" d_usgnames="$define" nametype=usg ;; esac;; esac : define a shorthand compile call compile=' mc_file=$1; shift; $cc $optimize $ccflags $ldflags -o ${mc_file} $* ${mc_file}.c $libs > /dev/null 2>&1;' : define a shorthand compile call for compilations that should be ok. compile_ok=' mc_file=$1; shift; $cc $optimize $ccflags $ldflags -o ${mc_file} $* ${mc_file}.c $libs;' echo " " echo "Checking for GNU C Library..." >&4 cat >gnulibc.c < int main() { #ifdef __GLIBC__ exit(0); #else exit(1); #endif } EOM set gnulibc if eval $compile_ok && ./gnulibc; then val="$define" echo "You are using the GNU C Library" else val="$undef" echo "You are not using the GNU C Library" fi $rm -f gnulibc* set d_gnulibc eval $setvar : see if nm is to be used to determine whether a symbol is defined or not case "$usenm" in '') dflt='' case "$d_gnulibc" in "$define") echo " " echo "nm probably won't work on the GNU C Library." >&4 dflt=n ;; esac case "$dflt" in '') if $test "$osname" = aix -a ! -f /lib/syscalls.exp; then echo " " echo "Whoops! This is an AIX system without /lib/syscalls.exp!" >&4 echo "'nm' won't be sufficient on this sytem." >&4 dflt=n fi ;; esac case "$dflt" in '') dflt=`$egrep 'inlibc|csym' $rsrc/Configure | wc -l 2>/dev/null` if $test $dflt -gt 20; then dflt=y else dflt=n fi ;; esac ;; *) case "$usenm" in true|$define) dflt=y;; *) dflt=n;; esac ;; esac $cat < /dev/null 2>&1; then nm_so_opt='--dynamic' fi ;; esac ;; esac case "$runnm" in true) : get list of predefined functions in a handy place echo " " case "$libc" in '') libc=unknown case "$libs" in *-lc_s*) libc=`./loc libc_s$_a $libc $libpth` esac ;; esac libnames=''; case "$libs" in '') ;; *) for thislib in $libs; do case "$thislib" in -lc|-lc_s) : Handle C library specially below. ;; -l*) thislib=`echo $thislib | $sed -e 's/^-l//'` if try=`./loc lib$thislib.$so.'*' X $libpth`; $test -f "$try"; then : elif try=`./loc lib$thislib.$so X $libpth`; $test -f "$try"; then : elif try=`./loc lib$thislib$_a X $libpth`; $test -f "$try"; then : elif try=`./loc $thislib$_a X $libpth`; $test -f "$try"; then : elif try=`./loc lib$thislib X $libpth`; $test -f "$try"; then : elif try=`./loc $thislib X $libpth`; $test -f "$try"; then : elif try=`./loc Slib$thislib$_a X $xlibpth`; $test -f "$try"; then : else try='' fi libnames="$libnames $try" ;; *) libnames="$libnames $thislib" ;; esac done ;; esac xxx=normal case "$libc" in unknown) set /lib/libc.$so for xxx in $libpth; do $test -r $1 || set $xxx/libc.$so : The messy sed command sorts on library version numbers. $test -r $1 || \ set `echo blurfl; echo $xxx/libc.$so.[0-9]* | \ tr ' ' $trnl | egrep -v '\.[A-Za-z]*$' | $sed -e ' h s/[0-9][0-9]*/0000&/g s/0*\([0-9][0-9][0-9][0-9][0-9]\)/\1/g G s/\n/ /' | \ sort | $sed -e 's/^.* //'` eval set \$$# done $test -r $1 || set /usr/ccs/lib/libc.$so $test -r $1 || set /lib/libsys_s$_a ;; *) set blurfl ;; esac if $test -r "$1"; then echo "Your (shared) C library seems to be in $1." libc="$1" elif $test -r /lib/libc && $test -r /lib/clib; then echo "Your C library seems to be in both /lib/clib and /lib/libc." xxx=apollo libc='/lib/clib /lib/libc' if $test -r /lib/syslib; then echo "(Your math library is in /lib/syslib.)" libc="$libc /lib/syslib" fi elif $test -r "$libc" || (test -h "$libc") >/dev/null 2>&1; then echo "Your C library seems to be in $libc, as you said before." elif $test -r $incpath/usr/lib/libc$_a; then libc=$incpath/usr/lib/libc$_a; echo "Your C library seems to be in $libc. That's fine." elif $test -r /lib/libc$_a; then libc=/lib/libc$_a; echo "Your C library seems to be in $libc. You're normal." else if tans=`./loc libc$_a blurfl/dyick $libpth`; $test -r "$tans"; then : elif tans=`./loc libc blurfl/dyick $libpth`; $test -r "$tans"; then libnames="$libnames "`./loc clib blurfl/dyick $libpth` elif tans=`./loc clib blurfl/dyick $libpth`; $test -r "$tans"; then : elif tans=`./loc Slibc$_a blurfl/dyick $xlibpth`; $test -r "$tans"; then : elif tans=`./loc Mlibc$_a blurfl/dyick $xlibpth`; $test -r "$tans"; then : else tans=`./loc Llibc$_a blurfl/dyick $xlibpth` fi if $test -r "$tans"; then echo "Your C library seems to be in $tans, of all places." libc=$tans else libc='blurfl' fi fi if $test $xxx = apollo -o -r "$libc" || (test -h "$libc") >/dev/null 2>&1; then dflt="$libc" cat < libpath cat >&4 < libnames set X `cat libnames` shift xxx=files case $# in 1) xxx=file; esac echo "Extracting names from the following $xxx for later perusal:" >&4 echo " " $sed 's/^/ /' libnames >&4 echo " " $echo $n "This may take a while...$c" >&4 for file in $*; do case $file in *.$so*) $nm $nm_so_opt $nm_opt $file 2>/dev/null;; *) $nm $nm_opt $file 2>/dev/null;; esac done >libc.tmp $echo $n ".$c" $grep fprintf libc.tmp > libc.ptf xscan='eval "libc.list"; $echo $n ".$c" >&4' xrun='eval "libc.list"; echo "done" >&4' xxx='[ADTSIW]' if com="$sed -n -e 's/__IO//' -e 's/^.* $xxx *_[_.]*//p' -e 's/^.* $xxx *//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^__*//' -e 's/^\([a-zA-Z_0-9$]*\).*xtern.*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e '/|UNDEF/d' -e '/FUNC..GL/s/^.*|__*//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.* D __*//p' -e 's/^.* D //p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^_//' -e 's/^\([a-zA-Z_0-9]*\).*xtern.*text.*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.*|FUNC |GLOB .*|//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$grep '|' | $sed -n -e '/|COMMON/d' -e '/|DATA/d' \ -e '/ file/d' -e 's/^\([^ ]*\).*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.*|FUNC |GLOB .*|//p' -e 's/^.*|FUNC |WEAK .*|//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^__//' -e '/|Undef/d' -e '/|Proc/s/ .*//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.*|Proc .*|Text *| *//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e '/Def. Text/s/.* \([^ ]*\)\$/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^[-0-9a-f ]*_\(.*\)=.*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/.*\.text n\ \ \ \.//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun # For AIX v4 elif com="$sed -n -e 's/^\.\([A-z][a-z]*\) *T .*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun else $nm -p $* 2>/dev/null >libc.tmp $grep fprintf libc.tmp > libc.ptf if com="$sed -n -e 's/^.* [ADTSIW] *_[_.]*//p' -e 's/^.* [ADTSIW] //p'";\ eval $xscan; $contains '^fprintf$' libc.list >/dev/null 2>&1 then nm_opt='-p' eval $xrun else echo " " echo "$nm didn't seem to work right. Trying $ar instead..." >&4 com='' if $ar t $libc > libc.tmp && $contains '^fprintf$' libc.tmp >/dev/null 2>&1; then for thisname in $libnames $libc; do $ar t $thisname >>libc.tmp done $sed -e "s/\\$_o\$//" < libc.tmp > libc.list echo "Ok." >&4 elif test "X$osname" = "Xos2" && $ar tv $libc > libc.tmp; then # Repeat libc to extract forwarders to DLL entries too for thisname in $libnames $libc; do $ar tv $thisname >>libc.tmp # Revision 50 of EMX has bug in $ar. # it will not extract forwarders to DLL entries # Use emximp which will extract exactly them. emximp -o tmp.imp $thisname \ 2>/dev/null && \ $sed -e 's/^\([_a-zA-Z0-9]*\) .*$/\1/p' \ < tmp.imp >>libc.tmp $rm tmp.imp done $sed -e "s/\\$_o\$//" -e 's/^ \+//' < libc.tmp > libc.list echo "Ok." >&4 else echo "$ar didn't seem to work right." >&4 echo "Maybe this is a Cray...trying bld instead..." >&4 if bld t $libc | $sed -e 's/.*\///' -e "s/\\$_o:.*\$//" > libc.list then for thisname in $libnames; do bld t $libnames | \ $sed -e 's/.*\///' -e "s/\\$_o:.*\$//" >>libc.list $ar t $thisname >>libc.tmp done echo "Ok." >&4 else echo "That didn't work either. Giving up." >&4 exit 1 fi fi fi fi nm_extract="$com" if $test -f /lib/syscalls.exp; then echo " " echo "Also extracting names from /lib/syscalls.exp for good ole AIX..." >&4 $sed -n 's/^\([^ ]*\)[ ]*syscall$/\1/p' /lib/syscalls.exp >>libc.list fi ;; esac $rm -f libnames libpath : is a C symbol defined? csym='tlook=$1; case "$3" in -v) tf=libc.tmp; tc=""; tdc="";; -a) tf=libc.tmp; tc="[0]"; tdc="[]";; *) tlook="^$1\$"; tf=libc.list; tc="()"; tdc="()";; esac; tx=yes; case "$reuseval-$4" in true-) ;; true-*) tx=no; eval "tval=\$$4"; case "$tval" in "") tx=yes;; esac;; esac; case "$tx" in yes) case "$runnm" in true) if $contains $tlook $tf >/dev/null 2>&1; then tval=true; else tval=false; fi;; *) echo "main() { extern short $1$tdc; printf(\"%hd\", $1$tc); }" > t.c; if $cc $optimize $ccflags $ldflags -o t t.c $libs >/dev/null 2>&1; then tval=true; else tval=false; fi; $rm -f t t.c;; esac;; *) case "$tval" in $define) tval=true;; *) tval=false;; esac;; esac; eval "$2=$tval"' : define an is-in-libc? function inlibc='echo " "; td=$define; tu=$undef; sym=$1; var=$2; eval "was=\$$2"; tx=yes; case "$reuseval$was" in true) ;; true*) tx=no;; esac; case "$tx" in yes) set $sym tres -f; eval $csym; case "$tres" in true) echo "$sym() found." >&4; case "$was" in $undef) . ./whoa; esac; eval "$var=\$td";; *) echo "$sym() NOT found." >&4; case "$was" in $define) . ./whoa; esac; eval "$var=\$tu";; esac;; *) case "$was" in $define) echo "$sym() found." >&4;; *) echo "$sym() NOT found." >&4;; esac;; esac' : see if gettimeofday and ftime exists set gettimeofday d_gettimeod eval $inlibc set ftime d_ftime eval $inlibc : see if getwd and getcwd exist set getwd d_getwd; eval $inlibc set getcwd d_getcwd; eval $inlibc : see how we will look up the hostname, if needed echo " " if $test "X$phostcmd" = "X"; then : host name is static d_gethname="$undef" d_uname="$undef" elif set gethostname val -f d_gethname; eval $csym; $val; then echo 'gethostname() found.' >&4 d_gethname="$define" d_uname="$undef" ans=gethostname elif set uname val -f d_uname; eval $csym; $val; then d_gethname="$undef" if ./xenix; then $cat <<'EOM' uname() was found, but you're running xenix, and older versions of xenix have a broken uname(). If you don't really know whether your xenix is old enough to have a broken system call, use the default answer. EOM dflt=y case "$d_uname" in "$define") dflt=n;; esac rp='Is your uname() broken?' . ./myread case "$ans" in n*) d_uname="$define" ;; *) d_uname="$undef" ;; esac else echo 'uname() found.' >&4 d_uname="$define" fi else d_gethname="$undef" d_uname="$undef" fi case "$phostcmd" in '') aphostcmd='';; *) case "$aphostcmd" in /*) ;; *) set X $phostcmd shift file=$1 shift file=`loc $file $file $pth` aphostcmd=`echo $file $*` ;; esac ;; esac case "$aphostcmd" in '') d_phostcmd="$undef";; *) d_phostcmd="$define";; esac : see how we will look up the domain, if needed echo " " case "$phost" in .|/*|~*) if set res_init val -f d_resinit; eval $csym; $val; then echo 'res_init() found.' >&4 d_resinit="$define" d_getdname="$undef" elif set getdomainname val -f d_getdname; eval $csym; $val; then echo 'getdomainname() found.' >&4 d_getdname="$define" d_resinit="$undef" else echo 'Neither res_init() nor getdomainname() found.' >&4 d_getdname="$undef" d_resinit="$undef" fi ;; *) d_resinit="$undef" d_getdname="$undef" ;; esac : see if getpwent exists set getpwent d_getpwent eval $inlibc : where do we get termlib routines from echo " " xxx=`./loc libcurses$_a x $libpth` case "$xxx" in /*) ar t $xxx >grimble if $contains tputs$_o grimble >/dev/null 2>&1; then termlib='-lcurses' d_havetlib="$define" echo "Terminfo library found." >&4 elif $test "x`$uname 2>/dev/null`" = xAIX; then # Ok, do the AIX shr.o fun thing /usr/ccs/bin/nm -en $xxx 2>/dev/null >grimble if $contains '^\.tputs.* T ' grimble >/dev/null 2>&1; then termlib='-lcurses' d_havetlib="$define" echo "AIX Terminfo library found." >&4 else xxx=x fi else xxx=x fi rm -f grimble ;; esac case "$xxx" in x) xxx=`./loc libtermlib$_a x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-ltermlib' d_havetlib="$define" echo "Termlib library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Termlib library found." >&4 ;; *) xxx=x esac ;; esac case "$xxx" in x) xxx=`./loc libtermcap$_a x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-ltermcap' d_havetlib="$define" echo "Termcap library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Termcap library found." >&4 ;; *) xxx=x esac ;; esac case "$xxx" in x) xxx=`./loc libcurses.$so'*' x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-lcurses' d_havetlib="$define" echo "Terminfo library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Terminfo library found." >&4 ;; *) xxx=x esac ;; esac case "$xxx" in x) xxx=`./loc libtermlib.$so'*' x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-ltermlib' d_havetlib="$define" echo "Termlib library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Termlib library found." >&4 ;; *) xxx=x esac ;; esac case "$xxx" in x) xxx=`./loc libtermcap.$so'*' x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-ltermcap' d_havetlib="$define" echo "Termcap library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Termcap library found." >&4 ;; *) xxx=x esac ;; esac case "$xxx" in x) dflt=y rp="Your system appears to NOT have termlib-style routines. Is this true?" . ./myread case "$ans" in n*|f*) d_havetlib="$define" $cat <<'EOM' Then where are the termlib-style routines kept? Specify either -llibname or a full pathname (~name ok). EOM dflt='' rp='Specify termlib path:' . ./myread termlib=`./filexp $ans` ;; *) d_havetlib="$undef" termlib='' echo "You will have to play around with term.c then." >&4 ;; esac ;; esac : Decide whether to ignore the ORGANIZATION environment variable case "$d_ignoreorg" in "$define") dflt=y ;; *) dflt=n ;; esac cat <&4 echo '#include ' > foo.c $cat >fieldn </dev/null | \ $grep '^[ ]*#.*stdio\.h' | \ while read cline; do pos=1 set \$cline while $test \$# -gt 0; do if $test -r \`echo \$1 | $tr -d '"'\`; then echo "\$pos" exit 0 fi shift pos=\`expr \$pos + 1\` done done EOF chmod +x fieldn fieldn=`./fieldn` $rm -f foo.c fieldn case $fieldn in '') pos='???';; 1) pos=first;; 2) pos=second;; 3) pos=third;; *) pos="${fieldn}th";; esac echo "Your cpp writes the filename in the $pos field of the line." : locate header file $cat >findhdr <" > foo\$\$.c $cppstdin $cppminus $cppflags < foo\$\$.c 2>/dev/null | \ $grep "^[ ]*#.*\$wanted" | \ while read cline; do name=\`echo \$cline | $awk "\$awkprg" | $tr -d '"'\` case "\$name" in */\$wanted) echo "\$name"; exit 0;; *) name='';; esac; done; $rm -f foo\$\$.c; case "\$name" in '') exit 1;; esac EOF chmod +x findhdr : see if there are directory access routines out there echo " " if $test `./findhdr ndir.h` && \ ( $test -r /usr/lib/libndir$_a || $test -r /usr/local/lib/libndir$_a ); then echo "Ndir library found." >&4 if $test -r /usr/lib/libndir$_a; then libndir='-lndir' else libndir="/usr/local/lib/libndir$_a" fi d_libndir="$define" d_usendir="$undef" ndirc='' ndiro='' else libndir='' d_libndir="$undef" if set readdir val -f; eval $csym; $val; then echo "No ndir library found, but you have readdir() so we'll use that." >&4 d_usendir="$undef" ndirc='' ndiro='' else echo "No ndir library found--using ./ndir.c." >&4 d_usendir="$define" ndirc='ndir.c' ndiro="ndir$_o" fi fi : see if memcmp exists set memcmp d_memcmp eval $inlibc : see if memcpy exists set memcpy d_memcpy eval $inlibc : see if memset exists set memset d_memset eval $inlibc : see if mkdir exists set mkdir d_mkdir eval $inlibc : get news administrator name case "$newsadmin" in '') if $contains "^news:" /etc/passwd >/dev/null 2>&1 ; then dflt=news elif $contains "^usenet:" /etc/passwd >/dev/null 2>&1 ; then dflt=usenet elif ./eunice; then dflt=system else dflt=root fi ;; *) dflt="$newsadmin" ;; esac $cat <<'EOM' Many systems keep their news in a private directory, or have a non-superuser in charge of administering news. (If you don't have such a user, take the default answer.) I need the login name (not directory) which is used for news administration. EOM rp="News admin login?" . ./myread newsadmin="$ans" case "$newsadmin" in root) val="$undef" ;; *) val="$define" ;; esac set d_newsadm eval $setvar : check for buffering of stdout echo " " case "$d_nolnbuf" in '') $cat <<'EOT' >blurfl.c #include main() { int i; for (i = 0; i < 5; i++) { putchar(i+'a'); sleep(1); } putchar('\n'); } EOT $cc -o blurfl blurfl.c >/dev/null 2>&1; $rm -f blurfl.c $cat >&4 <<'EOM' Checking for buffering of stdout to terminal. Observe the following characters as they are printed out, to see whether they print out all at once, or with a 1 second pause between each of them. If they print out one by one, you don't have buffering. If they print together (after about a 5 second pause), you do have buffering. EOM dflt='Type return to start printing the test characters' rp='' . ./myread xxx=y while $test "$xxx" = 'y'; do ./blurfl 1>&4 dflt=n rp='Would you like to see that again?' . ./myread case "$ans" in [yY]*) xxx="y";; *) xxx="n";; esac done dflt=y rp="Do you have buffering (printed all at once)?" . ./myread case "$ans" in n*) val="$define";; *) val="$undef";; esac ;; *) case "$d_nolnbuf" in "$define") dflt=n;; *) dflt=y;; esac rp="Do you have buffering on stdout to terminals?" . ./myread case "$ans" in n*) val="$define";; *) val="$undef";; esac ;; esac set d_nolnbuf eval $setvar : see if we need -ljobs and if we have sigset, etc. echo " " if $test -r /usr/lib/libjobs$_a || $test -r /usr/local/lib/libjobs$_a ; then echo "Jobs library found." >&4 val="$undef" jobslib='-ljobs' else if ./bsd; then echo "No jobs library found. (I suppose this is at least 4.2...)" >&4 else echo "No jobs library found. (That's okay, we all have our faults.)" >&4 fi val="$define" jobslib='' fi set d_normsig eval $setvar : check for void type echo " " $echo "Checking to see if your C compiler groks the void type..." $cat >void.c <<'EOCP' void main(){;} EOCP if $cc -c void.c >/dev/null 2>&1 ; then d_novoid="$undef" $echo "Yup, it does." else d_novoid="$define" $echo "Nope, it doesn't (boo hiss). I will substitute int." fi $rm void.* : see if rdchk exists set rdchk d_rdchk eval $inlibc : see if rename exists set rename d_rename eval $inlibc : see if sigblock exists set sigblock d_sigblock eval $inlibc : see if sighold exists set sighold d_sighold eval $inlibc : see if we need to define size_t if $contains 'size_t' /usr/include/string*.h >/dev/null 2>&1 ; then d_sizet="$undef" else d_sizet="$define" fi : see if strcasecmp exists set strcasecmp d_strccmp eval $inlibc : see which of string.h or strings.h is needed echo " " strings=`./findhdr string.h` if $test "$strings" && $test -r "$strings"; then echo "Using instead of ." >&4 val="$define" else val="$undef" strings=`./findhdr strings.h` if $test "$strings" && $test -r "$strings"; then echo "Using instead of ." >&4 else echo "No string header found -- You'll surely have problems." >&4 fi fi set i_string eval $setvar case "$i_string" in "$undef") strings=`./findhdr strings.h`;; *) strings=`./findhdr string.h`;; esac : index or strchr echo " " if set index val -f; eval $csym; $val; then if set strchr val -f d_strchr; eval $csym; $val; then if $contains strchr "$strings" >/dev/null 2>&1 ; then val="$define" echo "strchr() found." >&4 else val="$undef" echo "index() found." >&4 fi else val="$undef" echo "index() found." >&4 fi else if set strchr val -f d_strchr; eval $csym; $val; then val="$define" echo "strchr() found." >&4 else echo "No index() or strchr() found!" >&4 val="$undef" fi fi set d_strchr eval $setvar : see if strftime exists case "$strftimec" in '') ;; *) d_strftime="$undef";; esac set strftime d_strftime eval $inlibc case "$d_strftime" in "$undef") case "$strftimec" in ' ') dflt='none';; '') dflt="$rsrc/strftime.c";; *) dflt="$strftimec";; esac $cat <wait.out 2>/dev/null if $contains 'union.*wait.*{' wait.out >/dev/null 2>&1 ; then echo "Looks like your knows about 'union wait'..." >&4 val="$define" also='also ' if $contains 'extern.*wait[ ]*([ ]*int' wait.out >/dev/null 2>&1 then echo "But wait() seems to expect an 'int' pointer (POSIX way)." >&4 val="$undef" also='' elif $contains 'extern.*wait[ ]*([ ]*union' wait.out >/dev/null 2>&1 then echo "And indeed wait() expects an 'union wait' pointer (BSD way)." >&4 else echo "So we'll use that for wait()." >&4 fi else echo "No trace of 'union wait' in ..." >&4 val="$undef" echo "Your wait() should be happy with a plain 'int' pointer." >&4 fi set d_uwait eval $setvar $rm -f wait.out : backward compatibility for d_hvfork if test X$d_hvfork != X; then d_vfork="$d_hvfork" d_hvfork='' fi : see if there is a vfork val='' set vfork val eval $inlibc : Ok, but do we want to use it. vfork is reportedly unreliable in : perl on Solaris 2.x, and probably elsewhere. case "$val" in $define) echo " " case "$usevfork" in false) dflt='n';; *) dflt='y';; esac rp="Some systems have problems with vfork(). Do you want to use it?" . ./myread case "$ans" in y|Y) ;; *) echo "Ok, we won't use vfork()." val="$undef" ;; esac ;; esac set d_vfork eval $setvar case "$d_vfork" in $define) usevfork='true';; *) usevfork='false';; esac : see if we can use WIFxxx macros echo " " case "$d_wifstat" in "$define") echo "As before, you can safely use WIFEXITED and friends!" >&4;; "$undef") echo "You still can't use WIFEXITED and friends!" >&4;; *) echo "Let's see whether you can use the WIFEXITED(status) macro and its" case "$d_uwait" in "$define") type='union wait';; *) type='int';; esac echo "friends with status declared as '$type status'..." $cat >foo.c < #include main() { $type status; int e = 0; #ifdef WIFEXITED if (WIFEXITED(status)) printf("\n"); exit(0); #else exit(2); #endif } EOCP d_wifstat="$undef" set foo if eval $compile; then if ./foo >/dev/null; then echo "Great! Looks like we can use the WIFxxx macros." >&4 d_wifstat="$define" else echo "You don't seem to have WIFxxx macros, but that's ok." >&4 fi else echo "Apparently you can't use WIFxxx macros properly." >&4 fi $rm -f foo.* foo core ;; esac : determine default editor echo " " case "$defeditor" in '') case "$vi" in */*) dflt="$vi";; *) dflt=/usr/ucb/vi;; esac ;; *) dflt="$defeditor" ;; esac fn=f/ rp="What is the default editor on your system?" . ./getfile defeditor="$ans" : see if this is a dirent system echo " " if xinc=`./findhdr dirent.h`; $test "$xinc"; then val="$define" echo " found." >&4 else val="$undef" if xinc=`./findhdr sys/dir.h`; $test "$xinc"; then echo " found." >&4 echo " " else xinc=`./findhdr sys/ndir.h` fi echo " NOT found." >&4 fi set i_dirent eval $setvar : Look for type of directory structure. echo " " $cppstdin $cppflags $cppminus < "$xinc" > try.c case "$direntrytype" in ''|' ') case "$i_dirent" in $define) guess1='struct dirent' ;; *) guess1='struct direct' ;; esac ;; *) guess1="$direntrytype" ;; esac case "$guess1" in 'struct dirent') guess2='struct direct' ;; *) guess2='struct dirent' ;; esac if $contains "$guess1" try.c >/dev/null 2>&1; then direntrytype="$guess1" echo "Your directory entries are $direntrytype." >&4 elif $contains "$guess2" try.c >/dev/null 2>&1; then direntrytype="$guess2" echo "Your directory entries seem to be $direntrytype." >&4 else echo "I don't recognize your system's directory entries." >&4 rp="What type is used for directory entries on this system?" dflt="$guess1" . ./myread direntrytype="$ans" fi $rm -f try.c : must not allow self reference case "$privlib" in /*) filexp=$privlib/filexp ;; *) filexp=Rnfilexp ;; esac : determine the name of a reasonable mailer case "$mailer" in '') if $test -f "$sendmail"; then dflt="$sendmail" elif $test -f "$smail"; then dflt="$smail" elif $test -f "$rmail"; then dflt="$rmail" elif $test -f /bin/mail; then dflt=/bin/mail else dflt=$mail fi ;; *) dflt="$mailer";; esac $cat <&4 ;; esac : see if signal is declared as pointer to function returning int or void echo " " xxx=`./findhdr signal.h` $test "$xxx" && $cppstdin $cppminus $cppflags < $xxx >$$.tmp 2>/dev/null if $contains 'int.*\*[ ]*signal' $$.tmp >/dev/null 2>&1 ; then echo "You have int (*signal())() instead of void." >&4 val="$undef" elif $contains 'void.*\*[ ]*signal' $$.tmp >/dev/null 2>&1 ; then echo "You have void (*signal())()." >&4 val="$define" elif $contains 'extern[ ]*[(\*]*signal' $$.tmp >/dev/null 2>&1 ; then echo "You have int (*signal())() instead of void." >&4 val="$undef" elif $contains 'void.*\*.*sig' $$.tmp >/dev/null 2>&1 ; then echo "You have void (*signal())()." >&4 val="$define" else case "$d_voidsig" in '') echo "I can't determine whether signal handler returns void or int..." >&4 dflt=void rp="What type does your signal handler return?" . ./myread case "$ans" in v*) val="$define";; *) val="$undef";; esac;; "$define") echo "As you already told me, signal handler returns void." >&4 val="$define" ;; *) echo "As you already told me, signal handler returns int." >&4 val="$undef" ;; esac fi set d_voidsig eval $setvar case "$d_voidsig" in "$define") signal_t="void";; *) signal_t="int";; esac $rm -f $$.tmp : determine compiler compiler case "$yacc" in '') dflt=yacc;; *) dflt="$yacc";; esac echo " " comp='yacc' if $test -f "$byacc"; then dflt="$byacc" comp="byacc or $comp" fi if $test -f "$bison"; then comp="$comp or bison -y" fi rp="Which compiler compiler ($comp) shall I use?" . ./myread yacc="$ans" case "$yacc" in *bis*) case "$yacc" in *-y*) ;; *) yacc="$yacc -y" echo "(Adding -y option to bison to get yacc-compatible behaviour.)" ;; esac ;; esac : see if ptem.h exists if $test -r /usr/include/sys/ptem.h ; then $echo "sys/ptem.h found." i_ptem="$define" else i_ptem="$undef" fi : get C preprocessor symbols handy echo " " $echo $n "Hmm... $c" echo $al | $tr ' ' $trnl >Cppsym.know $cat <Cppsym $startsh case "\$1" in -l) list=true shift ;; esac unknown='' case "\$list\$#" in 1|2) for sym do if $contains "^\$1$" Cppsym.true >/dev/null 2>&1; then exit 0 elif $contains "^\$1$" Cppsym.know >/dev/null 2>&1; then : else unknown="\$unknown \$sym" fi done set X \$unknown shift ;; esac case \$# in 0) exit 1;; esac echo \$* | $tr ' ' '$trnl' | $sed -e 's/\(.*\)/\\ #ifdef \1\\ exit 0; _ _ _ _\1\\ \1\\ #endif\\ /' >Cppsym\$\$ echo "exit 1; _ _ _" >>Cppsym\$\$ $cppstdin $cppminus Cppsym2\$\$ case "\$list" in true) $awk 'NF > 5 {print substr(\$6,2,100)}' Cppsym.true : now check the C compiler for additional symbols $cat >ccsym <tmp.c <&1\` do case "\$i" in -D*) echo "\$i" | $sed 's/^-D//';; -A*) $test "$gccversion" && echo "\$i" | $sed 's/^-A\(.*\)(\(.*\))/\1=\2/';; esac done $rm -f try.c EOS chmod +x ccsym $eunicefix ccsym ./ccsym > ccsym1.raw if $test -s ccsym1.raw; then $sort ccsym1.raw | $uniq >ccsym.raw else mv ccsym1.raw ccsym.raw fi $awk '/\=/ { print $0; next } { print $0"=1" }' ccsym.raw >ccsym.list $awk '{ print $0"=1" }' Cppsym.true >ccsym.true $comm -13 ccsym.true ccsym.list >ccsym.own $comm -12 ccsym.true ccsym.list >ccsym.com $comm -23 ccsym.true ccsym.list >ccsym.cpp also='' symbols='symbols' if $test -z ccsym.raw; then echo "Your C compiler doesn't seem to define any symbol!" >&4 echo " " echo "However, your C preprocessor defines the following ones:" $cat Cppsym.true else if $test -s ccsym.com; then echo "Your C compiler and pre-processor define these symbols:" $sed -e 's/\(.*\)=.*/\1/' ccsym.com also='also ' symbols='ones' $test "$silent" || sleep 1 fi if $test -s ccsym.cpp; then $test "$also" && echo " " echo "Your C pre-processor ${also}defines the following $symbols:" $sed -e 's/\(.*\)=.*/\1/' ccsym.cpp also='further ' $test "$silent" || sleep 1 fi if $test -s ccsym.own; then $test "$also" && echo " " echo "Your C compiler ${also}defines the following cpp variables:" $sed -e 's/\(.*\)=1/\1/' ccsym.own $sed -e 's/\(.*\)=.*/\1/' ccsym.own | $uniq >>Cppsym.true $test "$silent" || sleep 1 fi fi $rm -f ccsym* : see if this is a termio system val="$undef" val2="$undef" val3="$undef" if $test `./findhdr termios.h`; then set tcsetattr i_termios eval $inlibc val3="$i_termios" fi echo " " case "$val3" in "$define") echo "You have POSIX termios.h... good!" >&4;; *) if ./Cppsym pyr; then case "`/bin/universe`" in ucb) if $test `./findhdr sgtty.h`; then val2="$define" echo " found." >&4 else echo "System is pyramid with BSD universe." echo " not found--you could have problems." >&4 fi;; *) if $test `./findhdr termio.h`; then val="$define" echo " found." >&4 else echo "System is pyramid with USG universe." echo " not found--you could have problems." >&4 fi;; esac elif ./usg; then if $test `./findhdr termio.h`; then echo " found." >&4 val="$define" elif $test `./findhdr sgtty.h`; then echo " found." >&4 val2="$define" else echo "Neither nor found--you could have problems." >&4 fi else if $test `./findhdr sgtty.h`; then echo " found." >&4 val2="$define" elif $test `./findhdr termio.h`; then echo " found." >&4 val="$define" else echo "Neither nor found--you could have problems." >&4 fi fi;; esac set i_termio; eval $setvar val=$val2; set i_sgtty; eval $setvar val=$val3; set i_termios; eval $setvar : define an alternate in-header-list? function inhdr='echo " "; td=$define; tu=$undef; yyy=$@; cont=true; xxf="echo \"<\$1> found.\" >&4"; case $# in 2) xxnf="echo \"<\$1> NOT found.\" >&4";; *) xxnf="echo \"<\$1> NOT found, ...\" >&4";; esac; case $# in 4) instead=instead;; *) instead="at last";; esac; while $test "$cont"; do xxx=`./findhdr $1` var=$2; eval "was=\$$2"; if $test "$xxx" && $test -r "$xxx"; then eval $xxf; eval "case \"\$$var\" in $undef) . ./whoa; esac"; eval "$var=\$td"; cont=""; else eval $xxnf; eval "case \"\$$var\" in $define) . ./whoa; esac"; eval "$var=\$tu"; fi; set $yyy; shift; shift; yyy=$@; case $# in 0) cont="";; 2) xxf="echo \"but I found <\$1> $instead.\" >&4"; xxnf="echo \"and I did not find <\$1> either.\" >&4";; *) xxf="echo \"but I found <\$1\> instead.\" >&4"; xxnf="echo \"there is no <\$1>, ...\" >&4";; esac; done; while $test "$yyy"; do set $yyy; var=$2; eval "was=\$$2"; eval "case \"\$$var\" in $define) . ./whoa; esac"; eval "$var=\$tu"; set $yyy; shift; shift; yyy=$@; done' : see if stdlib is available set stdlib.h i_stdlib eval $inhdr : see if this is an sysdir system set sys/dir.h i_sysdir eval $inhdr : see if ioctl defs are in sgtty, termio, sys/filio or sys/ioctl set sys/filio.h i_sysfilio eval $inhdr echo " " if $test `./findhdr sys/ioctl.h`; then val="$define" echo ' found.' >&4 else val="$undef" if $test $i_sysfilio = "$define"; then echo ' NOT found.' >&4 else $test $i_sgtty = "$define" && xxx="sgtty.h" $test $i_termio = "$define" && xxx="termio.h" $test $i_termios = "$define" && xxx="termios.h" echo "No found, assuming ioctl args are defined in <$xxx>." >&4 fi fi set i_sysioctl eval $setvar : see if this is an sysndir system set sys/ndir.h i_sysndir eval $inhdr : see if we should include time.h, sys/time.h, or both echo " " echo "Testing to see if we should include , or both." >&4 $echo $n "I'm now running the test program...$c" $cat >try.c <<'EOCP' #include #ifdef I_TIME #include #endif #ifdef I_SYSTIME #include #endif #ifdef I_SYSSELECT #include #endif main() { struct tm foo; #ifdef S_TIMEVAL struct timeval bar; #endif #ifdef S_TIMEZONE struct timezone tzp; #endif if (foo.tm_sec == foo.tm_sec) exit(0); #ifdef S_TIMEVAL if (bar.tv_sec == bar.tv_sec) exit(0); #endif exit(1); } EOCP flags='' for s_timezone in '-DS_TIMEZONE' ''; do sysselect='' for s_timeval in '-DS_TIMEVAL' ''; do for i_time in '' '-DI_TIME'; do for i_systime in '-DI_SYSTIME' ''; do case "$flags" in '') $echo $n ".$c" if $cc $ccflags \ $i_time $i_systime $sysselect $s_timeval $s_timezone \ try.c -o try >/dev/null 2>&1 ; then set X $i_time $i_systime $sysselect $s_timeval shift flags="$*" echo " " $echo $n "Succeeded with $flags$c" fi ;; esac done done done done echo " " case "$flags" in *I_TIME*) i_time="$define" echo "We'll include ." >&4;; *) i_time="$undef";; esac case "$flags" in *I_SYSTIME*) i_systime="$define" echo "We'll include ." >&4;; *) i_systime="$undef";; esac $rm -f try.c try : see if this is a syswait system set sys/wait.h i_syswait eval $inhdr : see if this is a unistd.h system set unistd.h i_unistd eval $inhdr : see if this is an utime system set utime.h i_utime eval $inhdr : see if this is a vfork system case "$d_vfork" in "$define") set vfork.h i_vfork eval $inhdr ;; *) i_vfork="$undef" ;; esac : end of configuration questions echo " " echo "End of configuration questions." echo " " : back to where it started if test -d ../UU; then cd .. fi : configuration may be patched via a 'config.over' file if $test -f config.over; then echo " " dflt=y rp='I see a config.over file. Do you wish to load it?' . UU/myread case "$ans" in n*) echo "OK, I'll ignore it.";; *) . ./config.over echo "Configuration override changes have been loaded." ;; esac fi : in case they want portability, strip down executable paths case "$d_portable" in "$define") echo " " echo "Stripping down executable paths..." >&4 for file in $loclist $trylist; do if test X$file != Xln -a X$file != Xar -o X$osname != Xos2; then eval $file="\$file" fi done (CONFIG=true ; . $src/filexp.SH) >/dev/null ;; esac : create config.sh file echo " " echo "Creating config.sh..." >&4 $spitshell <config.sh $startsh # # This file was produced by running the Configure script. It holds all the # definitions figured out by Configure. Should you modify one of these values, # do not forget to propagate your changes by running "Configure -der". You may # instead choose to run each of the .SH files by yourself, or "Configure -S". # # Package name : $package # Source directory : $src # Configuration time: $cf_time # Configured by : $cf_by # Target system : $myuname Id='$Id' Log='$Log' Mcc='$Mcc' _a='$_a' _exe='$_exe' _o='$_o' active='$active' acttimes='$acttimes' aphostcmd='$aphostcmd' archobjs='$archobjs' awk='$awk' basename='$basename' bash='$bash' bin='$bin' binexp='$binexp' byacc='$byacc' c='$c' cat='$cat' cc='$cc' ccflags='$ccflags' cf_by='$cf_by' cf_time='$cf_time' citydist='$citydist' cntrydist='$cntrydist' comm='$comm' contains='$contains' contdist='$contdist' cp='$cp' cpp='$cpp' cppflags='$cppflags' cpplast='$cpplast' cppminus='$cppminus' cpprun='$cpprun' cppstdin='$cppstdin' csh='$csh' d_acttimes='$d_acttimes' d_attribut='$d_attribut' d_berknames='$d_berknames' d_bsd='$d_bsd' d_dirnamlen='$d_dirnamlen' d_eunice='$d_eunice' d_ftime='$d_ftime' d_genauth='$d_genauth' d_getcwd='$d_getcwd' d_getdname='$d_getdname' d_gethname='$d_gethname' d_getpwent='$d_getpwent' d_gettimeod='$d_gettimeod' d_getwd='$d_getwd' d_gnulibc='$d_gnulibc' d_havetlib='$d_havetlib' d_ignoreorg='$d_ignoreorg' d_inews='$d_inews' d_internet='$d_internet' d_libndir='$d_libndir' d_local='$d_local' d_memcmp='$d_memcmp' d_memcpy='$d_memcpy' d_memset='$d_memset' d_mkdir='$d_mkdir' d_msdos='$d_msdos' d_newsadm='$d_newsadm' d_nntp='$d_nntp' d_nolnbuf='$d_nolnbuf' d_normsig='$d_normsig' d_novoid='$d_novoid' d_passnames='$d_passnames' d_phostcmd='$d_phostcmd' d_portable='$d_portable' d_rdchk='$d_rdchk' d_rename='$d_rename' d_resinit='$d_resinit' d_sigblock='$d_sigblock' d_sighold='$d_sighold' d_sizet='$d_sizet' d_strccmp='$d_strccmp' d_strchr='$d_strchr' d_strftime='$d_strftime' d_strstr='$d_strstr' d_uname='$d_uname' d_usendir='$d_usendir' d_usgnames='$d_usgnames' d_uwait3='$d_uwait3' d_uwait='$d_uwait' d_vfork='$d_vfork' d_voidsig='$d_voidsig' d_voidtty='$d_voidtty' d_wifstat='$d_wifstat' d_xenix='$d_xenix' d_xthread='$d_xthread' date='$date' defeditor='$defeditor' diff='$diff' direntrytype='$direntrytype' echo='$echo' ed='$ed' egrep='$egrep' eunicefix='$eunicefix' exe_ext='$exe_ext' expr='$expr' extrainews='$extrainews' filexp='$filexp' find='$find' firstmakefile='$firstmakefile' gccversion='$gccversion' glibpth='$glibpth' grep='$grep' groupdesc='$groupdesc' hint='$hint' hostbits='$hostbits' i_bsdioctl='$i_bsdioctl' i_dirent='$i_dirent' i_ptem='$i_ptem' i_sgtty='$i_sgtty' i_stdlib='$i_stdlib' i_string='$i_string' i_sysdir='$i_sysdir' i_sysfilio='$i_sysfilio' i_sysioctl='$i_sysioctl' i_sysndir='$i_sysndir' i_syssockio='$i_syssockio' i_systime='$i_systime' i_syswait='$i_syswait' i_termio='$i_termio' i_termios='$i_termios' i_time='$i_time' i_unistd='$i_unistd' i_utime='$i_utime' i_vfork='$i_vfork' incpath='$incpath' inews='$inews' installbin='$installbin' installinews='$installinews' installmansrc='$installmansrc' installprivlib='$installprivlib' ispell='$ispell' ispell_options='$ispell_options' ispell_prg='$ispell_prg' jobslib='$jobslib' ksh='$ksh' ldflags='$ldflags' less='$less' lib_ext='$lib_ext' libc='$libc' libndir='$libndir' libpth='$libpth' libs='$libs' libswanted='$libswanted' lint='$lint' lns='$lns' locdist='$locdist' locincpth='$locincpth' loclibpth='$loclibpth' mail='$mail' mailer='$mailer' mailfile='$mailfile' manext='$manext' mansrc='$mansrc' mansrcexp='$mansrcexp' mboxchar='$mboxchar' mimecap='$mimecap' mips='$mips' mips_type='$mips_type' mkdir='$mkdir' more='$more' multistatedist='$multistatedist' mv='$mv' myactive='$myactive' myuname='$myuname' n='$n' nametype='$nametype' ndirc='$ndirc' ndiro='$ndiro' newsadmin='$newsadmin' newslib='$newslib' newslibexp='$newslibexp' newsspool='$newsspool' nm='$nm' nm_opt='$nm_opt' nm_so_opt='$nm_so_opt' nroff='$nroff' obj_ext='$obj_ext' optimize='$optimize' orgdist='$orgdist' orgname='$orgname' osname='$osname' osvers='$osvers' overviewdir='$overviewdir' overviewfmt='$overviewfmt' package='$package' pager='$pager' path_sep='$path_sep' perl='$perl' perlpath='$perlpath' pg='$pg' pgp='$pgp' phost='$phost' phostcmd='$phostcmd' plibpth='$plibpth' prefix='$prefix' prefixexp='$prefixexp' prefshell='$prefshell' privlib='$privlib' privlibexp='$privlibexp' rm='$rm' rmail='$rmail' rootid='$rootid' runnm='$runnm' sed='$sed' sendmail='$sendmail' servername='$servername' sharpbang='$sharpbang' shsharp='$shsharp' signal_t='$signal_t' smail='$smail' so='$so' sort='$sort' spackage='$spackage' spell='$spell' spitshell='$spitshell' src='$src' startsh='$startsh' statedist='$statedist' strftimec='$strftimec' strftimeo='$strftimeo' strings='$strings' strn='$strn' subscriptions='$subscriptions' sysman='$sysman' tail='$tail' tee='$tee' termlib='$termlib' test='$test' threaddir='$threaddir' tk='$tk' tkinc='$tkinc' tklibs='$tklibs' tr='$tr' trn_init='$trn_init' trn_select='$trn_select' trnl='$trnl' uname='$uname' uniq='$uniq' useinews='$useinews' usenm='$usenm' usevfork='$usevfork' usrinc='$usrinc' uuname='$uuname' vi='$vi' void='$void' vspell='$vspell' who='$who' xlibpth='$xlibpth' yacc='$yacc' yaccflags='$yaccflags' EOT : add special variables $test -f $src/patchlevel.h && \ awk '/^#define/ {printf "%s=%s%s %s %s %s\n",$2,$3,$4,$5,$6,$7}' $src/patchlevel.h >>config.sh echo "CONFIG=true" >>config.sh : preserve RCS keywords in files with variable substitution, grrr Id='$Id' : Finish up by extracting the .SH files case "$alldone" in exit) $rm -rf UU echo "Done." exit 0 ;; cont) ;; '') dflt='' nostick=true $cat <&4 -c "$ans";; esac ;; esac : if this fails, just run all the .SH files by hand . ./config.sh echo " " exec 1>&4 . ./UU/extract $cat < while running trn). Personal defaults are saved in the .trn/trnrc file when you 'S'ave from the online options editor. Any options you find that you want to make the default for everyone can be placed into the INIT file in the trn library (copy them out of your ./trn/trnrc file). 9) Once trn is running ok, make sure any database software you've installed is going ok and interfacing properly with trn. Read the documentation that comes with the package of your choosing. 10) IMPORTANT! Help save the world! Communicate any problems and suggested patches to trn-workers@lists.sourceforge.net so we can keep the world in sync. If you have a problem, there's someone else out there who either has had or will have the same problem. If the problem affects regular rn, code, I will pass it on to Stan Barber. If possible, send in patches such that the patch program will apply them. Unified or regular context diffs are the best, then normal diffs. Don't send ed scripts--I've probably changed my copy since the version you have. Watch for trn patches in news.software.readers. Patches will always be in the unified context diff format, for application by the patch program. If you don't have a patch program that handles unified context diffs, you'll probably want to get one, such as patch version 2.1 available at any GNU archive. Otherwise, you can use the (included) filter "unipatch", which can be generated with the command "make unipatch". To apply patches using this filter, use the command: unipatch Makefile <>Makefile <<'!NO!SUBS!' public_backup = trn$(exe) trn-artchk$(exe) $(nntpexe) $(msdosexe) public_diff = Pnews Rnmail public = $(public_backup) $(public_diff) private_preserve = access.def private_diff = newsnews Speller Pnews.header private_blast = norm.saver mbox.saver makedir filexp private = $(private_preserve) $(private_diff) $(private_blast) util = makedepend mkpro mktd HelpFiles/config/environment manpages = trn.1 Pnews.1 Rnmail.1 #INEWSinewsexe=inews$(exe) #NNTPnntpexe=nntplist$(exe) #NNTPnntpsrc=nntpinit.c nntpclient.c nntpauth.c nntp.c #NNTPnntpobj1=nntpinit.$(o) nntpclient.$(o) nntpauth.$(o) #NNTPnntpobj2=nntp.$(o) #MSDOSmsdosexe=winsock.dll #MSDOSmsdossrc=popen.c #MSDOSmsdosobj=popen.$(o) h1 = addng.h art.h artio.h artsrch.h autosub.h backpage.h bits.h cache.h h2 = charsubst.h datasrc.h common.h decode.h env.h final.h hash.h head.h h3 = help.h init.h intrp.h kfile.h last.h list.h mime.h ng.h ngdata.h ngsrch.h h4 = ngstuff.h only.h opt.h rcln.h rcstuff.h respond.h rthread.h rt-mt.h h5 = rt-ov.h rt-page.h rt-process.h rt-select.h rt-util.h rt-wumpus.h h6 = search.h sw.h term.h trn.h util.h util2.h wildmat.h color.h filter.h h7a = scan.h scmd.h sdisp.h smisc.h sorder.h spage.h h7b = scanart.h samain.h samisc.h sadisp.h sacmd.h sadesc.h sathread.h h7c = url.h mempool.h univ.h h7all = $(h7a) $(h7b) $(h7c) h8 = score.h scorefile.h scoresave.h score-easy.h h9 = tkstuff.h tktree.h h = $(h1) $(h2) $(h3) $(h4) $(h5) $(h6) $(h7all) $(h8) $(h9) c1 = addng.c art.c artio.c artsrch.c autosub.c backpage.c bits.c cache.c c2 = charsubst.c datasrc.c decode.c edit_dist.c env.c final.c hash.c head.c c3 = help.c init.c intrp.c kfile.c last.c list.c $(ndirc) mime.c ng.c ngdata.c c4 = ngsrch.c ngstuff.c only.c opt.c rcln.c rcstuff.c respond.c rthread.c c5 = rt-mt.c rt-ov.c rt-process.c rt-page.c rt-select.c rt-util.c rt-wumpus.c c6 = search.c $(strftimec) sw.c term.c trn.c util.c util2.c c7 = uudecode.c $(nntpsrc) $(msdossrc) wildmat.c color.c filter.c c8a = scan.c scmd.c sdisp.c smisc.c sorder.c spage.c c8b = scanart.c samain.c samisc.c sadisp.c sacmd.c sadesc.c sathread.c c8c = url.c mempool.c univ.c c8all = $(c8a) $(c8b) $(c8c) c9 = score.c scorefile.c scoresave.c score-easy.c c10 = tkstuff.c tktree.c c = $(c1) $(c2) $(c3) $(c4) $(c5) $(c6) $(c7) $(c8all) $(c9) $(c10) obj1 = addng.$(o) art.$(o) artio.$(o) artsrch.$(o) autosub.$(o) backpage.$(o) obj2 = bits.$(o) cache.$(o) charsubst.$(o) datasrc.$(o) decode.$(o) obj3 = edit_dist.$(o) env.$(o) final.$(o) hash.$(o) head.$(o) help.$(o) obj4 = init.$(o) intrp.$(o) kfile.$(o) last.$(o) list.$(o) $(ndiro) mime.$(o) ng.$(o) obj5 = ngdata.$(o) ngsrch.$(o) ngstuff.$(o) only.$(o) opt.$(o) rcln.$(o) obj6 = rcstuff.$(o) respond.$(o) rthread.$(o) rt-mt.$(o) rt-ov.$(o) obj7 = rt-process.$(o) rt-page.$(o) rt-select.$(o) rt-util.$(o) rt-wumpus.$(o) obj8 = search.$(o) $(strftimeo) sw.$(o) term.$(o) trn.$(o) util.$(o) util2.$(o) obj9 = uudecode.$(o) parsedate.$(o) $(nntpobj1) $(nntpobj2) obj10= $(msdosobj) wildmat.$(o) color.$(o) filter.$(o) obj11a = scan.$(o) scmd.$(o) sdisp.$(o) smisc.$(o) sorder.$(o) spage.$(o) obj11b = scanart.$(o) samain.$(o) samisc.$(o) sadisp.$(o) sacmd.$(o) sadesc.$(o) sathread.$(o) obj11c = url.$(o) mempool.$(o) univ.$(o) obj11all = $(obj11a) $(obj11b) $(obj11c) obj12 = score.$(o) scorefile.$(o) scoresave.$(o) score-easy.$(o) obj13 = tkstuff.$(o) tktree.$(o) obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5) $(obj6) $(obj7) $(obj8) \ $(obj9) $(obj10) $(obj11all) $(obj12) $(obj13) addedbymake = $(public) $(inewsexe) $(private) $(util)\ Makefile.old config.h parsedate.c cppstdin myinstall all # grrr SHELL = /bin/sh .c.$(o): $(CC) -c $(CFLAGS) $< all: Makefile $(public) $(inewsexe) $(private) $(util) Policy.sh $(TOUCH) all #MSDOS #MSDOStrn: trn.exe trn$(exe): $(obj) #MSDOS $(LINK) $(LINKFLAGS) @trn.lnk #MSDOS #MSDOSjunk1: $(CC) $(LDFLAGS) $(TKLDFLAGS) $(obj) $(tklibs) $(libs) -o trn$(exe) #NNTP #NNTPnntpinit.$(o): nntpinit.c #NNTP $(CC) -c $(CFLAGS) $(NNTPFLAGS) $< #NNTP#MSDOS #NNTP#MSDOSnntplist: nntplist.exe #NNTP #NNTPnntplistobjs = nntplist.$(o) $(nntpobj1) util2.$(o) util3.$(o) \ #NNTP wildmat.$(o) #NNTP #NNTPnntplist$(exe): $(nntplistobjs) #NNTP#MSDOS $(LINK) $(LINKFLAGS) @nntplist.lnk #NNTP#MSDOS #NNTP#MSDOSjunk2: #NNTP $(CC) $(LDFLAGS) $(nntplistobjs) -o nntplist$(exe) $(libs) #INEWS#MSDOS #INEWS#MSDOSinews: inews.exe #INEWS #INEWSinewsobjs = inews.$(o) $(nntpobj1) env.$(o) util2.$(o) util3.$(o) #INEWS #INEWSinews$(exe): $(inewsobjs) #INEWS#MSDOS $(LINK) $(LINKFLAGS) @inews.lnk #INEWS#MSDOS #INEWS#MSDOSjunk3: #INEWS $(CC) $(LDFLAGS) $(inewsobjs) -o inews$(exe) $(libs) #MSDOStrn-artchk: trn-artchk.exe #MSDOS trnchkobjs = trn-artchk.$(o) $(nntpobj1) util2.$(o) util3.$(o) trn-artchk$(exe): $(trnchkobjs) #MSDOS $(LINK) $(LINKFLAGS) @trn-artchk.lnk #MSDOS #MSDOSjunk4: $(CC) $(LDFLAGS) $(trnchkobjs) -o trn-artchk$(exe) $(libs) #MSDOS #MSDOSwinsock.dll: winsock.c winsock.def #MSDOS $(CC) -c -WXDE -ml -w-par winsock.c #MSDOS $(LINK) /c -v -Twd -Lc:\bc4\lib @winsock.lnk tkstuff.o: tkstuff.c $(CC) -c $(CFLAGS) $(TKINC) $< tktree.o: tktree.c $(CC) -c $(CFLAGS) $(TKINC) $< parsedate.c: parsedate.y @echo 'Expect 6 shift/reduce conflicts' $(YACC) $(srcdir)/parsedate.y mv -f y.tab.c parsedate.c #MSDOS #MSDOSparsedate.$(o): parsedate.c #MSDOS $(CC) -c $(CFLAGS) -w- $< #STRFTIME #STRFTIME$(strftimeo): $(strftimec) #STRFTIME $(CC) -c $(CFLAGS) $(strftimec) #MSDOSunipatch: unipatch.exe #MSDOS unipatch$(exe): support/unipatch.$(o) #MSDOS $(CC) $(LDFLAGS) support/unipatch.$(o) #MSDOS #MSDOSjunk5: $(CC) $(LDFLAGS) support/unipatch.$(o) -o unipatch$(exe) Pnews.header: Pnews # if a .h file depends on another .h file... $(h): -$(TOUCH) $@ install: $(public) $(inewsexe) $(private) $(manpages) myinstall # won't work with csh export PATH || false # stop the make unless we are compatible - ./makedir `./filexp $(rnbin)` ./myinstall -sox `./filexp $(rnbin)` $(public_backup) ./myinstall -dox `./filexp $(rnbin)` $(public_diff) - if test $(installfilexp) = Rnfilexp; then\ ./myinstall -xf Rnfilexp `./filexp $(rnbin)` ./filexp; fi - ./makedir `./filexp $(rnlib)` - chmod o+r `./filexp $(rnlib)` #INEWS ./myinstall -sox `./filexp $(inewsbin)` inews$(exe) ./myinstall -x `./filexp $(rnlib)` $(private_blast) - if test "X$(mansrc)" != X -a "X`pwd`" != X`./filexp $(mansrc)`; then\ ./makedir `./filexp $(mansrc)`;\ for page in $(manpages); do\ page=`basename $$page`;\ dest=$(mansrc)/`basename $$page .1`.$(manext);\ rm -f $$dest; cp $(srcdir)/$$page $$dest; chmod 444 $$dest;\ done;\ fi ./myinstall -dor `./filexp $(rnlib)` $(private_diff) ./myinstall -dnr `./filexp $(rnlib)` $(srcdir)/INIT $(private_preserve) ./myinstall -oR `./filexp $(rnlib)` `pwd`/HelpFiles @echo ' ' @echo 'Use "make installclean" to remove any .old or .new files that' @echo 'were just created, but only after you are sure that the files' @echo 'contain no interesting changes and/or no one is running the' @echo 'old executables.' installclean: - (cd `./filexp $(rnbin)` ; for file in $(public) $(private_diff) ; do\ rm -f $${file}.old;\ done) - (cd `./filexp $(rnlib)` ; for file in HelpFiles ; do\ rm -rf $${file}.old;\ done) - (cd `./filexp $(rnlib)` ; for file in INIT $(private_preserve) ; do\ rm -f $${file}.new;\ done) #INEWS - (cd `./filexp $(inewsbin)` ; rm -f inews$(exe).old) clean: @echo 'Use "make realclean" to also remove the Makefile.' @echo 'Use "make spotless" to even remove config.sh, Policy.sh, and .config.' rm -rf UU $(extra_cleanup) rm -f *.$(o) core $(addedbymake) realclean: @echo 'You can use "Configure -S" to reverse this.' rm -rf UU rm -f *.$(o) core $(addedbymake) Makefile spotless: rm -rf UU .config rm -f *.$(o) core $(addedbymake) Makefile config.sh Policy.sh # The following lint has practically everything turned on. Unfortunately, # you have to wade through a lot of mumbo jumbo that can't be suppressed. # If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message # for that spot. lint: $(c) parsedate.c $(LINT) $(lintflags) $(defs) $? > trn.fuzz 2>&1 sabertrn: $(c) parsedate.c #load $(c) parsedate.c $(libs) depend: config.h Makefile makedepend ./makedepend pro: config.h mkpro ./mkpro td: config.h mktd ./mktd Makefile: Makefile.SH config.sh dependencies /bin/sh $(srcdir)/Makefile.SH @echo "The Makefile has changed -- please restart the make." false # stop the make Pnews: patchlevel.h newsnews: patchlevel.h tar: MANIFEST patchlevel.h export PATH || false # stop the make unless we are compatible @(ver=trn-`awk '{print $$4}' $(srcdir)/patchlevel.h` ; \ cd $(srcdir)/.. ; \ echo Creating $$ver.tar.gz ; \ ln -s trn4 $$ver ; \ tar cvzf $$ver.tar.gz \ `ls -1d trn4/hints/* | fgrep -v /CVS | \ cat trn4/MANIFEST - | \ sed -e "s:^\(trn4/\)*\([^ ]*\).*:$$ver/\2:"` ; \ rm $$ver ) # AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE !NO!SUBS! if test -f dependencies; then $cat dependencies >>Makefile else $cat $src/dependencies >>Makefile fi case "$d_nntp" in define) sedcmd="-e '/^#NNTP/s/^#NNTP//'" ;; *) sedcmd="-e '/^#NNTP/d'" ;; esac case "$d_inews" in define) sedcmd="$sedcmd -e '/^#INEWS/s/^#INEWS//'" ;; *) sedcmd="$sedcmd -e '/^#INEWS/d'" ;; esac case "$d_msdos" in define) sedcmd="$sedcmd -e '/^#MSDOS/s/^#MSDOS//'" ;; *) sedcmd="$sedcmd -e '/^#MSDOS/d'" ;; esac case "$strftimec" in ''|' ') sedcmd="$sedcmd -e '/^#STRFTIME/d'" ;; *) sedcmd="$sedcmd -e '/^#STRFTIME/s/^#STRFTIME//'" ;; esac eval "sed Makefile.new" mv Makefile.new Makefile $eunicefix Makefile trn-4.0-test77/Pnews.10000644000000000000000000000664507113613623013204 0ustar rootroot.\" Pnews.1 .\" .\" This software is copyrighted as detailed in the LICENSE file. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH PNEWS 1 LOCAL .SH NAME Pnews - a program for posting news articles .SH SYNOPSIS .B Pnews newsgroup title .br or .br .B Pnews -h headerfile [oldarticle] .br or .br .B Pnews .SH DESCRIPTION Pnews is a friendly interface for posting news articles. It will ask several questions, then allow you to enter your article, and then post it using the inews(1) program. If you type h and a carriage return at any point, .I Pnews will tell you what it wants to know. .PP If you put a .I .signature file in your home directory, your inews program will usually append it to your message after you are done editing it. If you prefer to see your signature while you are editing, rename your \&.signature file to be .news_sig and it will be appended before you begin to edit. Note that .news_sig obeys the setting of DOTDIR, but .signature may not, since it is out of Pnews's control. If both .news_sig and .signature exist you'll get two signatures appended. .PP The -h form is used when invoked from .I trn or .IR rn . If your editor can edit multiple files, and you want the article to which you are replying to show up as an alternate file, define the environment variable NEWSPOSTER as \*(L"Pnews -h %h %A\*(R". You can also modify the the NEWSHEADER environment variable to change the header file that .I [t]rn passes to Pnews. .SH ENVIRONMENT .IP AUTHORCOPY 8 If defined, contains the name of a file to which the finished article will be appended. .Sp Default: article not saved .IP DOTDIR 8 Where to find your dot files, if they aren't in your home directory. This is primarily for accounts which are shared by more than one person. .Sp Default: $HOME .IP EDITOR 8 The editor you want to use, if VISUAL is undefined. .Sp Default: whatever your news administrator installed, usually vi. .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .IP LOGNAME 8 Your login name, if USER is undefined. .Sp Default: value of \*(L"whoami\*(R". .IP NAME 8 Your full name. .Sp Default: name from /etc/passwd, or ~/.fullname. .IP NEWSORG 8 Either the name of your organization, or the name of a file containing the name of your organization. (For use at organizations where the ORGANIZATION environmental variable is in use for other purposes). If both NEWSORG and ORGANIZATION are set, NEWSORG will override ORGANIZATION. .IP ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. .Sp Default: whatever your news administrator chose. .IP REPLYTO 8 The contents of a \*(L"Reply-To:\*(R" header line to insert into your message. .Sp Default: header line not inserted. .IP USER 8 Your login name. .Sp Default: $LOGNAME .IP VISUAL 8 The editor you want to use. .Sp Default: $EDITOR .SH FILES $DOTDIR/.article .br $DOTDIR/.news_sig .br ~/dead.article .SH SEE ALSO trn(1), rn(1), Rnmail(1), inews(1) .SH DIAGNOSTICS .SH BUGS Not the speediest program in the world, but maybe that's a blessing to the net. trn-4.0-test77/Pnews.SH0000644000000000000000000005166511437640112013355 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac trnversion=`sed -e 's/.*"\(.*\)\"/\1/' $src/patchlevel.h 2>/dev/null` 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!' homedir=${HOME-$LOGDIR} dotdir=${DOTDIR-$homedir} tmpartcopy=$dotdir/.article tmpart=$tmpartcopy.$$ speller=$rnlib/Speller exitval=0 # see if PGP exists pgp=${PGPCOMMAND-$pgp} $test -n "$pgp" && pgpprompt=" PGP," pgpsearch='^-----BEGIN PGP SIG' tmp="${TMPDIR-/tmp}" case "$NEWSACTIVE" in '') ;; *) active="$NEWSACTIVE";; esac case "$active" in none|'') active="$tmp/Pnact.$$";; esac case "$NEWSDESCRIPTIONS" in '') ;; *) groupdesc="$NEWSDESCRIPTIONS";; esac case "$groupdesc" in none|'') groupdesc="$tmp/Pndesc.$$";; esac news_sig=${NEWSSIGNATURE-$dotdir/.news_sig} case "$NETSPEED" in [0-4]) ;; [1-9]*|f*) if $test ! -f "$groupdesc"; then groupdesc="$tmp/Pndesc.$$" rmlist="$groupdesc" cmdlist="$nntplist -o $groupdesc newsgroups ;" fi if $test ! -f "$active"; then active="$tmp/Pnact.$$" rmlist="$rmlist $active" cmdlist="$cmdlist $nntplist -o $active active" fi ;; esac if $test -f $dotdir/.pnewsexpert -o "X$FAST_PNEWS" != X ; 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 ;; *) $cp $headerfile $tmpart ;; esac rescue="$sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article; $echo Article appended to ${HOME-$LOGDIR}/dead.article; exit 0" trap "$cp $tmpart $tmpartcopy; $rm -f $rmlist $tmpart*; exit \$exitval" 0 trap "trap : 1; $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) $cat <<'EOM' The original author has requested that messages be sent back via mail rather than posting to news. Do you want to jump out of this and EOM $echo $n "mail your reply instead? [yn] $c" read ans case $ans in n*) $sed '1,/^[ ]*$/{/^Newsgroups:/d; s/^X-ORIGINAL-NEWSGROUPS:/Newsgroups:/;}' $tmpart >$tmpart.tmp $mv $tmpart.tmp $tmpart ;; *) exit 0 ;; esac $echo " " $echo "The Cc: line should have the poster's requested return address." ;; 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 case "$FAST_PNEWS" in y*) ;; *) # 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 "X$ans" = X ; 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 0 ;; esac done ;; esac # run nntplist in the background, if necessary if $test -n "$cmdlist"; then ( eval $cmdlist ) >/dev/null 2>&1 & fi # check for addresses in the .invalid TLD if $egrep -i '^Cc: .*\.invalid([][()<>@,;:\\". ]|$)' $tmpart >/dev/null 2>&1; then $cat <<'EOM' At least one of the addresses to which you are copying this article is marked as an invalid address, so you will not be able to send mail to it directly. You should look for clues in the previous article to find an address where the poster can receive mail. EOM fi case "$FAST_PNEWS" in y*) file='' $echo "" >> $tmpart state=edit ;; *) file=h;; esac while $test "X$file" = Xh ; do $echo "" $echo $n "Prepared file to include [none]: $c" read file || exit 0 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 "" if $test -r $news_sig; then $echo "-- " >> $tmpart $sed 4q $news_sig >> $tmpart fi while : ; 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 "X$tmp" = Xh ; do $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" read tmp || state=rescue # exits on EOF 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 if $test $state = edit ; then trap : 2 ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=check fi ;; check) # wait for possible background nntplist if $test -n "$cmdlist"; then wait cmdlist='' fi # warn about long lines, malformed headers, misspelled newsgroups ($artcheck $tmpart 79 $groupdesc $active) 2>/dev/null state=pgptest ;; ask) $echo "" $echo $n "Check spelling,$pgpprompt Send, Edit, List, or Abort? $c" read ans || state=rescue # exits on EOF case "$ans" in [aAqQ]*) state=rescue ;; [eE]*) set $ans case $# in 2) VISUAL="$2" ;; esac state=edit ;; [lL]*) $pager $tmpart state=ask ;; [cCiI]*|[sS][pP]*) $speller $tmpart state=pgptest ;; [pP]*) if $test -n "$pgpprompt"; then ans=`$echo $ans | $sed -e 's/^[pP] *//'` state=pgpask fi ;; [sS]*) state=send ;; [hH]*) $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 ;; '') ;; *) $echo "" $echo "Unknown command: $ans" ans='' ;; esac ;; pgpask) if test "X$ans" = X; then $echo "" $echo $n "Sign [user], Unsign, Test, or ? $c" read ans || state=rescue # exits on EOF fi case "$ans" in [sS]*) ans=`echo $ans | $sed -e 's/^[^ ]* *//'` case "$ans" in '') pgpsignopts='' ;; *) pgpsignopts="-u $ans" ;; esac state=pgpsign ;; [uU]*) state=pgpundo ;; [tT]*) state=pgptest ;; [aArR]*|'') state=ask ;; [hH]*) $cat <<'EOH' Type s to PGP-sign the article, u to unsign it, t to test the signature, or a to abort from this sub-menu. You can also specify the action from the previous prompt by using ps to PGP-sign, pu to unsign, etc. EOH ans='' ;; *) $echo "" $echo "Unknown command: $ans" ans='' ;; esac ;; pgptest) if $test -n "$pgpprompt"; then ans=`$grep "$pgpsearch" $tmpart` if $test "X$ans" != X; then $pgp +batchmode +verbose=0 -o $tmpart.tmp $tmpart $rm -f $tmpart.tmp else $echo "" $echo "Article is not PGP-signed." fi fi state=ask ;; pgpsign) $echo "" ans=`$grep "$pgpsearch" $tmpart` if $test "X$ans" != X; then $echo "Article is already signed." else $echo "Preparing PGP signature:" $sed -ne '1,/^[ ]*$/ p' $tmpart > $tmpart.head $sed -e '1,/^[ ]*$/ d' $tmpart | $pgp $pgpsignopts +verbose=0 -fast >$tmpart.tmp if $test -s $tmpart.tmp; then $cat $tmpart.head $tmpart.tmp >$tmpart $echo "" $echo "Article is now PGP-signed." fi $rm -f $tmpart.tmp fi state=ask ;; pgpundo) $echo "" ans=`$grep "$pgpsearch" $tmpart` if $test "X$ans" != X; then $sed -ne '1,/^[ ]*$/ p' $tmpart > $tmpart.head $sed -e '1,/^[ ]*$/ d' $tmpart | $pgp +verbose=0 -f >$tmpart.tmp 2>/dev/null if $test -s $tmpart.tmp; then $cat $tmpart.head $tmpart.tmp >$tmpart $echo "PGP signature removed from article." else $echo "Article was not changed." state=ask fi $rm -f $tmpart.tmp else $echo "Article is not PGP-signed." fi state=ask ;; send) set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'` shift case $# in 2) state=success headerstrip='1,/^[ ]*$/{/^[A-Z][-A-Za-z0-9]*:[ ]*$/d; /^X-ORIGINAL-NEWSGROUPS:/d; /^[Cc][Cc]:/d; /^Distribution: world/d; /^X-Newsreader:/d; /^X-Newsposter:/d; /^[ ]*$/i\ X-Newsreader: '$pnewsversion' }' case "$NNTPSERVER" in local|'') share_connection=no;; *) share_connection=$TRN_VERSION;; esac case "$share_connection" in 4.*) $sed "$headerstrip" $tmpart >$headerfile exitval=42 ;; *) NNTPFDS='' export NNTPFDS if $sed "$headerstrip" $tmpart | $inews -h ; then : null else state=ask fi ;; esac cc=`$sed -n '1,/^[ ]*$/{/^[Cc][Cc]:[ ][^ ]/p;}' $tmpart| $sed 's/^[Cc][Cc]:[ ][ ]*//'` if $test "X$cc" != X ; then case "$mailer" in *recmail) set X ;; *sendmail|*premail) set X -t ;; *) set X `echo $cc | $sed 's/,/ /g'` ;; esac shift ( case "$mailer" in *recmail|*sendmail|*premail) $echo To: $cc ;; esac $sed "$headerstrip" $tmpart | $sed '1,/^[ ]*$/s/^Newsgroups:/X-Also-Posted-To:/' if $test -f $homedir/.signature; then $echo "-- " $cat $homedir/.signature fi ) | $mailer $@ fi ;; *) $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 $tmpartcopy" else $echo "Null article discarded." fi exit 0 ;; success) case "${AUTHORCOPY-none}" in none) ;; *) set X ${USER-${LOGNAME-`$who am i`}} unknown shift ${POSTSAVER-$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 $exitval ;; esac done !NO!SUBS! $eunicefix Pnews chmod 755 Pnews $spitshell >Pnews.header <<'!NO!SUBS!' case $# in 0) ng=h while $test "X$ng" = Xh ; do $echo "" $echo $n "Newsgroup(s): $c" read ng || exit 0 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 "X$dist" = Xh ; 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 || exit 0 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 "X$title" = Xh ; do $echo "" $echo $n "Title/Subject: $c" read title || exit 0 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 "X$ypmatch" != X; then fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/"` elif $test "X$nidump" != X; then fullname=`$nidump passwd / | $sed -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"` fi if $test "X$fullname" = X; then fullname=`$sed /dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"` fi if $test "X$fullname" = X; 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 trn-4.0-test77/Policy_sh.SH0000644000000000000000000001046607141205160014201 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting Policy.sh (with variable substitutions)" $spitshell <Policy.sh $startsh # # This file was produced by running the Policy_sh.SH script, which # gets its values from config.sh, which is generally produced by # running Configure. # # login name of the person who configured trn (not particularly interesting). cf_by='$cf_by' # time of configuration (not particularly interesting). cf_time='$cf_time' # install directives. # The base of all our install directives prefix='$prefix' # bin directories (string values) # name of the final resting place bin='$bin' # how to get to the final resting place (thank you, AFS) installbin='$installbin' # private libraries # name of the final resting place for those items in the library # directory (string value) privlib='$privlib' # How to get to the library final resting place (thanks, AFS) installprivlib='$installprivlib' # interesting questions about man # where do man page sources go? mansrc='$mansrc' # what extention do man pages get? manext='$manext' # path to assorted programs that we might want to override. # name of the default editor. (string value) defeditor='$defeditor' # prefered user shell (string value) prefshell='$prefshell' # favorite local pager (string value) pager='$pager' # where is inews? (string value) d_inews='$d_inews' installinews='$installinews' useinews='$useinews' extrainews='$extrainews' # path to interactive speller or "none" (string value) ispell_prg='$ispell_prg' # spelling options for ispell_prg or "spell" if "none" (string value) ispell_options='$ispell_options' # internal options # ignore the ORGANIZATION environment variable? (define/undef) d_ignoreorg='$d_ignoreorg' # does the mailer understand FQDN addressing? (define/undef) d_internet='$d_internet' # do you have a news admin? (define/undef) d_newsadm='$d_newsadm' # name of the news admin? (string value) newsadmin='$newsadmin' # read via NNTP? (define/undef) d_nntp='$d_nntp' # use the XDATA NNTP extension? (define/undef) d_xdata='$d_xdata' # path to a file containing a server name, or a hostname (string value) servername='$servername' # distribution names (string values) # local city citydist='$citydist' # "local" country cntrydist='$cntrydist' # "local" continent contdist='$contdist' # site distribution locdist='$locdist' # organizational distribution orgdist='$orgdist' # state/province distribution name statedist='$statedist' # multistate region distribution name multistatedist='$multistatedist' # Naming information. # password file contains names (define/undef) d_passnames='$d_passnames' # berkeley style password entries (name first in GCOS) (define/undef) d_berknames='$d_berknames' # USG style password entries (account number first in GCOS) # (define/undef) d_usgnames='$d_usgnames' # what type of name to use.. (bsd/usg/other) nametype='$nametype' # How portable do we want to be? Determines if we do lookups now # or wait until run time. (define/undef) d_portable='$d_portable' # news library information # where is the news library (usually /usr/lib/news) may contain ~ newslib='$newslib' # absolute path name to /usr/lib/news. newslibexp='$newslibexp' # where is the news spool (usually /{var,usr}/spool/news) newsspool='$newsspool' # active file stuff, like where is it, what is its name, etc # path to the active file. (string value) active='$active' # do we have an active.times file? (define/undef) d_acttimes='$d_acttimes' # path to the active.times file. (string value) acttimes='$acttimes' # organizations name. path to file, or constant string orgname='$orgname' # only one of the two following is needed # command to find the posting hosts name (string value, optional) phostcmd='$phostcmd' # file containing posting hosts name or constant string # (string value, optional) # phost='$phost' # what should we use? mthreads or overview # use the mthreads format? (define/undef) d_usemt='$d_usemt' # where do we find the thread files? (string value) threaddir='$threaddir' # use the overview format? (define/undef) d_useov='$d_useov' # where do we find the .overview fils? (string value) overviewdir='$overviewdir' # trn start up options trn_init='$trn_init' # start up with the selector? trn_select='$trn_select' !GROK!THIS! $eunicefix Policy.sh # # This software is copyrighted as detailed in the LICENSE file. trn-4.0-test77/README0000644000000000000000000001030407141205556012674 0ustar rootroot Trn Kit, Version 4.0 Copyright (c) 1995, Wayne Davison Based on rn, Version 4.4 Copyright (c) 1985, Larry Wall Copyright (c) 1991, Stan Barber You may copy the trn kit in whole or in part as long as you don't try to make money off it, or pretend that you wrote it. -------------------------------------------------------------------------- See the file INSTALL for installation instructions. Failure to do so may void your warranty. :-) After you have unpacked your kit, you should have all the files listed in MANIFEST (Configure checks this for you). If you're unsure if you have the latest release, check ftp.uu.net: ftp.uu.net:networking/news/readers/trn/trn.tar.gz -> [latest.version].gz [A .Z version is also available and mthreads resides in the same spot, if you need it.] What is trn? ------------ Trn is Threaded RN -- a newsreader that uses an article's references to order the discussions in a very natural, reply-ordered sequence called threads. Having the replies associated with their parent articles not only makes following the discussion easier, but also makes it easy to back- track and (re-)read a specific discussion from the beginning. Trn also has a visual representation of the current thread in the upper right corner of the header, which will give you a feel for how the discussion is going and how the current article is related to the last one you read. In addition, a thread selector makes it easy to browse through a large group looking for interesting articles. You can even browse through the articles you've already read and select the one(s) you wish to read again. Other nice features include the extract commands for the source and binary groups, thread-oriented kill directives, a better newgroup finding strategy, and lots more. See the change-log for a list of the things that are new to trn 4.0 from previous versions (either view the file HelpFiles/changelog or type 'h' (help) from inside trn and select the "What's New?" entry). To make trn work faster you will probably want to create an auxiliary news database that summarises the available articles. Trn know how to use two different kinds (so far): thread files, which are maintained by the mthreads package and typically requires 3-5% of your newsspool size in disk storage; and overview files, which are maintained by INN v1.3 (or greater) or a modified version of C news and typically requires 8-10% of your newsspool size in disk storage. (Note that the space that mthreads saves you on your disk is paid for by a higher demand on your cpu and disks while updating the files.) See the package of your choice for details on how to setup the adjunct database, but it is not necessary to do this before trying out trn. Trn supports local news groups and news accessed remotely via NNTP. If you opt for remote access you will probably want to make the adjunct database available too. You can do this in a variety of way, but I recommend that you send the database from the server to the client via NNTP. To do this you either need to use INN or a modified reference NNTP -- version 1.5.11-t5 is the latest as of this writing. See ftp.uu.net:networking/news/nntp for the file nntp-t5.tar.gz. This version supports the XOVER command (to send overview files), the XTHREAD command (to send thread files), and the XINDEX command (though trn doesn't support using it). The alternative is to either mount the disk containing your database via NFS, or build it locally. See the mthreads package for details on how to do this. Note that trn is based on rn, and so it does a great job of pretending to be rn for those people that simply don't like to change their newsreading habits. It is possible to install trn as both rn and trn linked together and have it act as both newsreaders, thus saving you the hassle of maint- aining two separate newsreaders. A Configuration question will ask you if you want trn to check its name on startup. Where to send bug reports ------------------------- Mail your bug reports to trn-workers@lists.sourceforge.net. Use the 'v'ersion command from the newsgroup selection level of trn to be reminded of this address and see some configuration information to send along with your bug report. trn-4.0-test77/Rnmail.10000644000000000000000000000662607113613623013331 0ustar rootroot.\" Rnmail.1 .\" .\" This software is copyrighted as detailed in the LICENSE file. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH RNMAIL 1 LOCAL .SH NAME Rnmail - a program for replying via mail .SH SYNOPSIS .B Rnmail destination_list .br or .br .B Rnmail -h headerfile [oldarticle] .br or .br .B Rnmail .SH DESCRIPTION Rnmail is a friendly interface for mailing replies to news articles. It will ask several questions, then allow you to enter your letter, and then mail it off. If you type h and a carriage return at any point, .I Rnmail will tell you what it wants to know. .PP If you put a .I .signature file in your home directory, Rnmail will append it to your message after you are done editing it. If you prefer to see your signature while you are editing, rename your \&.signature file to be .mail_sig and it will be appended before you begin to edit. Note that both .mail_sig and .signature obey the setting of DOTDIR unless Rnmail was modified to take the .signature appending out of Rnmail's control and that if both files exist you get two signatures appended. .PP The -h form is used when invoked from .I trn or .IR rn . If your editor can edit multiple files, and you want the article to which you are replying to show up as an alternate file, define the environment variable MAILPOSTER as \*(L"Rnmail -h %h %A\*(R". You can also modify the the MAILHEADER environment variable to change the header file that .I [t]rn passes to Rnmail. .SH ENVIRONMENT .IP DOTDIR 8 If defined, specifies a place other than your home directory where 'dot' files may be stored. This is primarily for accounts which are shared by more than one person. .Sp Default: $HOME .IP EDITOR 8 The editor you want to use, if VISUAL is undefined. .Sp Default: whatever your news administrator installed, usually vi. .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .IP LOGNAME 8 Your login name, if USER is undefined. .Sp Default: value of \*(L"whoami\*(R". .IP MAILRECORD 8 If defined, contains the name of a file to which the finished message will be appended. .Sp Default: message not saved .IP NEWSORG 8 Either the name of your organization, or the name of a file containing the name of your organization. (For use at organizations where the ORGANIZATION environmental variable is in use for other purposes). If both NEWSORG and ORGANIZATION are set, NEWSORG will override ORGANIZATION. .Sp Default: whatever your news administrator chose. .IP ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. .Sp Default: whatever your news administrator chose. .IP REPLYTO 8 The contents of a \*(L"Reply-To:\*(R" header line to insert into your message. .Sp Default: header line not inserted. .IP USER 8 Your login name. .Sp Default: $LOGNAME .IP VISUAL 8 The editor you want to use. .Sp Default: $EDITOR .SH FILES $DOTDIR/.letter .br $DOTDIR/.signature .br $DOTDIR/.mail_sig .br ~/dead.letter .SH SEE ALSO trn(1), rn(1), Pnews(1), mail(1) .SH DIAGNOSTICS .SH BUGS Uses /bin/mail in the absence of sendmail. trn-4.0-test77/Rnmail.SH0000644000000000000000000002367611437640112013504 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting Rnmail (with variable substitutions)" $spitshell >Rnmail <>Rnmail <<'!NO!SUBS!' orgname=${NEWSORG-$orgname} !NO!SUBS! ;; *) $spitshell >>Rnmail <<'!NO!SUBS!' orgname=${NEWSORG-${ORGANIZATION-$orgname}} !NO!SUBS! ;; esac $spitshell >>Rnmail <<'!NO!SUBS!' dotdir=${DOTDIR-${HOME-$LOGDIR}} tmpartcopy=$dotdir/.letter tmpart=$tmpartcopy.$$ speller=$rnlib/Speller mail_sig=${MAILSIGNATURE-$dotdir/.mail_sig} # see if PGP exists pgp=${PGPCOMMAND-$pgp} $test -n "$pgp" && pgpprompt=" PGP," pgpsearch='^-----BEGIN PGP (SIGNED|MESSAGE)' deadfile=${HOME-$LOGDIR}/dead.letter if test -d $deadfile ; then deadfile=${deadfile}2 fi headerfile="" case $# in 0) ;; *) case $1 in -h) headerfile="$2" case $# in 3) oldart=$3 ;; esac ;; esac ;; esac case $headerfile in '') case $# in 0) to=h while $test "X$to" = Xh ; do $echo "" $echo $n "To: $c" read to || exit 0 case $to in h) $cat <<'EOH' Type the net address of those people to whom you wish the message sent. Additional recipients may be added on the Cc: line when you edit. Separate multiple addresses with spaces. EOH ;; esac done ;; *) to="$*" ;; esac to=`$echo "$to" | $sed 's/ */ /g'` title=h while $test "X$title" = Xh ; do $echo "" $echo $n "Title/Subject: $c" read title || exit 0 case $title in h) $cat <<'EOH' Type the title for your message. EOH ;; esac done # now build a file with a header for them to edit case $orgname in /*) orgname=`$cat $orgname` ;; esac $sed -e '/^Reply-To: $/d' > $tmpart < $tmpart ;; esac # check for addresses in the .invalid TLD if $egrep -i '^(To|Cc|Bcc): .*\.invalid([][()<>@,;:\\". ]|$)' $tmpart >/dev/null 2>&1; then $cat <<'EOM' At least one of the addresses you are about to mail is marked as an invalid address, so you will not be able to send mail to it directly. You should look for clues in the previous article to find an address where the poster can receive mail. EOM fi case "$FAST_RNMAIL" in y*) file='' $echo "" >> $tmpart state=edit ;; *) file=h;; esac while $test "X$file" = Xh ; do $echo "" $echo $n "Prepared file to include [none]: $c" read file || exit 0 case $file in h) $cat <<'EOH' If you have already produced the body of your message, type the filename for it here. If you just want to proceed directly to the editor, type a RETURN. In any event, you will be allowed to edit as many times as you want before you send off the message. EOH ;; '') $echo "" >> $tmpart state=edit ;; *) $cat $file >>$tmpart state=pgptest ;; esac done $echo "" if $test -r $mail_sig; then $echo "-- " >> $tmpart $cat $mail_sig >> $tmpart fi rescue="$sleep 1; $cat $tmpart >>$deadfile ; $echo Message appended to $deadfile ; exit 0" trap "$cp $tmpart $tmpartcopy ; $rm -f $tmpart*" 0 trap "trap : 1; $rescue" 1 trap "$rescue" 2 while : ; do case $state in edit) case "${VISUAL-${EDITOR-}}" in '') tmp=h ;; *) tmp='' ;; esac while $test "X$tmp" = Xh ; do $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" read tmp || state=rescue # exits on EOF 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 if $test $state = edit ; then trap : 2 ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=pgptest fi ;; ask) $echo "" $echo $n "Check spelling,$pgpprompt Send, Edit, List, or Abort? $c" read ans || state=rescue # exits on EOF case $ans in [aAqQ]*) state=rescue ;; [eE]*) set $ans case $# in 2) VISUAL="$2" ;; esac state=edit ;; [lL]*) $pager $tmpart state=ask ;; [cCiI]*|[sS][pP]*) $speller $tmpart state=pgptest ;; [pP]*) if $test -n "$pgpprompt"; then ans=`$echo $ans | $sed -e 's/^[pP] *//'` state=pgpask fi ;; [sS]*) state=send ;; [hH]*) $cat <<'EOH' Type c to check the message's spelling, s to send the message, a to abort and append the message to dead.letter, e to edit the message again, or l to list the message with your pager. To invoke an alternate editor, type 'e editor'. EOH ;; '') ;; *) $echo "" $echo "Unknown command: $ans" ans='' ;; esac ;; pgpask) if test "X$ans" = X; then $echo "" $echo $n "Sign, Encrypt, Both, Un-pgp, Test, or ? $c" read ans fi case "$ans" in [sS]*) state=pgpsign ;; [eE]*) state=pgpencrypt pgpopt='-e' ;; [bB]*) state=pgpboth pgpopt='-es' ;; [uU]*) state=pgpundo ;; [tT]*) state=pgptest ;; [rRaA]*|'') state=ask ;; [hH]*) $cat <<'EOH' Type s to PGP-sign the message, e to encrypt the message, b to both sign and encrypt the message, t to test the state the current state of the message, or a to abort this prompt. You can also specify the action from the previous prompt by using ps to PGP-sign, pe to encrypt, etc. EOH ans='' ;; *) $echo "" $echo "Unknown command: $ans" ans='' ;; esac ;; pgptest) state=ask if $test -n "$pgpprompt"; then ans=`$egrep "$pgpsearch" $tmpart` case "$ans" in *SIGNED*) $pgp +batchmode +verbose=0 -o $tmpart.tmp $tmpart $rm -f $tmpart.tmp ;; *MESSAGE*) $echo "" $echo "Message is PGP encoded." ;; *) $echo "" $echo "Message is not PGP-signed." ;; esac fi ;; pgpsign) $echo "" ans=`$egrep "$pgpsearch" $tmpart` if $test "X$ans" != X; then $echo "Article is already signed." else $echo "Preparing PGP signature:" $sed -ne '1,/^[ ]*$/ p' $tmpart > $tmpart.head $sed -e '1,/^[ ]*$/ d' $tmpart | $pgp +verbose=0 -fast >$tmpart.tmp if $test -s $tmpart.tmp; then $cat $tmpart.head $tmpart.tmp >$tmpart $echo "" $echo "Article is now PGP-signed." fi $rm -f $tmpart.tmp fi state=ask ;; pgpencrypt|pgpboth) $echo "" ans=`$egrep "$pgpsearch" $tmpart` if $test "X$ans" != X; then $echo "Article is already signed or encoded." else $sed -ne '1,/^[ ]*$/ p' $tmpart > $tmpart.head cc="`$egrep -i '^((Bcc)|(Cc)|(To)):' $tmpart.head | $sed -e 's/^Bcc://' -e 's/^Cc://' -e 's/^To://' -e 's/,/ /g'`" $echo "Processing message with pgp (to $cc):" $sed -e '1,/^[ ]*$/ d' $tmpart | $pgp +verbose=0 -fat $pgpopt $cc >$tmpart.tmp if $test -s $tmpart.tmp; then $cat $tmpart.head $tmpart.tmp >$tmpart $echo "" $echo "Article has been processed." fi $rm -f $tmpart.tmp fi state=ask ;; pgpundo) $echo "" ans=`$egrep "$pgpsearch" $tmpart` if $test "X$ans" != X; then $sed -ne '1,/^[ ]*$/ p' $tmpart > $tmpart.head case "$ans" in *SIGNED*) $sed -e '1,/^[ ]*$/ d' $tmpart | $pgp +verbose=0 -f >$tmpart.tmp 2>/dev/null ;; *MESSAGE*) $sed -e '1,/^[ ]*$/ d' $tmpart | $pgp +verbose=0 -f >$tmpart.tmp ;; esac $echo "" if $test -s $tmpart.tmp; then $cat $tmpart.head $tmpart.tmp >$tmpart $echo "PGP signature removed from article." else $echo "Article was not changed." state=ask fi $rm -f $tmpart.tmp else $echo "Article is not PGP-processed." fi state=ask ;; send) 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 case $mailer in *sendmail|*premail) $mailer -t <$tmpart ;; *qmail-inject) $mailer <$tmpart ;; # but recmail does not know about Bcc, alas *recmail) $mailer <$tmpart ;; *) set X `$sed <$tmpart -n -e '/^To:/{' -e 's/To: *//p' -e q -e '}'` shift set X "$@" `$sed <$tmpart -n -e '/^Cc:/{' -e 's/Cc: *//p' -e q -e '}'` shift set X "$@" `$sed <$tmpart -n -e '/^Bcc:/{' -e 's/Bcc: *//p' -e q -e '}'` shift $grep -v "^Bcc:" <$tmpart | $mailer "$@" ;; esac case $? in 0) state=cleanup ;; *) state=rescue ;; esac ;; rescue) $cat $tmpart >>$deadfile $echo "Message appended to $deadfile" $echo "A copy may be temporarily found in $tmpartcopy" exit ;; cleanup) case "${MAILRECORD-none}" in none) ;; *) set X ${USER-${LOGNAME-`$who am i`}} unknown shift ${REPLYSAVER-$rnlib/mbox.saver} $tmpart "." "." 0 0 Pnews $MAILRECORD "From $1 `LANG= date`" if $test $? -eq 0 ; then $echo "Message appended to $MAILRECORD" else $echo "Cannot append to $MAILRECORD" fi ;; esac exit ;; esac done !NO!SUBS! $eunicefix Rnmail chmod 755 Rnmail trn-4.0-test77/Speller.SH0000644000000000000000000000670607113133015013656 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting Speller (with variable substitutions)" $spitshell >Speller <>Speller <<'!NO!SUBS!' tmpdir="${TMPDIR-/tmp}" # get us some temporary files. hdrs=$tmpdir/sp$$hdr body=$tmpdir/sp$$body sig=$tmpdir/sp$$sig mine=$tmpdir/sp$$mine quoted=$tmpdir/sp$$quoted bad=$tmpdir/sp$$bad Cmdname=`$basename $0` if $test "X$1" = X; then $echo "$Cmdname: insufficent arguments" >&2 $echo "$Cmdname: usage: $Cmdname " >&2 exit 1 fi trap "$rm -f $hdrs $body $body~ $sig $mine $quoted $bad; exit 1" 0 1 2 15 while $test "X$1" != X; do # create the files, so that cat is quiet later.. >$hdrs >$body >$sig >$mine >$quoted # tear the wanted headers out and toss them at body, leaving the # the remainder to be put back in later. $awk 'BEGIN { inhdr = 1; keephdr = 0; insig = 0 } /^$/ { inhdr = 0; } /^-- $/ { if (!inhdr) { insig = 1; print $0 > Sig; next; } } /^(Subject|Keywords|Summary): / { if (inhdr) { keephdr = 1; print $0 > Body; next; } } /^[ \t]/ { if (inhdr) { if (keephdr) { print $0 > Body; } else { print $0 > Hdrs; } next; } } /^.*$/ { if (insig) { print $0 > Sig; } else if (inhdr) { keephdr = 0; print $0 > Hdrs; } else { print $0 > Body; } next; } ' Body=$body Hdrs=$hdrs Sig=$sig $1 # now rip out the quoted text from the article, so we only # spell check our own pristine prose.. if $test "X$QUOTECHARS" = X ; then $mv $body $mine else $sed -e "/^$QUOTECHARS/d" $body >$mine $diff -e $mine $body > $quoted fi # OK, we've torn everything asunder, now lets spell check # the guts of the article... if $test "X$ispell" = "Xnone"; then $spell $ispell_options $mine > $bad if $test -s $bad ; then ($echo ---- misspelled words ------------------------------------- #$cat $bad | fmt $cat $bad | pr -t -4 $echo ----------------------------------------------------------- ) | $pager else $echo 'No misspelled words.' fi else $ispell $ispell_options $mine fi if $test $? -ne 0; then $echo "$Cmdname: error returned, leaving message untouched" # don't want to mess with this file again, either shift continue fi # resurrect the body of the article.. if $test -s $quoted ; then ($cat $quoted; $echo w $body; $echo q) | $ed - $mine else $mv $mine $body fi # ..and re-assemble the article. $cat $hdrs $body $sig >$1 # move to the next filename! shift done !NO!SUBS! $eunicefix Speller chmod 755 Speller trn-4.0-test77/access_def.SH0000644000000000000000000000621007141205160014317 0ustar rootroot: see end of file for revision information case $CONFIG in '') . ./config.sh ;; esac echo "Extracting access.def (with variable substitutions)" cat >access.def <name, keylen); } static int build_addgroup_list(keylen, data, extra) int keylen; HASHDATUM* data; int extra; { ADDGROUP* node = (ADDGROUP*)data->dat_ptr; node->num = addgroup_cnt++; node->next = NULL; node->prev = last_addgroup; if (last_addgroup) last_addgroup->next = node; else first_addgroup = node; last_addgroup = node; return 0; } void addng_init() { ; } bool find_new_groups() { NEWSRC* rp; NG_NUM oldcnt = newsgroup_cnt; /* remember # newsgroups */ /* Skip this check if the -q flag was given. */ if (quickstart) return FALSE; for (rp = multirc->first; rp; rp = rp->next) { if (ALLBITS(rp->flags, RF_ADD_NEWGROUPS | RF_ACTIVE)) { #ifdef SUPPORT_NNTP if (rp->datasrc->flags & DF_REMOTE) new_nntp_groups(rp->datasrc); else #endif new_local_groups(rp->datasrc); } } addnewbydefault = 0; process_list(GNG_RELOC); return oldcnt != newsgroup_cnt; } static void process_list(flag) int flag; { ADDGROUP* node; ADDGROUP* prevnode; if (!flag) { sprintf(cmd_buf,"\nUnsubscribed but mentioned in your current newsrc%s:\n", multirc->first->next? "s" : nullstr); print_lines(cmd_buf, STANDOUT); } if ((node = first_addgroup) != NULL && flag && UseAddSelector) addgroup_selector(flag); while (node) { if (!flag) { sprintf(cmd_buf, "%s\n", node->name); print_lines(cmd_buf, NOMARKING); } else if (!UseAddSelector) get_ng(node->name,flag); /* add newsgroup -- maybe */ prevnode = node; node = node->next; free((char*)prevnode); } first_addgroup = NULL; last_addgroup = NULL; addgroup_cnt = 0; } #ifdef SUPPORT_NNTP static void new_nntp_groups(dp) DATASRC* dp; { register char* s; int len; time_t server_time; NGDATA* np; bool foundSomething = FALSE; long high, low; HASHTABLE* newngs; set_datasrc(dp); server_time = nntp_time(); if (server_time == -2) return; /*$$*/ if (nntp_newgroups(dp->lastnewgrp) < 1) { /*$$*/ printf("Can't get new groups from server:\n%s\n", ser_line); return; } newngs = hashcreate(33, addng_cmp); while (1) { high = 0, low = 1; if (nntp_gets(ser_line, sizeof ser_line) < 0) break; #ifdef DEBUG if (debug & DEB_NNTP) printf("<%s\n", ser_line) FLUSH; #endif if (nntp_at_list_end(ser_line)) break; foundSomething = TRUE; if ((s = index(ser_line, ' ')) != NULL) len = s - ser_line; else len = strlen(ser_line); if (dp->act_sf.fp) { if (find_actgrp(dp, buf, ser_line, len, (ART_NUM)0)) { if (!s) s = buf + len + 1; } else { char ch = 'y'; if (s) sscanf(s+1, "%ld %ld %c", &high, &low, &ch); else s = ser_line + len; sprintf(s, " %010ld %05ld %c\n", high, low, ch); (void) srcfile_append(&dp->act_sf, ser_line, len); } } if (s) { *s++ = '\0'; while (isdigit(*s) || isspace(*s)) s++; if (*s == 'x' || *s == '=') continue; } if ((np = find_ng(ser_line)) != NULL && np->toread > TR_UNSUB) continue; add_to_hash(newngs, ser_line, high-low, auto_subscribe(ser_line)); } if (foundSomething) { hashwalk(newngs, build_addgroup_list, 0); srcfile_end_append(&dp->act_sf, dp->extra_name); dp->lastnewgrp = server_time; } hashdestroy(newngs); } #endif static void new_local_groups(dp) DATASRC* dp; { register char* s; time_t lastone; NGDATA* np; char tmpbuf[LBUFLEN]; long high, low; char ch; HASHTABLE* newngs; datasrc = dp; /* did active.times file grow? */ stat(dp->extra_name,&filestat); if (filestat.st_size == dp->act_sf.recent_cnt) return; tmpfp = fopen(dp->extra_name,"r"); if (tmpfp == NULL) { printf(cantopen,dp->extra_name) FLUSH; termdown(1); return; } lastone = time((time_t*)NULL) - 24L * 60 * 60 - 1; newngs = hashcreate(33, addng_cmp); while (fgets(buf,LBUFLEN,tmpfp) != NULL) { if ((s = index(buf, ' ')) == NULL || (lastone = atol(s+1)) < dp->lastnewgrp) continue; *s = '\0'; if (!find_actgrp(datasrc, tmpbuf, buf, s - buf, (ART_NUM)0)) continue; high = 0, low = 1, ch = 'y'; sscanf(tmpbuf + (s-buf) + 1, "%ld %ld %c", &high, &low, &ch); if (ch == 'x' || ch == '=') continue; if ((np = find_ng(buf)) != NULL) continue; add_to_hash(newngs, buf, high-low, auto_subscribe(buf)); } fclose(tmpfp); hashwalk(newngs, build_addgroup_list, 0); hashdestroy(newngs); dp->lastnewgrp = lastone+1; dp->act_sf.recent_cnt = filestat.st_size; } static void add_to_hash(ng, name, toread, ch) HASHTABLE* ng; char* name; int toread; char_int ch; { HASHDATUM data; ADDGROUP* node; unsigned namelen = strlen(name); data.dat_len = namelen + sizeof (ADDGROUP); node = (ADDGROUP*)safemalloc(data.dat_len); data.dat_ptr = (char *)node; switch (ch) { case ':': node->flags = AGF_SEL; break; case '!': node->flags = AGF_DEL; break; default: node->flags = 0; break; } node->toread = (toread < 0)? 0 : toread; strcpy(node->name, name); node->datasrc = datasrc; node->next = node->prev = NULL; hashstore(ng, name, namelen, data); } static void add_to_list(name, toread, ch) char* name; int toread; char_int ch; { ADDGROUP* node = first_addgroup; while (node) { if (strEQ(node->name, name)) return; node = node->next; } node = (ADDGROUP*)safemalloc(strlen(name) + sizeof (ADDGROUP)); switch (ch) { case ':': node->flags = AGF_SEL; break; case '!': node->flags = AGF_DEL; break; default: node->flags = 0; break; } node->toread = (toread < 0)? 0 : toread; node->num = addgroup_cnt++; strcpy(node->name, name); node->datasrc = datasrc; node->next = NULL; node->prev = last_addgroup; if (last_addgroup) last_addgroup->next = node; else first_addgroup = node; last_addgroup = node; } bool scanactive(add_matching) bool_int add_matching; { DATASRC* dp; NG_NUM oldcnt = newsgroup_cnt; /* remember # of newsgroups */ if (!add_matching) print_lines("Completely unsubscribed newsgroups:\n", STANDOUT); for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) { if (!(dp->flags & DF_OPEN)) continue; set_datasrc(dp); if (dp->act_sf.fp) hashwalk(dp->act_sf.hp, list_groups, add_matching); #ifdef SUPPORT_NNTP else { if (maxngtodo != 1) strcpy(buf, "*"); else { if (ngtodo[0][0] == '^') sprintf(buf,"%s*", &ngtodo[0][1]); else sprintf(buf,"*%s*", ngtodo[0]); if (buf[strlen(buf)-2] == '$') buf[strlen(buf)-2] = '\0'; } if (nntp_list("active", buf, strlen(buf)) == 1) { while (!nntp_at_list_end(ser_line)) { scanline(ser_line,add_matching); if (nntp_gets(ser_line, sizeof ser_line) < 0) break; /*$$*/ } } } #endif } process_list(add_matching); if (in_ng) /*$$*/ set_datasrc(ngptr->rc->datasrc); return oldcnt != newsgroup_cnt; } static int list_groups(keylen, data, add_matching) int keylen; HASHDATUM* data; int add_matching; { char* bp = ((LISTNODE*)data->dat_ptr)->data + data->dat_len; int linelen = index(bp, '\n') - bp + 1; (void) bcopy(bp, buf, linelen); buf[linelen] = '\0'; scanline(buf,add_matching); return 0; } static void scanline(actline, add_matching) char* actline; bool_int add_matching; { register char* s; NGDATA* np; long high, low; char ch; if ((s = index(actline,' ')) == NULL) return; *s++ = '\0'; /* this buffer is expendable */ high = 0, low = 1, ch = 'y'; sscanf(s, "%ld %ld %c", &high, &low, &ch); if (ch == 'x' || strnEQ(actline,"to.",3)) return; if (!inlist(actline)) return; if ((np = find_ng(actline)) != NULL && np->toread > TR_UNSUB) return; if (add_matching || np) { /* it's not in a newsrc */ add_to_list(actline, high-low, 0); } else { strcat(actline,"\n"); print_lines(actline, NOMARKING); } } static int agorder_number(app1, app2) register ADDGROUP** app1; register ADDGROUP** app2; { ART_NUM eq = (*app1)->num - (*app2)->num; return eq > 0? sel_direction : -sel_direction; } static int agorder_groupname(app1, app2) register ADDGROUP** app1; register ADDGROUP** app2; { return strcaseCMP((*app1)->name, (*app2)->name) * sel_direction; } static int agorder_count(app1, app2) register ADDGROUP** app1; register ADDGROUP** app2; { long eq = (*app1)->toread - (*app2)->toread; if (eq) return eq > 0? sel_direction : -sel_direction; return agorder_groupname(app1, app2); } /* Sort the newsgroups into the chosen order. */ void sort_addgroups() { register ADDGROUP* ap; register int i; ADDGROUP** lp; ADDGROUP** ag_list; int (*sort_procedure)(); switch (sel_sort) { case SS_NATURAL: default: sort_procedure = agorder_number; break; case SS_STRING: sort_procedure = agorder_groupname; break; case SS_COUNT: sort_procedure = agorder_count; break; } ag_list = (ADDGROUP**)safemalloc(addgroup_cnt * sizeof *ag_list); for (lp = ag_list, ap = first_addgroup; ap; ap = ap->next) *lp++ = ap; assert(lp - ag_list == addgroup_cnt); qsort(ag_list, addgroup_cnt, sizeof *ag_list, sort_procedure); first_addgroup = ap = ag_list[0]; ap->prev = NULL; for (i = addgroup_cnt, lp = ag_list; --i; lp++) { lp[0]->next = lp[1]; lp[1]->prev = lp[0]; } last_addgroup = lp[0]; last_addgroup->next = NULL; free((char*)ag_list); } trn-4.0-test77/addng.h0000644000000000000000000000136307113133015013234 0ustar rootroot/* addng.h */ /* This software is copyrighted as detailed in the LICENSE file. */ struct addgroup { ADDGROUP* next; ADDGROUP* prev; DATASRC* datasrc; ART_NUM toread; /* number of articles to be read (for sorting) */ NG_NUM num; /* a possible sort order for this group */ char flags; char name[1]; }; #define AGF_SEL 0x01 #define AGF_DEL 0x02 #define AGF_DELSEL 0x04 #define AGF_INCLUDED 0x10 #define AGF_EXCLUDED 0x20 EXT ADDGROUP* first_addgroup; EXT ADDGROUP* last_addgroup; EXT ADDGROUP* sel_page_gp; EXT ADDGROUP* sel_next_gp; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void addng_init _((void)); bool find_new_groups _((void)); bool scanactive _((bool_int)); void sort_addgroups _((void)); trn-4.0-test77/addng.ih0000644000000000000000000000142607114304735013417 0ustar rootroot/* addng.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ static int addgroup_cnt; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int addng_cmp _((char*,int,HASHDATUM)); static int build_addgroup_list _((int,HASHDATUM*,int)); static void process_list _((int)); #ifdef SUPPORT_NNTP static void new_nntp_groups _((DATASRC*)); #endif static void new_local_groups _((DATASRC*)); static void add_to_hash _((HASHTABLE*,char*,int,char_int)); static void add_to_list _((char*,int,char_int)); static int list_groups _((int,HASHDATUM*,int)); static void scanline _((char*,bool_int)); static int agorder_number _((ADDGROUP**,ADDGROUP**)); static int agorder_groupname _((ADDGROUP**,ADDGROUP**)); static int agorder_count _((ADDGROUP**,ADDGROUP**)); trn-4.0-test77/art.c0000644000000000000000000007251611437640112012755 0ustar rootroot/* art.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "nntp.h" #include "ngstuff.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 "mime.h" #include "term.h" #include "env.h" #include "util.h" #include "util2.h" #include "sw.h" #include "kfile.h" #include "decode.h" #include "backpage.h" #include "intrp.h" #include "rthread.h" #include "rt-select.h" #include "rt-util.h" #include "rt-wumpus.h" #include "charsubst.h" #ifdef SCAN_ART #include "scanart.h" #endif #ifdef SCORE #include "score.h" /* for sc_lookahead() */ #endif #ifdef USE_TK #include "tkstuff.h" #include "tktree.h" #endif #include "color.h" #include "INTERN.h" #include "art.h" #include "artstate.h" /* somebody has to do it */ #define LINE_PTR(pos) (artbuf + (pos) - htype[PAST_HEADER].minpos) #define LINE_OFFSET(ptr) ((ptr) - artbuf + htype[PAST_HEADER].minpos) /* 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_POS restart = 0; /* if nonzero, the place where last */ /* line left off on line split */ ART_POS alinebeg; /* where in file current line began */ int more_prompt_col; /* non-zero when the more prompt is indented */ ART_LINE isrchline = 0; /* last line to display */ #ifdef INNERSEARCH COMPEX gcompex; /* in article search pattern */ #endif bool firstpage; /* is this the 1st page of article? */ bool continuation; /* this line/header is being continued */ void art_init() { #ifdef INNERSEARCH init_compex(&gcompex); #endif } int do_article() { register char* s; bool hide_this_line = FALSE; /* hidden header line? */ bool restart_color; ART_LINE linenum; /* line # on page, 1 origin */ #ifdef ULSMARTS bool under_lining = FALSE; /* are we underlining a word? */ #endif register char* bufptr = art_line; /* 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; register int outputok = TRUE; #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) artsize = raw_artsize = nntp_artsize(); else #endif { if (fstat(fileno(artfp),&filestat)) /* get article file stats */ return DA_CLEAN; if (!S_ISREG(filestat.st_mode)) return DA_NORM; artsize = raw_artsize = filestat.st_size; } #ifdef SCORE #ifdef USE_TK if (ttcl_running) ttcl_set_int("score_curvar",sc_score_art((int)art,TRUE)); #endif #endif sprintf(prompt_buf, mousebar_cnt>3? "%%sEnd of art %ld (of %ld) %%s[%%s]" : "%%sEnd of article %ld (of %ld) %%s-- 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); mime_SetArticle(); clear_artbuf(); seekart(artbuf_seek = htype[PAST_HEADER].minpos); } term_scrolled = 0; #ifdef USE_TK if (ThreadedGroup && ttk_running) ttk_draw_tree(curr_artp, 0, 0); #endif 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 */ if (!*artbuf) { mime_SetArticle(); artbuf_seek = htype[PAST_HEADER].minpos; } artpos = vrdary(artline); if (artpos < 0) artpos = -artpos; /* labs(), anyone? */ if (firstpage) artpos = (ART_POS)0; if (artpos < htype[PAST_HEADER].minpos) { in_header = SOME_LINE; seekart(htype[PAST_HEADER].minpos); seekartbuf(htype[PAST_HEADER].minpos); } else { seekart(artbuf_seek); seekartbuf(artpos); } do_fseek = FALSE; restart = 0; } linenum = 1; if (!do_hiding) is_mime = FALSE; if (firstpage) { if (firstline) { interp(art_line,sizeof art_line,firstline); linenum += tree_puts(art_line,linenum+topline,0); } else { ART_NUM i; int selected, unseen; selected = (curr_artp->flags & AF_SEL); unseen = article_unread(art)? 1 : 0; sprintf(art_line,"%s%s #%ld",ngname,moderated,(long)art); if (selected_only) { i = selected_count - (unseen && selected); sprintf(art_line+strlen(art_line)," (%ld + %ld more)", (long)i,(long)ngptr->toread - selected_count - (!selected && unseen)); } else if ((i = (ART_NUM)(ngptr->toread - unseen)) != 0 || (!ThreadedGroup && dmcount)) { sprintf(art_line+strlen(art_line), " (%ld more)",(long)i); } if (!ThreadedGroup && dmcount) sprintf(art_line+strlen(art_line)-1, " + %ld Marked to return)",(long)dmcount); linenum += tree_puts(art_line,linenum+topline,0); } start_header(art); forcelast = FALSE; /* we will have our day in court */ restart = 0; artline = 0; /* start counting lines */ artpos = 0; vwtary(artline,artpos); /* remember pos in file */ } for (restart_color = 1; /* linenum already set */ innersearch? (in_header || innermore()) : special? (linenum < slines) : (firstpage && !in_header)? (linenum < initlines) : (linenum < tc_LINES); linenum++) { /* for each line on page */ if (int_count) { /* exit via interrupt? */ newline(); /* get to left margin */ int_count = 0; /* reset interrupt count */ set_mode(gmode,oldmode); special = FALSE; return DA_NORM; /* skip out of loops */ } if (restart) { /* did not finish last line? */ bufptr = LINE_PTR(restart);/* then start again here */ restart = 0; /* and reset the flag */ continuation = 1; if (restart_color && do_hiding && !in_header) maybe_set_color(bufptr, 1); } else if (in_header && *(bufptr = headbuf + artpos)) continuation = *bufptr == ' ' || *bufptr == '\t'; else { if ((bufptr = readartbuf(auto_view_inline)) == NULL) { special = FALSE; if (innersearch) (void)innermore(); break; } if (do_hiding && !in_header) continuation = maybe_set_color(bufptr, restart_color); else continuation = 0; } alinebeg = artpos; /* remember where we began */ restart_color = 0; if (in_header) { hide_this_line = parseline(bufptr,do_hiding,hide_this_line); if (!in_header) { linenum += finish_tree(linenum+topline); end_header(); seekart(artbuf_seek); } } else if (notesfiles && do_hiding && !continuation && *bufptr == '#' && isupper(bufptr[1]) && bufptr[2] == ':' ) { if ((bufptr = readartbuf(auto_view_inline)) == NULL) break; for (s = bufptr; *s && *s != '\n' && *s != '!'; s++) ; if (*s != '!') readartbuf(auto_view_inline); mime_SetArticle(); clear_artbuf(); /* exclude notesfiles droppings */ artbuf_seek = htype[PAST_HEADER].minpos = tellart(); hide_this_line = TRUE; /* and do not print either */ notesfiles = FALSE; } #ifdef CUSTOMLINES if (hideline && !continuation && execute(&hide_compex,bufptr)) hide_this_line = TRUE; #endif if (in_header && do_hiding && (htype[in_header].flags & HT_MAGIC)) { switch (in_header) { case NGS_LINE: if ((s = index(bufptr,'\n')) != NULL) *s = '\0'; hide_this_line = (index(bufptr,',') == NULL) && strEQ(bufptr+12, ngname); if (s != NULL) *s = '\n'; break; case EXPIR_LINE: if (!(htype[EXPIR_LINE].flags & HT_HIDE)) { s = bufptr + htype[EXPIR_LINE].length + 1; hide_this_line = *s != ' ' || s[1] == '\n'; } break; case FROM_LINE: if ((s = index(bufptr,'\n')) != NULL && s-bufptr < sizeof art_line) safecpy(art_line,bufptr,s-bufptr+1); else safecpy(art_line,bufptr,sizeof art_line); if ((s = extract_name(art_line+6)) != NULL) { strcpy(art_line+6,s); bufptr = art_line; } break; #ifdef HAS_STRFTIME case DATE_LINE: if (curr_artp->date != -1) { strncpy(art_line,bufptr,6); strftime(art_line+6, (sizeof art_line)-6, getval("LOCALTIMEFMT", LOCALTIMEFMT), localtime(&curr_artp->date)); bufptr = art_line; } break; #endif } } if (in_header == SUBJ_LINE && do_hiding && (htype[SUBJ_LINE].flags & HT_MAGIC)) { /* handle the subject */ s = get_cached_line(artp, SUBJ_LINE, FALSE); if (s && continuation) { /* continuation lines were already output */ linenum--; } else { int length = strlen(bufptr+1); notesfiles = instr(&bufptr[length-10]," - (nf", TRUE)!=NULL; artline++; if (!s) bufptr += (continuation? 0 : 9); else bufptr = s; /* tree_puts(, ,1) underlines subject */ linenum += tree_puts(bufptr,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(bufptr,linenum+topline,0)-1; } else { /* just a normal line */ if (outputok && erase_each_line) erase_line(0); 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(); carriage_return(); } if (*bufptr == '\n') putchar(' '); } outputok = !hide_everything; /* registerize it, hopefully */ #ifdef CUSTOMLINES if (pagestop && !continuation && execute(&page_compex,bufptr)) linenum = 32700; #endif for (outpos = 0; outpos < tc_COLS; ) { /* while line has room */ if (AT_NORM_CHAR(bufptr)) { /* normal char? */ #ifdef ULSMARTS if (*bufptr == '_') { if (bufptr[1] == '\b') { if (outputok && !under_lining && highlight != artline) { under_lining++; if (tc_UG) { if (bufptr != buf && bufptr[-1]==' ') { outpos--; backspace(); } } underline(); } bufptr += 2; } } else { if (under_lining) { under_lining = 0; un_underline(); if (tc_UG) { outpos++; if (*bufptr == ' ') goto skip_put; } } } #endif /* handle rot-13 if wanted */ if (rotate && !in_header && isalpha(*bufptr)) { if (outputok) { if ((*bufptr & 31) <= 13) putchar(*bufptr+13); else putchar(*bufptr-13); } outpos++; } else { #ifdef CHARSUBST register int i; i = putsubstchar(*bufptr, tc_COLS - outpos, outputok); if (i < 0) { outpos += -i - 1; break; } outpos += i; #else if (outputok) putchar(*bufptr); outpos++; #endif /* CHARSUBST */ } if (*tc_UC && ((highlight==artline && marking == STANDOUT) #ifdef ULSMARTS || under_lining #endif )) { backspace(); underchar(); } skip_put: bufptr++; } else if (AT_NL(*bufptr) || !*bufptr) { /* newline? */ #ifdef ULSMARTS if (under_lining) { under_lining = 0; un_underline(); } #endif #ifdef DEBUG if (debug & DEB_INNERSRCH && outpos < tc_COLS - 6) { standout(); printf("%4d",artline); un_standout(); } #endif if (outputok) newline(); restart = 0; outpos = 1000; /* signal normal \n */ } else if (*bufptr == '\t') { /* tab? */ int incpos = 8 - outpos % 8; if (outputok) { if (tc_GT) putchar(*bufptr); else while (incpos--) putchar(' '); } bufptr++; outpos += 8 - outpos % 8; } else if (*bufptr == '\f') { /* form feed? */ if (outpos+2 > tc_COLS) break; if (outputok) fputs("^L",stdout); if (bufptr == LINE_PTR(alinebeg) && highlight != artline) linenum = 32700; /* how is that for a magic number? */ bufptr++; outpos += 2; } else { /* other control char */ if (dont_filter_control) { if (outputok) putchar(*bufptr); outpos++; } else if (*bufptr != '\r' || bufptr[1] != '\n') { if (outpos+2 > tc_COLS) break; if (outputok) { putchar('^'); if (highlight == artline && *tc_UC && marking == STANDOUT) { backspace(); underchar(); putchar((*bufptr & 0x7F) ^ 0x40); backspace(); underchar(); } else putchar((*bufptr & 0x7F) ^ 0x40); } outpos += 2; } bufptr++; } } /* end of column loop */ if (outpos < 1000) { /* did line overflow? */ restart = LINE_OFFSET(bufptr);/* restart here next time */ if (outputok) { if (!tc_AM || tc_XN || outpos < tc_COLS) newline(); else term_line++; } if (AT_NL(*bufptr)) /* 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(); carriage_return(); highlight = -1; /* no more we are */ /* in case terminal highlighted rest of line earlier */ /* when we did an eol with highlight turned on: */ if (erase_each_line) erase_eol(); } artline++; /* count the line just printed */ if (artline - tc_LINES + 1 > topline) /* did we just scroll top line off? */ topline = artline - tc_LINES + 1; /* then recompute top line # */ } /* determine actual position in file */ if (restart) /* stranded somewhere in the buffer? */ artpos += restart - alinebeg; else if (in_header) artpos = index(headbuf+artpos,'\n') - headbuf + 1; else artpos = artbuf_pos + htype[PAST_HEADER].minpos; vwtary(artline,artpos); /* remember pos in file */ } /* end of line loop */ #ifdef INNERSEARCH innersearch = 0; if (hide_everything) { *buf = hide_everything; hide_everything = 0; 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 */ highlight = -1; /* extra loop bombout */ #ifdef SUPPORT_NNTP if (artsize < 0 && (raw_artsize = nntp_artsize()) >= 0) artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos; recheck_pager: if (do_hiding && artbuf_pos == artbuf_len) { /* If we're filtering we need to figure out if any * remaining text is going to vanish or not. */ long seekpos = artbuf_pos + htype[PAST_HEADER].minpos; readartbuf(FALSE); seekartbuf(seekpos); } #endif if (artpos == artsize) {/* did we just now reach EOF? */ color_default(); set_mode(gmode,oldmode); return DA_NORM; /* avoid --MORE--(100%) */ } /* not done with this article, so pretend we are a pager */ reask_pager: if (term_line >= tc_LINES) { term_scrolled += term_line - tc_LINES + 1; term_line = tc_LINES-1; } more_prompt_col = term_col; unflush_output(); /* disable any ^O in effect */ maybe_eol(); color_default(); #ifdef SUPPORT_NNTP if (artsize < 0) strcpy(cmd_buf,"?"); else #endif sprintf(cmd_buf,"%ld",(long)(artpos*100/artsize)); #ifdef CHARSUBST sprintf(buf,"%s--MORE--(%s%%)",current_charsubst(),cmd_buf); #else sprintf(buf,"--MORE--(%s%%)",cmd_buf); #endif outpos = term_col + strlen(buf); draw_mousebar(tc_COLS - (term_line == tc_LINES-1? outpos+5 : 0), 1); color_string(COLOR_MORE,buf); fflush(stdout); term_col = outpos; eat_typeahead(); #ifdef DEBUG if (debug & DEB_CHECKPOINTING) { printf("(%d %d %d)",checkcount,linenum,artline); fflush(stdout); } #endif if (checkcount >= docheckwhen && linenum == tc_LINES && (artline > 40 || checkcount >= docheckwhen+10)) { /* while he is reading a whole page */ /* in an article he is interested in */ checkcount = 0; checkpoint_newsrcs(); /* update all newsrcs */ #ifdef KILLFILES update_thread_kfile(); #endif } cache_until_key(); #ifdef SUPPORT_NNTP if (artsize < 0 && (raw_artsize = nntp_artsize()) >= 0) { artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos; goto_xy(more_prompt_col,term_line); goto recheck_pager; } #endif set_mode(gmode,'p'); getcmd(buf); if (errno) { if (tc_LINES < 100 && !int_count) *buf = '\f';/* on CONT fake up refresh */ else { *buf = 'q'; /* on INTR or paper just quit */ } } erase_line(erase_screen && erase_each_line); fake_command: /* used by innersearch */ color_default(); output_chase_phrase = TRUE; /* parse and process pager command */ if (mousebar_cnt) clear_rest(); switch (page_switch()) { case PS_ASK: /* reprompt "--MORE--..." */ goto reask_pager; case PS_RAISE: /* reparse on article level */ set_mode(gmode,oldmode); return DA_RAISE; case PS_TOEND: /* fast pager loop exit */ set_mode(gmode,oldmode); return DA_TOEND; case PS_NORM: /* display more article */ break; } } /* end of page loop */ } int maybe_set_color(cp, backsearch) char* cp; bool_int backsearch; { register char ch = (cp == artbuf || cp == art_line? 0 : cp[-1]); if (ch == '\001') color_object(COLOR_MIMEDESC, 0); else if (ch == '\002') color_object(COLOR_MIMESEP, 0); else if (ch == WRAPPED_NL) { if (backsearch) { while (cp > artbuf && cp[-1] != '\n') cp--; maybe_set_color(cp, 0); } return 1; } else { while (*cp == ' ' || *cp == '\t') cp++; if (index(">}]#!:|", *cp)) color_object(COLOR_CITEDTEXT, 0); else color_object(COLOR_BODYTEXT, 0); } return 0; } /* process pager commands */ int page_switch() { register char* s; switch (*buf) { case '!': /* shell escape */ escapade(); return PS_ASK; #ifdef INNERSEARCH case Ctl('i'): { ART_LINE i = artline; ART_POS pos; gline = 3; s = LINE_PTR(alinebeg); while (AT_NL(*s) && i >= topline) { pos = vrdary(--i); if (pos < 0) pos = -pos; if (pos < htype[PAST_HEADER].minpos) break; seekartbuf(pos); if ((s = readartbuf(FALSE)) == NULL) { s = LINE_PTR(alinebeg); break; } } sprintf(cmd_buf,"^[^%c\n]",*s); 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)) != NULL) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; termdown(2); return PS_ASK; } erase_line(0); /* erase the prompt */ /* FALL THROUGH */ caseG: case 'G': { ART_POS start_where; bool success; char* nlptr; char ch; if (gline < 0 || gline > tc_LINES-2) gline = tc_LINES-2; #ifdef DEBUG if (debug & DEB_INNERSRCH) { printf("Start here? %d >=? %d\n",topline + gline + 1,artline) FLUSH; termdown(1); } #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].minpos) start_where = htype[PAST_HEADER].minpos; seekartbuf(start_where); innerlight = 0; innersearch = 0; /* assume not found */ while ((s = readartbuf(FALSE)) != NULL) { if ((nlptr = index(s,'\n')) != NULL) { ch = *++nlptr; *nlptr = '\0'; } #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("Test %s\n",s) FLUSH; #endif success = execute(&gcompex,s) != NULL; if (nlptr) *nlptr = ch; if (success) { innersearch = artbuf_pos + htype[PAST_HEADER].minpos; break; } } if (!innersearch) { seekartbuf(artpos); fputs("(Not found)",stdout) FLUSH; term_col = 11; return PS_ASK; } #ifdef DEBUG if (debug & DEB_INNERSRCH) { printf("On page? %ld <=? %ld\n",(long)innersearch,(long)artpos) FLUSH; termdown(1); } #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; termdown(1); } #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 = '\f'; } return PS_NORM; } #else case 'g': case 'G': case Ctl('g'): notincl("g"); return PS_ASK; #endif case '\n': /* one line down */ case '\r': special = TRUE; slines = 2; return PS_NORM; case 'X': rotate = !rotate; /* FALL THROUGH */ case 'l': case '\f': /* refresh screen */ refresh_screen: #ifdef DEBUG if (debug & DEB_INNERSRCH) { printf("Topline = %d",topline) FLUSH; fgets(buf, sizeof buf, stdin); } #endif clear(); do_fseek = TRUE; artline = topline; if (artline < 0) artline = 0; firstpage = (topline < 0); return PS_NORM; #ifdef INNERSEARCH case Ctl('e'): #ifdef SUPPORT_NNTP if (artsize < 0) { nntp_finishbody(FB_OUTPUT); raw_artsize = nntp_artsize(); artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos; } #endif if (do_hiding) { seekartbuf(artsize); seekartbuf(artpos); } topline = artline; innerlight = artline - 1; innersearch = artsize; gline = 0; hide_everything = 'b'; return PS_NORM; #endif case 'B': /* one line up */ if (topline < 0) break; if (*tc_IL && *tc_HO) { ART_POS pos; home_cursor(); insert_line(); carriage_return(); pos = vrdary(topline-1); if (pos < 0) pos = -pos; if (pos >= htype[PAST_HEADER].minpos) { seekartbuf(pos); if ((s = readartbuf(FALSE)) != NULL) { artpos = vrdary(topline); if (artpos < 0) artpos = -artpos; maybe_set_color(s, 1); for (pos = artpos - pos; pos-- && !AT_NL(*s); s++) putchar(*s); color_default(); putchar('\n') FLUSH; topline--; artpos = vrdary(--artline); if (artpos < 0) artpos = -artpos; seekartbuf(artpos); alinebeg = vrdary(artline-1); if (alinebeg < 0) alinebeg = -alinebeg; goto_xy(0,artline-topline); erase_line(0); return PS_ASK; } } } /* FALL THROUGH */ case 'b': case Ctl('b'): { /* back up a page */ ART_LINE target; if (erase_each_line) home_cursor(); else clear(); do_fseek = TRUE; /* reposition article file */ if (*buf == 'B') target = topline - 1; else { target = topline - (tc_LINES - 2); if (marking && (marking_areas & BACKPAGE_MARKING)) highlight = topline; } 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 */ help_page(); 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) { #ifdef CHARSUBST case 'C': if (!*(++charsubst)) charsubst = charsets; goto refresh_screen; #endif default: break; } goto leave_pager; case '\0': /* treat break as 'n' */ *buf = 'n'; /* FALL THROUGH */ case 'a': case 'A': case 'e': case 'k': case 'K': case 'J': case 'n': case 'N': case Ctl('n'): case 'F': case 'R': case 's': case 'S': case 'T': case 'u': case 'w': case 'W': case '|': mark_as_read(artp); /* mark article as read */ /* FALL THROUGH */ case 'U': case ',': case '<': case '>': case '[': case ']': case '{': case '}': case '(': case ')': case ':': case '+': case Ctl('v'): /* verify crypto signature */ #ifdef SCAN_ART case ';': /* enter article scan mode */ #endif #ifdef SCORE case '"': /* append to local scorefile */ case '\'': /* score command */ #endif 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 'f': case Ctl('f'): case 'h': case 'j': case Ctl('k'): case 'm': case 'M': case 'p': case 'P': case Ctl('p'): case '`': case 'Q': case 'r': case Ctl('r'): case 'v': case 'x': case Ctl('x'): case 'Y': case 'z': case 'Z': case '^': case Ctl('^'): case '\b': case '\177': leave_pager: reread = FALSE; if (index("nNpP\016\020",*buf) == NULL && index("wWsSe:!&|/?123456789.",*buf) != NULL) { setdfltcmd(); color_object(COLOR_CMD, 1); interpsearch(cmd_buf, sizeof cmd_buf, mailcall, buf); printf(prompt,cmd_buf, #ifdef CHARSUBST current_charsubst(), #else nullstr, #endif dfltcmd); /* print prompt, whatever it is */ color_pop(); /* of COLOR_CMD */ putchar(' '); fflush(stdout); } return PS_RAISE; /* and pretend we were at end */ case 'd': /* half page */ case Ctl('d'): special = TRUE; slines = tc_LINES / 2 + 1; /* no divide-by-zero, thank you */ if (tc_LINES > 2 && (tc_LINES & 1) && artline % (tc_LINES-2) >= tc_LINES/2 - 1) slines++; goto go_forward; case 'y': case ' ': /* continue current article */ if (erase_screen) { /* -e? */ if (erase_each_line) home_cursor(); else clear(); /* clear screen */ fflush(stdout); } else { special = TRUE; slines = tc_LINES; } go_forward: if (*LINE_PTR(alinebeg) != '\f' #ifdef CUSTOMLINES && (!pagestop || continuation || !execute(&page_compex,LINE_PTR(alinebeg))) #endif ) { if (!special || (marking && (*buf!='d' || (marking_areas&HALFPAGE_MARKING)))) { restart = alinebeg; artline--; /* restart this line */ artpos = alinebeg; if (special) up_line(); else topline = artline; if (marking) highlight = artline; } else slines--; } return PS_NORM; case 'i': if ((auto_view_inline = !auto_view_inline) != 0) first_view = 0; printf("\nAuto-View inlined mime is %s\n", auto_view_inline? "on" : "off"); termdown(2); break; case 'q': /* quit this article? */ return PS_TOEND; default: fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); return PS_ASK; } return PS_ASK; } 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 */ if (innerlight) highlight = innerlight; else 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; termdown(1); } #endif if (hide_everything) { /* forced refresh? */ topline = artline - gline - 1; if (topline < -1) topline = -1; return FALSE; /* let refresh do it all */ } } #ifdef DEBUG if (debug & DEB_INNERSRCH) { printf("Not far enough? %d tc_LINES/2) pushchar(' '); else if (topline != -1) pushchar('b'); break; case 1: if (ap) { select_subthread(ap, 0); special = TRUE; slines = 1; pushchar(Ctl('r')); } else if (y > tc_LINES/2) pushchar('\n'); else if (topline != -1) pushchar('B'); break; case 2: if (ap) { kill_subthread(ap, 0); special = TRUE; slines = 1; pushchar(Ctl('r')); } else if (y > tc_LINES/2) pushchar('n'); else pushchar(Ctl('r')); break; } } trn-4.0-test77/art.h0000644000000000000000000000176607113133015012754 0ustar rootroot/* art.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* do_article() return values */ #define DA_NORM 0 #define DA_RAISE 1 #define DA_CLEAN 2 #define DA_TOEND 3 EXT ART_LINE highlight INIT(-1);/* next line to be highlighted */ EXT ART_LINE first_view; EXT ART_POS raw_artsize; /* size in bytes of raw article */ EXT ART_POS artsize; /* size in bytes of article */ EXT char art_line[LBUFLEN]; /* place for article lines */ EXT int gline INIT(0); EXT ART_POS innersearch INIT(0); /* artpos of end of line we want to visit */ EXT ART_LINE innerlight INIT(0); /* highlight position for innersearch or 0 */ EXT char hide_everything INIT(0);/* if set, do not write page now, */ /* ...but execute char when done with page */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void art_init _((void)); int do_article _((void)); int maybe_set_color _((char*,bool_int)); int page_switch _((void)); bool innermore _((void)); void pager_mouse _((int,int,int,int,int,int)); trn-4.0-test77/artio.c0000644000000000000000000002367311437640112013305 0ustar rootroot/* artio.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "cache.h" #include "rthread.h" #include "head.h" #include "mime.h" #include "term.h" #include "ng.h" #include "art.h" #include "search.h" #include "artstate.h" #include "bits.h" #include "final.h" #include "util.h" #include "util2.h" #include "color.h" #include "decode.h" #include "INTERN.h" #include "artio.h" void artio_init() { artbuf_size = 8 * 1024; artbuf = safemalloc(artbuf_size); clear_artbuf(); } /* open an article, unless it's already open */ FILE* artopen(artnum, pos) ART_NUM artnum; ART_POS pos; { char artname[MAXFILENAME]; /* filename of current article */ ARTICLE* ap = article_find(artnum); if (!ap || !artnum || (ap->flags & (AF_EXISTS|AF_FAKE)) != AF_EXISTS) { errno = ENOENT; return NULL; } if (openart == artnum) { /* this article is already open? */ seekart(pos); /* yes: just seek the file */ return artfp; /* and say we succeeded */ } artclose(); retry_open: #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) nntp_body(artnum); else #endif { sprintf(artname,"%ld",(long)artnum); artfp = fopen(artname,"r"); /*artio_setbuf(artfp);$$*/ } if (!artfp) { #ifdef ETIMEDOUT if (errno == ETIMEDOUT) goto retry_open; #endif if (errno == EINTR) goto retry_open; uncache_article(ap,FALSE); } else { #ifdef LINKART #ifdef SUPPORT_NNTP if (!(datasrc->flags & DF_REMOTE)) #endif { char tmpbuf[256]; char* s; if (!fstat(fileno(artfp),&filestat) && filestat.st_size < sizeof tmpbuf) { fgets(tmpbuf,sizeof tmpbuf,artfp); if (FILE_REF(tmpbuf)) { /* is a "link" to another article */ fclose(artfp); if ((s = index(tmpbuf,'\n')) != NULL) *s = '\0'; if (!(artfp = fopen(tmpbuf,"r"))) uncache_article(ap,FALSE); else { if (*linkartname) free(linkartname); linkartname = savestr(tmpbuf); } } } } #endif openart = artnum; /* remember what we did here */ seekart(pos); } return artfp; /* and return either fp or NULL */ } void artclose() { if (artfp != NULL) { /* article still open? */ #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) nntp_finishbody(FB_DISCARD); #endif fclose(artfp); /* close it */ artfp = NULL; /* and tell the world */ openart = 0; clear_artbuf(); } } int seekart(pos) ART_POS pos; { #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) return nntp_seekart(pos); #endif return fseek(artfp,(long)pos,0); } ART_POS tellart() { #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) return nntp_tellart(); #endif return (ART_POS)ftell(artfp); } char* readart(s, limit) char* s; int limit; { #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) return nntp_readart(s,limit); #endif return fgets(s,limit,artfp); } void clear_artbuf() { *artbuf = '\0'; artbuf_pos = artbuf_seek = artbuf_len = 0; } int seekartbuf(pos) ART_POS pos; { if (!do_hiding) return seekart(pos); pos -= htype[PAST_HEADER].minpos; artbuf_pos = artbuf_len; while (artbuf_pos < pos) { if (!readartbuf(FALSE)) return -1; } artbuf_pos = pos; return 0; } char* readartbuf(view_inline) bool_int view_inline; { char* bp; char* s; int read_offset, line_offset, filter_offset, extra_offset, len, o; int word_wrap, extra_chars = 0; int read_something = 0; if (!do_hiding) { bp = readart(art_line,(sizeof art_line)-1); artbuf_pos = artbuf_seek = tellart() - htype[PAST_HEADER].minpos; return bp; } if (artbuf_pos == artsize - htype[PAST_HEADER].minpos) return NULL; bp = artbuf + artbuf_pos; if (*bp == '\001' || *bp == '\002') { bp++; artbuf_pos++; } if (*bp) { for (s = bp; *s && !AT_NL(*s); s++) ; if (*s) { len = s - bp + 1; goto done; } read_offset = line_offset = filter_offset = s - bp; } else read_offset = line_offset = filter_offset = 0; read_more: extra_offset = mime_state == HTMLTEXT_MIME? 1024 : 0; o = read_offset + extra_offset; if (artbuf_size < artbuf_pos + o + LBUFLEN) { artbuf_size += LBUFLEN * 4; artbuf = saferealloc(artbuf,artbuf_size); bp = artbuf + artbuf_pos; } switch (mime_state) { case IMAGE_MIME: case AUDIO_MIME: break; default: read_something = 1; /* The -1 leaves room for appending a newline, if needed */ if (!readart(bp+o, artbuf_size-artbuf_pos-o-1)) { if (!read_offset) { *bp = '\0'; len = 0; bp = NULL; goto done; } strcpy(bp+o, "\n"); read_something = -1; } len = strlen(bp+o) + read_offset; if (bp[len+extra_offset-1] != '\n') { if (read_something >= 0) { read_offset = len; goto read_more; } strcpy(bp + len++ + extra_offset, "\n"); } if (!is_mime) goto done; o = line_offset + extra_offset; mime_SetState(bp+o); if (bp[o] == '\0') { strcpy(bp+o, "\n"); len = line_offset+1; } break; } mime_switch: switch (mime_state) { case ISOTEXT_MIME: #if 0 /*def CHARSUBST*/ charsubst = "a"; /*$$*/ #endif mime_state = TEXT_MIME; /* FALL THROUGH */ case TEXT_MIME: case HTMLTEXT_MIME: if (mime_section->encoding == MENCODE_QPRINT) { o = line_offset + extra_offset; len = qp_decodestring(bp+o, bp+o, 0) + line_offset; if (len == line_offset || bp[len+extra_offset-1] != '\n') { if (read_something >= 0) { read_offset = line_offset = len; goto read_more; } strcpy(bp + len++ + extra_offset, "\n"); } } else if (mime_section->encoding == MENCODE_BASE64) { o = line_offset + extra_offset; len = b64_decodestring(bp+o, bp+o) + line_offset; if ((s = index(bp+o, '\n')) == NULL) { if (read_something >= 0) { read_offset = line_offset = len; goto read_more; } strcpy(bp + len++ + extra_offset, "\n"); } else { extra_chars += len; len = s - bp - extra_offset + 1; extra_chars -= len; } } if (mime_state != HTMLTEXT_MIME) break; o = filter_offset + extra_offset; len = filter_html(bp+filter_offset, bp+o) + filter_offset; if (len == filter_offset || (s = index(bp,'\n')) == NULL) { if (read_something >= 0) { read_offset = line_offset = filter_offset = len; goto read_more; } strcpy(bp + len++, "\n"); extra_chars = 0; } else { extra_chars = len; len = s - bp + 1; extra_chars -= len; } break; case DECODE_MIME: { MIMECAP_ENTRY* mcp; mcp = mime_FindMimecapEntry(mime_section->type_name, MCF_NEEDSTERMINAL|MCF_COPIOUSOUTPUT); if (mcp) { int save_term_line = term_line; nowait_fork = TRUE; color_object(COLOR_MIMEDESC, 1); if (decode_piece(mcp,bp) != 0) { strcpy(bp = artbuf + artbuf_pos, art_line); mime_SetState(bp); if (mime_state == DECODE_MIME) mime_state = SKIP_MIME; } else mime_state = SKIP_MIME; color_pop(); chdir_newsdir(); erase_line(FALSE); nowait_fork = FALSE; first_view = artline; term_line = save_term_line; if (mime_state != SKIP_MIME) goto mime_switch; } /* FALL THROUGH */ } case SKIP_MIME: { MIME_SECT* mp = mime_section; while ((mp = mp->prev) != NULL && !mp->boundary_len) ; if (!mp) { artbuf_len = artbuf_pos; artsize = artbuf_len + htype[PAST_HEADER].minpos; read_something = 0; bp = NULL; } else if (read_something >= 0) { *bp = '\0'; read_offset = line_offset = filter_offset = 0; goto read_more; } else *bp = '\0'; len = 0; break; } case END_OF_MIME: if (mime_section->prev) mime_state = SKIP_MIME; else { #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) { nntp_finishbody(FB_SILENT); raw_artsize = nntp_artsize(); } #endif seekart(raw_artsize); } /* FALL THROUGH */ case BETWEEN_MIME: len = strlen(multipart_separator) + 1; if (extra_offset && filter_offset) { extra_chars = len + 1; len = o = read_offset + 1; bp[o-1] = '\n'; } else { o = -1; artbuf_pos++; bp++; } sprintf(bp+o,"\002%s\n",multipart_separator); break; case UNHANDLED_MIME: mime_state = SKIP_MIME; *bp++ = '\001'; artbuf_pos++; mime_Description(mime_section,bp,tc_COLS); len = strlen(bp); break; case ALTERNATE_MIME: mime_state = SKIP_MIME; *bp++ = '\001'; artbuf_pos++; sprintf(bp,"[Alternative: %s]\n", mime_section->type_name); len = strlen(bp); break; case IMAGE_MIME: case AUDIO_MIME: if (!mime_article.total && !multimedia_mime) multimedia_mime = TRUE; /* FALL THROUGH */ default: if (view_inline && first_view < artline && (mime_section->flags & MSF_INLINE)) mime_state = DECODE_MIME; else mime_state = SKIP_MIME; *bp++ = '\001'; artbuf_pos++; mime_Description(mime_section,bp,tc_COLS); len = strlen(bp); break; } done: word_wrap = tc_COLS - word_wrap_offset; if (read_something && word_wrap_offset >= 0 && word_wrap > 20 && bp) { char* cp; for (cp = bp; *cp && (s = index(cp, '\n')) != NULL; cp = s+1) { if (s - cp > tc_COLS) { char* t; do { for (t = cp+word_wrap; *t!=' ' && *t!='\t' && t > cp; t--) ; if (t == cp) { for (t = cp+word_wrap; *t!=' ' && *t!='\t' && t<=cp+tc_COLS; t++) ; if (t > cp+tc_COLS) { t = cp + tc_COLS - 1; continue; } } if (cp == bp) { extra_chars += len; len = t - bp + 1; extra_chars -= len; } *t = wrapped_nl; if (t[1] == ' ' || t[1] == '\t') { int spaces = 1; for (t++; *++t == ' ' || *t == '\t'; spaces++) ; safecpy(t-spaces,t,extra_chars); extra_chars -= spaces; t -= spaces + 1; } } while (s - (cp = t+1) > word_wrap); } } } artbuf_pos += len; if (read_something) { artbuf_seek = tellart(); artbuf_len = artbuf_pos + extra_chars; if (artsize >= 0) artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos; } return bp; } trn-4.0-test77/artio.h0000644000000000000000000000174707113133015013303 0ustar rootroot/* artio.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT ART_POS artpos INIT(0); /* byte position in article file */ EXT ART_LINE artline INIT(0); /* current line number in article file */ EXT FILE* artfp INIT(NULL); /* current article file pointer */ EXT ART_NUM openart INIT(0); /* the article number we have open */ EXT char* artbuf; EXT long artbuf_size; EXT long artbuf_pos; EXT long artbuf_seek; EXT long artbuf_len; #define WRAPPED_NL '\003' #define AT_NL(c) ((c) == '\n' || (c) == WRAPPED_NL) EXT char wrapped_nl INIT(WRAPPED_NL); #ifdef LINKART EXT char* linkartname INIT(nullstr);/* real name of article for Eunice */ #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void artio_init _((void)); FILE* artopen _((ART_NUM,ART_POS)); void artclose _((void)); int seekart _((ART_POS)); ART_POS tellart _((void)); char* readart _((char*,int)); void clear_artbuf _((void)); int seekartbuf _((ART_POS)); char* readartbuf _((bool_int)); trn-4.0-test77/artsrch.c0000644000000000000000000002543407113133015013625 0ustar rootroot/* artsrch.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "search.h" #include "term.h" #include "util.h" #include "util2.h" #include "intrp.h" #include "cache.h" #include "bits.h" #include "kfile.h" #include "head.h" #include "final.h" #include "ng.h" #include "addng.h" #include "ngstuff.h" #include "artio.h" #include "rthread.h" #include "rt-util.h" #include "rt-select.h" #include "INTERN.h" #include "artsrch.h" void artsrch_init() { #ifdef ARTSEARCH init_compex(&sub_compex); init_compex(&art_compex); #endif } /* search for an article containing some pattern */ #ifdef ARTSEARCH int art_search(patbuf,patbufsiz,get_cmd) char* patbuf; /* if patbuf != buf, get_cmd must */ int patbufsiz; int get_cmd; /* be set to FALSE!!! */ { char* pattern; /* unparsed pattern */ register char cmdchr = *patbuf; /* what kind of search? */ register char* s; bool backward = cmdchr == '?' || cmdchr == Ctl('p'); /* direction of search */ COMPEX* compex; /* which compiled expression */ char* cmdlst = NULL; /* list of commands to do */ int ret = SRCH_NOTFOUND; /* assume no commands */ int saltaway = 0; /* store in KILL file? */ int howmuch; /* search scope: subj/from/Hdr/head/art */ int srchhdr; /* header to search if Hdr scope */ bool topstart = 0; bool doread; /* search read articles? */ bool foldcase = TRUE; /* fold upper and lower case? */ int ignorethru = 0; /* should we ignore the thru line? */ bool output_level = (!use_threads && gmode != 's'); ART_NUM srchfirst; int_count = 0; if (cmdchr == '/' || cmdchr == '?') { /* normal search? */ if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return SRCH_ABORT; compex = &art_compex; if (patbuf[1]) { howmuch = ARTSCOPE_SUBJECT; srchhdr = SOME_LINE; doread = FALSE; } else { howmuch = art_howmuch; srchhdr = art_srchhdr; doread = art_doread; } s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */ pattern = buf; if (*pattern) { if (*lastpat) free(lastpat); lastpat = savestr(pattern); } if (*s) { /* modifiers or commands? */ while (*++s) { switch (*s) { case 'f': /* scan the From line */ howmuch = ARTSCOPE_FROM; break; case 'H': /* scan a specific header */ howmuch = ARTSCOPE_ONEHDR; s = cpytill(msg, s+1, ':'); srchhdr = get_header_num(msg); goto loop_break; case 'h': /* scan header */ howmuch = ARTSCOPE_HEAD; break; case 'b': /* scan body sans signature */ howmuch = ARTSCOPE_BODY_NOSIG; break; case 'B': /* scan body */ howmuch = ARTSCOPE_BODY; break; case 'a': /* scan article */ howmuch = ARTSCOPE_ARTICLE; break; case 't': /* start from the top */ topstart = TRUE; break; case 'r': /* scan read articles */ doread = TRUE; break; case 'K': /* put into KILL file */ saltaway = 1; break; case 'c': /* make search case sensitive */ foldcase = FALSE; break; case 'I': /* ignore the killfile thru line */ ignorethru = 1; break; case 'N': /* override ignore if -k was used */ ignorethru = -1; break; default: goto loop_break; } } loop_break:; } while (isspace(*s) || *s == ':') s++; if (*s) { #ifdef OLD_RN_WAY if (*s == 'm' || *s == 'M') #else if (*s == 'm') #endif doread = TRUE; if (*s == 'k') /* grandfather clause */ *s = 'j'; cmdlst = savestr(s); ret = SRCH_DONE; } art_howmuch = howmuch; art_srchhdr = srchhdr; art_doread = doread; if (srchahead) srchahead = -1; } else { register char* h; int saltmode = patbuf[2] == 'g'? 2 : 1; char *finding_str = patbuf[1] == 'f'? "author" : "subject"; howmuch = patbuf[1] == 'f'? ARTSCOPE_FROM : ARTSCOPE_SUBJECT; srchhdr = SOME_LINE; doread = (cmdchr == Ctl('p')); if (cmdchr == Ctl('n')) ret = SRCH_SUBJDONE; compex = &sub_compex; pattern = patbuf+1; if (howmuch == ARTSCOPE_SUBJECT) { strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,patbufsiz - (h-patbuf),"%\\s"); /* fetch current subject */ } else { h = pattern; /*$$ if using thread files, make this "%\\)f" */ interp(pattern, patbufsiz - 1, "%\\>f"); } if (cmdchr == 'k' || cmdchr == 'K' || cmdchr == ',' || cmdchr == '+' || cmdchr == '.' || cmdchr == 's') { if (cmdchr != 'k') saltaway = saltmode; ret = SRCH_DONE; if (cmdchr == '+') { cmdlst = savestr("+"); if (!ignorethru && kill_thru_kludge) ignorethru = 1; } else if (cmdchr == '.') { cmdlst = savestr("."); if (!ignorethru && kill_thru_kludge) ignorethru = 1; } else if (cmdchr == 's') { cmdlst = savestr(patbuf); /*ignorethru = 1;*/ } else { if (cmdchr == ',') cmdlst = savestr(","); else cmdlst = savestr("j"); mark_as_read(article_ptr(art)); /* this article needs to die */ } if (!*h) { #ifdef VERBOSE IF(verbose) sprintf(msg, "Current article has no %s.", finding_str); ELSE #endif #ifdef TERSE sprintf(msg, "Null %s.", finding_str); #endif errormsg(msg); ret = SRCH_ABORT; goto exit; } #ifdef VERBOSE if (verbose) { if (cmdchr != '+' && cmdchr != '.') printf("\nMarking %s \"%s\" as read.\n",finding_str,h) FLUSH; else printf("\nSelecting %s \"%s\".\n",finding_str,h) FLUSH; termdown(2); } #endif } else if (!srchahead) srchahead = -1; { /* compensate for notesfiles */ register int i; for (i = 24; *h && i--; h++) if (*h == '\\') h++; *h = '\0'; } #ifdef DEBUG if (debug) { printf("\npattern = %s\n",pattern) FLUSH; termdown(2); } #endif } if ((s = compile(compex,pattern,TRUE,foldcase)) != NULL) { /* compile regular expression */ errormsg(s); ret = SRCH_ABORT; goto exit; } if (cmdlst && index(cmdlst,'=')) ret = SRCH_ERROR; /* listing subjects is an error? */ if (gmode == 's') { if (!cmdlst) { if (sel_mode == SM_ARTICLE)/* set the selector's default command */ cmdlst = savestr("+"); else cmdlst = savestr("++"); } ret = SRCH_DONE; } #ifdef KILLFILES if (saltaway) { char saltbuf[LBUFLEN], *f; s = saltbuf; f = pattern; *s++ = '/'; while (*f) { if (*f == '/') *s++ = '\\'; *s++ = *f++; } *s++ = '/'; if (doread) *s++ = 'r'; if (!foldcase) *s++ = 'c'; if (ignorethru) *s++ = (ignorethru == 1 ? 'I' : 'N'); if (howmuch != ARTSCOPE_SUBJECT) { *s++ = scopestr[howmuch]; if (howmuch == ARTSCOPE_ONEHDR) { safecpy(s,htype[srchhdr].name,LBUFLEN-(s-saltbuf)); s += htype[srchhdr].length; if (s - saltbuf > LBUFLEN-2) s = saltbuf+LBUFLEN-2; } } *s++ = ':'; if (!cmdlst) cmdlst = savestr("j"); safecpy(s,cmdlst,LBUFLEN-(s-saltbuf)); kf_append(saltbuf, saltaway == 2? KF_GLOBAL : KF_LOCAL); } #endif if (get_cmd) { if (use_threads) newline(); else { fputs("\nSearching...\n",stdout) FLUSH; termdown(2); } /* give them something to read */ } if (ignorethru == 0 && kill_thru_kludge && cmdlst && (*cmdlst == '+' || *cmdlst == '.')) ignorethru = 1; srchfirst = doread || sel_rereading? absfirst : (mode != 'k' || ignorethru > 0)? firstart : killfirst; if (topstart || art == 0) { art = lastart+1; topstart = FALSE; } if (backward) { if (cmdlst && art <= lastart) art++; /* include current article */ } else { if (art > lastart) art = srchfirst-1; else if (cmdlst && art >= absfirst) art--; /* include current article */ } if (srchahead > 0) { if (!backward) art = srchahead - 1; srchahead = -1; } assert(!cmdlst || *cmdlst); perform_status_init(ngptr->toread); for (;;) { /* check if we're out of articles */ if (backward? ((art = article_prev(art)) < srchfirst) : ((art = article_next(art)) > lastart)) break; if (int_count) { int_count = 0; ret = SRCH_INTR; break; } artp = article_ptr(art); if (doread || (!(artp->flags & AF_UNREAD) ^ !sel_rereading)) { if (wanted(compex,art,howmuch)) { /* does the shoe fit? */ if (!cmdlst) return SRCH_FOUND; if (perform(cmdlst,output_level && page_line == 1) < 0) { free(cmdlst); return SRCH_INTR; } } else if (output_level && !cmdlst && !(art%50)) { printf("...%ld",(long)art); fflush(stdout); } } if (!output_level && page_line == 1) perform_status(ngptr->toread, 60 / (howmuch+1)); } exit: if (cmdlst) free(cmdlst); return ret; } #endif /* ARTSEARCH */ /* determine if article fits pattern */ /* returns TRUE if it exists and fits pattern, FALSE otherwise */ #ifdef ARTSEARCH bool wanted(compex, artnum, scope) COMPEX* compex; ART_NUM artnum; char_int scope; { ARTICLE* ap = article_find(artnum); if (!ap || !(ap->flags & AF_EXISTS)) return FALSE; switch (scope) { case ARTSCOPE_SUBJECT: strcpy(buf,"Subject: "); strncpy(buf+9,fetchsubj(artnum,FALSE),256); #ifdef DEBUG if (debug & DEB_SEARCH_AHEAD) printf("%s\n",buf) FLUSH; #endif break; case ARTSCOPE_FROM: strcpy(buf, "From: "); strncpy(buf+6,fetchfrom(artnum,FALSE),256); break; case ARTSCOPE_ONEHDR: untrim_cache = TRUE; sprintf(buf, "%s: %s", htype[art_srchhdr].name, prefetchlines(artnum,art_srchhdr,FALSE)); untrim_cache = FALSE; break; default: { char* s; char* nlptr; char ch; bool success = FALSE, in_sig = FALSE; if (scope != ARTSCOPE_BODY && scope != ARTSCOPE_BODY_NOSIG) { if (!parseheader(artnum)) return FALSE; /* see if it's in the header */ if (execute(compex,headbuf)) /* does it match? */ return TRUE; /* say, "Eureka!" */ if (scope < ARTSCOPE_ARTICLE) return FALSE; } if (parsed_art == artnum) { if (!artopen(artnum,htype[PAST_HEADER].minpos)) return FALSE; } else { if (!artopen(artnum,(ART_POS)0)) return FALSE; if (!parseheader(artnum)) return FALSE; } /* loop through each line of the article */ seekartbuf(htype[PAST_HEADER].minpos); while ((s = readartbuf(FALSE)) != NULL) { if (scope == ARTSCOPE_BODY_NOSIG && *s == '-' && s[1] == '-' && (s[2] == '\n' || (s[2] == ' ' && s[3] == '\n'))) { if (in_sig && success) return TRUE; in_sig = TRUE; } if ((nlptr = index(s,'\n')) != NULL) { ch = *++nlptr; *nlptr = '\0'; } success = success || execute(compex,s) != NULL; if (nlptr) *nlptr = ch; if (success && !in_sig) /* does it match? */ return TRUE; /* say, "Eureka!" */ } return FALSE; /* out of article, so no match */ } } return execute(compex,buf) != NULL; } #endif /* ARTSEARCH */ trn-4.0-test77/artsrch.h0000644000000000000000000000220507113133015013621 0ustar rootroot/* artsrch.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifndef NBRA #include "search.h" #endif #ifdef ARTSEARCH #define SRCH_ABORT 0 #define SRCH_INTR 1 #define SRCH_FOUND 2 #define SRCH_NOTFOUND 3 #define SRCH_DONE 4 #define SRCH_SUBJDONE 5 #define SRCH_ERROR 6 #endif EXT char* lastpat INIT(nullstr); /* last search pattern */ #ifdef ARTSEARCH EXT COMPEX sub_compex; /* last compiled subject search */ EXT COMPEX art_compex; /* last compiled normal search */ EXT COMPEX* bra_compex INIT(&(art_compex)); /* current compex with brackets */ #define ARTSCOPE_SUBJECT 0 #define ARTSCOPE_FROM 1 #define ARTSCOPE_ONEHDR 2 #define ARTSCOPE_HEAD 3 #define ARTSCOPE_BODY_NOSIG 4 #define ARTSCOPE_BODY 5 #define ARTSCOPE_ARTICLE 6 EXT char scopestr[] INIT("sfHhbBa"); EXT int art_howmuch; /* search scope */ EXT int art_srchhdr; /* specific header number to search */ EXT bool art_doread; /* search read articles? */ #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void artsrch_init _((void)); #ifdef ARTSEARCH int art_search _((char*,int,int)); bool wanted _((COMPEX*,ART_NUM,char_int)); #endif trn-4.0-test77/artstate.h0000644000000000000000000000167307113133015014012 0ustar rootroot/* artstate.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT bool reread INIT(FALSE); /* consider current art temporarily */ /* unread? */ EXT bool do_fseek INIT(FALSE); /* should we back up in article file? */ EXT bool oldsubject INIT(FALSE); /* not 1st art in subject thread */ EXT ART_LINE topline INIT(-1); /* top line of current screen */ EXT bool do_hiding INIT(TRUE); /* hide header lines with -h? */ EXT bool is_mime INIT(FALSE); /* process mime in an article? */ EXT bool multimedia_mime INIT(FALSE); /* images/audio to see/hear? */ EXT bool rotate INIT(FALSE); /* has rotation been requested? */ EXT char* prompt; /* pointer to current prompt */ EXT char* firstline INIT(NULL); /* special first line? */ #ifdef CUSTOMLINES EXT char* hideline INIT(NULL); /* custom line hiding? */ EXT char* pagestop INIT(NULL); /* custom page terminator? */ EXT COMPEX hide_compex; EXT COMPEX page_compex; #endif trn-4.0-test77/autosub.c0000644000000000000000000000263107113133015013633 0ustar rootroot/* autosub.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "list.h" #include "ngdata.h" #include "ngsrch.h" #include "env.h" #include "util.h" #include "util2.h" #include "final.h" #include "INTERN.h" #include "autosub.h" /* Consider the newsgroup specified, and return: */ /* : if we should autosubscribe to it */ /* ! if we should autounsubscribe to it */ /* \0 if we should ask the user. */ int auto_subscribe(name) char* name; { char* s; if((s = getval("AUTOSUBSCRIBE", (char*)NULL)) && matchlist(s, name)) return ':'; if((s = getval("AUTOUNSUBSCRIBE", (char*)NULL)) && matchlist(s, name)) return '!'; return 0; } bool matchlist(patlist, s) char* patlist; char* s; { COMPEX ilcompex; char* p; char* err; bool result; bool tmpresult; result = FALSE; init_compex(&ilcompex); while(patlist && *patlist) { if (*patlist == '!') { patlist++; tmpresult = FALSE; } else tmpresult = TRUE; if ((p = index(patlist, ',')) != NULL) *p = '\0'; /* compile regular expression */ err = ng_comp(&ilcompex,patlist,TRUE,TRUE); if (p) *p++ = ','; if (err != NULL) { printf("\n%s\n", err) FLUSH; finalize(1); } if (execute(&ilcompex,s) != NULL) result = tmpresult; patlist = p; } free_compex(&ilcompex); return result; } trn-4.0-test77/autosub.h0000644000000000000000000000032707113133015013640 0ustar rootroot/* autosub.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int auto_subscribe _((char*)); bool matchlist _((char*,char*)); trn-4.0-test77/backpage.c0000644000000000000000000000371007113133015013705 0ustar rootroot/* backpage.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "util2.h" #include "final.h" #include "INTERN.h" #include "backpage.h" ART_LINE maxindx = -1; void backpage_init() { char* varyname; varyname = filexp(VARYNAME); close(creat(varyname,0600)); varyfd = open(varyname,2); UNLINK(varyname); if (varyfd < 0) { printf(cantopen,varyname) FLUSH; sig_catcher(0); } } /* virtual array read */ ART_POS vrdary(indx) ART_LINE indx; { int subindx; long offset; #ifdef DEBUG if (indx > maxindx) { printf("vrdary(%ld) > %ld\n",(long)indx, (long)maxindx) FLUSH; return 0; } #endif if (indx < 0) return 0; subindx = indx % VARYSIZE; offset = (indx - subindx) * sizeof(varybuf[0]); if (offset != oldoffset) { if (oldoffset >= 0) { #ifndef lint (void)lseek(varyfd,oldoffset,0); write(varyfd, (char*)varybuf,sizeof(varybuf)); #endif /* lint */ } #ifndef lint (void)lseek(varyfd,offset,0); read(varyfd,(char*)varybuf,sizeof(varybuf)); #endif /* lint */ oldoffset = offset; } return varybuf[subindx]; } /* write to virtual array */ void vwtary(indx,newvalue) ART_LINE indx; ART_POS newvalue; { int subindx; long offset; #ifdef DEBUG if (indx < 0) printf("vwtary(%ld)\n",(long)indx) FLUSH; if (!indx) maxindx = 0; if (indx > maxindx) { if (indx != maxindx + 1) printf("indx skipped %d-%d\n",maxindx+1,indx-1) FLUSH; maxindx = indx; } #endif subindx = indx % VARYSIZE; offset = (indx - subindx) * sizeof(varybuf[0]); if (offset != oldoffset) { if (oldoffset >= 0) { #ifndef lint (void)lseek(varyfd,oldoffset,0); write(varyfd,(char*)varybuf,sizeof(varybuf)); #endif /* lint */ } #ifndef lint (void)lseek(varyfd,offset,0); read(varyfd,(char*)varybuf,sizeof(varybuf)); #endif /* lint */ oldoffset = offset; } varybuf[subindx] = newvalue; } trn-4.0-test77/backpage.h0000644000000000000000000000102107113133015013703 0ustar rootroot/* backpage.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* things for doing the 'back page' command */ EXT int varyfd INIT(0); /* virtual array file for storing */ /* file offsets */ EXT ART_POS varybuf[VARYSIZE]; /* current window onto virtual array */ EXT long oldoffset INIT(-1); /* offset to block currently in window */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void backpage_init _((void)); ART_POS vrdary _((ART_LINE)); void vwtary _((ART_LINE,ART_POS)); trn-4.0-test77/bits.c0000644000000000000000000004607211437640112013126 0ustar rootroot/* bits.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "hash.h" #include "cache.h" #include "list.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "rcstuff.h" #include "head.h" #include "term.h" #include "util.h" #include "util2.h" #include "final.h" #include "trn.h" #include "ng.h" #include "artio.h" #include "intrp.h" #include "rcln.h" #include "ndir.h" #include "kfile.h" #include "rthread.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #include "INTERN.h" #include "bits.h" #include "bits.ih" #ifdef DBM_XREFS # ifdef NULL # undef NULL # endif # include #endif static long chase_count = 0; void bits_init() { ; } void rc_to_bits() { char* mybuf = buf; /* place to decode rc line */ register char* s; register char* c; register char* h; register long i; register ART_NUM unread; ARTICLE* ap; /* modify the article flags to reflect what has already been read */ for (s = ngptr->rcline + ngptr->numoffset; *s == ' '; s++) ; /* find numbers in rc line */ i = strlen(s); #ifndef lint if (i >= LBUFLEN-2) /* bigger than buf? */ mybuf = safemalloc((MEM_SIZE)(i+2)); #endif strcpy(mybuf,s); /* make scratch copy of line */ if (mybuf[0]) mybuf[i++] = ','; /* put extra comma on the end */ mybuf[i] = '\0'; s = mybuf; /* initialize the for loop below */ if (set_firstart(s)) { s = index(s,',') + 1; for (i = article_first(absfirst); i < firstart; i = article_next(i)) article_ptr(i)->flags &= ~AF_UNREAD; firstart = i; } else firstart = article_first(firstart); unread = 0; #ifdef DEBUG if (debug & DEB_CTLAREA_BITMAP) { printf("\n%s\n",mybuf) FLUSH; termdown(2); for (i = article_first(absfirst); i < firstart; i = article_next(i)) { if (article_unread(i)) printf("%ld ",(long)i) FLUSH; } } #endif i = firstart; for ( ; (c = index(s,',')) != NULL; s = ++c) { /* for each range */ ART_NUM min, max; *c = '\0'; /* do not let index see past comma */ h = index(s,'-'); min = atol(s); if (min < firstart) /* make sure range is in range */ min = firstart; if (min > lastart) min = lastart+1; for (; i < min; i = article_next(i)) { ap = article_ptr(i); if (ap->flags & AF_EXISTS) { if (ap->autofl & AUTO_KILLS) ap->flags &= ~AF_UNREAD; else { ap->flags |= AF_UNREAD; unread++; if (ap->autofl & AUTO_SELS) select_article(ap, ap->autofl); } } } if (!h) max = min; else if ((max = atol(h+1)) < min) max = min-1; if (max > lastart) max = lastart; /* mark all arts in range as read */ for ( ; i <= max; i = article_next(i)) article_ptr(i)->flags &= ~AF_UNREAD; #ifdef DEBUG if (debug & DEB_CTLAREA_BITMAP) { printf("\n%s\n",s) FLUSH; termdown(2); for (i = absfirst; i <= lastart; i++) { if (!was_read(i)) printf("%ld ",(long)i) FLUSH; } } #endif i = article_next(max); } for (; i <= lastart; i = article_next(i)) { ap = article_ptr(i); if (ap->flags & AF_EXISTS) { if (ap->autofl & AUTO_KILLS) ap->flags &= ~AF_UNREAD; else { ap->flags |= AF_UNREAD; unread++; if (ap->autofl & AUTO_SELS) select_article(ap, ap->autofl); } } } #ifdef DEBUG if (debug & DEB_CTLAREA_BITMAP) { fputs("\n(hit CR)",stdout) FLUSH; termdown(1); fgets(cmd_buf, sizeof cmd_buf, stdin); } #endif if (mybuf != buf) free(mybuf); ngptr->toread = unread; } bool set_firstart(s) char* s; { while (*s == ' ') s++; if (strnEQ(s,"1-",2)) { /* can we save some time here? */ firstart = atol(s+2)+1; /* process first range thusly */ if (firstart < absfirst) firstart = absfirst; return TRUE; } firstart = absfirst; return FALSE; } /* reconstruct the .newsrc line in a human readable form */ void bits_to_rc() { register char* s; register char* mybuf = buf; register ART_NUM i; ART_NUM count=0; int safelen = LBUFLEN - 32; strcpy(buf,ngptr->rcline); /* start with the newsgroup name */ s = buf + ngptr->numoffset - 1; /* use s for buffer pointer */ *s++ = ngptr->subscribechar; /* put the requisite : or !*/ for (i = article_first(absfirst); i <= lastart; i = article_next(i)) { if (article_unread(i)) break; } sprintf(s," 1-%ld,",(long)i-1); s += strlen(s); for (; i<=lastart; i++) { /* for each article in newsgroup */ if (s-mybuf > safelen) { /* running out of room? */ safelen *= 2; if (mybuf == buf) { /* currently static? */ *s = '\0'; mybuf = safemalloc((MEM_SIZE)safelen + 32); strcpy(mybuf,buf); /* so we must copy it */ s = mybuf + (s-buf); /* fix the pointer, too */ } else { /* just grow in place, if possible */ int oldlen = s - mybuf; mybuf = saferealloc(mybuf,(MEM_SIZE)safelen + 32); s = mybuf + oldlen; } } if (!was_read(i)) /* still unread? */ count++; /* then count it */ else { /* article was read */ ART_NUM oldi; sprintf(s,"%ld",(long)i); /* put out the min of the range */ s += strlen(s); /* keeping house */ oldi = i; /* remember this spot */ do i++; while (i <= lastart && was_read(i)); /* find 1st unread article or end */ i--; /* backup to last read article */ if (i > oldi) { /* range of more than 1? */ sprintf(s,"-%ld,",(long)i); /* then it out as a range */ s += strlen(s); /* and housekeep */ } else *s++ = ','; /* otherwise, just a comma will do */ } } if (*(s-1) == ',') /* is there a final ','? */ s--; /* take it back */ *s++ = '\0'; /* and terminate string */ #ifdef DEBUG if ((debug & DEB_NEWSRC_LINE) && !panic) { printf("%s: %s\n",ngptr->rcline,ngptr->rcline+ngptr->numoffset) FLUSH; printf("%s\n",mybuf) FLUSH; termdown(2); } #endif free(ngptr->rcline); /* return old rc line */ if (mybuf == buf) { ngptr->rcline = safemalloc((MEM_SIZE)(s-buf)+1); /* grab a new rc line */ strcpy(ngptr->rcline, buf); /* and load it */ } else { mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1); /* be nice to the heap */ ngptr->rcline = mybuf; } *(ngptr->rcline + ngptr->numoffset - 1) = '\0'; if (ngptr->subscribechar == NEGCHAR)/* did they unsubscribe? */ ngptr->toread = TR_UNSUB; /* make line invisible */ else ngptr->toread = (ART_UNREAD)count; /* otherwise, remember the count */ ngptr->rc->flags |= RF_RCCHANGED; } void find_existing_articles() { ART_NUM an; ARTICLE* ap; #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) { /* Parse the LISTGROUP output and remember everything we find */ if (/*nntp_rover() ||*/ nntp_artnums()) { /*char* s;*/ for (ap = article_ptr(article_first(absfirst)); ap && article_num(ap) <= lastart; ap = article_nextp(ap)) ap->flags &= ~AF_EXISTS; for (;;) { if (nntp_gets(ser_line, sizeof ser_line) < 0) break; /*$$*/ if (nntp_at_list_end(ser_line)) break; an = (ART_NUM)atol(ser_line); if (an < absfirst) continue; /* Ignore some wacked-out NNTP servers */ ap = article_ptr(an); if (!(ap->flags2 & AF2_BOGUS)) ap->flags |= AF_EXISTS; #if 0 s = index(ser_line, ' '); if (s) rover_thread(article_ptr(an), s); #endif } } else if (first_subject && cached_all_in_range) { if (!datasrc->ov_opened || datasrc->over_dir != NULL) { for (ap = article_ptr(article_first(first_cached)); ap && article_num(ap) <= last_cached; ap = article_nextp(ap)) { if (ap->flags & AF_CACHED) ap->flags |= AF_EXISTS; } } for (an = absfirst; an < first_cached; an++) { ap = article_ptr(an); if (!(ap->flags2 & AF2_BOGUS)) ap->flags |= AF_EXISTS; } for (an = last_cached+1; an <= lastart; an++) { ap = article_ptr(an); if (!(ap->flags2 & AF2_BOGUS)) ap->flags |= AF_EXISTS; } } else { for (an = absfirst; an <= lastart; an++) { ap = article_ptr(an); if (!(ap->flags2 & AF2_BOGUS)) ap->flags |= AF_EXISTS; } } } else #endif { ART_NUM first = lastart+1; ART_NUM last = 0; DIR* dirp; Direntry_t* dp; char ch; long lnum; /* Scan the directory to find which articles are present. */ if (!(dirp = opendir("."))) return; for (ap = article_ptr(article_first(absfirst)); ap && article_num(ap) <= lastart; ap = article_nextp(ap)) ap->flags &= ~AF_EXISTS; while ((dp = readdir(dirp)) != NULL) { if (sscanf(dp->d_name, "%ld%c", &lnum, &ch) == 1) { an = (ART_NUM)lnum; if (an <= lastart && an >= absfirst) { if (an < first) first = an; if (an > last) last = an; ap = article_ptr(an); if (!(ap->flags2 & AF2_BOGUS)) ap->flags |= AF_EXISTS; } } } closedir(dirp); ngptr->abs1st = first; ngptr->ngmax = last; if (first > absfirst) { checkexpired(ngptr,first); for (absfirst = article_first(absfirst); absfirst < first; absfirst = article_next(absfirst)) { onemissing(article_ptr(absfirst)); } absfirst = first; } lastart = last; } if (firstart < absfirst) firstart = absfirst; if (firstart > lastart) firstart = lastart + 1; if (first_cached < absfirst) first_cached = absfirst; if (last_cached < absfirst) last_cached = absfirst - 1; } /* mark an article unread, keeping track of toread[] */ void onemore(ap) ARTICLE* ap; { if (!(ap->flags & AF_UNREAD)) { register ART_NUM artnum = article_num(ap); check_first(artnum); ap->flags |= AF_UNREAD; ap->flags &= ~AF_DEL; ngptr->toread++; if (ap->subj) { if (selected_only) { if (ap->subj->flags & sel_mask) { ap->flags |= sel_mask; selected_count++; } } else ap->subj->flags |= SF_VISIT; } } } /* mark an article read, keeping track of toread[] */ void oneless(ap) ARTICLE* ap; { if (ap->flags & AF_UNREAD) { ap->flags &= ~AF_UNREAD; /* Keep selected_count accurate */ if (ap->flags & sel_mask) { selected_count--; ap->flags &= ~sel_mask; } if (ngptr->toread > TR_NONE) ngptr->toread--; } } void oneless_artnum(artnum) ART_NUM artnum; { ARTICLE* ap = article_find(artnum); if (ap) oneless(ap); } void onemissing(ap) ARTICLE* ap; { missing_count += (ap->flags & AF_UNREAD) != 0; oneless(ap); ap->flags = (ap->flags & ~(AF_HAS_RE|AF_YANKBACK|AF_FROMTRUNCED|AF_EXISTS)) | AF_CACHED|AF_THREADED; } /* mark an article as unread, with possible xref chasing */ void unmark_as_read(ap) register ARTICLE* ap; { onemore(ap); #ifdef MCHASE if (ap->xrefs != nullstr && !(ap->flags & AF_MCHASE)) { ap->flags |= AF_MCHASE; chase_count++; } #endif } /* Mark an article as read in this newsgroup and possibly chase xrefs. ** Don't call this on missing articles. */ void set_read(ap) register ARTICLE* ap; { oneless(ap); if (!olden_days && ap->xrefs != nullstr && !(ap->flags & AF_KCHASE)) { ap->flags |= AF_KCHASE; chase_count++; } } /* temporarily mark article as read. When newsgroup is exited, articles */ /* will be marked as unread. Called via M command */ void delay_unmark(ap) ARTICLE* ap; { if (!(ap->flags & AF_YANKBACK)) { ap->flags |= AF_YANKBACK; dmcount++; } } /* mark article as read. If article is cross referenced to other */ /* newsgroups, mark them read there also. */ void mark_as_read(ap) register ARTICLE* ap; { oneless(ap); if (ap->xrefs != nullstr && !(ap->flags & AF_KCHASE)) { ap->flags |= AF_KCHASE; chase_count++; } checkcount++; /* get more worried about crashes */ } void mark_missing_articles() { register ARTICLE* ap; for (ap = article_ptr(article_first(absfirst)); ap && article_num(ap) <= lastart; ap = article_nextp(ap)) { if (!(ap->flags & AF_EXISTS)) onemissing(ap); } } /* keep firstart pointing at the first unread article */ void check_first(min) ART_NUM min; { if (min < absfirst) min = absfirst; if (min < firstart) firstart = min; } /* bring back articles marked with M */ void yankback() { if (dmcount) { /* delayed unmarks pending? */ if (panic) ; else if (gmode == 's') sprintf(msg, "Returned %ld Marked article%s.",(long)dmcount, PLURAL(dmcount)); else { #ifdef VERBOSE printf("\nReturning %ld Marked article%s...\n",(long)dmcount, PLURAL(dmcount)) FLUSH; #endif termdown(2); } article_walk(yank_article, 0); dmcount = 0; } } static bool yank_article(ptr, arg) char* ptr; int arg; { register ARTICLE* ap = (ARTICLE*)ptr; if (ap->flags & AF_YANKBACK) { unmark_as_read(ap); if (selected_only) select_article(ap, 0); ap->flags &= ~AF_YANKBACK; } return 0; } int chase_xrefs(until_key) bool_int until_key; { if (!chase_count) return 1; if (until_key) setspin(SPIN_BACKGROUND); article_walk(check_chase, until_key); chase_count = 0; return 1; } static bool check_chase(ptr, until_key) char* ptr; int until_key; { register ARTICLE* ap = (ARTICLE*)ptr; if (ap->flags & AF_KCHASE) { chase_xref(article_num(ap),TRUE); ap->flags &= ~AF_KCHASE; if (!--chase_count) return 1; } #ifdef MCHASE if (ap->flags & AF_MCHASE) { chase_xref(article_num(ap),TRUE); ap->flags &= ~AF_MCHASE; if (!--chase_count) return 1; } #endif if (until_key && input_pending()) return 1; return 0; } /* run down xref list and mark as read or unread */ #ifndef DBM_XREFS /*=-=-=-=*/ static int chase_xref(artnum,markread) /* The Xref-line-using version */ ART_NUM artnum; int markread; { register char* xartnum; register ART_NUM x; char* xref_buf, *curxref; char tmpbuf[128]; if (datasrc->flags & DF_NOXREFS) return 0; if (inbackground()) spin(10); else { if (output_chase_phrase) { # ifdef VERBOSE IF(verbose) fputs("\nChasing xrefs", stdout); ELSE # endif # ifdef TERSE fputs("\nXrefs", stdout); # endif termdown(1); output_chase_phrase = 0; } putchar('.'); fflush(stdout); } xref_buf = fetchcache(artnum, XREF_LINE, FILL_CACHE); if (!xref_buf || !*xref_buf) return 0; xref_buf = savestr(xref_buf); # ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("Xref: %s\n",xref_buf) FLUSH; termdown(1); } # endif curxref = cpytill(tmpbuf,xref_buf,' ') + 1; # ifdef VALIDATE_XREF_SITE if (valid_xref_site(artnum,tmpbuf)) # endif { while (*curxref) { /* for each newsgroup */ curxref = cpytill(tmpbuf,curxref,' '); xartnum = index(tmpbuf,':'); if (!xartnum) break; *xartnum++ = '\0'; if (!(x = atol(xartnum))) continue; if (strEQ(tmpbuf,ngname)) {/* is this the current newsgroup? */ if (x < absfirst || x > lastart) continue; if (markread) oneless(article_ptr(x)); /* take care of old C newses */ # ifdef MCHASE else onemore(article_ptr(x)); # endif } else { if (markread) { if (addartnum(datasrc,x,tmpbuf)) break; } # ifdef MCHASE else subartnum(datasrc,x,tmpbuf); # endif } while (*curxref && isspace(*curxref)) curxref++; } } free(xref_buf); return 0; } /* Make sure the site name on Xref matches what inews thinks the site * is. Check first against last inews_site. If it matches, fine. * If not, fetch inews_site from current Path or Relay-Version line and * check again. This is so that if the new administrator decides * to change the system name as known to inews, rn will still do * Xrefs correctly--each article need only match itself to be valid. */ # ifdef VALIDATE_XREF_SITE static bool valid_xref_site(artnum, site) ART_NUM artnum; char* site; { static char* inews_site = NULL; char* sitebuf; char* s; if (inews_site && strEQ(site,inews_site)) return TRUE; if (inews_site) free(inews_site); #ifndef ANCIENT_NEWS /* Grab the site from the first component of the Path line */ sitebuf = fetchlines(artnum,PATH_LINE); if ((s = index(sitebuf, '!')) != NULL) { *s = '\0'; inews_site = savestr(sitebuf); } #else /* ANCIENT_NEWS */ /* Grab the site from the Posting-Version line */ sitebuf = fetchlines(artnum,RVER_LINE); if ((s = instr(sitebuf,"; site ",TRUE)) != NULL) { char* t = index(s+7, '.'); if (t) *t = '\0'; inews_site = savestr(s+7); } #endif /* ANCIENT_NEWS */ else inews_site = savestr(nullstr); free(sitebuf); if (strEQ(site,inews_site)) return TRUE; #ifdef DEBUG if (debug) { printf("Xref not from %s -- ignoring\n",inews_site) FLUSH; termdown(1); } #endif return FALSE; } # endif /* VALIDATE_XREF_SITE */ #else /* DBM_XREFS */ static int chase_xref(artnum,markread) /* The DBM version */ ART_NUM artnum; int markread; { datum lhs, rhs; datum fetch(); register char* idp; char* ident_buf; static FILE* hist_file = NULL; long pos; register char* xartnum; register ART_NUM x; char* xref_buf; char* curxref; char tmpbuf[128]; if (datasrc->flags & DF_NOXREFS) return; if (inbackground()) spin(10); else { if (output_chase_phrase) { # ifdef VERBOSE IF(verbose) fputs("\nChasing xrefs", stdout); ELSE # endif # ifdef TERSE fputs("\nXrefs", stdout); # endif termdown(1); output_chase_phrase = 0; } putchar('.'); fflush(stdout); } xref_buf = fetchcache(artnum, NGS_LINE, FILL_CACHE); if (!xref_buf || !*xref_buf) return 0; xref_buf = safemalloc((MEM_SIZE)BUFSIZ); if (hist_file == NULL) { /* Init. file accesses */ # ifdef DEBUG if (debug) { printf("chase_xref: opening files\n"); termdown(1); } # endif dbminit(filexp(ARTFILE)); if ((hist_file = fopen(filexp(ARTFILE), "r")) == NULL) return 0; } ident_buf = fetchlines(artnum,MSGID_LINE); /* get Message-ID */ # ifdef DEBUG if (debug) { printf ("chase_xref: Message-ID: %s\n", ident_buf); termdown(1); } # endif if ((idp = index(ident_buf, '@')) != NULL) { while (*++idp) /* make message-id case insensitive */ if (isupper(*idp)) *idp = tolower(*idp); } lhs.dptr = ident_buf; /* look up article by id */ lhs.dsize = strlen(lhs.dptr) + 1; rhs = fetch(lhs); /* fetch the record */ if (rhs.dptr == NULL) /* if NULL, nothing there */ goto wild_goose; bcopy(rhs.dptr,(char*)&pos, 4); fseek(hist_file, pos, 0); /* datum returned is position in hist file */ fgets(xref_buf, BUFSIZ, hist_file); # ifdef DEBUG if (debug) { printf ("Xref from history: %s\n", xref_buf); termdown(1); } # endif curxref = cpytill(tmpbuf, xref_buf, '\t') + 1; curxref = cpytill(tmpbuf, curxref, '\t') + 1; # ifdef DEBUG if (debug) { printf ("chase_xref: curxref: %s\n", curxref); termdown(1); } # endif while (*curxref) { /* for each newsgroup */ curxref = cpytill(tmpbuf,curxref,' '); xartnum = index(tmpbuf,'/'); if (!xartnum) /* probably an old-style Xref */ break; *xartnum++ = '\0'; if (!(x = atol(xartnum))) continue; if (strNE(tmpbuf,ngname)) { /* not the current newsgroup? */ if (markread) { if (addartnum(datasrc,x,tmpbuf)) goto wild_goose; } # ifdef MCHASE else subartnum(datasrc,x,tmpbuf); # endif } while (*curxref && isspace(*curxref)) curxref++; } wild_goose: free(xref_buf); free(ident_buf); return 0; } #endif /* DBM_XREFS */ trn-4.0-test77/bits.h0000644000000000000000000000127307113133015013120 0ustar rootroot/* bits.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT int dmcount INIT(0); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void bits_init _((void)); void rc_to_bits _((void)); bool set_firstart _((char*)); void bits_to_rc _((void)); void find_existing_articles _((void)); void onemore _((ARTICLE*)); void oneless _((ARTICLE*)); void oneless_artnum _((ART_NUM)); void onemissing _((ARTICLE*)); void unmark_as_read _((ARTICLE*)); void set_read _((ARTICLE*)); void delay_unmark _((ARTICLE*)); void mark_as_read _((ARTICLE*)); void mark_missing_articles _((void)); void check_first _((ART_NUM)); void yankback _((void)); int chase_xrefs _((bool_int)); trn-4.0-test77/bits.ih0000644000000000000000000000053607113133015013272 0ustar rootroot/* bits.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static bool yank_article _((char*,int)); static bool check_chase _((char*,int)); static int chase_xref _((ART_NUM,int)); #ifdef VALIDATE_XREF_SITE static bool valid_xref_site _((ART_NUM,char*)); #endif trn-4.0-test77/cache.c0000644000000000000000000005275007242615166013242 0ustar rootroot/* cache.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "intrp.h" #include "search.h" #include "ng.h" #include "trn.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "term.h" #include "final.h" #include "artsrch.h" #include "head.h" #include "mime.h" #include "bits.h" #include "kfile.h" #include "rcstuff.h" #include "rthread.h" #include "rt-ov.h" #include "rt-page.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #ifdef SCORE #include "score.h" #endif #include "env.h" #include "util.h" #include "util2.h" #include "INTERN.h" #include "cache.h" #include "cache.ih" #ifdef PENDING # ifdef ARTSEARCH COMPEX srchcompex; /* compiled regex for searchahead */ # endif #endif HASHTABLE* subj_hash = 0; HASHTABLE* shortsubj_hash = 0; void cache_init() { #ifdef PENDING # ifdef ARTSEARCH init_compex(&srchcompex); # endif #endif } static NGDATA* cached_ng = NULL; static time_t cached_time = 0; void build_cache() { if (cached_ng == ngptr && time((time_t*)NULL) < cached_time + 6*60*60L) { ART_NUM an; cached_time = time((time_t*)NULL); if (sel_mode == SM_ARTICLE) set_selector(sel_mode, sel_artsort); else set_selector(sel_threadmode, sel_threadsort); for (an = last_cached+1; an <= lastart; an++) article_ptr(an)->flags |= AF_EXISTS; rc_to_bits(); article_list->high = lastart; thread_grow(); return; } close_cache(); cached_ng = ngptr; cached_time = time((time_t*)NULL); article_list = new_list(absfirst, lastart, sizeof (ARTICLE), 371, LF_SPARSE, init_artnode); subj_hash = hashcreate(991, subject_cmp); /*TODO: pick a better size */ set_firstart(ngptr->rcline + ngptr->numoffset); first_cached = thread_always? absfirst : firstart; last_cached = first_cached-1; cached_all_in_range = FALSE; #ifdef PENDING subj_to_get = xref_to_get = firstart; #endif /* Cache as much data in advance as possible, possibly threading ** articles as we go. */ thread_open(); } void close_cache() { SUBJECT* sp; SUBJECT* next; #ifdef SUPPORT_NNTP nntp_artname(0, FALSE); /* clear the tmpfile cache */ #endif if (subj_hash) { hashdestroy(subj_hash); subj_hash = 0; } if (shortsubj_hash) { hashdestroy(shortsubj_hash); shortsubj_hash = 0; } /* Free all the subjects. */ for (sp = first_subject; sp; sp = next) { next = sp->next; free(sp->str); free((char*)sp); } first_subject = last_subject = NULL; subject_count = 0; /* just to be sure */ parsed_art = 0; if (artptr_list) { free((char*)artptr_list); artptr_list = NULL; } artptr = NULL; thread_close(); if (article_list) { walk_list(article_list, clear_artitem, 0); delete_list(article_list); article_list = NULL; } cached_ng = NULL; } /* Initialize the memory for an entire node's worth of article's */ static void init_artnode(list, node) LIST* list; LISTNODE* node; { register ART_NUM i; register ARTICLE* ap; bzero(node->data, list->items_per_node * list->item_size); for (i = node->low, ap = (ARTICLE*)node->data; i <= node->high; i++, ap++) ap->num = i; } static bool clear_artitem(cp, arg) char* cp; int arg; { clear_article((ARTICLE*)cp); return 0; } /* The article has all it's data in place, so add it to the list of articles ** with the same subject. */ void cache_article(ap) register ARTICLE* ap; { register ARTICLE* next; register ARTICLE* ap2; if (!(next = ap->subj->articles) || ap->date < next->date) ap->subj->articles = ap; else { while ((next = (ap2 = next)->subj_next) && next->date <= ap->date) ; ap2->subj_next = ap; } ap->subj_next = next; ap->flags |= AF_CACHED; if (!!(ap->flags & AF_UNREAD) ^ sel_rereading) { if (ap->subj->flags & sel_mask) select_article(ap, 0); else { if (ap->subj->flags & SF_WASSELECTED) { #if 0 if (selected_only) ap->flags |= sel_mask; else #endif select_article(ap, 0); } ap->subj->flags |= SF_VISIT; } } if (join_subject_len != 0) check_for_near_subj(ap); } void check_for_near_subj(ap) ARTICLE* ap; { register SUBJECT* sp; if (!shortsubj_hash) { shortsubj_hash = hashcreate(401, subject_cmp); /*TODO: pick a better size */ sp = first_subject; } else { sp = ap->subj; if (sp->next) sp = 0; } while (sp) { if ((int)strlen(sp->str+4) >= join_subject_len && sp->thread) { SUBJECT* sp2; HASHDATUM data; data = hashfetch(shortsubj_hash, sp->str+4, join_subject_len); if (!(sp2 = (SUBJECT*)data.dat_ptr)) { data.dat_ptr = (char*)sp; hashstorelast(data); } else if (sp->thread != sp2->thread) { merge_threads(sp2, sp); } } sp = sp->next; } } void change_join_subject_len(len) int len; { if (join_subject_len != len) { if (shortsubj_hash) { hashdestroy(shortsubj_hash); shortsubj_hash = 0; } join_subject_len = len; if (len && first_subject && first_subject->articles) check_for_near_subj(first_subject->articles); } } void check_poster(ap) register ARTICLE* ap; { if (auto_select_postings && (ap->flags & AF_EXISTS) && ap->from) { if (ap->flags & AF_FROMTRUNCED) { strcpy(cmd_buf,realName); if (strEQ(ap->from,compress_name(cmd_buf,16))) { untrim_cache = TRUE; fetchfrom(article_num(ap),FALSE); untrim_cache = FALSE; } } if (!(ap->flags & AF_FROMTRUNCED)) { char* s = cmd_buf; char* u; char* h; strcpy(s,ap->from); if ((h=index(s,'<')) != NULL) { /* grab the good part */ s = h+1; if ((h=index(s,'>')) != NULL) *h = '\0'; } else if ((h=index(s,'(')) != NULL) { while (h-- != s && *h == ' ') ; h[1] = '\0'; /* or strip the comment */ } if ((h = index(s,'%')) != NULL || (h = index(s,'@')) != NULL) { *h++ = '\0'; u = s; } else if ((u = rindex(s,'!')) != NULL) { *u++ = '\0'; h = s; } else h = u = s; if (strEQ(u,loginName)) { if (instr(h,hostname,FALSE)) { switch (auto_select_postings) { case '.': select_subthread(ap,AUTO_SEL_FOL); break; case '+': select_arts_thread(ap,AUTO_SEL_THD); break; case 'p': if (ap->parent) select_subthread(ap->parent,AUTO_SEL_FOL); else select_subthread(ap,AUTO_SEL_FOL); break; } } else { #ifdef REPLYTO_POSTER_CHECKING char* reply_buf = fetchlines(article_num(ap),REPLY_LINE); if (instr(reply_buf,loginName,TRUE)) select_subthread(ap,AUTO_SEL_FOL); free(reply_buf); #endif } } } } } /* The article turned out to be a duplicate, so remove it from the cached ** list and possibly destroy the subject (should only happen if the data ** was corrupt and the duplicate id got a different subject). */ void uncache_article(ap, remove_empties) register ARTICLE* ap; bool_int remove_empties; { register ARTICLE* next; if (ap->subj) { if (ALLBITS(ap->flags, AF_CACHED | AF_EXISTS)) { if ((next = ap->subj->articles) == ap) ap->subj->articles = ap->subj_next; else { register ARTICLE* ap2; while (next) { if ((ap2 = next->subj_next) == ap) { next->subj_next = ap->subj_next; break; } next = ap2; } } } if (remove_empties && !ap->subj->articles) { register SUBJECT* sp = ap->subj; if (sp == first_subject) first_subject = sp->next; else sp->prev->next = sp->next; if (sp == last_subject) last_subject = sp->prev; else sp->next->prev = sp->prev; hashdelete(subj_hash, sp->str+4, strlen(sp->str+4)); free((char*)sp); ap->subj = NULL; subject_count--; } } ap->flags2 |= AF2_BOGUS; onemissing(ap); } /* get the header line from an article's cache or parse the article trying */ char* fetchcache(artnum,which_line,fill_cache) ART_NUM artnum; int which_line; bool_int fill_cache; { register char* s; register ARTICLE* ap; register bool cached = (htype[which_line].flags & HT_CACHED); /* article_find() returns a NULL if the artnum value is invalid */ if (!(ap = article_find(artnum)) || !(ap->flags & AF_EXISTS)) return nullstr; if (cached && (s=get_cached_line(ap,which_line,untrim_cache)) != NULL) return s; if (!fill_cache) return NULL; if (!parseheader(artnum)) return nullstr; if (cached) return get_cached_line(ap,which_line,untrim_cache); return NULL; } /* Return a pointer to a cached header line for the indicated article. ** Truncated headers (e.g. from a .thread file) are optionally ignored. */ char* get_cached_line(ap, which_line, no_truncs) register ARTICLE* ap; int which_line; bool_int no_truncs; { register char* s; switch (which_line) { case SUBJ_LINE: if (!ap->subj || (no_truncs && (ap->subj->flags & SF_SUBJTRUNCED))) s = NULL; else s = ap->subj->str + ((ap->flags & AF_HAS_RE) ? 0 : 4); break; case FROM_LINE: if (no_truncs && (ap->flags & AF_FROMTRUNCED)) s = NULL; else s = ap->from; break; #ifdef DBM_XREFS case NGS_LINE: #else case XREF_LINE: #endif s = ap->xrefs; break; case MSGID_LINE: s = ap->msgid; break; #ifdef USE_FILTER case REFS_LINE: s = ap->refs; break; #endif case LINES_LINE: { static char linesbuf[32]; sprintf(linesbuf, "%ld", ap->lines); s = linesbuf; break; } case BYTES_LINE: { static char bytesbuf[32]; sprintf(bytesbuf, "%ld", ap->bytes); s = bytesbuf; break; } default: s = NULL; break; } return s; } void set_subj_line(ap, subj, size) ARTICLE* ap; char* subj; /* not yet allocated, so we can tweak it first */ int size; { HASHDATUM data; SUBJECT* sp; char* newsubj; char* subj_start; short def_flags = 0; if (subject_has_Re(subj, &subj_start)) ap->flags |= AF_HAS_RE; if ((size -= subj_start - subj) < 0) size = 0; newsubj = safemalloc(size + 4 + 1); strcpy(newsubj, "Re: "); size = decode_header(newsubj + 4, subj_start, size); /* Do the Re:-stripping over again, just in case it was encoded. */ if (subject_has_Re(newsubj + 4, &subj_start)) ap->flags |= AF_HAS_RE; if (subj_start != newsubj + 4) { safecpy(newsubj + 4, subj_start, size); if ((size -= subj_start - newsubj - 4) < 0) size = 0; } if (ap->subj && strnEQ(ap->subj->str+4, newsubj+4, size)) { free(newsubj); return; } if (ap->subj) { /* This only happens when we freshen truncated subjects */ hashdelete(subj_hash, ap->subj->str+4, strlen(ap->subj->str+4)); free(ap->subj->str); ap->subj->str = newsubj; ap->subj->flags |= def_flags; data.dat_ptr = (char*)ap->subj; hashstore(subj_hash, newsubj + 4, size, data); } else { data = hashfetch(subj_hash, newsubj + 4, size); if (!(sp = (SUBJECT*)data.dat_ptr)) { sp = (SUBJECT*)safemalloc(sizeof (SUBJECT)); bzero((char*)sp, sizeof (SUBJECT)); subject_count++; if ((sp->prev = last_subject) != NULL) sp->prev->next = sp; else first_subject = sp; last_subject = sp; sp->str = newsubj; sp->thread_link = sp; sp->flags = def_flags; data.dat_ptr = (char*)sp; hashstorelast(data); } else free(newsubj); ap->subj = sp; } } int decode_header(t, f, size) char* t; char* f; int size; { int i; for (i = size; i--; ) { if (AT_GREY_SPACE(f)) { while (i && *++f && AT_GREY_SPACE(f)) i--, size--; *t++ = ' '; } else if (*f == '=' && f[1] == '?') { char* q = index(f+2,'?'); char ch = (q && q[2] == '?')? q[1] : 0; char* e; if (ch == 'q' || ch == 'Q' || ch == 'b' || ch == 'B') { e = q+2; do { e = index(e+1, '?'); } while (e && e[1] != '='); if (e) { int len = e - f + 2; i -= len-1; size -= len; q += 3; f = e+2; *e = '\0'; if (ch == 'q' || ch == 'Q') len = qp_decodestring(t, q, 1); else len = b64_decodestring(t, q); *e = '?'; size += len; for ( ; len--; t++) { if (AT_GREY_SPACE(t)) *t = ' '; } } else *t++ = *f++; } else *t++ = *f++; } else if (*f != '\n') *t++ = *f++; else f++, size--; } while (size > 1 && t[-1] == ' ') t--, size--; *t = '\0'; return size; } void dectrl(str) char* str; { for ( ; *str; str++) { if (AT_GREY_SPACE(str)) *str = ' '; } } void set_cached_line(ap, which_line, s) register ARTICLE* ap; register int which_line; register char* s; /* already allocated, ready to save */ { char* cp; /* SUBJ_LINE is handled specially above */ switch (which_line) { case FROM_LINE: ap->flags &= ~AF_FROMTRUNCED; if (ap->from) free(ap->from); decode_header(s, s, strlen(s)); ap->from = s; break; #ifdef DBM_XREFS case NGS_LINE: if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); if (!index(s, ',')) { /* if no comma, no Xref! */ free(s); s = nullstr; } ap->xrefs = s; break; #else case XREF_LINE: if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); /* Exclude an xref for just this group or "(none)". */ cp = index(s, ':'); if (!cp || !index(cp+1, ':')) { free(s); s = nullstr; } ap->xrefs = s; break; #endif case MSGID_LINE: if (ap->msgid) free(ap->msgid); ap->msgid = s; break; #ifdef USE_FILTER case REFS_LINE: if (ap->refs && ap->refs != nullstr) free(ap->refs); ap->refs = s; break; #endif case LINES_LINE: ap->lines = atol(s); break; case BYTES_LINE: ap->bytes = atol(s); break; } } int subject_cmp(key, keylen, data) char* key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, ((SUBJECT*)data.dat_ptr)->str+4, keylen); } /* see what we can do while they are reading */ #ifdef PENDING void look_ahead() { #ifdef ARTSEARCH register char* h; register char* s; #ifdef DEBUG if (debug && srchahead) { printf("(%ld)",(long)srchahead); fflush(stdout); } #endif #endif if (ThreadedGroup) { artp = curr_artp; inc_art(selected_only,FALSE); if (artp) parseheader(art); } else #ifdef ARTSEARCH if (srchahead && srchahead < art) { /* in ^N mode? */ char* pattern; pattern = buf+1; strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,(sizeof buf) - (h-buf),"%\\s"); { /* compensate for notesfiles */ register int i; for (i = 24; *h && i--; h++) if (*h == '\\') h++; *h = '\0'; } #ifdef DEBUG if (debug & DEB_SEARCH_AHEAD) { fputs("(hit CR)",stdout); fflush(stdout); fgets(buf+128, sizeof buf-128, stdin); printf("\npattern = %s\n",pattern); termdown(2); } #endif if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != NULL) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; termdown(2); srchahead = 0; } if (srchahead) { srchahead = art; for (;;) { srchahead++; /* go forward one article */ if (srchahead > lastart) { /* out of articles? */ #ifdef DEBUG if (debug) fputs("(not found)",stdout); #endif break; } if (!was_read(srchahead) && wanted(&srchcompex,srchahead,0)) { /* does the shoe fit? */ #ifdef DEBUG if (debug) printf("(%ld)",(long)srchahead); #endif parseheader(srchahead); break; } if (input_pending()) break; } fflush(stdout); } } else #endif /* ARTSEARCH */ { if (article_next(art) <= lastart) /* how about a pre-fetch? */ parseheader(article_next(art)); /* look for the next article */ } } #endif /* PENDING */ /* see what else we can do while they are reading */ void cache_until_key() { if (!in_ng) return; #ifdef PENDING if (input_pending()) return; # ifdef SUPPORT_NNTP if ((datasrc->flags & DF_REMOTE) && nntp_finishbody(FB_BACKGROUND)) return; # endif # ifdef NICEBG if (wait_key_pause(10)) return; # endif untrim_cache = TRUE; sentinel_artp = curr_artp; /* Prioritize our caching based on what mode we're in */ if (gmode == 's') { if (cache_subjects()) { if (cache_xrefs()) { if (chase_xrefs(TRUE)) { if (ThreadedGroup) cache_all_arts(); else cache_unread_arts(); } } } } else { if (!ThreadedGroup || cache_all_arts()) { if (cache_subjects()) { if (cache_unread_arts()) { if (cache_xrefs()) chase_xrefs(TRUE); } } } } # ifdef SCORE if (!input_pending() && sc_initialized) sc_lookahead(TRUE,TRUE); # endif setspin(SPIN_OFF); untrim_cache = FALSE; #endif #ifdef SUPPORT_NNTP check_datasrcs(); #endif } #ifdef PENDING bool cache_subjects() { register ART_NUM an; if (subj_to_get > lastart) return TRUE; setspin(SPIN_BACKGROUND); for (an=article_first(subj_to_get); an <= lastart; an=article_next(an)) { if (input_pending()) break; if (article_unread(an)) fetchsubj(an,FALSE); } subj_to_get = an; return subj_to_get > lastart; } bool cache_xrefs() { register ART_NUM an; if (olden_days || (datasrc->flags & DF_NOXREFS) || xref_to_get > lastart) return TRUE; setspin(SPIN_BACKGROUND); for (an=article_first(xref_to_get); an <= lastart; an=article_next(an)) { if (input_pending()) break; if (article_unread(an)) fetchxref(an,FALSE); } xref_to_get = an; return xref_to_get > lastart; } bool cache_all_arts() { int old_last_cached = last_cached; if (!cached_all_in_range) last_cached = first_cached-1; if (last_cached >= lastart && first_cached <= absfirst) return TRUE; /* turn it on as late as possible to avoid fseek()ing openart */ setspin(SPIN_BACKGROUND); if (last_cached < lastart) { if (datasrc->ov_opened) ov_data(last_cached+1, lastart, TRUE); if (!art_data(last_cached+1, lastart, TRUE, TRUE)) { last_cached = old_last_cached; return FALSE; } cached_all_in_range = TRUE; } if (first_cached > absfirst) { if (datasrc->ov_opened) ov_data(absfirst, first_cached-1, TRUE); else art_data(absfirst, first_cached-1, TRUE, TRUE); /* If we got interrupted, make a quick exit */ if (first_cached > absfirst) { last_cached = old_last_cached; return FALSE; } } /* We're all done threading the group, so if the current article is ** still in doubt, tell them it's missing. */ if (curr_artp && !(curr_artp->flags & AF_CACHED) && !input_pending()) pushchar('\f' | 0200); /* A completely empty group needs a count & a sort */ if (gmode != 's' && !obj_count && !selected_only) thread_grow(); return TRUE; } bool cache_unread_arts() { if (last_cached >= lastart) return TRUE; setspin(SPIN_BACKGROUND); return art_data(last_cached+1, lastart, TRUE, FALSE); } #endif bool art_data(first, last, cheating, all_articles) ART_NUM first, last; bool_int cheating; bool_int all_articles; { register ART_NUM i; ART_NUM expected_i = first; int cachemask = (ThreadedGroup ? AF_THREADED : AF_CACHED) + (all_articles? 0 : AF_UNREAD); int cachemask2 = (all_articles? 0 : AF_UNREAD); if (cheating) setspin(SPIN_BACKGROUND); else { #ifdef SUPPORT_NNTP int lots2do = ((datasrc->flags & DF_REMOTE)? netspeed : 20) * 25; #else int lots2do = 20 * 25; #endif setspin(spin_estimate > lots2do? SPIN_BARGRAPH : SPIN_FOREGROUND); } /*assert(first >= absfirst && last <= lastart);*/ for (i = article_first(first); i <= last; i = article_next(i)) { if ((article_ptr(i)->flags & cachemask) ^ cachemask2) continue; spin_todo -= i - expected_i; expected_i = i + 1; /* This parses the header which will cache/thread the article */ (void) parseheader(i); if (int_count) { int_count = 0; break; } if (cheating) { if (input_pending()) break; /* If the current article is no longer a '?', let them know. */ if (curr_artp != sentinel_artp) { pushchar('\f' | 0200); break; } } } setspin(SPIN_POP); if (i > last) i = last; if (i > last_cached) last_cached = i; if (i == last) { if (first < first_cached) first_cached = first; return TRUE; } return FALSE; } bool cache_range(first,last) ART_NUM first; ART_NUM last; { bool success = TRUE; bool all_arts = (sel_rereading || thread_always); ART_NUM count = 0; if (sel_rereading && !cached_all_in_range) { first_cached = first; last_cached = first-1; } if (first < first_cached) count = first_cached-first; if (last > last_cached) count += last-last_cached; if (!count) return TRUE; spin_todo = count; if (first_cached > last_cached) { if (sel_rereading) { if (first_subject) count -= ngptr->toread; } else if (first == firstart && last == lastart && !all_arts) count = ngptr->toread; } spin_estimate = count; printf("\n%sing %ld article%s.", ThreadedGroup? "Thread" : "Cach", (long)count, PLURAL(count)); termdown(1); setspin(SPIN_FOREGROUND); if (first < first_cached) { if (datasrc->ov_opened) { ov_data(absfirst,first_cached-1,FALSE); success = (first_cached == absfirst); } else { success = art_data(first, first_cached-1, FALSE, all_arts); cached_all_in_range = (all_arts && success); } } if (success && last_cached < last) { if (datasrc->ov_opened) ov_data(last_cached+1, last, FALSE); success = art_data(last_cached+1, last, FALSE, all_arts); cached_all_in_range = (all_arts && success); } setspin(SPIN_POP); return success; } void clear_article(ap) register ARTICLE* ap; { if (ap->from) free(ap->from); if (ap->msgid) free(ap->msgid); if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); #ifdef USE_FILTER if (ap->refs && ap->refs != nullstr) free(ap->refs); #endif } trn-4.0-test77/cache.h0000644000000000000000000001107607113133015013224 0ustar rootroot/* cache.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* Subjects get their own structure */ struct subject { SUBJECT* next; SUBJECT* prev; ARTICLE* articles; ARTICLE* thread; SUBJECT* thread_link; char* str; time_t date; short flags; short misc; /* used for temporary totals and subject numbers */ }; /* subject flags */ #define SF_SEL 0x0001 #define SF_DEL 0x0002 #define SF_DELSEL 0x0004 #define SF_OLDVISIT 0x0008 #define SF_INCLUDED 0x0010 #define SF_VISIT 0x0200 #define SF_WASSELECTED 0x0400 #define SF_SUBJTRUNCED 0x1000 #define SF_ISOSUBJ 0x2000 /* This is our article-caching structure */ struct article { ART_NUM num; time_t date; SUBJECT* subj; char* from; char* msgid; char* xrefs; #ifdef USE_FILTER char* refs; #endif ARTICLE* parent; /* parent article */ ARTICLE* child1; /* first child of a chain */ ARTICLE* sibling; /* our next sibling */ ARTICLE* subj_next; /* next article in subject order */ long bytes; long lines; #ifdef SCORE int score; unsigned short scoreflags; #endif unsigned short flags; /* article state flags */ unsigned short flags2; /* more state flags */ unsigned short autofl; /* auto-processing flags */ }; /* article flags */ #define AF_SEL 0x0001 #define AF_DEL 0x0002 #define AF_DELSEL 0x0004 #define AF_OLDSEL 0x0008 #define AF_INCLUDED 0x0010 #define AF_UNREAD 0x0020 #define AF_CACHED 0x0040 #define AF_THREADED 0x0080 #define AF_EXISTS 0x0100 #define AF_HAS_RE 0x0200 #define AF_KCHASE 0x0400 #define AF_MCHASE 0x0800 #define AF_YANKBACK 0x1000 #define AF_FROMTRUNCED 0x2000 #define AF_TMPMEM 0x4000 #define AF_FAKE 0x8000 #define AF2_WASUNREAD 0x0001 #define AF2_NODEDRAWN 0x0002 #define AF2_CHANGED 0x0004 #define AF2_BOGUS 0x0008 /* See kfile.h for the AUTO_* flags */ #define article_ptr(an) ((ARTICLE*)listnum2listitem(article_list,(long)(an))) #define article_num(ap) ((ap)->num) #define article_find(an) ((an) <= lastart && article_hasdata(an)? \ article_ptr(an) : NULL) #define article_walk(cb,ag) walk_list(article_list,cb,ag) #define article_hasdata(an) existing_listnum(article_list,(long)(an),0) #define article_first(an) existing_listnum(article_list,(long)(an),1) #define article_next(an) existing_listnum(article_list,(long)(an)+1,1) #define article_last(an) existing_listnum(article_list,(long)(an),-1) #define article_prev(an) existing_listnum(article_list,(long)(an)-1,-1) #define article_nextp(ap) ((ARTICLE*)next_listitem(article_list,(char*)(ap))) #define article_exists(an) (article_ptr(an)->flags & AF_EXISTS) #define article_unread(an) (article_ptr(an)->flags & AF_UNREAD) #define was_read(an) (!article_hasdata(an) || !article_unread(an)) #define is_available(an) ((an) <= lastart && article_hasdata(an) \ && article_exists(an)) #define is_unavailable(an) (!is_available(an)) EXT LIST* article_list INIT(0); /* a list of ARTICLEs */ EXT ARTICLE** artptr_list INIT(0); /* the article-selector creates this */ EXT ARTICLE** artptr; /* ditto -- used for article order */ EXT ART_NUM artptr_list_size INIT(0); #ifdef ARTSEARCH EXT ART_NUM srchahead INIT(0); /* are we in subject scan mode? */ /* (if so, contains art # found or -1) */ #endif EXT ART_NUM first_cached; EXT ART_NUM last_cached; EXT bool cached_all_in_range; EXT ARTICLE* sentinel_artp; #define DONT_FILL_CACHE 0 #define FILL_CACHE 1 EXT SUBJECT* first_subject INIT(0); EXT SUBJECT* last_subject INIT(0); EXT bool untrim_cache INIT(FALSE); #ifdef PENDING EXT ART_NUM subj_to_get; EXT ART_NUM xref_to_get; #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void cache_init _((void)); void build_cache _((void)); void close_cache _((void)); void cache_article _((ARTICLE*)); void check_for_near_subj _((ARTICLE*)); void change_join_subject_len _((int)); void check_poster _((ARTICLE*)); void uncache_article _((ARTICLE*,bool_int)); char* fetchcache _((ART_NUM,int,bool_int)); char* get_cached_line _((ARTICLE*,int,bool_int)); void set_subj_line _((ARTICLE*,char*,int)); int decode_header _((char*,char*,int)); void dectrl _((char*)); void set_cached_line _((ARTICLE*,int,char*)); int subject_cmp _((char*,int,HASHDATUM)); #ifdef PENDING void look_ahead _((void)); #endif void cache_until_key _((void)); #ifdef PENDING bool cache_subjects _((void)); #endif bool cache_xrefs _((void)); bool cache_all_arts _((void)); bool cache_unread_arts _((void)); bool art_data _((ART_NUM,ART_NUM,bool_int,bool_int)); bool cache_range _((ART_NUM,ART_NUM)); void clear_article _((ARTICLE*)); trn-4.0-test77/cache.ih0000644000000000000000000000035707113133015013375 0ustar rootroot/* chache.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void init_artnode _((LIST*,LISTNODE*)); static bool clear_artitem _((char*,int)); trn-4.0-test77/charsubst.c0000644000000000000000000001376507113133015014161 0ustar rootroot/* charsubst.c */ /* * 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 authors 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 "search.h" #include "artstate.h" #include "util2.h" #include "INTERN.h" #include "charsubst.h" #include "charsubst.ih" #ifdef CHARSUBST /* TeX encoding table - gives ISO char for "x (x=32..127) */ static Uchar textbl[96] = { 0, 0,'"', 0, 0, 0, 0,'"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,214, 0, 0, 0, 0, 0,220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '"',228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,246, 0, 0, 0,223, 0,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static char texchar = '\0'; int putsubstchar(c, limit, outputok) int c; int limit; bool_int outputok; { Uchar d, oc[2], nc[5]; int t, i = 0; switch (*charsubst) { case 'm': t = 1; goto doconvert; case 'a': t = 2; /* FALL THROUGH */ doconvert: oc[0] = (Uchar)c; oc[1] = '\0'; if ((i = Latin1toASCII(nc, oc, sizeof nc, t)) <= limit) { if (outputok) { for (t = 0; t < i; t++) putchar((char)nc[t]); } } else i = -1; break; case 't': if (c == '\\' || c == '"') { if (texchar && (c == '\\' || texchar != '\\')) { if (outputok) putchar(texchar); i++; } texchar = (char)c; break; } else if (texchar == '\\') { if (outputok) putchar('\\'); if (limit == 1) { i = -2; break; } i++; } else if (texchar == '"') { if (c < 32 || c > 128) d = '\0'; else d = textbl[c-32]; texchar = '\0'; if (d) c = d; else { if (outputok) putchar('"'); if (limit == 1) { i = -2; break; } i++; } } /* FALL THROUGH */ default: if (outputok) putchar(c); i++; break; } return i; } char* current_charsubst() { static char* show; switch (*charsubst) { case 'm': #ifdef VERBOSE IF(verbose) show = "[ISO->USmono] "; ELSE #endif #ifdef TERSE show = "[M] "; #endif break; case 'a': #ifdef VERBOSE IF(verbose) show = "[ISO->US] "; ELSE #endif #ifdef TERSE show = "[U] "; #endif break; case 't': #ifdef VERBOSE IF(verbose) show = "[TeX->ISO] "; ELSE #endif #ifdef TERSE show = "[T] "; #endif break; default: show = nullstr; break; } return show; } int strcharsubst(outb, inb, limit, subst) char* outb; char* inb; int limit; char_int subst; { char* s; int len; switch (subst) { case 'm': return Latin1toASCII((Uchar*)outb, (Uchar*)inb, limit, 1); case 'a': return Latin1toASCII((Uchar*)outb, (Uchar*)inb, limit, 2); default: if ((s = index(inb,'\n')) != NULL && s - inb + 1 < limit) { len = s - inb + 1; limit = len + 1; } else len = strlen(inb); safecpy(outb, inb, limit); return len; } } /* The following is an adapted version of iso2asc by Markus Kuhn, University of Erlangen, Germany */ #define ISO_TABLES 2 /* originally: 7 */ /* Conversion tables for displaying the G1 set (0xa0-0xff) of ISO Latin 1 (ISO 8859-1) with 7-bit ASCII characters. Version 1.2 -- error corrections are welcome Table Purpose 0 universal table for many languages 1 single-spacing universal table 2 table for Danish, Dutch, German, Norwegian and Swedish 3 table for Danish, Finnish, Norwegian and Swedish using the appropriate ISO 646 variant. 4 table with RFC 1345 codes in brackets 5 table for printers that allow overstriking with backspace Markus Kuhn */ /* In this version, I have taken out all tables except 1 and 2 -ot */ #define SUB NULL /* used if no reasonable ASCII string is possible */ static char* iso2asc[ISO_TABLES][96] = { { " ","!","c",SUB,SUB,"Y","|",SUB,"\"","c","a","<","-","-","R","-", " ",SUB,"2","3","'","u","P",".",",","1","o",">",SUB,SUB,SUB,"?", "A","A","A","A","A","A","A","C","E","E","E","E","I","I","I","I", "D","N","O","O","O","O","O","x","O","U","U","U","U","Y","T","s", "a","a","a","a","a","a","a","c","e","e","e","e","i","i","i","i", "d","n","o","o","o","o","o",":","o","u","u","u","u","y","t","y" },{ " ","!","c",SUB,SUB,"Y","|",SUB,"\"","(c)","a","<<","-","-","(R)","-", " ","+/-","2","3","'","u","P",".",",","1","o",">>"," 1/4"," 1/2"," 3/4","?", "A","A","A","A","Ae","Aa","AE","C","E","E","E","E","I","I","I","I", "D","N","O","O","O","O","Oe","x","Oe","U","U","U","Ue","Y","Th","ss", "a","a","a","a","ae","aa","ae","c","e","e","e","e","i","i","i","i", "d","n","o","o","o","o","oe",":","oe","u","u","u","ue","y","th","ij" } }; /* * Transform an 8-bit ISO Latin 1 string iso into a 7-bit ASCII string asc * readable on old terminals using conversion table t. * * worst case: strlen(iso) == 4*strlen(asc) */ static int Latin1toASCII(asc, iso, limit, t) Uchar* asc; Uchar* iso; int limit; int t; { Uchar* s = asc; char* p; char** tab; if (iso == NULL || asc == NULL || limit <= 0) return 0; if (limit == 1) goto done; t--; /* offset correction -ot */ tab = iso2asc[t] - 0xa0; while (*iso) { if (*iso > 0x9f) { p = tab[*iso++]; if (p) { while (*p) { *s++ = *p++; if (!--limit) goto done; } } } else { if (*iso < 0x80) *s++ = *iso++; else { *s++ = ' '; iso++; } if (!--limit) break; } } done: *s = '\0'; return s - asc; } #endif trn-4.0-test77/charsubst.h0000644000000000000000000000172607113133015014160 0ustar rootroot/* charsubst.h */ /* * 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 authors 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. */ #ifdef CHARSUBST /* Conversions are: plain, ISO->USascii, TeX->ISO, ISO->USascii monospaced */ EXT char* charsets INIT("patm"); EXT char* charsubst; #define HEADER_CONV() (*charsubst=='a' || *charsubst=='m'? *charsubst : '\0') #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int putsubstchar _((int,int,bool_int)); char* current_charsubst _((void)); int strcharsubst _((char*,char*,int,char_int)); trn-4.0-test77/charsubst.ih0000644000000000000000000000031607113133015014323 0ustar rootroot/* charsubst.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int Latin1toASCII _((Uchar*,Uchar*,int,int)); trn-4.0-test77/color.c0000644000000000000000000002066611437640112013304 0ustar rootroot/* color.c - Color handling for trn 4.0. */ /* This software is copyrighted as detailed in the LICENSE file, and * this file is also Copyright 1995 by Gran Larsson . */ /* ** The color handling is implemented as an attribute stack containing ** foreground color, background color, and video attribute for an object. ** Objects are screen features like "thread tree", "header lines", ** "subject line", and "command prompt". The intended use is something ** like this: ** ** color_object(COLOR_HEADER, 1); ** fputs(header_string, stdout); ** color_pop(); ** ** The color_pop function will take care of restoring all colors and ** attribute to the state before the color_object function was called. ** ** Colors and attributes are parsed from the [attribute] section ** in the trnrc file. Escape sequences for the colors are picked up ** from term.c by calling the function tc_color_capability. ** ** If colors were specified in the [attribute] section, then colors ** are used, otherwise only normal monochrome video attributes. */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "final.h" #include "INTERN.h" #include "color.h" #include "color.ih" /* ** Object properties. ** ** Give default attributes that are used if the trnrc file has no, ** or just a few, lines in the [attribute] section. */ static COLOR_OBJ objects[MAX_COLORS] = { { "default", nullstr, nullstr, NOMARKING }, { "ngname", nullstr, nullstr, STANDOUT }, { "plus", nullstr, nullstr, LASTMARKING }, { "minus", nullstr, nullstr, LASTMARKING }, { "star", nullstr, nullstr, LASTMARKING }, { "header", nullstr, nullstr, LASTMARKING }, { "subject", nullstr, nullstr, UNDERLINE }, { "tree", nullstr, nullstr, LASTMARKING }, { "tree marker", nullstr, nullstr, STANDOUT }, { "more", nullstr, nullstr, STANDOUT }, { "heading", nullstr, nullstr, STANDOUT }, { "command", nullstr, nullstr, STANDOUT }, { "mouse bar", nullstr, nullstr, STANDOUT }, { "notice", nullstr, nullstr, STANDOUT }, { "score", nullstr, nullstr, STANDOUT }, { "art heading", nullstr, nullstr, LASTMARKING }, { "mime separator", nullstr, nullstr, STANDOUT }, { "mime description",nullstr,nullstr, UNDERLINE }, { "cited text", nullstr, nullstr, LASTMARKING }, { "body text", nullstr, nullstr, NOMARKING }, }; /* The attribute stack. The 0th element is always the "normal" object. */ static struct { COLOR_OBJ object; } color_stack[STACK_SIZE]; static int stack_pointer = 0; /* Initialize color support after trnrc is read. */ void color_init() { if (use_colors) { char* fg; char* bg; int i; /* Get default capabilities. */ if ((fg = tc_color_capability("fg default")) == NULL) { fprintf(stderr,"trn: you need a 'fg default' definition in the [termcap] section.\n"); finalize(1); } if ((bg = tc_color_capability("bg default")) == NULL) { fprintf(stderr,"trn: you need a 'bg default' definition in the [termcap] section.\n"); finalize(1); } if (strEQ(fg, bg)) bg = nullstr; for (i = 0; i < MAX_COLORS; i++) { if (objects[i].fg == nullstr) objects[i].fg = fg; if (objects[i].bg == nullstr) objects[i].bg = bg; } } if (objects[COLOR_DEFAULT].attr == LASTMARKING) objects[COLOR_DEFAULT].attr = NOMARKING; /* Set color to default. */ color_default(); } /* Parse a line from the [attribute] section of trnrc. */ void color_rc_attribute(object, value) char* object; char* value; { char* s; char* t; char* n = NULL; int i; /* Find the specified object. */ for (i = 0; i < MAX_COLORS; i++) { if (strcaseEQ(object, objects[i].name)) break; } if (i >= MAX_COLORS) { fprintf(stderr,"trn: unknown object '%s' in [attribute] section.\n", object); finalize(1); } /* Parse the video attribute. */ if (*value == 's' || *value == 'S') objects[i].attr = STANDOUT; else if (*value == 'u' || *value == 'U') objects[i].attr = UNDERLINE; else if (*value == 'n' || *value == 'N') objects[i].attr = NOMARKING; else if (*value == '-') objects[i].attr = LASTMARKING; else { fprintf(stderr,"trn: bad attribute '%s' for %s in [attribute] section.\n", value, object); finalize(1); } /* See if they specified a color */ for (s = value; *s && !isspace(*s); s++) ; while (isspace(*s)) s++; if (!*s) { objects[i].fg = nullstr; objects[i].bg = nullstr; return; } for (t = s; *t && !isspace(*t); t++) ; if (*t) { *(n = t++) = '\0'; while (isspace(*t)) t++; } /* We have both colors and attributes, so turn colors on. */ use_colors = TRUE; /* Parse the foreground color. */ if (*s == '-') objects[i].fg = NULL; else { sprintf(buf, "fg %s", s); objects[i].fg = tc_color_capability(buf); if (objects[i].fg == NULL) { fprintf(stderr,"trn: no color '%s' for %s in [attribute] section.\n", buf, object); finalize(1); } } if (n) { *n = ' '; n = NULL; } /* Make sure we have one more parameter. */ for (s = t; *t && !isspace(*t); t++) ; if (*t) { *(n = t++) = '\0'; while (isspace(*t)) t++; } if (!*s || *t) { fprintf(stderr,"trn: wrong number of parameters for %s in [attribute] section.\n", object); finalize(1); } /* Parse the background color. */ if (*s == '-') objects[i].bg = NULL; else { sprintf(buf, "bg %s", s); objects[i].bg = tc_color_capability(buf); if (objects[i].bg == NULL) { fprintf(stderr,"trn: no color '%s' for %s in [attribute] section.\n", buf, object); finalize(1); } } if (n) *n = ' '; } /* Turn on color attribute for an object. */ void color_object(object, push) int object; bool_int push; { COLOR_OBJ merged; /* Merge in the colors/attributes that we are not setting * from the current object. */ merged = color_stack[stack_pointer].object; /* Merge in the new colors/attributes. */ if (objects[object].fg) merged.fg = objects[object].fg; if (objects[object].bg) merged.bg = objects[object].bg; if (objects[object].attr != LASTMARKING) merged.attr = objects[object].attr; /* Push onto stack. */ if (push && ++stack_pointer >= STACK_SIZE) { /* error reporting? $$ */ stack_pointer = 0; /* empty stack */ color_default(); /* and set normal colors */ return; } color_stack[stack_pointer].object = merged; /* Set colors/attributes. */ output_color(); } /* Pop the color/attribute stack. */ void color_pop() { /* Trying to pop an empty stack? */ if (--stack_pointer < 0) stack_pointer = 0; else output_color(); } /* Color a string with the given object's color/attribute. */ void color_string(object, str) int object; char* str; { int len = strlen(str); if (str[len-1] == '\n') { strcpy(msg, str); msg[len-1] = '\0'; str = msg; len = 0; } if (!use_colors && *tc_UC && objects[object].attr == UNDERLINE) underprint(str); /* hack for stupid terminals */ else { color_object(object, 1); fputs(str, stdout); color_pop(); } if (!len) putchar('\n'); } /* Turn off color attribute. */ void color_default() { color_stack[stack_pointer].object = objects[COLOR_DEFAULT]; output_color(); } /* Set colors/attribute for an object. */ static void output_color() { static COLOR_OBJ prior = { nullstr, NULL, NULL, NOMARKING }; COLOR_OBJ* op = &color_stack[stack_pointer].object; /* If no change, just return. */ if (op->attr == prior.attr && op->fg == prior.fg && op->bg == prior.bg) return; /* Start by turning off any existing colors and/or attributes. */ if (use_colors) { if (objects[COLOR_DEFAULT].fg != prior.fg || objects[COLOR_DEFAULT].bg != prior.bg) { fputs(prior.fg = objects[COLOR_DEFAULT].fg, stdout); fputs(prior.bg = objects[COLOR_DEFAULT].bg, stdout); } } switch (prior.attr) { case NOMARKING: break; case STANDOUT: un_standout(); break; case UNDERLINE: un_underline(); break; } /* For color terminals we set the foreground and background color. */ if (use_colors) { if (op->fg != prior.fg) fputs(prior.fg = op->fg, stdout); if (op->bg != prior.bg) fputs(prior.bg = op->bg, stdout); } /* For both monochrome and color terminals we set the video attribute. */ switch (prior.attr = op->attr) { case NOMARKING: break; case STANDOUT: #ifdef NOFIREWORKS no_sofire(); #endif standout(); break; case UNDERLINE: #ifdef NOFIREWORKS no_ulfire(); #endif underline(); break; } } trn-4.0-test77/color.h0000644000000000000000000000277007113133015013300 0ustar rootroot/* color.h - Color handling for trn 4.0. */ /* This software is copyrighted as detailed in the LICENSE file, and * this file is also Copyright 1995 by Gran Larsson . */ /* ** Object numbers. */ #define COLOR_DEFAULT 0 #define COLOR_NGNAME 1 /* NG name in thread selector */ #define COLOR_PLUS 2 /* + in thread selector */ #define COLOR_MINUS 3 /* - in thread selector */ #define COLOR_STAR 4 /* * in thread selector */ #define COLOR_HEADER 5 /* headers in article display */ #define COLOR_SUBJECT 6 /* subject in article display */ #define COLOR_TREE 7 /* tree in article display */ #define COLOR_TREE_MARK 8 /* tree in article display, marked */ #define COLOR_MORE 9 /* the more prompt */ #define COLOR_HEADING 10 /* any heading */ #define COLOR_CMD 11 /* the command prompt */ #define COLOR_MOUSE 12 /* the mouse bar */ #define COLOR_NOTICE 13 /* notices, e.g. kill handling */ #define COLOR_SCORE 14 /* score objects */ #define COLOR_ARTLINE1 15 /* the article heading */ #define COLOR_MIMESEP 16 /* the multipart mime separator */ #define COLOR_MIMEDESC 17 /* the multipart mime description line */ #define COLOR_CITEDTEXT 18 /* cited text color */ #define COLOR_BODYTEXT 19 /* regular body text */ #define MAX_COLORS 20 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void color_init _((void)); void color_rc_attribute _((char*,char*)); void color_object _((int,bool_int)); void color_pop _((void)); void color_string _((int,char*)); void color_default _((void)); trn-4.0-test77/color.ih0000644000000000000000000000061107113133015013441 0ustar rootroot/* color.ih - Color handling for trn 4.0. */ /* This software is copyrighted as detailed in the LICENSE file, and * this file is also Copyright 1995 by Gran Larsson . */ struct color_obj { char* name; char* fg; char* bg; int attr; }; #define STACK_SIZE 16 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void output_color _((void)); trn-4.0-test77/common.h0000644000000000000000000006522007203302263013454 0ustar rootroot/* common.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #include #include #include #include #include "config.h" /* generated by installation script */ #include "config2.h" #include #include #ifdef I_SYS_FILIO # include #else # ifdef I_SYS_IOCTL # include # endif #endif #ifdef I_VFORK # include #endif #include #ifdef I_TERMIO # include #else # ifdef I_TERMIOS # include # if !defined (O_NDELAY) # define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */ # endif # else # ifdef I_SGTTY # include # endif # endif #endif #ifdef I_PTEM #include #include #endif #ifdef I_TIME #include #endif #ifdef I_SYS_TIME #include #endif #include "typedef.h" #define BITSPERBYTE 8 #define LBUFLEN 1024 /* line buffer length */ /* (don't worry, .newsrc lines can exceed this) */ #define CBUFLEN 512 /* command buffer length */ #define PUSHSIZE 256 #define MAXFILENAME 512 #define FINISHCMD 0177 #define OV_MAX_FIELDS 9 /* Things we can figure out ourselves */ #ifdef SIGTSTP # define BERKELEY /* include job control signals? */ #endif #if defined(FIONREAD) || defined(HAS_RDCHK) || defined(O_NDELAY) || defined(MSDOS) # define PENDING #endif #ifdef EUNICE # define LINKART /* add 1 level of possible indirection */ # define UNLINK(victim) while (!unlink(victim)) #else # define UNLINK(victim) unlink(victim) #endif #ifdef HAS_RENAME # define RENAME(from,to) rename(from,to) #else # define RENAME(from,to) safelink(from,to), UNLINK(from) #endif #ifdef HAS_STRSTR # define STRSTR(s1,s2) strstr((s1),(s2)) #else # define STRSTR(s1,s2) trn_strstr((s1),(s2)) #endif /* Valid substitutions for strings marked with % comment are: * %a Current article number * %A Full name of current article (%P/%c/%a) * (if LINKART defined, is the name of the real article) * %b Destination of a save command, a mailbox or command * %B The byte offset to the beginning of the article for saves * with or without the header * %c Current newsgroup, directory form * %C Current newsgroup, dot form * %d %P/%c * %D Old Distribution: line * %e Extract program * %E Extract destination directory * %f Old From: line or Reply-To: line * %F Newsgroups to followup to from Newsgroups: and Followup-To: * %h Name of header file to pass to mail or news poster * %H Host name (yours) * %i Old Message-I.D.: line, with <> * %j The terminal speed (e.g. 9600) * %I Inclusion indicator * %l News administrator login name * %L Login name (yours) * %m The current mode of trn. * %M Number of articles marked with M * %n Newsgroups from source article * %N Full name (yours) * %o Organization (yours) * %O Original working directory (where you ran trn from) * %p Your private news directory (-d switch) * %P Public news spool directory (NEWSSPOOL) * %r Last reference (parent article id) * %q The last quoted input (via %"). * %R New references list * %s Subject, with all Re's and (nf)'s stripped off * %S Subject, with one Re stripped off * %t New To: line derived from From: and Reply-To (Internet always) * %T New To: line derived from Path: * %u Number of unread articles * %U Number of unread articles disregarding current article * %v Number of unselected articles disregarding current article * %W The thread directory root * %x News library directory, usually /usr/lib/news * %X Trn's private library directory, usually %x/trn * %y From line with domain shortening (name@*.domain.nam) * %Y The tmp directory to use * %z Size of current article in bytes. * %Z Number of selected threads. * %~ Home directory * %. Directory containing . files, usually %~ * %+ Directory containing a user's init files, usually %./.trn * %# count of articles saved in current command (from 1 to n) * %^# ever-increasing number (from 1 to n) * %$ current process number * %{name} Environment variable "name". %{name-default} form allowed. * %[name] Header line beginning with "Name: ", without "Name: " * %"prompt" * Print prompt and insert what is typed. * %`command` * Insert output of command. * %(test_text=pattern?if_text:else_text) * Substitute if_text if test_text matches pattern, otherwise * substitute else_text. Use != for negated match. * % substitutions are done on test_text, if_text, and else_text. * %digit Substitute the text matched by the nth bracket in the last * pattern that had brackets. %0 matches the last bracket * matched, in case you had alternatives. * %? Insert a space unless the entire result is > 79 chars, in * which case the space becomes a newline. * * Put ^ in the middle to capitalize the first letter: %^C = Rec.humor * Put _ in the middle to capitalize last component: %_c = net/Jokes * Put \ in the middle to quote regexp and % characters in the result * Put > in the middle to return the address portion of a name. * Put ) in the middle to return the comment portion of a name. * Put ' in the middle to protect "'"s in arguments you've put in "'"s. * Put :FMT in the middle to format the result: %:-30.30t * * ~ interpretation in filename expansion happens after % expansion, so * you could put ~%{NEWSLOGNAME-news} and it will expand correctly. */ /* *** System Dependent Stuff *** */ /* NOTE: many of these are defined in the config.h file */ /* name of organization */ #ifndef ORGNAME # define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota" #endif #ifndef MBOXCHAR # define MBOXCHAR 'F' /* how to recognize a mailbox by 1st char */ #endif #ifndef ROOTID # define ROOTID 0 /* uid of superuser */ #endif #ifdef NORMSIG # define sigset signal # define sigignore(sig) signal(sig,SIG_IGN) #endif #ifndef PASSFILE # ifdef LIMITED_FILENAMES # define PASSFILE "%X/passwd" # else # define PASSFILE "/etc/passwd" # endif #endif #ifndef LOGDIRFIELD # define LOGDIRFIELD 6 /* Which field (origin 1) is the */ /* login directory in /etc/passwd? */ /* (If it is not kept in passwd, */ /* but getpwnam() returns it, */ /* define the symbol HAS_GETPWENT) */ #endif #ifndef GCOSFIELD # define GCOSFIELD 5 #endif #ifndef NEGCHAR # define NEGCHAR '!' #endif /* Space conservation section */ /* To save D space, cut down size of MAXNGTODO and VARYSIZE. */ #define MAXNGTODO 512 /* number of newsgroups allowed on command line */ #define VARYSIZE 256 /* this makes a block 1024 bytes long in DECville */ /* (used by virtual array routines) */ /* Undefine any of the following features to save both I and D space */ /* In general, earlier ones are easier to get along without */ #define USE_FILTER /* external kill/score filter */ #define CUSTOMLINES /* include code for HIDELINE and PAGESTOP */ #define WORDERASE /* enable ^W to erase a word */ #define MAILCALL /* check periodically for mail */ #define NOFIREWORKS /* keep whole screen from flashing on certain */ /* terminals such as older Televideos */ #define VERIFY /* echo the command they just typed */ #define BACKTICK /* allow %`command` */ #define PROMPTTTY /* allow %"prompt" */ #define ULSMARTS /* catch _^H in text and do underlining */ #define TERMMOD /* allow terminal type modifier on switches */ #define BAUDMOD /* allow baudrate modifier on switches */ #define ORGFILE /* if organization begins with /, look up in file */ #define TILDENAME /* allow ~logname expansion */ #define SETENV /* allow command line environment variable setting */ #define MAKEDIR /* use our makedir() instead of shell script */ #define MEMHELP /* keep help messages in memory */ #define VERBOSE /* compile in more informative messages */ #define TERSE /* compile in shorter messages */ /* (Note: both VERBOSE and TERSE can be defined; -t * sets terse mode. One or the other MUST be defined. */ #define CHARSUBST /* enable the _C command */ #define DELBOGUS /* ask if bogus newsgroups should be deleted */ #define RELOCATE /* allow newsgroup rearranging */ #define ESCSUBS /* escape substitutions in multi-character commands */ #undef MCHASE /* unmark xrefed articles on m or M */ #define MUNGHEADER /* allow alternate header formatting via */ /* environment variable ALTHEADER (not impl) */ #define ASYNC_PARSE /* allow parsing headers asyncronously to reading */ /* used by MCHASE and MUNGHEADER */ #define FINDNEWNG /* check for new newsgroups on startup */ #define FASTNEW /* do optimizations on FINDNEWNG for faster startup */ #define INNERSEARCH /* search command 'g' with article */ #define CATCHUP /* catchup command at newsgroup level */ #define NGSEARCH /* newsgroup pattern matching */ #define KILLFILES /* automatic article killer files */ #define ARTSEARCH /* pattern searches among articles */ /* /, ?, ^N, ^P, k, K */ #define EDIT_DISTANCE /* Allow -G to specify a fuzzy 'go' command */ #undef VALIDATE_XREF_SITE /* are xrefs possibly invalid? */ /*#define LONG_THREAD_NAMES *//**/ /* USEURL may soon become a Configure script option */ /*#define USEURL *//* allow trn to fetch/use WWW URLs */ /* if USEFTP is defined, trn will use the ftpgrab script for ftp: URLs * USEFTP is not very well tested, and the ftpgrab script is not * installed with make install. May go away later */ /*#define USEFTP *//**/ /* The NICEBG functions are probably only useful now for *slow* systems */ /*#define NICEBG *//* use some kind of nice-background call */ /* the specific type of NICEBG is used only if NICBG is defined */ /* select() call for nice background. */ #define NBG_SELECT /**/ /* TERMIO(S) style of nice background available. Second choice */ /*#define NBG_TERMIO *//**/ /* SIGIO style of nice background (last resort) */ /*#define NBG_SIGIO *//**/ /* SHORTSCORENAMES has not been tested recently */ /*#define SHORTSCORENAMES *//* foo/bar/baz/SCORE instead of foo.bar.baz */ /* if SCOREFILE_CACHE is defined, scorefiles will be kept in memory. */ #define SCOREFILE_CACHE /* some dependencies among options */ #ifdef SCAN # define SCAN_ART #else # ifdef SCAN_ART # undef SCAN_ART # endif #endif /* SCAN */ #ifndef SCORE # ifdef USE_FILTER # undef USE_FILTER # endif #endif /* SCORE */ #ifndef ARTSEARCH # undef KILLFILES # undef INNERSEARCH #endif #ifndef SETUIDGID # define eaccess access #endif #ifdef VERBOSE # ifdef TERSE # define IF(c) if (c) # define ELSE else # else # define IF(c) # define ELSE # endif #else /* !VERBOSE */ # ifndef TERSE # define TERSE # endif # define IF(c) ..."IF" outside of VERBOSE # define ELSE ..."ELSE" outside of VERBOSE #endif #ifdef SUPPORT_NNTP #define ElseIf else if #else #define ElseIf if #endif #ifdef DEBUG # define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}} #else # define assert(ex) ; #endif /* If you're strapped for space use the help messages in shell scripts */ /* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */ #ifdef MEMHELP /* undef MEMHELP above to get them all as sh scripts */ # undef NGHELP # undef ARTHELP # undef PAGERHELP # undef SUBSHELP #else # ifndef NGHELP /* % and ~ */ # define NGHELP "%X/ng.help" # endif # ifndef ARTHELP /* % and ~ */ # define ARTHELP "%X/art.help" # endif # ifndef PAGERHELP /* % and ~ */ # define PAGERHELP "%X/pager.help" # endif # ifndef SUBSHELP /* % and ~ */ # define SUBSHELP "%X/subs.help" # endif #endif #define TCSIZE 512 /* capacity for termcap strings */ #ifdef EDIT_DISTANCE # define MIN_DIST 7 /* Maximum error count for acceptable match */ #endif /* Additional ideas: * Make the do_newsgroup() routine a separate process. * Keep .newsrc on disk instead of in memory. * Overlays, if you have them. * Get a bigger machine. */ /* End of Space Conservation Section */ /* More System Dependencies */ /* news library */ #ifndef NEWSLIB /* ~ and %l only ("~%l" is permissable) */ # define NEWSLIB "/usr/lib/news" #endif /* path to private executables */ #ifndef PRIVLIB /* ~, %x and %l only */ # define PRIVLIB "%x/trn" #endif /* system-wide RNINIT switches */ #ifndef GLOBINIT # define GLOBINIT "%X/INIT" #endif /* where to find news files */ #ifndef NEWSSPOOL /* % and ~ */ # define NEWSSPOOL "/usr/spool/news" #endif #ifndef THREAD_DIR # undef LONG_THREAD_NAMES #endif /* default characters to use in the selection menu */ #ifndef SELECTCHARS # define SELECTCHARS "abdefgijlorstuvwxyz1234567890BCFGIKVW" #endif /* file containing list of active newsgroups and max article numbers */ #ifndef ACTIVE /* % and ~ */ # define ACTIVE "%x/active" #endif #ifndef ACTIVE_TIMES # define ACTIVE_TIMES "none" #endif #ifndef GROUPDESC # define GROUPDESC "%x/newsgroups" #endif #ifndef DBINIT # define DBINIT "%W/db.init" #endif /* location of history file */ #ifndef ARTFILE /* % and ~ */ # define ARTFILE "%x/history" #endif /* preferred shell for use in doshell routine */ /* ksh or sh would be okay here */ #ifndef PREFSHELL # define PREFSHELL "/bin/csh" #endif /* path to fastest starting shell */ #ifndef SH # define SH "/bin/sh" #endif /* path to default editor */ #ifndef DEFEDITOR # define DEFEDITOR "/usr/ucb/vi" #endif /* the user's init files */ #ifndef TRNDIR # ifdef LIMITED_FILENAMES # define TRNDIR "%./trn" # else # define TRNDIR "%./.trn" # endif #endif /* location of macro file for trn and rn modes */ #ifndef TRNMACRO # define TRNMACRO "%+/macros" #endif #ifndef RNMACRO # ifdef LIMITED_FILENAMES # define RNMACRO "%./rnmac" # else # define RNMACRO "%./.rnmac" # endif #endif /* location of full name */ #ifndef FULLNAMEFILE # ifndef PASSNAMES # ifdef LIMITED_FILENAMES # define FULLNAMEFILE "%./fullname" # else # define FULLNAMEFILE "%./.fullname" # endif # endif #endif /* The name to append to the directory name to read an overview file. */ #ifndef OV_FILE_NAME # ifdef LIMITED_FILENAMES # define OV_FILE_NAME "/overview" # else # define OV_FILE_NAME "/.overview" # endif #endif /* The name to append to the directory name to read a thread file. */ #ifndef MT_FILE_NAME # ifdef LIMITED_FILENAMES # define MT_FILE_NAME "/thread" # else # define MT_FILE_NAME "/.thread" # endif #endif /* virtual array file name template */ #ifndef VARYNAME /* % and ~ */ # define VARYNAME "%Y/rnvary.%$" #endif /* file to pass header to followup article poster */ #ifndef HEADNAME /* % and ~ */ # ifdef LIMITED_FILENAMES # define HEADNAME "%Y/tmpart.%$" # else # define HEADNAME "%./.rnhead.%$" # endif #endif #ifndef MAKEDIR /* shell script to make n-deep subdirectories */ # ifndef DIRMAKER /* % and ~ */ # define DIRMAKER "%X/makedir" # endif #endif /* trn's default access list */ #ifndef DEFACCESS # define DEFACCESS "%X/access.def" #endif /* trn's access list */ #ifndef TRNACCESS # define TRNACCESS "%+/access" #endif /* location of newsrc file */ #ifndef RCNAME /* % and ~ */ # ifdef LIMITED_FILENAMES # define RCNAME "%./newsrc" # else # define RCNAME "%./.newsrc" # endif #endif /* temporary newsrc file in case we crash while writing out */ #ifndef RCNAME_NEW # define RCNAME_NEW "%s.new" #endif /* newsrc file at the beginning of this session */ #ifndef RCNAME_OLD # define RCNAME_OLD "%s.old" #endif /* lockfile for each newsrc that is not ~/.newsrc (which uses .rnlock) */ #ifndef RCNAME_LOCK # define RCNAME_LOCK "%s.LOCK" #endif /* news source info for each newsrc that is not ~/.newsrc (.rnlast) */ #ifndef RCNAME_INFO # define RCNAME_INFO "%s.info" #endif /* if existent, contains process number of current or crashed trn */ #ifndef LOCKNAME /* % and ~ */ # ifdef LIMITED_FILENAMES # define LOCKNAME "%+/lock" # else # define LOCKNAME "%./.rnlock" # endif #endif /* information from last invocation of trn */ #ifndef LASTNAME /* % and ~ */ # ifdef LIMITED_FILENAMES # define LASTNAME "%+/rnlast" # else # define LASTNAME "%./.rnlast" # endif #endif #ifndef SIGNATURE_FILE # ifdef LIMITED_FILENAMES # define SIGNATURE_FILE "%./signatur" # else # define SIGNATURE_FILE "%./.signature" # endif #endif #ifndef NNTP_AUTH_FILE # define NNTP_AUTH_FILE "%./.nntpauth" #endif /* a motd-like file for trn */ #ifndef NEWSNEWSNAME /* % and ~ */ # define NEWSNEWSNAME "%X/newsnews" #endif /* command to send a reply */ #ifndef MAILPOSTER /* % and ~ */ # define MAILPOSTER "Rnmail -h %h" #endif #ifdef INTERNET # ifndef MAILHEADER /* % */ # define MAILHEADER "To: %t\nSubject: %(%i=^$?:Re: %S\nX-Newsgroups: %n\nIn-Reply-To: %i)\n%(%{FROM}=^$?:From: %{FROM}\n)%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)%(%[references]=^$?:References: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # endif #else # ifndef MAILHEADER /* % */ # define MAILHEADER "To: %T\nSubject: %(%i=^$?:Re: %S\n%(%{FROM}=^$?:From: %{FROM}\n)%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)X-Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # endif #endif #ifndef YOUSAID /* % */ # define YOUSAID "In article %i you write:" #endif /* command to forward an article */ #define FORWARDPOSTER MAILPOSTER #ifdef INTERNET # ifndef FORWARDHEADER /* % */ # define FORWARDHEADER "To: %\"\n\nTo: \"\nSubject: %(%i=^$?:%[subject] (fwd\\)\n%(%{FROM}=^$?:From: %{FROM}\n)%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)X-Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nMime-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"=%$%^#=--\"\nCc: \nBcc: \n\n" # endif #else # ifndef FORWARDHEADER /* % */ # define FORWARDHEADER "To: %\"\n\nTo: \"\nSubject: %(%i=^$?:%[subject] (fwd\\)\n%(%{FROM}=^$?:From: %{FROM}\n)%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)X-Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nMime-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"=%$%^#=--\"\nCc: \nBcc: \n\n" # endif #endif #ifndef FORWARDMSG /* % */ # define FORWARDMSG "------- start of forwarded message -------" #endif #ifndef FORWARDMSGEND /* % */ # define FORWARDMSGEND "------- end of forwarded message -------" #endif /* command to submit a followup article */ #ifndef NEWSPOSTER /* % and ~ */ # define NEWSPOSTER "Pnews -h %h" #endif #ifndef NEWSHEADER /* % */ # define NEWSHEADER "%(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n\n))Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \n%(%{FROM}=^$?:From: %{FROM}\n)%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Distribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\nCc: %(%F=poster?%t:%(%F!=@?:%F))\n\n" #endif #ifndef ATTRIBUTION /* % */ # define ATTRIBUTION "In article %i,%?%)f <%>f> wrote:" #endif #ifndef PIPESAVER /* % */ # define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b" #endif #ifndef SHARSAVER # define SHARSAVER "tail +%Bc %A | /bin/sh" #endif #ifndef CUSTOMSAVER # define CUSTOMSAVER "tail +%Bc %A | %e" #endif #ifndef VERIFY_RIPEM # define VERIFY_RIPEM "ripem -d -Y fgs -i %A" #endif #ifndef VERIFY_PGP # define VERIFY_PGP "pgp +batchmode -m %A" #endif #ifdef MKDIRS # ifndef SAVEDIR /* % and ~ */ # define SAVEDIR "%p/%c" # endif # ifndef SAVENAME /* % */ # define SAVENAME "%a" # endif #else # ifndef SAVEDIR /* % and ~ */ # define SAVEDIR "%p" # endif # ifndef SAVENAME /* % */ # define SAVENAME "%^C" # endif #endif #ifndef KILLGLOBAL /* % and ~ */ # define KILLGLOBAL "%p/KILL" #endif #ifndef KILLLOCAL /* % and ~ */ # define KILLLOCAL "%p/%c/KILL" #endif #ifndef KILLTHREADS /* % and ~ */ # define KILLTHREADS "%+/Kill/Threads" #endif #ifdef USE_FILTER # ifndef FILTERPROG # define FILTERPROG "%+/filter" # endif #endif /* USE_FILTER */ /* how to cancel an article */ #ifndef CALL_INEWS # ifdef BNEWS # define CALL_INEWS "%x/inews -h <%h" # else # define CALL_INEWS "inews -h <%h" # endif #endif /* how to cancel an article, continued */ #ifndef CANCELHEADER # define CANCELHEADER "Newsgroups: %n\nSubject: cancel\n%(%{FROM}=^$?:From: %{FROM}\n)Control: cancel %i\nDistribution: %D\n\n%i was cancelled from within trn.\n" #endif /* how to supersede an article */ #ifndef SUPERSEDEHEADER # define SUPERSEDEHEADER "Newsgroups: %n\nSubject: %[subject]\n%(%{FROM}=^$?:From: %{FROM}\n)Summary: %[summary]\nExpires: %[expires]\nReferences: %[references]\nFrom: %[from]\nReply-To: %[reply-to]\nSupersedes: %i\nSender: %[sender]\nFollowup-To: %[followup-to]\nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n" #endif #ifndef LOCALTIMEFMT # define LOCALTIMEFMT "%a %b %d %X %Z %Y" #endif /* where to find the mail file */ #ifndef MAILFILE # define MAILFILE "/usr/spool/mail/%L" #endif /* dependencies of nice background scoring */ #ifdef PENDING # ifdef NICEBG # ifndef I_TERMIO # ifndef I_TERMIOS # undef NBG_TERMIO # endif # endif /* later insert defines to turn off NBG_SIGIO when needed */ # endif #else /* !PENDING */ # undef NICEBG #endif #ifndef HAS_VFORK # define vfork fork #endif /* Winsock is only initialized if NNTP_SUPPORT is defined */ #ifdef WINSOCK # ifndef NNTP_SUPPORT # ifdef USEURL # undef USEURL # endif # endif #endif /* TK requires TCL */ #ifdef USE_TK # ifndef USE_TCL # define USE_TCL # endif #endif /* *** end of the machine dependent stuff *** */ /* GLOBAL THINGS */ /* file statistics area */ EXT struct stat filestat; /* various things of type char */ EXT char msg[CBUFLEN]; /* general purpose message buffer */ EXT char buf[LBUFLEN+1]; /* general purpose line buffer */ EXT char cmd_buf[CBUFLEN]; /* buffer for formatting system commands */ EXT char* indstr INIT(">"); /* indent for old article embedded in followup */ EXT char* cwd INIT(NULL); /* current working directory */ EXT char* dfltcmd INIT(NULL); /* 1st char is default command */ /* switches */ #ifdef DEBUG EXT int debug INIT(0); /* -D */ # define DEB_COREDUMPSOK 2 # define DEB_HEADER 4 # define DEB_INTRP 8 # define DEB_NNTP 16 # define DEB_INNERSRCH 32 # define DEB_FILEXP 64 # define DEB_HASH 128 # define DEB_XREF_MARKER 256 # define DEB_CTLAREA_BITMAP 512 # define DEB_RCFILES 1024 # define DEB_NEWSRC_LINE 2048 # define DEB_SEARCH_AHEAD 4096 # define DEB_CHECKPOINTING 8192 # define DEB_FEED_XREF 16384 #endif #ifdef ARTSEARCH EXT int scanon INIT(0); /* -S */ #endif EXT bool use_threads INIT(THREAD_INIT); /* -x */ EXT int max_tree_lines INIT(6); EXT char UnivSelCmds[3] INIT("Z>"); EXT char NewsrcSelCmds[3] INIT("Z>"); EXT char AddSelCmds[3] INIT("Z>"); EXT char NewsgroupSelCmds[3] INIT("Z>"); EXT char NewsSelCmds[3] INIT("Z>"); EXT char OptionSelCmds[3] INIT("Z>"); EXT int UnivSelBtnCnt; EXT int NewsrcSelBtnCnt; EXT int AddSelBtnCnt; EXT int NewsgroupSelBtnCnt; EXT int NewsSelBtnCnt; EXT int OptionSelBtnCnt; EXT int ArtPagerBtnCnt; EXT char* UnivSelBtns INIT(NULL); EXT char* NewsrcSelBtns INIT(NULL); EXT char* AddSelBtns INIT(NULL); EXT char* NewsgroupSelBtns INIT(NULL); EXT char* NewsSelBtns INIT(NULL); EXT char* OptionSelBtns INIT(NULL); EXT char* ArtPagerBtns INIT(NULL); EXT bool dont_filter_control INIT(FALSE); /* -j */ EXT int join_subject_len INIT(0); /* -J */ EXT bool kill_thru_kludge INIT(TRUE); /* -k */ EXT bool keep_the_group_static INIT(FALSE); /* -K */ EXT bool mbox_always INIT(FALSE); /* -M */ EXT bool norm_always INIT(FALSE); /* -N */ EXT bool thread_always INIT(FALSE); /* -a */ EXT int auto_arrow_macros INIT(2); /* -A */ EXT bool breadth_first INIT(FALSE); /* -b */ EXT bool bkgnd_spinner INIT(FALSE); /* -B */ EXT bool novice_delays INIT(TRUE); /* +f */ EXT int olden_days INIT(FALSE); /* -o */ EXT char auto_select_postings INIT(0); /* -p */ EXT bool checkflag INIT(FALSE); /* -c */ EXT char* savedir INIT(NULL); /* -d */ EXT bool suppress_cn INIT(FALSE); /* -s */ EXT int countdown INIT(5); /* how many lines to list before invoking -s */ EXT bool muck_up_clear INIT(FALSE); /* -loco */ EXT bool erase_screen INIT(FALSE); /* -e */ EXT bool can_home INIT(FALSE); EXT bool erase_each_line INIT(FALSE); /* fancy -e */ EXT int findlast INIT(0); /* -r */ EXT bool allow_typeahead INIT(FALSE); /* -T */ #ifdef EDIT_DISTANCE EXT bool fuzzyGet INIT(FALSE); /* -G */ #endif #ifdef VERBOSE # ifdef TERSE EXT bool verbose INIT(TRUE); /* +t */ # endif #endif EXT bool unbroken_subjects INIT(FALSE); /* -u */ EXT bool unsafe_rc_saves INIT(FALSE); /* -U */ #ifdef VERIFY EXT bool verify INIT(FALSE); /* -v */ #endif EXT bool quickstart INIT(FALSE); /* -q */ #define DEFAULT_REFETCH_SECS 4L*60*60 /* 4 hours */ EXT time_t defRefetchSecs INIT(DEFAULT_REFETCH_SECS); /* -z */ EXT int word_wrap_offset INIT(8); /* right-hand column size (0 is off) */ #define NOMARKING 0 #define STANDOUT 1 #define UNDERLINE 2 #define LASTMARKING 3 EXT int marking INIT(NOMARKING); /* -m */ #define HALFPAGE_MARKING 1 #define BACKPAGE_MARKING 2 EXT int marking_areas INIT(HALFPAGE_MARKING); EXT ART_LINE initlines INIT(0); /* -i */ EXT bool append_unsub /* -I */ #ifdef APPEND_UNSUB INIT(1); #else INIT(0); #endif EXT bool UseUnivSelector INIT(FALSE); EXT bool UseNewsrcSelector INIT(FALSE); EXT bool UseAddSelector INIT(TRUE); EXT bool UseNewsgroupSelector INIT(TRUE); EXT int UseNewsSelector INIT(SELECT_INIT-1); EXT bool UseMouse INIT(FALSE); EXT char MouseModes[32] INIT("acjlptwvK"); EXT bool use_colors INIT(FALSE); EXT bool UseTk INIT(FALSE); EXT bool UseTcl INIT(FALSE); EXT bool UseSelNum INIT(FALSE); EXT bool SelNumGoto INIT(FALSE); /* miscellania */ EXT bool in_ng INIT(FALSE); /* TRUE if in a newsgroup */ EXT char mode INIT('i'); /* current state of trn */ EXT char gmode INIT('I'); /* general mode of trn */ EXT FILE* tmpfp INIT(NULL); /* scratch fp used for .rnlock, .rnlast, etc. */ #define PLURAL(num) ((num)==1? nullstr : ess) #define ALLBITS(val,bits) (((val) & (bits)) == (bits)) /* Factored strings */ EXT char nullstr[1] INIT(""); EXT char ess[2] INIT("s"); EXT char sh[] INIT(SH); EXT char defeditor[] INIT(DEFEDITOR); EXT char hforhelp[] INIT("Type h for help.\n"); #ifdef STRICTCR EXT char badcr[] INIT("\nUnnecessary CR ignored.\n"); #endif EXT char readerr[] INIT("rn read error"); EXT char unsubto[] INIT("Unsubscribed to newsgroup %s\n"); EXT char cantopen[] INIT("Can't open %s\n"); EXT char cantcreate[] INIT("Can't create %s\n"); EXT char cantrecreate[] INIT("Can't recreate %s -- restoring older version.\n\ Perhaps you are near or over quota?\n"); #ifdef VERBOSE EXT char nocd[] INIT("Can't chdir to directory %s\n"); #else EXT char nocd[] INIT("Can't find %s\n"); #endif #ifdef NOLINEBUF #define FLUSH ,fflush(stdout) #else #define FLUSH #endif #ifdef lint #undef FLUSH #define FLUSH #undef putchar #define putchar(c) #endif #define nntp_advise(str) fputs(str,stdout) #define nntp_init_error(str) fputs(str,stdout) #define nntp_error(str) fputs(str,stderr) #define NNTP_ERROR_IS_FATAL #define NNTP_HANDLE_TIMEOUT #define NNTP_HANDLE_AUTH_ERR trn-4.0-test77/config2.h0000644000000000000000000000601307113133015013503 0ustar rootroot/* config2.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifdef HAS_GETPWENT # include #endif #ifdef I_UNISTD # include #endif #ifdef I_STDLIB # include #else # ifndef USE_DEBUGGING_MALLOC char* malloc(); char* realloc(); char* getenv(); # endif #endif #ifdef USE_DEBUGGING_MALLOC # include "malloc.h" # define safemalloc malloc # define saferealloc realloc #endif #ifdef I_STRING # include #else # include #endif #ifndef S_ISDIR # define S_ISDIR(m) ( ((m) & S_IFMT) == S_IFDIR ) #endif #ifndef S_ISCHR # define S_ISCHR(m) ( ((m) & S_IFMT) == S_IFCHR ) #endif #ifndef S_ISREG # define S_ISREG(m) ( ((m) & S_IFMT) == S_IFREG ) #endif #ifndef isalnum # define isalnum(c) (isalpha(c) || isdigit(c)) #endif #ifdef MSDOS #include "msdos.h" #endif /* what to do with ansi prototypes -- '()' == ignore, 'x' == use */ #ifndef _ # if defined(__STDC__) || defined (MSDOS) # define _(x) x # ifndef CONST # define CONST const # endif # else # define _(x) () # ifndef CONST # define CONST # endif # endif #endif /* some handy defs */ #define bool char #define bool_int int #define char_int int #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define Ctl(ch) (ch & 037) #define strNE(s1,s2) (strcmp(s1,s2)) #define strEQ(s1,s2) (!strcmp(s1,s2)) #define strnNE(s1,s2,l) (strncmp(s1,s2,l)) #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l)) #ifdef HAS_STRCASECMP #define strcaseCMP(s1,s2) strcasecmp(s1,s2) #define strcaseNE(s1,s2) (strcasecmp(s1,s2)) #define strcaseEQ(s1,s2) (!strcasecmp(s1,s2)) #define strncaseCMP(s1,s2,l) strncasecmp(s1,s2,l) #define strncaseNE(s1,s2,l) (strncasecmp(s1,s2,l)) #define strncaseEQ(s1,s2,l) (!strncasecmp(s1,s2,l)) #else #define strcaseCMP(s1,s2) trn_casecmp(s1,s2) #define strcaseNE(s1,s2) (trn_casecmp(s1,s2)) #define strcaseEQ(s1,s2) (!trn_casecmp(s1,s2)) #define strncaseCMP(s1,s2,l) trn_ncasecmp(s1,s2,l) #define strncaseNE(s1,s2,l) (trn_ncasecmp(s1,s2,l)) #define strncaseEQ(s1,s2,l) (!trn_ncasecmp(s1,s2,l)) #endif /* some slight-of-hand for compatibility issues */ #ifdef HAS_STRCHR # ifndef index # define index strchr # endif # ifndef rindex # define rindex strrchr # endif #endif #ifdef HAS_MEMCMP # ifndef bcmp # define bcmp(s,d,l) memcmp((s),(d),(l)) # endif #endif #ifdef HAS_MEMCPY # ifndef bcopy # define bcopy(s,d,l) memcpy((d),(s),(l)) # endif #endif #ifdef HAS_MEMSET # ifndef bzero # define bzero(s,l) memset((s),0,(l)) # endif #endif #ifdef SUPPLEMENT_STRING_H char* index(); char* rindex(); char* strcat(); char* strcpy(); #endif #ifdef HAS_GETPWENT # ifndef __STDC__ struct passwd* getpwuid _((uid_t)); struct passwd* getpwnam _((char*)); # endif #endif #ifndef __STDC__ char* getcwd(); char* getlogin(); int fseek(); long atol(), ftell(); extern int errno; #endif #ifndef FILE_REF # define FILE_REF(s) (*(s) == '/' ? '/' : 0) #endif /* how to open binary format files */ #ifndef FOPEN_RB # define FOPEN_RB "r" #endif #ifndef FOPEN_WB # define FOPEN_WB "w" #endif trn-4.0-test77/config_h.SH0000644000000000000000000004234507245263176014045 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting config.h (with variable substitutions)" sed <config.h -e 's!^#undef\(.*\)/\*!/\*#define\1\*//\*!' -e 's!^#un-def!#undef!' /* * This file was produced by running the config_h.SH script, which * gets its values from config.sh, which is generally produced by * running Configure. * * Feel free to modify any of this as the need arises. Note, however, * that running config_h.SH again will wipe out any changes you've made. * For a more permanent change edit config.sh and rerun config_h.SH. * * \$Id: Config_h.U,v 3.0.1.5 1997/02/28 14:57:43 ram Exp $ */ /* * Package name : $package * Source directory : $src * Configuration time: $cf_time * Configured by : $cf_by * Target system : $myuname */ #ifndef _config_h_ #define _config_h_ /* EUNICE: * This symbol, if defined, indicates that the program is being compiled * under the EUNICE package under VMS. The program will need to handle * things like files that don't go away the first time you unlink them, * due to version numbering. It will also need to compensate for lack * of a respectable link() command. */ #$d_eunice EUNICE /**/ /* HASATTRIBUTE: * This symbol indicates the C compiler can check for function attributes, * such as printf formats. This is normally only supported by GNU cc. */ #$d_attribut HASATTRIBUTE /**/ #ifndef HASATTRIBUTE #define __attribute__(_arg_) #endif /* HAS_GETPWENT: * This symbol, if defined, indicates that the getpwent routine is * available for sequential access of the passwd database. * If this is not available, the older getpw() function may be available. */ #$d_getpwent HAS_GETPWENT /**/ /* INTERNET: * This symbol, if defined, indicates that there is a mailer available * which supports internet-style addresses (user@site.domain). */ #$d_internet INTERNET /**/ /* HAS_MEMCMP: * This symbol, if defined, indicates that the memcmp routine is available * to compare blocks of memory. */ #$d_memcmp HAS_MEMCMP /**/ /* HAS_MEMCPY: * This symbol, if defined, indicates that the memcpy routine is available * to copy blocks of memory. */ #$d_memcpy HAS_MEMCPY /**/ /* HAS_MEMSET: * This symbol, if defined, indicates that the memset routine is available * to set blocks of memory. */ #$d_memset HAS_MEMSET /**/ /* HAS_MKDIR: * This symbol, if defined, indicates that the mkdir routine is available * to create directories. Otherwise you should fork off a new process to * exec /bin/mkdir. */ #$d_mkdir HAS_MKDIR /**/ /* NEWS_ADMIN: * This symbol, if defined, contains the login name of the news * administrator. */ #$d_newsadm NEWS_ADMIN "$newsadmin" /**/ /* NOLINEBUF: * This symbol, if defined, indicates that stdout is not buffered, so that * the program can call setbuf() or setlinebuf() for efficiency. */ #$d_nolnbuf NOLINEBUF /**/ /* NORMSIG: * This symbol, if defined, indicates that normal signal handling routines * should be used, as opposed to the ones in 4.1bsd (sigset, etc.). */ #$d_normsig NORMSIG /**/ /* HAS_RDCHK: * This symbol, if defined, indicates that the rdchk routine is available * to find out if there is input pending on an IO channel. Generally * the routine is used only if FIONREAD and O_NDELAY aren't available. */ #$d_rdchk HAS_RDCHK /**/ /* HAS_SIGBLOCK: * This symbol, if defined, indicates that the sigblock routine is * available to block signal reception. */ #$d_sigblock HAS_SIGBLOCK /**/ /* HAS_SIGHOLD: * This symbol, if defined, indicates that the sighold routine is * available to hold signals. */ #$d_sighold HAS_SIGHOLD /**/ /* HAS_STRCASECMP: * This symbol, if defined, indicates that the strcasecmp() routine is * available for case-insensitive string compares. */ #$d_strccmp HAS_STRCASECMP /**/ /* UNION_WAIT: * This symbol if defined indicates to the C program that the argument * for the wait() system call should be declared as 'union wait status' * instead of 'int status'. You probably need to include * in the former case (see I_SYSWAIT). */ #$d_uwait UNION_WAIT /**/ /* HAS_VFORK: * This symbol, if defined, indicates that vfork() exists. */ #$d_vfork HAS_VFORK /**/ /* Signal_t: * This symbol's value is either "void" or "int", corresponding to the * appropriate return type of a signal handler. Thus, you can declare * a signal handler using "Signal_t (*handler)()", and define the * handler using "Signal_t handler(sig)". */ #define Signal_t $signal_t /* Signal handler's return type */ /* USE_WIFSTAT: * This symbol, if defined, indicates to the C program that the argument * for the WIFxxx set of macros such as WIFSIGNALED or WIFEXITED can * be of the same kind as the one used to hold the wait() status. Should * only matter on HP-UX, where the macros are incorrectly written and * therefore cause programs using them with an 'union wait' variable * to not compile properly. See also UNION_WAIT. */ #$d_wifstat USE_WIFSTAT /**/ /* DEFEDITOR: * This symbol contains the full pathname of the default editor. */ #define DEFEDITOR "$defeditor" /**/ /* I_DIRENT: * This symbol, if defined, indicates to the C program that it should * include . Using this symbol also triggers the definition * of the Direntry_t define which ends up being 'struct dirent' or * 'struct direct' depending on the availability of . */ /* Direntry_t: * This symbol is set to 'struct direct' or 'struct dirent' depending on * whether dirent is available or not. You should use this pseudo type to * portably declare your directory entries. */ #$i_dirent I_DIRENT /**/ #define Direntry_t $direntrytype /* I_STDLIB: * This symbol, if defined, indicates that exists and should * be included. */ #$i_stdlib I_STDLIB /**/ /* I_STRING: * This symbol, if defined, indicates to the C program that it should * include (USG systems) instead of (BSD systems). */ #$i_string I_STRING /**/ /* I_SYS_DIR: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_sysdir I_SYS_DIR /**/ /* I_SYS_IOCTL: * This symbol, if defined, indicates that exists and should * be included. Otherwise, include or . */ /* I_SYS_FILIO: * This symbol, if defined, indicates that exists and * should be included instead of . */ #$i_sysioctl I_SYS_IOCTL /**/ #$i_sysfilio I_SYS_FILIO /**/ /* I_SYS_NDIR: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_sysndir I_SYS_NDIR /**/ /* I_SYS_WAIT: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_syswait I_SYS_WAIT /**/ /* I_TERMIO: * This symbol, if defined, indicates that the program should include * rather than . There are also differences in * the ioctl() calls that depend on the value of this symbol. */ /* I_TERMIOS: * This symbol, if defined, indicates that the program should include * the POSIX termios.h rather than sgtty.h or termio.h. * There are also differences in the ioctl() calls that depend on the * value of this symbol. */ /* I_SGTTY: * This symbol, if defined, indicates that the program should include * rather than . There are also differences in * the ioctl() calls that depend on the value of this symbol. */ #$i_termio I_TERMIO /**/ #$i_termios I_TERMIOS /**/ #$i_sgtty I_SGTTY /**/ /* I_UNISTD: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_unistd I_UNISTD /**/ /* I_UTIME: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_utime I_UTIME /**/ /* I_VFORK: * This symbol, if defined, indicates to the C program that it should * include vfork.h. */ #$i_vfork I_VFORK /**/ /* MAILFILE: * This symbol contains the interpretable name of the mail spool file * for the current user. The program must be prepared to substitute * the HOME directory for %~, and the login id for %L. */ #define MAILFILE "$mailfile" /**/ /* MBOXCHAR: * This symbol contains a character which will match the beginning * of a mailbox file. */ #define MBOXCHAR '$mboxchar' /**/ /* PASSNAMES: * This symbol, if defined, indicates that full names are stored in * the /etc/passwd file. */ /* BERKNAMES: * This symbol, if defined, indicates that full names are stored in * the /etc/passwd file in Berkeley format (name first thing, everything * up to first comma, with & replaced by capitalized login id, yuck). */ #$d_passnames PASSNAMES /* (undef to take name from ~/.fullname) */ #$d_berknames BERKNAMES /* (that is, ":name,stuff:") */ /* INSTALLPREFIX: * This symbol contains the name of the install prefix for this package. */ #define INSTALLPREFIX "$prefix" /**/ /* PREFSHELL: * This symbol contains the full name of the preferred user shell on this * system. Usual values are /bin/csh, /bin/ksh, /bin/sh. */ #define PREFSHELL "$prefshell" /**/ /* ROOTID: * This symbol contains the uid of root, normally 0. */ #define ROOTID $rootid /**/ /* ACTIVE: * The name of the active file for the news system. This file contains * the list of active newsgroups. The name may have ~ on the front. */ /* ACTIVE_TIMES: * The name of the active.times file for the news system. */ #define ACTIVE "$active" /**/ #$d_acttimes ACTIVE_TIMES "$acttimes" /**/ /* HAS_FTIME: * This symbol, if defined, indicates that the ftime() routine exists. * It is basically a sub-second accuracy clock, but is less accurate * than gettimeofday(2) anyway. The type "Timeval" should be used to * refer to "struct timeb". */ /* HAS_GETTIMEOFDAY: * This symbol, if defined, indicates that the gettimeofday() system * call is available for a sub-second accuracy clock. Usually, the file * needs to be included (see I_SYS_RESOURCE). * The type "Timeval" should be used to refer to "struct timeval". */ #$d_ftime HAS_FTIME /**/ #$d_gettimeod HAS_GETTIMEOFDAY /**/ #ifdef HAS_GETTIMEOFDAY #define Timeval struct timeval /* Structure used by gettimeofday() */ #endif #if defined(HAS_FTIME) && !defined(Timeval) #define Timeval struct timeb /* Structure used by ftime() */ #endif /* HAS_GETHOSTNAME: * This symbol, if defined, indicates that the C program may use the * gethostname() routine to derive the host name. */ /* HAS_UNAME: * This symbol, if defined, indicates that the C program may use the * uname() routine to derive the host name. */ /* PHOSTCMD: * This symbol, if defined, indicates that the C program may use the * contents of PHOSTCMD as a command to feed to the popen() routine * to derive the host name. * Note that the command uses a fully qualified path, so that it is safe * even if used by a process with super-user privileges. */ /* HAS_RES_INIT: * This symbol, if defined, indicates that the C program may use the * res_init() routine to derive the domain. */ /* HAS_GETDOMAINNAME: * This symbol, if defined, indicates that the C program may use the * getdomainname() routine to derive the domain. */ #$d_gethname HAS_GETHOSTNAME /**/ #$d_uname HAS_UNAME /**/ #$d_phostcmd PHOSTCMD "$aphostcmd" /* How to get the host name */ #$d_resinit HAS_RES_INIT /**/ #$d_getdname HAS_GETDOMAINNAME /**/ /* HAS_GETWD: * This symbol, if defined, indicates that the getwd routine is * available to get the working directory. */ /* HAS_GETCWD: * This symbol, if defined, indicates that the getcwd routine is * available to get the working directory. */ #$d_getwd HAS_GETWD /**/ #$d_getcwd HAS_GETCWD /**/ /* HAS_TERMLIB: * This symbol, when defined, indicates that termlib-style routines * are available. There is nothing to include. */ #$d_havetlib HAS_TERMLIB /**/ /* IGNOREORG: * This symbol, if defined, indicates that the ORGANIZATION environment * variable does not contain an organization name. */ #$d_ignoreorg IGNOREORG /**/ /* SUPPORT_NNTP: * This symbol, if defined, indicates that NNTP support is possible. */ /* USE_GENAUTH: * This symbol, if defined, indicates that authinfo generic * authentication is to be supported. */ /* SERVER_NAME: * When using NNTP, this symbol indicates the server name or a * file to open to read the server name. */ /* SUPPORT_XTHREAD: * This symbol, if defined, indicates that .thread files can be * grabbed via NNTP. */ /* HAS_LOCAL_SPOOL: * This symbol, if defined, indicates that there's a local spool * directory configured into trn. */ #$d_nntp SUPPORT_NNTP /**/ #$d_genauth USE_GENAUTH /**/ #define SERVER_NAME "$servername" /**/ #$d_xthread SUPPORT_XTHREAD /**/ #$d_local HAS_LOCAL_SPOOL /**/ /* void: * This symbol is used for void functions. On implementations which * support void appropriately, its value is "void". Otherwise, its * value should be set to "int". */ #$d_novoid void int /**/ /* HAS_RENAME: * This symbol, if defined, indicates that the rename routine is available * to rename files. Otherwise you should do the unlink(), link(), unlink() * trick. */ #$d_rename HAS_RENAME /**/ /* size_t: * This symbol is defined as an int if no size_t definition exists. */ #$d_sizet size_t int /**/ /* HAS_STRCHR: * This symbol is defined to indicate that the strchr()/strrchr() * functions are available for string searching. If not, try the * index()/rindex() pair. */ #$d_strchr HAS_STRCHR /**/ /* HAS_STRFTIME: * This symbol, if defined, indicates that the strftime routine is * available to format locale-specific times. */ #$d_strftime HAS_STRFTIME /**/ /* HAS_STRSTR: * This symbol, if defined, indicates that the strstr routine is * available to find substrings. */ #$d_strstr HAS_STRSTR /**/ /* EMULATE_NDIR: * This symbol, if defined, indicates that the program should compile * the ndir.c code provided with the package. */ /* I_NDIR: * This symbol, if defined, indicates that the program should include the * system's version of ndir.h, rather than the one with this package. */ #$d_usendir EMULATE_NDIR /**/ #$d_libndir I_NDIR /**/ /* I_PTEM: * This symbol, if defined, indicates to the C program that it should * include ptem.h. */ #$i_ptem I_PTEM /**/ /* I_TIME: * This symbol, if defined, indicates to the C program that it should * include . */ /* I_SYS_TIME: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_time I_TIME /**/ #$i_systime I_SYS_TIME /**/ /* MIMECAP: * This symbol contains the pathname of mimecap file, which controls * what programs get run when handling mime articles. It is often * metamail's "mailcap" file, and is the same format. */ #define MIMECAP "%./.mimecap:~/.mailcap:$mimecap" /**/ /* NEWSLIB: * This symbol contains the name of the directory serving as the news * library. The program must be prepared to do ~ expansion on it. */ /* EXTRAINEWS: * Contains the full path and filename of an inews to use for local * postings, or nothing. */ /* GROUPDESC: * Contains the full path and filename of the "newsgroups" file for * group descriptions, or nothing. */ /* SUBSCRIPTIONS: * Contains the full path and filename of the list of default * subscriptions, or nothing. */ #define NEWSLIB "$newslib" /**/ #define EXTRAINEWS "$extrainews" /**/ #define GROUPDESC "$groupdesc" /**/ #define SUBSCRIPTIONS "$subscriptions" /**/ /* NEWSSPOOL: * This symbol contains the directory name where news articles are * spooled. The program must be prepared to do ~ expansion on it. */ #define NEWSSPOOL "$newsspool" /**/ /* ORGNAME: * This symbol contains either the organizaton name or the full pathname * of a file containing the organization name, which the program must * be prepared to open and substitute the contents of. */ #define ORGNAME "$orgname" /**/ /* PHOSTNAME: * This symbol contains the posting host's name or a file from which * to read its name. */ /* HOSTBITS: * Set to TRUE if we should compare only the domain portion of the * hostname when looking for local articles. */ #define PHOSTNAME "$phost" /**/ #define HOSTBITS $hostbits /**/ /* PRIVLIB: * This symbol contains the name of the private library for this package. * The library is private in the sense that it needn't be in anyone's * execution path, but it should be accessible by the world. The program * should be prepared to do ~ expansion. */ #define PRIVLIB "$privlib" /**/ /* SCAN: * This is defined if you want strn's scan mode. */ /* SCORE: * This is defined if you want strn's article scoring. */ #$strn SCAN /**/ #$strn SCORE /**/ /* THREAD_DIR: * This symbol indicates where the thread files go. */ /* OVERVIEW_DIR: * This symbol indicates where the overview files go. */ /* OVERVIEW_FMT: * The overview.fmt file. */ #define THREAD_DIR "$threaddir" /**/ #define OVERVIEW_DIR "$overviewdir" /**/ #define OVERVIEW_FMT "$overviewfmt" /**/ /* USE_TK: * This is defined if you want to link trn with Tk. */ #$tk USE_TK /**/ /* THREAD_INIT: * This symbol indicates we act like trn no matter what our name is. */ /* SELECT_INIT: * This symbol indicates we default to the selector for group entry. */ /* MSDOS: * This is defined if this is an MSDOS system. */ #define THREAD_INIT $trn_init #define SELECT_INIT $trn_select #define CALL_INEWS "$useinews -h <%h" #define NEWSPOSTER "$bin/Pnews -h %h" #define MAILPOSTER "$bin/Rnmail -h %h" /*#define ANCIENT_NEWS *//* if your B news system is <= 2.10.1 */ #$d_msdos MSDOS /**/ #endif !GROK!THIS! trn-4.0-test77/datasrc.c0000644000000000000000000006341511437640112013606 0ustar rootroot/* datasrc.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "term.h" #include "env.h" #include "util.h" #include "util2.h" #include "opt.h" #include "intrp.h" #include "init.h" #include "rcstuff.h" #include "edit_dist.h" #include "cache.h" #include "last.h" #include "rt-mt.h" #include "rt-ov.h" #include "rt-util.h" #include "INTERN.h" #include "datasrc.h" #include "datasrc.ih" #include "EXTERN.h" #include "nntp.h" void datasrc_init() { char** vals = prep_ini_words(datasrc_ini); char* machine = NULL; char* actname = NULL; char* s; datasrc_list = new_list(0,0,sizeof(DATASRC),20,LF_ZERO_MEM,NULL); #ifdef SUPPORT_NNTP nntp_auth_file = savestr(filexp(NNTP_AUTH_FILE)); machine = getenv("NNTPSERVER"); if (machine && strNE(machine,"local")) { vals[DI_NNTP_SERVER] = machine; vals[DI_AUTH_USER] = read_auth_file(nntp_auth_file, &vals[DI_AUTH_PASS]); #ifdef USE_GENAUTH vals[DI_AUTH_COMMAND] = getenv("NNTPAUTH"); #endif vals[DI_FORCE_AUTH] = getenv("NNTP_FORCE_AUTH"); new_datasrc("default",vals); } #endif trnaccess_mem = read_datasrcs(TRNACCESS); s = read_datasrcs(DEFACCESS); if (!trnaccess_mem) trnaccess_mem = s; else if (s) free(s); #ifdef SUPPORT_NNTP if (!machine) { machine = filexp(SERVER_NAME); if (FILE_REF(machine)) machine = nntp_servername(machine); if (strEQ(machine,"local")) { machine = NULL; actname = ACTIVE; } #else actname = ACTIVE; #endif prep_ini_words(datasrc_ini); /* re-zero the values */ vals[DI_NNTP_SERVER] = machine; vals[DI_ACTIVE_FILE] = actname; vals[DI_SPOOL_DIR] = NEWSSPOOL; vals[DI_THREAD_DIR] = THREAD_DIR; vals[DI_OVERVIEW_DIR] = OVERVIEW_DIR; vals[DI_OVERVIEW_FMT] = OVERVIEW_FMT; vals[DI_ACTIVE_TIMES] = ACTIVE_TIMES; vals[DI_GROUP_DESC] = GROUPDESC; #ifdef SUPPORT_NNTP if (machine) { vals[DI_AUTH_USER] = read_auth_file(nntp_auth_file, &vals[DI_AUTH_PASS]); #ifdef USE_GENAUTH vals[DI_AUTH_COMMAND] = getenv("NNTPAUTH"); #endif vals[DI_FORCE_AUTH] = getenv("NNTP_FORCE_AUTH"); } #endif new_datasrc("default",vals); #ifdef SUPPORT_NNTP } #endif unprep_ini_words(datasrc_ini); } char* read_datasrcs(filename) char* filename; { int fd; char* s; char* section; char* cond; char* filebuf = NULL; char** vals = INI_VALUES(datasrc_ini); if ((fd = open(filexp(filename),0)) >= 0) { fstat(fd,&filestat); if (filestat.st_size) { int len; filebuf = safemalloc((MEM_SIZE)filestat.st_size+2); len = read(fd,filebuf,(int)filestat.st_size); (filebuf)[len] = '\0'; prep_ini_data(filebuf,filename); s = filebuf; while ((s = next_ini_section(s,§ion,&cond)) != NULL) { if (*cond && !check_ini_cond(cond)) continue; if (strncaseEQ(section, "group ", 6)) continue; s = parse_ini_section(s, datasrc_ini); if (!s) break; new_datasrc(section,vals); } } close(fd); } return filebuf; } DATASRC* get_datasrc(name) char* name; { DATASRC* dp; for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) if (strEQ(dp->name,name)) return dp; return NULL; } DATASRC* new_datasrc(name,vals) char* name; char** vals; { DATASRC* dp = datasrc_ptr(datasrc_cnt++); char* v; if (vals[DI_NNTP_SERVER]) { #ifdef SUPPORT_NNTP dp->flags |= DF_REMOTE; #else datasrc_cnt--; return NULL; #endif } else if (!vals[DI_ACTIVE_FILE]) return NULL; /*$$*/ dp->name = savestr(name); if (strEQ(name,"default")) dp->flags |= DF_DEFAULT; #ifdef SUPPORT_NNTP if ((v = vals[DI_NNTP_SERVER]) != NULL) { char* cp; dp->newsid = savestr(v); if ((cp = index(dp->newsid, ';')) != NULL) { *cp = '\0'; dp->nntplink.port_number = atoi(cp+1); } if ((v = vals[DI_ACT_REFETCH]) != NULL && *v) dp->act_sf.refetch_secs = text2secs(v,defRefetchSecs); else if (!vals[DI_ACTIVE_FILE]) dp->act_sf.refetch_secs = defRefetchSecs; } else #endif /* SUPPORT_NNTP */ dp->newsid = savestr(filexp(vals[DI_ACTIVE_FILE])); if (!(dp->spool_dir = file_or_none(vals[DI_SPOOL_DIR]))) dp->spool_dir = savestr(tmpdir); dp->over_dir = dir_or_none(dp,vals[DI_OVERVIEW_DIR],DF_TRY_OVERVIEW); dp->over_fmt = file_or_none(vals[DI_OVERVIEW_FMT]); dp->thread_dir = dir_or_none(dp,vals[DI_THREAD_DIR],DF_TRY_THREAD); dp->grpdesc = dir_or_none(dp,vals[DI_GROUP_DESC],0); dp->extra_name = dir_or_none(dp,vals[DI_ACTIVE_TIMES],DF_ADD_OK); #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) { /* FYI, we know extra_name to be NULL in this case. */ if (vals[DI_ACTIVE_FILE]) { dp->extra_name = savestr(filexp(vals[DI_ACTIVE_FILE])); if (stat(dp->extra_name,&filestat) >= 0) dp->act_sf.lastfetch = filestat.st_mtime; } else { dp->extra_name = temp_filename(); dp->flags |= DF_TMPACTFILE; if (!dp->act_sf.refetch_secs) dp->act_sf.refetch_secs = 1; } if ((v = vals[DI_DESC_REFETCH]) != NULL && *v) dp->desc_sf.refetch_secs = text2secs(v,defRefetchSecs); else if (!dp->grpdesc) dp->desc_sf.refetch_secs = defRefetchSecs; if (dp->grpdesc) { if (stat(dp->grpdesc,&filestat) >= 0) dp->desc_sf.lastfetch = filestat.st_mtime; } else { dp->grpdesc = temp_filename(); dp->flags |= DF_TMPGRPDESC; if (!dp->desc_sf.refetch_secs) dp->desc_sf.refetch_secs = 1; } } if ((v = vals[DI_FORCE_AUTH]) != NULL && (*v == 'y' || *v == 'Y')) dp->nntplink.flags |= NNTP_FORCE_AUTH_NEEDED; if ((v = vals[DI_AUTH_USER]) != NULL) dp->auth_user = savestr(v); if ((v = vals[DI_AUTH_PASS]) != NULL) dp->auth_pass = savestr(v); #ifdef USE_GENAUTH if ((v = vals[DI_AUTH_COMMAND]) != NULL) dp->auth_command = savestr(v); #endif if ((v = vals[DI_XHDR_BROKEN]) != NULL && (*v == 'y' || *v == 'Y')) dp->flags |= DF_XHDR_BROKEN; if ((v = vals[DI_XREFS]) != NULL && (*v == 'n' || *v == 'N')) dp->flags |= DF_NOXREFS; #endif /* SUPPORT_NNTP */ return dp; } static char* dir_or_none(dp,dir,flag) DATASRC* dp; char* dir; int flag; { if (!dir || !*dir || strEQ(dir, "remote")) { dp->flags |= flag; #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) return NULL; #endif if (flag == DF_ADD_OK) { char* cp = safemalloc(strlen(dp->newsid)+6+1); sprintf(cp,"%s.times",dp->newsid); return cp; } if (flag == 0) { char* cp = rindex(dp->newsid,'/'); int len; if (!cp) return NULL; len = cp - dp->newsid + 1; cp = safemalloc(len+10+1); strcpy(cp,dp->newsid); strcpy(cp+len,"newsgroups"); return cp; } return dp->spool_dir; } if (strEQ(dir, "none")) return NULL; dp->flags |= flag; dir = filexp(dir); if (strEQ(dir,dp->spool_dir)) return dp->spool_dir; return savestr(dir); } static char* file_or_none(fn) char* fn; { if (!fn || !*fn || strEQ(fn, "none") || strEQ(fn, "remote")) return NULL; return savestr(filexp(fn)); } bool open_datasrc(dp) DATASRC* dp; { bool success; if (dp->flags & DF_UNAVAILABLE) return FALSE; set_datasrc(dp); if (dp->flags & DF_OPEN) return TRUE; #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) { if (nntp_connect(dp->newsid,1) <= 0) { dp->flags |= DF_UNAVAILABLE; return FALSE; } nntp_allow_timeout = FALSE; dp->nntplink = nntplink; if (dp->act_sf.refetch_secs) { switch (nntp_list("active", "control", 7)) { case 1: if (strnNE(ser_line, "control ", 8)) { strcpy(buf, ser_line); dp->act_sf.lastfetch = 0; success = actfile_hash(dp); break; } if (nntp_gets(buf, sizeof buf - 1) > 0 && !nntp_at_list_end(buf)) { nntp_finish_list(); success = actfile_hash(dp); break; } /* FALL THROUGH */ case 0: dp->flags |= DF_USELISTACT; if (dp->flags & DF_TMPACTFILE) { dp->flags &= ~DF_TMPACTFILE; free(dp->extra_name); dp->extra_name = NULL; dp->act_sf.refetch_secs = 0; success = srcfile_open(&dp->act_sf,(char*)NULL, (char*)NULL,(char*)NULL); } else success = actfile_hash(dp); break; case -2: printf("Failed to open news server %s:\n%s\n", dp->newsid, ser_line); termdown(2); success = FALSE; break; default: success = actfile_hash(dp); break; } } else success = actfile_hash(dp); } else #endif success = actfile_hash(dp); if (success) { dp->flags |= DF_OPEN; if (dp->flags & DF_TRY_OVERVIEW) ov_init(); if (dp->flags & DF_TRY_THREAD) mt_init(); } else dp->flags |= DF_UNAVAILABLE; #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) nntp_allow_timeout = TRUE; #endif return success; } void set_datasrc(dp) DATASRC* dp; { #ifdef SUPPORT_NNTP if (datasrc) datasrc->nntplink = nntplink; if (dp) nntplink = dp->nntplink; #endif datasrc = dp; } void check_datasrcs() { #ifdef SUPPORT_NNTP DATASRC* dp; time_t now = time((time_t*)NULL); time_t limit; if (datasrc_list) { for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) { if ((dp->flags & DF_OPEN) && dp->nntplink.rd_fp != NULL) { limit = ((dp->flags & DF_ACTIVE)? 30*60 : 10*60); if (now - dp->nntplink.last_command > limit) { DATASRC* save_datasrc = datasrc; /*printf("\n*** Closing %s ***\n", dp->name); $$*/ set_datasrc(dp); nntp_close(TRUE); dp->nntplink = nntplink; set_datasrc(save_datasrc); } } } } #endif } void close_datasrc(dp) DATASRC* dp; { #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) { if (dp->flags & DF_TMPACTFILE) UNLINK(dp->extra_name); else srcfile_end_append(&dp->act_sf, dp->extra_name); if (dp->grpdesc) { if (dp->flags & DF_TMPGRPDESC) UNLINK(dp->grpdesc); else srcfile_end_append(&dp->desc_sf, dp->grpdesc); } } #endif if (!(dp->flags & DF_OPEN)) return; #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) { DATASRC* save_datasrc = datasrc; set_datasrc(dp); nntp_close(TRUE); dp->nntplink = nntplink; set_datasrc(save_datasrc); } #endif srcfile_close(&dp->act_sf); srcfile_close(&dp->desc_sf); dp->flags &= ~DF_OPEN; if (datasrc == dp) datasrc = NULL; } bool actfile_hash(dp) DATASRC* dp; { int ret; #ifdef SUPPORT_NNTP if (dp->flags & DF_REMOTE) { DATASRC* save_datasrc = datasrc; set_datasrc(dp); spin_todo = dp->act_sf.recent_cnt; ret = srcfile_open(&dp->act_sf, dp->extra_name, "active", dp->newsid); if (spin_count > 0) dp->act_sf.recent_cnt = spin_count; set_datasrc(save_datasrc); } else #endif ret = srcfile_open(&dp->act_sf, dp->newsid, (char*)NULL, (char*)NULL); return ret; } bool find_actgrp(dp, outbuf, nam, len, high) DATASRC* dp; register char* outbuf; register char* nam; register int len; ART_NUM high; { HASHDATUM data; ACT_POS act_pos; FILE* fp = dp->act_sf.fp; char* lbp; int lbp_len; /* Do a quick, hashed lookup */ outbuf[0] = '\0'; data = hashfetch(dp->act_sf.hp, nam, len); if (data.dat_ptr) { LISTNODE* node = (LISTNODE*)data.dat_ptr; /*dp->act_sf.lp->recent = node;*/ act_pos = node->low + data.dat_len; lbp = node->data + data.dat_len; lbp_len = index(lbp, '\n') - lbp + 1; } else { lbp = NULL; lbp_len = 0; } #ifdef SUPPORT_NNTP if ((dp->flags & DF_USELISTACT) && (DATASRC_NNTP_FLAGS(dp) & NNTP_NEW_CMD_OK)) { DATASRC* save_datasrc = datasrc; set_datasrc(dp); switch (nntp_list("active", nam, len)) { case 0: set_datasrc(save_datasrc); return 0; case 1: sprintf(outbuf, "%s\n", ser_line); nntp_finish_list(); break; case -2: /*$$$$*/ break; } set_datasrc(save_datasrc); if (!lbp_len) { if (fp) (void) srcfile_append(&dp->act_sf, outbuf, len); return 1; } # ifndef ANCIENT_NEWS /* Safely update the low-water mark */ { char* f = rindex(outbuf, ' '); char* t = lbp + lbp_len; while (*--t != ' ') ; while (t > lbp) { if (*--t == ' ') break; if (f[-1] == ' ') *t = '0'; else *t = *--f; } } # endif high = (ART_NUM)atol(outbuf+len+1); } #endif if (lbp_len) { #ifdef SUPPORT_NNTP if ((dp->flags & DF_REMOTE) && dp->act_sf.refetch_secs) { int num; char* cp; if (high && high != (ART_NUM)atol(cp = lbp+len+1)) { while (isdigit(*cp)) cp++; while (*--cp != ' ') { num = high % 10; high = high / 10; *cp = '0' + (char)num; } fseek(fp, act_pos, 0); fwrite(lbp, 1, lbp_len, fp); } goto use_cache; } #endif /* hopefully this forces a reread */ fseek(fp,2000000000L,1); /*$$ if line has changed length or is not there, we should * discard/close the active file, and re-open it. $$*/ if (fseek(fp, act_pos, 0) >= 0 && fgets(outbuf, LBUFLEN, fp) != NULL && strnEQ(outbuf, nam, len) && outbuf[len] == ' ') { /* Remember the latest info in our cache. */ (void) bcopy(outbuf, lbp, lbp_len); return 1; } use_cache: /* Return our cached version */ (void) bcopy(lbp, outbuf, lbp_len); outbuf[lbp_len] = '\0'; return 1; } return 0; /* no such group */ } char* find_grpdesc(dp, groupname) DATASRC* dp; char* groupname; { HASHDATUM data; int grouplen; int ret; if (!dp->grpdesc) return nullstr; if (!dp->desc_sf.hp) { #ifdef SUPPORT_NNTP if ((dp->flags & DF_REMOTE) && dp->desc_sf.refetch_secs) { /*DATASRC* save_datasrc = datasrc;*/ set_datasrc(dp); if ((dp->flags & (DF_TMPGRPDESC|DF_NOXGTITLE)) == DF_TMPGRPDESC && netspeed < 5) { (void)srcfile_open(&dp->desc_sf,(char*)NULL,/*$$check return?*/ (char*)NULL,(char*)NULL); grouplen = strlen(groupname); goto try_xgtitle; } spin_todo = dp->desc_sf.recent_cnt; ret = srcfile_open(&dp->desc_sf, dp->grpdesc, "newsgroups", dp->newsid); if (spin_count > 0) dp->desc_sf.recent_cnt = spin_count; /*set_datasrc(save_datasrc);*/ } else #endif ret = srcfile_open(&dp->desc_sf, dp->grpdesc, (char*)NULL, (char*)NULL); if (!ret) { #ifdef SUPPORT_NNTP if (dp->flags & DF_TMPGRPDESC) { dp->flags &= ~DF_TMPGRPDESC; UNLINK(dp->grpdesc); } #endif free(dp->grpdesc); dp->grpdesc = NULL; return nullstr; } #ifdef SUPPORT_NNTP if (ret == 2 || !dp->desc_sf.refetch_secs) dp->flags |= DF_NOXGTITLE; #endif } grouplen = strlen(groupname); data = hashfetch(dp->desc_sf.hp, groupname, grouplen); if (data.dat_ptr) { LISTNODE* node = (LISTNODE*)data.dat_ptr; /*dp->act_sf.lp->recent = node;*/ return node->data + data.dat_len + grouplen + 1; } #ifdef SUPPORT_NNTP try_xgtitle: if ((dp->flags & (DF_REMOTE|DF_NOXGTITLE)) == DF_REMOTE) { set_datasrc(dp); sprintf(ser_line, "XGTITLE %s", groupname); if (nntp_command(ser_line) > 0 && nntp_check() > 0) { nntp_gets(buf, sizeof buf - 1); if (nntp_at_list_end(buf)) sprintf(buf, "%s \n", groupname); else { nntp_finish_list(); strcat(buf, "\n"); } groupname = srcfile_append(&dp->desc_sf, buf, grouplen); return groupname+grouplen+1; } dp->flags |= DF_NOXGTITLE; if (dp->desc_sf.lp->high == -1) { srcfile_close(&dp->desc_sf); if (dp->flags & DF_TMPGRPDESC) return find_grpdesc(dp, groupname); free(dp->grpdesc); dp->grpdesc = NULL; } } #endif return nullstr; } int srcfile_open(sfp, filename, fetchcmd, server) SRCFILE* sfp; char* filename; char* fetchcmd; char* server; { register unsigned offset; register char* s; HASHDATUM data; long node_low; int keylen, linelen; FILE* fp; char* lbp; #ifdef SUPPORT_NNTP time_t now = time((time_t*)NULL); bool use_buffered_nntp_gets = 0; if (!filename) fp = NULL; else if (server) { if (!sfp->refetch_secs) { server = NULL; fp = fopen(filename, "r"); spin_todo = 0; } else if (now - sfp->lastfetch > sfp->refetch_secs && (sfp->refetch_secs != 2 || !sfp->lastfetch)) { fp = fopen(filename, "w+"); if (fp) { printf("Getting %s file from %s.", fetchcmd, server); fflush(stdout); /* tell server we want the file */ if (!(nntplink.flags & NNTP_NEW_CMD_OK)) use_buffered_nntp_gets = 1; else if (nntp_list(fetchcmd, nullstr, 0) < 0) { printf("\nCan't get %s file from server: \n%s\n", fetchcmd, ser_line) FLUSH; termdown(2); fclose(fp); return 0; } sfp->lastfetch = now; if (netspeed > 8) spin_todo = 0; } } else { server = NULL; fp = fopen(filename, "r+"); if (!fp) { sfp->refetch_secs = 0; fp = fopen(filename, "r"); } spin_todo = 0; } if (sfp->refetch_secs & 3) sfp->refetch_secs += 365L*24*60*60; } else #endif { fp = fopen(filename, "r"); spin_todo = 0; } if (filename && fp == NULL) { printf(cantopen, filename) FLUSH; termdown(1); return 0; } setspin(spin_todo > 0? SPIN_BARGRAPH : SPIN_FOREGROUND); srcfile_close(sfp); /* Create a list with one character per item using a large chunk size. */ sfp->lp = new_list(0, 0, 1, SRCFILE_CHUNK_SIZE, 0, NULL); sfp->hp = hashcreate(3001, srcfile_cmp); sfp->fp = fp; #ifdef SUPPORT_NNTP if (!filename) { (void) listnum2listitem(sfp->lp, 0); sfp->lp->high = -1; setspin(SPIN_OFF); return 1; } #endif lbp = listnum2listitem(sfp->lp, 0); data.dat_ptr = (char*)sfp->lp->first; for (offset = 0, node_low = 0; ; offset += linelen, lbp += linelen) { #ifdef SUPPORT_NNTP if (server) { if (use_buffered_nntp_gets) use_buffered_nntp_gets = 0; else if (nntp_gets(buf, sizeof buf - 1) < 0) { printf("\nError getting %s file.\n", fetchcmd) FLUSH; termdown(2); srcfile_close(sfp); setspin(SPIN_OFF); return 0; } if (nntp_at_list_end(buf)) break; strcat(buf,"\n"); fputs(buf, fp); spin(200 * netspeed); } #endif ElseIf (!fgets(buf, sizeof buf, fp)) break; for (s = buf; *s && !isspace(*s); s++) ; if (!*s) { linelen = 0; continue; } keylen = s - buf; if (*++s != '\n' && isspace(*s)) { while (*++s != '\n' && isspace(*s)) ; strcpy(buf+keylen+1, s); s = buf+keylen+1; } for (s++; *s && *s != '\n'; s++) { if (AT_GREY_SPACE(s)) *s = ' '; } linelen = s - buf + 1; if (*s != '\n') { if (linelen == sizeof buf) { linelen = 0; continue; } *s++ = '\n'; *s = '\0'; } if (offset + linelen > SRCFILE_CHUNK_SIZE) { LISTNODE* node = sfp->lp->recent; node_low += offset; node->high = node_low - 1; node->data_high = node->data + offset - 1; offset = 0; lbp = listnum2listitem(sfp->lp, node_low); data.dat_ptr = (char*)sfp->lp->recent; } data.dat_len = offset; (void) bcopy(buf, lbp, linelen); hashstore(sfp->hp, buf, keylen, data); } sfp->lp->high = node_low + offset - 1; setspin(SPIN_OFF); #ifdef SUPPORT_NNTP if (server) { fflush(fp); if (ferror(fp)) { printf("\nError writing the %s file %s.\n",fetchcmd,filename) FLUSH; termdown(2); srcfile_close(sfp); return 0; } newline(); } #endif fseek(fp,0L,0); return server? 2 : 1; } #ifdef SUPPORT_NNTP char* srcfile_append(sfp, bp, keylen) SRCFILE* sfp; char* bp; int keylen; { LISTNODE* node; long pos; HASHDATUM data; char* s; char* lbp; int linelen; pos = sfp->lp->high + 1; lbp = listnum2listitem(sfp->lp, pos); node = sfp->lp->recent; data.dat_len = pos - node->low; s = bp + keylen + 1; if (sfp->fp && sfp->refetch_secs && *s != '\n') { fseek(sfp->fp, 0, 2); fputs(bp, sfp->fp); } if (*s != '\n' && isspace(*s)) { while (*++s != '\n' && isspace(*s)) ; strcpy(bp+keylen+1, s); s = bp+keylen+1; } for (s++; *s && *s != '\n'; s++) { if (AT_GREY_SPACE(s)) *s = ' '; } linelen = s - bp + 1; if (*s != '\n') { *s++ = '\n'; *s = '\0'; } if (data.dat_len + linelen > SRCFILE_CHUNK_SIZE) { node->high = pos - 1; node->data_high = node->data + data.dat_len - 1; lbp = listnum2listitem(sfp->lp, pos); node = sfp->lp->recent; data.dat_len = 0; } data.dat_ptr = (char*)node; (void) bcopy(bp, lbp, linelen); hashstore(sfp->hp, bp, keylen, data); sfp->lp->high = pos + linelen - 1; return lbp; } #endif /* SUPPORT_NNTP */ #ifdef SUPPORT_NNTP void srcfile_end_append(sfp, filename) SRCFILE* sfp; char* filename; { if (sfp->fp && sfp->refetch_secs) { fflush(sfp->fp); if (sfp->lastfetch) { struct utimbuf ut; time(&ut.actime); ut.modtime = sfp->lastfetch; (void) utime(filename, &ut); } } } #endif /* SUPPORT_NNTP */ void srcfile_close(sfp) SRCFILE* sfp; { if (sfp->fp) { fclose(sfp->fp); sfp->fp = NULL; } if (sfp->lp) { delete_list(sfp->lp); sfp->lp = NULL; } if (sfp->hp) { hashdestroy(sfp->hp); sfp->hp = NULL; } } static int srcfile_cmp(key, keylen, data) char* key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, ((LISTNODE*)data.dat_ptr)->data + data.dat_len, keylen); } #ifdef EDIT_DISTANCE /* Edit Distance extension to trn * * Mark Maimone (mwm@cmu.edu) * Carnegie Mellon Computer Science * 9 May 1993 * * This code helps trn handle typos in newsgroup names much more * gracefully. Instead of "... does not exist!!", it will pick the * nearest one, or offer you a choice if there are several options. */ /* find_close_match -- Finds the closest match for the string given in * global ngname. If found, the result will be the corrected string * returned in that global. * * We compare the (presumably misspelled) newsgroup name with all legal * newsgroups, using the Edit Distance metric. The edit distance between * two strings is the minimum number of simple operations required to * convert one string to another (the implementation here supports INSERT, * DELETE, CHANGE and SWAP). This gives every legal newsgroup an integer * rank. * * You might want to present all of the closest matches, and let the user * choose among them. But because I'm lazy I chose to only keep track of * all with newsgroups with the *single* smallest error, in array ngptrs[]. * A more flexible approach would keep around the 10 best matches, whether * or not they had precisely the same edit distance, but oh well. */ static char** ngptrs; /* List of potential matches */ static int ngn; /* Length of list in ngptrs[] */ static int best_match; /* Value of best match */ int find_close_match() { DATASRC* dp; int ret = 0; best_match = -1; ngptrs = (char**)safemalloc(MAX_NG * sizeof (char*)); ngn = 0; /* Iterate over all legal newsgroups */ for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) { if (dp->flags & DF_OPEN) { if (dp->act_sf.hp) hashwalk(dp->act_sf.hp, check_distance, 0); else ret = -1; } } if (ret < 0) { hashwalk(newsrc_hash, check_distance, 1); ret = 0; } /* ngn is the number of possibilities. If there's just one, go with it. */ switch (ngn) { case 0: break; case 1: { char* cp = index(ngptrs[0], ' '); if (cp) *cp = '\0'; #ifdef VERBOSE IF(verbose) printf("(I assume you meant %s)\n", ngptrs[0]) FLUSH; ELSE #endif #ifdef TERSE printf("(Using %s)\n", ngptrs[0]) FLUSH; #endif set_ngname(ngptrs[0]); if (cp) *cp = ' '; ret = 1; break; } default: ret = get_near_miss(); break; } free((char*)ngptrs); return ret; } static int check_distance(len, data, newsrc_ptr) int len; HASHDATUM* data; int newsrc_ptr; { int value; char* name; if (newsrc_ptr) name = ((NGDATA*)data->dat_ptr)->rcline; else name = ((LISTNODE*)data->dat_ptr)->data + data->dat_len; /* Efficiency: don't call edit_dist when the lengths are too different. */ if (len < ngname_len) { if (ngname_len - len > LENGTH_HACK) return 0; } else { if (len - ngname_len > LENGTH_HACK) return 0; } value = edit_distn(ngname, ngname_len, name, len); if (value > MIN_DIST) return 0; if (value < best_match) ngn = 0; if (best_match < 0 || value <= best_match) { int i; for (i = 0; i < ngn; i++) { if (strEQ(name, ngptrs[i])) return 0; } best_match = value; if (ngn < MAX_NG) ngptrs[ngn++] = name; } return 0; } /* Now we've got several potential matches, and have to choose between them ** somehow. Again, results will be returned in global ngname. */ static int get_near_miss() { char promptbuf[256]; char options[MAX_NG+10]; char* op = options; int i; #ifdef VERBOSE IF(verbose) printf("However, here are some close matches:\n") FLUSH; #endif if (ngn > 9) ngn = 9; /* Since we're using single digits.... */ for (i = 0; i < ngn; i++) { char* cp = index(ngptrs[i], ' '); if (cp) *cp = '\0'; printf(" %d. %s\n", i+1, ngptrs[i]); sprintf(op++, "%d", i+1); /* Expensive, but avoids ASCII deps */ if (cp) *cp = ' '; } *op++ = 'n'; *op = '\0'; #ifdef VERBOSE IF(verbose) sprintf(promptbuf, "Which of these would you like?"); ELSE #endif #ifdef TERSE sprintf(promptbuf, "Which?"); #endif reask: in_char(promptbuf, 'A', options); #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; switch (*buf) { case 'n': case 'N': case 'q': case 'Q': case 'x': case 'X': return 0; case 'h': case 'H': #ifdef VERBOSE IF(verbose) fputs(" You entered an illegal newsgroup name, and these are the nearest possible\n matches. If you want one of these, then enter its number. Otherwise\n just say 'n'.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Illegal newsgroup, enter a number or 'n'.\n", stdout) FLUSH; #endif goto reask; default: if (isdigit(*buf)) { char* s = index(options, *buf); i = s ? (s - options) : ngn; if (i >= 0 && i < ngn) { char* cp = index(ngptrs[i], ' '); if (cp) *cp = '\0'; set_ngname(ngptrs[i]); if (cp) *cp = ' '; return 1; } } fputs(hforhelp, stdout) FLUSH; break; } settle_down(); goto reask; } #endif /* EDIT_DISTANCE */ trn-4.0-test77/datasrc.h0000644000000000000000000000644007113133015013601 0ustar rootroot/* datasrc.h */ /* This software is copyrighted as detailed in the LICENSE file. */ struct srcfile { FILE* fp; /* the file pointer to read the data */ HASHTABLE* hp; /* the hash table for the data */ LIST* lp; /* the list used to store the data */ long recent_cnt; /* # lines/bytes this file might be */ #ifdef SUPPORT_NNTP time_t lastfetch; /* when the data was last fetched */ time_t refetch_secs; /* how long before we refetch this file */ #endif }; struct datasrc { char* name; /* our user-friendly name */ char* newsid; /* the active file name or host name */ SRCFILE act_sf; /* the active file's hashed contents */ char* grpdesc; /* the newsgroup description file or tmp */ SRCFILE desc_sf; /* the group description's hashed contents */ char* extra_name; /* local active.times or server's actfile */ #ifdef SUPPORT_NNTP NNTPLINK nntplink; #endif char* spool_dir; char* over_dir; char* over_fmt; char* thread_dir; char* auth_user; char* auth_pass; #ifdef USE_GENAUTH char* auth_command; #endif long lastnewgrp; /* time of last newgroup check */ FILE* ov_in; /* the overview's file handle */ time_t ov_opened; /* time overview file was opened */ Uchar fieldnum[OV_MAX_FIELDS]; Uchar fieldflags[OV_MAX_FIELDS]; int flags; }; #define DF_TRY_OVERVIEW 0x0001 #define DF_TRY_THREAD 0x0002 #define DF_ADD_OK 0x0004 #define DF_DEFAULT 0x0008 #define DF_OPEN 0x0010 #define DF_ACTIVE 0x0020 #define DF_UNAVAILABLE 0x0040 #ifdef SUPPORT_NNTP #define DF_REMOTE 0x0080 #define DF_TMPACTFILE 0x0100 #define DF_TMPGRPDESC 0x0200 #define DF_USELISTACT 0x0400 #define DF_XHDR_BROKEN 0x0800 #define DF_NOXGTITLE 0x1000 #define DF_NOLISTGROUP 0x2000 #define DF_NOXREFS 0x4000 #endif #define FF_HAS_FIELD 0x01 #define FF_CHECK4FIELD 0x02 #define FF_HAS_HDR 0x04 #define FF_CHECK4HDR 0x08 #define FF_FILTERSEND 0x10 #define DATASRC_NNTP_FLAGS(dp) (((dp) == datasrc? nntplink.flags : (dp)->nntplink.flags)) EXT LIST* datasrc_list; /* a list of all DATASRCs */ EXT DATASRC* datasrc; /* the current datasrc */ EXT int datasrc_cnt INIT(0); #define datasrc_ptr(n) ((DATASRC*)listnum2listitem(datasrc_list,(long)(n))) #define datasrc_first() ((DATASRC*)listnum2listitem(datasrc_list,0L)) #define datasrc_next(p) ((DATASRC*)next_listitem(datasrc_list,(char*)(p))) #define LENGTH_HACK 5 /* Don't bother comparing strings with lengths * that differ by more than this. */ #define MAX_NG 9 /* Maximum number of groups to offer. */ #define DATASRC_ALARM_SECS (5 * 60) EXT char* trnaccess_mem INIT(NULL); #ifdef SUPPORT_NNTP EXT char* nntp_auth_file; #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void datasrc_init _((void)); char* read_datasrcs _((char*)); DATASRC* get_datasrc _((char*)); DATASRC* new_datasrc _((char*,char**)); bool open_datasrc _((DATASRC*)); void set_datasrc _((DATASRC*)); void check_datasrcs _((void)); void close_datasrc _((DATASRC*)); bool actfile_hash _((DATASRC*)); bool find_actgrp _((DATASRC*,char*,char*,int,ART_NUM)); char* find_grpdesc _((DATASRC*,char*)); int srcfile_open _((SRCFILE*,char*,char*,char*)); #ifdef SUPPORT_NNTP char* srcfile_append _((SRCFILE*,char*,int)); void srcfile_end_append _((SRCFILE*,char*)); #endif void srcfile_close _((SRCFILE*)); int find_close_match _((void)); trn-4.0-test77/datasrc.ih0000644000000000000000000000275007113133015013752 0ustar rootroot/* datasrc.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifdef SUPPORT_NNTP # ifdef I_UTIME # include # else struct utimbuf { time_t actime; time_t modtime; }; # endif #endif #define SRCFILE_CHUNK_SIZE (32*1024) #define DI_NNTP_SERVER 1 #define DI_ACTIVE_FILE 2 #define DI_ACT_REFETCH 3 #define DI_SPOOL_DIR 4 #define DI_THREAD_DIR 5 #define DI_OVERVIEW_DIR 6 #define DI_ACTIVE_TIMES 7 #define DI_GROUP_DESC 8 #define DI_DESC_REFETCH 9 #define DI_AUTH_USER 10 #define DI_AUTH_PASS 11 #define DI_AUTH_COMMAND 12 #define DI_XHDR_BROKEN 13 #define DI_XREFS 14 #define DI_OVERVIEW_FMT 15 #define DI_FORCE_AUTH 16 INI_WORDS datasrc_ini[] = { { 0, "DATASRC", 0 }, { 0, "NNTP Server", 0 }, { 0, "Active File", 0 }, { 0, "Active File Refetch", 0 }, { 0, "Spool Dir", 0 }, { 0, "Thread Dir", 0 }, { 0, "Overview Dir", 0 }, { 0, "Active Times", 0 }, { 0, "Group Desc", 0 }, { 0, "Group Desc Refetch", 0 }, { 0, "Auth User", 0 }, { 0, "Auth Password", 0 }, { 0, "Auth Command", 0 }, { 0, "XHDR Broken", 0 }, { 0, "Xrefs", 0 }, { 0, "Overview Format File", 0 }, { 0, "Force Auth", 0 }, { 0, 0, 0 } }; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static char* dir_or_none _((DATASRC*,char*,int)); static char* file_or_none _((char*)); static int srcfile_cmp _((char*,int,HASHDATUM)); static int check_distance _((int,HASHDATUM*,int)); static int get_near_miss _((void)); trn-4.0-test77/decode.c0000644000000000000000000002264607207621030013407 0ustar rootroot/* decode.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "hash.h" #include "cache.h" #include "head.h" #include "art.h" #include "artio.h" #include "artstate.h" #include "intrp.h" #include "final.h" #include "respond.h" #include "mime.h" #include "env.h" #include "util.h" #include "util2.h" #include "uudecode.h" #include "INTERN.h" #include "decode.h" #include "decode.ih" void decode_init() { } char* decode_fix_fname(s) char* s; { char* t; #ifdef MSDOS int dotcount = 0; #endif if (!s) s = "unknown"; safefree(decode_filename); decode_filename = safemalloc(strlen(s) + 2); /*$$ we need to eliminate any "../"s from the string */ while (*s == '/' || *s == '~') s++; for (t = decode_filename; *s; s++) { #ifdef MSDOS /*$$ we should also handle backslashes here */ if (*s == '.' && (t == decode_filename || dotcount++)) continue; #endif if (isprint(*s) #ifdef GOODCHARS && index(GOODCHARS, *s) #else && !index(BADCHARS, *s) #endif ) *t++ = *s; } *t = '\0'; if (t == decode_filename || bad_filename(decode_filename)) { *t++ = 'x'; *t = '\0'; } return decode_filename; } /* Returns nonzero if "filename" is a bad choice */ static bool bad_filename(filename) char* filename; { int len = strlen(filename); #ifdef MSDOS if (len == 3) { if (strcaseEQ(filename, "aux") || strcaseEQ(filename, "con") || strcaseEQ(filename, "nul") || strcaseEQ(filename, "prn")) return TRUE; } else if (len == 4) { if (strcaseEQ(filename, "com1") || strcaseEQ(filename, "com2") || strcaseEQ(filename, "com3") || strcaseEQ(filename, "com4") || strcaseEQ(filename, "lpt1") || strcaseEQ(filename, "lpt2") || strcaseEQ(filename, "lpt3")) return TRUE; } #else if (len <= 2) { if (*filename == '.' && (*filename == '\0' || *filename == '.')) return TRUE; } #endif return 0; } /* Parse the subject looking for filename and part number information. */ char* decode_subject(artnum, partp, totalp) ART_NUM artnum; int* partp; int* totalp; { static char* subject = NULL; char* filename; char* s; char* t; char* end; int part = -1, total = 0, hasdot = 0; *partp = part; *totalp = total; safefree(subject); subject = fetchsubj(artnum,TRUE); if (!*subject) return NULL; /* Skip leading whitespace and other garbage */ s = subject; while (*s == ' ' || *s == '\t' || *s == '-') s++; if (strncaseEQ(s, "repost", 6)) { for (s += 6; *s == ' ' || *s == '\t' || *s == ':' || *s == '-'; s++); } while (strncaseEQ(s, "re:", 3)) { s += 3; while (isspace(*s)) s++; } /* Get filename */ /* Grab the first filename-like string. Explicitly ignore strings with * prefix "v" ending in ":", since that is a popular volume/issue * representation syntax */ end = s + strlen(s); do { while (*s && !isalnum(*s) && *s != '_') s++; filename = t = s; while (isalnum(*s) || *s == '-' || *s == '+' || *s == '&' || *s == '_' || *s == '.') { if (*s++ == '.') hasdot = 1; } if (!*s || *s == '\n') return NULL; } while (t == s || (t[0] == 'v' && isdigit(t[1]) && *s == ':')); *s++ = '\0'; /* Try looking for a filename with a "." in it later in the subject line. * Exclude ., since that is usually a version number. */ if (!hasdot) { while (*(t = s) != '\0' && *s != '\n') { while (isspace(*t)) t++; for (s = t; isalnum(*s) || *s == '-' || *s == '+' || *s == '&' || *s == '_' || *s == '.'; s++) { if (*s == '.' && (!isdigit(s[-1]) || !isdigit(s[1]))) { hasdot = 1; } } if (hasdot && s > t) { filename = t; *s++ = '\0'; break; } while (*s && *s != '\n' && !isalnum(*s)) s++; } s = filename + strlen(filename) + 1; } if (s >= end) return NULL; /* Get part number */ while (*s && *s != '\n') { /* skip over versioning */ if (*s == 'v' && isdigit(s[1])) { s++; while (isdigit(*s)) s++; } /* look for "1/6" or "1 / 6" or "1 of 6" or "1-of-6" or "1o6" */ if (isdigit(*s) && (s[1] == '/' || (s[1] == ' ' && s[2] == '/') || (s[1] == ' ' && s[2] == 'o' && s[3] == 'f') || (s[1] == '-' && s[2] == 'o' && s[3] == 'f') || (s[1] == 'o' && isdigit(s[2])))) { for (t = s; isdigit(t[-1]); t--) ; part = atoi(t); while (*++s != '\0' && *s != '\n' && !isdigit(*s)) ; total = isdigit(*s)? atoi(s) : 0; while (isdigit(*s)) s++; /* We don't break here because we want the last item on the line */ } /* look for "6 parts" or "part 1" */ if (strncaseEQ("part", s, 4)) { if (s[4] == 's') { for (t = s; t >= subject && !isdigit(*t); t--); if (t > subject) { while (t > subject && isdigit(t[-1])) t--; total = atoi(t); } } else { while (*s && *s != '\n' && !isdigit(*s)) s++; if (isdigit(*s)) part = atoi(s); s--; } } if (*s) s++; } if (total == 0 || part == -1 || part > total) return NULL; *partp = part; *totalp = total; return filename; } /* * Handle a piece of a split file. */ int decode_piece(mcp, first_line) MIMECAP_ENTRY* mcp; char* first_line; { char* dir; char* filename; FILE* fp; int part, total, state; DECODE_FUNC decoder; filename = decode_fix_fname(mime_section->filename); part = mime_section->part; total = mime_section->total; *msg = '\0'; if (!total && is_mime) total = part = 1; if (mcp || total != 1 || part != 1) { /* Create directory to store parts and copy this part there. */ dir = decode_mkdir(filename); if (!dir) { strcpy(msg, "Failed."); return 0; } } else dir = NULL; if (mcp) { if (chdir(dir)) { printf(nocd,dir) FLUSH; sig_catcher(0); } } if (total != 1 || part != 1) { sprintf(buf, "Saving part %d ", part); if (total) sprintf(buf+strlen(buf), "of %d ", total); strcat(buf, filename); fputs(buf,stdout); if (nowait_fork) fflush(stdout); else newline(); sprintf(buf, "%s%d", dir, part); fp = fopen(buf, "w"); if (!fp) { strcpy(msg,"Failed."); /*$$*/ return 0; } while (readart(art_line,sizeof art_line)) { if (mime_EndOfSection(art_line)) break; fputs(art_line,fp); if (total == 0 && *art_line == 'e' && art_line[1] == 'n' && art_line[2] == 'd' && isspace(art_line[3])) { /* This is the last part. Remember the fact */ total = part; sprintf(buf, "%sCT", dir); tmpfp = fopen(buf, "w"); if (!tmpfp) /*os_perror(buf)*/; else { fprintf(tmpfp, "%d\n", total); fclose(tmpfp); } } } fclose(fp); /* Retrieve any previously saved number of the last part */ if (total == 0) { sprintf(buf, "%sCT", dir); if ((fp = fopen(buf, "r")) != NULL) { if (fgets(buf, sizeof buf, fp)) { total = atoi(buf); if (total < 0) total = 0; } fclose(fp); } if (total == 0) return 1; } /* Check to see if we have all parts. Start from the highest numbers * as we are more likely not to have them. */ for (part = total; part; part--) { sprintf(buf, "%s%d", dir, part); fp = fopen(buf, "r"); if (!fp) return 1; if (part != 1) fclose(fp); } } else { fp = NULL; total = 1; } if (mime_section->type == MESSAGE_MIME) { mime_PushSection(); mime_ParseSubheader(fp,first_line); first_line = NULL; } mime_getc_line = first_line; decoder = decode_function(mime_section->encoding); if (!decoder) { strcpy(msg,"Unhandled encoding type -- aborting."); if (fp) fclose(fp); if (dir) decode_rmdir(dir); return 0; } /* Handle each part in order */ for (state = DECODE_START, part = 1; part <= total; part++) { if (part != 1) { sprintf(buf, "%s%d", dir, part); fp = fopen(buf, "r"); if (!fp) { /*os_perror(buf);*/ return 1; } } state = decoder(fp, state); if (fp) fclose(fp); if (state == DECODE_ERROR) { strcpy(msg,"Failed."); /*$$*/ return 0; } } if (state != DECODE_DONE) { (void) decoder((FILE*)NULL, DECODE_DONE); if (state != DECODE_MAYBEDONE) { strcpy(msg,"Premature EOF."); return 0; } } if (fp) { /* Cleanup all the pieces */ for (part = 0; part <= total; part++) { sprintf(buf, "%s%d", dir, part); UNLINK(buf); } sprintf(buf, "%sCT", dir); UNLINK(buf); } if (mcp) { mime_Exec(mcp->command); UNLINK(decode_filename); chdir(".."); } if (dir) decode_rmdir(dir); return 1; } DECODE_FUNC decode_function(encoding) int encoding; { switch (encoding) { case MENCODE_QPRINT: return qp_decode; case MENCODE_BASE64: return b64_decode; case MENCODE_UUE: return uudecode; case MENCODE_NONE: return cat_decode; default: return NULL; } } /* return a directory to use for unpacking the pieces of a given filename */ char* decode_mkdir(filename) char* filename; { static char dir[LBUFLEN]; char* s; #ifdef MSDOS interp(dir, sizeof dir, "%Y/parts/"); #else interp(dir, sizeof dir, "%Y/m-prts-%L/"); #endif strcat(dir, filename); s = dir + strlen(dir); if (s[-1] == '/') return NULL; *s++ = '/'; *s = '\0'; if (makedir(dir, MD_FILE) != 0) return NULL; return dir; } void decode_rmdir(dir) char* dir; { char* s; /* Remove trailing slash */ s = dir + strlen(dir) - 1; *s = '\0'; /*$$ conditionalize this */ rmdir(dir); } trn-4.0-test77/decode.h0000644000000000000000000000157607113133015013410 0ustar rootroot/* decode.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* decode_filename INIT(NULL); #define DECODE_DONE 0 #define DECODE_START 1 #define DECODE_INACTIVE 2 #define DECODE_SETLEN 3 #define DECODE_ACTIVE 4 #define DECODE_NEXT2LAST 5 #define DECODE_LAST 6 #define DECODE_MAYBEDONE 7 #define DECODE_ERROR 8 #ifdef MSDOS #define GOODCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \ "0123456789-_^#%" #else #define BADCHARS "!$&*()|\'\";<>[]{}?/`\\ \t" #endif typedef int (*DECODE_FUNC) _((FILE*,int)); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void decode_init _((void)); char* decode_fix_fname _((char*)); char* decode_subject _((ART_NUM,int*,int*)); int decode_piece _((MIMECAP_ENTRY*,char*)); DECODE_FUNC decode_function _((int)); char* decode_mkdir _((char*)); void decode_rmdir _((char*)); trn-4.0-test77/decode.ih0000644000000000000000000000027307113133015013552 0ustar rootroot/* decode.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static bool bad_filename _((char*)); trn-4.0-test77/default_topic0000644000000000000000000000350607113133015014554 0ustar rootroot#### default_topic "top" topic file ## To install this file as the default topic file, do the following: # # 1. Read the comments in the topic file below, and change entries as # appropriate. # # 2. Move this file ("default_topic") to the trn library directory # (the same directory which contains the INIT and newsnews files). # This will be done by "make install.full" or "make install.strn" # if the library directory does not have a default_topic file. # ## Users can override the default hierarchy by setting SCANGROUPS ## to point elsewhere, or by creating a ~/News/scangroups/top file. ## The easiest way to do this is to enter group scan mode, and select ## "Create a personalized top file for group scan mode" from the menu. ## Users can then add items to their own "top" file. ###Start of the default topic file ## Probably a good idea to start with this newsgroup--the user can always ## unsubscribe if s/he wishes. news.announce.newusers # Include an easy way to get to all the newsgroups "All newsgroups" * #A neat way to introduce the net to newbies... "Newsgroups by hierarchies" %X/hier_groups ## Use a line like the following to make local newsgroups more obvious. #"Widget Factory local news" widget.* # Change the following line to point to any local hierarchy structures. #"Default Hierarchy" /foo/bar/lib/strn/top ## If users write useful hierarchies, they can be shared by adding links ## to them. For instance, the following would link to some of Mary's topics: #"Mary's computer hierarchy" ~mary/News/scangroups/comp/main ## Any other shared topics/hierarchies can be added here. ##This entry will point to the user's hotlist.strn file. "Personal Hotlist" @%p/hotlist.strn #Uncomment the following for WWW stuff (not fully supported yet). #"Strn WWW home page." URL:http://www.digex.net/~caadams/strn/strn.top trn-4.0-test77/dependencies0000644000000000000000000011552007116412635014373 0ustar rootrootaddng.$(o): addng.c addng.$(o): addng.h addng.$(o): addng.ih addng.$(o): autosub.h addng.$(o): common.h addng.$(o): config2.h addng.$(o): config.h addng.$(o): datasrc.h addng.$(o): EXTERN.h addng.$(o): final.h addng.$(o): hash.h addng.$(o): INTERN.h addng.$(o): intrp.h addng.$(o): last.h addng.$(o): list.h addng.$(o): ngdata.h addng.$(o): nntpclient.h addng.$(o): nntp.h addng.$(o): only.h addng.$(o): rcstuff.h addng.$(o): rt-select.h addng.$(o): search.h addng.$(o): term.h addng.$(o): trn.h addng.$(o): util2.h addng.$(o): util.h art.$(o): addng.h art.$(o): art.c art.$(o): art.h art.$(o): artio.h art.$(o): artstate.h art.$(o): backpage.h art.$(o): bits.h art.$(o): cache.h art.$(o): charsubst.h art.$(o): color.h art.$(o): common.h art.$(o): config2.h art.$(o): config.h art.$(o): datasrc.h art.$(o): decode.h art.$(o): env.h art.$(o): EXTERN.h art.$(o): final.h art.$(o): hash.h art.$(o): head.h art.$(o): help.h art.$(o): INTERN.h art.$(o): intrp.h art.$(o): kfile.h art.$(o): list.h art.$(o): mime.h art.$(o): ngdata.h art.$(o): ng.h art.$(o): ngstuff.h art.$(o): nntpclient.h art.$(o): nntp.h art.$(o): rcstuff.h art.$(o): rthread.h art.$(o): rt-select.h art.$(o): rt-util.h art.$(o): rt-wumpus.h art.$(o): scanart.h art.$(o): score.h art.$(o): search.h art.$(o): sw.h art.$(o): term.h art.$(o): tkstuff.h art.$(o): tktree.h art.$(o): trn.h art.$(o): util2.h art.$(o): util.h artio.$(o): art.h artio.$(o): artio.c artio.$(o): artio.h artio.$(o): artstate.h artio.$(o): bits.h artio.$(o): cache.h artio.$(o): color.h artio.$(o): common.h artio.$(o): config2.h artio.$(o): config.h artio.$(o): datasrc.h artio.$(o): decode.h artio.$(o): EXTERN.h artio.$(o): final.h artio.$(o): hash.h artio.$(o): head.h artio.$(o): INTERN.h artio.$(o): list.h artio.$(o): mime.h artio.$(o): ngdata.h artio.$(o): ng.h artio.$(o): nntpclient.h artio.$(o): nntp.h artio.$(o): rthread.h artio.$(o): search.h artio.$(o): term.h artio.$(o): util2.h artio.$(o): util.h artsrch.$(o): addng.h artsrch.$(o): artio.h artsrch.$(o): artsrch.c artsrch.$(o): artsrch.h artsrch.$(o): bits.h artsrch.$(o): cache.h artsrch.$(o): common.h artsrch.$(o): config2.h artsrch.$(o): config.h artsrch.$(o): datasrc.h artsrch.$(o): EXTERN.h artsrch.$(o): final.h artsrch.$(o): hash.h artsrch.$(o): head.h artsrch.$(o): INTERN.h artsrch.$(o): intrp.h artsrch.$(o): kfile.h artsrch.$(o): list.h artsrch.$(o): ngdata.h artsrch.$(o): ng.h artsrch.$(o): ngstuff.h artsrch.$(o): nntpclient.h artsrch.$(o): nntp.h artsrch.$(o): rthread.h artsrch.$(o): rt-select.h artsrch.$(o): rt-util.h artsrch.$(o): search.h artsrch.$(o): term.h artsrch.$(o): util2.h artsrch.$(o): util.h autosub.$(o): autosub.c autosub.$(o): autosub.h autosub.$(o): common.h autosub.$(o): config2.h autosub.$(o): config.h autosub.$(o): env.h autosub.$(o): EXTERN.h autosub.$(o): final.h autosub.$(o): INTERN.h autosub.$(o): list.h autosub.$(o): ngdata.h autosub.$(o): ngsrch.h autosub.$(o): search.h autosub.$(o): util2.h autosub.$(o): util.h backpage.$(o): backpage.c backpage.$(o): backpage.h backpage.$(o): common.h backpage.$(o): config2.h backpage.$(o): config.h backpage.$(o): EXTERN.h backpage.$(o): final.h backpage.$(o): INTERN.h backpage.$(o): intrp.h backpage.$(o): util2.h bits.$(o): artio.h bits.$(o): bits.c bits.$(o): bits.h bits.$(o): bits.ih bits.$(o): cache.h bits.$(o): common.h bits.$(o): config2.h bits.$(o): config.h bits.$(o): datasrc.h bits.$(o): EXTERN.h bits.$(o): final.h bits.$(o): hash.h bits.$(o): head.h bits.$(o): INTERN.h bits.$(o): intrp.h bits.$(o): kfile.h bits.$(o): list.h bits.$(o): ndir.h bits.$(o): ngdata.h bits.$(o): ng.h bits.$(o): nntpclient.h bits.$(o): nntp.h bits.$(o): rcln.h bits.$(o): rcstuff.h bits.$(o): rthread.h bits.$(o): rt-process.h bits.$(o): rt-select.h bits.$(o): rt-util.h bits.$(o): term.h bits.$(o): trn.h bits.$(o): util2.h bits.$(o): util.h cache.$(o): artsrch.h cache.$(o): bits.h cache.$(o): cache.c cache.$(o): cache.h cache.$(o): cache.ih cache.$(o): common.h cache.$(o): config2.h cache.$(o): config.h cache.$(o): datasrc.h cache.$(o): env.h cache.$(o): EXTERN.h cache.$(o): final.h cache.$(o): hash.h cache.$(o): head.h cache.$(o): INTERN.h cache.$(o): intrp.h cache.$(o): kfile.h cache.$(o): list.h cache.$(o): mime.h cache.$(o): ngdata.h cache.$(o): ng.h cache.$(o): nntpclient.h cache.$(o): nntp.h cache.$(o): rcstuff.h cache.$(o): rthread.h cache.$(o): rt-ov.h cache.$(o): rt-page.h cache.$(o): rt-process.h cache.$(o): rt-select.h cache.$(o): rt-util.h cache.$(o): score.h cache.$(o): search.h cache.$(o): term.h cache.$(o): trn.h cache.$(o): util2.h cache.$(o): util.h charsubst.$(o): artstate.h charsubst.$(o): charsubst.c charsubst.$(o): charsubst.h charsubst.$(o): charsubst.ih charsubst.$(o): common.h charsubst.$(o): config2.h charsubst.$(o): config.h charsubst.$(o): EXTERN.h charsubst.$(o): INTERN.h charsubst.$(o): search.h charsubst.$(o): util2.h color.$(o): color.c color.$(o): color.h color.$(o): color.ih color.$(o): common.h color.$(o): config2.h color.$(o): config.h color.$(o): EXTERN.h color.$(o): final.h color.$(o): INTERN.h color.$(o): term.h color.$(o): util.h datasrc.$(o): cache.h datasrc.$(o): common.h datasrc.$(o): config2.h datasrc.$(o): config.h datasrc.$(o): datasrc.c datasrc.$(o): datasrc.h datasrc.$(o): datasrc.ih datasrc.$(o): edit_dist.h datasrc.$(o): env.h datasrc.$(o): EXTERN.h datasrc.$(o): hash.h datasrc.$(o): init.h datasrc.$(o): INTERN.h datasrc.$(o): intrp.h datasrc.$(o): last.h datasrc.$(o): list.h datasrc.$(o): ngdata.h datasrc.$(o): nntpclient.h datasrc.$(o): nntp.h datasrc.$(o): opt.h datasrc.$(o): rcstuff.h datasrc.$(o): rt-mt.h datasrc.$(o): rt-ov.h datasrc.$(o): rt-util.h datasrc.$(o): term.h datasrc.$(o): trn.h datasrc.$(o): util2.h datasrc.$(o): util.h decode.$(o): art.h decode.$(o): artio.h decode.$(o): artstate.h decode.$(o): cache.h decode.$(o): common.h decode.$(o): config2.h decode.$(o): config.h decode.$(o): decode.c decode.$(o): decode.h decode.$(o): decode.ih decode.$(o): env.h decode.$(o): EXTERN.h decode.$(o): final.h decode.$(o): hash.h decode.$(o): head.h decode.$(o): INTERN.h decode.$(o): intrp.h decode.$(o): mime.h decode.$(o): respond.h decode.$(o): term.h decode.$(o): util2.h decode.$(o): util.h decode.$(o): uudecode.h edit_dist.$(o): common.h edit_dist.$(o): config2.h edit_dist.$(o): config.h edit_dist.$(o): edit_dist.c edit_dist.$(o): edit_dist.h edit_dist.$(o): EXTERN.h edit_dist.$(o): INTERN.h edit_dist.$(o): util.h env.$(o): common.h env.$(o): config2.h env.$(o): config.h env.$(o): env.c env.$(o): env.h env.$(o): env.ih env.$(o): EXTERN.h env.$(o): final.h env.$(o): init.h env.$(o): INTERN.h env.$(o): util2.h env.$(o): util.h filter.$(o): cache.h filter.$(o): common.h filter.$(o): config2.h filter.$(o): config.h filter.$(o): datasrc.h filter.$(o): env.h filter.$(o): EXTERN.h filter.$(o): filter.c filter.$(o): filter.h filter.$(o): filter.ih filter.$(o): final.h filter.$(o): hash.h filter.$(o): INTERN.h filter.$(o): list.h filter.$(o): ngdata.h filter.$(o): nntpclient.h filter.$(o): rt-ov.h filter.$(o): trn.h filter.$(o): util2.h final.$(o): artio.h final.$(o): bits.h final.$(o): cache.h final.$(o): color.h final.$(o): common.h final.$(o): config2.h final.$(o): config.h final.$(o): datasrc.h final.$(o): env.h final.$(o): EXTERN.h final.$(o): filter.h final.$(o): final.c final.$(o): final.h final.$(o): hash.h final.$(o): init.h final.$(o): INTERN.h final.$(o): intrp.h final.$(o): kfile.h final.$(o): last.h final.$(o): list.h final.$(o): ngdata.h final.$(o): ng.h final.$(o): nntpclient.h final.$(o): nntp.h final.$(o): nntpinit.h final.$(o): rcstuff.h final.$(o): score.h final.$(o): scoresave.h final.$(o): term.h final.$(o): tkstuff.h final.$(o): util.h hash.$(o): common.h hash.$(o): config2.h hash.$(o): config.h hash.$(o): EXTERN.h hash.$(o): final.h hash.$(o): hash.c hash.$(o): hash.h hash.$(o): hash.ih hash.$(o): INTERN.h hash.$(o): util.h head.$(o): artio.h head.$(o): cache.h head.$(o): common.h head.$(o): config2.h head.$(o): config.h head.$(o): datasrc.h head.$(o): EXTERN.h head.$(o): final.h head.$(o): hash.h head.$(o): head.c head.$(o): head.h head.$(o): INTERN.h head.$(o): list.h head.$(o): mempool.h head.$(o): ngdata.h head.$(o): ng.h head.$(o): nntpclient.h head.$(o): nntp.h head.$(o): nntpinit.h head.$(o): parsedate.h head.$(o): rthread.h head.$(o): rt-process.h head.$(o): rt-util.h head.$(o): util2.h head.$(o): util.h help.$(o): common.h help.$(o): config2.h help.$(o): config.h help.$(o): EXTERN.h help.$(o): help.c help.$(o): help.h help.$(o): INTERN.h help.$(o): list.h help.$(o): ngdata.h help.$(o): term.h inews.$(o): common.h inews.$(o): config2.h inews.$(o): config.h inews.$(o): env.h inews.$(o): EXTERN.h inews.$(o): inews.c inews.$(o): init.h inews.$(o): nntpclient.h inews.$(o): nntpinit.h inews.$(o): util2.h inews.$(o): util3.h init.$(o): addng.h init.$(o): art.h init.$(o): artio.h init.$(o): artsrch.h init.$(o): backpage.h init.$(o): bits.h init.$(o): cache.h init.$(o): color.h init.$(o): common.h init.$(o): config2.h init.$(o): config.h init.$(o): datasrc.h init.$(o): decode.h init.$(o): env.h init.$(o): EXTERN.h init.$(o): filter.h init.$(o): final.h init.$(o): hash.h init.$(o): head.h init.$(o): help.h init.$(o): init.c init.$(o): init.h init.$(o): INTERN.h init.$(o): intrp.h init.$(o): kfile.h init.$(o): last.h init.$(o): list.h init.$(o): mempool.h init.$(o): mime.h init.$(o): ngdata.h init.$(o): ng.h init.$(o): ngsrch.h init.$(o): ngstuff.h init.$(o): nntpclient.h init.$(o): nntp.h init.$(o): nntpinit.h init.$(o): only.h init.$(o): opt.h init.$(o): rcln.h init.$(o): rcstuff.h init.$(o): respond.h init.$(o): rthread.h init.$(o): scan.h init.$(o): score.h init.$(o): search.h init.$(o): sw.h init.$(o): term.h init.$(o): tkstuff.h init.$(o): trn.h init.$(o): univ.h init.$(o): util2.h init.$(o): util.h intrp.$(o): artio.h intrp.$(o): artsrch.h intrp.$(o): bits.h intrp.$(o): cache.h intrp.$(o): common.h intrp.$(o): config2.h intrp.$(o): config.h intrp.$(o): datasrc.h intrp.$(o): env.h intrp.$(o): EXTERN.h intrp.$(o): final.h intrp.$(o): hash.h intrp.$(o): head.h intrp.$(o): init.h intrp.$(o): INTERN.h intrp.$(o): intrp.c intrp.$(o): intrp.h intrp.$(o): intrp.ih intrp.$(o): list.h intrp.$(o): ngdata.h intrp.$(o): ng.h intrp.$(o): nntpclient.h intrp.$(o): nntp.h intrp.$(o): rcstuff.h intrp.$(o): respond.h intrp.$(o): rthread.h intrp.$(o): rt-select.h intrp.$(o): rt-util.h intrp.$(o): search.h intrp.$(o): term.h intrp.$(o): trn.h intrp.$(o): util2.h intrp.$(o): util.h kfile.$(o): addng.h kfile.$(o): artsrch.h kfile.$(o): bits.h kfile.$(o): cache.h kfile.$(o): color.h kfile.$(o): common.h kfile.$(o): config2.h kfile.$(o): config.h kfile.$(o): datasrc.h kfile.$(o): env.h kfile.$(o): EXTERN.h kfile.$(o): hash.h kfile.$(o): INTERN.h kfile.$(o): intrp.h kfile.$(o): kfile.c kfile.$(o): kfile.h kfile.$(o): kfile.ih kfile.$(o): list.h kfile.$(o): ngdata.h kfile.$(o): ng.h kfile.$(o): ngstuff.h kfile.$(o): nntpclient.h kfile.$(o): nntp.h kfile.$(o): rcstuff.h kfile.$(o): rthread.h kfile.$(o): rt-process.h kfile.$(o): rt-select.h kfile.$(o): rt-util.h kfile.$(o): search.h kfile.$(o): term.h kfile.$(o): trn.h kfile.$(o): util2.h kfile.$(o): util.h last.$(o): common.h last.$(o): config2.h last.$(o): config.h last.$(o): EXTERN.h last.$(o): init.h last.$(o): INTERN.h last.$(o): intrp.h last.$(o): last.c last.$(o): last.h last.$(o): list.h last.$(o): trn.h last.$(o): util2.h last.$(o): util.h list.$(o): common.h list.$(o): config2.h list.$(o): config.h list.$(o): EXTERN.h list.$(o): INTERN.h list.$(o): list.c list.$(o): list.h list.$(o): list.ih list.$(o): util.h mempool.$(o): common.h mempool.$(o): config2.h mempool.$(o): config.h mempool.$(o): EXTERN.h mempool.$(o): final.h mempool.$(o): INTERN.h mempool.$(o): mempool.c mempool.$(o): mempool.h mempool.$(o): mempool.ih mempool.$(o): util2.h mempool.$(o): util.h mime.$(o): art.h mime.$(o): artio.h mime.$(o): artstate.h mime.$(o): backpage.h mime.$(o): cache.h mime.$(o): charsubst.h mime.$(o): color.h mime.$(o): common.h mime.$(o): config2.h mime.$(o): config.h mime.$(o): decode.h mime.$(o): env.h mime.$(o): EXTERN.h mime.$(o): hash.h mime.$(o): head.h mime.$(o): INTERN.h mime.$(o): list.h mime.$(o): mime.c mime.$(o): mime.h mime.$(o): mime.ih mime.$(o): ng.h mime.$(o): respond.h mime.$(o): search.h mime.$(o): term.h mime.$(o): util2.h mime.$(o): util.h ndir.$(o): common.h ndir.$(o): config2.h ndir.$(o): config.h ndir.$(o): EXTERN.h ndir.$(o): INTERN.h ndir.$(o): ndir.c ndir.$(o): ndir.h ng.$(o): addng.h ng.$(o): art.h ng.$(o): artio.h ng.$(o): artsrch.h ng.$(o): artstate.h ng.$(o): backpage.h ng.$(o): bits.h ng.$(o): cache.h ng.$(o): charsubst.h ng.$(o): color.h ng.$(o): common.h ng.$(o): config2.h ng.$(o): config.h ng.$(o): datasrc.h ng.$(o): decode.h ng.$(o): env.h ng.$(o): EXTERN.h ng.$(o): filter.h ng.$(o): final.h ng.$(o): hash.h ng.$(o): head.h ng.$(o): help.h ng.$(o): INTERN.h ng.$(o): intrp.h ng.$(o): kfile.h ng.$(o): last.h ng.$(o): list.h ng.$(o): mime.h ng.$(o): ng.c ng.$(o): ngdata.h ng.$(o): ng.h ng.$(o): ng.ih ng.$(o): ngstuff.h ng.$(o): nntpclient.h ng.$(o): nntp.h ng.$(o): rcln.h ng.$(o): rcstuff.h ng.$(o): respond.h ng.$(o): rthread.h ng.$(o): rt-select.h ng.$(o): rt-util.h ng.$(o): rt-wumpus.h ng.$(o): scanart.h ng.$(o): scan.h ng.$(o): score.h ng.$(o): search.h ng.$(o): smisc.h ng.$(o): sw.h ng.$(o): term.h ng.$(o): tkstuff.h ng.$(o): trn.h ng.$(o): univ.h ng.$(o): util2.h ng.$(o): util.h ngdata.$(o): bits.h ngdata.$(o): cache.h ngdata.$(o): common.h ngdata.$(o): config2.h ngdata.$(o): config.h ngdata.$(o): datasrc.h ngdata.$(o): env.h ngdata.$(o): EXTERN.h ngdata.$(o): final.h ngdata.$(o): hash.h ngdata.$(o): head.h ngdata.$(o): INTERN.h ngdata.$(o): intrp.h ngdata.$(o): kfile.h ngdata.$(o): list.h ngdata.$(o): ndir.h ngdata.$(o): ngdata.c ngdata.$(o): ngdata.h ngdata.$(o): ngdata.ih ngdata.$(o): ng.h ngdata.$(o): nntpclient.h ngdata.$(o): nntp.h ngdata.$(o): rcln.h ngdata.$(o): rcstuff.h ngdata.$(o): rthread.h ngdata.$(o): rt-select.h ngdata.$(o): scanart.h ngdata.$(o): scan.h ngdata.$(o): score.h ngdata.$(o): term.h ngdata.$(o): trn.h ngdata.$(o): util2.h ngdata.$(o): util.h ngsrch.$(o): addng.h ngsrch.$(o): cache.h ngsrch.$(o): common.h ngsrch.$(o): config2.h ngsrch.$(o): config.h ngsrch.$(o): datasrc.h ngsrch.$(o): EXTERN.h ngsrch.$(o): final.h ngsrch.$(o): hash.h ngsrch.$(o): INTERN.h ngsrch.$(o): list.h ngsrch.$(o): ngdata.h ngsrch.$(o): ngsrch.c ngsrch.$(o): ngsrch.h ngsrch.$(o): ngstuff.h ngsrch.$(o): nntpclient.h ngsrch.$(o): nntp.h ngsrch.$(o): rcln.h ngsrch.$(o): rcstuff.h ngsrch.$(o): rt-select.h ngsrch.$(o): rt-util.h ngsrch.$(o): search.h ngsrch.$(o): term.h ngsrch.$(o): trn.h ngsrch.$(o): util2.h ngsrch.$(o): util.h ngstuff.$(o): addng.h ngstuff.$(o): bits.h ngstuff.$(o): cache.h ngstuff.$(o): common.h ngstuff.$(o): config2.h ngstuff.$(o): config.h ngstuff.$(o): datasrc.h ngstuff.$(o): decode.h ngstuff.$(o): EXTERN.h ngstuff.$(o): final.h ngstuff.$(o): hash.h ngstuff.$(o): head.h ngstuff.$(o): INTERN.h ngstuff.$(o): intrp.h ngstuff.$(o): kfile.h ngstuff.$(o): list.h ngstuff.$(o): ngdata.h ngstuff.$(o): ng.h ngstuff.$(o): ngstuff.c ngstuff.$(o): ngstuff.h ngstuff.$(o): nntpclient.h ngstuff.$(o): nntp.h ngstuff.$(o): only.h ngstuff.$(o): opt.h ngstuff.$(o): rcln.h ngstuff.$(o): rcstuff.h ngstuff.$(o): respond.h ngstuff.$(o): rthread.h ngstuff.$(o): rt-select.h ngstuff.$(o): rt-util.h ngstuff.$(o): rt-wumpus.h ngstuff.$(o): search.h ngstuff.$(o): sw.h ngstuff.$(o): term.h ngstuff.$(o): trn.h ngstuff.$(o): util2.h ngstuff.$(o): util.h nntpauth.$(o): common.h nntpauth.$(o): config2.h nntpauth.$(o): config.h nntpauth.$(o): env.h nntpauth.$(o): EXTERN.h nntpauth.$(o): final.h nntpauth.$(o): hash.h nntpauth.$(o): INTERN.h nntpauth.$(o): list.h nntpauth.$(o): ngdata.h nntpauth.$(o): nntpauth.c nntpauth.$(o): nntpauth.h nntpauth.$(o): nntpclient.h nntpauth.$(o): nntp.h nntpauth.$(o): util.h nntp.$(o): artio.h nntp.$(o): bits.h nntp.$(o): cache.h nntp.$(o): common.h nntp.$(o): config2.h nntp.$(o): config.h nntp.$(o): datasrc.h nntp.$(o): EXTERN.h nntp.$(o): final.h nntp.$(o): hash.h nntp.$(o): head.h nntp.$(o): init.h nntp.$(o): INTERN.h nntp.$(o): list.h nntp.$(o): ngdata.h nntp.$(o): nntp.c nntp.$(o): nntpclient.h nntp.$(o): nntp.h nntp.$(o): nntp.ih nntp.$(o): rcln.h nntp.$(o): rcstuff.h nntp.$(o): term.h nntp.$(o): trn.h nntp.$(o): util2.h nntp.$(o): util.h nntpclient.$(o): common.h nntpclient.$(o): config2.h nntpclient.$(o): config.h nntpclient.$(o): EXTERN.h nntpclient.$(o): INTERN.h nntpclient.$(o): nntpclient.c nntpclient.$(o): nntpclient.h nntpclient.$(o): nntpinit.h nntpinit.$(o): common.h nntpinit.$(o): config2.h nntpinit.$(o): config.h nntpinit.$(o): EXTERN.h nntpinit.$(o): INTERN.h nntpinit.$(o): nntpclient.h nntpinit.$(o): nntpinit.c nntpinit.$(o): nntpinit.h nntpinit.$(o): nntpinit.ih nntplist.$(o): common.h nntplist.$(o): config2.h nntplist.$(o): config.h nntplist.$(o): env.h nntplist.$(o): EXTERN.h nntplist.$(o): nntpclient.h nntplist.$(o): nntpinit.h nntplist.$(o): nntplist.c nntplist.$(o): util2.h nntplist.$(o): util3.h nntplist.$(o): wildmat.h only.$(o): common.h only.$(o): config2.h only.$(o): config.h only.$(o): EXTERN.h only.$(o): final.h only.$(o): INTERN.h only.$(o): list.h only.$(o): ngdata.h only.$(o): ngsrch.h only.$(o): only.c only.$(o): only.h only.$(o): search.h only.$(o): term.h only.$(o): util2.h only.$(o): util.h opt.$(o): art.h opt.$(o): cache.h opt.$(o): charsubst.h opt.$(o): color.h opt.$(o): common.h opt.$(o): config2.h opt.$(o): config.h opt.$(o): env.h opt.$(o): EXTERN.h opt.$(o): final.h opt.$(o): hash.h opt.$(o): head.h opt.$(o): init.h opt.$(o): INTERN.h opt.$(o): intrp.h opt.$(o): list.h opt.$(o): mime.h opt.$(o): ngdata.h opt.$(o): ng.h opt.$(o): only.h opt.$(o): opt.c opt.$(o): opt.h opt.$(o): opt.ih opt.$(o): rt-page.h opt.$(o): rt-select.h opt.$(o): scanart.h opt.$(o): scan.h opt.$(o): scorefile.h opt.$(o): score.h opt.$(o): search.h opt.$(o): sw.h opt.$(o): term.h opt.$(o): univ.h opt.$(o): util2.h opt.$(o): util.h parsedate.$(o): config.h parsedate.$(o): parsedate.y popen.$(o): common.h popen.$(o): config2.h popen.$(o): config.h popen.$(o): EXTERN.h popen.$(o): popen.c popen.$(o): util2.h popen.$(o): util3.h rcln.$(o): common.h rcln.$(o): config2.h rcln.$(o): config.h rcln.$(o): datasrc.h rcln.$(o): EXTERN.h rcln.$(o): hash.h rcln.$(o): INTERN.h rcln.$(o): list.h rcln.$(o): ngdata.h rcln.$(o): nntpclient.h rcln.$(o): nntp.h rcln.$(o): rcln.c rcln.$(o): rcln.h rcln.$(o): rcstuff.h rcln.$(o): term.h rcln.$(o): util2.h rcln.$(o): util.h rcstuff.$(o): autosub.h rcstuff.$(o): bits.h rcstuff.$(o): cache.h rcstuff.$(o): common.h rcstuff.$(o): config2.h rcstuff.$(o): config.h rcstuff.$(o): datasrc.h rcstuff.$(o): env.h rcstuff.$(o): EXTERN.h rcstuff.$(o): final.h rcstuff.$(o): hash.h rcstuff.$(o): init.h rcstuff.$(o): INTERN.h rcstuff.$(o): intrp.h rcstuff.$(o): last.h rcstuff.$(o): list.h rcstuff.$(o): ngdata.h rcstuff.$(o): nntpclient.h rcstuff.$(o): nntp.h rcstuff.$(o): only.h rcstuff.$(o): rcln.h rcstuff.$(o): rcstuff.c rcstuff.$(o): rcstuff.h rcstuff.$(o): rcstuff.ih rcstuff.$(o): rt-page.h rcstuff.$(o): rt-select.h rcstuff.$(o): search.h rcstuff.$(o): term.h rcstuff.$(o): trn.h rcstuff.$(o): util2.h rcstuff.$(o): util.h respond.$(o): art.h respond.$(o): artio.h respond.$(o): artstate.h respond.$(o): cache.h respond.$(o): charsubst.h respond.$(o): common.h respond.$(o): config2.h respond.$(o): config.h respond.$(o): datasrc.h respond.$(o): decode.h respond.$(o): env.h respond.$(o): EXTERN.h respond.$(o): final.h respond.$(o): hash.h respond.$(o): head.h respond.$(o): INTERN.h respond.$(o): intrp.h respond.$(o): list.h respond.$(o): mime.h respond.$(o): ngdata.h respond.$(o): ng.h respond.$(o): nntpclient.h respond.$(o): nntp.h respond.$(o): respond.c respond.$(o): respond.h respond.$(o): respond.ih respond.$(o): search.h respond.$(o): term.h respond.$(o): trn.h respond.$(o): util2.h respond.$(o): util.h respond.$(o): uudecode.h rthread.$(o): artstate.h rthread.$(o): bits.h rthread.$(o): cache.h rthread.$(o): common.h rthread.$(o): config2.h rthread.$(o): config.h rthread.$(o): datasrc.h rthread.$(o): EXTERN.h rthread.$(o): final.h rthread.$(o): hash.h rthread.$(o): head.h rthread.$(o): INTERN.h rthread.$(o): intrp.h rthread.$(o): kfile.h rthread.$(o): list.h rthread.$(o): ngdata.h rthread.$(o): ng.h rthread.$(o): nntpclient.h rthread.$(o): nntp.h rthread.$(o): rcln.h rthread.$(o): rcstuff.h rthread.$(o): rthread.c rthread.$(o): rthread.h rthread.$(o): rthread.ih rthread.$(o): rt-mt.h rthread.$(o): rt-ov.h rthread.$(o): rt-page.h rthread.$(o): rt-process.h rthread.$(o): rt-select.h rthread.$(o): rt-util.h rthread.$(o): rt-wumpus.h rthread.$(o): score.h rthread.$(o): search.h rthread.$(o): trn.h rthread.$(o): util2.h rthread.$(o): util.h rt-mt.$(o): bits.h rt-mt.$(o): cache.h rt-mt.$(o): common.h rt-mt.$(o): config2.h rt-mt.$(o): config.h rt-mt.$(o): datasrc.h rt-mt.$(o): EXTERN.h rt-mt.$(o): hash.h rt-mt.$(o): INTERN.h rt-mt.$(o): intrp.h rt-mt.$(o): kfile.h rt-mt.$(o): list.h rt-mt.$(o): ngdata.h rt-mt.$(o): ng.h rt-mt.$(o): nntpclient.h rt-mt.$(o): nntp.h rt-mt.$(o): rcln.h rt-mt.$(o): rthread.h rt-mt.$(o): rt-mt.c rt-mt.$(o): rt-mt.h rt-mt.$(o): rt-mt.ih rt-mt.$(o): rt-process.h rt-mt.$(o): trn.h rt-mt.$(o): util2.h rt-mt.$(o): util.h rt-ov.$(o): bits.h rt-ov.$(o): cache.h rt-ov.$(o): common.h rt-ov.$(o): config2.h rt-ov.$(o): config.h rt-ov.$(o): datasrc.h rt-ov.$(o): env.h rt-ov.$(o): EXTERN.h rt-ov.$(o): final.h rt-ov.$(o): hash.h rt-ov.$(o): head.h rt-ov.$(o): INTERN.h rt-ov.$(o): intrp.h rt-ov.$(o): list.h rt-ov.$(o): ngdata.h rt-ov.$(o): ng.h rt-ov.$(o): nntpclient.h rt-ov.$(o): nntp.h rt-ov.$(o): parsedate.h rt-ov.$(o): rthread.h rt-ov.$(o): rt-ov.c rt-ov.$(o): rt-ov.h rt-ov.$(o): rt-ov.ih rt-ov.$(o): rt-process.h rt-ov.$(o): rt-util.h rt-ov.$(o): term.h rt-ov.$(o): trn.h rt-ov.$(o): util2.h rt-ov.$(o): util.h rt-page.$(o): addng.h rt-page.$(o): cache.h rt-page.$(o): color.h rt-page.$(o): common.h rt-page.$(o): config2.h rt-page.$(o): config.h rt-page.$(o): datasrc.h rt-page.$(o): env.h rt-page.$(o): EXTERN.h rt-page.$(o): hash.h rt-page.$(o): INTERN.h rt-page.$(o): list.h rt-page.$(o): ngdata.h rt-page.$(o): nntpclient.h rt-page.$(o): nntp.h rt-page.$(o): only.h rt-page.$(o): opt.h rt-page.$(o): rcln.h rt-page.$(o): rcstuff.h rt-page.$(o): rthread.h rt-page.$(o): rt-page.c rt-page.$(o): rt-page.h rt-page.$(o): rt-page.ih rt-page.$(o): rt-select.h rt-page.$(o): rt-util.h rt-page.$(o): search.h rt-page.$(o): term.h rt-page.$(o): trn.h rt-page.$(o): univ.h rt-page.$(o): util2.h rt-page.$(o): util.h rt-process.$(o): bits.h rt-process.$(o): cache.h rt-process.$(o): common.h rt-process.$(o): config2.h rt-process.$(o): config.h rt-process.$(o): datasrc.h rt-process.$(o): EXTERN.h rt-process.$(o): final.h rt-process.$(o): hash.h rt-process.$(o): INTERN.h rt-process.$(o): intrp.h rt-process.$(o): kfile.h rt-process.$(o): list.h rt-process.$(o): ngdata.h rt-process.$(o): ng.h rt-process.$(o): nntpclient.h rt-process.$(o): nntp.h rt-process.$(o): rcln.h rt-process.$(o): rthread.h rt-process.$(o): rt-process.c rt-process.$(o): rt-process.h rt-process.$(o): rt-process.ih rt-process.$(o): rt-select.h rt-process.$(o): trn.h rt-process.$(o): util2.h rt-process.$(o): util.h rt-select.$(o): addng.h rt-select.$(o): artsrch.h rt-select.$(o): bits.h rt-select.$(o): cache.h rt-select.$(o): color.h rt-select.$(o): common.h rt-select.$(o): config2.h rt-select.$(o): config.h rt-select.$(o): datasrc.h rt-select.$(o): EXTERN.h rt-select.$(o): final.h rt-select.$(o): hash.h rt-select.$(o): help.h rt-select.$(o): INTERN.h rt-select.$(o): intrp.h rt-select.$(o): kfile.h rt-select.$(o): list.h rt-select.$(o): ngdata.h rt-select.$(o): ng.h rt-select.$(o): ngsrch.h rt-select.$(o): ngstuff.h rt-select.$(o): nntpclient.h rt-select.$(o): nntp.h rt-select.$(o): only.h rt-select.$(o): opt.h rt-select.$(o): rcln.h rt-select.$(o): rcstuff.h rt-select.$(o): rthread.h rt-select.$(o): rt-page.h rt-select.$(o): rt-select.c rt-select.$(o): rt-select.h rt-select.$(o): rt-select.ih rt-select.$(o): rt-util.h rt-select.$(o): search.h rt-select.$(o): term.h rt-select.$(o): tkstuff.h rt-select.$(o): tktree.h rt-select.$(o): trn.h rt-select.$(o): univ.h rt-select.$(o): util2.h rt-select.$(o): util.h rt-util.$(o): artio.h rt-util.$(o): cache.h rt-util.$(o): charsubst.h rt-util.$(o): common.h rt-util.$(o): config2.h rt-util.$(o): config.h rt-util.$(o): datasrc.h rt-util.$(o): EXTERN.h rt-util.$(o): hash.h rt-util.$(o): INTERN.h rt-util.$(o): intrp.h rt-util.$(o): list.h rt-util.$(o): ngdata.h rt-util.$(o): ng.h rt-util.$(o): nntpclient.h rt-util.$(o): nntp.h rt-util.$(o): rthread.h rt-util.$(o): rt-select.h rt-util.$(o): rt-util.c rt-util.$(o): rt-util.h rt-util.$(o): rt-util.ih rt-util.$(o): term.h rt-util.$(o): tkstuff.h rt-util.$(o): util2.h rt-util.$(o): util.h rt-wumpus.$(o): artio.h rt-wumpus.$(o): artstate.h rt-wumpus.$(o): backpage.h rt-wumpus.$(o): cache.h rt-wumpus.$(o): charsubst.h rt-wumpus.$(o): color.h rt-wumpus.$(o): common.h rt-wumpus.$(o): config2.h rt-wumpus.$(o): config.h rt-wumpus.$(o): EXTERN.h rt-wumpus.$(o): final.h rt-wumpus.$(o): hash.h rt-wumpus.$(o): head.h rt-wumpus.$(o): INTERN.h rt-wumpus.$(o): list.h rt-wumpus.$(o): ngdata.h rt-wumpus.$(o): ng.h rt-wumpus.$(o): rthread.h rt-wumpus.$(o): rt-select.h rt-wumpus.$(o): rt-wumpus.c rt-wumpus.$(o): rt-wumpus.h rt-wumpus.$(o): rt-wumpus.ih rt-wumpus.$(o): term.h rt-wumpus.$(o): util2.h rt-wumpus.$(o): util.h sacmd.$(o): addng.h sacmd.$(o): bits.h sacmd.$(o): cache.h sacmd.$(o): common.h sacmd.$(o): config2.h sacmd.$(o): config.h sacmd.$(o): datasrc.h sacmd.$(o): decode.h sacmd.$(o): EXTERN.h sacmd.$(o): hash.h sacmd.$(o): head.h sacmd.$(o): help.h sacmd.$(o): INTERN.h sacmd.$(o): intrp.h sacmd.$(o): list.h sacmd.$(o): ngdata.h sacmd.$(o): ng.h sacmd.$(o): ngstuff.h sacmd.$(o): nntpclient.h sacmd.$(o): respond.h sacmd.$(o): sacmd.c sacmd.$(o): sacmd.h sacmd.$(o): sadesc.h sacmd.$(o): sadisp.h sacmd.$(o): samain.h sacmd.$(o): samisc.h sacmd.$(o): sathread.h sacmd.$(o): scanart.h sacmd.$(o): scan.h sacmd.$(o): scmd.h sacmd.$(o): score.h sacmd.$(o): sdisp.h sacmd.$(o): smisc.h sacmd.$(o): sorder.h sacmd.$(o): spage.h sacmd.$(o): term.h sacmd.$(o): util2.h sacmd.$(o): util.h sadesc.$(o): bits.h sadesc.$(o): cache.h sadesc.$(o): common.h sadesc.$(o): config2.h sadesc.$(o): config.h sadesc.$(o): EXTERN.h sadesc.$(o): hash.h sadesc.$(o): head.h sadesc.$(o): INTERN.h sadesc.$(o): list.h sadesc.$(o): ngdata.h sadesc.$(o): rthread.h sadesc.$(o): rt-util.h sadesc.$(o): sacmd.h sadesc.$(o): sadesc.c sadesc.$(o): sadesc.h sadesc.$(o): sadisp.h sadesc.$(o): samain.h sadesc.$(o): sathread.h sadesc.$(o): scanart.h sadesc.$(o): scan.h sadesc.$(o): score.h sadesc.$(o): term.h sadisp.$(o): bits.h sadisp.$(o): cache.h sadisp.$(o): color.h sadisp.$(o): common.h sadisp.$(o): config2.h sadisp.$(o): config.h sadisp.$(o): EXTERN.h sadisp.$(o): hash.h sadisp.$(o): INTERN.h sadisp.$(o): ng.h sadisp.$(o): sadesc.h sadisp.$(o): sadisp.c sadisp.$(o): sadisp.h sadisp.$(o): samain.h sadisp.$(o): samisc.h sadisp.$(o): sathread.h sadisp.$(o): scanart.h sadisp.$(o): scan.h sadisp.$(o): score.h sadisp.$(o): sdisp.h sadisp.$(o): term.h sadisp.$(o): trn.h sadisp.$(o): util.h samain.$(o): bits.h samain.$(o): cache.h samain.$(o): common.h samain.$(o): config2.h samain.$(o): config.h samain.$(o): EXTERN.h samain.$(o): hash.h samain.$(o): head.h samain.$(o): INTERN.h samain.$(o): list.h samain.$(o): ngdata.h samain.$(o): sacmd.h samain.$(o): sadesc.h samain.$(o): sadisp.h samain.$(o): samain.c samain.$(o): samain.h samain.$(o): samisc.h samain.$(o): sathread.h samain.$(o): scanart.h samain.$(o): scan.h samain.$(o): scmd.h samain.$(o): score.h samain.$(o): sdisp.h samain.$(o): smisc.h samain.$(o): sorder.h samain.$(o): spage.h samain.$(o): util.h samisc.$(o): artio.h samisc.$(o): bits.h samisc.$(o): cache.h samisc.$(o): common.h samisc.$(o): config2.h samisc.$(o): config.h samisc.$(o): EXTERN.h samisc.$(o): final.h samisc.$(o): hash.h samisc.$(o): head.h samisc.$(o): INTERN.h samisc.$(o): list.h samisc.$(o): ngdata.h samisc.$(o): ng.h samisc.$(o): rcstuff.h samisc.$(o): rthread.h samisc.$(o): rt-select.h samisc.$(o): samain.h samisc.$(o): samisc.c samisc.$(o): samisc.h samisc.$(o): sathread.h samisc.$(o): scanart.h samisc.$(o): score.h samisc.$(o): sorder.h samisc.$(o): term.h samisc.$(o): trn.h samisc.$(o): util.h sathread.$(o): bits.h sathread.$(o): cache.h sathread.$(o): common.h sathread.$(o): config2.h sathread.$(o): config.h sathread.$(o): EXTERN.h sathread.$(o): hash.h sathread.$(o): head.h sathread.$(o): INTERN.h sathread.$(o): list.h sathread.$(o): mempool.h sathread.$(o): ngdata.h sathread.$(o): sadesc.h sathread.$(o): samain.h sathread.$(o): samisc.h sathread.$(o): sathread.c sathread.$(o): sathread.h sathread.$(o): scanart.h sathread.$(o): sorder.h sathread.$(o): util.h scanart.$(o): artsrch.h scanart.$(o): artstate.h scanart.$(o): bits.h scanart.$(o): cache.h scanart.$(o): common.h scanart.$(o): config2.h scanart.$(o): config.h scanart.$(o): EXTERN.h scanart.$(o): hash.h scanart.$(o): INTERN.h scanart.$(o): ngdata.h scanart.$(o): ng.h scanart.$(o): rt-select.h scanart.$(o): samain.h scanart.$(o): samisc.h scanart.$(o): scanart.c scanart.$(o): scanart.h scanart.$(o): scan.h scanart.$(o): search.h scanart.$(o): smisc.h scanart.$(o): spage.h scanart.$(o): term.h scan.$(o): common.h scan.$(o): config2.h scan.$(o): config.h scan.$(o): EXTERN.h scan.$(o): final.h scan.$(o): INTERN.h scan.$(o): scan.c scan.$(o): scan.h scan.$(o): sorder.h scan.$(o): util.h scmd.$(o): addng.h scmd.$(o): bits.h scmd.$(o): cache.h scmd.$(o): common.h scmd.$(o): config2.h scmd.$(o): config.h scmd.$(o): datasrc.h scmd.$(o): EXTERN.h scmd.$(o): final.h scmd.$(o): hash.h scmd.$(o): help.h scmd.$(o): INTERN.h scmd.$(o): ng.h scmd.$(o): ngstuff.h scmd.$(o): nntpclient.h scmd.$(o): sacmd.h scmd.$(o): samain.h scmd.$(o): scan.h scmd.$(o): scmd.c scmd.$(o): scmd.h scmd.$(o): score.h scmd.$(o): sdisp.h scmd.$(o): smisc.h scmd.$(o): sorder.h scmd.$(o): spage.h scmd.$(o): term.h scmd.$(o): univ.h scmd.$(o): util.h score.$(o): artio.h score.$(o): bits.h score.$(o): cache.h score.$(o): common.h score.$(o): config2.h score.$(o): config.h score.$(o): EXTERN.h score.$(o): filter.h score.$(o): final.h score.$(o): hash.h score.$(o): head.h score.$(o): INTERN.h score.$(o): intrp.h score.$(o): list.h score.$(o): ngdata.h score.$(o): ng.h score.$(o): rt-util.h score.$(o): samain.h score.$(o): samisc.h score.$(o): scanart.h score.$(o): scan.h score.$(o): score.c score.$(o): score-easy.h score.$(o): scorefile.h score.$(o): score.h score.$(o): scoresave.h score.$(o): search.h score.$(o): sorder.h score.$(o): term.h score.$(o): trn.h score.$(o): util.h score-easy.$(o): common.h score-easy.$(o): config2.h score-easy.$(o): config.h score-easy.$(o): EXTERN.h score-easy.$(o): INTERN.h score-easy.$(o): score-easy.c score-easy.$(o): score-easy.h score-easy.$(o): scorefile.h score-easy.$(o): score.h score-easy.$(o): search.h score-easy.$(o): term.h score-easy.$(o): util.h scorefile.$(o): bits.h scorefile.$(o): cache.h scorefile.$(o): common.h scorefile.$(o): config2.h scorefile.$(o): config.h scorefile.$(o): env.h scorefile.$(o): EXTERN.h scorefile.$(o): hash.h scorefile.$(o): head.h scorefile.$(o): INTERN.h scorefile.$(o): list.h scorefile.$(o): mempool.h scorefile.$(o): ngdata.h scorefile.$(o): ng.h scorefile.$(o): rt-util.h scorefile.$(o): samain.h scorefile.$(o): scanart.h scorefile.$(o): scorefile.c scorefile.$(o): scorefile.h scorefile.$(o): scorefile.ih scorefile.$(o): score.h scorefile.$(o): search.h scorefile.$(o): term.h scorefile.$(o): url.h scorefile.$(o): util2.h scorefile.$(o): util.h scoresave.$(o): bits.h scoresave.$(o): cache.h scoresave.$(o): common.h scoresave.$(o): config2.h scoresave.$(o): config.h scoresave.$(o): env.h scoresave.$(o): EXTERN.h scoresave.$(o): hash.h scoresave.$(o): INTERN.h scoresave.$(o): intrp.h scoresave.$(o): list.h scoresave.$(o): ngdata.h scoresave.$(o): ng.h scoresave.$(o): samain.h scoresave.$(o): samisc.h scoresave.$(o): scanart.h scoresave.$(o): scan.h scoresave.$(o): score.h scoresave.$(o): scoresave.c scoresave.$(o): scoresave.h scoresave.$(o): util2.h scoresave.$(o): util.h sdisp.$(o): cache.h sdisp.$(o): common.h sdisp.$(o): config2.h sdisp.$(o): config.h sdisp.$(o): EXTERN.h sdisp.$(o): final.h sdisp.$(o): hash.h sdisp.$(o): INTERN.h sdisp.$(o): ng.h sdisp.$(o): sadisp.h sdisp.$(o): samain.h sdisp.$(o): scanart.h sdisp.$(o): scan.h sdisp.$(o): sdisp.c sdisp.$(o): sdisp.h sdisp.$(o): smisc.h sdisp.$(o): sorder.h sdisp.$(o): term.h search.$(o): common.h search.$(o): config2.h search.$(o): config.h search.$(o): EXTERN.h search.$(o): INTERN.h search.$(o): search.c search.$(o): search.h search.$(o): util2.h search.$(o): util.h smisc.$(o): cache.h smisc.$(o): charsubst.h smisc.$(o): common.h smisc.$(o): config2.h smisc.$(o): config.h smisc.$(o): EXTERN.h smisc.$(o): hash.h smisc.$(o): INTERN.h smisc.$(o): intrp.h smisc.$(o): rt-util.h smisc.$(o): sadesc.h smisc.$(o): samisc.h smisc.$(o): scan.h smisc.$(o): sdisp.h smisc.$(o): smisc.c smisc.$(o): smisc.h smisc.$(o): sorder.h smisc.$(o): term.h smisc.$(o): util.h sorder.$(o): common.h sorder.$(o): config2.h sorder.$(o): config.h sorder.$(o): EXTERN.h sorder.$(o): INTERN.h sorder.$(o): samisc.h sorder.$(o): scanart.h sorder.$(o): scan.h sorder.$(o): smisc.h sorder.$(o): sorder.c sorder.$(o): sorder.h sorder.$(o): util.h spage.$(o): cache.h spage.$(o): common.h spage.$(o): config2.h spage.$(o): config.h spage.$(o): EXTERN.h spage.$(o): final.h spage.$(o): hash.h spage.$(o): INTERN.h spage.$(o): list.h spage.$(o): ngdata.h spage.$(o): rt-util.h spage.$(o): samain.h spage.$(o): sathread.h spage.$(o): scanart.h spage.$(o): scan.h spage.$(o): sdisp.h spage.$(o): smisc.h spage.$(o): sorder.h spage.$(o): spage.c spage.$(o): spage.h spage.$(o): term.h strftime.$(o): common.h strftime.$(o): config2.h strftime.$(o): config.h strftime.$(o): EXTERN.h strftime.$(o): strftime.c sw.$(o): cache.h sw.$(o): charsubst.h sw.$(o): common.h sw.$(o): config2.h sw.$(o): config.h sw.$(o): env.h sw.$(o): EXTERN.h sw.$(o): hash.h sw.$(o): head.h sw.$(o): init.h sw.$(o): INTERN.h sw.$(o): intrp.h sw.$(o): list.h sw.$(o): ngdata.h sw.$(o): only.h sw.$(o): opt.h sw.$(o): rt-page.h sw.$(o): search.h sw.$(o): sw.c sw.$(o): sw.h sw.$(o): term.h sw.$(o): trn.h sw.$(o): util2.h sw.$(o): util.h term.$(o): art.h term.$(o): cache.h term.$(o): color.h term.$(o): common.h term.$(o): config2.h term.$(o): config.h term.$(o): datasrc.h term.$(o): env.h term.$(o): EXTERN.h term.$(o): final.h term.$(o): hash.h term.$(o): help.h term.$(o): init.h term.$(o): INTERN.h term.$(o): intrp.h term.$(o): list.h term.$(o): ngdata.h term.$(o): nntpclient.h term.$(o): opt.h term.$(o): rt-select.h term.$(o): scanart.h term.$(o): scan.h term.$(o): score.h term.$(o): sdisp.h term.$(o): term.c term.$(o): term.h term.$(o): term.ih term.$(o): tkstuff.h term.$(o): univ.h term.$(o): util2.h term.$(o): util.h tkstuff.$(o): common.h tkstuff.$(o): config2.h tkstuff.$(o): config.h tkstuff.$(o): EXTERN.h tkstuff.$(o): INTERN.h tkstuff.$(o): term.h tkstuff.$(o): tkstuff.c tkstuff.$(o): tkstuff.h tkstuff.$(o): tkstuff.ih tkstuff.$(o): tktree.h tkstuff.$(o): util2.h tkstuff.$(o): util.h tktree.$(o): cache.h tktree.$(o): common.h tktree.$(o): config2.h tktree.$(o): config.h tktree.$(o): EXTERN.h tktree.$(o): hash.h tktree.$(o): INTERN.h tktree.$(o): ngdata.h tktree.$(o): ng.h tktree.$(o): rt-select.h tktree.$(o): rt-wumpus.h tktree.$(o): score.h tktree.$(o): term.h tktree.$(o): tkstuff.h tktree.$(o): tktree.c tktree.$(o): tktree.h tktree.$(o): tktree.ih tktree.$(o): util.h trn-artchk.$(o): common.h trn-artchk.$(o): config2.h trn-artchk.$(o): config.h trn-artchk.$(o): EXTERN.h trn-artchk.$(o): nntpclient.h trn-artchk.$(o): nntpinit.h trn-artchk.$(o): trn-artchk.c trn-artchk.$(o): util2.h trn-artchk.$(o): util3.h trn.$(o): addng.h trn.$(o): cache.h trn.$(o): common.h trn.$(o): config2.h trn.$(o): config.h trn.$(o): datasrc.h trn.$(o): env.h trn.$(o): EXTERN.h trn.$(o): final.h trn.$(o): hash.h trn.$(o): help.h trn.$(o): init.h trn.$(o): INTERN.h trn.$(o): intrp.h trn.$(o): kfile.h trn.$(o): last.h trn.$(o): list.h trn.$(o): ngdata.h trn.$(o): ng.h trn.$(o): ngsrch.h trn.$(o): ngstuff.h trn.$(o): nntpclient.h trn.$(o): nntp.h trn.$(o): only.h trn.$(o): opt.h trn.$(o): patchlevel.h trn.$(o): rcln.h trn.$(o): rcstuff.h trn.$(o): rt-select.h trn.$(o): scan.h trn.$(o): search.h trn.$(o): sw.h trn.$(o): term.h trn.$(o): trn.c trn.$(o): trn.h trn.$(o): univ.h trn.$(o): util2.h trn.$(o): util.h univ.$(o): cache.h univ.$(o): common.h univ.$(o): config2.h univ.$(o): config.h univ.$(o): env.h univ.$(o): EXTERN.h univ.$(o): final.h univ.$(o): hash.h univ.$(o): head.h univ.$(o): help.h univ.$(o): INTERN.h univ.$(o): list.h univ.$(o): ngdata.h univ.$(o): ng.h univ.$(o): rcstuff.h univ.$(o): rt-select.h univ.$(o): rt-util.h univ.$(o): score.h univ.$(o): term.h univ.$(o): trn.h univ.$(o): univ.c univ.$(o): univ.h univ.$(o): univ.ih univ.$(o): url.h univ.$(o): util2.h univ.$(o): util.h url.$(o): common.h url.$(o): config2.h url.$(o): config.h url.$(o): EXTERN.h url.$(o): INTERN.h url.$(o): term.h url.$(o): url.c url.$(o): url.h url.$(o): url.ih url.$(o): util2.h url.$(o): util.h util2.$(o): common.h util2.$(o): config2.h util2.$(o): config.h util2.$(o): datasrc.h util2.$(o): EXTERN.h util2.$(o): hash.h util2.$(o): INTERN.h util2.$(o): list.h util2.$(o): ngdata.h util2.$(o): nntpauth.h util2.$(o): nntpclient.h util2.$(o): nntp.h util2.$(o): util2.c util2.$(o): util2.h util2.$(o): util3.h util2.$(o): util.h util3.$(o): config2.h util3.$(o): config.h util3.$(o): EXTERN.h util3.$(o): INTERN.h util3.$(o): nntpclient.h util3.$(o): util2.h util3.$(o): util3.c util3.$(o): util3.h util.$(o): common.h util.$(o): config2.h util.$(o): config.h util.$(o): datasrc.h util.$(o): env.h util.$(o): EXTERN.h util.$(o): final.h util.$(o): hash.h util.$(o): INTERN.h util.$(o): intrp.h util.$(o): list.h util.$(o): ngdata.h util.$(o): nntpauth.h util.$(o): nntpclient.h util.$(o): nntp.h util.$(o): only.h util.$(o): scan.h util.$(o): search.h util.$(o): smisc.h util.$(o): term.h util.$(o): univ.h util.$(o): util2.h util.$(o): util.c util.$(o): util.h uudecode.$(o): artio.h uudecode.$(o): common.h uudecode.$(o): config2.h uudecode.$(o): config.h uudecode.$(o): decode.h uudecode.$(o): EXTERN.h uudecode.$(o): INTERN.h uudecode.$(o): mime.h uudecode.$(o): respond.h uudecode.$(o): term.h uudecode.$(o): util2.h uudecode.$(o): uudecode.c uudecode.$(o): uudecode.h uudecode.$(o): uudecode.ih wildmat.$(o): config2.h wildmat.$(o): config.h wildmat.$(o): wildmat.c wildmat.$(o): wildmat.h wildmat.$(o): wildmat.ih access.def: access_def.SH config.sh ; /bin/sh $(srcdir)/access_def.SH config.h: config_h.SH config.sh ; /bin/sh $(srcdir)/config_h.SH HelpFiles/config/environment: environment.SH config.sh makedir ; /bin/sh $(srcdir)/environment.SH filexp: filexp.SH config.sh ; /bin/sh $(srcdir)/filexp.SH makedepend: makedepend.SH config.sh ; /bin/sh $(srcdir)/makedepend.SH makedir: makedir.SH config.sh ; /bin/sh $(srcdir)/makedir.SH mbox.saver: mbox_saver.SH config.sh ; /bin/sh $(srcdir)/mbox_saver.SH mkpro: mkpro.SH config.sh ; /bin/sh $(srcdir)/mkpro.SH mktd: mktd.SH config.sh ; /bin/sh $(srcdir)/mktd.SH myinstall: myinstall.SH config.sh ; /bin/sh $(srcdir)/myinstall.SH newsnews: newsnews.SH ; /bin/sh $(srcdir)/newsnews.SH norm.saver: norm_saver.SH config.sh ; /bin/sh $(srcdir)/norm_saver.SH Pnews: Pnews.SH config.sh ; /bin/sh $(srcdir)/Pnews.SH Policy.sh: Policy_sh.SH config.sh ; /bin/sh $(srcdir)/Policy_sh.SH Rnmail: Rnmail.SH config.sh ; /bin/sh $(srcdir)/Rnmail.SH Speller: Speller.SH config.sh ; /bin/sh $(srcdir)/Speller.SH # WARNING: Put nothing here or make depend will gobble it up! trn-4.0-test77/edit_dist.c0000644000000000000000000001624207113133015014124 0ustar rootroot/* edit_dist.c */ /* The authors 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" /* Declare MEM_SIZE */ #include "util.h" /* Declare safemalloc() */ #include "INTERN.h" #include "edit_dist.h" #ifdef EDIT_DISTANCE /* edit_dist -- returns the minimum edit distance between two strings Program by: Mark Maimone CMU Computer Science 13 Nov 89 Last Modified: 28 Jan 90 If the input strings have length n and m, the algorithm runs in time O(nm) and space O(min(m,n)). HISTORY 13 Nov 89 (mwm) Created edit_dist(). 17 May 93 (mwm) Improved performance when used with trn's newsgroup processing; assume all costs are 1, and you can terminate when a threshold is exceeded. */ #define TRN_SPEEDUP /* Use a less-general version of the routine, one that's better for trn. All change costs are 1, and it's okay to terminate if the edit distance is known to exceed MIN_DIST */ #define THRESHOLD 4000 /* worry about allocating more memory only when this # of bytes is exceeded */ #define STRLENTHRESHOLD ((int) ((THRESHOLD / sizeof (int) - 3) / 2)) #define SAFE_ASSIGN(x,y) (((x) != NULL) ? (*(x) = (y)) : (y)) #define swap_int(x,y) (_iswap = (x), (x) = (y), (y) = _iswap) #define swap_char(x,y) (_cswap = (x), (x) = (y), (y) = _cswap) #define min3(x,y,z) (_mx = (x), _my = (y), _mz = (z), (_mx < _my ? (_mx < _mz ? _mx : _mz) : (_mz < _my) ? _mz : _my)) #define min2(x,y) (_mx = (x), _my = (y), (_mx < _my ? _mx : _my)) static int insert_cost = 1; static int delete_cost = 1; static int _iswap; /* swap_int temp variable */ static char* _cswap; /* swap_char temp variable */ static int _mx, _my, _mz; /* min2, min3 temp variables */ /* edit_distn -- returns the edit distance between two strings, or -1 on failure */ int edit_distn(from, from_len, to, to_len) char* from; register int from_len; char* to; register int to_len; { #ifndef TRN_SPEEDUP register int ins, del, ch; /* local copies of edit costs */ #endif register int row, col, index; /* dynamic programming counters */ register int radix; /* radix for modular indexing */ #ifdef TRN_SPEEDUP register int low; #endif int* buffer; /* pointer to storage for one row of the d.p. array */ static int store[THRESHOLD / sizeof (int)]; /* a small amount of static storage, to be used when the input strings are small enough */ /* Handle trivial cases when one string is empty */ if (from == NULL || !from_len) { if (to == NULL || !to_len) return 0; return to_len * insert_cost; } if (to == NULL || !to_len) return from_len * delete_cost; /* Initialize registers */ radix = 2 * from_len + 3; #ifdef TRN_SPEEDUP #define ins 1 #define del 1 #define ch 1 #define swap_cost 1 #else ins = insert_cost; del = delete_cost; ch = change_cost; #endif /* Make from short enough to fit in the static storage, if it's at all possible */ if (from_len > to_len && from_len > STRLENTHRESHOLD) { swap_int(from_len, to_len); swap_char(from, to); #ifndef TRN_SPEEDUP swap_int(ins, del); #endif } /* if from_len > to_len */ /* Allocate the array storage (from the heap if necessary) */ if (from_len <= STRLENTHRESHOLD) buffer = store; else buffer = (int*)safemalloc((MEM_SIZE) radix * sizeof (int)); /* Here's where the fun begins. We will find the minimum edit distance using dynamic programming. We only need to store two rows of the matrix at a time, since we always progress down the matrix. For example, given the strings "one" and "two", and insert, delete and change costs equal to 1: _ o n e _ 0 1 2 3 t 1 1 2 3 w 2 2 2 3 o 3 2 3 3 The dynamic programming recursion is defined as follows: ar(x,0) := x * insert_cost ar(0,y) := y * delete_cost ar(x,y) := min(a(x - 1, y - 1) + (from[x] == to[y] ? 0 : change), a(x - 1, y) + insert_cost, a(x, y - 1) + delete_cost, a(x - 2, y - 2) + (from[x] == to[y-1] && from[x-1] == to[y] ? swap_cost : infinity)) Since this only looks at most two rows and three columns back, we need only store the values for the two preceeding rows. In this implementation, we do not explicitly store the zero column, so only 2 * from_len + 2 words are needed. However, in the implementation of the swap_cost check, the current matrix value is used as a buffer; we can't overwrite the earlier value until the swap_cost check has been performed. So we use 2 * from_len + 3 elements in the buffer. */ #define ar(x,y,index) (((x) == 0) ? (y) * del : (((y) == 0) ? (x) * ins : \ buffer[mod(index)])) #define NW(x,y) ar(x, y, index + from_len + 2) #define N(x,y) ar(x, y, index + from_len + 3) #define W(x,y) ar(x, y, index + radix - 1) #define NNWW(x,y) ar(x, y, index + 1) #define mod(x) ((x) % radix) index = 0; #ifdef DEBUG_EDITDIST printf(" "); for (col = 0; col < from_len; col++) printf(" %c ", from[col]); printf("\n "); for (col = 0; col <= from_len; col++) printf("%2d ", col * del); #endif /* Row 0 is handled implicitly; its value at a given column is col*del. The loop below computes the values for Row 1. At this point we know the strings are nonempty. We also don't need to consider swap costs in row 1. COMMENT: the indicies row and col below point into the STRING, so the corresponding MATRIX indicies are row+1 and col+1. */ buffer[index++] = min2(ins + del, (from[0] == to[0] ? 0 : ch)); #ifdef TRN_SPEEDUP low = buffer[mod(index + radix - 1)]; #endif #ifdef DEBUG_EDITDIST printf("\n %c %2d %2d ", to[0], ins, buffer[index - 1]); #endif for (col = 1; col < from_len; col++) { buffer[index] = min3( col * del + ((from[col] == to[0]) ? 0 : ch), (col + 1) * del + ins, buffer[index - 1] + del); #ifdef TRN_SPEEDUP if (buffer[index] < low) low = buffer[index]; #endif index++; #ifdef DEBUG_EDITDIST printf("%2d ", buffer[index - 1]); #endif } /* for col = 1 */ #ifdef DEBUG_EDITDIST printf("\n %c %2d ", to[1], 2 * ins); #endif /* Now handle the rest of the matrix */ for (row = 1; row < to_len; row++) { for (col = 0; col < from_len; col++) { buffer[index] = min3( NW(row, col) + ((from[col] == to[row]) ? 0 : ch), N(row, col + 1) + ins, W(row + 1, col) + del); if (from[col] == to[row - 1] && col > 0 && from[col - 1] == to[row]) buffer[index] = min2(buffer[index], NNWW(row - 1, col - 1) + swap_cost); #ifdef DEBUG_EDITDIST printf("%2d ", buffer[index]); #endif #ifdef TRN_SPEEDUP if (buffer[index] < low || col == 0) low = buffer[index]; #endif index = mod(index + 1); } /* for col = 1 */ #ifdef DEBUG_EDITDIST if (row < to_len - 1) printf("\n %c %2d ", to[row+1], (row + 2) * ins); else printf("\n"); #endif #ifdef TRN_SPEEDUP if (low > MIN_DIST) break; #endif } /* for row = 1 */ row = buffer[mod(index + radix - 1)]; if (buffer != store) free((char*)buffer); return row; } /* edit_distn */ #endif /* EDIT_DISTANCE */ trn-4.0-test77/edit_dist.h0000644000000000000000000000047507113133015014132 0ustar rootroot/* edit_dist.h */ /* The authors 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. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int edit_distn _((char*,int,char*,int)); trn-4.0-test77/env.c0000644000000000000000000001721207113133015012742 0ustar rootroot/* env.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "init.h" #include "final.h" #include "util.h" #include "util2.h" #include "INTERN.h" #include "env.h" #include "env.ih" #ifdef HAS_RES_INIT #include #include #include #endif bool env_init(tcbuf, lax) char* tcbuf; bool_int lax; { bool fully_successful = TRUE; if ((homedir = getenv("HOME")) == NULL) homedir = getenv("LOGDIR"); if ((tmpdir = getenv("TMPDIR")) == NULL) tmpdir = getval("TMP","/tmp"); /* try to set loginName */ if (lax) { loginName = getenv("USER"); if (!loginName) loginName = getenv("LOGNAME"); } #ifndef MSDOS if (!lax || !loginName) { loginName = getlogin(); if (loginName) loginName = savestr(loginName); } #endif /* Set realName, and maybe set loginName and homedir (if NULL). */ if (!setusername(tcbuf)) { if (!loginName) loginName = nullstr; if (!realName) realName = nullstr; fully_successful = FALSE; } env_init2(); /* set phostname to the hostname of our local machine */ if (!setphostname(tcbuf)) fully_successful = FALSE; #ifdef SUPPORT_NNTP { char* cp = getval("NETSPEED","5"); if (*cp == 'f') netspeed = 10; else if (*cp == 's') netspeed = 1; else { netspeed = atoi(cp); if (netspeed < 1) netspeed = 1; } } #endif return fully_successful; } static void env_init2() { if (dotdir) /* Avoid running multiple times. */ return; if (!homedir) homedir = "/"; dotdir = getval("DOTDIR",homedir); trndir = savestr(filexp(getval("TRNDIR",TRNDIR))); lib = savestr(filexp(NEWSLIB)); rnlib = savestr(filexp(PRIVLIB)); } /* Set loginName to the user's login name and realName to the user's ** real name. */ bool setusername(tmpbuf) char* tmpbuf; { char* s; char* c; #ifdef HAS_GETPWENT struct passwd* pwd; if (loginName == NULL) pwd = getpwuid(getuid()); else pwd = getpwnam(loginName); if (!pwd) return 0; if (!loginName) loginName = savestr(pwd->pw_name); if (!homedir) homedir = savestr(pwd->pw_dir); s = pwd->pw_gecos; #else /* !HAS_GETPWENT */ int i; if (getpw(getuid(), tmpbuf+1) != 0) return 0; if (!loginName) { cpytill(buf,tmpbuf+1,':'); loginName = savestr(buf); } for (s = tmpbuf, i = GCOSFIELD-1; i; i--) { if (s) s = index(s+1,':'); } if (!s) return 0; s = cpytill(tmpbuf,s+1,':'); if (!homedir) { cpytill(buf,s+1,':'); homedir = savestr(buf); } s = tmpbuf; #endif /* !HAS_GETPWENT */ #ifdef PASSNAMES #ifdef BERKNAMES #ifdef BERKJUNK while (*s && !isalnum(*s) && *s != '&') s++; #endif if ((c = index(s, ',')) != NULL) *c = '\0'; if ((c = index(s, ';')) != NULL) *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,loginName); /* before the morning after */ strcat(c,s+1); if (islower(*c)) *c = toupper(*c); /* gack and double gack */ } realName = savestr(buf); #else /* !BERKNAMES */ if ((c = index(s, '(')) != NULL) *c = '\0'; if ((c = index(s, '-')) != NULL) s = c; realName = savestr(s); #endif /* !BERKNAMES */ #else /* !PASSNAMES */ { FILE* fp; env_init2(); /* Make sure homedir/dotdir/etc. are set. */ if ((fp = fopen(filexp(FULLNAMEFILE),"r")) != NULL) { fgets(buf,sizeof buf,fp); fclose(fp); buf[strlen(buf)-1] = '\0'; realName = savestr(buf); } else s = "PUT_YOUR_NAME_HERE"; } #endif /* !PASSNAMES */ #ifdef HAS_GETPWENT endpwent(); #endif return 1; } bool setphostname(tmpbuf) char* tmpbuf; { FILE* fp; bool hostname_ok = TRUE; /* Find the local hostname */ #ifdef HAS_GETHOSTNAME gethostname(tmpbuf,TCBUF_SIZE); #else # ifdef HAS_UNAME /* get sysname */ uname(&utsn); strcpy(tmpbuf,utsn.nodename); # else # ifdef PHOSTCMD { FILE* popen(); FILE* pipefp = popen(PHOSTCMD,"r"); if (pipefp == NULL) { printf("Can't find hostname\n"); finalize(1); } fgets(tmpbuf,TCBUF_SIZE,pipefp); tmpbuf[strlen(tmpbuf)-1] = '\0'; /* wipe out newline */ pclose(pipefp); } # else strcpy(tmpbuf, "!INVALID!"); # endif /* PHOSTCMD */ # endif /* HAS_UNAME */ #endif /* HAS_GETHOSTNAME */ localhost = savestr(tmpbuf); /* Build the host name that goes in postings */ phostname = PHOSTNAME; if (FILE_REF(phostname) || *phostname == '~') { phostname = filexp(phostname); if ((fp = fopen(phostname,"r")) == NULL) strcpy(tmpbuf,"."); else { fgets(tmpbuf,TCBUF_SIZE,fp); fclose(fp); phostname = tmpbuf + strlen(tmpbuf) - 1; if (*phostname == '\n') *phostname = '\0'; } } else strcpy(tmpbuf,phostname); if (*tmpbuf == '.') { if (tmpbuf[1] != '\0') strcpy(buf,tmpbuf); else *buf = '\0'; strcpy(tmpbuf,localhost); strcat(tmpbuf,buf); } if (!index(tmpbuf,'.')) { if (*tmpbuf) strcat(tmpbuf, "."); #ifdef HAS_RES_INIT if (!(_res.options & RES_INIT)) res_init(); if (_res.defdname != NULL) strcat(tmpbuf,_res.defdname); else #endif #ifdef HAS_GETDOMAINNAME if (getdomainname(buf,LBUFLEN) == 0) strcat(tmpbuf,buf); else #endif { strcat(tmpbuf,"UNKNOWN.HOST"); hostname_ok = FALSE; } } phostname = savestr(tmpbuf); return hostname_ok; } char* getval(nam,def) char* nam; char* def; { char* val; if ((val = getenv(nam)) == NULL || !*val) return def; return val; } static bool firstexport = TRUE; extern char** environ; char* export(nam,val) char* nam; char* val; { int namlen = strlen(nam); register int i=envix(nam,namlen); /* where does it go? */ if (!environ[i]) { /* does not exist yet */ if (firstexport) { /* need we copy environment? */ int j; #ifndef lint char** tmpenv = (char**) /* point our wand at memory */ safemalloc((MEM_SIZE) (i+2) * sizeof(char*)); #else char** tmpenv = NULL; #endif /* lint */ firstexport = FALSE; for (j = 0; j < i; j++) /* copy environment */ tmpenv[j] = environ[j]; environ = tmpenv; /* tell exec where it is now */ } #ifndef lint else environ = (char**) saferealloc((char*) environ, (MEM_SIZE) (i+2) * sizeof(char*)); /* just expand it a bit */ #endif /* lint */ environ[i+1] = NULL; /* make sure it's null terminated */ } environ[i] = safemalloc((MEM_SIZE)(namlen + strlen(val) + 2)); /* this may or may not be in */ /* the old environ structure */ sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */ return environ[i] + namlen + 1; } void un_export(export_val) char* export_val; { if (export_val[-1] == '=' && export_val[-2] != '_') { export_val[0] = export_val[-2]; export_val[1] = '\0'; export_val[-2] = '_'; } } void re_export(export_val, new_val, limit) char* export_val; char* new_val; int limit; { if (export_val[-1] == '=' && export_val[-2] == '_' && !export_val[1]) export_val[-2] = export_val[0]; safecpy(export_val, new_val, limit+1); } static int envix(nam, len) char* nam; int len; { register int i; for (i = 0; environ[i]; i++) { if (strnEQ(environ[i],nam,len) && environ[i][len] == '=') break; /* strnEQ must come first to avoid */ } /* potential SEGV's */ return i; } #ifdef MSDOS char* GetEnv(var) char* var; { #undef getenv char* s = getenv(var); if (s && isalpha(*s) && s[1] == ':') { char* t = index(s,'\\'); if (t) { char ebuf[MAXDIR+32]; strcpy(ebuf,s); t = ebuf + (t-s); do { *t = '/'; } while ((t = index(t,'\\')) != NULL); s = export(var,ebuf); } } return s; } #endif trn-4.0-test77/env.h0000644000000000000000000000204207113133015012742 0ustar rootroot/* env.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* homedir INIT(NULL); /* login directory */ EXT char* dotdir INIT(NULL); /* where . files go */ EXT char* trndir INIT(NULL); /* usually %./.trn */ EXT char* lib INIT(NULL); /* news library */ EXT char* rnlib INIT(NULL); /* private news program library */ EXT char* tmpdir INIT(NULL); /* where tmp files go */ EXT char* loginName INIT(NULL); /* login id of user */ EXT char* realName INIT(NULL); /* real name of user */ EXT char* phostname INIT(NULL); /* host name in a posting */ EXT char* localhost INIT(NULL); /* local host name */ #ifdef SUPPORT_NNTP EXT int netspeed INIT(20); /* how fast our net-connection is */ #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool env_init _((char*,bool_int)); bool setusername _((char*)); bool setphostname _((char*)); char* getval _((char*,char*)); char* export _((char*,char*)); void un_export _((char*)); void re_export _((char*,char*,int)); #ifdef MSDOS char* GetEnv _((char*)); #endif trn-4.0-test77/env.ih0000644000000000000000000000032507113133015013115 0ustar rootroot/* env.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void env_init2 _((void)); static int envix _((char*,int)); trn-4.0-test77/environment.SH0000644000000000000000000005653011437640112014621 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting HelpFiles/config/environment (with variable substitutions)" if test "X$src" != "X." ; then for ff in `$grep '^HelpFiles/' $src/MANIFEST | sed -e 's/[ ].*//'`; do ./makedir $ff 1 cp $src/$ff $ff done fi $spitshell >HelpFiles/config/environment < %j The terminal speed (e.g. 9600) %I Inclusion indicator %l News administrator login name %L Login name (yours) %m The current mode of trn. i Initializing. n Newsgroup-list level. f End (finis) of newsgroup-list level. t The thread/subject/article selector. c Newsrc selector. w Newsgroup selector. j Addgroup selector. l Option selector. a Article level ("What next?"). e End of the article level. p Pager level ("MORE" prompt). u Unkill prompt. d Selector mode prompt. o Selector order prompt. m Memorize thread command prompt. r Memorize subject command prompt. z Option edit prompt. k Processing memorized (KILL-file) commands. A Add this newsgroup? B Abandon confirmation. C Catchup confirmation. D Delete bogus newsgroups? F Is follow-up a new topic? M Use mailbox format? R Resubscribe to this newsgroup? K Press any key prompt. %M Number of articles marked with M %n Newsgroups from source article %N Full name (yours) %o Organization (yours) %O Original working directory (where you ran trn from) %p Your private news directory (-d switch) %P Public news spool directory %r Last reference (parent article id) %q The last quoted input (via %"). %R References list for a new article %s Subject, with all Re's and (nf)'s stripped off %S Subject, with one Re stripped off %t New To: line derived from From: and Reply-To (Internet always) %T New To: line derived from Path: %u Number of unread articles %U Number of unread articles disregarding current article %v Number of unselected articles disregarding current article %W The thread directory root %x News library directory, usually /usr/lib/news %X Trn's private library directory, usually %x/trn %y From line with domain shortening (name@*.domain.nam) %Y The tmp directory to use %z Size of current article in bytes. %Z Number of selected threads. %~ Home directory (yours) %. Directory containing dotfiles, usually %~ %+ Directory containing a user's init files, usually %./.trn %# count of articles saved in current command (from 1 to n) %^# ever-increasing number (from 1 to n) %\$ current process number %{name} Environment variable "name". %{name-default} form allowed. % Like %{name}, but variable "name" will itself be subject to interpolation before being used. %[name] Header line beginning with "Name: ", without "Name: " %"prompt" Print prompt and insert what is typed. %\`command\` Insert output of command. %(test_text=pattern?if_text:else_text) Substitute if_text if test_text matches pattern, otherwise substitute else_text. Use != for negated match. % substitutions are done on test_text, if_text, and else_text. %digit Substitute the text matched by the nth bracket in the last pattern that had brackets. %0 matches the last bracket matched, in case you had alternatives. %? Insert a space unless the entire result is > 79 chars, in which case the space becomes a newline. Put ^ in the middle to capitalize the first letter: %^C = Rec.humor Put _ in the middle to capitalize last component: %_c = net/Jokes Put \ in the middle to quote regexp and % characters in the result Put > in the middle to return the address portion of a name. Put ) in the middle to return the comment portion of a name. Put ' in the middle to protect "'"s in arguments you've put in "'"s. Put :FMT in the middle to format the result: %:-30.30t ~ interpretation in filename expansion happens after % expansion, so you could put ~%{NEWSLOGNAME-news} and it will expand correctly. The variables: ATTRIBUTION (%) Gives the format of the attribution line in front of the quoted article included by an F (followup) command. Default: In article %i,%?%)f <%>f> wrote: AUTHORCOPY If defined, contains the name of a file to which Pnews will append finished articles. Default: Article is not saved. AUTOSUBSCRIBE When trn is checking for new newsgroups and finds one matching one of the patterns in AUTOSUBSCRIBE, the new group is automatically added to the end of the .newsrc, subscribed. Newsgroups not matching this or AUTOUNSUBSCRIBE, below, are offered to the user. AUTOSUBSCRIBE is a comma separated list of newsgroup patterns ala 'o', '/', etc. It can also include "but not" entries preceded by '!'. "a,b,!c,d" is read as "matching a or b, unless it also matches c; matching d regardless". Another way to look at it is "(((a or b) and not c) or d)". To automatically subscribe to all local groups but be choosy about non-local groups, one might say "*,!*.*". Default: (none) AUTOUNSUBSCRIBE AUTOUNSUBSCRIBE is very similar to AUTOSUBSCRIBE, above, but new newsgroups matching it are automatically added to the end of the .newsrc file, unsubscribed. If a newsgroup matches AUTOSUBSCRIBE, AUTOUNSUBSCRIBE is not consulted. Default: (none) CANCEL (~) The shell command used to cancel an article. Default: inews -h < %h CANCELHEADER (%) The format of the file to pass to the CANCEL command in order to cancel an article. Default: Newsgroups: %n Subject: cancel %(%{FROM}=^$?:From: %{FROM} )Control: cancel %i Distribution: %D %i was cancelled from within trn. DOTDIR (%) Where to find your dot files, if they aren't in your home directory. Default: \$HOME EDITOR (~) The name of your editor, if VISUAL is undefined. Default: $defeditor EXSAVER (%) The shell command to execute in order to extract data to either /bin/sh or a user-specified command. Default: tail +%Bc %A | %e FIRSTLINE (%) Controls the format of the line displayed at the top of an article. Warning: this may go away. The default (ignoring the Marked to return display in unthreaded groups) is approximately: %C #%a%(%Z=^0\$?%(%U!=^0\$? (%U more\)): (%U + %v more\)) FORWARDHEADER (%) The format of the header file for forwarding messages. If these headers contain a Content-Type header with a multipart/* type, Rnmail will attempt to forward the message MIME-encapsulated. This may fail if you are using a non-standard FORWARDPOSTER which does not pass on MIME headers properly. See also FORWARDPOSTER. Default: To: %"\n\nTo: " Subject: %(%i=^\$?:%[subject] (fwd\\) %(%{FROM}=^\$?:From: %{FROM} )%(%{REPLYTO}=^\$?:Reply-To: %{REPLYTO} )X-Newsgroups: %n In-Reply-To: %i) %(%[references]=^\$?:References: %[references] )Organization: %o Mime-Version: 1.0 Content-Type: multipart/mixed; boundary=\"=%\$%^#=--\" Cc: Bcc: \n\n FORWARDMSG (%) The line added to the message body when forwarding a message, signifying the beginning of the forwarded message. This is only added if the message is not being forwarded as a MIME attachment (see FORWARDHEADER). Default: ------- start of forwarded message ------- FORWARDMSGEND (%) The line added to the message body when forwarding a message, signifying the end of the forwarded message. This is only added if the message is not being forwarded as a MIME attachment (see FORWARDHEADER). Default: ------- end of forwarded message ------- FORWARDPOSTER (~) The shell command to be used by the forward command (^F) in order to allow you to edit and deliver the file. trn will not itself call upon an editor for replies -- this is a function of the program referenced by FORWARDPOSTER. See also FORWARDHEADER and MAILPOSTER. Default: Rnmail -h %h FROM (%) What to put in the From: header of your posts, email replies, and email forwards, instead of whatever the default name and address are for your system. This will only work if you use the default setings for the NEWSHEADER, MAILHEADER, and FORWARDHEADER variables, or if your custom ones use FROM to set the From: header. Regardless of the settings of NEWSHEADER, MAILHEADER, and FORWARDHEADER, the setting of FROM is used to determine which articles may be cancelled or superseded. Default: undefined HELPPAGER The pager used by the online help system, if you want to use something different from the default or the content of the PAGER variable (see PAGER). Default: \$PAGER or 'more'. HIDELINE If defined, contains a regular expression which matches article lines to be hidden, in order, for instance, to suppress quoted material. A recommended string for this purpose is "^>...", which doesn't hide lines with only '>', to give some indication that quoted material is being skipped. If you want to hide more than one pattern, you can use "|" to separate the alternatives. You can view the hidden lines by restarting the article with the 'v' command. There is some overhead involved in matching each line of the article against a regular expression. You might wish to use a baud-rate modifier to enable this feature only at low baud rates. Default: undefined HOME Your home directory. Affects ~ interpretation, and the location of your dot files if DOTDIR is not defined. Default: \$LOGDIR KILLGLOBAL (~) Where to find the KILL file to apply to every newsgroup. See the '^K' command at the newsgroup-selection level. Default: %p/KILL KILLLOCAL (~) Where to find the KILL file for the current newsgroup. See the commands 'K' and '^K' at the article selection level, and the search modifier 'K'. Default: %p/%c/KILL LOGDIR Your home directory if HOME is undefined. Affects ~ interpretation, and the location of your dot files if DOTDIR is not defined. Default: none. Explanation: you must have either \$HOME or \$LOGDIR. LOGNAME Your login name, if USER is undefined. May be interpolated using "%L". Default: value of getlogin(). LOCALTIMEFMT The format used by strftime() to print the local time. The Date line is only displayed in local time if the group is threaded (see the -H option for more information on Date). Default: %a %b %e %X %Z %Y which is the same format as the date(1) command. MAILCALL (~) What to say when there is new mail. Default: (Mail) MAILCAPS A colon-separated search path for mailcap files. Default: %./.mimecap:~/.mailcap:$mimecap MAILFILE (~) Where to check for mail. Default: $mailfile MAILHEADER (%) The format of the header file for mail replies. See also MAILPOSTER. Default: To: %t Subject: %(%i=^\$?:Re: %S X-Newsgroups: %n In-Reply-To: %i) %(%{FROM}=^\$?:From: %{FROM} )%(%{REPLYTO}=^\$?:Reply-To: %{REPLYTO} )%(%[references]=^\$?:References: %[references] )Organization: %o Cc: Bcc: \n\n MAILPOSTER (~) The shell command to be used by the reply commands (r and R) in order to allow you to enter and deliver the response. trn will not itself call upon an editor for replies -- this is a function of the program referenced by MAILPOSTER. See also MAILHEADER. Default: Rnmail -h %h MBOXSAVER (~) The shell command to save an article in mailbox format. Default: %X/mbox.saver %A %P %c %a %B %C "%b" "From %t %\`date\`" Explanation: the first seven arguments are the same as for NORMSAVER. The eighth argument to the shell script is the new From line for the article, including the posting date, derived either directly from the Posted: line, or not-so-directly from the Date: line. Header munging at its finest. MODSTRING The string to insert in the group summary line, which heads each article, for a moderated group. See also NOPOSTRING. Default: " (moderated)" NAME Your full name. May be interpolated using "%N". Default: name from /etc/passwd, or ~/.fullname. NEWSHEADER (%) The format of the header file for follow-ups. See also NEWSPOSTER. Default: %(%[followup-to]=^\$?:%(%[followup-to]=^%n\$?:X-ORIGINAL-NEWSGROUPS: %n ))Newsgroups: %(%F=^\$?%C:%F) Subject: %(%S=^\$?%\"\n\nSubject: \":Re: %S) Summary: Expires: %(%R=^\$?:References: %R )Sender: Followup-To: %(%{FROM}=^\$?:From: %{FROM} )%(%{REPLYTO}=^\$?:Reply-To: %{REPLYTO} )Distribution: %(%i=^\$?%\"Distribution: \":%D) Organization: %o Keywords: %[keywords] Cc: %(%F=poster?%t:%(%F!=@?:%F))\n\n NEWSORG Either the name of your organization, or the name of a file containing the name of your organization. (For use at sites where the ORGANIZATION environmental variable is already in use. NEWSORG will override ORGANIZATION if both are present.) May be interpolated using "%o". Default: $orgname NEWSPOSTER (~) The shell command to be used by the follow-up commands (f and F) in order to allow you to enter and post a follow-up news article. If not set, trn handles the whole process and calls inews directly. See also NEWSHEADER. Default: Pnews -h %h NEWSSIGNATURE A signature file used by Pnews when generating news articles. Note that your inews may use a .signature file; you probably don't want to have both (it will result in two signatures being added to your articles). Using NEWSSIGNATURE will cause your signature to appear in your editor when composing an article, while .signature will not. Default: \$DOTDIR/.news_sig. NNTPSERVER The hostname of your news server. [This does not apply unless you are running the NNTP version of trn.] Default: $servername NO_ORIGINATOR If set, instructs inews not to add its Originator header. The value is unimportant. Default: not set NOPOSTRING The string to insert in the group summary line, which heads each article, for a group to which local posting is not allowed. See also MODSTRING. Default: " (no posting)" NORMSAVER (~) The shell command to save an article in the normal (non-mailbox) format. Default: %X/norm.saver %A %P %c %a %B %C "%b" ORGANIZATION Either the name of your organization, or the name of a file containing the name of your organization. (If NEWSORG is set, it will override ORGANIZATION.) May be interpolated using "%o". Default: $orgname PAGER The pager to be used by the internal help system. See also HELPPAGER. Default: $pager PAGESTOP If defined, contains a regular expression which matches article lines to be treated as form-feeds. There are at least two things you might want to do with this. To cause page breaks between articles in a digest, you might define it as "^--------". To force a page break before a signature, you could define it as "^-- \$". (Then, when you see "--" at the bottom of the page, you can skip the signature if you so desire by typing 'n' instead of space.) To do both, you could use "^--". If you want to break on more than one pattern, you can use "|" to separate the alternatives. There is some overhead involved in matching each line of the article against a regular expression. You might wish to use a baud-rate modifier to enable this feature only at low baud rates. Default: undefined PIPESAVER (%) The shell command to execute in order to accomplish a save to a pipe ("s | command" or "w | command"). The command typed by the user is substituted in as %b. Default: %(%B=^0\$?<%A:tail +%Bc %A |) %b Explanation: if %B is 0, the command is "<%A %b", otherwise the command is "tail +%Bc %A | %b". REPLYTO The value of the "Reply-To:" header, if needed. RNINIT This variable is used when initializing trn in rn-compatibility mode (see the -x switch) or when the TRNINIT variable isn't defined. See the TRNINIT variable for a description. RNMACRO (~) The name of the file containing macros and key mappings when running trn as rn. See also the TRNMACRO variable. Default: %./.rnmac SAVEDIR (~) The name of the directory to save to, if the save command does not specify a directory name. Default: If -/ is set: %p/%c If +/ is set: %p SAVENAME (%) The name of the file to save to, if the save command contains only a directory name. Default: If -/ is set: %a If +/ is set: %^C SAVESCOREFILE (~) A file where trn saves article scores, read when trn starts and written when trn exits. If you do not want to save scores, you can use "savescores off" in a local or global scorefile. Default: %+/savedscores SCOREDIR (~) The directory where scorefiles are kept. Default: %+/scores SELECTCHARS The characters used by the thread selector to select the associated thread of discussion. You can specify up to 64 visible characters, including upper- and lower-case letters, numbers, and many punctuation characters. Selection characters override command characters in the selector, but are not excluded from macro expansion, so be careful. Default: abdefgijlorstuvwxyz1234567890BCFGHIKMVW (You'll notice various characters are omitted to allow them to be typed as commands in the selector.) SHELL The name of your preferred shell. It will be used by the '!', 'S' and 'W' commands. Default: $prefshell SUBJLINE (%) Controls the format of the lines displayed by the '=' command at the article selection level. Default: %s SUPERSEDEHEADER (%) The format of the header file for a supersede article. Default: Newsgroups: %n Subject: %[subject] %(%{FROM}=^$?:From: %{FROM} Summary: %[summary] Expires: %[expires] References: %[references] From: %[from] Reply-To: %[reply-to] Supersedes: %i Sender: %[sender] Followup-To: %[followup-to] Distribution: %D Organization: %o Keywords: %[keywords]\n\n TERM Determines which termcap entry to use, unless TERMCAP contains the entry. TERMCAP Holds either the name of your termcap file, or a termcap entry. Default: /etc/termcap, normally. TRNINIT Default values for switches may be passed to trn by placing them in the TRNINIT variable (or RNINIT if you're starting trn in rn-compatibility mode). Any switch that is set in this way may be overruled on the command line, or via the '&' command from within trn. Binary-valued switches that are set with "-switch" may be unset using "+switch". If TRNINIT begins with a '/' it is assumed to be the name of a file containing switches. You can put comments in this file by preceding them with a '#' as long as this is the first character on a line or it follows some white-space (which delimits the switches in the file). If you want to set many environment variables but don't want to keep them all in your environment, or if the use of any of these variables conflicts with other programs, you can use this feature along with the -E switch to set the environment variables upon startup. Default: " ". TRNMACRO (~) The name of the file containing macros and key mappings. If the file is not found, the RNMACRO variable is used to look for your rn macros. For information on what to put into this file, see the CUSTOM MACROS section of the documentation. Default: %+/macros UNSHAR (~) The shell command to execute in order to accomplish the unshar'ing of a shell archive. Default: /bin/sh USER Your login name. May be interpolated using "%L". Default: \$LOGNAME VISUAL (~) The name of your editor. Default: \$EDITOR XTERMMOUSE If you set this variable to 'y' (yes), trn will enable the use of the xterm mouse in the selector if you are using an xterm. Once enabled, left-clicking on an item selects it while middle-clicking an item will move to that item. If you click the top (header) line of the selector it moves up a page. If you click the bottom (footer) line of the selector it executes the default command for the page (left click) or goes down a page (middle click). You can also use the right mouse button to move up or down a page by clicking in the upper-half or lower-half of the screen, respectively. YOUSAID (%) Gives the format of the attribution line in front of the quoted article included by an R (reply-by-mail) command. Default: In article %i you write: !GROK!THIS! trn-4.0-test77/filexp.SH0000644000000000000000000000173607113133015013535 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting filexp (with variable substitutions)" $spitshell >filexp <>filexp <<'!NO!SUBS!' : expand filename case "$1" in ~/*|~) echo $1 | $sed "s|~|${HOME-$LOGDIR}|" ;; ~~/*|~~) echo $1 | $sed "s|~~|${TRNPREFIX-$prefix}|" ;; ~*) if $test -f /bin/csh; then /bin/csh -f -c "glob $1" failed=$? echo "" exit $failed else name=`$expr x$1 : '..\([^/]*\)'` dir=`$sed -n -e "/^${name}:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\).*"'$'"/\1/" -e p -e q -e '}' &2 exit 1 fi case "$1" in */*) echo $dir/`$expr x$1 : '..[^/]*/\(.*\)'` ;; *) echo $dir ;; esac fi ;; *) echo $1 ;; esac !NO!SUBS! chmod +x filexp $eunicefix filexp trn-4.0-test77/filter.c0000644000000000000000000001275507113133015013446 0ustar rootroot/* filter.c -- external kill/score processing */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #ifdef USE_FILTER #include "ngdata.h" #include "util2.h" #include "hash.h" #include "cache.h" #include "list.h" #include "env.h" #include "trn.h" #include "final.h" #include "rt-ov.h" #include "nntpclient.h" #include "datasrc.h" #include "INTERN.h" #include "filter.h" #include "filter.ih" static FILE* filt_wr; static FILE* filt_rd; static int pipe1[2], pipe2[2]; static int filter_restarting; static int need_to_filter = 0; static int want_filter = 1; /* consider making an option later */ static pid_t filter_pid = 0; #ifdef FILTER_DEBUG extern FILE* filter_error_file; #endif void filter_init() { filt_wr = filt_rd = NULL; pipe1[0] = pipe1[1] = pipe2[0] = pipe2[1] = -1; filter_restarting = 0; } static void filter_restart() { char* filter; /* external filter file */ struct stat f_inode; /* stat buf */ static int recursing = 0; /* prevent unbounded recursion (e.g. filter_restart -> filter_nginit -> filter_send -> filter_restart) */ if (recursing) return; recursing = 1; filter = filexp(getval("FILTER", FILTERPROG)); want_filter = (strNE(filter,"NOFILTER") && stat(filter, &f_inode) == 0); if (!want_filter) { recursing = 0; return; } if (!(f_inode.st_mode & S_IXUSR)) { perror("opening filter"); recursing = 0; return; } if (verbose) { printf("\nStarting filter `%s'", filter); fflush(stdout); /* print it *now* */ } if (pipe(pipe1) < 0) perror("creating output pipe"); if (pipe(pipe2) < 0) perror("creating input pipe"); if ((filter_pid = fork()) < 0) perror("filter_restart"); else if (filter_pid > 0) { if ((filt_wr = fdopen(pipe1[1], "w")) == NULL) { perror("configuring output pipe"); filter_cleanup(); return; } if ((filt_rd = fdopen(pipe2[0], "r")) == NULL) { perror("configuring input pipe"); filter_cleanup(); return; } setvbuf(filt_wr, NULL, _IOLBF, BUFSIZ); /* line buffering */ setvbuf(filt_rd, NULL, _IOLBF, BUFSIZ); close(pipe1[0]); close(pipe2[1]); } else { close(STDIN_FILENO); close(STDOUT_FILENO); sigignore(SIGINT); if (dup2(pipe1[0], STDIN_FILENO) < 0) { perror("reopening child input stream"); finalize(1); } if (dup2(pipe2[1], STDOUT_FILENO) < 0) { perror("reopening child output stream"); finalize(1); } close(pipe1[1]); close(pipe2[0]); if (execl(filter, filter, NULL) < 0) { perror("switching to filter process"); finalize(1); } } filter_nginit(); recursing = 0; } void filter_nginit() { static int recursing = 0; char buf[1024]; if (recursing) return; need_to_filter = 0; if (!want_filter) return; recursing = 1; sprintf(buf, "newsgroup %s\n", ngname); if (filter_send(buf) == 0) { if (filter_recv(buf) != NULL) { Uchar* fieldflags = datasrc->fieldflags; int i; if (strncaseEQ(buf, "overview", 8)) { for (i = 1; i < OV_MAX_FIELDS; i++) { if (fieldflags[i] & (FF_HAS_FIELD | FF_CHECK4FIELD)) fieldflags[i] |= FF_FILTERSEND; } need_to_filter = 1; } else if (strncaseEQ(buf, "want", 4)) { char* s = buf + strlen(buf) - 1; if (*s == '\n') *s = '\0'; s = buf + 4; for (i = 1; i < OV_MAX_FIELDS; i++) fieldflags[i] &= ~FF_FILTERSEND; for (;;) { while (*s == ' ') s++; if (!*s) break; s = cpytill(buf,s,' '); i = ov_num(buf,(char*)NULL); if (i) { fieldflags[i] |= FF_FILTERSEND; need_to_filter = 1; } } } } } recursing = 0; } int filter(a) ART_NUM a; { char buf[256]; long score = 0, sc = 0; ARTICLE* ap; ART_NUM i; char* s; if (!need_to_filter) return 0; ap = article_find(a); if (ap && ap->refs) { Uchar* fieldflags = datasrc->fieldflags; sprintf(buf, "art %ld", ap->num); if (filter_send(buf) < 0) return 0; for (i = 1; i < OV_MAX_FIELDS; i++) { if (filter_send("\t") < 0) return 0; if (fieldflags[i] & FF_FILTERSEND) { if (fieldflags[i] & FF_HAS_HDR) { if (filter_send(ov_fieldname(i)) < 0 || filter_send(": ") < 0) return 0; } if ((s = ov_field(ap, i)) != NULL) { if (filter_send(s) < 0) return 0; } } } if (filter_send("\n") < 0 || filter_send("scores\n") < 0) return 0; while (filter_recv(buf) != NULL && strncaseNE(buf, "done", 4)) { sscanf(buf, "%ld %ld", &i, &sc); if (i == a) score = sc; #ifdef FILTER_DEBUG else { fprintf(filter_error_file, "article %d: filter returned %s", a, buf); } #endif } } return score; } static int filter_send(cmd) char* cmd; { if (filt_wr == NULL || fputs(cmd, filt_wr) == EOF) { filter_restart(); if (filt_wr == NULL) { #ifdef FILTER_DEBUG fprintf (stderr, "filt_wr is still NULL\n"); #endif return -1; } else if (fputs (cmd, filt_wr) == EOF) { fprintf(stderr, "Could not restart filter process.\n"); return -1; } } return 0; } static char* filter_recv(buf) char* buf; { if (filt_rd == NULL) return NULL; return (fgets(buf, 256, filt_rd)); } void filter_cleanup() { if (filter_pid > 0) { kill(filter_pid, SIGTERM); filter_pid = 0; } if (filt_wr != NULL) { fputs("bye\n", filt_wr); fclose(filt_wr); filt_wr = NULL; } if (filt_rd != NULL) { fclose(filt_rd); filt_rd = NULL; } pipe1[0] = pipe1[1] = 0; pipe2[0] = pipe2[1] = 0; } #endif /* USE_FILTER */ trn-4.0-test77/filter.h0000644000000000000000000000044607113133015013445 0ustar rootroot/* filter.h -- declarations for filter.c. */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void filter_init _((void)); void filter_nginit _((void)); int filter _((ART_NUM)); void filter_cleanup _((void)); trn-4.0-test77/filter.ih0000644000000000000000000000040407113133015013610 0ustar rootroot/* filter.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void filter_restart _((void)); static int filter_send _((char*)); static char* filter_recv _((char*)); trn-4.0-test77/final.c0000644000000000000000000001377211437640112013257 0ustar rootroot/* final.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "env.h" #include "util.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "term.h" #include "color.h" #include "ng.h" #include "init.h" #include "last.h" #include "ngdata.h" #include "nntpclient.h" #include "nntpinit.h" #include "datasrc.h" #include "nntp.h" #include "rcstuff.h" #include "artio.h" #include "intrp.h" #include "kfile.h" #ifdef SCORE #include "score.h" #include "scoresave.h" #endif #ifdef USE_TCL #include "tkstuff.h" #endif #ifdef USE_FILTER #include "filter.h" #endif #include "INTERN.h" #include "final.h" #ifndef sigmask #define sigmask(m) (1 << ((m)-1)) #endif void final_init() { #ifdef SIGTSTP sigset(SIGTSTP, stop_catcher); /* job control signals */ sigset(SIGTTOU, stop_catcher); /* job control signals */ sigset(SIGTTIN, stop_catcher); /* job control signals */ #endif sigset(SIGINT, int_catcher); /* always catch interrupts */ #ifdef SIGHUP sigset(SIGHUP, sig_catcher); /* and hangups */ #endif #ifdef SIGWINCH sigset(SIGWINCH, winch_catcher); #endif #ifdef SUPPORT_NNTP sigset(SIGPIPE,pipe_catcher); #endif #ifndef lint #ifdef SIGEMT sigignore(SIGEMT); /* Ignore EMT signals from old [t]rn's */ #endif #endif #ifdef DEBUG /* sometimes we WANT a core dump */ if (debug & DEB_COREDUMPSOK) return; #endif sigset(SIGILL, sig_catcher); #ifdef SIGTRAP sigset(SIGTRAP, sig_catcher); #endif sigset(SIGFPE, sig_catcher); #ifdef SIGBUS sigset(SIGBUS, sig_catcher); #endif sigset(SIGSEGV, sig_catcher); #ifdef SIGSYS sigset(SIGSYS, sig_catcher); #endif sigset(SIGTERM, sig_catcher); #ifdef SIGXCPU sigset(SIGXCPU, sig_catcher); #endif #ifdef SIGXFSZ sigset(SIGXFSZ, sig_catcher); #endif } void /* very much void */ finalize(status) int status; { #ifdef SUPPORT_NNTP int i; char* s; #endif #ifdef SCORE sc_sv_savefile(); /* save any scores from memory to disk */ #endif #ifdef USE_FILTER filter_cleanup(); #endif #ifdef KILLFILES update_thread_kfile(); #endif color_default(); termlib_reset(); if (bizarre) resetty(); xmouse_off(); /* turn off mouse tracking (if on) */ fflush(stdout); chdir(tmpdir); if (!checkflag) unuse_multirc(multirc); if (datasrc_list) { DATASRC* dp; for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) close_datasrc(dp); } #ifdef SUPPORT_NNTP for (i = 0; i < MAX_NNTP_ARTICLES; i++) { s = nntp_tmpname(i); UNLINK(s); } cleanup_nntp(); #endif if (headname) UNLINK(headname); #ifdef USE_TCL if (ttcl_running) ttcl_finalize(status); #endif if (status < 0) { sigset(SIGILL,SIG_DFL); #ifdef HAS_SIGBLOCK sigsetmask(sigblock(0) & ~(sigmask(SIGILL) | sigmask(SIGIOT))); #endif abort(); } #ifdef RESTORE_ORIGDIR if (origdir) chdir(origdir); #endif exit(status); } /* come here on interrupt */ Signal_t int_catcher(dummy) int dummy; { sigset(SIGINT,int_catcher); #ifdef DEBUG if (debug) write(2,"int_catcher\n",12); #endif if (!waiting) { if (int_count++) { /* was there already an interrupt? */ if (int_count == 3 || int_count > 5) { write(2,"\nBye-bye.\n",10); sig_catcher(0); /* emulate the other signals */ } write(2,"\n(Interrupt -- one more to kill trn)\n",37); } } } /* come here on signal other than interrupt, stop, or cont */ Signal_t sig_catcher(signo) int signo; { #ifdef VERBOSE static char* signame[] = { "", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "???" #ifdef SIGTSTP ,"STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "TINT", "XCPU", "XFSZ" #ifdef SIGPROF ,"VTALARM", "PROF" #endif #endif }; #endif #ifdef DEBUG if (debug) { printf("\nSIG%s--.newsrc not restored in debug\n",signame[signo]); finalize(-1); } #endif if (panic) { #ifdef HAS_SIGBLOCK sigsetmask(sigblock(0) & ~(sigmask(SIGILL) | sigmask(SIGIOT))); #endif abort(); } (void) sigset(SIGILL,SIG_DFL); panic = TRUE; /* disable terminal I/O */ if (doing_ng) { /* need we reconstitute rc line? */ yankback(); bits_to_rc(); /* then do so (hope this works) */ } doing_ng = FALSE; if (!write_newsrcs(multirc)) { /* write anything that's changed */ /*$$ get_old_newsrcs(multirc); ?? */ } #ifdef KILLFILES update_thread_kfile(); #endif #ifdef SIGHUP if (signo != SIGHUP) { #endif #ifdef VERBOSE IF(verbose) printf("\nCaught %s%s--.newsrc restored\n", signo ? "a SIG" : "an internal error", signame[signo]); ELSE #endif #ifdef TERSE printf("\nSignal %d--bye bye\n",signo); #endif #ifdef SIGHUP } #endif switch (signo) { #ifdef SIGBUS case SIGBUS: #endif case SIGILL: case SIGSEGV: finalize(-signo); } finalize(1); /* and blow up */ } #ifdef SUPPORT_NNTP Signal_t pipe_catcher(signo) int signo; { ;/*$$ we lost the current nntp connection */ sigset(SIGPIPE,pipe_catcher); } #endif /* come here on stop signal */ #ifdef SIGTSTP Signal_t stop_catcher(signo) int signo; { if (!waiting) { xmouse_off(); checkpoint_newsrcs(); /* good chance of crash while stopped */ if (bos_on_stop) { goto_xy(0, tc_LINES-1); putchar('\n') FLUSH; } termlib_reset(); resetty(); /* this is the point of all this */ #ifdef DEBUG if (debug) write(2,"stop_catcher\n",13); #endif fflush(stdout); sigset(signo,SIG_DFL); /* enable stop */ #ifdef HAS_SIGBLOCK sigsetmask(sigblock(0) & ~(1 << (signo-1))); #endif kill(0,signo); /* and do the stop */ savetty(); #ifdef MAILCALL mailcount = 0; /* force recheck */ #endif if (!panic) { if (!waiting) { termlib_init(); noecho(); /* set no echo */ crmode(); /* set cbreak mode */ forceme("\f"); /* cause a refresh */ /* (defined only if TIOCSTI defined) */ errno = 0; /* needed for getcmd */ } } xmouse_check(); } sigset(signo,stop_catcher); /* unenable the stop */ } #endif trn-4.0-test77/final.h0000644000000000000000000000155707113133015013255 0ustar rootroot/* final.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* cleanup status for fast exits */ EXT bool panic INIT(FALSE); /* we got hung up or something-- */ /* so leave tty alone */ EXT bool doing_ng INIT(FALSE); /* do we need to reconstitute */ /* current rc line? */ EXT char int_count INIT(0); /* how many interrupts we've had */ EXT bool bos_on_stop INIT(FALSE); /* set when handling the stop signal */ /* would leave the screen a mess */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void final_init _((void)); void finalize _((int)) #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR >= 5) __attribute__((noreturn)) #endif ; Signal_t int_catcher _((int)); Signal_t sig_catcher _((int)); #ifdef SUPPORT_NNTP Signal_t pipe_catcher _((int)); #endif #ifdef SIGTSTP Signal_t stop_catcher _((int)); #endif trn-4.0-test77/ftpgrab0000644000000000000000000000062007113133015013351 0ustar rootroot#!/bin/sh #This script requires six arguments: #1. site name #2. account name to use (usually "ftp") #3. password to use (usually user@site.net) #4. directory to CD to #5. filename on remote system #6. filename on local system #Localization note: if ftp is not always in a users path, you may want #to use a full path name below. ftp -n<ht; tbl->ht_size = size; tbl->ht_magic = HASHMAG; tbl->ht_cmp = (cmpfunc == NULL? default_cmp: cmpfunc); tbl->ht_addr = aap->hepa; return tbl; } /* Free all the memory associated with tbl, erase the pointers to it, and ** invalidate tbl to prevent further use via other pointers to it. */ void hashdestroy(tbl) register HASHTABLE* tbl; { register unsigned idx; register HASHENT* hp; register HASHENT* next; register HASHENT** hepp; register int tblsize; if (BADTBL(tbl)) return; tblsize = tbl->ht_size; hepp = tbl->ht_addr; for (idx = 0; idx < tblsize; idx++) { for (hp = hepp[idx]; hp != NULL; hp = next) { next = hp->he_next; hp->he_next = NULL; hefree(hp); } hepp[idx] = NULL; } tbl->ht_magic = 0; /* de-certify this table */ tbl->ht_addr = NULL; free((char*)tbl); } void hashstore(tbl, key, keylen, data) register HASHTABLE* tbl; char* key; int keylen; HASHDATUM data; { register HASHENT* hp; register HASHENT** nextp; nextp = hashfind(tbl, key, keylen); hp = *nextp; if (hp == NULL) { /* absent; allocate an entry */ hp = healloc(); hp->he_next = NULL; hp->he_keylen = keylen; *nextp = hp; /* append to hash chain */ } hp->he_data = data; /* supersede any old data for this key */ } void hashdelete(tbl, key, keylen) register HASHTABLE* tbl; char* key; int keylen; { register HASHENT* hp; register HASHENT** nextp; nextp = hashfind(tbl, key, keylen); hp = *nextp; if (hp == NULL) /* absent */ return; *nextp = hp->he_next; /* skip this entry */ hp->he_next = NULL; hp->he_data.dat_ptr = NULL; hefree(hp); } HASHENT** slast_nextp; int slast_keylen; HASHDATUM /* data corresponding to key */ hashfetch(tbl, key, keylen) register HASHTABLE* tbl; char* key; int keylen; { register HASHENT* hp; register HASHENT** nextp; static HASHDATUM errdatum = { NULL, 0 }; nextp = hashfind(tbl, key, keylen); slast_nextp = nextp; slast_keylen = keylen; hp = *nextp; if (hp == NULL) /* absent */ return errdatum; return hp->he_data; } void hashstorelast(data) HASHDATUM data; { register HASHENT* hp; hp = *slast_nextp; if (hp == NULL) { /* absent; allocate an entry */ hp = healloc(); hp->he_next = NULL; hp->he_keylen = slast_keylen; *slast_nextp = hp; /* append to hash chain */ } hp->he_data = data; /* supersede any old data for this key */ } /* Visit each entry by calling nodefunc at each, with keylen, data, ** and extra as arguments. */ void hashwalk(tbl, nodefunc, extra) HASHTABLE* tbl; register int (*nodefunc) _((int,HASHDATUM*,int)); register int extra; { register unsigned idx; register HASHENT* hp; register HASHENT* next; register HASHENT** hepp; register int tblsize; if (BADTBL(tbl)) return; hepp = tbl->ht_addr; tblsize = tbl->ht_size; for (idx = 0; idx < tblsize; idx++) { slast_nextp = &hepp[idx]; for (hp = *slast_nextp; hp != NULL; hp = next) { next = hp->he_next; if ((*nodefunc)(hp->he_keylen, &hp->he_data, extra) < 0) { *slast_nextp = next; hp->he_next = NULL; hefree(hp); } else slast_nextp = &hp->he_next; } } } /* The returned value is the address of the pointer that refers to the ** found object. Said pointer may be NULL if the object was not found; ** if so, this pointer should be updated with the address of the object ** to be inserted, if insertion is desired. */ static HASHENT** hashfind(tbl, key, keylen) register HASHTABLE* tbl; char* key; register int keylen; { register HASHENT* hp; register HASHENT* prevhp = NULL; register HASHENT** hepp; register unsigned size; if (BADTBL(tbl)) { fputs("Hash table is invalid.",stderr); finalize(1); } size = tbl->ht_size; hepp = &tbl->ht_addr[hash(key,keylen) % size]; for (hp = *hepp; hp != NULL; prevhp = hp, hp = hp->he_next) { if (hp->he_keylen == keylen && !(*tbl->ht_cmp)(key, keylen, hp->he_data)) break; } /* assert: *(returned value) == hp */ return (prevhp == NULL? hepp: &prevhp->he_next); } static unsigned /* not yet taken modulus table size */ hash(key, keylen) register char* key; register int keylen; { register unsigned hash = 0; while (keylen--) hash += *key++; return hash; } static int default_cmp(key, keylen, data) char* key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, data.dat_ptr, keylen); } static HASHENT* healloc() /* allocate a hash entry */ { register HASHENT* hp; if (hereuse == NULL) { int i; /* make a nice big block of hashents to play with */ hp = (HASHENT*)safemalloc(HEBLOCKSIZE * sizeof (HASHENT)); /* set up the pointers within the block */ for (i = 0; i < HEBLOCKSIZE-1; i++) (hp+i)->he_next = hp + i + 1; /* The last block is the end of the list */ (hp+i)->he_next = NULL; hereuse = hp; /* start of list is the first item */ reusables += HEBLOCKSIZE; } /* pull the first reusable one off the pile */ hp = hereuse; hereuse = hereuse->he_next; hp->he_next = NULL; /* prevent accidents */ reusables--; return hp; } static void hefree(hp) /* free a hash entry */ register HASHENT* hp; { #ifdef HASH_FREE_ENTRIES if (reusables >= RETAIN) /* compost heap is full? */ free((char*)hp); /* yup, just pitch this one */ else { /* no, just stash for reuse */ ++reusables; hp->he_next = hereuse; hereuse = hp; } #else /* always add to list */ ++reusables; hp->he_next = hereuse; hereuse = hp; #endif } /* * Copyright (c) 1992 Geoffrey Collyer * All rights reserved. * Written by Geoffrey Collyer. * * This software is not subject to any license of the American Telephone * and Telegraph Company, the Regents of the University of California, or * the Free Software Foundation. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. The author is not responsible for the consequences of use of this * software, no matter how awful, even if they arise from flaws in it. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Since few users ever read sources, * credits must appear in the documentation. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. Since few users * ever read sources, credits must appear in the documentation. * * 4. This notice may not be removed or altered. */ trn-4.0-test77/hash.h0000644000000000000000000000127307113133015013102 0ustar rootroot/* * general-purpose in-core hashing */ /* This file is an altered version of a set of hash routines by * Geoffrey Collyer. See hash.c for his copyright. */ struct hashdatum { char* dat_ptr; unsigned dat_len; }; #define HASH_DEFCMPFUNC (int(*)_((char*,int,HASHDATUM)))NULL /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ HASHTABLE* hashcreate _((unsigned,int(*) _((char*,int,HASHDATUM)))); void hashdestroy _((HASHTABLE*)); void hashstore _((HASHTABLE*,char*,int,HASHDATUM)); void hashdelete _((HASHTABLE*,char*,int)); HASHDATUM hashfetch _((HASHTABLE*,char*,int)); void hashstorelast _((HASHDATUM)); void hashwalk _((HASHTABLE*,int(*) _((int,HASHDATUM*,int)),int)); trn-4.0-test77/hash.ih0000644000000000000000000000125607113133015013254 0ustar rootroot/* hash.ih */ #define BADTBL(tbl) ((tbl) == NULL || (tbl)->ht_magic != HASHMAG) #define HASHMAG ((char)0257) struct hashent { HASHENT* he_next; /* in hash chain */ HASHDATUM he_data; int he_keylen; /* to help verify a match */ }; struct hashtable { HASHENT** ht_addr; /* array of HASHENT pointers */ unsigned ht_size; char ht_magic; int (*ht_cmp) _((char*,int,HASHDATUM)); }; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static HASHENT** hashfind _((HASHTABLE*,char*,int)); static unsigned hash _((char*,int)); static int default_cmp _((char*,int,HASHDATUM)); static HASHENT* healloc _((void)); static void hefree _((HASHENT*)); trn-4.0-test77/head.c0000644000000000000000000003365707116177123013101 0ustar rootroot/* head.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "artio.h" #include "hash.h" #include "cache.h" #include "ng.h" #include "ngdata.h" #include "nntpclient.h" #include "nntpinit.h" #include "datasrc.h" #include "nntp.h" #include "util.h" #include "util2.h" #include "rthread.h" #include "rt-process.h" #include "rt-util.h" #include "final.h" #include "parsedate.h" #ifdef SCAN #include "mempool.h" #endif #include "INTERN.h" #include "head.h" bool first_one; /* is this the 1st occurance of this header line? */ #ifdef SUPPORT_NNTP bool reading_nntp_header; #endif static short htypeix[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; void head_init() { register int i; for (i = HEAD_FIRST+1; i < HEAD_LAST; i++) htypeix[*htype[i].name - 'a'] = i; user_htype_max = 10; user_htype = (USER_HEADTYPE*)safemalloc(user_htype_max * sizeof (USER_HEADTYPE)); user_htype[user_htype_cnt++].name = "*"; headbuf_size = LBUFLEN * 8; headbuf = safemalloc(headbuf_size); } #ifdef DEBUG void dumpheader(where) char* where; { register int i; printf("header: %ld %s", (long)parsed_art, where); for (i = HEAD_FIRST-1; i < HEAD_LAST; i++) { printf("%15s %4ld %4ld %03o\n",htype[i].name, (long)htype[i].minpos, (long)htype[i].maxpos, htype[i].flags) FLUSH; } } #endif int set_line_type(bufptr,colon) char* bufptr; register char* colon; { register char* t; register char* f; register int i, len; if (colon-bufptr > sizeof msg) return SOME_LINE; for (t = msg, f = bufptr; f < colon; f++, t++) { /* guard against space before : */ if (isspace(*f)) return SOME_LINE; *t = isupper(*f) ? tolower(*f) : *f; } *t = '\0'; f = msg; /* get msg into a register */ len = t - f; /* now scan the HEADTYPE table, backwards so we don't have to supply an * extra terminating value, using first letter as index, and length as * optimization to avoid calling subroutine strEQ unnecessarily. Hauls. */ if (*f >= 'a' && *f <= 'z') { for (i = htypeix[*f - 'a']; *htype[i].name == *f; i--) { if (len == htype[i].length && strEQ(f, htype[i].name)) return i; } if (len == htype[CUSTOM_LINE].length && strEQ(f, htype[CUSTOM_LINE].name)) return CUSTOM_LINE; for (i = user_htypeix[*f - 'a']; *user_htype[i].name == *f; i--) { if (len >= user_htype[i].length && strnEQ(f, user_htype[i].name, user_htype[i].length)) { if (user_htype[i].flags & HT_HIDE) return HIDDEN_LINE; return SHOWN_LINE; } } } return SOME_LINE; } int get_header_num(s) char* s; { char* end = s + strlen(s); int i; i = set_line_type(s, end); /* Sets msg to lower-cased header name */ if (i <= SOME_LINE && i != CUSTOM_LINE) { char* bp; char ch; if (htype[CUSTOM_LINE].name != nullstr) free(htype[CUSTOM_LINE].name); htype[CUSTOM_LINE].name = savestr(msg); htype[CUSTOM_LINE].length = end - s; htype[CUSTOM_LINE].flags = htype[i].flags; htype[CUSTOM_LINE].minpos = -1; htype[CUSTOM_LINE].maxpos = 0; for (bp = headbuf; *bp; bp = end) { if (!(end = index(bp,'\n')) || end == bp) break; ch = *++end; *end = '\0'; s = index(bp,':'); *end = ch; if (!s || (i = set_line_type(bp,s)) != CUSTOM_LINE) continue; htype[CUSTOM_LINE].minpos = bp - headbuf; while (*end == ' ' || *end == '\t') { if (!(end = index(end, '\n'))) { end = bp + strlen(bp); break; } end++; } htype[CUSTOM_LINE].maxpos = end - headbuf; break; } i = CUSTOM_LINE; } return i; } void start_header(artnum) ART_NUM artnum; { register int i; #ifdef DEBUG if (debug & DEB_HEADER) dumpheader("start_header\n"); #endif for (i = 0; i < HEAD_LAST; i++) { htype[i].minpos = -1; htype[i].maxpos = 0; } in_header = SOME_LINE; first_one = FALSE; parsed_art = artnum; parsed_artp = article_ptr(artnum); } void end_header_line() { if (first_one) { /* did we just pass 1st occurance? */ first_one = FALSE; /* remember where line left off */ htype[in_header].maxpos = artpos; if (htype[in_header].flags & HT_CACHED) { if (!get_cached_line(parsed_artp, in_header, TRUE)) { int start = htype[in_header].minpos + htype[in_header].length + 1; MEM_SIZE size; while (headbuf[start] == ' ' || headbuf[start] == '\t') start++; size = artpos - start + 1 - 1; /* pre-strip newline */ if (in_header == SUBJ_LINE) set_subj_line(parsed_artp,headbuf+start,size-1); else { char* s = safemalloc(size); safecpy(s,headbuf+start,size); set_cached_line(parsed_artp,in_header,s); } } } } } bool parseline(art_buf,newhide,oldhide) char* art_buf; int newhide, oldhide; { char* s; if (*art_buf == ' ' || *art_buf == '\t') /* continuation line? */ return oldhide; end_header_line(); s = index(art_buf,':'); if (s == NULL) { /* is it the end of the header? */ #ifdef SUPPORT_NNTP /* Did NNTP ship us a mal-formed header line? */ if (reading_nntp_header && *art_buf && *art_buf != '\n') { in_header = SOME_LINE; return newhide; } #endif in_header = PAST_HEADER; } else { /* it is a new header line */ in_header = set_line_type(art_buf,s); first_one = (htype[in_header].minpos < 0); if (first_one) { htype[in_header].minpos = artpos; if (in_header == DATE_LINE) { if (!parsed_artp->date) parsed_artp->date = parsedate(art_buf+6); } } #ifdef DEBUG if (debug & DEB_HEADER) dumpheader(art_buf); #endif if (htype[in_header].flags & HT_HIDE) return newhide; } return FALSE; /* don't hide this line */ } void end_header() { register ARTICLE* ap = parsed_artp; end_header_line(); in_header = PAST_HEADER; /* just to be sure */ if (!ap->subj) set_subj_line(ap,"",6); #ifdef SUPPORT_NNTP if (reading_nntp_header) { reading_nntp_header = FALSE; htype[PAST_HEADER].minpos = artpos + 1; /* nntp_body will fix this */ } else #endif htype[PAST_HEADER].minpos = tellart(); /* If there's no References: line, then the In-Reply-To: line may give us ** more information. */ if (ThreadedGroup && (!(ap->flags & AF_THREADED) || htype[INREPLY_LINE].minpos >= 0)) { if (valid_article(ap)) { ARTICLE* artp_hold = artp; char* references = fetchlines(parsed_art, REFS_LINE); char* inreply = fetchlines(parsed_art, INREPLY_LINE); int reflen = strlen(references) + 1; growstr(&references, &reflen, reflen + strlen(inreply) + 1); safecat(references, inreply, reflen); thread_article(ap, references); free(inreply); free(references); artp = artp_hold; check_poster(ap); } } else if (!(ap->flags & AF_CACHED)) { cache_article(ap); check_poster(ap); } } /* read the header into memory and parse it if we haven't already */ bool parseheader(artnum) ART_NUM artnum; { register char* bp; register int len; bool had_nl = TRUE; int found_nl; if (parsed_art == artnum) return TRUE; if (artnum > lastart) return FALSE; spin(20); #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) { char *s = nntp_artname(artnum, FALSE); if (s) { if (!artopen(artnum,(ART_POS)0)) return FALSE; } else if (nntp_header(artnum) <= 0) { uncache_article(article_ptr(artnum),FALSE); return FALSE; } else reading_nntp_header = TRUE; } #endif ElseIf (!artopen(artnum,(ART_POS)0)) return FALSE; start_header(artnum); artpos = 0; bp = headbuf; while (in_header) { if (headbuf_size < artpos + LBUFLEN) { len = bp - headbuf; headbuf_size += LBUFLEN * 4; headbuf = saferealloc(headbuf,headbuf_size); bp = headbuf + len; } #ifdef SUPPORT_NNTP if (reading_nntp_header) { found_nl = nntp_gets(bp,LBUFLEN); if (found_nl < 0) strcpy(bp,"."); /*$$*/ if (had_nl && *bp == '.') { if (!bp[1]) { *bp++ = '\n'; /* tag the end with an empty line */ break; } strcpy(bp,bp+1); } len = strlen(bp); if (found_nl) bp[len++] = '\n'; bp[len] = '\0'; } else #endif { if (readart(bp,LBUFLEN) == NULL) break; len = strlen(bp); found_nl = (bp[len-1] == '\n'); } if (had_nl) parseline(bp,FALSE,FALSE); had_nl = found_nl; artpos += len; bp += len; } *bp = '\0'; end_header(); return TRUE; } /* get a header line from an article */ char* fetchlines(artnum,which_line) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ { char* s; char* t; register ART_POS firstpos; register ART_POS lastpos; int size; /* Only return a cached line if it isn't the current article */ if (parsed_art != artnum) { /* If the line is not in the cache, this will parse the header */ s = fetchcache(artnum,which_line,FILL_CACHE); if (s) return savestr(s); } if ((firstpos = htype[which_line].minpos) < 0) return savestr(nullstr); firstpos += htype[which_line].length + 1; lastpos = htype[which_line].maxpos; size = lastpos - firstpos; t = headbuf + firstpos; while (*t == ' ' || *t == '\t') t++, size--; #ifdef DEBUG if (debug && (size < 1 || size > 1000)) { printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos); fgets(cmd_buf, sizeof cmd_buf, stdin); } #endif s = safemalloc((MEM_SIZE)size); safecpy(s,t,size); return s; } /* (strn) like fetchlines, but for memory pools */ #ifdef SCAN char* mp_fetchlines(artnum,which_line,pool) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ int pool; /* which memory pool to use */ { char* s; char* t; register ART_POS firstpos; register ART_POS lastpos; int size; /* Only return a cached line if it isn't the current article */ if (parsed_art != artnum) { /* If the line is not in the cache, this will parse the header */ s = fetchcache(artnum,which_line,FILL_CACHE); if (s) return mp_savestr(s,pool); } if ((firstpos = htype[which_line].minpos) < 0) return mp_savestr(nullstr,pool); firstpos += htype[which_line].length + 1; lastpos = htype[which_line].maxpos; size = lastpos - firstpos; t = headbuf + firstpos; while (*t == ' ' || *t == '\t') t++, size--; #ifdef DEBUG if (debug && (size < 1 || size > 1000)) { printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos); fgets(cmd_buf, sizeof cmd_buf, stdin); } #endif s = mp_malloc(size,pool); safecpy(s,t,size); return s; } #endif /* prefetch a header line from one or more articles */ char* prefetchlines(artnum,which_line,copy) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ bool_int copy; /* do you want it savestr()ed? */ { char* s; char* t; register ART_POS firstpos; register ART_POS lastpos; int size; #ifdef SUPPORT_NNTP if ((datasrc->flags & DF_REMOTE) && parsed_art != artnum) { ARTICLE* ap; int size; register ART_NUM num, priornum, lastnum; bool cached; bool hasxhdr = TRUE; s = fetchcache(artnum,which_line,DONT_FILL_CACHE); if (s) { if (copy) s = savestr(s); return s; } spin(20); if (copy) s = safemalloc((MEM_SIZE)(size = LBUFLEN)); else { s = cmd_buf; size = sizeof cmd_buf; } *s = '\0'; priornum = artnum-1; if ((cached = (htype[which_line].flags & HT_CACHED)) != 0) { lastnum = artnum + PREFETCH_SIZE - 1; if (lastnum > lastart) lastnum = lastart; sprintf(ser_line,"XHDR %s %ld-%ld",htype[which_line].name, artnum,lastnum); } else { lastnum = artnum; sprintf(ser_line,"XHDR %s %ld",htype[which_line].name,artnum); } if (nntp_command(ser_line) <= 0) finalize(1); /*$$*/ if (nntp_check() > 0) { char* line; char* last_buf = ser_line; MEM_SIZE last_buflen = sizeof ser_line; for (;;) { line = nntp_get_a_line(last_buf,last_buflen,last_buf!=ser_line); # ifdef DEBUG if (debug & DEB_NNTP) printf("<%s", line? line : "") FLUSH; # endif if (nntp_at_list_end(line)) break; last_buf = line; last_buflen = buflen_last_line_got; if ((t = index(line, '\r')) != NULL) *t = '\0'; if (!(t = index(line, ' '))) continue; t++; num = atol(line); if (num < artnum || num > lastnum) continue; if (!(datasrc->flags & DF_XHDR_BROKEN)) { while ((priornum = article_next(priornum)) < num) uncache_article(article_ptr(priornum),FALSE); } ap = article_find(num); if (which_line == SUBJ_LINE) set_subj_line(ap, t, strlen(t)); else if (cached) set_cached_line(ap, which_line, savestr(t)); if (num == artnum) safecat(s,t,size); } if (last_buf != ser_line) free(last_buf); } else { hasxhdr = FALSE; lastnum = artnum; if (!parseheader(artnum)) { fprintf(stderr,"\nBad NNTP response.\n"); finalize(1); } s = fetchlines(artnum,which_line); } if (hasxhdr && !(datasrc->flags & DF_XHDR_BROKEN)) { for (priornum = article_first(priornum); priornum < lastnum; priornum = article_next(priornum)) uncache_article(article_ptr(priornum),FALSE); } if (copy) s = saferealloc(s, (MEM_SIZE)strlen(s)+1); return s; } #endif /* SUPPORT_NNTP */ /* Only return a cached line if it isn't the current article */ s = NULL; if (parsed_art != artnum) s = fetchcache(artnum,which_line,FILL_CACHE); if (parsed_art == artnum && (firstpos = htype[which_line].minpos) < 0) s = nullstr; if (s) { if (copy) s = savestr(s); return s; } firstpos += htype[which_line].length + 1; lastpos = htype[which_line].maxpos; size = lastpos - firstpos; t = headbuf + firstpos; while (*t == ' ' || *t == '\t') t++, size--; if (copy) s = safemalloc((MEM_SIZE)size); else { /* hope this is okay--we're */ s = cmd_buf; /* really scraping for space here */ if (size > sizeof cmd_buf) size = sizeof cmd_buf; } safecpy(s,t,size); return s; } trn-4.0-test77/head.h0000644000000000000000000001343407113133015013062 0ustar rootroot/* head.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #define HEAD_FIRST SOME_LINE /* types of header lines (if only C really believed in enums) * (These must stay in alphabetic order at least in the first letter. * Within each letter it helps to arrange in increasing likelihood.) */ #define PAST_HEADER 0 /* body */ #define SHOWN_LINE (PAST_HEADER+1) /* unrecognized but shown */ #define HIDDEN_LINE (SHOWN_LINE+1) /* unrecognized but hidden */ #define CUSTOM_LINE (HIDDEN_LINE+1) /* to isolate a custom line */ #define SOME_LINE (CUSTOM_LINE+1) /* default for unrecognized */ #define AUTHOR_LINE (SOME_LINE+1) /* Author */ #define BYTES_LINE (AUTHOR_LINE+1) /* Bytes */ #define CONTNAME_LINE (BYTES_LINE+1) /* Content-Name */ #define CONTDISP_LINE (CONTNAME_LINE+1) /* Content-Disposition */ #define CONTLEN_LINE (CONTDISP_LINE+1) /* Content-Length */ #define CONTXFER_LINE (CONTLEN_LINE+1) /* Content-Transfer-Encoding */ #define CONTTYPE_LINE (CONTXFER_LINE+1) /* Content-Type */ #define DIST_LINE (CONTTYPE_LINE+1) /* distribution */ #define DATE_LINE (DIST_LINE+1) /* date */ #define EXPIR_LINE (DATE_LINE+1) /* expires */ #define FOLLOW_LINE (EXPIR_LINE+1) /* followup-to */ #define FROM_LINE (FOLLOW_LINE+1) /* from */ #define INREPLY_LINE (FROM_LINE+1) /* in-reply-to */ #define KEYW_LINE (INREPLY_LINE+1) /* keywords */ #define LINES_LINE (KEYW_LINE+1) /* lines */ #define MIMEVER_LINE (LINES_LINE+1) /* mime-version */ #define MSGID_LINE (MIMEVER_LINE+1) /* message-id */ #define NGS_LINE (MSGID_LINE+1) /* newsgroups */ #define PATH_LINE (NGS_LINE+1) /* path */ #define RVER_LINE (PATH_LINE+1) /* relay-version */ #define REPLY_LINE (RVER_LINE+1) /* reply-to */ #define REFS_LINE (REPLY_LINE+1) /* references */ #define SUMRY_LINE (REFS_LINE+1) /* summary */ #define SUBJ_LINE (SUMRY_LINE+1) /* subject */ #define XREF_LINE (SUBJ_LINE+1) /* xref */ #define HEAD_LAST (XREF_LINE+1) /* total # of headers */ struct headtype { char* name; /* header line identifier */ ART_POS minpos; /* pointer to beginning of line in article */ ART_POS maxpos; /* pointer to end of line in article */ char length; /* the header's string length */ char flags; /* the header's flags */ }; struct user_headtype { char* name; /* user-defined headers */ char length; /* the header's string length */ char flags; /* the header's flags */ }; #define HT_HIDE 0x01 /* hide this line */ #define HT_MAGIC 0x02 /* do any special processing on this line */ #define HT_CACHED 0x04 /* this information is cached article data */ #define HT_DEFHIDE 0x08 /* hidden by default */ #define HT_DEFMAGIC 0x10 /* magic by default */ #define HT_MAGICOK 0x20 /* magic even possible for line */ /* This array must stay in the same order as the list above */ #ifndef DOINIT EXT struct headtype htype[HEAD_LAST]; #else #define HIDDEN (HT_HIDE|HT_DEFHIDE) #define MAGIC_ON (HT_MAGICOK|HT_MAGIC|HT_DEFMAGIC) #define MAGIC_OFF (HT_MAGICOK) #ifdef DBM_XREFS #define XREF_CACHED 0 #define NGS_CACHED HT_CACHED #else #define XREF_CACHED HT_CACHED #define NGS_CACHED 0 #endif #ifdef USE_FILTER #define FILT_CACHED HT_CACHED #else #define FILT_CACHED 0 #endif struct headtype htype[HEAD_LAST] = { /* name minpos maxpos length flag */ {nullstr,/*BODY*/ 0, 0, 0, 0 }, {nullstr,/*SHOWN*/ 0, 0, 0, 0 }, {nullstr,/*HIDDEN*/ 0, 0, 0, HIDDEN }, {nullstr,/*CUSTOM*/ 0, 0, 0, 0 }, {"unrecognized", 0, 0, 12, HIDDEN }, {"author", 0, 0, 6, 0 }, {"bytes", 0, 0, 5, HIDDEN|HT_CACHED}, {"content-name", 0, 0, 12, HIDDEN }, {"content-disposition", 0, 0, 19, HIDDEN }, {"content-length", 0, 0, 14, HIDDEN }, {"content-transfer-encoding", 0, 0, 25, HIDDEN }, {"content-type", 0, 0, 12, HIDDEN }, {"distribution", 0, 0, 12, 0 }, {"date", 0, 0, 4, MAGIC_ON }, {"expires", 0, 0, 7, HIDDEN|MAGIC_ON }, {"followup-to", 0, 0, 11, 0 }, {"from", 0, 0, 4, MAGIC_OFF|HT_CACHED}, {"in-reply-to", 0, 0, 11, HIDDEN }, {"keywords", 0, 0, 8, 0 }, {"lines", 0, 0, 5, HT_CACHED }, {"mime-version", 0, 0, 12, MAGIC_ON|HIDDEN }, {"message-id", 0, 0, 10, HIDDEN|HT_CACHED}, {"newsgroups", 0, 0, 10, MAGIC_ON|HIDDEN|NGS_CACHED}, {"path", 0, 0, 4, HIDDEN }, {"relay-version", 0, 0, 13, HIDDEN }, {"reply-to", 0, 0, 8, 0 }, {"references", 0, 0, 10, HIDDEN|FILT_CACHED}, {"summary", 0, 0, 7, 0 }, {"subject", 0, 0, 7, MAGIC_ON|HT_CACHED}, {"xref", 0, 0, 4, HIDDEN|XREF_CACHED}, }; #undef HIDDEN #undef MAGIC_ON #undef MAGIC_OFF #undef NGS_CACHED #undef XREF_CACHED #endif EXT struct user_headtype* user_htype INIT(NULL); EXT short user_htypeix[26]; EXT int user_htype_cnt INIT(0); EXT int user_htype_max INIT(0); EXT ART_NUM parsed_art INIT(0); /* the article number we've parsed */ EXT ARTICLE* parsed_artp INIT(NULL); /* the article ptr we've parsed */ EXT int in_header INIT(0); /* are we decoding the header? */ EXT char* headbuf; EXT long headbuf_size; #define PREFETCH_SIZE 5 #define fetchsubj(artnum,copy) prefetchlines(artnum,SUBJ_LINE,copy) #define fetchfrom(artnum,copy) prefetchlines(artnum,FROM_LINE,copy) #ifdef DBM_XREFS #define fetchxref(artnum,copy) prefetchlines(artnum,NGS_LINE,copy) #else #define fetchxref(artnum,copy) prefetchlines(artnum,XREF_LINE,copy) #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void head_init _((void)); #ifdef DEBUG void dumpheader _((char*)); #endif int set_line_type _((char*,char*)); int get_header_num _((char*)); void start_header _((ART_NUM)); void end_header_line _((void)); bool parseline _((char*,int,int)); void end_header _((void)); bool parseheader _((ART_NUM)); char* fetchlines _((ART_NUM,int)); #ifdef SCAN char* mp_fetchlines _((ART_NUM,int,int)); #endif char* prefetchlines _((ART_NUM,int,bool_int)); trn-4.0-test77/help.c0000644000000000000000000011366511437640112013120 0ustar rootroot/* help.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "ngdata.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_start(); if ((cmd = print_lines("\ Paging commands:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ SP Display the next page.\n\ x Change the current article filter (between ROT-13 and WWW viewer).\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=current filter).\n\ ",NOMARKING)) || (cmd = print_lines("\ b Back up one page.\n\ ^E Display the last page of the article.\n\ ^L,X Refresh the screen (X=rot13).\n\ _C Switch characterset conversion.\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\ h, ? Enter online help browser.\n\ H This help.\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)) || #ifdef SCAN (cmd = print_lines("\ ; Enter article-scan mode.\n\ ",NOMARKING)) || #endif #ifdef SCORE (cmd = print_lines("\ Scoring commands are enabled.\n\ (Type h at end of article for description of Scoring commands.)\n\ ",NOMARKING)) || #endif (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_start(); 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: a, e, j, m, M, s, S, t, T, |, +, ++, -, and --.\n\ :cmd Perform a command on all the selected articles.\n\ ::cmd Perform a command on all non-selected articles.\n\ :.cmd Perform a command on the current thread or its selected articles.\n\ ::.cmd Perform a command on the unselected articles in the current thread.\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, t to scan from the top of the group.)\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. If the m command is first, it implies 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\ ^F Forward article through net mail.\n\ e dir{|command}\n\ Extract a mime, uue, or shar file to \"dir\" (using 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\ a View article attachments.\n\ ",NOMARKING)) || (cmd = print_lines("\ C Cancel this article, if yours.\n\ ^R,v Restart article (v=verbose).\n\ x Change the current article filter (between ROT-13 and WWW viewer).\n\ ^X View article using current filter (ROT-13 (default) or WWW viewer).\n\ _C Switch characterset conversion.\n\ c Catch up (mark all articles as read).\n\ b Back up one page.\n\ ",NOMARKING)) || (cmd = print_lines("\ ^E Display the last page of the article.\n\ ^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\ & Start the option selector.\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)) || #ifdef SCAN (cmd = print_lines("\ ; Enter article-scan mode.\n\ ",NOMARKING)) || #endif (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)) #ifdef SCORE || (cmd = print_lines("\ Scoring commands:\n\ ",STANDOUT)) || (cmd = print_lines("\ \"\" \n\ Adds to the local (this newsgroup's) scorefile.\n\ \"* \n\ Adds to the global scorefile.\n\ \"C \n\ Adds to the scorefile represented by the abbreviation C.\n\ \"? List all scorefile abbreviations and the filenames they represent.\n\ \"! \n\ Adds to the internal list of scoring rules as if it was read\n\ from a file. Articles are rescored after this command.\n\ 'd Debugging dump. (Will go away in later versions.)\n\ 'e \n\ Edits the scorefile named by the following file abbreviation\n\ character or full filename. If no arguments are given, edits the\n\ local scorefile (the one for the current newsgroup).\n\ 'f Continue scoring articles until interrupted.\n\ 'r Discard old scoring data and rescore from score files.\n\ 's Show scoring rules which contributed to this article's score.\n\ 't Test command: may do varying things. (will usually be disabled)\n\ ",NOMARKING)) #endif /* SCORE */ ) return cmd; #endif return 0; } int help_ng() { int cmd; #ifdef NGHELP doshell(sh,filexp(NGHELP)); #else page_start(); if ((cmd = print_lines("\ Newsgroup Selection commands:\n\ ",STANDOUT)) != 0) return cmd; if (ngptr) { 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\ ",NOMARKING)) || #ifdef SCAN (cmd = print_lines("\ ; Enter article-scan mode for this newsgroup.\n\ ",NOMARKING)) || #endif (cmd = print_lines("\ u Unsubscribe from this newsgroup.\n\ ^N Switch to next news source (the numbered GROUPs in ~/.trn/access).\n\ ^P Switch to the previous news source.\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 end of newsgroups.\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\ O pat Like o, but skip empty groups.\n\ a pat Like o, but also scans for unsubscribed newsgroups matching pattern.\n\ L List current .newsrc.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Start the option selector.\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("\ ` switch to the newsgroup selector.\n\ 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()) != 0) return cmd; show_macros(); return 0; } int help_ngsel() { int cmd; page_start(); if ((cmd = print_lines("\ The Newsgroup Selector:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ a-z,0-9 Select/deselect the indicated group by its letter or number. Many of\n\ the alpha letters are omitted for the following commands.\n\ SP Perform the default command (usually > or Z).\n\ CR Enter newsgroup(s). Selects the current group if nothing is selected.\n\ . Toggle the current group's selection.\n\ ",NOMARKING)) || (cmd = print_lines("\ k, ',' Mark the current group to kill all unread articles.\n\ - Set a range, as in d - f. Repeats the last marking action.\n\ @ Toggle the selection of all visible groups.\n\ ",NOMARKING)) || (cmd = print_lines("\ E Toggle exclusion of non-selected groups from the selection list.\n\ n, ] Move down to the next group (try down-arrow also).\n\ p, [ Move up to the previous group (try up-arrow also).\n\ <, > Go to previous/next page (try left-/right-arrow also).\n\ ^, $ Go to first/last page.\n\ ",NOMARKING)) || (cmd = print_lines("\ O Set the selector's order.\n\ R Reverse the current sort order.\n\ U Switch between selecting empty/non-empty newsgroups.\n\ ",NOMARKING)) || (cmd = print_lines("\ c Catch up a group, marking the articles as read without chasing xrefs.\n\ = Refresh the article counts in the selector (will re-grab active).\n\ ^N Switch to next news source (the numbered GROUPs in ~/.trn/access).\n\ ^P Switch to the previous news source.\n\ ^K Edit the global KILL file.\n\ ",NOMARKING)) || (cmd = print_lines("\ \\ Escape next char as a command. A selector command is tried first,\n\ then a newsgroup-list command (force the latter with another \\).\n\ E.g. \\a will add groups, \\\\h gets you newsgroup-list help.\n\ :cmd Perform a command on all the selected groups.\n\ ::cmd Perform a command on all non-selected groups.\n\ :.cmd Perform a command on the current group.\n\ ::.cmd Perform a command on the all but the current.\n\ ",NOMARKING)) || (cmd = print_lines("\ /pattern/modifiers:command{:command}\n\ Apply one or more commands to the set of groups matching pattern.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Start the option selector or set command-line switches.\n\ && View or set macro definitions.\n\ !cmd Escape to a subshell.\n\ H This help message.\n\ h, ? Enter online help browser.\n\ `, ESC Leave the selector and return to the rn-style group selector.\n\ q Quit trn.\n\ Q Quit the selector and return to the rn-style group selector.\n\ ",NOMARKING)) ) return cmd; return 0; } int help_addsel() { int cmd; page_start(); if ((cmd = print_lines("\ The Add-Newsgroups Selector:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ a-z,0-9 Select/deselect the newsgroup by its letter or number. Many of\n\ the alpha letters are omitted for the following commands.\n\ SP Perform the default command (usually > or Z).\n\ Z,CR Add the selected groups to the end of your newsrc.\n\ . Toggle the current group's selection.\n\ ",NOMARKING)) || (cmd = print_lines("\ k, ',' Mark the current group as undesirable for subscribing.\n\ - Set a range, as in d - f. Repeats the last marking action.\n\ @ Toggle the selection of all visible groups.\n\ ",NOMARKING)) || (cmd = print_lines("\ E Toggle exclusion of non-selected groups from the selection list.\n\ n, ] Move down to the next group (try down-arrow also).\n\ p, [ Move up to the previous group (try up-arrow also).\n\ <, > Go to previous/next page (try left-/right-arrow also).\n\ ^, $ Go to first/last page.\n\ ",NOMARKING)) || (cmd = print_lines("\ O Set the selector's order (NOT WORKING YET).\n\ R Reverse the current sort order (NOT WORKING YET).\n\ ",NOMARKING)) || (cmd = print_lines("\ /pattern/modifiers:commands\n\ Scan all newsgroups for a name matching the pattern.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Start the option selector or set command-line switches.\n\ && View or set macro definitions.\n\ !cmd Escape to a subshell.\n\ H This help message.\n\ h, ? Enter online help browser.\n\ q Quit the selector.\n\ ",NOMARKING)) ) return cmd; return 0; } #ifdef ESCSUBS int help_subs() { int cmd; #ifdef SUBSHELP doshell(sh,filexp(SUBSHELP)); #else page_start(); 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\ ",NOMARKING)) || #ifdef SCORE (cmd = print_lines("\ y From line with the domain abbreviated.\n\ (foo@x.y.bar.edu (Foo's Bar) -> foo@*.bar.edu (Foo's Bar))\n\ ",NOMARKING)) || #endif (cmd = print_lines("\ Y The current tmp dir (usually /tmp)\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\ + Your trn config file directory (usually %./.trn)\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_artsel() { int cmd; page_start(); if ((cmd = print_lines("\ Thread/Subject/Article Selector 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 omitted 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)) || #ifdef SCAN (cmd = print_lines("\ ; Enter article-scan mode. If articles/subjects/threads were selected\n\ in the trn selector, article-scan will show only those articles.\n\ ",NOMARKING)) || #endif (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 (use :p to post).\n\ ::cmd Perform a command on all non-selected articles.\n\ :.cmd Perform a command on the current thread or its selected articles.\n\ ::.cmd Perform a command on the unselected articles in the current thread.\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\ ",NOMARKING)) || (cmd = print_lines("\ 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\ /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("\ & Start the option selector or set command-line switches.\n\ && View or set macro definitions.\n\ !cmd Escape to a subshell.\n\ H This help message.\n\ h, ? Enter online help browser.\n\ ESC, + Leave the selector but stay in the group (at last visited article).\n\ q Quit the selector and the group.\n\ Q Quit group and return to news group selection prompt for this group.\n\ ",NOMARKING)) ) return cmd; return 0; } int help_multirc() { int cmd; page_start(); if ((cmd = print_lines("\ The Newsrc Selector:\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 omitted for the following commands.\n\ SP Perform the default command (usually > or Z).\n\ CR Open the newsrc(s). Selects the current item if nothing is selected.\n\ . Toggle the current item's selection.\n\ ",NOMARKING)) || (cmd = print_lines("\ - Set a range, as in d - f. Repeats the last marking action.\n\ @ Toggle the selection of all visible items.\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\ ",NOMARKING)) || (cmd = print_lines("\ R Reverse the current sort order.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Start the option selector or set command-line switches.\n\ && View or set macro definitions.\n\ !cmd Escape to a subshell.\n\ H This help message.\n\ h, ? Enter online help browser.\n\ q Quit trn.\n\ ",NOMARKING)) ) return cmd; return 0; } int help_options() { int cmd; page_start(); if ((cmd = print_lines("\ The Option Selector:\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 omitted for the following commands.\n\ SP Perform the default command (usually > or Z).\n\ CR Accept changes. Changes the current item if nothing is selected.\n\ Z,TAB Accept changes.\n\ . Toggle the current item's selection.\n\ ",NOMARKING)) || (cmd = print_lines("\ - Set a range, as in d - f. Repeats the last marking action.\n\ @ Toggle the selection of all visible items.\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\ ",NOMARKING)) || (cmd = print_lines("\ R Reverse the current sort order.\n\ & Start the option selector or set command-line switches.\n\ && View or set macro definitions.\n\ ",NOMARKING)) || (cmd = print_lines("\ !cmd Escape to a subshell.\n\ H This help message.\n\ h, ? Enter online help browser.\n\ q Quit the option selector, abandoning changes.\n\ \n\ ",NOMARKING)) || (cmd = print_lines("\ The options are as follows:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ Use Universal Selector........ yes/no\n\ Universal Selector Order...... natural/points\n\ Universal Selector art. follow yes/no\n\ Universal Selector Commands... [Last-page-cmd][Other-page-cmd]\n\ Use Newsrc Selector........... yes/no\n\ Newsrc Selector Commands...... [Last-page-cmd][Other-page-cmd]\n\ Use Addgroup Selector......... yes/no\n\ Addgroup Selector Commands.... [Last-page-cmd][Other-page-cmd]\n\ Use Newsgroup Selector........ yes/no\n\ Newsgroup Selector Order...... natural/group/count\n\ Newsgroup Selector Commands... [Last-page-cmd][Other-page-cmd]\n\ Use News Selector............. yes/no/[# articles]\n\ ",NOMARKING)) || (cmd = print_lines("\ News Selector Mode............ threads/subjects/articles\n\ News Selector Order........... date/subject/author/groups/count/points\n\ News Selector Commands........ [Last-page-cmd][Other-page-cmd]\n\ News Selector Display Styles.. [e.g. lms=long/medium/short]\n\ Option Selector Commands...... [Last-page-cmd][Other-page-cmd]\n\ Use Selector Numbers.......... yes/no\n\ Selector Number Auto-Goto..... yes/no\n\ Use Threads................... yes/no\n\ Cited Text String............. [e.g. '>']\n\ Select My Postings............ parent/subthread/no\n\ ",NOMARKING)) || (cmd = print_lines("\ Read Breadth First............ yes/no\n\ Join Subject Lines............ no/[# chars]\n\ Terse Output.................. yes/no\n\ Check for New Groups.......... yes/no\n\ Initial Group List............ [# groups]\n\ Background Threading.......... yes/no\n\ Background Spinner............ yes/no\n\ Compress Subjects............. yes/no\n\ ",NOMARKING)) || (cmd = print_lines("\ Auto Savename................. yes/no\n\ Default Savefile Type......... norm/mail/ask\n\ Fuzzy Newsgroup Names......... yes/no\n\ Auto-Grow Groups.............. yes/no\n\ Ignore THRU on Select......... yes/no\n\ Auto Arrow Macros............. yes/no\n\ Save Dir...................... [directory path]\n\ Pager Line-Marking............ standout/underline/no\n\ ",NOMARKING)) || (cmd = print_lines("\ Erase Screen.................. yes/no\n\ Erase Each Line............... yes/no\n\ Muck Up Clear................. yes/no\n\ Novice Delays................. yes/no\n\ Header Magic.................. [[!]header,...]\n\ Header Hiding................. [[!]header,...]\n\ Initial Article Lines......... no/[# lines]\n\ Article Tree Lines............ no/[# lines]\n\ Append Unsubscribed Groups.... yes/no\n\ ",NOMARKING)) || (cmd = print_lines("\ Line Num for Goto............. [# line (1-n)]\n\ Verify Input.................. yes/no\n\ Eat Type-Ahead................ yes/no\n\ Charset....................... [e.g. patm]\n\ Filter Control Characters..... yes/no\n\ Scan Mode Count............... no/[# articles]\n\ Old Mthreads Database......... yes/no\n\ Checkpoint Newsrc Frequency... [# articles]\n\ Active File Refetch Mins...... no/[# mins]\n\ ",NOMARKING)) ) return cmd; return 0; } #ifdef SCAN int help_scanart() { int cmd; #ifdef UNDEF /* implement this later? */ doshell(sh,filexp(SAHELP)); #else page_start(); if ((cmd = print_lines("\ Article scan commands:\n\ ",STANDOUT)) || (cmd = print_lines("\ SP,CR Display article and enter the Article Level.\n\ Note: Marked articles (see 'm') will be displayed first.\n\ r Read the pointed-to article first, then read marked articles.\n\ ",NOMARKING)) || (cmd = print_lines("\ [0-9] Jump to item number. Enter a second digit or a command to jump\n\ to the item.\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\ ^N Move down to next article with the same subject.\n\ ^P Move up to previous article with the same subject.\n\ > Display next page of articles.\n\ < Display previous page of articles.\n\ ",NOMARKING)) || (cmd = print_lines("\ t Move to top of current page.\n\ b Move to bottom of current page.\n\ T,^ Move to top of all articles.\n\ B,$ Move to bottom of all articles.\n\ N Go to next newsgroup.\n\ P Go to previous newsgroup.\n\ ",NOMARKING)) || (cmd = print_lines("\ ^L,^R Redraw screen.\n\ j Junk the article pointed to.\n\ k Junk all articles (after pointer) with same subject.\n\ (much the same as 'k' at the Article Selection Level.\n\ ",NOMARKING)) || (cmd = print_lines("\ m Mark or unmark article for reading or a later command.\n\ M Toggle mark on following articles with same subject.\n\ (This is the same as typing 'm' on each of them.)\n\ X Junk (mark as read) all unmarked articles.\n\ D Junk unmarked articles on the same page. (similar to 'X')\n\ ['X' and 'D' act like the TRN thread selector options.]\n\ J Junk marked articles on page. (opposite of 'D')\n\ G Goto an article by number.\n\ e Extract marked articles (in mime, shar, or uuencoded format) to a\n\ directory. Multipart files may be extracted in any order.\n\ E End/Examine the last extracted file. A command can be entered to\n\ examine/view/play the extracted file. If the file was incomplete,\n\ it can be removed.\n\ [The e and E commands are disabled at the moment.]\n\ ",NOMARKING)) || (cmd = print_lines("\ s Select (for zoom) this article.\n\ S Select (for zoom) all following articles with same subject.\n\ z Enter/exit \"zoom\" mode--only selected articles are displayed.\n\ (Zoom mode is automatically entered when article-scan mode is\n\ entered from the TRN thread selector.)\n\ Z Clear (Zero) the zoom selections\n\ [Note: when in zoom mode commands which delete articles will\n\ only delete selected articles. This means, for instance, that\n\ 'X' will only delete *selected* unmarked articles. (Useful for\n\ reading only a few articles from a thread.)]\n\ ",NOMARKING)) || (cmd = print_lines("\ + Enter thread selection mode (only if group is threaded).\n\ ",NOMARKING)) || (cmd = print_lines("\ !cmd Escape to a subshell and execute cmd.\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\ h Print this help.\n\ H Enter help scan mode (an online documentation browser).\n\ q Quit reading this newsgroup and go to the next one.\n\ Q Leave article scan mode and begin reading articles, starting\n\ with the article pointed to. You will not return to\n\ article-scan mode unless you type ';' again.\n\ c Catch up on all articles in the current newsgroup.\n\ (All articles are marked as read.)\n\ ",NOMARKING)) || (cmd = print_lines("\ /text Search article descriptions forwards for text.\n\ ?text Search article descriptions backwards for text.\n\ gtext Search article descriptions forwards for text. If no matching\n\ descriptions are found, continue the search from the first\n\ article description. (wrap-around search)\n\ Searches are for case-insensitive substring of article\n\ descriptions matching text. Leading spaces in text are ignored\n\ (which means you can type \"g John Smith\" or \"gJohn Smith\")\n\ ",NOMARKING)) || (cmd = print_lines("\ f Toggle subject following mode. When enabled article threads\n\ will be followed normally. When disabled typing space or\n\ return at end of article will return to article-scan mode.\n\ Typing a command explicitly (like ^n) will still work.\n\ (If an explicit command is typed, follow mode will be temporarily\n\ turned on for the remainder of the thread.)\n\ F Toggle subject Folding mode. (show only first article with a\n\ subject. Note that \"first\" depends on ordering...)\n\ ",NOMARKING)) || (cmd = print_lines("\ U Toggle reading eligibility: read / unread+read\n\ TAB Toggle display of \"threadcount\" (# of articles following this one\n\ with the same subject)\n\ a Toggle display of authors.\n\ %% Toggle display of article numbers.\n\ ",NOMARKING)) #ifdef SCORE || (cmd = print_lines("\ Scoring commands:\n\ ",STANDOUT)) || (cmd = print_lines("\ K(num) Kill articles with scores equal to or below a number (num).\n\ Type the threshold and press return. (no spaces: type K-12)\n\ ^E Edit the score file for the current group.\n\ o Toggle article ordering: arrival or score-sorted.\n\ O Toggle article ordering between older-first and newer-first\n\ when in score-sorted order. (For two articles with same\n\ score, toggle between the older one first or the newer one first.)\n\ R Rescores all articles. (same as 'r at article level).\n\ ",NOMARKING)) || (cmd = print_lines("\ [Note: type ' or \" then RETURN for menus of scoring commands.]\n\ \"\" \n\ Adds to the local (this newsgroup's) scorefile.\n\ \"* \n\ Adds to the global scorefile.\n\ \"C \n\ Adds to the scorefile represented by the abbreviation C.\n\ \"? List all scorefile abbreviations and the filenames they represent.\n\ \"! \n\ Adds to the internal list of scoring rules as if it was read\n\ from a file. Articles are rescored after this command.\n\ 'd Debugging dump. (Will go away in later versions.)\n\ 'e \n\ Edits the scorefile named by the following file abbreviation\n\ character or full filename. If no arguments are given, edits the\n\ local scorefile (the one for the current newsgroup).\n\ 'f Continue scoring articles until interrupted.\n\ 'r Discard old scoring data and rescore from score files.\n\ 's Show scoring rules which contributed to this article's score.\n\ 't Test command: may do varying things. (will usually be disabled)\n\ ",NOMARKING)) #endif /* SCORE */ ) return cmd; #endif /* !UNDEF (later the !doshell part) */ return 0; } #endif /* SCAN */ int help_univ() { int cmd; page_start(); if ((cmd = print_lines("\ The Universal Selector:\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 omitted for the following commands.\n\ SP Perform the default command (usually > or Z).\n\ CR Enter item(s). Selects the current item if nothing is selected.\n\ TAB Start reading selected items.\n\ ",NOMARKING)) || (cmd = print_lines("\ - Set a range, as in d - f. Repeats the last marking action.\n\ @ Toggle the selection of all visible items.\n\ . Toggle the current item's selection.\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\ ",NOMARKING)) || (cmd = print_lines("\ O Set the selector's order.\n\ R Reverse the current sort order.\n\ U Switch between selecting empty/non-empty items.\n\ ",NOMARKING)) || (cmd = print_lines("\ ^E Edit the current universal configuration file.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Start the option selector or set command-line switches.\n\ && View or set macro definitions.\n\ !cmd Escape to a subshell.\n\ H This help message.\n\ h, ? Enter online help browser.\n\ q Quit current level. At top level, leave trn.\n\ Q Quit the current level. At top level, go to the next selector.\n\ ",NOMARKING)) ) return cmd; return 0; } int univ_key_help(where) int where; { switch (where) { case UHELP_PAGE: return help_page(); case UHELP_ART: return help_art(); case UHELP_NG: return help_ng(); case UHELP_NGSEL: return help_ngsel(); case UHELP_ADDSEL: return help_addsel(); #ifdef ESCSUBS case UHELP_SUBS: return help_subs(); #endif case UHELP_ARTSEL: return help_artsel(); case UHELP_MULTIRC: return help_multirc(); case UHELP_OPTIONS: return help_options(); #ifdef SCAN case UHELP_SCANART: return help_scanart(); #endif case UHELP_UNIV: return help_univ(); default: return 0; } } trn-4.0-test77/help.h0000644000000000000000000000150507113133016013106 0ustar rootroot/* help.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* help locations */ #define UHELP_PAGE 1 #define UHELP_ART 2 #define UHELP_NG 3 #define UHELP_NGSEL 4 #define UHELP_ADDSEL 5 #ifdef ESCSUBS #define UHELP_SUBS 6 #endif #define UHELP_ARTSEL 7 #define UHELP_MULTIRC 8 #define UHELP_OPTIONS 9 #ifdef SCAN #define UHELP_SCANART 10 #endif #define UHELP_UNIV 11 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void help_init _((void)); int help_page _((void)); int help_art _((void)); int help_ng _((void)); int help_ngsel _((void)); int help_addsel _((void)); #ifdef ESCSUBS int help_subs _((void)); #endif int help_artsel _((void)); int help_multirc _((void)); int help_options _((void)); #ifdef SCAN int help_scanart _((void)); #endif int help_univ _((void)); int univ_key_help _((int)); trn-4.0-test77/inews.c0000644000000000000000000001707207113133016013304 0ustar rootroot/* inews.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "env.h" #include "init.h" #include "util2.h" #include "util3.h" #include "nntpclient.h" #include "nntpinit.h" #define MAX_SIGNATURE 4 int debug = 0; int new_connection = FALSE; char* server_name; char* nntp_auth_file; char nullstr[1]; char buf[LBUFLEN+1]; int valid_header _((char*)); void append_signature _((void)); int main(argc, argv) int argc; char* argv[]; { bool has_fromline, in_header, has_pathline; bool found_nl, had_nl; int artpos, headbuf_size, len; char* headbuf; char* line_end; register char* cp; int i; headbuf_size = LBUFLEN * 8; headbuf = safemalloc(headbuf_size); #ifdef LAX_INEWS env_init(headbuf, 1); #else if (!env_init(headbuf, 0)) { fprintf(stderr,"Can't get %s information. Please contact your system adminstrator.\n", (*loginName || !*realName)? "user" : "host"); exit(1); } #endif argv++; while (argc > 1) { if (*argv[0] != '-') break; argv++; argc--; } if (argc > 1) { if (freopen(*argv, "r", stdin) == NULL) { perror(*argv); exit(1); } } cp = getenv("NNTPSERVER"); if (!cp) { cp = filexp(SERVER_NAME); if (FILE_REF(cp)) cp = nntp_servername(cp); } if (cp && *cp && strNE(cp,"local")) { server_name = savestr(cp); cp = index(server_name, ';'); if (cp) { *cp = '\0'; nntplink.port_number = atoi(cp+1); } line_end = "\r\n"; nntp_auth_file = filexp(NNTP_AUTH_FILE); if ((cp = getenv("NNTP_FORCE_AUTH")) != NULL && (*cp == 'y' || *cp == 'Y')) nntplink.flags |= NNTP_FORCE_AUTH_NEEDED; } else { server_name = NULL; line_end = "\n"; } in_header = 0; has_fromline = 0; has_pathline = 0; artpos = 0; cp = headbuf; had_nl = 1; for (;;) { if (headbuf_size < artpos + LBUFLEN + 1) { len = cp - headbuf; headbuf_size += LBUFLEN * 4; headbuf = saferealloc(headbuf,headbuf_size); cp = headbuf + len; } i = getc(stdin); if (server_name && had_nl && i == '.') *cp++ = '.'; if (i == '\n') { if (!in_header) continue; break; } else if (i == EOF || !fgets(cp+1, LBUFLEN-1, stdin)) { /* Still in header after EOF? Hmm... */ fprintf(stderr,"Article was all header -- no body.\n"); exit(1); } *cp = (char)i; len = strlen(cp); found_nl = (len && cp[len-1] == '\n'); if (had_nl) { if ((i = valid_header(cp)) == 0) { fprintf(stderr,"Invalid header:\n%s",cp); exit(1); } if (i == 2) { if (!in_header) continue; break; } in_header = 1; if (strncaseEQ(cp, "From:", 5)) has_fromline = 1; else if (strncaseEQ(cp, "Path:", 5)) has_pathline = 1; } artpos += len; cp += len; if ((had_nl = found_nl) != 0 && server_name) { cp[-1] = '\r'; *cp++ = '\n'; } } *cp = '\0'; /* Well, the header looks ok, so let's get on with it. */ if (server_name) { if ((cp = getenv("NNTPFDS")) != NULL) { int rd_fd, wr_fd; if (sscanf(cp,"%d.%d",&rd_fd,&wr_fd) == 2) { nntplink.rd_fp = fdopen(rd_fd, "r"); if (nntplink.rd_fp) { nntplink.wr_fp = fdopen(wr_fd, "w"); if (nntplink.wr_fp) nntplink.flags |= NNTP_NEW_CMD_OK; else nntp_close(FALSE); } } } if (!nntplink.wr_fp) { if (init_nntp() < 0 || !nntp_connect(server_name,0)) exit(1); new_connection = TRUE; } if (nntp_command("POST") <= 0 || nntp_check() <= 0) { if (new_connection) nntp_close(TRUE); fprintf(stderr,"Sorry, you can't post from this machine.\n"); exit(1); } } else { sprintf(buf, "%s -h", EXTRAINEWS); nntplink.wr_fp = popen(buf,"w"); if (!nntplink.wr_fp) { fprintf(stderr,"Unable to execute inews for local posting.\n"); exit(1); } } fputs(headbuf, nntplink.wr_fp); if (!has_pathline) fprintf(nntplink.wr_fp,"Path: not-for-mail%s",line_end); if (!has_fromline) { fprintf(nntplink.wr_fp,"From: %s@%s (%s)%s",loginName,phostname, getval("NAME",realName),line_end); } if (!getenv("NO_ORIGINATOR")) { fprintf(nntplink.wr_fp,"Originator: %s@%s (%s)%s",loginName,phostname, getval("NAME",realName),line_end); } fprintf(nntplink.wr_fp,"%s",line_end); had_nl = 1; while (fgets(headbuf, headbuf_size, stdin)) { /* Single . is eof, so put in extra one */ if (server_name && had_nl && *headbuf == '.') fputc('.', nntplink.wr_fp); /* check on newline */ cp = headbuf + strlen(headbuf); if (cp > headbuf && *--cp == '\n') { *cp = '\0'; fprintf(nntplink.wr_fp, "%s%s", headbuf, line_end); had_nl = 1; } else { fputs(headbuf, nntplink.wr_fp); had_nl = 0; } } if (!server_name) return pclose(nntplink.wr_fp); if (!had_nl) fputs(line_end, nntplink.wr_fp); append_signature(); fputs(".\r\n",nntplink.wr_fp); (void) fflush(nntplink.wr_fp); if (nntp_gets(ser_line, sizeof ser_line) < 0 || *ser_line != NNTP_CLASS_OK) { if (atoi(ser_line) == NNTP_POSTFAIL_VAL) { fprintf(stderr,"Article not accepted by server; not posted:\n"); for (cp = ser_line + 4; *cp && *cp != '\r'; cp++) { if (*cp == '\\') fputc('\n',stderr); else fputc(*cp,stderr); } fputc('\n', stderr); } else fprintf(stderr, "Remote error: %s\n", ser_line); if (new_connection) nntp_close(TRUE); exit(1); } if (new_connection) nntp_close(TRUE); cleanup_nntp(); return 0; } /* valid_header -- determine if a line is a valid header line */ int valid_header(h) register char* h; { char* colon; char* space; /* Blank or tab in first position implies this is a continuation header */ if (h[0] == ' ' || h[0] == '\t') { while (*++h == ' ' || *h == '\t') ; return *h && *h != '\n'? 1 : 2; } /* Just check for initial letter, colon, and space to make * sure we discard only invalid headers. */ colon = index(h, ':'); space = index(h, ' '); if (isalpha(h[0]) && colon && space == colon + 1) return 1; /* Anything else is a bad header */ return 0; } /* append_signature -- append the person's .signature file if * they have one. Limit .signature to MAX_SIGNATURE lines. * The rn-style DOTDIR environmental variable is used if present. */ void append_signature() { char* cp; FILE* fp; int count = 0; #ifdef NO_INEWS_DOTDIR dotdir = homedir; #endif if (dotdir == NULL) return; fp = fopen(filexp(SIGNATURE_FILE), "r"); if (fp == NULL) return; fprintf(nntplink.wr_fp, "-- \r\n"); while (fgets(ser_line, sizeof ser_line, fp)) { count++; if (count > MAX_SIGNATURE) { fprintf(stderr,"Warning: .signature files should be no longer than %d lines.\n", MAX_SIGNATURE); fprintf(stderr,"(Only %d lines of your .signature were posted.)\n", MAX_SIGNATURE); break; } /* Strip trailing newline */ cp = ser_line + strlen(ser_line) - 1; if (cp >= ser_line && *cp == '\n') *cp = '\0'; fprintf(nntplink.wr_fp, "%s\r\n", ser_line); } (void) fclose(fp); } #ifdef SUPPORT_NNTP int nntp_handle_timeout() { if (!new_connection) { static bool handling_timeout = FALSE; char last_command_save[NNTP_STRLEN]; if (strcaseEQ(last_command,"quit")) return 0; if (handling_timeout) return -1; handling_timeout = TRUE; strcpy(last_command_save, last_command); nntp_close(FALSE); if (init_nntp() < 0 || nntp_connect(server_name,0) <= 0) exit(1); if (nntp_command(last_command_save) <= 0) return -1; handling_timeout = FALSE; new_connection = TRUE; return 1; } fputs("\n503 Server timed out.\n",stderr); return -2; } #endif trn-4.0-test77/init.c0000644000000000000000000000756507222733631013142 0ustar rootroot/* init.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "env.h" #include "util.h" #include "util2.h" #include "final.h" #include "term.h" #include "last.h" #include "trn.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "nntpinit.h" #include "rcstuff.h" #include "only.h" #include "intrp.h" #include "addng.h" #include "sw.h" #include "opt.h" #include "art.h" #include "artsrch.h" #include "artio.h" #include "backpage.h" #include "cache.h" #include "bits.h" #include "head.h" #include "help.h" #include "mime.h" #include "kfile.h" #include "ngsrch.h" #include "ngstuff.h" #include "rcln.h" #include "respond.h" #include "rthread.h" #include "ng.h" #include "decode.h" #ifdef SCAN #include "scan.h" #endif #ifdef SCORE #include "score.h" #endif #include "mempool.h" #include "color.h" #ifdef USE_TCL #include "tkstuff.h" #endif #include "univ.h" #include "INTERN.h" #include "init.h" #ifdef USE_FILTER #include "filter.h" #endif bool initialize(argc,argv) int argc; char* argv[]; { char* tcbuf; bool foundany = FALSE; #ifdef NOLINEBUF static char std_out_buf[BUFSIZ]; /* must be static or malloced */ setbuf(stdout, std_out_buf); #endif tcbuf = safemalloc(TCBUF_SIZE); /* make temp buffer for termcap and */ /* other initialization stuff */ our_pid = (long)getpid(); /* init terminal */ term_init(); /* must precede opt_init() so that */ /* ospeed is set for baud-rate */ /* switches. Actually terminal */ /* mode setting is in term_set() */ mp_init(); /* init syntax etc. for searching (must also precede opt_init()) */ search_init(); /* we have to know rnlib to look up global switches in %X/INIT */ env_init(tcbuf, 1); head_init(); /* decode switches */ opt_init(argc,argv,&tcbuf); /* must not do % interps! */ /* (but may mung environment) */ color_init(); /* init signals, status flags */ final_init(); /* start up file expansion and the % interpreter */ intrp_init(tcbuf, TCBUF_SIZE); /* now make sure we have a current working directory */ if (!checkflag) cwd_check(); #ifdef SUPPORT_NNTP if (init_nntp() < 0) finalize(1); #endif /* if we aren't just checking, turn off echo */ if (!checkflag) term_set(tcbuf); /* get info on last trn run, if any */ last_init(); free(tcbuf); /* recover 1024 bytes */ #ifdef USE_TCL if (UseTcl #ifdef USE_TK || UseTk #endif ) { ttcl_init(); } #endif univ_init(); /* check for news news */ if (!checkflag) newsnews_check(); /* process the newsid(s) and associate the newsrc(s) */ datasrc_init(); ngdata_init(); /* now read in the .newsrc file(s) */ foundany = rcstuff_init(); /* it looks like we will actually read something, so init everything */ addng_init(); art_init(); artio_init(); artsrch_init(); backpage_init(); bits_init(); cache_init(); help_init(); kfile_init(); #ifdef USE_FILTER filter_init(); #endif mime_init(); ng_init(); ngsrch_init(); ngstuff_init(); only_init(); rcln_init(); respond_init(); trn_init(); decode_init(); thread_init(); util_init(); xmouse_init(argv[0]); writelast(); /* remember last runtime in .rnlast */ #ifdef FINDNEWNG if (maxngtodo) /* patterns on command line? */ foundany |= scanactive(TRUE); #endif return foundany; } void newsnews_check() { char* newsnewsname = filexp(NEWSNEWSNAME); if ((tmpfp = fopen(newsnewsname,"r")) != NULL) { fstat(fileno(tmpfp),&filestat); if (filestat.st_mtime > (time_t)lasttime) { while (fgets(buf,sizeof(buf),tmpfp) != NULL) fputs(buf,stdout) FLUSH; get_anything(); putchar('\n') FLUSH; } fclose(tmpfp); } } trn-4.0-test77/init.h0000644000000000000000000000053107113133016013117 0ustar rootroot/* init.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #define TCBUF_SIZE 1024 EXT long our_pid; /* default string for group entry */ #if 0 EXT char *group_default INIT(nullstr); #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool initialize _((int,char**)); void newsnews_check _((void)); trn-4.0-test77/intrp.c0000644000000000000000000006050707222733633013330 0ustar rootroot/* intrp.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "env.h" #include "util.h" #include "util2.h" #include "search.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "head.h" #include "trn.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "artsrch.h" #include "ng.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 "rt-util.h" #include "INTERN.h" #include "intrp.h" #include "intrp.ih" #include static char* regexp_specials = "^$.*[\\/?%"; char orgname[] = ORGNAME; #ifdef HAS_UNAME #include struct utsname utsn; #endif COMPEX cond_compex; void intrp_init(tcbuf, tcbuf_len) char* tcbuf; int tcbuf_len; { #if HOSTBITS != 0 int i; #endif init_compex(&cond_compex); /* get environmental stuff */ #ifdef NEWS_ADMIN { #ifdef HAS_GETPWENT 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 */ } /* if this is the news admin then load his UID into newsuid */ if (strEQ(loginName,NEWS_ADMIN)) newsuid = getuid(); #endif if (checkflag) /* that getwd below takes ~1/3 sec. */ return; /* and we do not need it for -c */ trn_getwd(tcbuf, tcbuf_len); /* find working directory name */ origdir = savestr(tcbuf); /* and remember it */ /* name of header file (%h) */ headname = savestr(filexp(HEADNAME)); /* the hostname to use in local-article comparisons */ #if HOSTBITS != 0 i = (HOSTBITS < 2? 2 : HOSTBITS); hostname = phostname+strlen(phostname)-1; while (i && hostname != phostname) { if (*--hostname == '.') i--; } if (*hostname == '.') hostname++; #else hostname = phostname; #endif } /* skip interpolations */ static char* skipinterp(pattern,stoppers) register char* pattern; char* stoppers; { #ifdef DEBUG if (debug & DEB_INTRP) printf("skipinterp %s (till %s)\n",pattern,stoppers?stoppers:""); #endif while (*pattern && (!stoppers || !index(stoppers,*pattern))) { if (*pattern == '%' && pattern[1]) { switch_again: switch (*++pattern) { case '^': case '_': case '\\': case '\'': case '>': case ')': goto switch_again; case ':': pattern++; while (*pattern && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) { pattern++; } pattern--; goto switch_again; case '{': for (pattern++; *pattern && *pattern != '}'; pattern++) if (*pattern == '\\') pattern++; break; case '[': for (pattern++; *pattern && *pattern != ']'; pattern++) if (*pattern == '\\') pattern++; break; 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; } #ifdef BACKTICK case '`': { pattern = skipinterp(pattern+1,"`"); break; } #endif #ifdef PROMPTTTY case '"': pattern = skipinterp(pattern+1,"\""); break; #endif default: break; } pattern++; } else { if (*pattern == '^' && ((Uchar)pattern[1]>='?' || pattern[1]=='(' || pattern[1]==')')) pattern += 2; else if (*pattern == '\\' && pattern[1]) pattern += 2; else pattern++; } } getout: return pattern; /* where we left off */ } /* interpret interpolations */ char* dointerp(dest,destsize,pattern,stoppers,cmd) register char* dest; register int destsize; register char* pattern; char* stoppers; char* cmd; { char* subj_buf = NULL; char* ngs_buf = NULL; char* refs_buf = NULL; char* artid_buf = NULL; char* reply_buf = NULL; char* from_buf = NULL; char* path_buf = NULL; char* follow_buf = NULL; char* dist_buf = NULL; char* line_buf = NULL; char* line_split = NULL; char* orig_dest = dest; register char* s; register char* h; register int i; char scrbuf[8192]; char spfbuf[512]; static char* input_str = NULL; static int input_siz = 0; bool upper = FALSE; bool lastcomp = FALSE; bool re_quote = FALSE; int tick_quote = 0; bool address_parse = FALSE; bool comment_parse = FALSE; bool proc_sprintf = FALSE; int metabit = 0; #ifdef DEBUG if (debug & DEB_INTRP) printf(">dointerp: %s (till %s)\n",pattern,stoppers?stoppers:""); #endif while (*pattern && (!stoppers || !index(stoppers,*pattern))) { if (*pattern == '%' && pattern[1]) { upper = FALSE; lastcomp = FALSE; re_quote = FALSE; tick_quote = 0; address_parse = FALSE; comment_parse = FALSE; proc_sprintf = FALSE; for (s=NULL; !s; ) { switch (*++pattern) { case '^': upper = TRUE; break; case '_': lastcomp = TRUE; break; case '\\': re_quote = TRUE; break; case '\'': tick_quote++; break; case '>': address_parse = TRUE; break; case ')': comment_parse = 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 ARTSEARCH s = scrbuf; if (!cmd || !index("/?g",*cmd)) *s++ = '/'; strcpy(s,lastpat); s += strlen(s); if (!cmd || *cmd != 'g') { if (cmd && index("/?",*cmd)) *s++ = *cmd; else *s++ = '/'; if (art_doread) *s++ = 'r'; if (art_howmuch != ARTSCOPE_SUBJECT) { *s++ = scopestr[art_howmuch]; if (art_howmuch == ARTSCOPE_ONEHDR) { safecpy(s,htype[art_srchhdr].name, (sizeof scrbuf) - (s-scrbuf)); if (!(s = index(s,':'))) s = scrbuf+(sizeof scrbuf)-1; else s++; } } } *s = '\0'; s = scrbuf; #else s = nullstr; #endif break; case '{': pattern = cpytill(scrbuf,pattern+1,'}'); if ((s = index(scrbuf,'-')) != NULL) *s++ = '\0'; else s = nullstr; s = getval(scrbuf,s); break; case '<': pattern = cpytill(scrbuf,pattern+1,'>'); if ((s = index(scrbuf,'-')) != NULL) *s++ = '\0'; else s = nullstr; interp(scrbuf, 8192, getval(scrbuf,s)); s = scrbuf; break; case '[': if (in_ng) { pattern = cpytill(scrbuf,pattern+1,']'); if (*scrbuf && (i = get_header_num(scrbuf)) != SOME_LINE) { safefree(line_buf); s = line_buf = fetchlines(art,i); } else s = nullstr; } else s = nullstr; break; case '(': { COMPEX *oldbra_compex = bra_compex; char rch; bool matched; pattern = dointerp(dest,destsize,pattern+1,"!=",cmd); rch = *pattern; if (rch == '!') pattern++; if (*pattern != '=') goto getout; pattern = cpytill(scrbuf,pattern+1,'?'); if (!*pattern) goto getout; s = scrbuf; h = spfbuf; proc_sprintf = FALSE; do { switch (*s) { case '^': *h++ = '\\'; break; case '\\': *h++ = '\\'; *h++ = '\\'; break; case '%': proc_sprintf = TRUE; break; } *h++ = *s; } while (*s++); if (proc_sprintf) { dointerp(scrbuf,sizeof scrbuf,spfbuf,(char*)NULL,cmd); proc_sprintf = FALSE; } if ((s = compile(&cond_compex,scrbuf,TRUE,TRUE)) != NULL) { printf("%s: %s\n",scrbuf,s) FLUSH; pattern += strlen(pattern); free_compex(&cond_compex); goto getout; } matched = (execute(&cond_compex,dest) != NULL); if (getbracket(&cond_compex, 0)) /* were there brackets? */ bra_compex = &cond_compex; if (matched==(rch == '=')) { pattern = dointerp(dest,destsize,pattern+1,":)",cmd); if (*pattern == ':') pattern = skipinterp(pattern+1,")"); } else { pattern = skipinterp(pattern+1,":)"); if (*pattern == ':') pattern++; pattern = dointerp(dest,destsize,pattern,")",cmd); } s = dest; bra_compex = oldbra_compex; free_compex(&cond_compex); break; } #ifdef BACKTICK case '`': { FILE* popen(); FILE* pipefp; pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`",cmd); pipefp = popen(scrbuf,"r"); if (pipefp != NULL) { 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,"\"",cmd); fputs(scrbuf,stdout) FLUSH; resetty(); fgets(scrbuf, sizeof scrbuf, stdin); noecho(); crmode(); i = strlen(scrbuf); if (scrbuf[i-1] == '\n') { scrbuf[--i] = '\0'; } growstr(&input_str, &input_siz, i+1); safecpy(input_str, scrbuf, i+1); s = input_str; break; #endif case '~': s = homedir; break; case '.': s = dotdir; break; case '+': s = trndir; break; case '$': s = scrbuf; sprintf(s,"%ld",our_pid); break; case '#': s = scrbuf; if (upper) { static int counter = 0; sprintf(s,"%d",++counter); } else sprintf(s,"%d",perform_cnt); break; case '?': s = " "; line_split = dest; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': s = getbracket(bra_compex,*pattern - '0'); break; case 'a': if (in_ng) { s = scrbuf; sprintf(s,"%ld",(long)art); } else s = nullstr; break; case 'A': if (in_ng) { #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) { if (artopen(art,(ART_POS)0)) { nntp_finishbody(FB_SILENT); sprintf(s = scrbuf,"%s/%s",datasrc->spool_dir, nntp_artname(art, FALSE)); } else s = nullstr; } else #endif #ifdef LINKART s = linkartname; /* for Eunice */ #else sprintf(s = scrbuf,"%s/%s/%ld",datasrc->spool_dir, ngdir,(long)art); #endif } else s = nullstr; break; case 'b': s = savedest? savedest : nullstr; break; case 'B': s = scrbuf; sprintf(s,"%ld",(long)savefrom); break; case 'c': s = ngdir? ngdir : nullstr; break; case 'C': s = ngname? ngname : nullstr; break; case 'd': if (ngdir) { s = scrbuf; sprintf(s,"%s/%s",datasrc->spool_dir,ngdir); } else s = nullstr; break; case 'D': if (in_ng) s = dist_buf = fetchlines(art,DIST_LINE); else s = nullstr; break; case 'e': s = extractprog? extractprog : "-"; break; case 'E': s = extractdest? extractdest : nullstr; break; case 'f': /* from line */ if (in_ng) { parseheader(art); if (htype[REPLY_LINE].minpos >= 0 && !comment_parse) { /* 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 = nullstr; break; case 'F': if (in_ng) { parseheader(art); if (htype[FOLLOW_LINE].minpos >= 0) /* is there a Followup-To line? */ s = follow_buf = fetchlines(art,FOLLOW_LINE); else s = ngs_buf = fetchlines(art,NGS_LINE); } else s = nullstr; break; case 'g': /* general mode */ s = scrbuf; *s = gmode; s[1] = '\0'; break; case 'h': /* header file name */ s = headname; break; case 'H': /* host name in postings */ s = phostname; break; case 'i': if (in_ng) { if (!(s=artid_buf)) s = artid_buf = fetchlines(art,MSGID_LINE); if (*s && *s != '<') { sprintf(scrbuf,"<%s>",artid_buf); s = scrbuf; } } else s = nullstr; break; case 'I': /* indent string for quoting */ s = scrbuf; sprintf(scrbuf,"'%s'",indstr); break; case 'j': s = scrbuf; sprintf(scrbuf,"%d",just_a_sec*10); break; case 'l': /* rn library */ #ifdef NEWS_ADMIN s = newsadmin; #else s = "???"; #endif break; case 'L': /* login id */ s = loginName; 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 */ if (in_ng) s = ngs_buf = fetchlines(art,NGS_LINE); else s = nullstr; break; case 'N': /* full name */ s = getval("NAME",realName); break; case 'o': /* organization */ #ifdef IGNOREORG s = getval("NEWSORG",orgname); #else s = getval("NEWSORG",NULL); if (s == NULL) s = getval("ORGANIZATION",orgname); #endif s = filexp(s); #ifdef ORGFILE if (FILE_REF(s)) { FILE* ofp = fopen(s,"r"); if (ofp) { if (fgets(scrbuf,sizeof scrbuf,ofp) == NULL) *scrbuf = '\0'; fclose(ofp); s = scrbuf+strlen(scrbuf)-1; if (*scrbuf && *s == '\n') *s = '\0'; s = scrbuf; } else s = nullstr; } #endif break; case 'O': s = origdir; break; case 'p': s = cwd; break; case 'P': s = datasrc? datasrc->spool_dir : nullstr; break; case 'q': s = input_str; break; case 'r': if (in_ng) { parseheader(art); safefree0(refs_buf); if (htype[REFS_LINE].minpos >= 0) { refs_buf = fetchlines(art,REFS_LINE); normalize_refs(refs_buf); if ((s = rindex(refs_buf,'<')) != NULL) break; } } s = nullstr; break; case 'R': { int len, j; if (!in_ng) { s = nullstr; break; } parseheader(art); safefree0(refs_buf); if (htype[REFS_LINE].minpos >= 0) { refs_buf = fetchlines(art,REFS_LINE); len = strlen(refs_buf)+1; normalize_refs(refs_buf); /* no more than 3 prior references PLUS the ** root article allowed, including the one ** concatenated below */ if ((s = rindex(refs_buf,'<')) != NULL && s > refs_buf) { *s = '\0'; h = rindex(refs_buf,'<'); *s = '<'; if (h && h > refs_buf) { s = index(refs_buf+1,'<'); if (s < h) safecpy(s,h,len); } } } else len = 0; if (!artid_buf) artid_buf = fetchlines(art,MSGID_LINE); i = refs_buf? strlen(refs_buf) : 0; j = strlen(artid_buf) + (i? 1 : 0) + (artid_buf[0] == '<'? 0 : 2) + 1; if (len < i + j) refs_buf = saferealloc(refs_buf, i + j); if (i) refs_buf[i++] = ' '; if (artid_buf[0] == '<') strcpy(refs_buf+i, artid_buf); else if (artid_buf[0]) sprintf(refs_buf+i, "<%s>", artid_buf); else refs_buf[i] = '\0'; s = refs_buf; break; } case 's': case 'S': { char* str; if (!in_ng || !art || !artp) { s = nullstr; break; } if ((str = subj_buf) == NULL) str = subj_buf = fetchsubj(art,TRUE); subject_has_Re(str,&str); if (*pattern == 's' && (h = instr(str,"- (nf", TRUE)) != NULL) *h = '\0'; s = str; break; } case 't': case 'T': if (!in_ng) { s = nullstr; break; } parseheader(art); if (htype[REPLY_LINE].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].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; } address_parse = TRUE; /* just the good part */ break; case 'u': if (in_ng) { sprintf(scrbuf,"%ld",(long)ngptr->toread); s = scrbuf; } else s = nullstr; break; case 'U': { int unseen; if (!in_ng) { s = nullstr; break; } unseen = (art <= lastart) && !was_read(art); if (selected_only) { int selected; selected = curr_artp && (curr_artp->flags & AF_SEL); sprintf(scrbuf,"%ld", (long)selected_count - (selected && unseen)); } else sprintf(scrbuf,"%ld",(long)ngptr->toread - unseen); s = scrbuf; break; } case 'v': { int selected, unseen; if (in_ng) { selected = curr_artp && (curr_artp->flags & AF_SEL); unseen = (art <= lastart) && !was_read(art); sprintf(scrbuf,"%ld",(long)ngptr->toread-selected_count - (!selected && unseen)); s = scrbuf; } else s = nullstr; break; } case 'V': s = patchlevel + 1; break; case 'W': s = datasrc? datasrc->thread_dir : nullstr; break; case 'x': /* news library */ s = lib; break; case 'X': /* rn library */ s = rnlib; break; #ifdef SCORE case 'y': /* from line with *-shortening */ if (!in_ng) { s = nullstr; break; } #ifdef ASYNC_PARSE /* parse_maybe(art); */ #endif /* XXX Rewrite this! */ { /* sick, but I don't want to hunt down a buf... */ static char tmpbuf[1024]; char* s2; char* s3; int i = 0; s2 = fetchlines(art,FROM_LINE); strcpy(tmpbuf,s2); free(s2); for (s2=tmpbuf; (*s2 && (*s2 != '@') && (*s2 !=' '));s2++) ; /* EMPTY */ if (*s2 == '@') { /* we have normal form... */ for (s3=s2+1;(*s3 && (*s3 != ' '));s3++) if (*s3 == '.') i++; } if (i>1) { /* more than one dot */ s3 = s2; /* will be incremented before use */ while (i>=2) { s3++; if (*s3 == '.') i--; } s2++; *s2 = '*'; s2++; *s2 = '\0'; from_buf = (char*)safemalloc( (strlen(tmpbuf)+strlen(s3)+1)*sizeof(char)); strcpy(from_buf,tmpbuf); strcat(from_buf,s3); } else { from_buf = savestr(tmpbuf); } s = from_buf; } break; #endif /* SCORE */ case 'Y': s = tmpdir; break; case 'z': if (!in_ng) { s = nullstr; break; } #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; case '\0': s = nullstr; break; default: if (--destsize <= 0) abort_interp(); *dest++ = *pattern | metabit; s = nullstr; break; } } if (proc_sprintf) { sprintf(scrbuf,spfbuf,s); s = scrbuf; } if (*pattern) 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. */ #ifdef DEBUG if (debug & DEB_INTRP) printf("%% = %s\n",s); #endif /* A maze of twisty little conditions, all alike... */ if (address_parse || comment_parse) { if (s != scrbuf) { safecpy(scrbuf,s,sizeof scrbuf); s = scrbuf; } decode_header(s, s, strlen(s)); if (address_parse) { if ((h=index(s,'<')) != NULL) { /* grab the good part */ s = h+1; if ((h=index(s,'>')) != NULL) *h = '\0'; } else if ((h=index(s,'(')) != NULL) { while (h-- != s && *h == ' ') ; h[1] = '\0'; /* or strip the comment */ } } else { if (!(s = extract_name(s))) s = nullstr; } } 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 || tick_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! */ abort_interp(); } while (*s) { if ((re_quote && index(regexp_specials, *s)) || (tick_quote == 2 && *s == '"')) { if (--destsize <= 0) abort_interp(); *dest++ = '\\'; } else if (re_quote && *s == ' ' && s[1] == ' ') { *dest++ = ' '; *dest++ = '*'; while (*++s == ' ') ; continue; } else if (tick_quote && *s == '\'') { if ((destsize -= 3) <= 0) abort_interp(); *dest++ = '\''; *dest++ = '\\'; *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++; i = *(Uchar*)pattern; /* get char after arrow into a register */ if (i == '?') *dest++ = '\177' | metabit; else if (i == '(') { metabit = 0200; destsize++; } else if (i == ')') { metabit = 0; destsize++; } else if (i >= '@') *dest++ = (i & 037) | metabit; else *dest++ = *--pattern | metabit; pattern++; } else if (*pattern == '\\' && pattern[1]) { ++pattern; /* skip backslash */ pattern = interp_backslash(dest, pattern) + 1; *dest++ |= metabit; } else if (*pattern) *dest++ = *pattern++ | metabit; } } *dest = '\0'; if (line_split != NULL) if ((int)strlen(orig_dest) > 79) *line_split = '\n'; getout: safefree(subj_buf); /* return any checked out storage */ safefree(ngs_buf); safefree(refs_buf); safefree(artid_buf); safefree(reply_buf); safefree(from_buf); safefree(path_buf); safefree(follow_buf); safefree(dist_buf); safefree(line_buf); #ifdef DEBUG if (debug & DEB_INTRP) printf("= '0' && i <= '7') { i = 0; while (i < 01000 && *pattern >= '0' && *pattern <= '7') { i <<= 3; i += *pattern++ - '0'; } *dest = (char)(i & 0377); return pattern - 1; } switch (i) { case 'b': *dest = '\b'; break; case 'f': *dest = '\f'; break; case 'n': *dest = '\n'; break; case 'r': *dest = '\r'; break; case 't': *dest = '\t'; break; case '\0': *dest = '\\'; return pattern - 1; default: *dest = (char)i; break; } return pattern; } /* helper functions */ char* interp(dest,destsize,pattern) register char* dest; register int destsize; register char* pattern; { return dointerp(dest,destsize,pattern,(char*)NULL,(char*)NULL); } char* interpsearch(dest,destsize,pattern,cmd) register char* dest; register int destsize; register char* pattern; char* cmd; { return dointerp(dest,destsize,pattern,(char*)NULL,cmd); } /* normalize a references line in place */ void normalize_refs(refs) char* refs; { register char* f; register char* t; for (f = t = refs; *f; ) { if (*f == '<') { while (*f && (*t++ = *f++) != '>') ; while (*f == ' ' || *f == '\t' || *f == '\n' || *f == ',') f++; if (f != t) *t++ = ' '; } else f++; } if (t != refs && t[-1] == ' ') t--; *t = '\0'; } static void abort_interp() { fputs("\n% interp buffer overflow!\n",stdout) FLUSH; sig_catcher(0); } trn-4.0-test77/intrp.h0000644000000000000000000000126107222733635013327 0ustar rootroot/* intrp.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* origdir INIT(NULL); /* cwd when rn invoked */ EXT char* hostname INIT(NULL); /* host name to match local postings */ EXT char* headname INIT(NULL); EXT int perform_cnt; #ifdef NEWS_ADMIN EXT char newsadmin[] INIT(NEWS_ADMIN);/* news administrator */ EXT int newsuid INIT(0); #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void intrp_init _((char*,int)); char* dointerp _((char*,int,char*,char*,char*)); char* interp_backslash _((char*,char*)); char* interp _((char*,int,char*)); char* interpsearch _((char*,int,char*,char*)); void normalize_refs _((char*)); trn-4.0-test77/intrp.ih0000644000000000000000000000034307113133016013462 0ustar rootroot/* intrp.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static char* skipinterp _((char*,char*)); static void abort_interp _((void)); trn-4.0-test77/kfile.c0000644000000000000000000004254407113340231013252 0ustar rootroot/* kfile.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "term.h" #include "env.h" #include "util.h" #include "util2.h" #include "hash.h" #include "cache.h" #include "artsrch.h" #include "ng.h" #include "ngdata.h" #include "intrp.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "nntp.h" #include "ngstuff.h" #include "rcstuff.h" #include "trn.h" #include "bits.h" #include "rthread.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #include "color.h" #include "INTERN.h" #include "kfile.h" #include "kfile.ih" #ifdef KILLFILES static bool exitcmds = FALSE; char thread_cmd_ltr[] = "JK,j+S.m"; unsigned short thread_cmd_flag[] = { AUTO_KILL_THD, AUTO_KILL_SBJ, AUTO_KILL_FOL, AUTO_KILL_1, AUTO_SEL_THD, AUTO_SEL_SBJ, AUTO_SEL_FOL, AUTO_SEL_1, }; static char killglobal[] = KILLGLOBAL; static char killlocal[] = KILLLOCAL; static char killthreads[] = KILLTHREADS; void kfile_init() { char* cp = getenv("KILLTHREADS"); if (!cp) cp = killthreads; if (*cp && strNE(cp, "none")) { FILE* fp; kf_daynum = KF_DAYNUM(0); kf_thread_cnt = kf_changethd_cnt = 0; if ((fp = fopen(filexp(cp), "r")) != NULL) { msgid_hash = hashcreate(1999, msgid_cmp); while (fgets(buf, sizeof buf, fp) != NULL) { if (*buf == '<') { int age; cp = index(buf,' '); if (!cp) cp = ","; else *cp++ = '\0'; age = kf_daynum - atol(cp+1); if (age > KF_MAXDAYS) { kf_changethd_cnt++; continue; } if ((cp = index(thread_cmd_ltr, *cp)) != NULL) { int auto_flag; HASHDATUM data; auto_flag = thread_cmd_flag[cp-thread_cmd_ltr]; data = hashfetch(msgid_hash,buf,strlen(buf)); if (!data.dat_ptr) data.dat_ptr = savestr(buf); else kf_changethd_cnt++; data.dat_len = auto_flag | age; hashstorelast(data); } kf_thread_cnt++; } } fclose(fp); } kf_state |= KFS_GLOBAL_THREADFILE; kfs_local_change_clear = KFS_LOCAL_CHANGES; kfs_thread_change_set = KFS_THREAD_CHANGES; } else { kfs_local_change_clear = KFS_LOCAL_CHANGES | KFS_THREAD_CHANGES; kfs_thread_change_set = KFS_LOCAL_CHANGES | KFS_THREAD_CHANGES; } } static void mention(str) char* str; { #ifdef VERBOSE IF(verbose) { color_string(COLOR_NOTICE,str); newline(); } ELSE #endif #ifdef TERSE putchar('.'); #endif fflush(stdout); } static bool kill_mentioned; int do_kfile(kfp,entering) FILE* kfp; int entering; { bool first_time = (entering && !killfirst); char last_kill_type = '\0'; int thread_kill_cnt = 0; int thread_select_cnt = 0; char* cp; char* bp; art = lastart+1; killfirst = firstart; fseek(kfp,0L,0); /* rewind file */ while (fgets(buf,LBUFLEN,kfp) != NULL) { if (*(cp = buf + strlen(buf) - 1) == '\n') *cp = '\0'; for (bp = buf; isspace(*bp); bp++) ; if (strnEQ(bp,"THRU",4)) { int len = strlen(ngptr->rc->name); cp = bp + 4; while (isspace(*cp)) cp++; if (strnNE(cp, ngptr->rc->name, len) || !isspace(cp[len])) continue; killfirst = atol(cp+len+1)+1; if (killfirst < firstart) killfirst = firstart; if (killfirst > lastart) killfirst = lastart+1; continue; } if (*bp == 'I') { FILE* incfile; int ret; for (cp = bp + 1; *cp && !isspace(*cp); cp++) ; while (isspace(*cp)) cp++; if (!*cp) continue; cp = filexp(cp); if (!index(cp,'/')) { set_ngname(cp); cp = filexp(getval("KILLLOCAL",killlocal)); set_ngname(ngptr->rcline); } if ((incfile = fopen(cp, "r")) != NULL) { ret = do_kfile(incfile, entering); fclose(incfile); if (ret) return ret; } continue; } if (*bp == 'X') { /* exit command? */ if (entering) { exitcmds = TRUE; continue; } bp++; } else if (!entering) continue; if (*bp == '&') { mention(bp); if (bp > buf) strcpy(buf, bp); switcheroo(); } else if (*bp == '/') { kf_state |= KFS_NORMAL_LINES; if (firstart > lastart) continue; if (last_kill_type) { if (perform_status_end(ngptr->toread,"article")) { kill_mentioned = TRUE; carriage_return(); fputs(msg, stdout); newline(); } } perform_status_init(ngptr->toread); last_kill_type = '/'; mention(bp); kill_mentioned = TRUE; switch (art_search(bp, (sizeof buf) - (bp - buf), FALSE)) { case SRCH_ABORT: continue; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif termdown(2); return -1; case SRCH_DONE: break; case SRCH_SUBJDONE: /*fputs("\tsubject not found (?)\n",stdout) FLUSH;*/ break; case SRCH_NOTFOUND: /*fputs("\tnot found\n",stdout) FLUSH;*/ break; case SRCH_FOUND: /*fputs("\tfound\n",stdout) FLUSH;*/ break; } } else if (first_time && *bp == '<') { register ARTICLE* ap; if (last_kill_type != '<') { if (last_kill_type) { if (perform_status_end(ngptr->toread,"article")) { kill_mentioned = TRUE; carriage_return(); fputs(msg, stdout); newline(); } } perform_status_init(ngptr->toread); last_kill_type = '<'; } cp = index(bp,' '); if (!cp) cp = "T,"; else *cp++ = '\0'; if ((ap = get_article(bp)) != NULL) { if ((ap->flags & AF_FAKE) && !ap->child1) { if (*cp == 'T') cp++; if ((cp = index(thread_cmd_ltr, *cp)) != NULL) { ap->autofl = thread_cmd_flag[cp-thread_cmd_ltr]; if (ap->autofl & AUTO_KILLS) thread_kill_cnt++; else thread_select_cnt++; } } else { art = article_num(ap); artp = ap; perform(cp,FALSE); if (ap->autofl & AUTO_SELS) thread_select_cnt++; else if (ap->autofl & AUTO_KILLS) thread_kill_cnt++; } } art = lastart+1; kf_state |= KFS_THREAD_LINES; } else if (*bp == '<') { kf_state |= KFS_THREAD_LINES; } else if (*bp == '*') { int killmask = AF_UNREAD; switch (bp[1]) { case 'X': killmask |= sel_mask; /* don't kill selected articles */ /* FALL THROUGH */ case 'j': article_walk(kfile_junk, killmask); break; } kf_state |= KFS_NORMAL_LINES; } } if (thread_kill_cnt) { sprintf(buf,"%ld auto-kill command%s.", (long)thread_kill_cnt, PLURAL(thread_kill_cnt)); mention(buf); kill_mentioned = TRUE; } if (thread_select_cnt) { sprintf(buf,"%ld auto-select command%s.", (long)thread_select_cnt, PLURAL(thread_select_cnt)); mention(buf); kill_mentioned = TRUE; } if (last_kill_type) { if (perform_status_end(ngptr->toread,"article")) { kill_mentioned = TRUE; carriage_return(); fputs(msg, stdout); newline(); } } return 0; } static bool kfile_junk(ptr, killmask) char* ptr; int killmask; { register ARTICLE* ap = (ARTICLE*)ptr; if ((ap->flags & killmask) == AF_UNREAD) set_read(ap); else if (ap->flags & sel_mask) { ap->flags &= ~sel_mask; if (!selected_count--) selected_count = 0; } return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char* message; int entering; { bool intr = FALSE; /* did we get an interrupt? */ ART_NUM oldfirst; char oldmode = mode; bool anytokill = (ngptr->toread > 0); set_mode('r','k'); if ((entering || exitcmds) && (localkfp || globkfp)) { exitcmds = FALSE; oldfirst = firstart; firstart = starting; clear(); #ifdef VERBOSE # ifdef TERSE if (message && (verbose || entering)) # else if (message) # endif #else if (message && entering) #endif fputs(message,stdout) FLUSH; kill_mentioned = FALSE; if (localkfp) { if (entering) kf_state |= KFS_LOCAL_CHANGES; intr = do_kfile(localkfp,entering); } open_kfile(KF_GLOBAL); /* Just in case the name changed */ if (globkfp && !intr) intr = do_kfile(globkfp,entering); newline(); if (entering && kill_mentioned && novice_delays) { #ifdef VERBOSE IF(verbose) get_anything(); ELSE #endif #ifdef TERSE pad(just_a_sec); #endif } if (anytokill) /* if there was anything to kill */ forcelast = FALSE; /* allow for having killed it all */ firstart = oldfirst; } if (!entering && (kf_state & KFS_LOCAL_CHANGES) && !intr) rewrite_kfile(lastart); set_mode(gmode,oldmode); } static FILE* newkfp; static int write_local_thread_commands(keylen, data, extra) int keylen; HASHDATUM* data; int extra; { ARTICLE* ap = (ARTICLE*)data->dat_ptr; int autofl = ap->autofl; char ch; if (autofl && ((ap->flags & AF_EXISTS) || ap->child1)) { int i; /* The arrays are in priority order, so find highest priority bit. */ for (i = 0; thread_cmd_ltr[i]; i++) { if (autofl & thread_cmd_flag[i]) { ch = thread_cmd_ltr[i]; break; } } fprintf(newkfp,"%s T%c\n", ap->msgid, ch); } return 0; } void rewrite_kfile(thru) ART_NUM thru; { bool has_content = (kf_state & (KFS_THREAD_LINES|KFS_GLOBAL_THREADFILE)) == KFS_THREAD_LINES; bool has_star_commands = FALSE; bool needs_newline = FALSE; char* killname = filexp(getval("KILLLOCAL",killlocal)); char* bp; if (localkfp) fseek(localkfp,0L,0); /* rewind current file */ else makedir(killname,MD_FILE); UNLINK(killname); /* to prevent file reuse */ kf_state &= ~(kfs_local_change_clear | KFS_NORMAL_LINES); if ((newkfp = fopen(killname,"w")) != NULL) { fprintf(newkfp,"THRU %s %ld\n",ngptr->rc->name,(long)thru); while (localkfp && fgets(buf,LBUFLEN,localkfp) != NULL) { if (strnEQ(buf,"THRU",4)) { char* cp = buf+4; int len = strlen(ngptr->rc->name); while (isspace(*cp)) cp++; if (isdigit(*cp)) continue; if (strnNE(cp, ngptr->rc->name, len) || (cp[len] && !isspace(cp[len]))) { fputs(buf,newkfp); needs_newline = !index(buf,'\n'); } continue; } for (bp = buf; isspace(*bp); bp++) ; /* Leave out any outdated thread commands */ if (*bp == 'T' || *bp == '<') continue; /* Write star commands after other kill commands */ if (*bp == '*') has_star_commands = TRUE; else { fputs(buf,newkfp); needs_newline = !index(bp,'\n'); } has_content = TRUE; } if (needs_newline) putc('\n', newkfp); if (has_star_commands) { fseek(localkfp,0L,0); /* rewind file */ while (fgets(buf,LBUFLEN,localkfp) != NULL) { for (bp = buf; isspace(*bp); bp++) ; if (*bp == '*') { fputs(buf,newkfp); needs_newline = !index(bp,'\n'); } } if (needs_newline) putc('\n', newkfp); } if (!(kf_state & KFS_GLOBAL_THREADFILE)) { /* Append all the still-valid thread commands */ hashwalk(msgid_hash, write_local_thread_commands, 0); } fclose(newkfp); if (!has_content) UNLINK(killname); open_kfile(KF_LOCAL); /* and reopen local file */ } else printf(cantcreate,buf) FLUSH; } static int write_global_thread_commands(keylen, data, appending) int keylen; HASHDATUM* data; int appending; { int autofl; int i, age; char* msgid; char ch; if (data->dat_len) { if (appending) return 0; autofl = data->dat_len; age = autofl & KF_AGE_MASK; msgid = data->dat_ptr; } else { register ARTICLE* ap = (ARTICLE*)data->dat_ptr; autofl = ap->autofl; if (!autofl || (appending && (autofl & AUTO_OLD))) return 0; ap->autofl |= AUTO_OLD; age = 0; msgid = ap->msgid; } /* The arrays are in priority order, so find highest priority bit. */ for (i = 0; thread_cmd_ltr[i]; i++) { if (autofl & thread_cmd_flag[i]) { ch = thread_cmd_ltr[i]; break; } } fprintf(newkfp,"%s %c %ld\n", msgid, ch, kf_daynum - age); kf_thread_cnt++; return 0; } static int age_thread_commands(keylen, data, elapsed_days) int keylen; HASHDATUM* data; int elapsed_days; { if (data->dat_len) { int age = (data->dat_len & KF_AGE_MASK) + elapsed_days; if (age > KF_MAXDAYS) { free(data->dat_ptr); kf_changethd_cnt++; return -1; } data->dat_len += elapsed_days; } else { register ARTICLE* ap = (ARTICLE*)data->dat_ptr; if (ap->autofl & AUTO_OLD) { ap->autofl &= ~AUTO_OLD; kf_changethd_cnt++; kf_state |= KFS_THREAD_CHANGES; } } return 0; } void update_thread_kfile() { char* cp; int elapsed_days; if (!(kf_state & KFS_GLOBAL_THREADFILE)) return; elapsed_days = KF_DAYNUM(kf_daynum); if (elapsed_days) { hashwalk(msgid_hash, age_thread_commands, elapsed_days); kf_daynum += elapsed_days; } if (!(kf_state & KFS_THREAD_CHANGES)) return; cp = filexp(getval("KILLTHREADS", killthreads)); makedir(cp,MD_FILE); if (kf_changethd_cnt*5 > kf_thread_cnt) { UNLINK(cp); /* to prevent file reuse */ if ((newkfp = fopen(cp,"w")) == NULL) return; /*$$ Yikes! */ kf_thread_cnt = kf_changethd_cnt = 0; hashwalk(msgid_hash, write_global_thread_commands, 0); /* Rewrite */ } else { if ((newkfp = fopen(cp, "a")) == NULL) return; /*$$ Yikes! */ hashwalk(msgid_hash, write_global_thread_commands, 1); /* Append */ } fclose(newkfp); kf_state &= ~KFS_THREAD_CHANGES; } void change_auto_flags(ap, auto_flag) ARTICLE* ap; int auto_flag; { if (auto_flag > (ap->autofl & (AUTO_KILLS|AUTO_SELS))) { if (ap->autofl & AUTO_OLD) kf_changethd_cnt++; ap->autofl = auto_flag; kf_state |= kfs_thread_change_set; } } void clear_auto_flags(ap) ARTICLE* ap; { if (ap->autofl) { if (ap->autofl & AUTO_OLD) kf_changethd_cnt++; ap->autofl = 0; kf_state |= kfs_thread_change_set; } } void perform_auto_flags(ap, thread_autofl, subj_autofl, chain_autofl) ARTICLE* ap; int thread_autofl; int subj_autofl; int chain_autofl; { if (thread_autofl & AUTO_SEL_THD) { if (sel_mode == SM_THREAD) select_arts_thread(ap, AUTO_SEL_THD); else select_arts_subject(ap, AUTO_SEL_THD); } else if (subj_autofl & AUTO_SEL_SBJ) select_arts_subject(ap, AUTO_SEL_SBJ); else if (chain_autofl & AUTO_SEL_FOL) select_subthread(ap, AUTO_SEL_FOL); else if (ap->autofl & AUTO_SEL_1) select_article(ap, AUTO_SEL_1); if (thread_autofl & AUTO_KILL_THD) { if (sel_mode == SM_THREAD) kill_arts_thread(ap, AFFECT_ALL|AUTO_KILL_THD); else kill_arts_subject(ap, AFFECT_ALL|AUTO_KILL_THD); } else if (subj_autofl & AUTO_KILL_SBJ) kill_arts_subject(ap, AFFECT_ALL|AUTO_KILL_SBJ); else if (chain_autofl & AUTO_KILL_FOL) kill_subthread(ap, AFFECT_ALL|AUTO_KILL_FOL); else if (ap->autofl & AUTO_KILL_1) mark_as_read(ap); } #else /* !KILLFILES */ void kfile_init() { ; } #endif /* !KILLFILES */ /* edit KILL file for newsgroup */ int edit_kfile() { #ifdef KILLFILES int r = -1; char* bp; if (in_ng) { if (kf_state & KFS_LOCAL_CHANGES) rewrite_kfile(lastart); if (!(kf_state & KFS_GLOBAL_THREADFILE)) { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) clear_subject(sp); } strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); } else strcpy(buf,filexp(getval("KILLGLOBAL",killglobal))); if ((r = makedir(buf,MD_FILE)) == 0) { sprintf(cmd_buf,"%s %s", filexp(getval("VISUAL",getval("EDITOR",defeditor))),buf); printf("\nEditing %s KILL file:\n%s\n", (in_ng?"local":"global"),cmd_buf) FLUSH; termdown(3); resetty(); /* make sure tty is friendly */ r = doshell(sh,cmd_buf);/* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ open_kfile(in_ng); if (localkfp) { fseek(localkfp,0L,0); /* rewind file */ kf_state &= ~KFS_NORMAL_LINES; while (fgets(buf,LBUFLEN,localkfp) != NULL) { for (bp = buf; isspace(*bp); bp++) ; if (*bp == '/' || *bp == '*') kf_state |= KFS_NORMAL_LINES; else if (*bp == '<') { register ARTICLE* ap; char* cp = index(bp,' '); if (!cp) cp = ","; else *cp++ = '\0'; if ((ap = get_article(bp)) != NULL) { if (*cp == 'T') cp++; if ((cp = index(thread_cmd_ltr, *cp)) != NULL) ap->autofl |= thread_cmd_flag[cp-thread_cmd_ltr]; } } } } } else { printf("Can't make %s\n",buf) FLUSH; termdown(1); } return r; #else /* !KILLFILES */ notincl("^K"); return -1; #endif } #ifdef KILLFILES void open_kfile(local) int local; { char* kname = filexp(local ? getval("KILLLOCAL",killlocal) : getval("KILLGLOBAL",killglobal) ); /* delete the file if it is empty */ if (stat(kname,&filestat) >= 0 && !filestat.st_size) UNLINK(kname); if (local) { if (localkfp) fclose(localkfp); localkfp = fopen(kname,"r"); } else { if (globkfp) fclose(globkfp); globkfp = fopen(kname,"r"); } } void kf_append(cmd, local) char* cmd; bool_int local; { strcpy(cmd_buf, filexp(local? getval("KILLLOCAL",killlocal) : getval("KILLGLOBAL",killglobal))); if (makedir(cmd_buf,MD_FILE) == 0) { #ifdef VERBOSE IF(verbose) printf("\nDepositing command in %s...",cmd_buf); ELSE #endif #ifdef TERSE printf("\n--> %s...",cmd_buf); #endif fflush(stdout); if (novice_delays) sleep(2); if ((tmpfp = fopen(cmd_buf,"a+")) != NULL) { char ch; if (fseek(tmpfp,-1L,2) < 0) ch = '\n'; else ch = getc(tmpfp); fseek(tmpfp,0L,2); if (ch != '\n') putc('\n', tmpfp); fprintf(tmpfp,"%s\n",cmd); fclose(tmpfp); if (local && !localkfp) open_kfile(KF_LOCAL); fputs("done\n",stdout) FLUSH; } else printf(cantopen,cmd_buf) FLUSH; termdown(2); } kf_state |= KFS_NORMAL_LINES; } #endif /* KILLFILES */ trn-4.0-test77/kfile.h0000644000000000000000000000405007212000743013246 0ustar rootroot/* kfile.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #define KF_GLOBAL 0 #define KF_LOCAL 1 #define KFS_LOCAL_CHANGES 0x0001 #define KFS_THREAD_CHANGES 0x0002 #define KFS_NORMAL_LINES 0x0010 #define KFS_THREAD_LINES 0x0020 #define KFS_GLOBAL_THREADFILE 0x1000 #define AUTO_KILL_THD 0x8000 #define AUTO_KILL_SBJ 0x4000 #define AUTO_KILL_FOL 0x2000 #define AUTO_KILL_1 0x1000 #define AUTO_SEL_THD 0x0800 #define AUTO_SEL_SBJ 0x0400 #define AUTO_SEL_FOL 0x0200 #define AUTO_SEL_1 0x0100 #define AUTO_OLD 0x0080 #define AUTO_KILLS 0xF000 #define AUTO_SELS 0x0F00 /* The following defines are only valid as flags to function calls, used * in combination with the AUTO_* flags above. */ #define AFFECT_UNSEL 0 #define AFFECT_ALL 0x0001 #define ALSO_ECHO 0x0002 /* only works with [un]select_article() */ #define SET_TORETURN 0x0004 /* only works with kill_*() */ #define KF_AGE_MASK 0x003F #define KF_DAYNUM(x) ((long)time((time_t*)NULL) / 86400 - 10490 - (x)) #define KF_MAXDAYS 30 #ifdef KILLFILES EXT FILE* globkfp INIT(NULL); /* global article killer file */ EXT FILE* localkfp INIT(NULL); /* local (for this newsgroup) file */ EXT int kf_state; /* the state of our kill files */ EXT int kfs_local_change_clear; /* bits to clear local changes */ EXT int kfs_thread_change_set; /* bits to set for thread changes */ EXT int kf_thread_cnt; /* # entries in the thread kfile */ EXT int kf_changethd_cnt; /* # entries changed from old to new */ EXT long kf_daynum; /* day number for thread killfile */ EXT ART_NUM killfirst; /* used as firstart when killing */ #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void kfile_init _((void)); int do_kfile _((FILE*,int)); void kill_unwanted _((ART_NUM,char*,int)); void rewrite_kfile _((ART_NUM)); void update_thread_kfile _((void)); void change_auto_flags _((ARTICLE*,int)); void clear_auto_flags _((ARTICLE*)); void perform_auto_flags _((ARTICLE*,int,int,int)); int edit_kfile _((void)); #ifdef KILLFILES void open_kfile _((int)); #endif void kf_append _((char*,bool_int)); trn-4.0-test77/kfile.ih0000644000000000000000000000062507113133016013423 0ustar rootroot/* kfile.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void mention _((char*)); static bool kfile_junk _((char*,int)); static int write_local_thread_commands _((int,HASHDATUM*,int)); static int write_global_thread_commands _((int,HASHDATUM*,int)); static int age_thread_commands _((int,HASHDATUM*,int)); trn-4.0-test77/last.c0000644000000000000000000000262207113133016013115 0ustar rootroot/* last.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "list.h" #include "util.h" #include "util2.h" #include "intrp.h" #include "init.h" #include "INTERN.h" #include "last.h" static char* lastfile = NULL; /* path name of .rnlast file */ static long starttime; void last_init() { lastfile = savestr(filexp(LASTNAME)); starttime = (long)time((time_t*)NULL); readlast(); } void readlast() { if ((tmpfp = fopen(lastfile,"r")) != NULL) { if (fgets(buf,sizeof buf,tmpfp) != NULL) { long old_last = lasttime; buf[strlen(buf)-1] = '\0'; if (*buf) { safefree0(lastngname); lastngname = savestr(buf); } fscanf(tmpfp,"%ld %ld %ld %ld",&lasttime,&lastactsiz, &lastnewtime,&lastextranum); if (!lastnewtime) lastnewtime = starttime; if (old_last > lasttime) lasttime = old_last; } fclose(tmpfp); } } /* Put out certain values for next run of trn */ void writelast() { sprintf(buf,"%s.%ld", lastfile, our_pid); if ((tmpfp = fopen(buf,"w")) != NULL) { if (lasttime < starttime) lasttime = starttime; fprintf(tmpfp,"%s\n%ld\n%ld\n%ld\n%ld\n", ngname? ngname : nullstr,lasttime, lastactsiz,lastnewtime,lastextranum); fclose(tmpfp); UNLINK(lastfile); RENAME(buf,lastfile); } else { printf(cantcreate,buf) FLUSH; /*termdown(1);*/ } } trn-4.0-test77/last.h0000644000000000000000000000076507113133016013130 0ustar rootroot/* last.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* lastngname INIT(NULL); /* last newsgroup read */ EXT long lasttime INIT(0); /* time last we ran */ EXT long lastactsiz INIT(0); /* last known size of active file */ EXT long lastnewtime INIT(0); /* time of last newgroup request */ EXT long lastextranum INIT(0); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void last_init _((void)); void readlast _((void)); void writelast _((void)); trn-4.0-test77/list.c0000644000000000000000000001256307113133016013132 0ustar rootroot/* list.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "INTERN.h" #include "list.h" #include "list.ih" void list_init() { ; } /* Create the header for a dynamic list of items. */ LIST* new_list(low, high, item_size, items_per_node, flags, init_node) long low; long high; int item_size; int items_per_node; int flags; void (*init_node) _((LIST*,LISTNODE*)); { LIST* list = (LIST*)safemalloc(sizeof (LIST)); list->first = list->recent = NULL; list->init_node = init_node? init_node : def_init_node; list->low = low; list->high = high; list->item_size = item_size; list->items_per_node = items_per_node; list->flags = flags; return list; } /* The default way to initialize a node */ static void def_init_node(list, node) LIST* list; LISTNODE* node; { if (list->flags & LF_ZERO_MEM) bzero(node->data, list->items_per_node * list->item_size); } /* Take the number of a list element and return its pointer. This ** will allocate new list items as needed, keeping the list->high ** value up to date. */ char* listnum2listitem(list, num) LIST* list; long num; { LISTNODE* node = list->recent; LISTNODE* prevnode = NULL; if (node && num < node->low) node = list->first; for (;;) { if (!node || num < node->low) { node = (LISTNODE*)safemalloc(list->items_per_node*list->item_size + sizeof (LISTNODE) - 1); if (list->flags & LF_SPARSE) node->low = ((num - list->low) / list->items_per_node) * list->items_per_node + list->low; else node->low = num; node->high = node->low + list->items_per_node - 1; node->data_high = node->data + (list->items_per_node - 1) * list->item_size; if (node->high > list->high) list->high = node->high; if (prevnode) { node->next = prevnode->next; prevnode->next = node; } else { node->next = list->first; list->first = node; } /*node->mid = $$;*/ list->init_node(list, node); break; } if (num <= node->high) break; prevnode = node; node = node->next; } list->recent = node; return node->data + (num - node->low) * list->item_size; } /* Take the pointer of a list element and return its number. The item ** must already exist or this will infinite loop. */ long listitem2listnum(list, ptr) LIST* list; char* ptr; { LISTNODE* node; char* cp; int i; int item_size = list->item_size; for (node = list->recent; ; node = node->next) { if (!node) node = list->first; i = node->high - node->low + 1; for (cp = node->data; i--; cp += item_size) { if (ptr == cp) { list->recent = node; return (ptr - node->data) / list->item_size + node->low; } } } return -1; } /* Execute the indicated callback function on every item in the list. */ bool walk_list(list, callback, arg) LIST* list; bool (*callback) _((char*,int)); int arg; { LISTNODE* node; char* cp; int i; int item_size = list->item_size; for (node = list->first; node; node = node->next) { i = node->high - node->low + 1; for (cp = node->data; i--; cp += item_size) if (callback(cp, arg)) return 1; } return 0; } /* Since the list can be sparsely allocated, find the nearest number ** that is already allocated, rounding in the indicated direction from ** the initial list number. */ long existing_listnum(list, num, direction) LIST* list; long num; int direction; { register LISTNODE* node = list->recent; LISTNODE* prevnode = NULL; if (node && num < node->low) node = list->first; while (node) { if (num <= node->high) { if (direction > 0) { if (num < node->low) num = node->low; } else if (direction == 0) { if (num < node->low) num = 0; } else if (num < node->low) { if (!prevnode) break; list->recent = prevnode; return prevnode->high; } list->recent = node; return num; } prevnode = node; node = node->next; } if (!direction) return 0; if (direction > 0) return list->high + 1; return list->low - 1; } /* Increment the item pointer to the next allocated item. ** Returns NULL if ptr is the last one. */ char* next_listitem(list, ptr) LIST* list; char* ptr; { register LISTNODE* node = list->recent; if (ptr == node->data_high) { node = node->next; if (!node) return NULL; list->recent = node; return node->data; } #if 0 if (node->high > list->high) { if ((ptr - node->data) / list->item_size + node->low >= list->high) return NULL; } #endif return ptr += list->item_size; } /* Decrement the item pointer to the prev allocated item. ** Returns NULL if ptr is the first one. */ char* prev_listitem(list, ptr) LIST* list; char* ptr; { register LISTNODE* node = list->recent; if (ptr == node->data) { LISTNODE* prev = list->first; if (prev == node) return NULL; while (prev->next != node) prev = prev->next; list->recent = prev; return prev->data_high; } return ptr -= list->item_size; } /* Delete the list and all its allocated nodes. If you need to cleanup ** the individual nodes, call walk_list() with a cleanup function before ** calling this. */ void delete_list(list) LIST* list; { LISTNODE* node = list->first; LISTNODE* prevnode = NULL; while (node) { prevnode = node; node = node->next; free((char*)prevnode); } free((char*)list); } trn-4.0-test77/list.h0000644000000000000000000000165007113133016013132 0ustar rootroot/* list.h */ /* This software is copyrighted as detailed in the LICENSE file. */ struct listnode { LISTNODE* next; /*LISTNODE* mid;*/ long low; long high; char* data_high; char data[1]; /* this is actually longer */ }; struct list { LISTNODE* first; LISTNODE* recent; void (*init_node) _((LIST*,LISTNODE*)); long low; long high; int item_size; int items_per_node; int flags; }; #define LF_ZERO_MEM 0x0001 #define LF_SPARSE 0x0002 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void list_init _((void)); LIST* new_list _((long,long,int,int,int,void(*) _((LIST*,LISTNODE*)))); char* listnum2listitem _((LIST*,long)); long listitem2listnum _((LIST*,char*)); bool walk_list _((LIST*,bool(*) _((char*,int)),int)); long existing_listnum _((LIST*,long,int)); char* next_listitem _((LIST*,char*)); char* prev_listitem _((LIST*,char*)); void delete_list _((LIST*)); trn-4.0-test77/list.ih0000644000000000000000000000030407113133016013276 0ustar rootroot/* list.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void def_init_node _((LIST*,LISTNODE*)); trn-4.0-test77/makedepend.SH0000644000000000000000000000527007141205160014342 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting makedepend (with variable substitutions)" if test "X$src" = "X." ; then vincludes='' else vincludes="-I. -I$src" fi $spitshell >makedepend <>makedepend <<'!NO!SUBS!' deptmp=$objdir/.deptmp $cat /dev/null >$deptmp $echo "(Note: this is going to take a while.)" cd $srcdir for file in *.[cy]; do case "$file" in *.c) filebase=`$basename $file .c`;; *.y) filebase=`$basename $file .y`;; esac $echo "Finding dependencies for $filebase.o." $sed -n <$file \ -e '/^#/{' \ -e 's|/\*.*$||' \ -e 's/\\[ ]*$//' \ -e p \ -e '}' | $cc_cmd | $sed \ -e '/^# *line/s/line//' \ -e '/^# *[0-9]*[ ]*"/!d' \ -e 's/^.*"\(.*\)".*$/'$filebase$objext': \1/' \ -e 's|: \./|: |' \ -e "s|$objdir/||" \ -e 's/: *$/: '$file'/' \ -e 's/: .*\.c/: '$file'/' | \ $uniq | $sort | $uniq >>$deptmp done for file in *.SH; do case "$file" in Makefile.SH) ;; newsnews.SH) $echo `$basename $file .SH | $sed -e 's/_/./'`: $file \; /bin/sh "\$(srcdir)/$file" >>$deptmp ;; environment.SH) $echo HelpFiles/config/environment: $file config.sh makedir \; /bin/sh "\$(srcdir)/$file" >>$deptmp ;; *) $echo `$basename $file .SH | $sed -e 's/_/./'`: $file config.sh \; /bin/sh "\$(srcdir)/$file" >>$deptmp ;; esac done cd $objdir $sed Makefile.new -e '1,/^# AUTOMATICALLY/!d' if $test -s $deptmp; then $echo "Updating Makefile..." $sed -e 's/\$[^(]/$$/g' $deptmp | $egrep -v "$ignorefiles" >dependencies else $echo "You don't seem to have a proper C preprocessor. Using egrep instead." cd $srcdir $egrep '^#include ' *.[cyh] ?.[cyh] >$deptmp cd $objdir $echo "Updating Makefile..." <$deptmp $sed -n 's|c:#include "\(.*\)".*$$|o: \1|p' > dependencies <$deptmp $sed -n 's|y:#include "\(.*\)".*$$|o: \1|p' >> dependencies <$deptmp $sed -n 's|h:#include "\(.*\)".*$$|h: \1|p' >> dependencies fi $echo "# WARNING: Put nothing here or make depend will gobble it up!" >> dependencies $mv Makefile Makefile.old $mv Makefile.new Makefile $cat dependencies >>Makefile $rm $deptmp !NO!SUBS! $eunicefix makedepend chmod 755 makedepend trn-4.0-test77/makedir.SH0000644000000000000000000000164307113133016013660 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting makedir (with variable substitutions)" $spitshell >makedir </dev/null 2>&1 done !GROK!THIS! $eunicefix makedir chmod +x makedir trn-4.0-test77/mbox_saver.SH0000644000000000000000000000226107222721072014414 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting mbox.saver (with variable substitutions)" $spitshell >mbox.saver <>mbox.saver <> \$7 !GROK!THIS! ;; *) $spitshell >>mbox.saver <&/" $echo "" $echo "" ) >> \$7 !GROK!THIS! ;; esac $eunicefix mbox.saver chmod 755 mbox.saver trn-4.0-test77/mempool.c0000644000000000000000000000715207113133016013625 0ustar rootroot/* * mempool.c */ #include "EXTERN.h" #include "common.h" #include "final.h" #include "util.h" #include "util2.h" #include "INTERN.h" #include "mempool.h" #include "mempool.ih" /* any of these defines can be increased arbitrarily */ #define MAX_MEM_POOLS 16 #define MAX_MEM_FRAGS 4000 /* number of bytes in a fragment */ #define FRAG_SIZE ((1024*64)-32) typedef struct mp_frag { char* data; char* lastfree; long bytesfree; int next; } MP_FRAG; /* structure for extensibility */ typedef struct mp_head { int current; /* index into mp_frag of most recent alloc */ } MP_HEAD; static MP_FRAG mpfrags[MAX_MEM_FRAGS]; /* zero is unused */ static int mp_first_free_frag; static MP_HEAD mpheads[MAX_MEM_POOLS]; void mp_init() { int i; for (i = 1; i < MAX_MEM_FRAGS; i++) { mpfrags[i].data = NULL; mpfrags[i].lastfree = NULL; mpfrags[i].bytesfree = 0; mpfrags[i].next = i+1; } mpfrags[i-1].next = 0; mp_first_free_frag = 1; /* first free fragment */ for (i = 0; i < MAX_MEM_POOLS; i++) mpheads[i].current = 0; } /* returns the fragment number */ static int mp_alloc_frag() { int f; f = mp_first_free_frag; if (f == 0) { printf("trn: out of memory pool fragments!\n"); sig_catcher(0); /* die. */ } mp_first_free_frag = mpfrags[f].next; if (mpfrags[f].bytesfree) return f; /* already allocated */ mpfrags[f].data = (char*)safemalloc(FRAG_SIZE); mpfrags[f].lastfree = mpfrags[f].data; mpfrags[f].bytesfree = FRAG_SIZE; return f; } /* frees a fragment number */ static void mp_free_frag(f) int f; { #if 0 /* old code to actually free the blocks */ if (mpfrags[f].data) free(mpfrags[f].data); mpfrags[f].lastfree = NULL; mpfrags[f].bytesfree = 0; #else /* instead of freeing it, reset it for later use */ mpfrags[f].lastfree = mpfrags[f].data; mpfrags[f].bytesfree = FRAG_SIZE; #endif mpfrags[f].next = mp_first_free_frag; mp_first_free_frag = f; } char* mp_savestr(str,pool) char* str; int pool; { int f, oldf; int len; char* s; if (!str) { #if 1 printf("\ntrn: mp_savestr(NULL,%d) error.\n",pool); assert(FALSE); #else return NULL; /* only a flesh wound... (;-) */ #endif } len = strlen(str); if (len >= FRAG_SIZE) { printf("trn: string too big (len = %d) for memory pool!\n",len); printf("trn: (maximum length allowed is %d)\n",FRAG_SIZE); sig_catcher(0); /* die. */ } f = mpheads[pool].current; /* just to be extra safe, keep 2 bytes unused at end of block */ if (f == 0 || len >= mpfrags[f].bytesfree-2) { oldf = f; f = mp_alloc_frag(); mpfrags[f].next = oldf; mpheads[pool].current = f; } s = mpfrags[f].lastfree; safecpy(s,str,len+1); mpfrags[f].lastfree += len+1; mpfrags[f].bytesfree -= len+1; return s; } /* returns a pool-allocated string */ char* mp_malloc(len,pool) int len; int pool; { int f,oldf; char* s; if (len == 0) len = 1; if (len >= FRAG_SIZE) { printf("trn: malloc size too big (len = %d) for memory pool!\n",len); printf("trn: (maximum length allowed is %d)\n",FRAG_SIZE); sig_catcher(0); /* die. */ } f = mpheads[pool].current; if (f == 0 || len >= mpfrags[f].bytesfree) { oldf = f; f = mp_alloc_frag(); mpfrags[f].next = oldf; mpheads[pool].current = f; } s = mpfrags[f].lastfree; mpfrags[f].lastfree += len+1; mpfrags[f].bytesfree -= len+1; return s; } /* free a whole memory pool */ void mp_free(pool) int pool; { int oldnext; int f; f = mpheads[pool].current; while (f) { oldnext = mpfrags[f].next; mp_free_frag(f); f = oldnext; } mpheads[pool].current = 0; } trn-4.0-test77/mempool.h0000644000000000000000000000054507113133016013631 0ustar rootroot/* * mempool.h */ /* memory pool numbers */ /* scoring rule text */ #define MP_SCORE1 0 /* scorefile cache */ #define MP_SCORE2 1 /* sathread.c storage */ #define MP_SATHREAD 2 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void mp_init _((void)); char* mp_savestr _((char*,int)); char* mp_malloc _((int,int)); void mp_free _((int)); trn-4.0-test77/mempool.ih0000644000000000000000000000033607113133016014000 0ustar rootroot/* mempool.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int mp_alloc_frag _((void)); static void mp_free_frag _((int)); trn-4.0-test77/mime.c0000644000000000000000000010477511437640112013121 0ustar rootroot/* mime.c */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "cache.h" #include "head.h" #include "search.h" #include "art.h" #include "artio.h" #include "artstate.h" #include "ng.h" #include "term.h" #include "decode.h" #include "respond.h" #include "env.h" #include "color.h" #include "util.h" #include "util2.h" #include "backpage.h" #include "charsubst.h" #include "INTERN.h" #include "mime.h" #include "mime.ih" static char text_plain[] = "text/plain"; void mime_init() { char* s; char* t; char* mcname; mimecap_list = new_list(0,-1,sizeof(MIMECAP_ENTRY),40,LF_ZERO_MEM,NULL); if ((mcname = getenv("MIMECAPS")) == NULL) mcname = getval("MAILCAPS", MIMECAP); mcname = s = savestr(mcname); do { if ((t = index(s, ':')) != NULL) *t++ = '\0'; if (*s) mime_ReadMimecap(s); s = t; } while (s && *s); free(mcname); } void mime_ReadMimecap(mcname) char* mcname; { FILE* fp; char* bp; char* s; char* t; char* arg; int buflen = 2048; int linelen; MIMECAP_ENTRY* mcp; int i; if ((fp = fopen(filexp(mcname), "r")) == NULL) return; bp = safemalloc(buflen); for (i = mimecap_list->high; !feof(fp); ) { *(s = bp) = '\0'; linelen = 0; while (fgets(s, buflen - linelen, fp)) { if (*s == '#') continue; linelen += strlen(s); if (linelen == 0) continue; if (bp[linelen-1] == '\n') { if (--linelen == 0) continue; if (bp[linelen-1] != '\\') { bp[linelen] = '\0'; break; } bp[--linelen] = '\0'; } if (linelen+1024 > buflen) { buflen *= 2; bp = saferealloc(bp, buflen); } s = bp + linelen; } for (s = bp; isspace(*s); s++) ; if (!*s) continue; t = mime_ParseEntryArg(&s); if (!s) { fprintf(stderr, "trn: Ignoring invalid mimecap entry: %s\n", bp); continue; } mcp = mimecap_ptr(++i); mcp->contenttype = savestr(t); mcp->command = savestr(mime_ParseEntryArg(&s)); while (s) { t = mime_ParseEntryArg(&s); if ((arg = index(t, '=')) != NULL) { char* f = arg+1; while (arg != t && isspace(arg[-1])) arg--; *arg++ = '\0'; while (isspace(*f)) f++; if (*f == '"') f = cpytill(arg,f+1,'"'); else arg = f; } if (*t) { if (strcaseEQ(t, "needsterminal")) mcp->flags |= MCF_NEEDSTERMINAL; else if (strcaseEQ(t, "copiousoutput")) mcp->flags |= MCF_COPIOUSOUTPUT; else if (arg && strcaseEQ(t, "test")) mcp->testcommand = savestr(arg); else if (arg && strcaseEQ(t, "description")) mcp->label = savestr(arg); else if (arg && strcaseEQ(t, "label")) mcp->label = savestr(arg); /* bogus old name for description */ } } } mimecap_list->high = i; free(bp); fclose(fp); } static char* mime_ParseEntryArg(cpp) char** cpp; { char* s = *cpp; char* f; char* t; while (isspace(*s)) s++; for (f = t = s; *f && *f != ';'; ) { if (*f == '\\') { if (*++f == '%') *t++ = '%'; else if (!*f) break; } *t++ = *f++; } while (isspace(*f) || *f == ';') f++; if (!*f) f = NULL; while (t != s && isspace(t[-1])) t--; *t = '\0'; *cpp = f; return s; } MIMECAP_ENTRY* mime_FindMimecapEntry(contenttype, skip_flags) char* contenttype; int skip_flags; { MIMECAP_ENTRY* mcp; int i; for (i = 0; i <= mimecap_list->high; i++) { mcp = mimecap_ptr(i); if (!(mcp->flags & skip_flags) && mime_TypesMatch(contenttype, mcp->contenttype)) { if (!mcp->testcommand) return mcp; if (mime_Exec(mcp->testcommand) == 0) return mcp; } } return NULL; } bool mime_TypesMatch(ct,pat) char* ct; char* pat; { char* s = index(pat,'/'); int len = (s? s - pat : strlen(pat)); bool iswild = (!s || strEQ(s+1,"*")); return strcaseEQ(ct,pat) || (iswild && strncaseEQ(ct,pat,len) && ct[len] == '/'); } int mime_Exec(cmd) char* cmd; { char* f; char* t; for (f = cmd, t = cmd_buf; *f && t-cmd_buf < CBUFLEN-2; f++) { if (*f == '%') { switch (*++f) { case 's': safecpy(t, decode_filename, CBUFLEN-(t-cmd_buf)); t += strlen(t); break; case 't': *t++ = '\''; safecpy(t, mime_section->type_name, CBUFLEN-(t-cmd_buf)-1); t += strlen(t); *t++ = '\''; break; case '{': { char* s = index(f, '}'); char* p; if (!s) return -1; f++; *s = '\0'; p = mime_FindParam(mime_section->type_params, f); *s = '}'; /* restore */ f = s; *t++ = '\''; safecpy(t, p, CBUFLEN-(t-cmd_buf)-1); t += strlen(t); *t++ = '\''; break; } case '%': *t++ = '%'; break; case 'n': case 'F': return -1; } } else *t++ = *f; } *t = '\0'; return doshell(sh, cmd_buf); } void mime_InitSections() { while (mime_PopSection()) ; mime_ClearStruct(mime_section); mime_state = NOT_MIME; } void mime_PushSection() { MIME_SECT* mp = (MIME_SECT*)safemalloc(sizeof (MIME_SECT)); bzero((char*)mp, sizeof (MIME_SECT)); mp->prev = mime_section; mime_section = mp; } bool mime_PopSection() { MIME_SECT* mp = mime_section->prev; if (mp) { mime_ClearStruct(mime_section); free((char*)mime_section); mime_section = mp; mime_state = mp->type; return TRUE; } mime_state = mime_article.type; return FALSE; } /* Free up this mime structure's resources */ void mime_ClearStruct(mp) MIME_SECT* mp; { safefree0(mp->filename); safefree0(mp->type_name); safefree0(mp->type_params); safefree0(mp->boundary); safefree0(mp->html_blks); mp->type = NOT_MIME; mp->encoding = MENCODE_NONE; mp->part = mp->total = mp->boundary_len = mp->flags = mp->html = mp->html_blkcnt = 0; mp->html_line_start = 0; } /* Setup mime_article structure based on article's headers */ void mime_SetArticle() { char* s; mime_InitSections(); /*$$ Check mime version #? */ multimedia_mime = FALSE; is_mime = (htype[MIMEVER_LINE].flags & HT_MAGIC) && htype[MIMEVER_LINE].minpos >= 0; if (is_mime) { s = fetchlines(art,CONTXFER_LINE); mime_ParseEncoding(mime_section,s); free(s); s = fetchlines(art,CONTTYPE_LINE); mime_ParseType(mime_section,s); free(s); s = fetchlines(art,CONTDISP_LINE); mime_ParseDisposition(mime_section,s); free(s); mime_state = mime_section->type; if (mime_state == NOT_MIME || (mime_state == TEXT_MIME && mime_section->encoding == MENCODE_NONE)) is_mime = FALSE; else if (!mime_section->type_name) mime_section->type_name = savestr(text_plain); } } /* Use the Content-Type to set values in the mime structure */ void mime_ParseType(mp, s) MIME_SECT* mp; char* s; { char* t; safefree0(mp->type_name); safefree0(mp->type_params); mp->type_params = mime_ParseParams(s); if (!*s) { mp->type = NOT_MIME; return; } mp->type_name = savestr(s); t = mime_FindParam(mp->type_params,"name"); if (t) { safefree(mp->filename); mp->filename = savestr(t); } if (strncaseEQ(s, "text", 4)) { mp->type = TEXT_MIME; s += 4; if (*s++ != '/') return; #if 0 t = mime_FindParam(mp->type_params,"charset"); if (t && strncaseNE(t, "us-ascii", 8)) mp->type = ISOTEXT_MIME; #endif if (strncaseEQ(s, "html", 4)) mp->type = HTMLTEXT_MIME; else if (strncaseEQ(s, "x-vcard", 7)) mp->type = UNHANDLED_MIME; return; } if (strncaseEQ(s, "message/", 8)) { s += 8; mp->type = MESSAGE_MIME; if (strcaseEQ(s, "partial")) { t = mime_FindParam(mp->type_params,"id"); if (!t) return; safefree(mp->filename); mp->filename = savestr(t); t = mime_FindParam(mp->type_params,"number"); if (t) mp->part = (short)atoi(t); t = mime_FindParam(mp->type_params,"total"); if (t) mp->total = (short)atoi(t); if (!mp->total) { mp->part = 0; return; } return; } return; } if (strncaseEQ(s, "multipart/", 10)) { s += 10; t = mime_FindParam(mp->type_params,"boundary"); if (!t) { mp->type = UNHANDLED_MIME; return; } if (strncaseEQ(s, "alternative", 11)) mp->flags |= MSF_ALTERNATIVE; safefree(mp->boundary); mp->boundary = savestr(t); mp->boundary_len = (short)strlen(t); mp->type = MULTIPART_MIME; return; } if (strncaseEQ(s, "image/", 6)) { mp->type = IMAGE_MIME; return; } if (strncaseEQ(s, "audio/", 6)) { mp->type = AUDIO_MIME; return; } mp->type = UNHANDLED_MIME; } /* Use the Content-Disposition to set values in the mime structure */ void mime_ParseDisposition(mp, s) MIME_SECT* mp; char* s; { char* params; params = mime_ParseParams(s); if (strcaseEQ(s,"inline")) mp->flags |= MSF_INLINE; s = mime_FindParam(params,"filename"); if (s) { safefree(mp->filename); mp->filename = savestr(s); } safefree(params); } /* Use the Content-Transfer-Encoding to set values in the mime structure */ void mime_ParseEncoding(mp, s) MIME_SECT* mp; char* s; { s = mime_SkipWhitespace(s); if (!*s) { mp->encoding = MENCODE_NONE; return; } if (*s == '7' || *s == '8') { if (strncaseEQ(s+1, "bit", 3)) { s += 4; mp->encoding = MENCODE_NONE; } } else if (strncaseEQ(s, "quoted-printable", 16)) { s += 16; mp->encoding = MENCODE_QPRINT; } else if (strncaseEQ(s, "binary", 6)) { s += 6; mp->encoding = MENCODE_NONE; } else if (strncaseEQ(s, "base64", 6)) { s += 6; mp->encoding = MENCODE_BASE64; } else if (strncaseEQ(s, "x-uue", 5)) { s += 5; mp->encoding = MENCODE_UUE; if (strncaseEQ(s, "ncode", 5)) s += 5; } else { mp->encoding = MENCODE_UNHANDLED; return; } if (*s != '\0' && !isspace(*s) && *s != ';' && *s != '(') mp->encoding = MENCODE_UNHANDLED; } /* Parse a multipart mime header and affect the *mime_section structure */ void mime_ParseSubheader(ifp, next_line) FILE* ifp; char* next_line; { static char* line = NULL; static int line_size = 0; char* s; int pos, linetype, len; mime_ClearStruct(mime_section); mime_section->type = TEXT_MIME; for (;;) { for (pos = 0; ; pos += strlen(line+pos)) { len = pos + (next_line? strlen(next_line) : 0) + LBUFLEN; if (line_size < len) { line_size = len + LBUFLEN; line = saferealloc(line, line_size); } if (next_line) { safecpy(line+pos, next_line, line_size - pos); next_line = NULL; } else if (ifp) { if (!fgets(line + pos, LBUFLEN, ifp)) break; } else if (!readart(line + pos, LBUFLEN)) break; if (line[0] == '\n') break; if (pos && line[pos] != ' ' && line[pos] != '\t') { next_line = line + pos; line[pos-1] = '\0'; break; } } s = index(line,':'); if (s == NULL) break; linetype = set_line_type(line,s); switch (linetype) { case CONTTYPE_LINE: mime_ParseType(mime_section,s+1); break; case CONTXFER_LINE: mime_ParseEncoding(mime_section,s+1); break; case CONTDISP_LINE: mime_ParseDisposition(mime_section,s+1); break; case CONTNAME_LINE: safefree(mime_section->filename); s = mime_SkipWhitespace(s+1); mime_section->filename = savestr(s); break; #if 0 case CONTLEN_LINE: mime_section->content_len = atol(s+1); break; #endif } } mime_state = mime_section->type; if (!mime_section->type_name) mime_section->type_name = savestr(text_plain); } void mime_SetState(bp) char* bp; { int ret; if (mime_state == BETWEEN_MIME) { mime_ParseSubheader((FILE*)NULL,bp); *bp = '\0'; if (mime_section->prev->flags & MSF_ALTERNADONE) mime_state = ALTERNATE_MIME; else if (mime_section->prev->flags & MSF_ALTERNATIVE) mime_section->prev->flags |= MSF_ALTERNADONE; } while (mime_state == MESSAGE_MIME) { mime_PushSection(); mime_ParseSubheader((FILE*)NULL,*bp? bp : (char*)NULL); *bp = '\0'; } if (mime_state == MULTIPART_MIME) { mime_PushSection(); mime_state = SKIP_MIME; /* Skip anything before 1st part */ } ret = mime_EndOfSection(bp); switch (ret) { case 0: break; case 1: while (!mime_section->prev->boundary_len) mime_PopSection(); mime_state = BETWEEN_MIME; break; case 2: while (!mime_section->prev->boundary_len) mime_PopSection(); mime_PopSection(); mime_state = END_OF_MIME; break; } } int mime_EndOfSection(bp) char* bp; { MIME_SECT* mp = mime_section->prev; while (mp && !mp->boundary_len) mp = mp->prev; if (mp) { /* have we read all the data in this part? */ if (bp[0] == '-' && bp[1] == '-' && strnEQ(bp+2,mp->boundary,mp->boundary_len)) { int len = 2 + mp->boundary_len; /* have we found the last boundary? */ if (bp[len] == '-' && bp[len+1] == '-' && (bp[len+2] == '\n' || bp[len+2] == '\0')) return 2; return bp[len] == '\n' || bp[len] == '\0'; } } return 0; } /* Return a saved string of all the extra parameters on this mime * header line. The passed-in string is transformed into just the * first word on the line. */ char* mime_ParseParams(str) char* str; { char* s; char* t; char* e; s = e = mime_SkipWhitespace(str); while (*e && *e != ';' && !isspace(*e) && *e != '(') e++; t = savestr(mime_SkipWhitespace(e)); *e = '\0'; if (s != str) safecpy(str, s, e - s + 1); str = s = t; while (*s == ';') { s = mime_SkipWhitespace(s+1); while (*s && *s != ';' && *s != '(' && *s != '=' && !isspace(*s)) *t++ = *s++; s = mime_SkipWhitespace(s); if (*s == '=') { *t++ = *s; s = mime_SkipWhitespace(s+1); if (*s == '"') { s = cpytill(t,s+1,'"'); if (*s == '"') s++; t += strlen(t); } else while (*s && *s != ';' && !isspace(*s) && *s != '(') *t++ = *s++; } *t++ = '\0'; } *t = '\0'; return str; } char* mime_FindParam(s, param) char* s; char* param; { int param_len = strlen(param); while (s && *s) { if (strncaseEQ(s, param, param_len) && s[param_len] == '=') return s + param_len + 1; s += strlen(s) + 1; } return NULL; } /* Skip whitespace and RFC-822 comments. */ char* mime_SkipWhitespace(s) char* s; { int comment_level = 0; while (*s) { if (*s == '(') { s++; comment_level++; while (comment_level) { switch (*s++) { case '\0': return s-1; case '\\': s++; break; case '(': comment_level++; break; case ')': comment_level--; break; } } } else if (!isspace(*s)) break; else s++; } return s; } void mime_DecodeArticle(view) bool_int view; { MIMECAP_ENTRY* mcp = NULL; seekart(savefrom); *art_line = '\0'; while (1) { if (mime_state != MESSAGE_MIME || !mime_section->total) { if (!readart(art_line,sizeof art_line)) break; mime_SetState(art_line); } switch (mime_state) { case BETWEEN_MIME: case END_OF_MIME: break; case TEXT_MIME: case HTMLTEXT_MIME: case ISOTEXT_MIME: case MESSAGE_MIME: /* $$ Check for uuencoded file here? */ mime_state = SKIP_MIME; /* FALL THROUGH */ case SKIP_MIME: { MIME_SECT* mp = mime_section; while ((mp = mp->prev) != NULL && !mp->boundary_len) ; if (!mp) return; break; } default: if (view) { mcp = mime_FindMimecapEntry(mime_section->type_name,0); if (!mcp) { printf("No view method for %s -- skipping.\n", mime_section->type_name); mime_state = SKIP_MIME; break; } } mime_state = DECODE_MIME; if (decode_piece(mcp, *art_line == '\n'? NULL : art_line) != 0) { mime_SetState(art_line); if (mime_state == DECODE_MIME) mime_state = SKIP_MIME; } else { if (*msg) { newline(); fputs(msg,stdout); } mime_state = SKIP_MIME; } newline(); break; } } } void mime_Description(mp, s, limit) MIME_SECT* mp; char* s; int limit; { char* fn = decode_fix_fname(mp->filename); int len, flen = strlen(fn); limit -= 2; /* leave room for the trailing ']' and '\n' */ sprintf(s, "[Attachment type=%s, name=", mp->type_name); len = strlen(s); if (len + flen <= limit) sprintf(s+len, "%s]\n", fn); else if (len+3 >= limit) strcpy(s+limit-3, "...]\n"); else { #if 0 sprintf(s+len, "...%s]\n", fn + flen - (limit-(len+3))); #else safecpy(s+len, fn, limit - (len+3)); strcat(s, "...]\n"); #endif } } #define XX 255 static Uchar index_hex[256] = { XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX, XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, }; int qp_decodestring(t, f, in_header) char* t; char* f; bool_int in_header; { char* save_t = t; while (*f) { switch (*f) { case '_': if (in_header) { *t++ = ' '; f++; } else *t++ = *f++; break; case '=': /* decode a hex-value */ if (f[1] == '\n') { f += 2; break; } if (index_hex[(Uchar)f[1]] != XX && index_hex[(Uchar)f[2]] != XX) { *t = (index_hex[(Uchar)f[1]] << 4) + index_hex[(Uchar)f[2]]; f += 3; if (*t != '\r') t++; break; } /* FALL THROUGH */ default: *t++ = *f++; break; } } *t = '\0'; return t - save_t; } int qp_decode(ifp,state) FILE* ifp; int state; { static FILE* ofp = NULL; int c1, c2; if (state == DECODE_DONE) { if (ofp) fclose(ofp); ofp = NULL; return state; } if (state == DECODE_START) { char* filename = decode_fix_fname(mime_section->filename); ofp = fopen(filename, FOPEN_WB); if (!ofp) return DECODE_ERROR; erase_line(0); printf("Decoding %s", filename); if (nowait_fork) fflush(stdout); else newline(); } while ((c1 = mime_getc(ifp)) != EOF) { check_c1: if (c1 == '=') { c1 = mime_getc(ifp); if (c1 == '\n') continue; if (index_hex[(Uchar)c1] == XX) { putc('=', ofp); goto check_c1; } c2 = mime_getc(ifp); if (index_hex[(Uchar)c2] == XX) { putc('=', ofp); putc(c1, ofp); c1 = c2; goto check_c1; } c1 = (index_hex[(Uchar)c1] << 4) | index_hex[(Uchar)c2]; if (c1 != '\r') putc(c1, ofp); } else putc(c1, ofp); } return DECODE_MAYBEDONE; } static Uchar index_b64[256] = { XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, }; int b64_decodestring(t, f) char* t; char* f; { char* save_t = t; Uchar ch1, ch2; while (*f && *f != '=') { ch1 = index_b64[(Uchar)*f++]; if (ch1 == XX) continue; do { if (!*f || *f == '=') goto dbl_break; ch2 = index_b64[(Uchar)*f++]; } while (ch2 == XX); *t++ = (ch1 << 2) | (ch2 >> 4); do { if (!*f || *f == '=') goto dbl_break; ch1 = index_b64[(Uchar)*f++]; } while (ch1 == XX); *t++ = ((ch2 & 0x0f) << 4) | (ch1 >> 2); do { if (!*f || *f == '=') goto dbl_break; ch2 = index_b64[(Uchar)*f++]; } while (ch2 == XX); *t++ = ((ch1 & 0x03) << 6) | ch2; } dbl_break: *t = '\0'; return t - save_t; } int b64_decode(ifp, state) FILE* ifp; int state; { static FILE* ofp = NULL; int c1, c2, c3, c4; if (state == DECODE_DONE) { all_done: if (ofp) fclose(ofp); ofp = NULL; return state; } if (state == DECODE_START) { char* filename = decode_fix_fname(mime_section->filename); ofp = fopen(filename, FOPEN_WB); if (!ofp) return DECODE_ERROR; printf("Decoding %s", filename); if (nowait_fork) fflush(stdout); else newline(); state = DECODE_ACTIVE; } while ((c1 = mime_getc(ifp)) != EOF) { if (c1 != '=' && index_b64[c1] == XX) continue; do { c2 = mime_getc(ifp); if (c2 == EOF) return state; } while (c2 != '=' && index_b64[c2] == XX); do { c3 = mime_getc(ifp); if (c3 == EOF) return state; } while (c3 != '=' && index_b64[c3] == XX); do { c4 = mime_getc(ifp); if (c4 == EOF) return state; } while (c4 != '=' && index_b64[c4] == XX); if (c1 == '=' || c2 == '=') { state = DECODE_DONE; break; } c1 = index_b64[c1]; c2 = index_b64[c2]; c1 = (c1 << 2) | (c2 >> 4); putc(c1, ofp); if (c3 == '=') { state = DECODE_DONE; break; } c3 = index_b64[c3]; c2 = ((c2 & 0x0f) << 4) | (c3 >> 2); putc(c2, ofp); if (c4 == '=') { state = DECODE_DONE; break; } c4 = index_b64[c4]; c3 = ((c3 & 0x03) << 6) | c4; putc(c3, ofp); } if (state == DECODE_DONE) goto all_done; return DECODE_MAYBEDONE; } #undef XX static int mime_getc(fp) FILE* fp; { if (fp) return fgetc(fp); if (!mime_getc_line || !*mime_getc_line) { mime_getc_line = readart(art_line,sizeof art_line); if (mime_EndOfSection(art_line)) return EOF; if (!mime_getc_line) return EOF; } return *mime_getc_line++; } int cat_decode(ifp, state) FILE* ifp; int state; { static FILE* ofp = NULL; if (state == DECODE_DONE) { if (ofp) fclose(ofp); ofp = NULL; return state; } if (state == DECODE_START) { char* filename = decode_fix_fname(mime_section->filename); ofp = fopen(filename, FOPEN_WB); if (!ofp) return DECODE_ERROR; printf("Decoding %s", filename); if (nowait_fork) fflush(stdout); else newline(); } if (ifp) { while (fgets(buf, sizeof buf, ifp)) fputs(buf, ofp); } else { while (readart(buf, sizeof buf)) { if (mime_EndOfSection(buf)) break; fputs(buf, ofp); } } return DECODE_MAYBEDONE; } static int word_wrap_in_pre, normal_word_wrap, word_wrap; int filter_html(t, f) char* t; char* f; { static char tagword[32]; static int tagword_len; char* bp; char* cp; if (word_wrap_offset < 0) { normal_word_wrap = tc_COLS - 8; word_wrap_in_pre = 0; } else word_wrap_in_pre = normal_word_wrap = tc_COLS - word_wrap_offset; if (normal_word_wrap <= 20) normal_word_wrap = 0; if (word_wrap_in_pre <= 20) word_wrap_in_pre = 0; word_wrap = (mime_section->html & HF_IN_PRE)? word_wrap_in_pre : normal_word_wrap; if (!mime_section->html_line_start) mime_section->html_line_start = t - artbuf; if (!mime_section->html_blks) { mime_section->html_blks = (HBLK*)safemalloc(HTML_MAX_BLOCKS * sizeof (HBLK)); } for (bp = t; *f; f++) { if (mime_section->html & HF_IN_DQUOTE) { if (*f == '"') mime_section->html &= ~HF_IN_DQUOTE; else if (tagword_len < (sizeof tagword) - 1) tagword[tagword_len++] = *f; } else if (mime_section->html & HF_IN_SQUOTE) { if (*f == '\'') mime_section->html &= ~HF_IN_SQUOTE; else if (tagword_len < (sizeof tagword) - 1) tagword[tagword_len++] = *f; } else if (mime_section->html & HF_IN_COMMENT) { if (*f == '-' && f[1] == '-') { f++; mime_section->html &= ~HF_IN_COMMENT; } } else if (mime_section->html & HF_IN_TAG) { if (*f == '>') { mime_section->html &= ~HF_IN_TAG; tagword[tagword_len] = '\0'; if (*tagword == '/') t = tag_action(t, tagword+1, CLOSING_TAG); else t = tag_action(t, tagword, OPENING_TAG); } else if (*f == '-' && f[1] == '-') { f++; mime_section->html |= HF_IN_COMMENT; } else if (*f == '"') mime_section->html |= HF_IN_DQUOTE; else if (*f == '\'') mime_section->html |= HF_IN_SQUOTE; else if (tagword_len < (sizeof tagword) - 1) { tagword[tagword_len++] = AT_GREY_SPACE(f)? ' ' : *f; } } else if (*f == '<') { tagword_len = 0; mime_section->html |= HF_IN_TAG; } else if (mime_section->html & HF_IN_HIDING) ; else if (*f == '&') { t = output_prep(t); if (strncaseEQ(f+1,"lt;",3)) { *t++ = '<'; f += 3; } else if (strncaseEQ(f+1,"gt;",3)) { *t++ = '>'; f += 3; } else if (strncaseEQ(f+1,"amp;",4)) { *t++ = '&'; f += 4; } else if (strncaseEQ(f+1,"nbsp;",5)) { *t++ = ' '; f += 5; } else if (strncaseEQ(f+1,"quot;",5)) { *t++ = '"'; f += 5; } else *t++ = *f; mime_section->html |= HF_NL_OK|HF_P_OK|HF_SPACE_OK; } else if (AT_GREY_SPACE(f) && !(mime_section->html & HF_IN_PRE)) { /* We don't want to call output_prep() here. */ if (mime_section->html & HF_SPACE_OK) { mime_section->html &= ~HF_SPACE_OK; *t++ = ' '; } } else if (*f == '\n') { /* Handle the HF_IN_PRE case */ t = output_prep(t); mime_section->html |= HF_NL_OK; t = do_newline(t, HF_NL_OK); } else { t = output_prep(t); *t++ = *f; mime_section->html |= HF_NL_OK|HF_P_OK|HF_SPACE_OK; } if (word_wrap && t - artbuf - mime_section->html_line_start > tc_COLS) { char* line_start = mime_section->html_line_start + artbuf; for (cp = line_start + word_wrap; cp > line_start && *cp != ' ' && *cp != '\t'; cp--) ; if (cp == line_start) { for (cp = line_start + word_wrap; cp - line_start <= tc_COLS && *cp != ' ' && *cp != '\t'; cp++) ; if (cp - line_start > tc_COLS) { mime_section->html_line_start += tc_COLS; cp = NULL; } } if (cp) { int flag_save = mime_section->html; int fudge; char* s; mime_section->html |= HF_NL_OK; cp = line_start = do_newline(cp, HF_NL_OK); fudge = do_indent((char*)NULL); while (*cp == ' ' || *cp == '\t') cp++; if ((fudge -= cp - line_start) != 0) { if (fudge < 0) bcopy(cp, cp + fudge, t - cp); else for (s = t; s-- != cp; ) s[fudge] = *s; (void) do_indent(line_start); t += fudge; } mime_section->html = flag_save; } } } *t = '\0'; return t - bp; } static char bullets[3] = {'*', 'o', '+'}; static char letters[2] = {'a', 'A'}; static char roman_letters[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I'}; static int roman_values[] = {1000, 500, 100, 50, 10, 5, 1 }; static char* tag_action(t, word, opening_tag) char* t; char* word; bool_int opening_tag; { char* cp; int i, j, tnum, len, itype, ch, cnt, num; bool match = 0; HBLK* blks = mime_section->html_blks; for (cp = word; *cp && *cp != ' '; cp++) ; len = cp - word; if (!isalpha(*word)) return t; ch = isupper(*word)? tolower(*word) : *word; for (tnum = 0; tnum < LAST_TAG && *tagattr[tnum].name != ch; tnum++) ; for ( ; tnum < LAST_TAG && *tagattr[tnum].name == ch; tnum++) { if (len == tagattr[tnum].length && strncaseEQ(word, tagattr[tnum].name, len)) { match = 1; break; } } if (!match) return t; if (!opening_tag && !(tagattr[tnum].flags & (TF_BLOCK|TF_HAS_CLOSE))) return t; if ((mime_section->html & HF_IN_HIDING) && (opening_tag || tnum != blks[mime_section->html_blkcnt-1].tnum)) return t; if (tagattr[tnum].flags & TF_BR) mime_section->html |= HF_NL_OK; if (opening_tag) { if (tagattr[tnum].flags & TF_NL) { t = output_prep(t); t = do_newline(t, HF_NL_OK); } if ((num = tagattr[tnum].flags & (TF_P|TF_LIST)) == TF_P || (num == (TF_P|TF_LIST) && !(mime_section->html & HF_COMPACT))) { t = output_prep(t); t = do_newline(t, HF_P_OK); } if (tagattr[tnum].flags & TF_SPACE) { if (mime_section->html & HF_SPACE_OK) { mime_section->html &= ~HF_SPACE_OK; *t++ = ' '; } } if (tagattr[tnum].flags & TF_TAB) { if (mime_section->html & HF_NL_OK) { mime_section->html &= ~HF_SPACE_OK; *t++ = '\t'; } } if ((tagattr[tnum].flags & TF_BLOCK) && mime_section->html_blkcnt < HTML_MAX_BLOCKS) { j = mime_section->html_blkcnt++; blks[j].tnum = tnum; blks[j].indent = 0; blks[j].cnt = 0; if (tagattr[tnum].flags & TF_LIST) mime_section->html |= HF_COMPACT; else mime_section->html &= ~HF_COMPACT; } else j = mime_section->html_blkcnt - 1; if ((tagattr[tnum].flags & (TF_BLOCK|TF_HIDE)) == (TF_BLOCK|TF_HIDE)) mime_section->html |= HF_IN_HIDING; switch (tnum) { case TAG_BLOCKQUOTE: if (((cp = find_attr(word, "type")) != NULL && strncaseEQ(cp, "cite", 4)) || ((cp = find_attr(word, "style")) != NULL && strncaseEQ(cp, "border-left:", 12))) blks[j].indent = '>'; else blks[j].indent = ' '; break; case TAG_HR: t = output_prep(t); *t++ = '-'; *t++ = '-'; mime_section->html |= HF_NL_OK; t = do_newline(t, HF_NL_OK); break; case TAG_IMG: t = output_prep(t); if (mime_section->html & HF_SPACE_OK) *t++ = ' '; strcpy(t, "[Image] "); t += 8; mime_section->html &= ~HF_SPACE_OK; break; case TAG_OL: itype = 4; if ((cp = find_attr(word, "type")) != NULL) { switch (*cp) { case '1': itype = 4; break; case 'a': itype = 5; break; case 'A': itype = 6; break; case 'i': itype = 7; break; case 'I': itype = 8; break; } } blks[j].indent = itype; break; case TAG_UL: itype = 1; if ((cp = find_attr(word, "type")) != NULL) { switch (*cp) { case 'd': case 'D': itype = 1; break; case 'c': case 'C': itype = 2; break; case 's': case 'S': itype = 3; break; } } else { for (i = 0; i < mime_section->html_blkcnt; i++) { if (blks[i].indent && blks[i].indent < ' ') { if (++itype == 3) break; } } } blks[j].indent = itype; break; case TAG_LI: t = output_prep(t); ch = j < 0? ' ' : blks[j].indent; switch (ch) { case 1: case 2: case 3: t[-2] = bullets[ch-1]; break; case 4: sprintf(t-4, "%2d. ", ++blks[j].cnt); if (*t) t += strlen(t); break; case 5: case 6: cnt = blks[j].cnt++; if (cnt >= 26*26) cnt = blks[j].cnt = 0; if (cnt >= 26) t[-4] = letters[ch-5] + (cnt / 26) - 1; t[-3] = letters[ch-5] + (cnt % 26); t[-2] = '.'; break; case 7: for (i = 0; i < 7; i++) { if (isupper(roman_letters[i])) roman_letters[i] = tolower(roman_letters[i]); } goto roman_numerals; case 8: for (i = 0; i < 7; i++) { if (islower(roman_letters[i])) roman_letters[i] = toupper(roman_letters[i]); } roman_numerals: cp = t - 6; cnt = ++blks[j].cnt; for (i = 0; cnt && i < 7; i++) { num = roman_values[i]; while (cnt >= num) { *cp++ = roman_letters[i]; cnt -= num; } j = (i | 1) + 1; if (j < 7) { num -= roman_values[j]; if (cnt >= num) { *cp++ = roman_letters[j]; *cp++ = roman_letters[i]; cnt -= num; } } } if (cp < t - 2) { t -= 2; for (cnt = t - cp; cp-- != t - 4; ) cp[cnt] = *cp; while (cnt--) *++cp = ' '; } else t = cp; *t++ = '.'; *t++ = ' '; break; default: *t++ = '*'; *t++ = ' '; break; } mime_section->html |= HF_NL_OK|HF_P_OK; break; case TAG_PRE: mime_section->html |= HF_IN_PRE; word_wrap = word_wrap_in_pre; break; } } else { if ((tagattr[tnum].flags & TF_BLOCK)) { for (j = mime_section->html_blkcnt; j--; ) { if (blks[j].tnum == tnum) { for (i = mime_section->html_blkcnt; --i > j; ) { t = tag_action(t, tagattr[blks[i].tnum].name, CLOSING_TAG); } mime_section->html_blkcnt = j; break; } } mime_section->html &= ~HF_IN_HIDING; while (j-- > 0) { if (tagattr[blks[j].tnum].flags & TF_HIDE) { mime_section->html |= HF_IN_HIDING; break; } } } j = mime_section->html_blkcnt - 1; if (j >= 0 && (tagattr[blks[j].tnum].flags & TF_LIST)) mime_section->html |= HF_COMPACT; else mime_section->html &= ~HF_COMPACT; if ((tagattr[tnum].flags & TF_NL) && (mime_section->html & HF_NL_OK)) { mime_section->html |= HF_QUEUED_NL; mime_section->html &= ~HF_SPACE_OK; } if ((num = tagattr[tnum].flags & (TF_P|TF_LIST)) == TF_P || (num == (TF_P|TF_LIST) && !(mime_section->html & HF_COMPACT))) { if (mime_section->html & HF_P_OK) { mime_section->html |= HF_QUEUED_P; mime_section->html &= ~HF_SPACE_OK; } } switch (tnum) { case TAG_PRE: mime_section->html &= ~HF_IN_PRE; word_wrap = normal_word_wrap; break; } } return t; } static char* output_prep(t) char* t; { if (mime_section->html & HF_QUEUED_P) { mime_section->html &= ~HF_QUEUED_P; t = do_newline(t, HF_P_OK); } if (mime_section->html & HF_QUEUED_NL) { mime_section->html &= ~HF_QUEUED_NL; t = do_newline(t, HF_NL_OK); } return t + do_indent(t); } static char* do_newline(t, flag) char* t; int flag; { if (mime_section->html & flag) { mime_section->html &= ~(flag|HF_SPACE_OK); t += do_indent(t); *t++ = '\n'; mime_section->html_line_start = t - artbuf; mime_section->html |= HF_NEED_INDENT; } return t; } static int do_indent(t) char* t; { HBLK* blks; int j, ch, spaces, len = 0; if (!(mime_section->html & HF_NEED_INDENT)) return len; if (t) mime_section->html &= ~HF_NEED_INDENT; if ((blks = mime_section->html_blks) != NULL) { for (j = 0; j < mime_section->html_blkcnt; j++) { if ((ch = blks[j].indent) != 0) { switch (ch) { case '>': spaces = 1; break; case ' ': spaces = 3; break; case 7: case 8: ch = ' '; spaces = 5; break; default: ch = ' '; spaces = 3; break; } len += spaces + 1; if (len > 64) { len -= spaces + 1; break; } if (t) { *t++ = ch; while (spaces--) *t++ = ' '; } } } } return len; } static char* find_attr(str, attr) char* str; char* attr; { int len = strlen(attr); char* cp = str; char* s; while ((cp = index(cp+1, '=')) != NULL) { for (s = cp; s[-1] == ' '; s--) ; while (cp[1] == ' ') cp++; if (s - str > len && s[-len-1] == ' ' && strncaseEQ(s-len,attr,len)) return cp+1; } return NULL; } trn-4.0-test77/mime.h0000644000000000000000000001102307113133016013101 0ustar rootroot/* mime.h */ struct hblk { int tnum; short cnt; char indent; }; struct mime_sect { MIME_SECT* prev; char* filename; char* type_name; char* type_params; char* boundary; int html_line_start; HBLK* html_blks; short type; short encoding; short part; short total; short boundary_len; short flags; short html; short html_blkcnt; }; #define MSF_INLINE 0x0001 #define MSF_ALTERNATIVE 0x0002 #define MSF_ALTERNADONE 0x0004 /* Only used with HTMLTEXT_MIME */ #define HF_IN_TAG 0x0001 #define HF_IN_COMMENT 0x0002 #define HF_IN_HIDING 0x0004 #define HF_IN_PRE 0x0008 #define HF_IN_DQUOTE 0x0010 #define HF_IN_SQUOTE 0x0020 #define HF_QUEUED_P 0x0040 #define HF_P_OK 0x0080 #define HF_QUEUED_NL 0x0100 #define HF_NL_OK 0x0200 #define HF_NEED_INDENT 0x0400 #define HF_SPACE_OK 0x0800 #define HF_COMPACT 0x1000 #define HTML_MAX_BLOCKS 256 #define TF_BLOCK 0x0001 /* This implies TF_HAS_CLOSE */ #define TF_HAS_CLOSE 0x0002 #define TF_NL 0x0004 #define TF_P 0x0008 #define TF_BR 0x0010 #define TF_LIST 0x0020 #define TF_HIDE 0x0040 #define TF_SPACE 0x0080 #define TF_TAB 0x0100 #define TAG_BLOCKQUOTE 0 #define TAG_BR (TAG_BLOCKQUOTE+1) #define TAG_DIV (TAG_BR+1) #define TAG_HR (TAG_DIV+1) #define TAG_IMG (TAG_HR+1) #define TAG_LI (TAG_IMG+1) #define TAG_OL (TAG_LI+1) #define TAG_P (TAG_OL+1) #define TAG_PRE (TAG_P+1) #define TAG_SCRIPT (TAG_PRE+1) #define TAG_STYLE (TAG_SCRIPT+1) #define TAG_TD (TAG_STYLE+1) #define TAG_TH (TAG_TD+1) #define TAG_TR (TAG_TH+1) #define TAG_TITLE (TAG_TR+1) #define TAG_UL (TAG_TITLE+1) #define LAST_TAG (TAG_UL+1) #define CLOSING_TAG 0 #define OPENING_TAG 1 struct html_tags { char* name; char length; int flags; }; #ifndef DOINIT EXT HTML_TAGS tagattr[LAST_TAG]; #else HTML_TAGS tagattr[LAST_TAG] = { /* name length flags */ {"blockquote", 10, TF_BLOCK | TF_P | TF_NL }, {"br", 2, TF_NL | TF_BR }, {"div", 3, TF_BLOCK | TF_NL }, {"hr", 2, TF_NL }, {"img", 3, 0 }, {"li", 2, TF_NL }, {"ol", 2, TF_BLOCK | TF_P | TF_NL | TF_LIST }, {"p", 1, TF_HAS_CLOSE | TF_P | TF_NL }, {"pre", 3, TF_BLOCK | TF_P | TF_NL }, {"script", 6, TF_BLOCK | TF_HIDE }, {"style", 5, TF_BLOCK | TF_HIDE }, {"td", 2, TF_TAB }, {"th", 2, TF_TAB }, {"tr", 2, TF_NL }, {"title", 5, TF_BLOCK | TF_HIDE }, {"ul", 2, TF_BLOCK | TF_P | TF_NL | TF_LIST }, }; #endif EXT LIST* mimecap_list; #define mimecap_ptr(n) ((MIMECAP_ENTRY*)listnum2listitem(mimecap_list,(long)(n))) EXT MIME_SECT mime_article; EXT MIME_SECT* mime_section INIT(&mime_article); EXT short mime_state; EXT char* multipart_separator INIT("-=-=-=-=-=-"); #define NOT_MIME 0 #define TEXT_MIME 1 #define ISOTEXT_MIME 2 #define MESSAGE_MIME 3 #define MULTIPART_MIME 4 #define IMAGE_MIME 5 #define AUDIO_MIME 6 #define APP_MIME 7 #define UNHANDLED_MIME 8 #define SKIP_MIME 9 #define DECODE_MIME 10 #define BETWEEN_MIME 11 #define END_OF_MIME 12 #define HTMLTEXT_MIME 13 #define ALTERNATE_MIME 14 #define MENCODE_NONE 0 #define MENCODE_BASE64 1 #define MENCODE_QPRINT 2 #define MENCODE_UUE 3 #define MENCODE_UNHANDLED 4 struct mimecap_entry { char* contenttype; char* command; char* testcommand; char* label; int flags; }; #define MCF_NEEDSTERMINAL 0x0001 #define MCF_COPIOUSOUTPUT 0x0002 EXT bool auto_view_inline INIT(FALSE); EXT char* mime_getc_line INIT(NULL); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void mime_init _((void)); void mime_ReadMimecap _((char*)); MIMECAP_ENTRY* mime_FindMimecapEntry _((char*,int)); bool mime_TypesMatch _((char*,char*)); int mime_Exec _((char*)); void mime_InitSections _((void)); void mime_PushSection _((void)); bool mime_PopSection _((void)); void mime_ClearStruct _((MIME_SECT*)); void mime_SetArticle _((void)); void mime_ParseType _((MIME_SECT*,char*)); void mime_ParseDisposition _((MIME_SECT*,char*)); void mime_ParseEncoding _((MIME_SECT*,char*)); void mime_ParseSubheader _((FILE*,char*)); void mime_SetState _((char*)); int mime_EndOfSection _((char*)); char* mime_ParseParams _((char*)); char* mime_FindParam _((char*,char*)); char* mime_SkipWhitespace _((char*)); void mime_DecodeArticle _((bool_int)); void mime_Description _((MIME_SECT*,char*,int)); int qp_decodestring _((char*,char*,bool_int)); int qp_decode _((FILE*,int)); int b64_decodestring _((char*,char*)); int b64_decode _((FILE*,int)); int cat_decode _((FILE*,int)); int filter_html _((char*,char*)); trn-4.0-test77/mime.ih0000644000000000000000000000065407113133016013262 0ustar rootroot/* mime.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static char* mime_ParseEntryArg _((char**)); static int mime_getc _((FILE*)); static char* tag_action _((char*,char*,bool_int)); static char* output_prep _((char*)); static char* do_newline _((char*,int)); static int do_indent _((char*)); static char* find_attr _((char*,char*)); trn-4.0-test77/mkpro.SH0000644000000000000000000001321411437640112013375 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting mkpro (with variable substitutions)" $spitshell >mkpro <>mkpro <<'!NO!SUBS!' $dont_line="/* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */\n"; $changes = 0; # We're looking for the following limited format: # # void # function(arg1, arg2) # char* arg1; # int (*arg2) _((void)); # { # # Once we find the '{' on a line by itself, we scan backward, parsing # the arguments until we find the function line, at which point we snag # the return value and check for a preceding #if line. # # Public prototypes for foo.c are written to foo.h, while the private # (i.e. static) ones are written to foo.ih. When updating the files, # we generate foo.nh and foo.nih and discard any file that is not # different from the original. If a file is different, the original # is moved into the directory "sanity", and the new file renamed. foreach $file (<*.c>) { next if $file eq 'ndir.c' || $file eq 'parsedate.c'; next unless $file =~ s/\.c$//; open(IN_C, "$file.c") || die "Can't open $file.c for input: $!\n"; # Copy everything in foo.h up to the "DON'T EDIT" line to foo.nh if (!open(IN, "$file.h")) { #print STDERR "Can't open $file.h -- skipping $file.c.\n"; next; } open(OUT_H, ">$file.nh") || die "Can't open $file.nh for output: $!\n"; while () { last if /DON'T EDIT BELOW/; print OUT_H $_; } print OUT_H $dont_line . "\n"; close IN; # Copy everything in foo.ih up to the "DON'T EDIT" line to foo.nih open(OUT_I, ">$file.nih") || die "Can't open $file.nih for output: $!\n"; if (open(IN, "$file.ih")) { while () { last if /DON'T EDIT BELOW/; print OUT_I $_; } close IN; $need_I = 1; $new_I = ''; } else { # If foo.ih doesn't yet exist, create a foo.nih anyway print OUT_I "/* $file.ih\n */\n\n"; $need_I = 0; $new_I = ' **NEW**'; } print OUT_I $dont_line . "\n"; $cond_H = 'none'; $cond_I = 'none'; while () { # Limit the lines stack to 20 entries shift @lines if $#lines >= 20; push(@lines, $_); next unless /^{\s*$/; pop @lines; undef @args; undef $func; # The default is to use the .h file unless we see a "static" $out = OUT_H; $cond = $cond_H; while ($_ = pop @lines) { s|\s*/\*.*\*/||; # trim any comment if (/^(\S*).*;/) { # A line with ';' is an argument $arg = $1; # Discard "register" prefix if ($arg eq "register") { /^register\s*(\S*)\s/; $arg = $1; } # If the arg line has _((...)), remember that bit as well. if (/(_\(\(.*\)\))/) { $arg .= "(*) $1"; push(@args,$arg); } else { # Append an extra '*' for arg[] $arg .= '*' if /]/; push(@args,$arg); # We allow only one duplicate, e.g. int arg1, arg2 if (/,/) { push(@args,$arg); } } } elsif (/^(.*)\(/) { # A line with '(' but without ';' is the function name. $func = $1; } elsif (/,/) { # Any remaining line with a comma is a continuation-line # of the function's arg-list, e.g. " arg8,arg9,arg10)" } else { chop; $ret = $_; if (/^static\s/) { # A static function goes in .ih, not .h $out = OUT_I; $cond = $cond_I; $need_I = 1; } # Pop the previous line (and another if empty) $_ = pop @lines; $_ = pop @lines if /^$/; if (/^\#\s*if([^\/]*)/) { # Remember conditional preceding function $newcond = $1; $newcond =~ s/\s*$//; } elsif (/^\#\s*else/) { # Discard a function after an #else -- it's a duplicate last; } else { # No conditional -- turn it off $newcond = 'none'; } # Output conditional, but only if it's different. if ($cond ne $newcond) { # Out with the old... if ($cond ne 'none') { print $out "#endif\n"; } # In with the new... if ($newcond ne 'none') { print $out "#if$newcond\n"; } } # At last, output the actual prototype. print $out "$ret $func _(("; if ($_ = pop @args) { print $out $_; while ($_ = pop @args) { print $out ",$_"; } } else { print $out "void"; } # Some special processing for non-returning "finalize" if ($func eq "finalize") { print $out ")) #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR >= 5) __attribute__((noreturn)) #endif ;\n"; } else { print $out "));\n"; } # Remember the conditional state of the current .h|.ih file. if ($ret =~ /^static\s/) { $cond_I = $newcond; } else { $cond_H = $newcond; } last; } } } close IN_C; # Close any dangling conditionals if ($cond_H ne 'none') { print OUT_H "#endif\n"; } close OUT_H; if ($cond_I ne 'none') { print OUT_I "#endif\n"; } close OUT_I; # Check for any differences and update if changed. if (system "diff $file.h $file.nh >/dev/null") { print "Updating $file.h\n"; mkdir("sanity",0755) unless -d "sanity"; rename("$file.h","sanity/$file.h"); rename("$file.nh","$file.h"); $changes++; } else { unlink "$file.nh"; } # The .ih file is a little different, in that it might not be needed. if ($need_I) { if ($new_I ne '') { system "touch $file.ih"; } if (system "diff $file.ih $file.nih >/dev/null") { print "Updating $file.ih$new_I\n"; mkdir("sanity",0755) unless -d "sanity"; rename("$file.ih","sanity/$file.ih"); rename("$file.nih","$file.ih"); $changes++; } else { unlink "$file.nih"; } } else { unlink "$file.nih"; } } print "No changes to prototypes.\n" if !$changes; !NO!SUBS! $eunicefix mkpro chmod 755 mkpro trn-4.0-test77/mktd.SH0000644000000000000000000000324007254451006013206 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting mktd (with variable substitutions)" $spitshell >mktd <>mktd <<'!NO!SUBS!' # We scan the .h and .ih files for definitions of the form: # # struct foo { ... }; # and # union bar { ... }; # # and create typedefs of: # # typedef struct foo FOO; # and # typedef union bar BAR; # # in the file typedef.nh. If it differs from typedef.h, move the # old version to the directory "sanity" and rename the new one. open(IN, "typedef.nh") || die "Can't open typedef.nh for output\n"; # Copy everything up to the DON'T EDIT line. while () { print OUT $_; last if /DON'T EDIT BELOW/; } close IN; # Scan through all the .h and .ih files foreach $file (sort(<*.h>, <*.ih>)) { open(IN, $file) || die "Can't open $file for input\n"; # The first typedef from this file needs a preceding comment. $need_comment = 1; while () { next unless /^(struct|union)\s+([a-z][a-z0-9_]*)\s+{/; if ($need_comment) { print OUT "\n/* $file */\n\n"; $need_comment = 0; } $upper = $2; $upper =~ tr/a-z/A-Z/; print OUT "typedef $1 $2 $upper;\n"; } close IN; } close OUT; # Check for any differences and update if changed. if (system "diff -q typedef.h typedef.nh >/dev/null") { print "Updating typedef.h\n"; mkdir("sanity",0755) unless -d "sanity"; rename("typedef.h","sanity/typedef.h"); rename("typedef.nh","typedef.h"); } else { print "No change to typedef.h\n"; unlink "typedef.nh"; } !NO!SUBS! $eunicefix mktd chmod 755 mktd trn-4.0-test77/msdos.h0000644000000000000000000000140407113133016013301 0ustar rootroot/* msdos.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #include #include #include #include #include FILE* popen(char*,char*); int pclose(FILE*); #define FILE_REF(s) (*(s)=='/'?'/':(isalpha(*s)&&(s)[1]==':'?(s)[2]:0)) #define chdir ChDir #define getenv GetEnv #define FOPEN_RB "rb" #define FOPEN_WB "wb" #define B19200 19200 #define B9600 9600 #define B4800 4800 #define B2400 2400 #define B1800 1800 #define B1200 1200 #define B600 600 #define B300 300 #define B200 200 #define B150 150 #define B134 134 #define B110 110 #define B75 75 #define B50 50 #define LIMITED_FILENAMES #define RESTORE_ORIGDIR #define NO_FILELINKS #define WINSOCK #define LAX_INEWS #define mkdir(dir,mode) mkdir(dir) trn-4.0-test77/myinstall.SH0000644000000000000000000000325207141205160014257 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting myinstall (with variable substitutions)" $spitshell >myinstall <>myinstall <<'!NO!SUBS!' case "$1" in -*) args=$1 shift case "$args" in *f*) tofile=$1 ; shift ;; esac esac case "$#" in 0|1) echo "Usage: myinstall [-opts] dir file [file...]" exit 1 esac dir=$1 shift for file in $* ; do case "$args" in *f*) ;; *) tofile=`echo $file | sed -e 's!.*/\(.*\)!\1!' 2>/dev/null` esac if test -f $dir/$tofile ; then case "$args" in *d*) if cmp -s $file $dir/$tofile ; then continue fi ;; esac case "$args" in *o*) rm -f $dir/$tofile.old mv $dir/$tofile $dir/$tofile.old;; *n*) cat <dd_fd = fd; dirp->dd_loc = 0; return dirp; } /* * read an old style directory entry and present it as a new one */ #ifndef pyr #define ODIRSIZ 14 struct olddirect { ino_t od_ino; char od_name[ODIRSIZ]; }; #else /* an Pyramid in the ATT universe */ #define ODIRSIZ 248 struct olddirect { long od_ino; short od_fill1, od_fill2; char od_name[ODIRSIZ]; }; #endif /* * get next entry in a directory. */ Direntry_t* readdir(dirp) register DIR* dirp; { register struct olddirect* dp; static Direntry_t dir; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) return NULL; } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct olddirect*)(dirp->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->od_ino == 0) continue; dir.d_ino = dp->od_ino; strncpy(dir.d_name, dp->od_name, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } /* * close a directory. */ void closedir(dirp) register DIR* dirp; { close(dirp->dd_fd); dirp->dd_fd = -1; dirp->dd_loc = 0; free(dirp); } #endif /* EMULATE_NDIR */ trn-4.0-test77/ndir.h0000644000000000000000000000260007113133016013107 0ustar rootroot/* ndir.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #if defined(I_DIRENT) && !defined(EMULATE_NDIR) #include #else #ifdef I_NDIR #include #else #ifdef I_SYS_NDIR #include #else #ifdef I_SYS_DIR #include #else #ifndef DEV_BSIZE #define DEV_BSIZE 512 #endif #define DIRBLKSIZ DEV_BSIZE #define MAXNAMLEN 255 Direntry_t { long d_ino; /* inode number of entry */ short d_reclen; /* length of this record */ short d_namlen; /* length of string in d_name */ char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ }; /* * The DIRSIZ macro gives the minimum record length which will hold * the directory entry. This requires the amount of space in Direntry_t * without the d_name field, plus enough space for the name with a terminating * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. */ #undef DIRSIZ #define DIRSIZ(dp) ((sizeof(Direntry_t)-(MAXNAMLEN+1))+(((dp)->d_namlen+1+3)&~3)) /* * Definitions for library routines operating on directories. */ typedef struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; } DIR; #ifndef NULL #define NULL 0 #endif DIR* opendir _((char*)); Direntry_t* readdir _((DIR*)); long telldir _((DIR*)); void seekdir _((DIR*)); #define rewinddir(dirp) seekdir((dirp), (long)0) void closedir _((DIR*)); #endif #endif #endif #endif trn-4.0-test77/newsnews.SH0000644000000000000000000000116707141205736014130 0ustar rootroot: see end of file for revision information case $CONFIG in '') . ./config.sh ;; esac trnversion=`sed -e 's/.*" \(.*\)\"/\1/' $src/patchlevel.h 2>/dev/null` echo "Extracting newsnews (with version substitution)" cat >newsnews < !GROK!THIS! # # This software is copyrighted as detailed in the LICENSE file. trn-4.0-test77/ng.c0000644000000000000000000013363611437640112012574 0ustar rootroot/* ng.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "term.h" #include "final.h" #include "env.h" #include "util.h" #include "util2.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "artsrch.h" #include "help.h" #include "kfile.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "rcstuff.h" #include "head.h" #include "mime.h" #include "art.h" #include "artio.h" #include "addng.h" #include "ngstuff.h" #include "intrp.h" #include "respond.h" #include "backpage.h" #include "rcln.h" #include "sw.h" #include "last.h" #include "search.h" #include "rthread.h" #include "rt-select.h" #include "rt-wumpus.h" #include "rt-util.h" #include "decode.h" #include "charsubst.h" #ifdef SCAN #include "scan.h" #include "smisc.h" #include "scanart.h" #endif #ifdef SCORE #include "score.h" #endif #ifdef USE_TK #include "tkstuff.h" #endif #include "univ.h" #include "artstate.h" #include "color.h" #include "INTERN.h" #include "ng.h" #include "ng.ih" #ifdef USE_FILTER #include "filter.h" #endif /* art_switch() return values */ #define AS_NORM 0 #define AS_INP 1 #define AS_ASK 2 #define AS_CLEAN 3 #define AS_SA 4 #define AS_QUITNOW 5 #define AS_SV 6 int exit_code = NG_NORM; void ng_init() { setdfltcmd(); #ifdef KILLFILES open_kfile(KF_GLOBAL); #endif #ifdef CUSTOMLINES init_compex(&hide_compex); init_compex(&page_compex); #endif } /* do newsgroup pointed to by ngptr with name ngname * * The basic structure is: * for each desired article * for each desired page * for each line on page * if we need another line from file * get it * if it's a header line * do special things * for each column on page * put out a character * end loop * end loop * end loop * end loop * * (Actually, the pager is in another routine.) * * The chief problem is deciding what is meant by "desired". Most of * the messiness of this routine is due to the fact that people want * to do unstructured things all the time. I have used a few judicious * goto's where I thought it improved readability. The rest of the messiness * arises from trying to be both space and time efficient. Have fun. */ int do_newsgroup(start_command) char* start_command; /* command to fake up first */ { char mode_save = mode; char gmode_save = gmode; int ret; char* whatnext = "%s%sWhat next? [%s]"; bool ng_virtual = FALSE; set_datasrc(ngptr->rc->datasrc); if (chdir(datasrc->spool_dir)) { printf(nocd,datasrc->spool_dir) FLUSH; return -1; } exit_code = NG_NORM; kf_state &= ~(KFS_LOCAL_CHANGES | KFS_THREAD_CHANGES |KFS_NORMAL_LINES | KFS_THREAD_LINES); killfirst = 0; safefree0(extractdest); safefree0(extractprog); /* initialize the newsgroup data structures */ sel_rereading = 0; sel_mask = AF_SEL; ret = access_ng(); if (ret == -2) return NG_NOSERVER; if (ret <= 0) return NG_ERROR; #ifdef ARTSEARCH srchahead = (scanon && !ThreadedGroup /* did they say -S? */ && ((ART_NUM)ngptr->toread) >= scanon ? -1 : 0); #endif /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */ forcelast = TRUE; /* if 0 unread, do not bomb out */ recent_artp = curr_artp = NULL; recent_art = curr_art = lastart+1; prompt = whatnext; #ifdef CHARSUBST charsubst = charsets; #endif /* see if there are any special searches to do */ #ifdef KILLFILES open_kfile(KF_LOCAL); # ifdef VERBOSE IF(verbose) kill_unwanted(firstart,"Processing memorized commands...\n\n", TRUE); ELSE # endif # ifdef TERSE kill_unwanted(firstart,"Auto-processing...\n\n",TRUE); # endif #endif #ifdef USE_FILTER /* Tell the filter process what newsgroup we're in. We should do this before sc_init, or filter_nginit will be called twice. (Not destructive, but potentially wasteful.) */ filter_nginit(); #endif /* USE_FILTER */ #ifdef SCORE #ifdef SCAN_ART sc_init((sa_never_initialized || sa_mode_order == 2) && start_command && *start_command == ';'); #else sc_init(FALSE); #endif /* SCAN_ART */ #endif /* SCORE */ if (univ_ng_virtflag) { univ_ng_virtual(); goto cleanup; } if (!selected_count) selected_only = FALSE; top_article(); /* do they want a special top line? */ firstline = getval("FIRSTLINE",(char*)NULL); /* custom line suppression, custom page ending */ #ifdef CUSTOMLINES if ((hideline = getval("HIDELINE",(char*)NULL)) != NULL) compile(&hide_compex,hideline,TRUE,TRUE); if ((pagestop = getval("PAGESTOP",(char*)NULL)) != NULL) compile(&page_compex,pagestop,TRUE,TRUE); #endif /* now read each unread article */ /* CAA: if we are going directly to an article, set things up here */ if (ng_go_artnum) { ng_virtual = TRUE; if (ng_go_artnum >= absfirst) { art = ng_go_artnum; artp = article_ptr(art); } ng_go_artnum = 0; } else if (ng_go_msgid) { /* not implemented yet */ ng_virtual = TRUE; ng_go_msgid = 0; } doing_ng = TRUE; /* enter the twilight zone */ ngptr->rc->flags |= RF_RCCHANGED; if (!unsafe_rc_saves) checkcount = 0; /* do not checkpoint for a while */ do_fseek = FALSE; /* start 1st article at top */ for (; art <= lastart+1; ) { /* for each article */ set_mode('r','a'); /* do we need to "grow" the newsgroup? */ if ((art > lastart || forcegrow) && !keep_the_group_static) { ART_NUM oldlast = lastart; #ifdef SUPPORT_NNTP if (artsize < 0) nntp_finishbody(FB_SILENT); if (datasrc->flags & DF_REMOTE) { if (datasrc->act_sf.fp || getngsize(ngptr) > lastart) { if (nntp_group(ngname,ngptr) <= 0) { exit_code = NG_NOSERVER; goto cleanup; } grow_ng(ngptr->ngmax); } } else #endif grow_ng(getngsize(ngptr)); if (forcelast && art > oldlast) art = lastart+1; } if (art != 0 || (artp && !(artp->flags & AF_TMPMEM))) artp = article_find(art); if (start_command) { /* do we have an initial command? */ if (start_command == nullstr) { if (UseNewsSelector >= 0 && !ng_virtual && ngptr->toread >= (ART_UNREAD)UseNewsSelector) pushchar('+'); } else { hide_pending(); pushstring(start_command, 0); free(start_command); } start_command = NULL; if (input_pending()) { art = curr_art = lastart+1; artp = curr_artp = NULL; goto reinp_article; } } if (art > lastart) { /* are we off the end still? */ art = lastart + 1; /* keep pointer references sane */ if (!forcelast && ngptr->toread && selected_only && !selected_count) { art = curr_art; artp = curr_artp; strcpy(buf, "+"); goto article_level; } count_subjects(CS_RETAIN); article_walk(count_unCACHED_article, 0); ngptr->toread = (ART_UNREAD)obj_count; if (artp != curr_artp) { recent_art = curr_art; /* remember last article # (for '-') */ curr_art = art; /* set current article # */ recent_artp = curr_artp; curr_artp = artp; #ifdef CHARSUBST charsubst = charsets; #endif first_view = 0; } #ifdef SCAN_ART if (sa_in) { sa_go = TRUE; goto article_level; } #endif if (erase_screen) clear(); /* clear the screen */ else { fputs("\n\n",stdout) FLUSH; termdown(2); } #ifdef VERBOSE IF(verbose) printf("End of newsgroup %s.",ngname); /* print pseudo-article */ ELSE #endif #ifdef TERSE printf("End of %s",ngname); #endif if (obj_count) { if (selected_only) printf(" (%ld + %ld articles still unread)", (long)selected_count, (long)obj_count-selected_count); else printf(" (%ld article%s still unread)", (long)obj_count,PLURAL(obj_count)); } if (redirected) { if (redirected == nullstr) printf("\n\n** This group has been disabled by your news admin **"); else printf("\n\n** Please start using %s **", redirected); termdown(2); } else if (!obj_count && !forcelast) goto cleanup; /* actually exit newsgroup */ set_mode(gmode,'e'); prompt = whatnext; #ifdef ARTSEARCH srchahead = 0; /* no more subject search mode */ #endif fputs("\n\n",stdout) FLUSH; termdown(2); } else if (!reread && (was_read(art) || (selected_only && !(artp->flags & AF_SEL)))) { /* has this article been read? */ inc_art(selected_only,FALSE);/* then skip it */ continue; } else if (!reread && (!(artp->flags & AF_EXISTS) || !parseheader(art))) { oneless(artp); /* mark deleted as read */ ng_skip(); continue; } else { /* we have a real live article */ if (artp != curr_artp) { recent_art = curr_art; /* remember last article # (for '-') */ curr_art = art; /* set current article # */ recent_artp = curr_artp; curr_artp = artp; #ifdef CHARSUBST charsubst = charsets; #endif first_view = 0; do_hiding = TRUE; rotate = FALSE; } if (!do_fseek) { /* starting at top of article? */ artline = 0; /* start at the beginning */ topline = -1; /* and remember top line of screen */ /* (line # within article file) */ } clear(); /* clear screen */ #ifdef SUPPORT_NNTP if (art == 0 && artp && artp->msgid && (datasrc->flags&DF_REMOTE) && !(artp->flags & AF_CACHED)) { art = nntp_stat_id(artp->msgid); if (art < 0) { exit_code = NG_NOSERVER; goto cleanup; } if (art) artp = article_find(art); } #endif /* make sure article is found & open */ if (!artopen(art,(ART_POS)0)) { char tmpbuf[256]; ART_LINE linenum; /* see if we have tree data for this article anyway */ init_tree(); sprintf(tmpbuf,"%s: article is not available.",ngname); if (artp && !(artp->flags & AF_CACHED)) { if (absfirst < first_cached || last_cached < lastart || !cached_all_in_range) sprintf(tmpbuf,"%s: article may show up in a moment.", ngname); } linenum = tree_puts(tmpbuf,0,0); vwtary(artline,(ART_POS)0); finish_tree(linenum); prompt = whatnext; #ifdef ARTSEARCH srchahead = 0; #endif } else { /* found it, so print it */ switch (do_article()) { case DA_CLEAN: /* quit newsgroup */ goto cleanup; case DA_TOEND: /* do not mark as read */ goto reask_article; case DA_RAISE: /* reparse command at end of art */ goto article_level; case DA_NORM: /* normal end of article */ break; } } if (art >= absfirst) /* don't mark non-existant articles */ mark_as_read(artp); /* mark current article as read */ } /* if these gotos bother you, think of this as a little state machine */ reask_article: #ifdef MAILCALL setmail(FALSE); #endif setdfltcmd(); if (erase_screen && erase_each_line) erase_line(1); if (term_line >= tc_LINES) { term_scrolled += term_line - tc_LINES + 1; term_line = tc_LINES-1; } unflush_output(); /* disable any ^O in effect */ /* print prompt, whatever it is */ interp(cmd_buf, sizeof cmd_buf, mailcall); sprintf(buf,prompt,cmd_buf, #ifdef CHARSUBST current_charsubst(), #else nullstr, #endif dfltcmd); draw_mousebar(tc_COLS - (term_line == tc_LINES-1? strlen(buf)+5 : 0), 1); color_string(COLOR_CMD,buf); putchar(' '); fflush(stdout); term_col = strlen(buf) + 1; reinp_article: reread = FALSE; forcelast = FALSE; eat_typeahead(); #ifdef PENDING look_ahead(); /* see what we can do in advance */ cache_until_key(); #endif art = curr_art; artp = curr_artp; getcmd(buf); if (errno || *buf == '\f') { if (tc_LINES < 100 && !int_count) *buf = '\f'; /* on CONT fake up refresh */ else { newline(); /* but only on a crt */ goto reask_article; } } article_level: output_chase_phrase = TRUE; /* Allow "Chasing Xrefs..." output */ if (mousebar_cnt) clear_rest(); #ifdef SCAN_ART if (sa_go) { switch (sa_main()) { case SA_NORM: continue; /* ...the article (for) loop */ case SA_NEXT: /* goto next newsgroup */ exit_code = NG_SELNEXT; goto cleanup; case SA_PRIOR: /* goto prior newsgroup */ exit_code = NG_SELPRIOR; goto cleanup; case SA_QUIT: case SA_ERR: goto cleanup; case SA_QUIT_SEL: exit_code = NG_ASK; goto cleanup; case SA_FAKE: lastchar = buf[0]; /* needed for fake to work */ break; /* fall through to art_switch */ } } #endif /* SCAN_ART */ /* parse and process article level command */ switch (art_switch()) { case AS_INP: /* multichar command rubbed out */ goto reinp_article; case AS_ASK: /* reprompt "End of article..." */ goto reask_article; case AS_CLEAN: /* exit newsgroup */ goto cleanup; case AS_QUITNOW: /* just leave, cleanup already done */ goto cleanup2; case AS_NORM: /* display article art */ break; #ifdef SCAN_ART case AS_SA: /* go to article scan mode */ sa_go = TRUE; goto article_level; #endif } } /* end of article selection loop */ /* shut down newsgroup */ cleanup: #ifdef KILLFILES kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE); /* do cleanup from KILL file, if any */ #endif #ifdef SCAN_ART if (sa_initialized) sa_cleanup(); #endif #ifdef SCORE if (sc_initialized) sc_cleanup(); #endif chase_xrefs(FALSE); if (!univ_ng_virtflag) { #ifdef USE_TK if (ttcl_running) ttcl_eval("endgroup"); #endif } in_ng = FALSE; /* leave newsgroup state */ artclose(); if (!univ_ng_virtflag) newline(); deselect_all(); yankback(); /* do a Y command */ bits_to_rc(); /* reconstitute .newsrc line */ cleanup2: /* go here if already cleaned up */ doing_ng = FALSE; /* tell sig_catcher to cool it */ /* XXX later, make an option for faster/less-safe virtual groups */ if (!univ_ng_virtflag && !(univ_read_virtflag && !(univ_follow || univ_follow_temp))) { if (!unsafe_rc_saves) { if (!write_newsrcs(multirc)) /* and update .newsrc */ get_anything(); #ifdef KILLFILES update_thread_kfile(); #endif } } #ifdef KILLFILES if (localkfp) { fclose(localkfp); localkfp = NULL; } #endif set_mode(gmode_save,mode_save); return exit_code; } /* Whew! */ /* decide what to do at the end of an article */ int art_switch() { setdef(buf,dfltcmd); #ifdef VERIFY printcmd(); #endif buf[2] = '\0'; switch (*buf) { case Ctl('v'): /* verify signature */ verify_sig(); return AS_ASK; #ifdef SCAN_ART case ';': /* enter ScanArticle mode */ sa_go_explicit = TRUE; return AS_SA; #endif #ifdef SCORE case '"': /* append to local SCORE file */ buf[0] = ':'; /* enter command on next line */ buf[1] = FINISHCMD; printf("\nEnter score append command or type RETURN for a menu\n"); termdown(2); fflush(stdout); if (finish_command(TRUE)) /* command entered successfully */ sc_append(buf+1); return AS_ASK; case '\'': /* execute scoring command */ buf[0] = ':'; buf[1] = FINISHCMD; printf("\nEnter scoring command or type RETURN for a menu\n"); termdown(2); fflush(stdout); if (finish_command(TRUE)) /* command entered successfully */ sc_score_cmd(buf+1); return AS_ASK; #endif case '<': /* goto previous subject/thread */ visit_prev_thread(); return AS_NORM; case '>': /* goto next subject/thread */ visit_next_thread(); return AS_NORM; case 'U': { /* unread some articles */ char* u_prompt; char* u_help_thread; if (!artp) { u_help_thread = nullstr; #ifdef VERBOSE IF(verbose) u_prompt = "\nUnkill: +select or all?"; ELSE #endif #ifdef TERSE u_prompt = "\nUnkill?"; #endif dfltcmd = "+anq"; } else { #ifdef VERBOSE IF(verbose) { u_prompt = "\n\ Unkill: +select, thread, subthread, or all?"; u_help_thread = "\ Type t or SP to mark this thread's articles as unread.\n\ Type s to mark the current article and its descendants as unread.\n"; } ELSE #endif #ifdef TERSE { u_prompt = "\nUnkill?"; u_help_thread = "\ t or SP to mark thread unread.\n\ s to mark subthread unread.\n"; } #endif dfltcmd = "+tsanq"; } reask_unread: in_char(u_prompt,'u',dfltcmd); #ifdef VERIFY printcmd(); #endif newline(); if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { fputs("\ Type + to enter select thread mode using all the already-read articles.\n\ (The selected threads will be marked as unread and displayed as usual.)\n\ ",stdout) FLUSH; fputs(u_help_thread,stdout); fputs("\ Type a to mark all articles in this group as unread.\n\ Type n or q to change nothing.\n\ ",stdout) FLUSH; termdown(6); } ELSE #endif #ifdef TERSE { fputs("\ + to select threads from the unread.\n\ ",stdout) FLUSH; fputs(u_help_thread,stdout); fputs("\ a to mark all articles unread.\n\ n or q to change nothing.\n\ ",stdout) FLUSH; termdown(5); } #endif goto reask_unread; } else if (*buf == 'n' || *buf == 'q') return AS_ASK; else if (*buf == 't' && u_help_thread != nullstr) { if (artp->subj->thread) unkill_thread(artp->subj->thread); else unkill_subject(artp->subj); if ((artp = first_art(artp->subj)) != NULL) art = article_num(artp); } else if (*buf == 's' && u_help_thread != nullstr) unkill_subthread(artp); else if (*buf == 'a') { check_first(absfirst); article_walk(mark_all_unREAD, 0); count_subjects(CS_NORM); ngptr->toread = (ART_UNREAD)obj_count; } else if (*buf == '+') { *buf = 'U'; goto run_the_selector; } else { fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); goto reask_unread; } return AS_NORM; } case '[': /* goto parent article */ case '{': /* goto thread's root article */ if (artp && ThreadedGroup) { if (!find_parent(*buf == '{')) { register char* cp = (*buf=='['?"parent":"root"); #ifdef VERBOSE IF(verbose) printf("\nThere is no %s article prior to this one.\n", cp) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo prior %s.\n",cp) FLUSH; #endif termdown(2); return AS_ASK; } reread = TRUE; #ifdef SCAN s_follow_temp = TRUE; #endif univ_follow_temp = TRUE; return AS_NORM; } not_threaded: if (!artp) { #ifdef VERBOSE IF(verbose) fputs("\nYou're at the end of the group.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nEnd of group.\n",stdout) FLUSH; #endif termdown(2); return AS_ASK; } #ifdef VERBOSE IF(verbose) fputs("\nThis group is not threaded.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nUnthreaded group.\n",stdout) FLUSH; #endif termdown(2); return AS_ASK; case ']': /* goto child article */ case '}': /* goto thread's leaf article */ if (artp && ThreadedGroup) { if (!find_leaf(*buf == '}')) { #ifdef VERBOSE IF(verbose) fputs("\n\ This is the last leaf in this tree.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nLast leaf.\n",stdout) FLUSH; #endif termdown(2); return AS_ASK; } reread = TRUE; #ifdef SCAN s_follow_temp = TRUE; #endif univ_follow_temp = TRUE; return AS_NORM; } goto not_threaded; case '(': /* goto previous sibling */ case ')': /* goto next sibling */ if (artp && ThreadedGroup) { if (!(*buf == '(' ? find_prev_sib() : find_next_sib())) { register char* cp = (*buf == '(' ? "previous" : "next"); #ifdef VERBOSE IF(verbose) printf("\nThis article has no %s sibling.\n",cp) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo %s sibling.\n",cp) FLUSH; #endif termdown(2); return AS_ASK; } reread = TRUE; #ifdef SCAN s_follow_temp = TRUE; #endif univ_follow_temp = TRUE; return AS_NORM; } goto not_threaded; case 'T': if (!ThreadedGroup) goto not_threaded; /* FALL THROUGH */ case 'A': if (!artp) goto not_threaded; switch (ask_memorize(*buf)) { case ',': case 'J': case 'K': case 'j': return AS_NORM; } return AS_ASK; case 'K': if (!artp) goto not_threaded; /* first, write kill-subject command */ (void)art_search(buf, (sizeof buf), TRUE); art = curr_art; artp = curr_artp; kill_subject(artp->subj,AFFECT_ALL);/* take care of any prior subjects */ #ifdef SCAN_ART if (sa_in && !(sa_follow || s_follow_temp)) return AS_SA; #endif return AS_NORM; case ',': /* kill this node and all descendants */ if (!artp) goto not_threaded; if (ThreadedGroup) kill_subthread(artp,AFFECT_ALL); else if (art >= absfirst && art <= lastart) mark_as_read(artp); #ifdef SCAN_ART if (sa_in && !(sa_follow || s_follow_temp)) return AS_SA; #endif return AS_NORM; case 'J': /* Junk all nodes in this thread */ if (!artp) goto not_threaded; if (ThreadedGroup) { kill_thread(artp->subj->thread,AFFECT_ALL); #ifdef SCAN_ART if (sa_in) return AS_SA; #endif return AS_NORM; } /* FALL THROUGH */ case 'k': /* kill current subject */ if (!artp) goto not_threaded; kill_subject(artp->subj,AFFECT_ALL); if (!ThreadedGroup || last_cached < lastart) { *buf = 'k'; goto normal_search; } #ifdef SCAN_ART if (sa_in && !(sa_follow || s_follow_temp)) return AS_SA; #endif return AS_NORM; case 't': erase_line(erase_screen && erase_each_line); page_line = 1; entire_tree(curr_artp); return AS_ASK; case ':': /* execute command on selected articles */ page_line = 1; if (!thread_perform()) return AS_INP; carriage_return(); perform_status_end(ngptr->toread, "article"); fputs(msg, stdout); newline(); art = curr_art; artp = curr_artp; return AS_ASK; case 'p': /* find previous unread article */ #ifdef SCAN s_follow_temp = TRUE; /* keep going until change req. */ #endif univ_follow_temp = TRUE; do { dec_art(selected_only,FALSE); } while (art >= firstart && (was_read(art) || !parseheader(art))); #ifdef ARTSEARCH srchahead = 0; #endif if (art >= firstart) return AS_NORM; art = absfirst; /* FALL THROUGH */ case 'P': /* goto previous article */ #ifdef SCAN s_follow_temp = TRUE; /* keep going until change req. */ #endif univ_follow_temp = TRUE; dec_art(FALSE,TRUE); check_dec_art: if (art < absfirst) { #ifdef VERBOSE IF(verbose) printf("\nThere are no%s%s articles prior to this one.\n", *buf=='P'?nullstr:" unread", selected_only?" selected":nullstr) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo previous%s%s articles\n", *buf=='P'?nullstr:" unread", selected_only?" selected":nullstr) FLUSH; #endif termdown(2); art = curr_art; artp = curr_artp; return AS_ASK; } reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '-': case '\b': case '\177': if (recent_art >= 0) { art = recent_art; artp = recent_artp; reread = TRUE; forcelast = TRUE; #ifdef ARTSEARCH srchahead = -(srchahead != 0); #endif return AS_NORM; } exit_code = NG_MINUS; return AS_CLEAN; case 'n': /* find next unread article? */ #ifdef SCAN_ART if (sa_in && s_default_cmd && !(sa_follow || s_follow_temp)) return AS_SA; #endif if (univ_read_virtflag && univ_default_cmd && #ifdef SCAN_ART !(sa_in && (sa_follow || s_follow_temp)) && #endif !(univ_follow || univ_follow_temp)) { exit_code = NG_NEXT; return AS_CLEAN; } if (!univ_default_cmd) univ_follow_temp = TRUE; #ifdef SCAN if (!s_default_cmd) s_follow_temp = TRUE; /* keep going until change req. */ #endif if (art > lastart) { if (!ngptr->toread) return AS_CLEAN; top_article(); #ifdef SCAN_ART if (sa_in) return AS_SA; #endif } #ifdef ARTSEARCH else if (scanon && !ThreadedGroup && srchahead) { *buf = Ctl('n'); if (!next_art_with_subj()) goto normal_search; return AS_NORM; } #endif else { #ifdef SCAN_ART /* $$ will this work with 4.0? CAA */ if (sa_in && ThreadedGroup) { ARTICLE* old_artp = artp; inc_art(selected_only,FALSE); if (!artp || !old_artp) return AS_SA; switch (sel_mode) { case SM_ARTICLE: if (s_default_cmd) return AS_SA; break; case SM_SUBJECT: if (old_artp->subj != artp->subj) return AS_SA; break; case SM_THREAD: if (old_artp->subj->thread != artp->subj->thread) return AS_SA; break; default: /* HUH? Just hope for the best */ break; } } else #endif inc_art(selected_only,FALSE); if (art > lastart) top_article(); } #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case 'N': /* goto next article */ #ifdef SCAN_ART if (sa_in && s_default_cmd && !(sa_follow || s_follow_temp)) return AS_SA; #endif if (univ_read_virtflag && univ_default_cmd && #ifdef SCAN_ART !(sa_in && (sa_follow || s_follow_temp)) && #endif !(univ_follow || univ_follow_temp)) { exit_code = NG_NEXT; return AS_CLEAN; } if (!univ_default_cmd) univ_follow_temp = TRUE; #ifdef SCAN if (!s_default_cmd) s_follow_temp = TRUE; /* keep going until change req. */ #endif if (art > lastart) { if (!first_subject) { art = absfirst; artp = article_ptr(art); } else { artp = first_subject->articles; if (artp->flags & AF_EXISTS) art = article_num(artp); else inc_art(FALSE,TRUE); } } else inc_art(FALSE,TRUE); if (art <= lastart) reread = TRUE; else forcelast = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '$': art = lastart+1; artp = NULL; forcelast = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '0': case '1': case '2': case '3': /* goto specified article */ case '4': case '5': case '6': /* or do something with a range */ case '7': case '8': case '9': case '.': forcelast = TRUE; switch (numnum()) { case NN_INP: return AS_INP; case NN_ASK: return AS_ASK; case NN_REREAD: reread = TRUE; #ifdef ARTSEARCH if (srchahead) srchahead = -1; #endif break; case NN_NORM: if (use_threads) { erase_line(0); perform_status_end(ngptr->toread, "article"); fputs(msg, stdout) FLUSH; } newline(); return AS_ASK; } return AS_NORM; case Ctl('k'): edit_kfile(); return AS_ASK; case Ctl('n'): /* search for next article with same subject */ case Ctl('p'): /* search for previous article with same subject */ #ifdef SCAN_ART if (sa_in && s_default_cmd && *buf == Ctl('n') && !(sa_follow || s_follow_temp)) return AS_SA; #endif if (univ_read_virtflag && univ_default_cmd && (*buf == Ctl('n')) && #ifdef SCAN_ART !(sa_in && (sa_follow || s_follow_temp)) && #endif !(univ_follow || univ_follow_temp)) { exit_code = NG_NEXT; return AS_CLEAN; } if (!univ_default_cmd) univ_follow_temp = TRUE; #ifdef SCAN if (!s_default_cmd) s_follow_temp = TRUE; /* keep going until change req. */ #endif if (*buf == Ctl('n')? next_art_with_subj() : prev_art_with_subj()) return AS_NORM; case '/': case '?': normal_search: #ifdef ARTSEARCH { /* search for article by pattern */ char cmd = *buf; reread = TRUE; /* assume this */ page_line = 1; switch (art_search(buf, (sizeof buf), TRUE)) { case SRCH_ERROR: art = curr_art; return AS_ASK; case SRCH_ABORT: art = curr_art; return AS_INP; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif termdown(2); art = curr_art; /* restore to current article */ return AS_ASK; case SRCH_DONE: if (use_threads) { erase_line(0); perform_status_end(ngptr->toread, "article"); printf("%s\n",msg) FLUSH; } else fputs("done\n",stdout) FLUSH; termdown(1); pad(just_a_sec/3); /* 1/3 second */ if (!srchahead) { art = curr_art; return AS_ASK; } top_article(); reread = FALSE; return AS_NORM; case SRCH_SUBJDONE: #ifdef SCAN_ART if (sa_in) return AS_SA; #endif top_article(); reread = FALSE; return AS_NORM; case SRCH_NOTFOUND: fputs("\n\n\n\nNot found.\n",stdout) FLUSH; termdown(5); art = curr_art; /* restore to current article */ #ifdef SCAN_ART if (sa_in) return AS_SA; #endif return AS_ASK; case SRCH_FOUND: if (cmd == Ctl('n') || cmd == Ctl('p')) { oldsubject = TRUE; reread = FALSE; } break; } return AS_NORM; } #else /* !ARTSEARCH */ buf[1] = '\0'; notincl(buf); return AS_ASK; #endif case 'u': /* unsubscribe from this newsgroup? */ newline(); printf(unsubto,ngname) FLUSH; termdown(1); ngptr->subscribechar = NEGCHAR; ngptr->rc->flags |= RF_RCCHANGED; newsgroup_toread--; return AS_CLEAN; case 'M': if (art <= lastart) { delay_unmark(artp); oneless(artp); printf("\nArticle %ld will return.\n",(long)art) FLUSH; termdown(2); } return AS_ASK; case 'm': if (art >= absfirst && art <= lastart) { unmark_as_read(artp); printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH; termdown(2); } return AS_ASK; case 'c': /* catch up */ switch (ask_catchup()) { case 'n': return AS_ASK; case 'u': return AS_CLEAN; } art = lastart+1; artp = NULL; forcelast = FALSE; return AS_NORM; case 'Q': case '`': exit_code = NG_ASK; return AS_CLEAN; case 'q': /* go back up to newsgroup level? */ exit_code = NG_NEXT; return AS_CLEAN; case 'i': if ((auto_view_inline = !auto_view_inline) != 0) first_view = 0; printf("\nAuto-View inlined mime is %s\n", auto_view_inline? "on" : "off"); termdown(2); break; case 'j': newline(); if (art >= absfirst && art <= lastart) mark_as_read(artp); return AS_ASK; case 'h': univ_help(UHELP_ART); return AS_ASK; case 'H': /* help? */ help_art(); return AS_ASK; case '&': if (switcheroo()) /* get rest of command */ return AS_INP; /* if rubbed out, try something else */ return AS_ASK; case '#': #ifdef VERBOSE IF(verbose) printf("\nThe last article is %ld.\n",(long)lastart) FLUSH; ELSE #endif #ifdef TERSE printf("\n%ld\n",(long)lastart) FLUSH; #endif termdown(2); return AS_ASK; case '+': /* enter selection mode */ run_the_selector: if (art_sel_ilock) { printf("\nAlready inside article selector!\n") FLUSH; termdown(2); return AS_ASK; } #ifdef SCAN_ART /* modes do not mix very well, so turn off the SA mode */ sa_in = FALSE; #endif #ifdef SCAN /* turn on temporary follow */ s_follow_temp = TRUE; #endif univ_follow_temp = TRUE; art_sel_ilock = TRUE; *buf = article_selector(*buf); art_sel_ilock = FALSE; switch (*buf) { case '+': newline(); term_scrolled = tc_LINES; term_line = tc_LINES-1; return AS_ASK; case 'Q': exit_code = NG_ASK; break; case 'q': exit_code = NG_NEXT; break; case 'N': exit_code = NG_SELNEXT; break; case 'P': exit_code = NG_SELPRIOR; break; #ifdef SCAN_ART case ';': sa_do_selthreads = TRUE; sa_go_explicit = TRUE; return AS_SA; #endif default: if (ngptr->toread) return AS_NORM; break; } return AS_CLEAN; case '=': { /* list subjects */ ART_NUM oldart = art; page_start(); article_walk(output_subject, AF_UNREAD); int_count = 0; subjline = NULL; art = oldart; return AS_ASK; } case '^': top_article(); #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; #ifdef DEBUG case 'D': printf("\nFirst article: %ld\n",(long)firstart) FLUSH; termdown(2); article_walk(debug_article_output, 0); int_count = 0; return AS_ASK; #endif case 'v': if (art <= lastart) { reread = TRUE; do_hiding = FALSE; } return AS_NORM; case Ctl('r'): do_hiding = TRUE; rotate = FALSE; if (art <= lastart) reread = TRUE; else forcelast = TRUE; return AS_NORM; case 'x': case Ctl('x'): /* In the future the behavior of 'x' may change back to a * filter-select mechanism. * Currently, both keys do ROT-13 translation. */ rotate = TRUE; if (art <= lastart) reread = TRUE; else forcelast = TRUE; return AS_NORM; case 'X': rotate = !rotate; /* FALL THROUGH */ case 'l': case Ctl('l'): /* refresh screen */ refresh_screen: if (art <= lastart) { reread = TRUE; clear(); do_fseek = TRUE; artline = topline; if (artline < 0) artline = 0; } return AS_NORM; case Ctl('^'): erase_line(0); /* erase the prompt */ #ifdef MAILCALL setmail(TRUE); /* force a mail check */ #endif return AS_ASK; #ifdef INNERSEARCH case Ctl('e'): if (art <= lastart) { #ifdef SUPPORT_NNTP if (artsize < 0) { nntp_finishbody(FB_OUTPUT); raw_artsize = nntp_artsize(); artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos; } #endif if (do_hiding) { seekartbuf(artsize); seekartbuf(artpos); } reread = TRUE; do_fseek = TRUE; topline = artline; innerlight = artline - 1; innersearch = artsize; gline = 0; hide_everything = 'b'; } return AS_NORM; #endif case 'B': /* back up one line */ case 'b': case Ctl('b'): /* back up a page */ if (art <= lastart) { ART_LINE target; reread = TRUE; clear(); do_fseek = TRUE; if (*buf == 'B') target = topline - 1; else { target = topline - (tc_LINES - 2); if (marking && (marking_areas & BACKPAGE_MARKING)) { highlight = topline; } } artline = topline; if (artline >= 0) do { artline--; } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0); topline = artline; if (artline < 0) artline = 0; } return AS_NORM; case '!': /* shell escape */ if (escapade()) return AS_INP; return AS_ASK; case 'C': cancel_article(); return AS_ASK; case 'Z': case 'z': supersede_article(); /* supersedes */ return AS_ASK; case 'R': case 'r': { /* reply? */ reply(); return AS_ASK; } case 'F': case 'f': { /* followup command */ followup(); forcegrow = TRUE; /* recalculate lastart */ return AS_ASK; } case Ctl('f'): { /* forward? */ forward(); return AS_ASK; } case '|': case 'w': case 'W': case 's': case 'S': /* save command */ case 'e': /* extract command */ if (save_article() == SAVE_ABORT) return AS_INP; int_count = 0; return AS_ASK; #if 0 case 'E': if (decode_fp) decode_end(); else newline(); return AS_ASK; #endif case 'a': /* attachment-view command */ newline(); if (view_article() == SAVE_ABORT) return AS_INP; int_count = 0; return AS_ASK; case 'Y': /* yank back M articles */ yankback(); top_article(); /* from the beginning */ return AS_NORM; /* pretend nothing happened */ #ifdef STRICTCR case '\n': case '\r': fputs(badcr,stdout) FLUSH; return AS_ASK; #endif case '_': if (!finish_dblchar()) return AS_INP; switch (buf[1] & 0177) { case 'P': art--; goto check_dec_art; case 'N': if (art > lastart) art = absfirst; else art++; if (art <= lastart) reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '+': if (!artp) goto not_threaded; if (ThreadedGroup) { select_arts_thread(artp, 0); printf("\nSelected all articles in this thread.\n"); } else { select_arts_subject(artp, 0); printf("\nSelected all articles in this subject.\n"); } termdown(2); if ((artp = first_art(artp->subj)) != NULL) { if (art == article_num(artp)) return AS_ASK; art = article_num(artp); } return AS_NORM; case '-': if (!artp) goto not_threaded; if (sel_mode == SM_THREAD) { deselect_arts_thread(artp); printf("\nDeselected all articles in this thread.\n"); } else { deselect_arts_subject(artp); printf("\nDeselected all articles in this subject.\n"); } termdown(2); return AS_ASK; #ifdef CHARSUBST case 'C': if (!*(++charsubst)) charsubst = charsets; goto refresh_screen; #endif case 'a': case 's': case 't': case 'T': *buf = buf[1]; goto run_the_selector; case 'm': if (!artp) goto not_threaded; kill_subthread(artp, SET_TORETURN | AFFECT_ALL); return AS_NORM; case 'M': if (!artp) goto not_threaded; kill_arts_thread(artp, SET_TORETURN | AFFECT_ALL); return AS_NORM; } /* FALL THROUGH */ default: printf("\n%s",hforhelp) FLUSH; termdown(2); settle_down(); break; } return AS_ASK; } /* see if there is any mail */ #ifdef MAILCALL void setmail(force) bool_int force; { if (force) mailcount = 0; if (!(mailcount++)) { char* mailfile = filexp(getval("MAILFILE",MAILFILE)); if (stat(mailfile,&filestat) < 0 || !filestat.st_size || filestat.st_atime > filestat.st_mtime) mailcall = nullstr; else mailcall = getval("MAILCALL","(Mail) "); } mailcount %= 5; /* check every 5 articles */ } #endif void setdfltcmd() { if (!ngptr || !ngptr->toread) dfltcmd = "npq"; else { #if 0 if (multimedia_mime == TRUE) { multimedia_mime++; dfltcmd = "anpq"; } else #endif #ifdef ARTSEARCH if (srchahead) dfltcmd = "^Nnpq"; else #endif dfltcmd = "npq"; } } /* Ask the user about catching-up the current group. Returns 'y' if yes, ** 'n' or 'N' if no ('N' means we used one line when in the selector), ** or 'u' for yes with unsubscribe. Actually performs the catchup and ** unsubscription as needed. */ char ask_catchup() { char ch; bool use_one_line = (gmode == 's'); int leave_unread = 0; if (!use_one_line) newline(); reask_catchup: #ifdef VERBOSE IF(verbose) sprintf(buf,"Mark everything in %s as read?",ngname); ELSE #endif #ifdef TERSE sprintf(buf,"Catchup %s?",ngname); #endif in_char(buf,'C',"yn#h"); #ifdef VERIFY printcmd(); #endif if ((ch = *buf) == 'h' || ch == 'H') { use_one_line = FALSE; #ifdef VERBOSE IF(verbose) fputs("\n\ Type y or SP to mark all articles as read.\n\ Type n to leave articles marked as they are.\n\ The # means enter a number to mark all but the last # articles as read.\n\ Type u to mark everything read and unsubscribe.\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ y or SP to mark all read.\n\ n to forget it.\n\ # means enter a number to leave unread.\n\ u to mark all and unsubscribe.\n\n\ ",stdout) FLUSH; #endif termdown(6); goto reask_catchup; } if (ch == 'n' || ch == 'q') { if (use_one_line) return 'N'; newline(); return 'n'; } if (ch == '#') { use_one_line = FALSE; in_char("\nEnter approx. number of articles to leave unread: ", 'C', "0"); if ((ch = *buf) == '0') ch = 'y'; } if (isdigit(ch)) { buf[1] = FINISHCMD; if (!finish_command(FALSE)) { use_one_line = FALSE; newline(); goto reask_catchup; } else { leave_unread = atoi(buf); ch = 'y'; } } if (ch != 'y' && ch != 'u') { use_one_line = FALSE; printf("\n%s\n", hforhelp) FLUSH; termdown(3); settle_down(); goto reask_catchup; } if (in_ng) { article_walk(mark_all_READ, leave_unread); if (leave_unread) { count_subjects(CS_NORM); ngptr->toread = (ART_UNREAD)obj_count; } else { selected_count = selected_subj_cnt = selected_only = 0; ngptr->toread = 0; if (dmcount) yankback(); } newline(); } else { newline(); catch_up(ngptr, leave_unread, 1); } if (ch == 'u') { ngptr->subscribechar = NEGCHAR; ngptr->rc->flags |= RF_RCCHANGED; newsgroup_toread--; newline(); printf(unsubto,ngname); printf("(If you meant to hit 'y' instead of 'u', press '-'.)\n") FLUSH; termdown(2); } return ch; } static bool count_unCACHED_article(ptr, arg) char* ptr; int arg; { register ARTICLE* ap = (ARTICLE*)ptr; if ((ap->flags & (AF_UNREAD|AF_CACHED)) == AF_UNREAD) obj_count++; return 0; } static bool mark_all_READ(ptr, leave_unread) char* ptr; int leave_unread; { register ARTICLE* ap = (ARTICLE*)ptr; if (article_num(ap) > lastart - leave_unread) return 1; ap->flags &= ~(sel_mask|AF_UNREAD); return 0; } static bool mark_all_unREAD(ptr, arg) char* ptr; int arg; { register ARTICLE* ap = (ARTICLE*)ptr; if ((ap->flags & (AF_UNREAD|AF_EXISTS)) == AF_EXISTS) { ap->flags |= AF_UNREAD; /* mark as unread */ obj_count++; } return 0; } bool output_subject(ptr, flag) char* ptr; int flag; { register ARTICLE* ap; register ART_NUM i; char tmpbuf[256]; int len; char* s; if (int_count) return 1; if (!subjline) { subjline = getval("SUBJLINE",(char*)NULL); if (!subjline) subjline = nullstr; } ap = (ARTICLE*)ptr; if (flag && !(ap->flags & flag)) return 0; i = article_num(ap); if ((s = fetchsubj(i,FALSE)) != NULL) { sprintf(tmpbuf,"%-5ld ", i); len = strlen(tmpbuf); if (subjline != nullstr) { art = i; interp(tmpbuf + len, sizeof tmpbuf - len, subjline); } else safecpy(tmpbuf + len, s, sizeof tmpbuf - len); if (mode == 'k') page_line = 1; if (print_lines(tmpbuf,NOMARKING) != 0) return 1; } return 0; } #ifdef DEBUG static bool debug_article_output(ptr, arg) char* ptr; int arg; { register ARTICLE* ap = (ARTICLE*)ptr; if (int_count) return 1; if (article_num(ap) >= firstart && ap->subj) { printf("%5ld %c %s\n", article_num(ap), (ap->flags & AF_UNREAD)? 'y' : 'n', ap->subj->str) FLUSH; termdown(1); } return 0; } #endif char ask_memorize(ch) char_int ch; { bool thread_cmd = (ch == 'T'); bool use_one_line = (gmode == 's'); bool global_save = FALSE; char* mode_string = (thread_cmd? "thread" : "subject"); char* mode_phrase = (thread_cmd? "replies to this article" : "this subject and all replies"); ART_NUM art_hold = art; ARTICLE* artp_hold = artp; if (!use_one_line) newline(); reask_memorize: sprintf(cmd_buf,"%sMemorize %s command:", global_save?"Global-" : nullstr, mode_string); in_char(cmd_buf, 'm', thread_cmd? "+S.mJK,jcC" : "+S.mJK,jcCfg"); #ifdef VERIFY printcmd(); #endif ch = *buf; if (!thread_cmd && ch == 'f') { mode_string = *mode_string == 'a'? "subject" : "author"; erase_line(0); goto reask_memorize; } if (!thread_cmd && ch == 'g') { global_save = !global_save; erase_line(0); goto reask_memorize; } if (ch == 'h' || ch == 'H') { use_one_line = FALSE; #ifdef VERBOSE IF(verbose) { printf("\n\ Type + or SP to auto-select this %s (i.e. includes future articles).\n\ Type S to auto-select the current subject.\n\ Type . to auto-select %s.\n\ Type m to auto-select the current article.\n\ Type J to auto-kill (junk) this %s.\n\ Type K to auto-kill the current subject.\n\ Type , to auto-kill %s.\n\ Type j to auto-kill the current article.\n\ Type C to clear all selection/killing on %s.\n\ Type c to clear all selection/killing on this %s.\n\ Type q to abort the operation.\n\ ",mode_string,mode_phrase,mode_string,mode_phrase,mode_phrase,mode_string) FLUSH; if (!thread_cmd) { printf("\ Type f to toggle author (from-line) searching.\n\ Type g to toggle global memorization.\n") FLUSH; termdown(2); } } ELSE #endif #ifdef TERSE { printf("\n\ + or SP auto-selects this %s.\n\ S auto-selects the subject.\n\ . auto-selects %s.\n\ m auto-selects this article.\n\ J auto-kills this %s.\n\ K auto-kills the subject.\n\ , auto-kills %s.\n\ j auto-kills the current article.\n\ C clears auto-commands for %s.\n\ c clears auto-commands for this %s.\n\ q aborts.\n\ ",mode_string,mode_phrase,mode_string,mode_phrase,mode_phrase,mode_string) FLUSH; if (!thread_cmd) { printf("\ f toggles author (from) mode.\n\ g toggles global memorization.\n"); termdown(2); } } #endif newline(); termdown(9); goto reask_memorize; } if (ch == 'q') { if (use_one_line) return 'Q'; newline(); return 'q'; } if (!thread_cmd) { buf[1] = *mode_string == 'a'? 'f' : 's'; buf[2] = global_save? 'g' : 'l'; } if (ch == '+') { if (!thread_cmd) { (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; ch = '.'; } else { select_arts_thread(artp, AUTO_SEL_THD); ch = (use_one_line? '+' : '.'); } if (gmode != 's') { printf("\nSelection memorized.\n"); termdown(2); } } else if (ch == 'S') { select_arts_subject(artp, AUTO_SEL_SBJ); ch = (use_one_line? '+' : '.'); if (gmode != 's') { printf("\nSelection memorized.\n"); termdown(2); } } else if (ch == '.') { if (!thread_cmd) { (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } else { select_subthread(artp, AUTO_SEL_FOL); ch = (use_one_line? '+' : '.'); } if (gmode != 's') { printf("\nSelection memorized.\n"); termdown(2); } } else if (ch == 'm') { if (artp) { change_auto_flags(artp, AUTO_SEL_1); ch = (use_one_line? '+' : '.'); if (gmode != 's') { printf("\nSelection memorized.\n"); termdown(2); } } } else if (ch == 'J') { if (!thread_cmd) { *buf = 'K'; (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } else kill_thread(artp->subj->thread,AFFECT_ALL|AUTO_KILL_THD); if (gmode != 's') { printf("\nKill memorized.\n"); termdown(2); } } else if (ch == 'j') { if (artp) { mark_as_read(artp); change_auto_flags(artp, AUTO_KILL_1); if (gmode != 's') { printf("\nKill memorized.\n"); termdown(2); } } } else if (ch == 'K') { kill_subject(artp->subj,AFFECT_ALL|AUTO_KILL_SBJ); if (gmode != 's') { printf("\nKill memorized.\n"); termdown(2); } } else if (ch == ',') { if (!thread_cmd) { (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } else kill_subthread(artp,AFFECT_ALL|AUTO_KILL_FOL); if (gmode != 's') { printf("\nKill memorized.\n"); termdown(2); } } else if (ch == 'C') { if (thread_cmd) clear_thread(artp->subj->thread); else clear_subject(artp->subj); } else if (ch == 'c') { clear_subthread(artp); } #if 0 else if (ch == 's') { buf[1] = FINISHCMD; finish_command(1); (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } #endif else { use_one_line = FALSE; printf("\n%s\n", hforhelp) FLUSH; termdown(3); settle_down(); goto reask_memorize; } if (!use_one_line) newline(); return ch; } trn-4.0-test77/ng.h0000644000000000000000000000273607113133016012571 0ustar rootroot/* ng.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT ART_NUM art INIT(0); /* current or prospective article # */ EXT ART_NUM recent_art; /* previous article # for '-' command */ EXT ART_NUM curr_art; /* current article # */ EXT ARTICLE* recent_artp INIT(0);/* article_ptr equivilents */ EXT ARTICLE* curr_artp INIT(0); EXT ARTICLE* artp INIT(0); /* the article ptr we use when art is 0 */ EXT int checkcount INIT(0); /* how many articles have we read */ /* in the current newsgroup since */ /* the last checkpoint? */ EXT int docheckwhen INIT(20); /* how often to do checkpoint */ EXT char* subjline INIT(NULL); /* what format to use for '=' */ #ifdef MAILCALL EXT int mailcount INIT(0); /* check for mail when 0 mod 10 */ #endif EXT char* mailcall INIT(nullstr); EXT bool forcelast INIT(FALSE); /* ought we show "End of newsgroup"? */ EXT bool forcegrow INIT(FALSE); /* do we want to recalculate size */ /* of newsgroup, e.g. after posting? */ #define NG_ERROR -1 #define NG_NORM 0 #define NG_ASK 1 #define NG_MINUS 2 #define NG_SELPRIOR 3 #define NG_SELNEXT 4 #define NG_NOSERVER 5 #define NG_NEXT 6 #define NG_GO_ARTICLE 7 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void ng_init _((void)); int do_newsgroup _((char*)); int art_switch _((void)); #ifdef MAILCALL void setmail _((bool_int)); #endif void setdfltcmd _((void)); char ask_catchup _((void)); bool output_subject _((char*,int)); char ask_memorize _((char_int)); trn-4.0-test77/ng.ih0000644000000000000000000000054007113133016012731 0ustar rootroot/* ng.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static bool count_unCACHED_article _((char*,int)); static bool mark_all_READ _((char*,int)); static bool mark_all_unREAD _((char*,int)); #ifdef DEBUG static bool debug_article_output _((char*,int)); #endif trn-4.0-test77/ngdata.c0000644000000000000000000001732607113133016013417 0ustar rootroot/* ngdata.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "hash.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 "term.h" #include "env.h" #include "util.h" #include "util2.h" #include "ndir.h" #ifdef SCORE #include "score.h" #endif #ifdef SCAN_ART #include "scan.h" #include "scanart.h" #endif #include "INTERN.h" #include "ngdata.h" #include "ngdata.ih" #include "EXTERN.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "rcstuff.h" #include "rcln.h" void ngdata_init() { ; } /* set current newsgroup */ void set_ng(np) NGDATA* np; { ngptr = np; if (ngptr) set_ngname(ngptr->rcline); } int access_ng() { #ifdef SUPPORT_NNTP ART_NUM old_first = ngptr->abs1st; if (datasrc->flags & DF_REMOTE) { int ret = nntp_group(ngname,ngptr); if (ret == -2) return -2; if (ret <= 0) { ngptr->toread = TR_BOGUS; return 0; } if ((lastart = getngsize(ngptr)) < 0) /* Impossible... */ return 0; absfirst = ngptr->abs1st; if (absfirst > old_first) checkexpired(ngptr,absfirst); } else #endif { if (eaccess(ngdir,5)) { /* directory read protected? */ if (eaccess(ngdir,0)) { # ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s does not have a spool directory!\n", ngname) FLUSH; ELSE # endif # ifdef TERSE printf("\nNo spool for %s!\n",ngname) FLUSH; # endif termdown(2); } 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 termdown(2); } /* make this newsgroup temporarily invisible */ ngptr->toread = TR_NONE; return 0; } /* chdir to newsgroup subdirectory */ if (chdir(ngdir)) { printf(nocd,ngdir) FLUSH; return 0; } if ((lastart = getngsize(ngptr)) < 0) /* Impossible... */ return 0; absfirst = ngptr->abs1st; } dmcount = 0; missing_count = 0; in_ng = TRUE; /* tell the world we are here */ build_cache(); return 1; } void chdir_newsdir() { if (chdir(datasrc->spool_dir) || ( #ifdef SUPPORT_NNTP !(datasrc->flags & DF_REMOTE) && #endif chdir(ngdir))) { printf(nocd,ngdir) FLUSH; sig_catcher(0); } } void grow_ng(newlast) ART_NUM newlast; { ART_NUM tmpfirst; forcegrow = FALSE; if (newlast > lastart) { ART_NUM tmpart = art; ngptr->toread += (ART_UNREAD)(newlast-lastart); tmpfirst = lastart+1; #ifdef SCAN_ART /* Increase the size of article scan arrays. */ sa_grow(lastart,newlast); #endif do { lastart++; article_ptr(lastart)->flags |= AF_EXISTS|AF_UNREAD; } while (lastart < newlast); article_list->high = lastart; thread_grow(); #ifdef SCORE /* Score all new articles now just in case they weren't done above. */ sc_fill_scorelist(tmpfirst,newlast); #endif #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 termdown(2); if (kf_state & KFS_NORMAL_LINES) { bool forcelast_save = forcelast; ARTICLE* artp_save = artp; kill_unwanted(tmpfirst,buf,TRUE); artp = artp_save; forcelast = forcelast_save; } #endif art = tmpart; } } static int ngorder_number(npp1, npp2) register NGDATA** npp1; register NGDATA** npp2; { return (int)((*npp1)->num - (*npp2)->num) * sel_direction; } static int ngorder_groupname(npp1, npp2) register NGDATA** npp1; register NGDATA** npp2; { return strcaseCMP((*npp1)->rcline, (*npp2)->rcline) * sel_direction; } static int ngorder_count(npp1, npp2) register NGDATA** npp1; register NGDATA** npp2; { int eq; if ((eq = (int)((*npp1)->toread - (*npp2)->toread)) != 0) return eq * sel_direction; return (int)((*npp1)->num - (*npp2)->num); } /* Sort the newsgroups into the chosen order. */ void sort_newsgroups() { register NGDATA* np; register int i; NGDATA** lp; NGDATA** ng_list; int (*sort_procedure)(); /* If we don't have at least two newsgroups, we're done! */ if (!first_ng || !first_ng->next) return; switch (sel_sort) { case SS_NATURAL: default: sort_procedure = ngorder_number; break; case SS_STRING: sort_procedure = ngorder_groupname; break; case SS_COUNT: sort_procedure = ngorder_count; break; } ng_list = (NGDATA**)safemalloc(newsgroup_cnt * sizeof (NGDATA*)); for (lp = ng_list, np = first_ng; np; np = np->next) *lp++ = np; assert(lp - ng_list == newsgroup_cnt); qsort(ng_list, newsgroup_cnt, sizeof (NGDATA*), sort_procedure); first_ng = np = ng_list[0]; np->prev = NULL; for (i = newsgroup_cnt, lp = ng_list; --i; lp++) { lp[0]->next = lp[1]; lp[1]->prev = lp[0]; } last_ng = lp[0]; last_ng->next = NULL; free((char*)ng_list); } void ng_skip() { #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) { 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 */ termdown(1); if (novice_delays) { pad(just_a_sec/3); sleep(1); } art = article_next(art); artp = article_ptr(art); 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_EXISTS) return; art = article_next(art); artp = article_ptr(art); } } while (art <= lastart); } else #endif { 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 termdown(2); if (novice_delays) { pad(just_a_sec); sleep(2); } } inc_art(selected_only,FALSE); /* try next article */ } } /* find the maximum article number of a newsgroup */ ART_NUM getngsize(gp) register NGDATA* gp; { register int len; register char* nam; char tmpbuf[LBUFLEN]; long last, first; char ch; nam = gp->rcline; len = gp->numoffset - 1; if (!find_actgrp(gp->rc->datasrc,tmpbuf,nam,len,gp->ngmax)) { if (gp->subscribechar == ':') { gp->subscribechar = NEGCHAR; gp->rc->flags |= RF_RCCHANGED; newsgroup_toread--; } return TR_BOGUS; } #ifdef ANCIENT_NEWS sscanf(tmpbuf+len+1, "%ld %c", &last, &ch); first = 1; #else sscanf(tmpbuf+len+1, "%ld %ld %c", &last, &first, &ch); #endif if (!gp->abs1st) gp->abs1st = (ART_NUM)first; if (!in_ng) { if (redirected) { if (redirected != nullstr) free(redirected); redirected = NULL; } switch (ch) { case 'n': moderated = getval("NOPOSTRING"," (no posting)"); break; case 'm': moderated = getval("MODSTRING", " (moderated)"); break; case 'x': redirected = nullstr; moderated = " (DISABLED)"; break; case '=': len = strlen(tmpbuf); if (tmpbuf[len-1] == '\n') tmpbuf[len-1] = '\0'; redirected = savestr(rindex(tmpbuf, '=') + 1); moderated = " (REDIRECTED)"; break; default: moderated = nullstr; break; } } if (last <= gp->ngmax) return gp->ngmax; return gp->ngmax = (ART_NUM)last; } trn-4.0-test77/ngdata.h0000644000000000000000000000451007113133016013413 0ustar rootroot/* ngdata.h */ /* This software is copyrighted as detailed in the LICENSE file. */ struct ngdata { NGDATA* prev; NGDATA* next; NEWSRC* rc; /* which rc is this line from? */ char* rcline; /* pointer to group's .newsrc line */ ART_NUM abs1st; /* 1st real article in newsgroup */ ART_NUM ngmax; /* high message num for the group */ ART_UNREAD toread; /* number of articles to be read in newsgroup */ /* < 0 is invalid or unsubscribed newsgroup */ NG_NUM num; /* a possible sort order for this group */ int numoffset; /* offset from rcline to numbers on line */ char subscribechar; /* holds the character : or ! while spot is \0 */ char flags; /* flags for each group */ }; EXT LIST* ngdata_list INIT(NULL); /* a list of NGDATA */ EXT int ngdata_cnt INIT(0); EXT NG_NUM newsgroup_cnt INIT(0); /* all newsgroups in our current newsrc(s) */ EXT NG_NUM newsgroup_toread INIT(0); EXT ART_UNREAD ng_min_toread INIT(1); /* == TR_ONE or TR_NONE */ EXT NGDATA* first_ng INIT(NULL); EXT NGDATA* last_ng INIT(NULL); EXT NGDATA* ngptr INIT(NULL); /* current newsgroup data ptr */ EXT NGDATA* current_ng INIT(NULL);/* stable current newsgroup so we can ditz with ngptr */ EXT NGDATA* recent_ng INIT(NULL); /* the prior newsgroup we visited */ EXT NGDATA* starthere INIT(NULL); /* set to the first newsgroup with unread news on startup */ #define ngdata_ptr(ngnum) ((NGDATA*)listnum2listitem(ngdata_list,(long)(ngnum))) /*#define ngdata_num(ngptr) listitem2listnum(ngdata_list,(char*)ngptr)*/ EXT NGDATA* sel_page_np; EXT NGDATA* sel_next_np; EXT ART_NUM absfirst INIT(0); /* 1st real article in current newsgroup */ EXT ART_NUM firstart INIT(0); /* minimum unread article number in newsgroup */ EXT ART_NUM lastart INIT(0); /* maximum article number in newsgroup */ EXT ART_UNREAD missing_count; /* for reports on missing articles */ EXT char* moderated; EXT char* redirected; EXT bool ThreadedGroup; /* CAA goto-newsgroup extensions */ EXT NGDATA* ng_go_ngptr INIT(NULL); EXT ART_NUM ng_go_artnum INIT(0); EXT char* ng_go_msgid INIT(NULL); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void ngdata_init _((void)); void set_ng _((NGDATA*)); int access_ng _((void)); void chdir_newsdir _((void)); void grow_ng _((ART_NUM)); void sort_newsgroups _((void)); void ng_skip _((void)); ART_NUM getngsize _((NGDATA*)); trn-4.0-test77/ngdata.ih0000644000000000000000000000045607113133016013571 0ustar rootroot/* ngdata.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int ngorder_number _((NGDATA**,NGDATA**)); static int ngorder_groupname _((NGDATA**,NGDATA**)); static int ngorder_count _((NGDATA**,NGDATA**)); trn-4.0-test77/ngsrch.c0000644000000000000000000001111007113133016013426 0ustar rootroot/* ngsrch.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "ngdata.h" #include "hash.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "nntp.h" #include "ngstuff.h" #include "rcstuff.h" #include "final.h" #include "search.h" #include "trn.h" #include "util.h" #include "util2.h" #include "term.h" #include "rcln.h" #include "cache.h" #include "rt-select.h" #include "rt-util.h" #include "INTERN.h" #include "ngsrch.h" #ifdef NGSEARCH COMPEX ngcompex; #endif void ngsrch_init() { #ifdef NGSEARCH init_compex(&ngcompex); #endif } #ifdef NGSEARCH int ng_search(patbuf,get_cmd) char* patbuf; /* if patbuf != buf, get_cmd must */ int get_cmd; /* be set to FALSE!!! */ { register char cmdchr = *patbuf; /* what kind of search? */ register char* s; char* pattern; /* unparsed pattern */ char* cmdlst = NULL; /* list of commands to do */ int ret = NGS_NOTFOUND; /* assume no commands */ bool backward = cmdchr == '?'; /* direction of search */ bool output_level = (!use_threads && gmode != 's'); NGDATA* ng_start = ngptr; int_count = 0; if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return NGS_ABORT; perform_status_init(newsgroup_toread); s = cpytill(buf,patbuf+1,cmdchr); /* ok to cpy buf+1 to buf */ for (pattern = buf; *pattern == ' '; pattern++) ; if (*pattern) ng_doempty = FALSE; if (*s) { /* modifiers or commands? */ while (*++s) { switch (*s) { case 'r': ng_doempty = TRUE; break; default: goto loop_break; } } loop_break:; } while (isspace(*s) || *s == ':') s++; if (*s) cmdlst = savestr(s); else if (gmode == 's') cmdlst = savestr("+"); if (cmdlst) ret = NGS_DONE; if ((s = ng_comp(&ngcompex,pattern,TRUE,TRUE)) != NULL) { /* compile regular expression */ errormsg(s); ret = NGS_ERROR; goto exit; } if (!cmdlst) { fputs("\nSearching...",stdout); /* give them something to read */ fflush(stdout); } if (first_addgroup) { ADDGROUP *gp = first_addgroup; do { if (execute(&ngcompex,gp->name) != NULL) { if (!cmdlst) return NGS_FOUND; if (addgrp_perform(gp,cmdlst,output_level && page_line==1)<0) { free(cmdlst); return NGS_INTR; } } if (!output_level && page_line == 1) perform_status(newsgroup_toread, 50); } while ((gp = gp->next) != NULL); goto exit; } if (backward) { if (!ngptr) ng_start = ngptr = last_ng; else if (!cmdlst) { if (ngptr == first_ng) /* skip current newsgroup */ ngptr = last_ng; else ngptr = ngptr->prev; } } else { if (!ngptr) ng_start = ngptr = first_ng; else if (!cmdlst) { if (ngptr == last_ng) /* skip current newsgroup */ ngptr = first_ng; else ngptr = ngptr->next; } } if (!ngptr) return NGS_NOTFOUND; do { if (int_count) { int_count = 0; ret = NGS_INTR; break; } if (ngptr->toread >= TR_NONE && ng_wanted(ngptr)) { if (ngptr->toread == TR_NONE) set_toread(ngptr, ST_LAX); if (ng_doempty || ((ngptr->toread > TR_NONE) ^ sel_rereading)) { if (!cmdlst) return NGS_FOUND; set_ng(ngptr); if (ng_perform(cmdlst,output_level && page_line == 1) < 0) { free(cmdlst); return NGS_INTR; } } if (output_level && !cmdlst) { printf("\n[0 unread in %s -- skipping]",ngptr->rcline); fflush(stdout); } } if (!output_level && page_line == 1) perform_status(newsgroup_toread, 50); } while ((ngptr = (backward? (ngptr->prev? ngptr->prev : last_ng) : (ngptr->next? ngptr->next : first_ng))) != ng_start); exit: if (cmdlst) free(cmdlst); return ret; } #endif /* NGSEARCH */ #ifdef NGSEARCH bool ng_wanted(np) NGDATA* np; { return execute(&ngcompex,np->rcline) != NULL; } #endif /* NGSEARCH */ char* ng_comp(compex,pattern,RE,fold) COMPEX* compex; char* pattern; bool_int RE; bool_int fold; { char ng_pattern[128]; register char* s = pattern; register char* d = ng_pattern; if (!*s) { if (compile(compex, "", RE, fold)) return "No previous search pattern"; return NULL; /* reuse old pattern */ } for (; *s; s++) { if (*s == '.') { *d++ = '\\'; *d++ = *s; } else if (*s == '?') { *d++ = '.'; } else if (*s == '*') { *d++ = '.'; *d++ = *s; } #if OLD_RN_WAY else if (strnEQ(s,"all",3)) { *d++ = '.'; *d++ = '*'; s += 2; } #endif else *d++ = *s; } *d = '\0'; return compile(compex,ng_pattern,RE,fold); } trn-4.0-test77/ngsrch.h0000644000000000000000000000101207113133016013433 0ustar rootroot/* ngsrch.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifdef NGSEARCH #define NGS_ABORT 0 #define NGS_FOUND 1 #define NGS_INTR 2 #define NGS_NOTFOUND 3 #define NGS_ERROR 4 #define NGS_DONE 5 EXT bool ng_doempty INIT(FALSE); /* search empty newsgroups? */ #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void ngsrch_init _((void)); #ifdef NGSEARCH int ng_search _((char*,int)); bool ng_wanted _((NGDATA*)); #endif char* ng_comp _((COMPEX*,char*,bool_int,bool_int)); trn-4.0-test77/ngstuff.c0000644000000000000000000004053607222733641013647 0ustar rootroot/* ngstuff.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "term.h" #include "util.h" #include "util2.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "ng.h" #include "intrp.h" #include "head.h" #include "final.h" #include "sw.h" #include "rthread.h" #include "rt-select.h" #include "rt-wumpus.h" #include "rt-util.h" #include "trn.h" #include "rcln.h" #include "rcstuff.h" #include "respond.h" #include "kfile.h" #include "decode.h" #include "addng.h" #include "opt.h" #include "only.h" #include "INTERN.h" #include "ngstuff.h" void ngstuff_init() { ; } /* do a shell escape */ int escapade() { register char* s; bool interactive = (buf[1] == FINISHCMD); bool docd; char whereiam[1024]; if (!finish_command(interactive)) /* get remainder of command */ return -1; s = buf+1; docd = *s != '!'; if (!docd) { s++; } else { trn_getwd(whereiam, sizeof(whereiam)); if (chdir(cwd)) { printf(nocd,cwd) FLUSH; sig_catcher(0); } } while (*s == ' ') s++; /* skip leading spaces */ interp(cmd_buf, (sizeof cmd_buf), s);/* interpret any % escapes */ resetty(); /* make sure tty is friendly */ doshell((char*)NULL,cmd_buf); /* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ if (docd) { if (chdir(whereiam)) { printf(nocd,whereiam) FLUSH; sig_catcher(0); } } #ifdef MAILCALL mailcount = 0; /* force recheck */ #endif return 0; } /* process & command */ int switcheroo() { if (!finish_command(TRUE)) /* get rest of command */ return -1; /* if rubbed out, try something else */ if (!buf[1]) { char* prior_savedir = savedir; if (option_sel_ilock) { buf[1] = '\0'; return 0; } option_sel_ilock = TRUE; if (gmode != 's' || sel_mode != SM_OPTIONS)/*$$*/ option_selector(); option_sel_ilock = FALSE; if (savedir != prior_savedir) cwd_check(); buf[1] = '\0'; } else if (buf[1] == '&') { if (!buf[2]) { page_start(); show_macros(); } else { char tmpbuf[LBUFLEN]; register char* s; for (s=buf+2; isspace(*s); s++); mac_line(s,tmpbuf,(sizeof tmpbuf)); } } else { bool docd = (instr(buf,"-d", TRUE) != NULL); char whereami[1024]; char tmpbuf[LBUFLEN+16]; if (docd) trn_getwd(whereami, sizeof(whereami)); if (buf[1] == '-' || buf[1] == '+') { strcpy(tmpbuf,buf+1); sw_list(tmpbuf); } else { sprintf(tmpbuf,"[options]\n%s\n",buf+1); prep_ini_data(tmpbuf,"'&' input"); parse_ini_section(tmpbuf+10,options_ini); set_options(INI_VALUES(options_ini)); } if (docd) { cwd_check(); if (chdir(whereami)) { /* -d does chdirs */ printf(nocd,whereami) FLUSH; sig_catcher(0); } } } return 0; } /* process range commands */ int numnum() { ART_NUM min, max; char* cmdlst = NULL; register char* s; register char* c; ART_NUM oldart = art; char tmpbuf[LBUFLEN]; bool output_level = (!use_threads && gmode != 's'); bool justone = TRUE; /* assume only one article */ if (!finish_command(TRUE)) /* get rest of command */ return NN_INP; if (lastart < 1) { errormsg("No articles"); return NN_ASK; } #ifdef ARTSEARCH if (srchahead) srchahead = -1; #endif perform_status_init(ngptr->toread); for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++) if (!isdigit(*s)) justone = FALSE; if (*s) { cmdlst = savestr(s); justone = FALSE; } else if (!justone) cmdlst = savestr("m"); *s++ = ','; *s = '\0'; safecpy(tmpbuf,buf,LBUFLEN); if (!output_level && !justone) { printf("Processing..."); fflush(stdout); } for (s = tmpbuf; (c = index(s,',')) != NULL; s = ++c) { *c = '\0'; if (*s == '.') min = oldart; else min = atol(s); if (min < absfirst) { min = absfirst; sprintf(msg,"(First article is %ld)",(long)absfirst); warnmsg(msg); } if ((s=index(s,'-')) != NULL) { s++; if (*s == '$') max = lastart; else if (*s == '.') max = oldart; else max = atol(s); } else max = min; if (max>lastart) { max = lastart; if (min > max) min = max; sprintf(msg,"(Last article is %ld)",(long)lastart) FLUSH; warnmsg(msg); } if (max < min) { errormsg("Bad range"); if (cmdlst) free(cmdlst); return NN_ASK; } if (justone) { art = min; return NN_REREAD; } for (art = article_first(min); art <= max; art = article_next(art)) { artp = article_ptr(art); if (perform(cmdlst,output_level && page_line == 1) < 0) { #ifdef VERBOSE IF(verbose) sprintf(msg,"(Interrupted at article %ld)",(long)art); ELSE #endif #ifdef TERSE sprintf(msg,"(Intr at %ld)",(long)art); #endif errormsg(msg); if (cmdlst) free(cmdlst); return NN_ASK; } if (!output_level) perform_status(ngptr->toread, 50); } } art = oldart; if (cmdlst) free(cmdlst); return NN_NORM; } int thread_perform() { register SUBJECT* sp; register ARTICLE* ap; bool want_unread; char* cmdstr; int len; int bits; bool output_level = (!use_threads && gmode != 's'); bool one_thread = FALSE; if (!finish_command(TRUE)) /* get rest of command */ return 0; if (!buf[1]) return -1; len = 1; if (buf[1] == ':') { bits = 0; len++; } else bits = SF_VISIT; if (buf[len] == '.') { if (!artp) return -1; one_thread = TRUE; len++; } cmdstr = savestr(buf+len); want_unread = !sel_rereading && *cmdstr != 'm'; perform_status_init(ngptr->toread); len = strlen(cmdstr); if (!output_level && !one_thread) { printf("Processing..."); fflush(stdout); } /* A few commands can just loop through the subjects. */ if ((len == 1 && (*cmdstr == 't' || *cmdstr == 'J')) || (len == 2 && (((*cmdstr == '+' || *cmdstr == '-') && cmdstr[0] == cmdstr[1]) || *cmdstr == 'T' || *cmdstr == 'A'))) { performed_article_loop = FALSE; if (one_thread) sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); else sp = next_subj((SUBJECT*)NULL,bits); for ( ; sp; sp = next_subj(sp,bits)) { if ((!(sp->flags & sel_mask) ^ !bits) || !sp->misc) continue; artp = first_art(sp); if (artp) { art = article_num(artp); if (perform(cmdstr, 0) < 0) { errormsg("Interrupted"); goto break_out; } } if (one_thread) break; } #if 0 } else if (strEQ(cmdstr, "E")) { /* The 'E'nd-decode command doesn't do any looping at all. */ if (decode_fp) decode_end(); #endif } else if (*cmdstr == 'p') { ART_NUM oldart = art; art = lastart+1; followup(); forcegrow = TRUE; art = oldart; page_line++; /*$$*/ } else { /* The rest loop through the articles. */ /* Use the explicit article-order if it exists */ if (artptr_list) { ARTICLE** app; ARTICLE** limit = artptr_list + artptr_list_size; sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); for (app = artptr_list; app < limit; app++) { ap = *app; if (one_thread && ap->subj->thread != sp->thread) continue; if ((!(ap->flags & AF_UNREAD) ^ want_unread) && !(ap->flags & sel_mask) ^ !!bits) { art = article_num(ap); artp = ap; if (perform(cmdstr, output_level && page_line == 1) < 0) { errormsg("Interrupted"); goto break_out; } } if (!output_level) perform_status(ngptr->toread, 50); } } else { if (one_thread) sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); else sp = next_subj((SUBJECT*)NULL,bits); for ( ; sp; sp = next_subj(sp,bits)) { for (ap = first_art(sp); ap; ap = next_art(ap)) if ((!(ap->flags & AF_UNREAD) ^ want_unread) && !(ap->flags & sel_mask) ^ !!bits) { art = article_num(ap); artp = ap; if (perform(cmdstr,output_level && page_line==1) < 0) { errormsg("Interrupted"); goto break_out; } } if (one_thread) break; if (!output_level) perform_status(ngptr->toread, 50); } } } break_out: free(cmdstr); return 1; } int perform(cmdlst,output_level) register char* cmdlst; int output_level; { register int ch; int savemode = 0; char tbuf[LBUFLEN+1]; /* A quick fix to avoid reuse of buf and cmdlst by shell commands. */ safecpy(tbuf, cmdlst, sizeof tbuf); cmdlst = tbuf; if (output_level == 1) { printf("%-6ld ",art); fflush(stdout); } perform_cnt++; for (; (ch = *cmdlst) != 0; cmdlst++) { if (isspace(ch) || ch == ':') continue; if (ch == 'j') { if (savemode) { mark_as_read(artp); change_auto_flags(artp, AUTO_KILL_1); } else if (!was_read(art)) { mark_as_read(artp); #ifdef VERBOSE IF(output_level && verbose) fputs("\tJunked",stdout); #endif } if (sel_rereading) deselect_article(artp, output_level? ALSO_ECHO : 0); } else if (ch == '+') { if (savemode || cmdlst[1] == '+') { if (sel_mode == SM_THREAD) select_arts_thread(artp, savemode? AUTO_SEL_THD : 0); else select_arts_subject(artp, savemode? AUTO_SEL_SBJ : 0); if (cmdlst[1] == '+') cmdlst++; } else select_article(artp, output_level? ALSO_ECHO : 0); } else if (ch == 'S') { select_arts_subject(artp, AUTO_SEL_SBJ); } else if (ch == '.') { select_subthread(artp, savemode? AUTO_SEL_FOL : 0); } else if (ch == '-') { if (cmdlst[1] == '-') { if (sel_mode == SM_THREAD) deselect_arts_thread(artp); else deselect_arts_subject(artp); cmdlst++; } else deselect_article(artp, output_level? ALSO_ECHO : 0); } else if (ch == ',') { kill_subthread(artp, AFFECT_ALL | (savemode? AUTO_KILL_FOL : 0)); } else if (ch == 'J') { if (sel_mode == SM_THREAD) kill_arts_thread(artp,AFFECT_ALL|(savemode? AUTO_KILL_THD:0)); else kill_arts_subject(artp,AFFECT_ALL|(savemode? AUTO_KILL_SBJ:0)); } else if (ch == 'K' || ch == 'k') { kill_arts_subject(artp, AFFECT_ALL|(savemode? AUTO_KILL_SBJ : 0)); } else if (ch == 'x') { if (!was_read(art)) { oneless(artp); #ifdef VERBOSE IF(output_level && verbose) fputs("\tKilled",stdout); #endif } if (sel_rereading) deselect_article(artp, 0); } else if (ch == 't') { entire_tree(artp); } else if (ch == 'T') { savemode = 1; } else if (ch == 'A') { savemode = 2; } else if (ch == 'm') { if (savemode) change_auto_flags(artp, AUTO_SEL_1); else if ((artp->flags & (AF_UNREAD|AF_EXISTS)) == AF_EXISTS) { unmark_as_read(artp); #ifdef VERBOSE IF(output_level && verbose) fputs("\tMarked unread",stdout); #endif } } else if (ch == 'M') { delay_unmark(artp); oneless(artp); #ifdef VERBOSE IF(output_level && verbose) fputs("\tWill return",stdout); #endif } else if (ch == '=') { carriage_return(); output_subject((char*)artp,0); output_level = 0; } else if (ch == 'C') { #ifdef ASYNC_PARSE int ret = cancel_article(); # ifdef VERBOSE IF(output_level && verbose) printf("\t%sanceled",ret? "Not c" : "C"); # endif #else notincl("C"); return -1; #endif } else if (ch == '%') { #ifdef ASYNC_PARSE char tmpbuf[512]; if (one_command) interp(tmpbuf, (sizeof tmpbuf), cmdlst); else cmdlst = dointerp(tmpbuf,sizeof tmpbuf,cmdlst,":",(char*)NULL) - 1; perform_cnt--; if (perform(tmpbuf,output_level?2:0) < 0) return -1; #else notincl("%"); return -1; #endif } else if (index("!&sSwWae|",ch)) { if (one_command) strcpy(buf,cmdlst); else cmdlst = cpytill(buf,cmdlst,':') - 1; /* we now have the command in buf */ if (ch == '!') { escapade(); #ifdef VERBOSE IF(output_level && verbose) fputs("\tShell escaped",stdout); #endif } else if (ch == '&') { switcheroo(); #ifdef VERBOSE IF(output_level && verbose) if (buf[1] && buf[1] != '&') fputs("\tSwitched",stdout); #endif } else { if (output_level != 1) { erase_line(FALSE); printf("%-6ld ",art); } if (ch == 'a') view_article(); else save_article(); newline(); output_level = 0; } } else { sprintf(msg,"Unknown command: %s",cmdlst); errormsg(msg); return -1; } #ifdef VERBOSE IF(output_level && verbose) fflush(stdout); #endif if (one_command) break; } #ifdef VERBOSE IF(output_level && verbose) newline(); #endif if (int_count) { int_count = 0; return -1; } return 1; } int ngsel_perform() { char* cmdstr; int len; int bits; bool one_group = FALSE; if (!finish_command(TRUE)) /* get rest of command */ return 0; if (!buf[1]) return -1; len = 1; if (buf[1] == ':') { bits = 0; len++; } else bits = NF_INCLUDED; if (buf[len] == '.') { if (!ngptr) return -1; one_group = TRUE; len++; } cmdstr = savestr(buf+len); perform_status_init(newsgroup_toread); len = strlen(cmdstr); if (one_group) { ng_perform(cmdstr, 0); goto break_out; } for (ngptr = first_ng; ngptr; ngptr = ngptr->next) { if (sel_rereading? ngptr->toread != TR_NONE : ngptr->toread < ng_min_toread) continue; set_ng(ngptr); if ((ngptr->flags & bits) == bits && (!(ngptr->flags & sel_mask) ^ !!bits)) { if (ng_perform(cmdstr, 0) < 0) break; } perform_status(newsgroup_toread, 50); } break_out: free(cmdstr); return 1; } int ng_perform(cmdlst, output_level) register char* cmdlst; int output_level; { register int ch; if (output_level == 1) { printf("%s ",ngname); fflush(stdout); } perform_cnt++; for (; (ch = *cmdlst) != 0; cmdlst++) { if (isspace(ch) || ch == ':') continue; switch (ch) { case '+': if (!(ngptr->flags & sel_mask)) { ngptr->flags = ((ngptr->flags | sel_mask) & ~NF_DEL); selected_count++; } break; case 'c': catch_up(ngptr, 0, 0); /* FALL THROUGH */ case '-': deselect: if (ngptr->flags & sel_mask) { ngptr->flags &= ~sel_mask; if (sel_rereading) ngptr->flags |= NF_DEL; selected_count--; } break; case 'u': #ifdef VERBOSE IF(output_level && verbose) { printf(unsubto,ngptr->rcline) FLUSH; termdown(1); } #endif ngptr->subscribechar = NEGCHAR; ngptr->toread = TR_UNSUB; ngptr->rc->flags |= RF_RCCHANGED; ngptr->flags &= ~sel_mask; newsgroup_toread--; goto deselect; default: sprintf(msg,"Unknown command: %s",cmdlst); errormsg(msg); return -1; } #ifdef VERBOSE IF(output_level && verbose) fflush(stdout); #endif if (one_command) break; } #ifdef VERBOSE IF(output_level && verbose) newline(); #endif if (int_count) { int_count = 0; return -1; } return 1; } int addgrp_sel_perform() { register ADDGROUP* gp; char* cmdstr; int len; int bits; bool one_group = FALSE; if (!finish_command(TRUE)) /* get rest of command */ return 0; if (!buf[1]) return -1; len = 1; if (buf[1] == ':') { bits = 0; len++; } else bits = sel_mask; if (buf[len] == '.') { if (first_addgroup) /*$$*/ return -1; one_group = TRUE; len++; } cmdstr = savestr(buf+len); perform_status_init(newsgroup_toread); len = strlen(cmdstr); if (one_group) { /*addgrp_perform(gp, cmdstr, 0);$$*/ goto break_out; } for (gp = first_addgroup; gp; gp = gp->next) { if (!(gp->flags & sel_mask) ^ !!bits) { if (addgrp_perform(gp, cmdstr, 0) < 0) break; } perform_status(newsgroup_toread, 50); } break_out: free(cmdstr); return 1; } int addgrp_perform(gp, cmdlst, output_level) register ADDGROUP* gp; register char* cmdlst; int output_level; { register int ch; if (output_level == 1) { printf("%s ",gp->name); fflush(stdout); } perform_cnt++; for (; (ch = *cmdlst) != 0; cmdlst++) { if (isspace(ch) || ch == ':') continue; if (ch == '+') { gp->flags |= AGF_SEL; selected_count++; } else if (ch == '-') { gp->flags &= ~AGF_SEL; selected_count--; } else { sprintf(msg,"Unknown command: %s",cmdlst); errormsg(msg); return -1; } #ifdef VERBOSE IF(output_level && verbose) fflush(stdout); #endif if (one_command) break; } #ifdef VERBOSE IF(output_level && verbose) newline(); #endif if (int_count) { int_count = 0; return -1; } return 1; } trn-4.0-test77/ngstuff.h0000644000000000000000000000146507113133016013637 0ustar rootroot/* ngstuff.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #define NN_NORM 0 #define NN_INP 1 #define NN_REREAD 2 #define NN_ASK 3 EXT bool one_command INIT(FALSE); /* no ':' processing in perform() */ /* CAA: given the new and complex universal/help possibilities, * the following interlock variable may save some trouble. * (if TRUE, we are currently processing options) */ EXT bool option_sel_ilock INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void ngstuff_init _((void)); int escapade _((void)); int switcheroo _((void)); int numnum _((void)); int thread_perform _((void)); int perform _((char*,int)); int ngsel_perform _((void)); int ng_perform _((char*,int)); int addgrp_sel_perform _((void)); int addgrp_perform _((ADDGROUP*,char*,int)); trn-4.0-test77/nntp.c0000644000000000000000000003160411437640112013137 0ustar rootroot/* nntp.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "util.h" #include "util2.h" #include "init.h" #include "trn.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "INTERN.h" #include "nntp.h" #include "nntp.ih" #include "EXTERN.h" #include "rcln.h" #include "cache.h" #include "bits.h" #include "head.h" #include "term.h" #include "final.h" #include "artio.h" #include "rcstuff.h" #ifdef SUPPORT_NNTP int nntp_list(type, arg, len) char* type; char* arg; int len; { int ret; #ifdef DEBUG /*$$*/ if (len && (debug & 1) && strcaseEQ(type,"active")) return -1; #endif if (len) sprintf(ser_line, "LIST %s %.*s", type, len, arg); else if (strcaseEQ(type,"active")) strcpy(ser_line, "LIST"); else sprintf(ser_line, "LIST %s", type); if (nntp_command(ser_line) <= 0) return -2; if ((ret = nntp_check()) <= 0) return ret? ret : -1; if (!len) return 1; if ((ret = nntp_gets(ser_line, sizeof ser_line)) < 0) return ret; #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf("<%s\n", ser_line) FLUSH; #endif if (nntp_at_list_end(ser_line)) return 0; return 1; } void nntp_finish_list() { int ret; do { while ((ret = nntp_gets(ser_line, sizeof ser_line)) == 0) { /* A line w/o a newline is too long to be the end of the ** list, so grab the rest of this line and try again. */ while ((ret = nntp_gets(ser_line, sizeof ser_line)) == 0) ; if (ret < 0) return; } } while (ret > 0 && !nntp_at_list_end(ser_line)); } /* try to access the specified group */ int nntp_group(group, gp) char* group; NGDATA* gp; { sprintf(ser_line, "GROUP %s", group); if (nntp_command(ser_line) <= 0) return -2; switch (nntp_check()) { case -2: return -2; case -1: case 0: { int ser_int = atoi(ser_line); if (ser_int != NNTP_NOSUCHGROUP_VAL && ser_int != NNTP_SYNTAX_VAL) { if (ser_int != NNTP_AUTH_NEEDED_VAL && ser_int != NNTP_ACCESS_VAL && ser_int != NNTP_AUTH_REJECT_VAL) { fprintf(stderr, "\nServer's response to GROUP %s:\n%s\n", group, ser_line); return -1; } } return 0; } } if (gp) { long count, first, last; (void) sscanf(ser_line,"%*d%ld%ld%ld",&count,&first,&last); /* NNTP mangles the high/low values when no articles are present. */ if (!count) gp->abs1st = gp->ngmax+1; else { gp->abs1st = (ART_NUM)first; gp->ngmax = (ART_NUM)last; } } return 1; } /* check on an article's existence */ int nntp_stat(artnum) ART_NUM artnum; { sprintf(ser_line, "STAT %ld", (long)artnum); if (nntp_command(ser_line) <= 0) return -2; return nntp_check(); } /* check on an article's existence by its message id */ ART_NUM nntp_stat_id(msgid) char* msgid; { long artnum; sprintf(ser_line, "STAT %s", msgid); if (nntp_command(ser_line) <= 0) return -2; artnum = nntp_check(); if (artnum > 0 && sscanf(ser_line, "%*d%ld", &artnum) != 1) artnum = 0; return (ART_NUM)artnum; } ART_NUM nntp_next_art() { long artnum; if (nntp_command("NEXT") <= 0) return -2; artnum = nntp_check(); if (artnum > 0 && sscanf(ser_line, "%*d %ld", &artnum) != 1) artnum = 0; return (ART_NUM)artnum; } /* prepare to get the header */ int nntp_header(artnum) ART_NUM artnum; { sprintf(ser_line, "HEAD %ld", (long)artnum); if (nntp_command(ser_line) <= 0) return -2; return nntp_check(); } /* copy the body of an article to a temporary file */ void nntp_body(artnum) ART_NUM artnum; { char* artname; artname = nntp_artname(artnum, FALSE); /* Is it already in a tmp file? */ if (artname) { if (body_pos >= 0) nntp_finishbody(FB_DISCARD); artfp = fopen(artname,"r"); if (artfp && fstat(fileno(artfp),&filestat) == 0) body_end = filestat.st_size; return; } artname = nntp_artname(artnum, TRUE); /* Allocate a tmp file */ if (!(artfp = fopen(artname, "w+"))) { fprintf(stderr, "\nUnable to write temporary file: '%s'.\n", artname); finalize(1); /*$$*/ } #ifndef MSDOS chmod(artname, 0600); #endif /*artio_setbuf(artfp);$$*/ if (parsed_art == artnum) sprintf(ser_line, "BODY %ld", (long)artnum); else sprintf(ser_line, "ARTICLE %ld", (long)artnum); if (nntp_command(ser_line) <= 0) finalize(1); /*$$*/ switch (nntp_check()) { case -2: case -1: finalize(1); /*$$*/ case 0: fclose(artfp); artfp = NULL; errno = ENOENT; /* Simulate file-not-found */ return; } body_pos = 0; if (parsed_art == artnum) { fwrite(headbuf, 1, strlen(headbuf), artfp); htype[PAST_HEADER].minpos = body_end = (ART_POS)ftell(artfp); } else { char b[NNTP_STRLEN]; ART_POS prev_pos = body_end = 0; while (nntp_copybody(b, sizeof b, body_end+1) > 0) { if (*b == '\n' && body_end - prev_pos < sizeof b) break; prev_pos = body_end; } } fseek(artfp, 0L, 0); nntplink.flags &= ~NNTP_NEW_CMD_OK; } long nntp_artsize() { return body_pos < 0 ? body_end : -1; } static int nntp_copybody(s, limit, pos) char* s; int limit; ART_POS pos; { int len; bool had_nl = TRUE; int found_nl; while (pos > body_end || !had_nl) { found_nl = nntp_gets(s, limit); if (found_nl < 0) strcpy(s,"."); /*$$*/ if (had_nl) { if (nntp_at_list_end(s)) { fseek(artfp, (long)body_pos, 0); body_pos = -1; return 0; } if (s[0] == '.') safecpy(s,s+1,limit); } len = strlen(s); if (found_nl) strcpy(s+len, "\n"); fputs(s, artfp); body_end = ftell(artfp); had_nl = found_nl; } return 1; } int nntp_finishbody(bmode) int bmode; { char b[NNTP_STRLEN]; if (body_pos < 0) return 0; if (bmode == FB_DISCARD) { /*printf("Discarding the rest of the article...\n") FLUSH; $$*/ #if 0 /* Implement this if flushing the data becomes possible */ nntp_artname(openart, -1); /* Or something... */ openart = 0; /* Since we didn't finish the art, forget its number */ #endif } else if (bmode == FB_OUTPUT) { #ifdef VERBOSE IF(verbose) printf("Receiving the rest of the article..."), fflush(stdout); ELSE #endif #ifdef TERSE printf("Receiving..."), fflush(stdout); #endif } if (body_end != body_pos) fseek(artfp, (long)body_end, 0); if (bmode != FB_BACKGROUND) nntp_copybody(b, sizeof b, (ART_POS)0x7fffffffL); else { while (nntp_copybody(b, sizeof b, body_end+1)) { if (input_pending()) break; } if (body_pos >= 0) fseek(artfp, (long)body_pos, 0); } if (bmode == FB_OUTPUT) erase_line(0); /* erase the prompt */ return 1; } int nntp_seekart(pos) ART_POS pos; { if (body_pos >= 0) { if (body_end < pos) { char b[NNTP_STRLEN]; fseek(artfp, (long)body_end, 0); nntp_copybody(b, sizeof b, pos); if (body_pos >= 0) body_pos = pos; } else body_pos = pos; } return fseek(artfp, (long)pos, 0); } ART_POS nntp_tellart() { return body_pos < 0 ? (ART_POS)ftell(artfp) : body_pos; } char* nntp_readart(s, limit) char* s; int limit; { if (body_pos >= 0) { if (body_pos == body_end) { if (nntp_copybody(s, limit, body_pos+1) <= 0) return NULL; if (body_end - body_pos < limit) { body_pos = body_end; return s; } fseek(artfp, (long)body_pos, 0); } s = fgets(s, limit, artfp); body_pos = ftell(artfp); if (body_pos == body_end) fseek(artfp, (long)body_pos, 0); /* Prepare for coming write */ return s; } return fgets(s, limit, artfp); } /* This is a 1-relative list */ static int maxdays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t nntp_time() { char* s; int year, month, day, hh, mm; time_t ss; if (nntp_command("DATE") <= 0) return -2; if (nntp_check() <= 0) return time((time_t*)NULL); s = rindex(ser_line, ' ') + 1; month = (s[4] - '0') * 10 + (s[5] - '0'); day = (s[6] - '0') * 10 + (s[7] - '0'); hh = (s[8] - '0') * 10 + (s[9] - '0'); mm = (s[10] - '0') * 10 + (s[11] - '0'); ss = (s[12] - '0') * 10 + (s[13] - '0'); s[4] = '\0'; year = atoi(s); /* This simple algorithm will be valid until the year 2100 */ if (year % 4) maxdays[2] = 28; else maxdays[2] = 29; if (month < 1 || month > 12 || day < 1 || day > maxdays[month] || hh < 0 || hh > 23 || mm < 0 || mm > 59 || ss < 0 || ss > 59) return time((time_t*)NULL); for (month--; month; month--) day += maxdays[month]; ss = ((((year-1970) * 365 + (year-1969)/4 + day - 1) * 24L + hh) * 60 + mm) * 60 + ss; return ss; } int nntp_newgroups(t) time_t t; { struct tm *ts; ts = gmtime(&t); sprintf(ser_line, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT", ts->tm_year % 100, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec); if (nntp_command(ser_line) <= 0) return -2; return nntp_check(); } int nntp_artnums() { if (datasrc->flags & DF_NOLISTGROUP) return 0; if (nntp_command("LISTGROUP") <= 0) return -2; if (nntp_check() <= 0) { datasrc->flags |= DF_NOLISTGROUP; return 0; } return 1; } #if 0 int nntp_rover() { if (datasrc->flags & DF_NOXROVER) return 0; if (nntp_command("XROVER 1-") <= 0) return -2; if (nntp_check() <= 0) { datasrc->flags |= DF_NOXROVER; return 0; } return 1; } #endif ART_NUM nntp_find_real_art(after) ART_NUM after; { ART_NUM an; if (last_cached > after || last_cached < absfirst || nntp_stat(last_cached) <= 0) { if (nntp_stat_id("") > after) return 0; } while ((an = nntp_next_art()) > 0) { if (an > after) return an; if (after - an > 10) break; } return 0; } char* nntp_artname(artnum, allocate) ART_NUM artnum; bool_int allocate; { static ART_NUM artnums[MAX_NNTP_ARTICLES]; static time_t artages[MAX_NNTP_ARTICLES]; time_t now, lowage; int i, j; if (!artnum) { for (i = 0; i < MAX_NNTP_ARTICLES; i++) { artnums[i] = 0; artages[i] = 0; } return NULL; } now = time((time_t*)NULL); for (i = j = 0, lowage = now; i < MAX_NNTP_ARTICLES; i++) { if (artnums[i] == artnum) { artages[i] = now; return nntp_tmpname(i); } if (artages[i] <= lowage) lowage = artages[j = i]; } if (allocate) { artnums[j] = artnum; artages[j] = now; return nntp_tmpname(j); } return NULL; } char* nntp_tmpname(ndx) int ndx; { static char artname[20]; sprintf(artname,"rrn.%ld.%d",our_pid,ndx); return artname; } int nntp_handle_nested_lists() { if (strcaseEQ(last_command,"quit")) return 0; /*$$ flush data needed? */ if (nntp_finishbody(FB_DISCARD)) return 1; fprintf(stderr,"Programming error! Nested NNTP calls detected.\n"); return -1; } int nntp_handle_timeout() { static bool handling_timeout = FALSE; char last_command_save[NNTP_STRLEN]; if (strcaseEQ(last_command,"quit")) return 0; if (handling_timeout) return -1; handling_timeout = TRUE; strcpy(last_command_save, last_command); nntp_close(FALSE); datasrc->nntplink = nntplink; if (nntp_connect(datasrc->newsid, 0) <= 0) return -2; datasrc->nntplink = nntplink; if (in_ng && nntp_group(ngname, (NGDATA*)NULL) <= 0) return -2; if (nntp_command(last_command_save) <= 0) return -1; strcpy(last_command, last_command_save); /*$$ Is this really needed? */ handling_timeout = FALSE; return 1; } void nntp_server_died(dp) DATASRC* dp; { MULTIRC* mp = multirc; close_datasrc(dp); dp->flags |= DF_UNAVAILABLE; unuse_multirc(mp); if (!use_multirc(mp)) multirc = NULL; fprintf(stderr,"\n%s\n", ser_line); get_anything(); } /* nntp_readcheck -- get a line of text from the server, interpreting ** it as a status message for a binary command. Call this once ** before calling nntp_read() for the actual data transfer. */ #ifdef SUPPORT_XTHREAD long nntp_readcheck() { /* try to get the status line and the status code */ switch (nntp_check()) { case -2: return -2; case -1: case 0: return rawbytes = -1; } /* try to get the number of bytes being transfered */ if (sscanf(ser_line, "%*d%ld", &rawbytes) != 1) return rawbytes = -1; return rawbytes; } #endif /* nntp_read -- read data from the server in binary format. This call must ** be preceeded by an appropriate binary command and an nntp_readcheck call. */ #ifdef SUPPORT_XTHREAD long nntp_read(buf, n) char* buf; long n; { /* if no bytes to read, then just return EOF */ if (rawbytes < 0) return 0; #ifdef HAS_SIGHOLD sighold(SIGINT); #endif /* try to read some data from the server */ if (rawbytes) { n = fread(buf, 1, n > rawbytes ? rawbytes : n, nntplink.rd_fp); rawbytes -= n; } else n = 0; /* if no more left, then fetch the end-of-command signature */ if (!rawbytes) { char buf[5]; /* "\r\n.\r\n" */ fread(buf, 1, 5, nntplink.rd_fp); rawbytes = -1; } #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif return n; } #endif /* SUPPORT_XTHREAD */ #endif /* SUPPORT_NNTP */ trn-4.0-test77/nntp.h0000644000000000000000000000223407113133016013135 0ustar rootroot/* nntp.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifdef SUPPORT_NNTP #define FB_BACKGROUND 0 #define FB_OUTPUT 1 #define FB_SILENT 2 #define FB_DISCARD 3 #define MAX_NNTP_ARTICLES 10 #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef SUPPORT_NNTP int nntp_list _((char*,char*,int)); #endif void nntp_finish_list _((void)); int nntp_group _((char*,NGDATA*)); int nntp_stat _((ART_NUM)); ART_NUM nntp_stat_id _((char*)); ART_NUM nntp_next_art _((void)); int nntp_header _((ART_NUM)); void nntp_body _((ART_NUM)); long nntp_artsize _((void)); int nntp_finishbody _((int)); int nntp_seekart _((ART_POS)); ART_POS nntp_tellart _((void)); char* nntp_readart _((char*,int)); time_t nntp_time _((void)); int nntp_newgroups _((time_t)); int nntp_artnums _((void)); #if 0 int nntp_rover _((void)); #endif ART_NUM nntp_find_real_art _((ART_NUM)); char* nntp_artname _((ART_NUM,bool_int)); char* nntp_tmpname _((int)); int nntp_handle_nested_lists _((void)); int nntp_handle_timeout _((void)); void nntp_server_died _((DATASRC*)); #ifdef SUPPORT_XTHREAD long nntp_readcheck _((void)); long nntp_read _((char*,long)); #endif trn-4.0-test77/nntp.ih0000644000000000000000000000054207113133016013306 0ustar rootroot/* nntp.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ static ART_POS body_pos = -1; static ART_POS body_end = 0; #ifdef SUPPORT_XTHREAD static long rawbytes = -1; /* bytes remaining to be transfered */ #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int nntp_copybody _((char*,int,ART_POS)); trn-4.0-test77/nntpauth.c0000644000000000000000000000353307113133016014015 0ustar rootroot/* nntpauth.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "nntp.h" #include "final.h" #include "util.h" #include "env.h" #include "INTERN.h" #include "nntpauth.h" #ifdef SUPPORT_NNTP int nntp_handle_auth_err() { char last_command_save[NNTP_STRLEN]; #ifdef USE_GENAUTH char* auth_command; #endif /* save previous command */ strcpy(last_command_save, last_command); #ifdef USE_GENAUTH if ((auth_command = get_auth_command()) != NULL) { /* issue authentication request */ sprintf(ser_line, "AUTHINFO GENERIC %s", auth_command); if (nntp_command(ser_line) <= 0 || nntp_auth(auth_command) <= 0) return -2; } else #endif { char* auth_user = get_auth_user(); char* auth_pass = get_auth_pass(); if (!auth_user || !auth_pass) return -2; sprintf(ser_line, "AUTHINFO USER %s", auth_user); if (nntp_command(ser_line) <= 0 || nntp_check() <= 0) return -2; sprintf(ser_line, "AUTHINFO PASS %s", auth_pass); if (nntp_command(ser_line) <= 0 || nntp_check() <= 0) return -2; } if (nntp_command(last_command_save) <= 0) return -2; return 1; } #ifdef USE_GENAUTH int nntp_auth(authc) char* authc; { int ret; if (nntplink.cookiefd == 0) { FILE* fp = tmpfile(); if (fp) nntplink.cookiefd = fileno(fp); } #if 0 /*termlib_reset();*/ resetty(); /* restore tty state */ #endif export_nntp_fds = TRUE; ret = doshell(sh,authc); export_nntp_fds = FALSE; #if 0 noecho(); /* revert to cbreaking */ crmode(); /*termlib_init();*/ #endif if (ret) { strcpy(ser_line, "502 Authentication failed"); return -1; } strcpy(ser_line, "281 Ok"); return 1; } #endif /* USE_GENAUTH */ #endif /* SUPPORT_NNTP */ trn-4.0-test77/nntpauth.h0000644000000000000000000000041307113133016014014 0ustar rootroot/* nntpauth.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef SUPPORT_NNTP int nntp_handle_auth_err _((void)); #endif #ifdef USE_GENAUTH int nntp_auth _((char*)); #endif trn-4.0-test77/nntpclient.c0000644000000000000000000001541611437640112014341 0ustar rootroot/* nntpclient.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #ifdef SUPPORT_NNTP #include "nntpinit.h" #include "INTERN.h" #include "nntpclient.h" time_t last_command_diff; char* savestr _((char*)); #ifndef USE_DEBUGGING_MALLOC char* safemalloc _((MEM_SIZE)); #endif #ifdef NNTP_HANDLE_TIMEOUT int nntp_handle_timeout _((void)); #endif int nntp_handle_nested_lists _((void)); #ifdef NNTP_HANDLE_AUTH_ERR int nntp_handle_auth_err _((void)); #endif int nntp_connect(machine,verbose) char* machine; bool_int verbose; { int response; if (nntplink.rd_fp) return 1; if (nntplink.flags & NNTP_FORCE_AUTH_NEEDED) nntplink.flags |= NNTP_FORCE_AUTH_NOW; nntplink.flags |= NNTP_NEW_CMD_OK; #if 0 try_to_connect: #endif if (verbose) { printf("Connecting to %s...",machine); fflush(stdout); } switch (response = server_init(machine)) { case NNTP_GOODBYE_VAL: if (atoi(ser_line) == response) { char tmpbuf[LBUFLEN]; if (verbose) printf("failed: %s\n",&ser_line[4]); else { sprintf(tmpbuf,"News server \"%s\" is unavailable: %s\n", machine,&ser_line[4]); nntp_init_error(tmpbuf); } response = 0; break; } case -1: if (verbose) printf("failed.\n"); else { sprintf(ser_line,"News server \"%s\" is unavailable.\n",machine); nntp_init_error(ser_line); } response = 0; break; case NNTP_ACCESS_VAL: if (verbose) printf("access denied.\n"); else { sprintf(ser_line, "This machine does not have permission to use the %s news server.\n\n", machine); nntp_init_error(ser_line); } response = -1; break; case NNTP_NOPOSTOK_VAL: if (verbose) printf("Done (but no posting).\n\n"); response = 1; break; case NNTP_POSTOK_VAL: if (verbose) printf("Done.\n") FLUSH; response = 1; break; default: if (verbose) printf("unknown response: %d.\n",response); else { sprintf(ser_line,"\nUnknown response code %d from %s.\n", response,machine); nntp_init_error(ser_line); } response = 0; break; } #if 0 if (!response) { if (handle_no_connect() > 0) goto try_to_connect; } #endif return response; } char* nntp_servername(name) char* name; { FILE* fp; if (FILE_REF(name) && (fp = fopen(name, "r")) != NULL) { while (fgets(ser_line, sizeof ser_line, fp) != NULL) { if (*ser_line == '\n' || *ser_line == '#') continue; if ((name = index(ser_line, '\n')) != NULL) *name = '\0'; name = ser_line; break; } fclose(fp); } return name; } int nntp_command(bp) char* bp; { time_t now; #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf(">%s\n", bp) FLUSH; #endif #if defined(NNTP_HANDLE_TIMEOUT) || defined(NNTP_HANDLE_AUTH_ERR) strcpy(last_command, bp); # ifdef NNTP_HANDLE_TIMEOUT if (!nntplink.rd_fp) return nntp_handle_timeout(); # endif # ifdef NNTP_HANDLE_AUTH_ERR if (nntplink.flags & NNTP_FORCE_AUTH_NOW) { nntplink.flags &= ~NNTP_FORCE_AUTH_NOW; return nntp_handle_auth_err(); } # endif #endif if (!(nntplink.flags & NNTP_NEW_CMD_OK)) { int ret = nntp_handle_nested_lists(); if (ret <= 0) return ret; } if (fprintf(nntplink.wr_fp, "%s\r\n", bp) < 0 || fflush(nntplink.wr_fp) < 0) #ifdef NNTP_HANDLE_TIMEOUT return nntp_handle_timeout(); #else return -2; #endif now = time((time_t*)NULL); last_command_diff = now - nntplink.last_command; nntplink.last_command = now; return 1; } int nntp_check() { int ret; int len = 0; read_it: #ifdef HAS_SIGHOLD sighold(SIGINT); #endif errno = 0; ret = (fgets(ser_line, sizeof ser_line, nntplink.rd_fp) == NULL)? -2 : 0; #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif if (ret < 0) { if (errno == EINTR) goto read_it; strcpy(ser_line, "503 Server closed connection."); } #ifdef NNTP_HANDLE_TIMEOUT if (len == 0 && atoi(ser_line) == NNTP_TMPERR_VAL && nntp_allow_timeout && last_command_diff > 60) { ret = nntp_handle_timeout(); switch (ret) { case 1: len = 1; goto read_it; case 0: /* We're quitting, so pretend it's OK */ strcpy(ser_line, "205 Ok"); break; default: break; } } else #endif if (*ser_line <= NNTP_CLASS_CONT && *ser_line >= NNTP_CLASS_INF) ret = 1; /* (this includes NNTP_CLASS_OK) */ else if (*ser_line == NNTP_CLASS_FATAL) ret = -1; /* Even though the following check doesn't catch all possible lists, the * bit will get set right when the caller checks nntp_at_list_end(). */ if (atoi(ser_line) == NNTP_LIST_FOLLOWS_VAL) nntplink.flags &= ~NNTP_NEW_CMD_OK; else nntplink.flags |= NNTP_NEW_CMD_OK; len = strlen(ser_line); if (len >= 2 && ser_line[len-1] == '\n' && ser_line[len-2] == '\r') ser_line[len-2] = '\0'; #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf("<%s\n", ser_line) FLUSH; #endif #ifdef NNTP_HANDLE_AUTH_ERR if (atoi(ser_line) == NNTP_AUTH_NEEDED_VAL) { ret = nntp_handle_auth_err(); if (ret > 0) goto read_it; } #endif return ret; } bool nntp_at_list_end(s) char* s; { if (!s || (*s == '.' && (s[1] == '\0' || s[1] == '\r'))) { nntplink.flags |= NNTP_NEW_CMD_OK; return 1; } nntplink.flags &= ~NNTP_NEW_CMD_OK; return 0; } /* This returns 1 when it reads a full line, 0 if it reads a partial * line, and -2 on EOF/error. The maximum length includes a spot for * the null-terminator, and we need room for our "\r\n"-stripping code * to work right, so "len" MUST be at least 3. */ int nntp_gets(bp, len) char* bp; int len; { int ch, n = 0; char* cp = bp; #ifdef HAS_SIGHOLD sighold(SIGINT); #endif if (nntplink.trailing_CR) { *cp++ = '\r'; len--; nntplink.trailing_CR = 0; } while (1) { if (len == 1) { if (cp[-1] == '\r') { /* Hold a trailing CR until next time because we may need * to strip it if it is followed by a newline. */ cp--; nntplink.trailing_CR = 1; } break; } do { errno = 0; ch = fgetc(nntplink.rd_fp); } while (errno == EINTR); if (ch == EOF) { nntplink.flags |= NNTP_NEW_CMD_OK; n = -2; break; } if (ch == '\n') { if (cp != bp && cp[-1] == '\r') cp--; n = 1; break; } *cp++ = ch; len--; } *cp = '\0'; #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif return n; } void nntp_close(send_quit) bool_int send_quit; { if (send_quit && nntplink.wr_fp != NULL && nntplink.rd_fp != NULL) { if (nntp_command("QUIT") > 0) nntp_check(); } /* the nntp_check() above might have closed these already. */ if (nntplink.wr_fp != NULL) { fclose(nntplink.wr_fp); nntplink.wr_fp = NULL; } if (nntplink.rd_fp != NULL) { fclose(nntplink.rd_fp); nntplink.rd_fp = NULL; } nntplink.flags |= NNTP_NEW_CMD_OK; } #endif /* SUPPORT_NNTP */ trn-4.0-test77/nntpclient.h0000644000000000000000000000407211437640112014342 0ustar rootroot/* nntpclient.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifdef SUPPORT_NNTP struct nntplink { FILE* rd_fp; FILE* wr_fp; time_t last_command; int port_number; int flags; #ifdef USE_GENAUTH int cookiefd; #endif bool trailing_CR; }; #define NNTP_NEW_CMD_OK 0x0001 #define NNTP_FORCE_AUTH_NEEDED 0x0002 #define NNTP_FORCE_AUTH_NOW 0x0004 EXT NNTPLINK nntplink; /* the current server's file handles */ EXT bool nntp_allow_timeout INIT(FALSE); #define nntp_get_a_line(buf,len,realloc) get_a_line(buf,len,realloc,nntplink.rd_fp) /* RFC 977 defines these, so don't change them */ #define NNTP_CLASS_INF '1' #define NNTP_CLASS_OK '2' #define NNTP_CLASS_CONT '3' #define NNTP_CLASS_ERR '4' #define NNTP_CLASS_FATAL '5' #define NNTP_POSTOK_VAL 200 /* Hello -- you can post */ #define NNTP_NOPOSTOK_VAL 201 /* Hello -- you can't post */ #define NNTP_LIST_FOLLOWS_VAL 215 /* There's a list a-comin' next */ #define NNTP_GOODBYE_VAL 400 /* Have to hang up for some reason */ #define NNTP_NOSUCHGROUP_VAL 411 /* No such newsgroup */ #define NNTP_NONEXT_VAL 421 /* No next article */ #define NNTP_NOPREV_VAL 422 /* No previous article */ #define NNTP_POSTFAIL_VAL 441 /* Posting failed */ #define NNTP_AUTH_NEEDED_VAL 480 /* Authorization Failed */ #define NNTP_AUTH_REJECT_VAL 482 /* Authorization data rejected */ #define NNTP_BAD_COMMAND_VAL 500 /* Command not recognized */ #define NNTP_SYNTAX_VAL 501 /* Command syntax error */ #define NNTP_ACCESS_VAL 502 /* Access to server denied */ #define NNTP_TMPERR_VAL 503 /* Program fault, command not performed */ #define NNTP_AUTH_BAD_VAL 580 /* Authorization Failed */ #define NNTP_STRLEN 512 EXT char ser_line[NNTP_STRLEN]; EXT char last_command[NNTP_STRLEN]; #endif /* SUPPORT_NNTP */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int nntp_connect _((char*,bool_int)); char* nntp_servername _((char*)); int nntp_command _((char*)); int nntp_check _((void)); bool nntp_at_list_end _((char*)); int nntp_gets _((char*,int)); void nntp_close _((bool_int)); trn-4.0-test77/nntpinit.c0000644000000000000000000002560011437640112014022 0ustar rootroot/* nntpinit.c */ /* This software is copyrighted as detailed in the LICENSE file. */ /*#define DECNET *//* If you want decnet support */ /*#define EXCELAN *//* Excelan EXOS 205 support */ /*#define NONETD *//* Define if you're missing netdb.h */ #include "EXTERN.h" #include "common.h" #include "nntpclient.h" #include "INTERN.h" #include "nntpinit.h" #include "nntpinit.ih" #ifdef SUPPORT_NNTP #ifdef WINSOCK #include WSADATA wsaData; #else #include #include #ifdef NONETDB # define IPPORT_NNTP ((unsigned short) 119) #else # include #endif #endif #ifdef EXCELAN int connect _((int, struct sockaddr*)); unsigned short htons _((unsigned short)); unsigned long rhost _((char**)); int rresvport p((int)); int socket _((int, struct sockproto *, struct sockaddr_in *, int)); #endif /* EXCELAN */ #ifdef DECNET #include #include #endif /* DECNET */ #ifndef WINSOCK unsigned long inet_addr _((char*)); #ifndef NONETDB struct servent* getservbyname(); struct hostent* gethostbyname(); #endif #endif int init_nntp() { #ifdef WINSOCK if (WSAStartup(0x0101,&wsaData) == 0) { if (wsaData.wVersion == 0x0101) return 1; WSACleanup(); } fprintf(stderr,"Unable to initialize WinSock DLL.\n"); return -1; #else return 1; #endif } int server_init(machine) char* machine; { int sockt_rd, sockt_wr; #ifdef DECNET char* cp; #endif #ifdef DECNET cp = index(machine, ':'); if (cp && cp[1] == ':') { *cp = '\0'; sockt_rd = get_dnet_socket(machine); } else sockt_rd = get_tcp_socket(machine, nntplink.port_number, "nntp"); #else /* !DECNET */ sockt_rd = get_tcp_socket(machine, nntplink.port_number, "nntp"); #endif if (sockt_rd < 0) return -1; sockt_wr = dup(sockt_rd); /* Now we'll make file pointers (i.e., buffered I/O) out of ** the socket file descriptor. Note that we can't just ** open a fp for reading and writing -- we have to open ** up two separate fp's, one for reading, one for writing. */ if ((nntplink.rd_fp = fdopen(sockt_rd, "r")) == NULL) { perror("server_init: fdopen #1"); return -1; } if ((nntplink.wr_fp = fdopen(sockt_wr, "w")) == NULL) { perror("server_init: fdopen #2"); nntplink.rd_fp = NULL; return -1; } /* Now get the server's signon message */ nntp_check(); if (*ser_line == NNTP_CLASS_OK) { char save_line[NNTP_STRLEN]; strcpy(save_line, ser_line); /* Try MODE READER just in case we're talking to innd. ** If it is not an invalid command, use the new reply. */ if (nntp_command("MODE READER") <= 0) sprintf(ser_line, "%d failed to send MODE READER\n", NNTP_ACCESS_VAL); else if (nntp_check() <= 0 && atoi(ser_line) == NNTP_BAD_COMMAND_VAL) strcpy(ser_line, save_line); } return atoi(ser_line); } void cleanup_nntp() { #ifdef WINSOCK WSACleanup(); #endif } int get_tcp_socket(machine, port, service) char* machine; int port; char* service; { int s; #if INET6 struct addrinfo hints; struct addrinfo* res; struct addrinfo* res0; char portstr[8]; char* cause = NULL; int error; memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; if (port) sprintf(service = portstr, "%d", port); error = getaddrinfo(machine, service, &hints, &res0); if (error) { fprintf(stderr, "%s", gai_strerror(error)); return -1; } for (res = res0; res; res = res->ai_next) { char buf[64] = ""; s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) { cause = "socket"; continue; } inet_ntop(res->ai_family, res->ai_addr, buf, sizeof buf); if (res != res0) fprintf(stderr, "trying %s...", buf); if (connect(s, res->ai_addr, res->ai_addrlen) >= 0) break; /* okay we got one */ fprintf(stderr, "connection to %s: ", buf); perror(""); cause = "connect"; close(s); s = -1; } if (s < 0) { fprintf(stderr, "giving up... "); perror(cause); } freeaddrinfo(res0); #else /* !INET6 */ struct sockaddr_in sin; #ifdef __hpux int socksize = 0; int socksizelen = sizeof socksize; #endif #ifdef NONETDB bzero((char*)&sin, sizeof sin); sin.sin_family = AF_INET; #else struct hostent* hp; #ifdef h_addr int x = 0; register char** cp; static char* alist[1]; #endif /* h_addr */ static struct hostent def; static struct in_addr defaddr; static char namebuf[256]; bzero((char*)&sin, sizeof sin); if (port) sin.sin_port = htons(port); else { struct servent* sp; if ((sp = getservbyname(service, "tcp")) == NULL) { fprintf(stderr, "%s/tcp: Unknown service.\n", service); return -1; } sin.sin_port = sp->s_port; } /* If not a raw ip address, try nameserver */ if (!isdigit(*machine) #ifdef INADDR_NONE || (defaddr.s_addr = inet_addr(machine)) == INADDR_NONE) #else || (long)(defaddr.s_addr = inet_addr(machine)) == -1) #endif hp = gethostbyname(machine); else { /* Raw ip address, fake */ (void) strcpy(namebuf, machine); def.h_name = namebuf; #ifdef h_addr def.h_addr_list = alist; #endif def.h_addr = (char*)&defaddr; def.h_length = sizeof (struct in_addr); def.h_addrtype = AF_INET; def.h_aliases = 0; hp = &def; } if (hp == NULL) { fprintf(stderr, "%s: Unknown host.\n", machine); return -1; } sin.sin_family = hp->h_addrtype; #endif /* !NONETDB */ /* The following is kinda gross. The name server under 4.3 ** returns a list of addresses, each of which should be tried ** in turn if the previous one fails. However, 4.2 hostent ** structure doesn't have this list of addresses. ** Under 4.3, h_addr is a #define to h_addr_list[0]. ** We use this to figure out whether to include the NS specific ** code... */ #ifdef h_addr /* get a socket and initiate connection -- use multiple addresses */ for (cp = hp->h_addr_list; cp && *cp; cp++) { extern char* inet_ntoa _((const struct in_addr)); s = socket(hp->h_addrtype, SOCK_STREAM, 0); if (s < 0) { perror("socket"); return -1; } bcopy(*cp, (char*)&sin.sin_addr, hp->h_length); if (x < 0) fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr)); x = connect(s, (struct sockaddr*)&sin, sizeof (sin)); if (x == 0) break; fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr)); perror(""); (void) close(s); } if (x < 0) { fprintf(stderr, "giving up...\n"); return -1; } #else /* no name server */ #ifdef EXCELAN s = socket(SOCK_STREAM, (struct sockproto*)NULL, &sin, SO_KEEPALIVE); if (s < 0) { /* Get the socket */ perror("socket"); return -1; } bzero((char*)&sin, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(port? port : *service == 'n'? IPPORT_NNTP : 80); /* set up addr for the connect */ if ((sin.sin_addr.s_addr = rhost(&machine)) == -1) { fprintf(stderr, "%s: Unknown host.\n", machine); return -1; } /* And then connect */ if (connect(s, (struct sockaddr*)&sin) < 0) { perror("connect"); (void) close(s); return -1; } #else /* !EXCELAN */ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } /* And then connect */ bcopy(hp->h_addr, (char*)&sin.sin_addr, hp->h_length); if (connect(s, (struct sockaddr*)&sin, sizeof sin) < 0) { perror("connect"); (void) close(s); return -1; } #endif /* !EXCELAN */ #endif /* !h_addr */ #endif /* !INET6 */ #ifdef __hpux /* recommended by raj@cup.hp.com */ #define HPSOCKSIZE 0x8000 getsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, (caddr_t)&socksizelen); if (socksize < HPSOCKSIZE) { socksize = HPSOCKSIZE; setsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, sizeof (socksize)); } socksize = 0; socksizelen = sizeof (socksize); getsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, (caddr_t)&socksizelen); if (socksize < HPSOCKSIZE) { socksize = HPSOCKSIZE; setsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, sizeof (socksize)); } #endif return s; } #ifdef DECNET static int get_dnet_socket(machine) char* machine; { int s, area, node; struct sockaddr_dn sdn; struct nodeent *getnodebyname(), *np; bzero((char*)&sdn, sizeof sdn); switch (s = sscanf(machine, "%d%*[.]%d", &area, &node)) { case 1: node = area; area = 0; case 2: node += area*1024; sdn.sdn_add.a_len = 2; sdn.sdn_family = AF_DECnet; sdn.sdn_add.a_addr[0] = node % 256; sdn.sdn_add.a_addr[1] = node / 256; break; default: if ((np = getnodebyname(machine)) == NULL) { fprintf(stderr, "%s: Unknown host.\n", machine); return -1; } bcopy(np->n_addr, (char*)sdn.sdn_add.a_addr, np->n_length); sdn.sdn_add.a_len = np->n_length; sdn.sdn_family = np->n_addrtype; break; } sdn.sdn_objnum = 0; sdn.sdn_flags = 0; sdn.sdn_objnamel = strlen("NNTP"); bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel); if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) { nerror("socket"); return -1; } /* And then connect */ if (connect(s, (struct sockaddr*)&sdn, sizeof sdn) < 0) { nerror("connect"); close(s); return -1; } return s; } #endif /* DECNET */ /* * inet_addr for EXCELAN (which does not have it!) */ #ifdef NONETDB unsigned long inet_addr(cp) register char* cp; { unsigned long val, base, n; register char c; unsigned long octet[4], *octetptr = octet; #ifndef htonl extern unsigned long htonl(); #endif /* htonl */ again: /* Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, other=decimal. */ val = 0; base = 10; if (*cp == '0') base = 8, cp++; if (*cp == 'x' || *cp == 'X') base = 16, cp++; while (c = *cp) { if (isdigit(c)) { val = (val * base) + (c - '0'); cp++; continue; } if (base == 16 && isxdigit(c)) { val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A')); cp++; continue; } break; } if (*cp == '.') { /* Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ if (octetptr >= octet + 4) return -1; *octetptr++ = val, cp++; goto again; } /* Check for trailing characters. */ if (*cp && !isspace(*cp)) return -1; *octetptr++ = val; /* Concoct the address according to the number of octet specified. */ n = octetptr - octet; switch (n) { case 1: /* a -- 32 bits */ val = octet[0]; break; case 2: /* a.b -- 8.24 bits */ val = (octet[0] << 24) | (octet[1] & 0xffffff); break; case 3: /* a.b.c -- 8.8.16 bits */ val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) | (octet[2] & 0xffff); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) | ((octet[2] & 0xff) << 8) | (octet[3] & 0xff); break; default: return -1; } val = htonl(val); return val; } #endif /* NONETDB */ #endif /* SUPPORT_NNTP */ trn-4.0-test77/nntpinit.h0000644000000000000000000000041011437640112014017 0ustar rootroot/* nntpinit.h */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int init_nntp _((void)); int server_init _((char*)); void cleanup_nntp _((void)); int get_tcp_socket _((char*,int,char*)); #ifdef NONETDB unsigned long inet_addr _((char*)); #endif trn-4.0-test77/nntpinit.ih0000644000000000000000000000032411437640112014174 0ustar rootroot/* nntpinit.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef DECNET static int get_dnet_socket _((char*)); #endif trn-4.0-test77/nntplist.c0000644000000000000000000000765107113133016014034 0ustar rootroot/* nntplist.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "env.h" #include "util2.h" #include "util3.h" #include "wildmat.h" #include "nntpclient.h" #include "nntpinit.h" void Usage _((void)); char* server_name; char* nntp_auth_file; int debug = 0; /* make nntpclient.c happy */ char* homedir; char* dotdir; #ifdef USE_GENAUTH char* loginName; #endif int main(argc, argv) int argc; char* argv[]; { char command[32]; char* action = NULL; char* wildarg = NULL; char* cp; FILE* in_fp; register FILE* out_fp = NULL; while (--argc) { if (**++argv == '-') { switch (*++*argv) { case 'o': if (out_fp || !--argc) Usage(); out_fp = fopen(*++argv, "w"); if (out_fp == NULL) { perror(*argv); exit(1); } break; case 'x': if (wildarg || !--argc) Usage(); wildarg = *++argv; break; default: Usage(); /* NO RETURN */ } } else if (!action) action = *argv; else Usage(); } if (action && strcaseEQ(action,"active")) action = NULL; if (!out_fp) out_fp = stdout; #ifdef USE_GENAUTH /* get login name */ loginName = getenv("USER"); if (loginName == NULL) { loginName = getenv("LOGNAME"); #ifdef GETLOGIN if (loginName == NULL) loginName = getlogin(); #endif } #endif homedir = getenv("HOME"); if (homedir == NULL) homedir = getenv("LOGDIR"); dotdir = getenv("DOTDIR"); if (!dotdir) dotdir = homedir; cp = getenv("NNTPSERVER"); if (!cp) { cp = filexp(SERVER_NAME); if (*cp == '/') cp = nntp_servername(cp); } if (strNE(cp,"local")) { server_name = savestr(cp); cp = index(server_name, ';'); #ifndef DECNET if (!cp) cp = index(server_name, ':'); #endif if (cp) { *cp = '\0'; nntplink.port_number = atoi(cp+1); } nntp_auth_file = filexp(NNTP_AUTH_FILE); if ((cp = getenv("NNTP_FORCE_AUTH")) != NULL && (*cp == 'y' || *cp == 'Y')) nntplink.flags |= NNTP_FORCE_AUTH_NEEDED; } if (server_name) { if (init_nntp() < 0 || nntp_connect(server_name,0) <= 0) exit(1); if (action) sprintf(command,"LIST %s",action); else strcpy(command,"LIST"); if (wildarg) sprintf(command+strlen(command)," %s",wildarg); if (nntp_command(command) <= 0) exit(1); #ifdef HAS_SIGHOLD sighold(SIGINT); #endif if (nntp_check() <= 0) { fprintf(stderr,"nntplist: Can't get %s file from server.\n",action? action : "active"); fprintf(stderr, "Server said: %s\n", ser_line); finalize(1); } while (nntp_gets(ser_line, sizeof ser_line) == 1) { if (nntp_at_list_end(ser_line)) break; fputs(ser_line, out_fp); putc('\n', out_fp); } #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif nntp_close(TRUE); cleanup_nntp(); } else { cp = NULL; if (!action) cp = ACTIVE; else if (strcaseEQ(action,"active.times")) cp = ACTIVE_TIMES; else if (strcaseEQ(action,"newsgroups")) cp = GROUPDESC; else if (strcaseEQ(action,"subscriptions")) cp = SUBSCRIPTIONS; else if (strcaseEQ(action,"overview.fmt")) cp = OVERVIEW_FMT; if (!cp || !*cp) { fprintf(stderr, "Don't know how to list `%s' from your local system.\n", action); exit(1); } if ((in_fp = fopen(filexp(cp), "r")) == NULL) { fprintf(stderr,"Unable to open `%s'.\n", cp); exit(1); } while (fgets(ser_line, sizeof ser_line, in_fp)) { if (wildarg) { for (cp = ser_line; *cp && !isspace(*cp); cp++) ; if (!cp) continue; *cp = '\0'; if (!wildmat(ser_line, wildarg)) continue; *cp = ' '; } fputs(ser_line, out_fp); } } return 0; } void Usage() { fprintf(stderr, "Usage: nntplist [-x WildSpec] [-o OutputFile] [type]\n\ \nWhere type is any of the LIST command arguments your server accepts.\n"); exit(1); } #ifdef SUPPORT_NNTP int nntp_handle_timeout() { fputs("\n503 Server timed out.\n",stderr); return -2; } #endif trn-4.0-test77/norm_saver.SH0000644000000000000000000000134307141205160014415 0ustar rootrootcase $CONFIG in '') . ./config.sh ;; esac echo "Extracting norm.saver (with variable substitutions)" $spitshell >norm.saver <> \$7 !GROK!THIS! $eunicefix norm.saver chmod 755 norm.saver trn-4.0-test77/only.c0000644000000000000000000000355207113133016013136 0ustar rootroot/* only.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "util.h" #include "util2.h" #include "final.h" #include "list.h" #include "term.h" #include "ngdata.h" #include "ngsrch.h" #include "INTERN.h" #include "only.h" static int save_maxngtodo = 0; void only_init() { ; } void setngtodo(pat) char* pat; { char* s; register int i = maxngtodo + save_maxngtodo; if (!*pat) return; if (i < MAXNGTODO) { ngtodo[i] = savestr(pat); #ifndef lint compextodo[i] = (COMPEX*)safemalloc(sizeof(COMPEX)); #endif init_compex(compextodo[i]); compile(compextodo[i],pat,TRUE,TRUE); if ((s = ng_comp(compextodo[i],pat,TRUE,TRUE)) != NULL) { printf("\n%s\n",s) FLUSH; finalize(1); } maxngtodo++; } } /* if command line list is non-null, is this newsgroup wanted? */ bool inlist(ngnam) char* ngnam; { register int i; if (maxngtodo == 0) return TRUE; for (i = save_maxngtodo; i < maxngtodo + save_maxngtodo; i++) { if (execute(compextodo[i],ngnam)) return TRUE; } return FALSE; } void end_only() { if (maxngtodo) { /* did they specify newsgroup(s) */ int i; #ifdef VERBOSE IF(verbose) sprintf(msg, "Restriction %s%s removed.",ngtodo[0], maxngtodo > 1 ? ", etc." : nullstr); ELSE #endif #ifdef TERSE sprintf(msg, "Exiting \"only\"."); #endif for (i = save_maxngtodo; i < maxngtodo + save_maxngtodo; i++) { free(ngtodo[i]); free_compex(compextodo[i]); #ifndef lint free((char*)compextodo[i]); #endif } maxngtodo = 0; ng_min_toread = 1; } } void push_only() { save_maxngtodo = maxngtodo; maxngtodo = 0; } void pop_only() { ART_UNREAD save_ng_min_toread = ng_min_toread; end_only(); maxngtodo = save_maxngtodo; save_maxngtodo = 0; ng_min_toread = save_ng_min_toread; } trn-4.0-test77/only.h0000644000000000000000000000113707113133016013140 0ustar rootroot/* only.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifndef NBRA #include "search.h" #endif EXT char* ngtodo[MAXNGTODO]; /* restrictions in effect */ EXT COMPEX* compextodo[MAXNGTODO]; /* restrictions in compiled form */ EXT int maxngtodo INIT(0); /* 0 => no restrictions */ /* >0 => # of entries in ngtodo */ EXT char empty_only_char INIT('o'); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void only_init _((void)); void setngtodo _((char*)); bool inlist _((char*)); void end_only _((void)); void push_only _((void)); void pop_only _((void)); trn-4.0-test77/opt.c0000644000000000000000000007337111437640112012771 0ustar rootroot/* opt.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "hash.h" #include "init.h" #include "env.h" #include "util.h" #include "util2.h" #include "intrp.h" #include "final.h" #include "list.h" #include "art.h" #include "cache.h" #include "head.h" #include "ng.h" #include "mime.h" #include "only.h" #include "ngdata.h" #include "search.h" #include "rt-select.h" #include "univ.h" #include "rt-page.h" #include "charsubst.h" #include "term.h" #include "sw.h" #ifdef SCAN #include "scan.h" #ifdef SCAN_ART #include "scanart.h" #endif #endif #ifdef SCORE #include "score.h" #include "scorefile.h" #endif #include "color.h" #include "INTERN.h" #include "opt.h" #include "opt.ih" COMPEX optcompex; void opt_init(argc,argv,tcbufptr) int argc; char* argv[]; char** tcbufptr; { register int i; char* s; sel_grp_dmode = savestr(sel_grp_dmode) + 1; sel_art_dmode = savestr(sel_art_dmode) + 1; UnivSelBtnCnt = parse_mouse_buttons(&UnivSelBtns, "[Top]^ [PgUp]< [PgDn]> [ OK ]^j [Quit]q [Help]?"); NewsrcSelBtnCnt = parse_mouse_buttons(&NewsrcSelBtns, "[Top]^ [PgUp]< [PgDn]> [ OK ]^j [Quit]q [Help]?"); AddSelBtnCnt = parse_mouse_buttons(&AddSelBtns, "[Top]^ [Bot]$ [PgUp]< [PgDn]> [ OK ]Z [Quit]q [Help]?"); OptionSelBtnCnt = parse_mouse_buttons(&OptionSelBtns, "[Find]/ [FindNext]/^j [Top]^ [Bot]$ [PgUp]< [PgDn]> [Use]^i [Save]S [Abandon]q [Help]?"); NewsgroupSelBtnCnt = parse_mouse_buttons(&NewsgroupSelBtns, "[Top]^ [PgUp]< [PgDn]> [ OK ]Z [Quit]q [Help]?"); NewsSelBtnCnt = parse_mouse_buttons(&NewsSelBtns, "[Top]^ [Bot]$ [PgUp]< [PgDn]> [KillPg]D [ OK ]Z [Quit]q [Help]?"); ArtPagerBtnCnt = parse_mouse_buttons(&ArtPagerBtns, "[Next]n [Sel]+ [Quit]q [Help]h"); prep_ini_words(options_ini); if (argc >= 2 && strEQ(argv[1],"-c")) checkflag=TRUE; /* so we can optimize for -c */ interp(*tcbufptr,TCBUF_SIZE,GLOBINIT); opt_file(*tcbufptr,tcbufptr,FALSE); option_def_vals = (char**)safemalloc(INI_LEN(options_ini)*sizeof(char*)); bzero((char*)option_def_vals,INI_LEN(options_ini) * sizeof (char*)); /* Set DEFHIDE and DEFMAGIC to current values and clear user_htype list */ set_header_list(HT_DEFHIDE,HT_HIDE,nullstr); set_header_list(HT_DEFMAGIC,HT_MAGIC,nullstr); if (use_threads) s = getval("TRNRC","%+/trnrc"); else s = getval("RNRC","%+/rnrc"); ini_file = savestr(filexp(s)); s = filexp("%+"); if (stat(s,&filestat) < 0 || !S_ISDIR(filestat.st_mode)) { printf("Creating the directory %s.\n",s); if (makedir(s,MD_DIR) != 0) { printf("Unable to create `%s'.\n",s); finalize(1); /*$$??*/ } } if (stat(ini_file,&filestat) == 0) opt_file(ini_file,tcbufptr,TRUE); if (!use_threads || (s = getenv("TRNINIT")) == NULL) s = getenv("RNINIT"); if (*safecpy(*tcbufptr,s,TCBUF_SIZE)) { if (*s == '-' || *s == '+' || isspace(*s)) sw_list(*tcbufptr); else sw_file(tcbufptr); } option_saved_vals = (char**)safemalloc(INI_LEN(options_ini)*sizeof(char*)); bzero((char*)option_saved_vals,INI_LEN(options_ini) * sizeof (char*)); option_flags = (char*)safemalloc(INI_LEN(options_ini)*sizeof(char)); bzero(option_flags,INI_LEN(options_ini) * sizeof (char)); if (argc > 1) { for (i = 1; i < argc; i++) decode_switch(argv[i]); } init_compex(&optcompex); } void opt_file(filename,tcbufptr,bleat) char* filename; char** tcbufptr; bool_int bleat; { char* filebuf = *tcbufptr; char* s; char* section; char* cond; int fd = open(filename,0); if (fd >= 0) { fstat(fd,&filestat); if (filestat.st_size >= TCBUF_SIZE-1) { filebuf = saferealloc(filebuf,(MEM_SIZE)filestat.st_size+2); *tcbufptr = filebuf; } if (filestat.st_size) { int len = read(fd,filebuf,(int)filestat.st_size); filebuf[len] = '\0'; prep_ini_data(filebuf,filename); s = filebuf; while ((s = next_ini_section(s,§ion,&cond)) != NULL) { if (*cond && !check_ini_cond(cond)) continue; if (strEQ(section, "options")) { s = parse_ini_section(s, options_ini); if (!s) break; set_options(INI_VALUES(options_ini)); } else if (strEQ(section, "environment")) { while (*s && *s != '[') { section = s; s += strlen(s) + 1; export(section,s); s += strlen(s) + 1; } } else if (strEQ(section, "termcap")) { while (*s && *s != '[') { section = s; s += strlen(s) + 1; add_tc_string(section, s); s += strlen(s) + 1; } } else if (strEQ(section, "attribute")) { while (*s && *s != '[') { section = s; s += strlen(s) + 1; color_rc_attribute(section, s); s += strlen(s) + 1; } } } } close(fd); } else if (bleat) { printf(cantopen,filename) FLUSH; /*termdown(1);*/ } *filebuf = '\0'; } #define YES(s) (*(s) == 'y' || *(s) == 'Y') #define NO(s) (*(s) == 'n' || *(s) == 'N') void set_options(vals) char** vals; { int limit = INI_LEN(options_ini); int i; for (i = 1; i < limit; i++) { if (*++vals) set_option(i, *vals); } } void set_option(num, s) int num; char* s; { if (option_saved_vals) { if (!option_saved_vals[num]) { option_saved_vals[num] = savestr(option_value(num)); if (!option_def_vals[num]) option_def_vals[num] = option_saved_vals[num]; } } else if (option_def_vals) { if (!option_def_vals[num]) option_def_vals[num] = savestr(option_value(num)); } switch (num) { case OI_USE_THREADS: use_threads = YES(s); break; case OI_USE_MOUSE: UseMouse = YES(s); if (UseMouse) { /* set up the Xterm mouse sequence */ set_macro("\033[M+3","\003"); } break; case OI_MOUSE_MODES: safecpy(MouseModes, s, sizeof MouseModes); break; case OI_USE_UNIV_SEL: UseUnivSelector = YES(s); break; case OI_UNIV_SEL_CMDS: *UnivSelCmds = *s; if (s[1]) UnivSelCmds[1] = s[1]; break; case OI_UNIV_SEL_BTNS: UnivSelBtnCnt = parse_mouse_buttons(&UnivSelBtns,s); break; case OI_UNIV_SEL_ORDER: set_sel_order(SM_UNIVERSAL,s); break; case OI_UNIV_FOLLOW: univ_follow = YES(s); break; case OI_USE_NEWSRC_SEL: UseNewsrcSelector = YES(s); break; case OI_NEWSRC_SEL_CMDS: *NewsrcSelCmds = *s; if (s[1]) NewsrcSelCmds[1] = s[1]; break; case OI_NEWSRC_SEL_BTNS: NewsrcSelBtnCnt = parse_mouse_buttons(&NewsrcSelBtns,s); break; case OI_USE_ADD_SEL: UseAddSelector = YES(s); break; case OI_ADD_SEL_CMDS: *AddSelCmds = *s; if (s[1]) AddSelCmds[1] = s[1]; break; case OI_ADD_SEL_BTNS: AddSelBtnCnt = parse_mouse_buttons(&AddSelBtns,s); break; case OI_USE_NEWSGROUP_SEL: UseNewsgroupSelector = YES(s); break; case OI_NEWSGROUP_SEL_ORDER: set_sel_order(SM_NEWSGROUP,s); break; case OI_NEWSGROUP_SEL_CMDS: *NewsgroupSelCmds = *s; if (s[1]) NewsgroupSelCmds[1] = s[1]; break; case OI_NEWSGROUP_SEL_BTNS: NewsgroupSelBtnCnt = parse_mouse_buttons(&NewsgroupSelBtns,s); break; case OI_NEWSGROUP_SEL_STYLES: free(option_value(OI_NEWSGROUP_SEL_STYLES)-1); sel_grp_dmode = safemalloc(strlen(s)+2); *sel_grp_dmode++ = '*'; strcpy(sel_grp_dmode, s); break; case OI_USE_NEWS_SEL: if (isdigit(*s)) UseNewsSelector = atoi(s); else UseNewsSelector = YES(s)-1; break; case OI_NEWS_SEL_MODE: { int save_sel_mode = sel_mode; set_sel_mode(*s); if (save_sel_mode != SM_ARTICLE && save_sel_mode != SM_SUBJECT && save_sel_mode != SM_THREAD) { sel_mode = save_sel_mode; set_selector(0,0); } break; } case OI_NEWS_SEL_ORDER: set_sel_order(sel_defaultmode,s); break; case OI_NEWS_SEL_CMDS: *NewsSelCmds = *s; if (s[1]) NewsSelCmds[1] = s[1]; break; case OI_NEWS_SEL_BTNS: NewsSelBtnCnt = parse_mouse_buttons(&NewsSelBtns,s); break; case OI_NEWS_SEL_STYLES: free(option_value(OI_NEWS_SEL_STYLES)-1); sel_art_dmode = safemalloc(strlen(s)+2); *sel_art_dmode++ = '*'; strcpy(sel_art_dmode, s); break; case OI_OPTION_SEL_CMDS: *OptionSelCmds = *s; if (s[1]) OptionSelCmds[1] = s[1]; break; case OI_OPTION_SEL_BTNS: OptionSelBtnCnt = parse_mouse_buttons(&OptionSelBtns,s); break; case OI_AUTO_SAVE_NAME: if (!checkflag) { if (YES(s)) { export("SAVEDIR", "%p/%c"); export("SAVENAME", "%a"); } else if (strEQ(getval("SAVEDIR",nullstr),"%p/%c") && strEQ(getval("SAVENAME",nullstr),"%a")) { export("SAVEDIR", "%p"); export("SAVENAME", "%^C"); } } break; case OI_BKGND_THREADING: thread_always = !YES(s); break; case OI_AUTO_ARROW_MACROS: { int prev = auto_arrow_macros; if (YES(s) || *s == 'r' || *s == 'R') auto_arrow_macros = 2; else auto_arrow_macros = !NO(s); if (mode != 'i' && auto_arrow_macros != prev) { char tmpbuf[1024]; arrow_macros(tmpbuf); } break; } case OI_READ_BREADTH_FIRST: breadth_first = YES(s); break; case OI_BKGND_SPINNER: bkgnd_spinner = YES(s); break; case OI_CHECKPOINT_NEWSRC_FREQUENCY: docheckwhen = atoi(s); break; case OI_SAVE_DIR: if (!checkflag) { savedir = savestr(s); if (cwd) { chdir(cwd); free(cwd); } cwd = savestr(filexp(s)); } break; case OI_ERASE_SCREEN: erase_screen = YES(s); break; case OI_NOVICE_DELAYS: novice_delays = YES(s); break; case OI_CITED_TEXT_STRING: indstr = savestr(s); break; case OI_GOTO_LINE_NUM: gline = atoi(s)-1; break; case OI_FUZZY_NEWSGROUP_NAMES: fuzzyGet = YES(s); break; case OI_HEADER_MAGIC: if (!checkflag) set_header_list(HT_MAGIC, HT_DEFMAGIC, s); break; case OI_HEADER_HIDING: set_header_list(HT_HIDE, HT_DEFHIDE, s); break; case OI_INITIAL_ARTICLE_LINES: initlines = atoi(s); break; case OI_APPEND_UNSUBSCRIBED_GROUPS: append_unsub = YES(s); break; case OI_FILTER_CONTROL_CHARACTERS: dont_filter_control = !YES(s); break; case OI_JOIN_SUBJECT_LINES: if (isdigit(*s)) change_join_subject_len(atoi(s)); else change_join_subject_len(YES(s)? 30 : 0); break; case OI_IGNORE_THRU_ON_SELECT: kill_thru_kludge = YES(s); break; case OI_AUTO_GROW_GROUPS: keep_the_group_static = !YES(s); break; case OI_MUCK_UP_CLEAR: muck_up_clear = YES(s); break; case OI_ERASE_EACH_LINE: erase_each_line = YES(s); break; case OI_SAVEFILE_TYPE: mbox_always = (*s == 'm' || *s == 'M'); norm_always = (*s == 'n' || *s == 'N'); break; case OI_PAGER_LINE_MARKING: if (isdigit(*s)) marking_areas = atoi(s); else marking_areas = HALFPAGE_MARKING; if (NO(s)) marking = NOMARKING; else if (*s == 'u') marking = UNDERLINE; else marking = STANDOUT; break; case OI_OLD_MTHREADS_DATABASE: if (isdigit(*s)) olden_days = atoi(s); else olden_days = YES(s); break; case OI_SELECT_MY_POSTS: if (NO(s)) auto_select_postings = 0; else { switch (*s) { case 't': auto_select_postings = '+'; break; case 'p': auto_select_postings = 'p'; break; default: auto_select_postings = '.'; break; } } break; case OI_MULTIPART_SEPARATOR: multipart_separator = savestr(s); break; case OI_AUTO_VIEW_INLINE: auto_view_inline = YES(s); break; case OI_NEWGROUP_CHECK: quickstart = !YES(s); break; case OI_RESTRICTION_INCLUDES_EMPTIES: empty_only_char = YES(s)? 'o' : 'O'; break; case OI_CHARSET: #ifdef CHARSUBST charsets = savestr(s); #endif break; case OI_INITIAL_GROUP_LIST: if (isdigit(*s)) { countdown = atoi(s); suppress_cn = (countdown == 0); } else { suppress_cn = NO(s); if (!suppress_cn) countdown = 5; } break; case OI_RESTART_AT_LAST_GROUP: findlast = YES(s) * (mode == 'i'? 1 : -1); break; case OI_SCANMODE_COUNT: #ifdef ARTSEARCH if (isdigit(*s)) scanon = atoi(s); else scanon = YES(s)*3; #endif break; case OI_TERSE_OUTPUT: #if defined(VERBOSE) && defined(TERSE) verbose = !YES(s); if (!verbose) novice_delays = FALSE; #endif break; case OI_EAT_TYPEAHEAD: allow_typeahead = !YES(s); break; case OI_COMPRESS_SUBJECTS: unbroken_subjects = !YES(s); break; case OI_VERIFY_INPUT: #ifdef VERIFY verify = YES(s); #endif break; case OI_ARTICLE_TREE_LINES: if (isdigit(*s)) { if ((max_tree_lines = atoi(s)) > 11) max_tree_lines = 11; } else max_tree_lines = YES(s) * 6; break; case OI_WORD_WRAP_MARGIN: if (isdigit(*s)) word_wrap_offset = atoi(s); else if (YES(s)) word_wrap_offset = 8; else word_wrap_offset = -1; break; case OI_DEFAULT_REFETCH_TIME: defRefetchSecs = text2secs(s, DEFAULT_REFETCH_SECS); break; case OI_ART_PAGER_BTNS: ArtPagerBtnCnt = parse_mouse_buttons(&ArtPagerBtns,s); break; #ifdef SCAN case OI_SCAN_ITEMNUM: s_itemnum = YES(s); break; case OI_SCAN_VI: s_mode_vi = YES(s); break; #ifdef SCAN_ART case OI_SCANA_FOLLOW: sa_follow = YES(s); break; case OI_SCANA_FOLD: sa_mode_fold = YES(s); break; case OI_SCANA_UNZOOMFOLD: sa_unzoomrefold = YES(s); break; case OI_SCANA_MARKSTAY: sa_mark_stay = YES(s); break; case OI_SCANA_DISPANUM: sa_mode_desc_artnum = YES(s); break; case OI_SCANA_DISPAUTHOR: sa_mode_desc_author = YES(s); break; case OI_SCANA_DISPSCORE: #ifdef SCORE sa_mode_desc_score = YES(s); #endif break; case OI_SCANA_DISPSUBCNT: sa_mode_desc_threadcount = YES(s); break; case OI_SCANA_DISPSUBJ: #if 0 /* CAA: for now, always on. */ sa_mode_desc_subject = YES(s); #endif break; case OI_SCANA_DISPSUMMARY: sa_mode_desc_summary = YES(s); break; case OI_SCANA_DISPKEYW: sa_mode_desc_keyw = YES(s); break; #endif #endif /* SCAN */ #ifdef SCORE case OI_SC_VERBOSE: sf_verbose = YES(s); break; #endif case OI_USE_SEL_NUM: UseSelNum = YES(s); break; case OI_SEL_NUM_GOTO: SelNumGoto = YES(s); break; default: printf("*** Internal error: Unknown Option ***\n"); break; } } void save_options(filename) char* filename; { int i; int fd_in; FILE* fp_out; char* filebuf = NULL; char* line = NULL; static bool first_time = TRUE; sprintf(buf,"%s.new",filename); fp_out = fopen(buf,"w"); if (!fp_out) { printf(cantcreate,buf); return; } if ((fd_in = open(filename,0)) >= 0) { char* cp; char* nlp = NULL; char* comments = NULL; fstat(fd_in,&filestat); if (filestat.st_size) { int len; filebuf = safemalloc((MEM_SIZE)filestat.st_size+2); len = read(fd_in,filebuf,(int)filestat.st_size); filebuf[len] = '\0'; } close(fd_in); for (line = filebuf; line && *line; line = nlp) { cp = line; nlp = index(cp, '\n'); if (nlp) *nlp++ = '\0'; while (isspace(*cp)) cp++; if (*cp == '[' && strnEQ(cp+1,"options]",8)) { for (cp += 9; isspace(*cp); cp++) ; if (!*cp) break; } fputs(line, fp_out); fputc('\n', fp_out); } for (line = nlp; line && *line; line = nlp) { cp = line; nlp = index(cp, '\n'); if (nlp) nlp++; while (*cp != '\n' && isspace(*cp)) cp++; if (*cp == '[') break; if (isalpha(*cp)) comments = NULL; else if (!comments) comments = line; } if (comments) line = comments; } else { char *t = use_threads? "T" : ""; printf("\n\ This is the first save of the option file, %s.\n\ By default this file overrides your %sRNINIT variable, but if you\n\ want to continue to use an old-style init file (that overrides the\n\ settings in the option file), edit the option file and change the\n\ line that sets %sRNINIT.\n", ini_file, t, t); get_anything(); fprintf(fp_out, "# trnrc file auto-generated\n[environment]\n"); write_init_environment(fp_out); fprintf(fp_out, "%sRNINIT = ''\n\n", t); } fprintf(fp_out,"[options]\n"); for (i = 1; options_ini[i].checksum; i++) { if (*options_ini[i].item == '*') fprintf(fp_out,"# ==%s========\n",options_ini[i].item+1); else { fprintf(fp_out,"%s = ",options_ini[i].item); if (!option_def_vals[i]) fputs("#default of ",fp_out); fprintf(fp_out,"%s\n",quote_string(option_value(i))); if (option_saved_vals[i]) { if (option_saved_vals[i] != option_def_vals[i]) free(option_saved_vals[i]); option_saved_vals[i] = NULL; } } } if (line) { /*putc('\n',fp_out);*/ fputs(line,fp_out); } fclose(fp_out); safefree(filebuf); if (first_time) { if (fd_in >= 0) { sprintf(buf,"%s.old",filename); UNLINK(buf); RENAME(filename,buf); } first_time = FALSE; } else UNLINK(filename); sprintf(buf,"%s.new",filename); RENAME(buf,filename); } char* option_value(num) int num; { switch (num) { case OI_USE_THREADS: return YESorNO(use_threads); case OI_USE_MOUSE: return YESorNO(UseMouse); case OI_MOUSE_MODES: return MouseModes; case OI_USE_UNIV_SEL: return YESorNO(UseUnivSelector); case OI_UNIV_SEL_CMDS: return UnivSelCmds; case OI_UNIV_SEL_BTNS: return expand_mouse_buttons(UnivSelBtns,UnivSelBtnCnt); case OI_UNIV_SEL_ORDER: return get_sel_order(SM_UNIVERSAL); case OI_UNIV_FOLLOW: return YESorNO(univ_follow); break; case OI_USE_NEWSRC_SEL: return YESorNO(UseNewsrcSelector); case OI_NEWSRC_SEL_CMDS: return NewsrcSelCmds; case OI_NEWSRC_SEL_BTNS: return expand_mouse_buttons(NewsrcSelBtns,NewsrcSelBtnCnt); case OI_USE_ADD_SEL: return YESorNO(UseAddSelector); case OI_ADD_SEL_CMDS: return AddSelCmds; case OI_ADD_SEL_BTNS: return expand_mouse_buttons(AddSelBtns,AddSelBtnCnt); case OI_USE_NEWSGROUP_SEL: return YESorNO(UseNewsgroupSelector); case OI_NEWSGROUP_SEL_ORDER: return get_sel_order(SM_NEWSGROUP); case OI_NEWSGROUP_SEL_CMDS: return NewsgroupSelCmds; case OI_NEWSGROUP_SEL_BTNS: return expand_mouse_buttons(NewsgroupSelBtns,NewsgroupSelBtnCnt); case OI_NEWSGROUP_SEL_STYLES: { char* s = sel_grp_dmode; while (s[-1] != '*') s--; return s; } case OI_USE_NEWS_SEL: if (UseNewsSelector < 1) return YESorNO(UseNewsSelector+1); sprintf(buf,"%d",UseNewsSelector); return buf; case OI_NEWS_SEL_MODE: { int save_sel_mode = sel_mode; int save_Threaded = ThreadedGroup; char* s; ThreadedGroup = TRUE; set_selector(sel_defaultmode, 0); s = sel_mode_string; sel_mode = save_sel_mode; ThreadedGroup = save_Threaded; set_selector(0, 0); return s; } case OI_NEWS_SEL_ORDER: return get_sel_order(sel_defaultmode); case OI_NEWS_SEL_CMDS: return NewsSelCmds; case OI_NEWS_SEL_BTNS: return expand_mouse_buttons(NewsSelBtns,NewsSelBtnCnt); case OI_NEWS_SEL_STYLES: { char* s = sel_art_dmode; while (s[-1] != '*') s--; return s; } case OI_OPTION_SEL_CMDS: return OptionSelCmds; case OI_OPTION_SEL_BTNS: return expand_mouse_buttons(OptionSelBtns,OptionSelBtnCnt); case OI_AUTO_SAVE_NAME: return YESorNO(strEQ(getval("SAVEDIR",SAVEDIR),"%p/%c")); case OI_BKGND_THREADING: return YESorNO(!thread_always); case OI_AUTO_ARROW_MACROS: switch (auto_arrow_macros) { case 2: return "regular"; case 1: return "alternate"; default: return YESorNO(0); } case OI_READ_BREADTH_FIRST: return YESorNO(breadth_first); case OI_BKGND_SPINNER: return YESorNO(bkgnd_spinner); case OI_CHECKPOINT_NEWSRC_FREQUENCY: sprintf(buf,"%d",docheckwhen); return buf; case OI_SAVE_DIR: return savedir? savedir : "%./News"; case OI_ERASE_SCREEN: return YESorNO(erase_screen); case OI_NOVICE_DELAYS: return YESorNO(novice_delays); case OI_CITED_TEXT_STRING: return indstr; case OI_GOTO_LINE_NUM: sprintf(buf,"%d",gline+1); return buf; case OI_FUZZY_NEWSGROUP_NAMES: return YESorNO(fuzzyGet); case OI_HEADER_MAGIC: return magic_list(); case OI_HEADER_HIDING: return hidden_list(); case OI_INITIAL_ARTICLE_LINES: if (!option_def_vals[OI_INITIAL_ARTICLE_LINES]) return "$LINES"; sprintf(buf,"%d",initlines); return buf; case OI_APPEND_UNSUBSCRIBED_GROUPS: return YESorNO(append_unsub); case OI_FILTER_CONTROL_CHARACTERS: return YESorNO(!dont_filter_control); case OI_JOIN_SUBJECT_LINES: if (join_subject_len) { sprintf(buf,"%d",join_subject_len); return buf; } return YESorNO(0); case OI_IGNORE_THRU_ON_SELECT: return YESorNO(kill_thru_kludge); case OI_AUTO_GROW_GROUPS: return YESorNO(!keep_the_group_static); case OI_MUCK_UP_CLEAR: return YESorNO(muck_up_clear); case OI_ERASE_EACH_LINE: return YESorNO(erase_each_line); case OI_SAVEFILE_TYPE: return mbox_always? "mail" : (norm_always? "norm" : "ask"); case OI_PAGER_LINE_MARKING: if (marking == NOMARKING) return YESorNO(0); if (marking_areas != HALFPAGE_MARKING) sprintf(buf,"%d",marking_areas); else *buf = '\0'; if (marking == UNDERLINE) strcat(buf,"underline"); else strcat(buf,"standout"); return buf; case OI_OLD_MTHREADS_DATABASE: if (olden_days <= 1) return YESorNO(olden_days); sprintf(buf,"%d",olden_days); return buf; case OI_SELECT_MY_POSTS: switch (auto_select_postings) { case '+': return "thread"; case 'p': return "parent"; case '.': return "subthread"; default: break; } return YESorNO(0); case OI_MULTIPART_SEPARATOR: return multipart_separator; case OI_AUTO_VIEW_INLINE: return YESorNO(auto_view_inline); case OI_NEWGROUP_CHECK: return YESorNO(!quickstart); case OI_RESTRICTION_INCLUDES_EMPTIES: return YESorNO(empty_only_char == 'o'); case OI_CHARSET: #ifdef CHARSUBST return charsets; #else return ""; #endif case OI_INITIAL_GROUP_LIST: if (suppress_cn) return YESorNO(0); sprintf(buf,"%d",countdown); return buf; case OI_RESTART_AT_LAST_GROUP: return YESorNO(findlast != 0); #ifdef ARTSEARCH case OI_SCANMODE_COUNT: sprintf(buf,"%d",scanon); return buf; #endif #if defined(VERBOSE) && defined(TERSE) case OI_TERSE_OUTPUT: return YESorNO(!verbose); #endif case OI_EAT_TYPEAHEAD: return YESorNO(!allow_typeahead); case OI_COMPRESS_SUBJECTS: return YESorNO(!unbroken_subjects); #ifdef VERIFY case OI_VERIFY_INPUT: return YESorNO(verify); #endif case OI_ARTICLE_TREE_LINES: sprintf(buf,"%d",max_tree_lines); return buf; case OI_WORD_WRAP_MARGIN: if (word_wrap_offset >= 0) { sprintf(buf,"%d",word_wrap_offset); return buf; } return YESorNO(0); case OI_DEFAULT_REFETCH_TIME: return secs2text(defRefetchSecs); case OI_ART_PAGER_BTNS: return expand_mouse_buttons(ArtPagerBtns,ArtPagerBtnCnt); #ifdef SCAN case OI_SCAN_ITEMNUM: return YESorNO(s_itemnum); case OI_SCAN_VI: return YESorNO(s_mode_vi); #ifdef SCAN_ART case OI_SCANA_FOLLOW: return YESorNO(sa_follow); case OI_SCANA_FOLD: return YESorNO(sa_mode_fold); case OI_SCANA_UNZOOMFOLD: return YESorNO(sa_unzoomrefold); case OI_SCANA_MARKSTAY: return YESorNO(sa_mark_stay); case OI_SCANA_DISPANUM: return YESorNO(sa_mode_desc_artnum); case OI_SCANA_DISPAUTHOR: return YESorNO(sa_mode_desc_author); case OI_SCANA_DISPSCORE: return YESorNO(sa_mode_desc_score); case OI_SCANA_DISPSUBCNT: return YESorNO(sa_mode_desc_threadcount); case OI_SCANA_DISPSUBJ: return YESorNO(sa_mode_desc_subject); case OI_SCANA_DISPSUMMARY: return YESorNO(sa_mode_desc_summary); case OI_SCANA_DISPKEYW: return YESorNO(sa_mode_desc_keyw); #endif #endif #ifdef SCORE case OI_SC_VERBOSE: return YESorNO(sf_verbose); #endif case OI_USE_SEL_NUM: return YESorNO(UseSelNum); break; case OI_SEL_NUM_GOTO: return YESorNO(SelNumGoto); break; default: printf("*** Internal error: Unknown Option ***\n"); break; } return ""; } static char* hidden_list() { int i; buf[0] = '\0'; buf[1] = '\0'; for (i = 1; i < user_htype_cnt; i++) { sprintf(buf+strlen(buf),",%s%s", user_htype[i].flags? nullstr : "!", user_htype[i].name); } return buf+1; } static char* magic_list() { int i; buf[0] = '\0'; buf[1] = '\0'; for (i = HEAD_FIRST; i < HEAD_LAST; i++) { if (!(htype[i].flags & HT_MAGIC) != !(htype[i].flags & HT_DEFMAGIC)) { sprintf(buf+strlen(buf),",%s%s", (htype[i].flags & HT_DEFMAGIC)? "!" : nullstr, htype[i].name); } } return buf+1; } static void set_header_list(flag,defflag,str) int flag; int defflag; char* str; { int i; bool setit; char* cp; if (flag == HT_HIDE || flag == HT_DEFHIDE) { /* Free old user_htype list */ while (user_htype_cnt > 1) free(user_htype[--user_htype_cnt].name); bzero((char*)user_htypeix, sizeof user_htypeix); } if (!*str) str = " "; for (i = HEAD_FIRST; i < HEAD_LAST; i++) htype[i].flags = ((htype[i].flags & defflag) ? (htype[i].flags | flag) : (htype[i].flags & ~flag)); for (;;) { if ((cp = index(str,',')) != NULL) *cp = '\0'; if (*str == '!') { setit = FALSE; str++; } else setit = TRUE; set_header(str,flag,setit); if (!cp) break; *cp = ','; str = cp+1; } } void set_header(s, flag, setit) char* s; int flag; bool_int setit; { int i; int len = strlen(s); for (i = HEAD_FIRST; i < HEAD_LAST; i++) { if (!len || strncaseEQ(s,htype[i].name,len)) { if (setit && (flag != HT_MAGIC || (htype[i].flags & HT_MAGICOK))) htype[i].flags |= flag; else htype[i].flags &= ~flag; } } if (flag == HT_HIDE && *s && isalpha(*s)) { char ch = isupper(*s)? tolower(*s) : *s; int add_at = 0, killed = 0; bool save_it = TRUE; for (i = user_htypeix[ch - 'a']; *user_htype[i].name == ch; i--) { if (len <= user_htype[i].length && strncaseEQ(s,user_htype[i].name,len)) { free(user_htype[i].name); user_htype[i].name = NULL; killed = i; } else if (len > user_htype[i].length && strncaseEQ(s,user_htype[i].name,user_htype[i].length)) { if (!add_at) { if (user_htype[i].flags == (setit? flag : 0)) save_it = FALSE; add_at = i+1; } } } if (save_it) { if (!killed || (add_at && user_htype[add_at].name)) { if (user_htype_cnt >= user_htype_max) { user_htype = (USER_HEADTYPE*) realloc(user_htype, (user_htype_max += 10) * sizeof (USER_HEADTYPE)); } if (!add_at) { add_at = user_htypeix[ch - 'a']+1; if (add_at == 1) add_at = user_htype_cnt; } for (i = user_htype_cnt; i > add_at; i--) user_htype[i] = user_htype[i-1]; user_htype_cnt++; } else if (!add_at) add_at = killed; user_htype[add_at].length = len; user_htype[add_at].flags = setit? flag : 0; user_htype[add_at].name = savestr(s); for (s = user_htype[add_at].name; *s; s++) { if (isupper(*s)) *s = tolower(*s); } } if (killed) { while (killed < user_htype_cnt && user_htype[killed].name != NULL) killed++; for (i = killed+1; i < user_htype_cnt; i++) { if (user_htype[i].name != NULL) user_htype[killed++] = user_htype[i]; } user_htype_cnt = killed; } bzero((char*)user_htypeix, sizeof user_htypeix); for (i = 1; i < user_htype_cnt; i++) user_htypeix[*user_htype[i].name - 'a'] = i; } } static int parse_mouse_buttons(cpp, btns) char** cpp; char* btns; { char* t = *cpp; int cnt = 0; safefree(t); while (*btns == ' ') btns++; t = *cpp = safemalloc(strlen(btns)+1); while (*btns) { if (*btns == '[') { if (!btns[1]) break; while (*btns) { if (*btns == '\\' && btns[1]) btns++; else if (*btns == ']') break; *t++ = *btns++; } *t++ = '\0'; if (*btns) while (*++btns == ' ') ; } while (*btns && *btns != ' ') *t++ = *btns++; while (*btns == ' ') btns++; *t++ = '\0'; cnt++; } return cnt; } static char* expand_mouse_buttons(cp, cnt) char* cp; int cnt; { *buf = '\0'; while (cnt--) { if (*cp == '[') { int len = strlen(cp); cp[len] = ']'; safecat(buf,cp,sizeof buf); cp += len; *cp++ = '\0'; } else safecat(buf,cp,sizeof buf); cp += strlen(cp)+1; } return buf; } char* quote_string(val) char* val; { static char* bufptr = NULL; char* cp; int needs_quotes = 0; int ticks = 0; int quotes = 0; int backslashes = 0; safefree(bufptr); bufptr = NULL; if (isspace(*val)) needs_quotes = 1; for (cp = val; *cp; cp++) { switch (*cp) { case ' ': case '\t': if (!cp[1] || isspace(cp[1])) needs_quotes++; break; case '\n': case '#': needs_quotes++; break; case '\'': ticks++; break; case '"': quotes++; break; case '\\': backslashes++; break; } } if (needs_quotes || ticks || quotes || backslashes) { char usequote = quotes > ticks? '\'' : '"'; bufptr = safemalloc(strlen(val)+2+(quotes > ticks? ticks : quotes) +backslashes+1); cp = bufptr; *cp++ = usequote; while (*val) { if (*val == usequote || *val == '\\') *cp++ = '\\'; *cp++ = *val++; } *cp++ = usequote; *cp = '\0'; return bufptr; } return val; } void cwd_check() { char tmpbuf[LBUFLEN]; if (!cwd) cwd = savestr(filexp("~/News")); strcpy(tmpbuf,cwd); if (chdir(cwd) != 0) { safecpy(tmpbuf,filexp(cwd),sizeof tmpbuf); if (makedir(tmpbuf,MD_DIR) != 0 || chdir(tmpbuf) != 0) { interp(cmd_buf, (sizeof cmd_buf), "%~/News"); if (makedir(cmd_buf,MD_DIR) != 0) strcpy(tmpbuf,homedir); else strcpy(tmpbuf,cmd_buf); chdir(tmpbuf); #ifdef VERBOSE IF(verbose) printf("\ Cannot make directory %s--\n\ articles will be saved to %s\n\ \n\ ",cwd,tmpbuf) FLUSH; ELSE #endif #ifdef TERSE printf("\ Can't make %s--\n\ using %s\n\ \n\ ",cwd,tmpbuf) FLUSH; #endif } } free(cwd); trn_getwd(tmpbuf, sizeof(tmpbuf)); if (eaccess(tmpbuf,2)) { #ifdef VERBOSE IF(verbose) printf("\ Current directory %s is not writeable--\n\ articles will be saved to home directory\n\n\ ",tmpbuf) FLUSH; ELSE #endif #ifdef TERSE printf("%s not writeable--using ~\n\n",tmpbuf) FLUSH; #endif strcpy(tmpbuf,homedir); } cwd = savestr(tmpbuf); } trn-4.0-test77/opt.h0000644000000000000000000001323407113133016012762 0ustar rootroot/* opt.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* ini_file; EXT char* yesorno[2] #ifdef DOINIT = {"no", "yes"}; #else ; #endif #define YESorNO(v) yesorno[(int)(v)] /* Display Options */ #define OI_TERSE_OUTPUT 2 #define OI_PAGER_LINE_MARKING (OI_TERSE_OUTPUT+1) #define OI_ERASE_SCREEN (OI_PAGER_LINE_MARKING+1) #define OI_ERASE_EACH_LINE (OI_ERASE_SCREEN+1) #define OI_MUCK_UP_CLEAR (OI_ERASE_EACH_LINE+1) #define OI_BKGND_SPINNER (OI_MUCK_UP_CLEAR+1) #define OI_CHARSET (OI_BKGND_SPINNER+1) #define OI_FILTER_CONTROL_CHARACTERS (OI_CHARSET+1) /* Selector Options */ #define OI_USE_UNIV_SEL (OI_FILTER_CONTROL_CHARACTERS+2) #define OI_UNIV_SEL_ORDER (OI_USE_UNIV_SEL+1) #define OI_UNIV_FOLLOW (OI_UNIV_SEL_ORDER+1) #define OI_UNIV_SEL_CMDS (OI_UNIV_FOLLOW+1) #define OI_USE_NEWSRC_SEL (OI_UNIV_SEL_CMDS+1) #define OI_NEWSRC_SEL_CMDS (OI_USE_NEWSRC_SEL+1) #define OI_USE_ADD_SEL (OI_NEWSRC_SEL_CMDS+1) #define OI_ADD_SEL_CMDS (OI_USE_ADD_SEL+1) #define OI_USE_NEWSGROUP_SEL (OI_ADD_SEL_CMDS+1) #define OI_NEWSGROUP_SEL_ORDER (OI_USE_NEWSGROUP_SEL+1) #define OI_NEWSGROUP_SEL_CMDS (OI_NEWSGROUP_SEL_ORDER+1) #define OI_NEWSGROUP_SEL_STYLES (OI_NEWSGROUP_SEL_CMDS+1) #define OI_USE_NEWS_SEL (OI_NEWSGROUP_SEL_STYLES+1) #define OI_NEWS_SEL_MODE (OI_USE_NEWS_SEL+1) #define OI_NEWS_SEL_ORDER (OI_NEWS_SEL_MODE+1) #define OI_NEWS_SEL_CMDS (OI_NEWS_SEL_ORDER+1) #define OI_NEWS_SEL_STYLES (OI_NEWS_SEL_CMDS+1) #define OI_OPTION_SEL_CMDS (OI_NEWS_SEL_STYLES+1) #define OI_USE_SEL_NUM (OI_OPTION_SEL_CMDS+1) #define OI_SEL_NUM_GOTO (OI_USE_SEL_NUM+1) /* Newsreading Options */ #define OI_USE_THREADS (OI_SEL_NUM_GOTO+2) #define OI_SELECT_MY_POSTS (OI_USE_THREADS+1) #define OI_INITIAL_ARTICLE_LINES (OI_SELECT_MY_POSTS+1) #define OI_ARTICLE_TREE_LINES (OI_INITIAL_ARTICLE_LINES+1) #define OI_WORD_WRAP_MARGIN (OI_ARTICLE_TREE_LINES+1) #define OI_AUTO_GROW_GROUPS (OI_WORD_WRAP_MARGIN+1) #define OI_COMPRESS_SUBJECTS (OI_AUTO_GROW_GROUPS+1) #define OI_JOIN_SUBJECT_LINES (OI_COMPRESS_SUBJECTS+1) #define OI_GOTO_LINE_NUM (OI_JOIN_SUBJECT_LINES+1) #define OI_IGNORE_THRU_ON_SELECT (OI_GOTO_LINE_NUM+1) #define OI_READ_BREADTH_FIRST (OI_IGNORE_THRU_ON_SELECT+1) #define OI_BKGND_THREADING (OI_READ_BREADTH_FIRST+1) #define OI_SCANMODE_COUNT (OI_BKGND_THREADING+1) #define OI_HEADER_MAGIC (OI_SCANMODE_COUNT+1) #define OI_HEADER_HIDING (OI_HEADER_MAGIC+1) /* Posting Options */ #define OI_CITED_TEXT_STRING (OI_HEADER_HIDING+2) /* Save Options */ #define OI_SAVE_DIR (OI_CITED_TEXT_STRING+2) #define OI_AUTO_SAVE_NAME (OI_SAVE_DIR+1) #define OI_SAVEFILE_TYPE (OI_AUTO_SAVE_NAME+1) /* Mouse Options */ #define OI_USE_MOUSE (OI_SAVEFILE_TYPE+2) #define OI_MOUSE_MODES (OI_USE_MOUSE+1) #define OI_UNIV_SEL_BTNS (OI_MOUSE_MODES+1) #define OI_NEWSRC_SEL_BTNS (OI_UNIV_SEL_BTNS+1) #define OI_ADD_SEL_BTNS (OI_NEWSRC_SEL_BTNS+1) #define OI_NEWSGROUP_SEL_BTNS (OI_ADD_SEL_BTNS+1) #define OI_NEWS_SEL_BTNS (OI_NEWSGROUP_SEL_BTNS+1) #define OI_OPTION_SEL_BTNS (OI_NEWS_SEL_BTNS+1) #define OI_ART_PAGER_BTNS (OI_OPTION_SEL_BTNS+1) /* MIME Options */ #define OI_MULTIPART_SEPARATOR (OI_ART_PAGER_BTNS+2) #define OI_AUTO_VIEW_INLINE (OI_MULTIPART_SEPARATOR+1) /* Misc Options */ #define OI_NEWGROUP_CHECK (OI_AUTO_VIEW_INLINE+2) #define OI_RESTRICTION_INCLUDES_EMPTIES (OI_NEWGROUP_CHECK+1) #define OI_APPEND_UNSUBSCRIBED_GROUPS (OI_RESTRICTION_INCLUDES_EMPTIES+1) #define OI_INITIAL_GROUP_LIST (OI_APPEND_UNSUBSCRIBED_GROUPS+1) #define OI_RESTART_AT_LAST_GROUP (OI_INITIAL_GROUP_LIST+1) #define OI_EAT_TYPEAHEAD (OI_RESTART_AT_LAST_GROUP+1) #define OI_VERIFY_INPUT (OI_EAT_TYPEAHEAD+1) #define OI_FUZZY_NEWSGROUP_NAMES (OI_VERIFY_INPUT+1) #define OI_AUTO_ARROW_MACROS (OI_FUZZY_NEWSGROUP_NAMES+1) #define OI_CHECKPOINT_NEWSRC_FREQUENCY (OI_AUTO_ARROW_MACROS+1) #define OI_DEFAULT_REFETCH_TIME (OI_CHECKPOINT_NEWSRC_FREQUENCY+1) #define OI_NOVICE_DELAYS (OI_DEFAULT_REFETCH_TIME+1) #define OI_OLD_MTHREADS_DATABASE (OI_NOVICE_DELAYS+1) #define OI_TRN_LAST (OI_OLD_MTHREADS_DATABASE) #ifdef SCAN_ART /* CAA: There are no current plans for scan modes other than SCAN_ART. * The general-scan options might as well be displayed in the same * section as the article-scan options. */ # define OI_SCANA_FOLLOW (OI_TRN_LAST+2) # define OI_SCANA_FOLD (OI_SCANA_FOLLOW+1) # define OI_SCANA_UNZOOMFOLD (OI_SCANA_FOLD+1) # define OI_SCANA_MARKSTAY (OI_SCANA_UNZOOMFOLD+1) # define OI_SCAN_VI (OI_SCANA_MARKSTAY+1) # define OI_SCAN_ITEMNUM (OI_SCAN_VI+1) # define OI_SCANA_DISPANUM (OI_SCAN_ITEMNUM+1) # define OI_SCANA_DISPAUTHOR (OI_SCANA_DISPANUM+1) # define OI_SCANA_DISPSCORE (OI_SCANA_DISPAUTHOR+1) # define OI_SCANA_DISPSUBCNT (OI_SCANA_DISPSCORE+1) # define OI_SCANA_DISPSUBJ (OI_SCANA_DISPSUBCNT+1) # define OI_SCANA_DISPSUMMARY (OI_SCANA_DISPSUBJ+1) # define OI_SCANA_DISPKEYW (OI_SCANA_DISPSUMMARY+1) # define OI_SCAN_LAST (OI_SCANA_DISPKEYW) #else /* !SCAN_ART */ # define OI_SCAN_LAST (OI_TRN_LAST) #endif /* SCAN_ART */ #ifdef SCORE # define OI_SC_VERBOSE (OI_SCAN_LAST+2) # define OI_SCORE_LAST (OI_SC_VERBOSE) #else # define OI_SCORE_LAST (OI_SCAN_LAST) #endif extern INI_WORDS options_ini[]; EXT char** option_def_vals; EXT char** option_saved_vals; EXT char* option_flags; #define OF_SEL 0x0001 #define OF_INCLUDED 0x0010 EXT int sel_page_op; EXT int sel_next_op; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void opt_init _((int,char**,char**)); void opt_file _((char*,char**,bool_int)); void set_options _((char**)); void set_option _((int,char*)); void save_options _((char*)); char* option_value _((int)); void set_header _((char*,int,bool_int)); char* quote_string _((char*)); void cwd_check _((void)); trn-4.0-test77/opt.ih0000644000000000000000000001243007113133016013130 0ustar rootroot/* opt.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ static char yes_or_no[] = "yes/no"; static char selector_commands[] = ""; INI_WORDS options_ini[] = { { 0, "OPTIONS", 0 }, { 0, "*Display Options", 0 }, { 0, "Terse Output", yes_or_no }, { 0, "Pager Line-Marking", "standout/underline/no" }, { 0, "Erase Screen", yes_or_no }, { 0, "Erase Each Line", yes_or_no }, { 0, "Muck Up Clear", yes_or_no }, { 0, "Background Spinner", yes_or_no }, { 0, "Charset", "" }, { 0, "Filter Control Characters", yes_or_no }, { 0, "*Selector Options", 0 }, { 0, "Use Universal Selector", yes_or_no }, { 0, "Universal Selector Order", "natural/points" }, { 0, "Universal Selector article follow", yes_or_no }, { 0, "Universal Selector Commands", selector_commands }, { 0, "Use Newsrc Selector", yes_or_no }, { 0, "Newsrc Selector Commands", selector_commands }, { 0, "Use Addgroup Selector", yes_or_no }, { 0, "Addgroup Selector Commands", selector_commands }, { 0, "Use Newsgroup Selector", yes_or_no }, { 0, "Newsgroup Selector Order", "natural/group/count" }, { 0, "Newsgroup Selector Commands", selector_commands }, { 0, "Newsgroup Selector Display Styles", "" }, { 0, "Use News Selector", "yes/no/<# articles>" }, { 0, "News Selector Mode", "threads/subjects/articles" }, { 0, "News Selector Order", "[reverse] date/subject/author/groups/cnt/points" }, { 0, "News Selector Commands", selector_commands }, { 0, "News Selector Display Styles", "" }, { 0, "Option Selector Commands", selector_commands }, { 0, "Use Selector Numbers", yes_or_no }, { 0, "Selector Number Auto-Goto", yes_or_no }, { 0, "*Newsreading Options", 0 }, { 0, "Use Threads", yes_or_no }, { 0, "Select My Postings", "subthread/parent/thread/no" }, { 0, "Initial Article Lines", "no/<# lines>" }, { 0, "Article Tree Lines", "no/<# lines>" }, { 0, "Word-Wrap Margin", "no/<# chars in margin>" }, { 0, "Auto-Grow Groups", yes_or_no }, { 0, "Compress Subjects", yes_or_no }, { 0, "Join Subject Lines", "no/<# chars>" }, { 0, "Line Num for Goto", "<# line (1-n)>" }, { 0, "Ignore THRU on Select", yes_or_no }, { 0, "Read Breadth First", yes_or_no }, { 0, "Background Threading", yes_or_no }, { 0, "Scan Mode Count", "no/<# articles>" }, { 0, "Header Magic", "<[!]header,...>" }, { 0, "Header Hiding", "<[!]header,...>" }, { 0, "*Posting Options", 0 }, { 0, "Cited Text String", "'>" }, #if 0 { 0, "Attribute string", "" }, { 0, "Reply To", "" }, #endif { 0, "*Save Options", 0 }, { 0, "Save Dir", "" }, { 0, "Auto Savename", yes_or_no }, { 0, "Default Savefile Type", "norm/mail/ask" }, { 0, "*Mouse Options", 0 }, { 0, "Use XTerm Mouse", yes_or_no }, { 0, "Mouse Modes", "" }, { 0, "Universal Selector Mousebar", " Z [Quit]q>" }, { 0, "Newsrc Selector Mousebar", " Z [Quit]q>" }, { 0, "Addgroup Selector Mousebar", "" }, { 0, "Newsgroup Selector Mousebar", "" }, { 0, "News Selector Mousebar", "" }, { 0, "Option Selector Mousebar", "" }, { 0, "Article Pager Mousebar", "" }, { 0, "*MIME Options", 0 }, { 0, "Multipart Separator", "" }, { 0, "Auto-View Inline", yes_or_no }, { 0, "*Misc Options", 0 }, { 0, "Check for New Groups", yes_or_no }, { 0, "Restriction Includes Empty Groups", yes_or_no }, { 0, "Append Unsubscribed Groups", yes_or_no }, { 0, "Initial Group List", "no/<# groups>" }, { 0, "Restart At Last Group", yes_or_no }, { 0, "Eat Type-Ahead", yes_or_no }, { 0, "Verify Input", yes_or_no }, { 0, "Fuzzy Newsgroup Names", yes_or_no }, { 0, "Auto Arrow Macros", "regular/alternate/no" }, { 0, "Checkpoint Newsrc Frequency", "<# articles>" }, { 0, "Default Refetch Time", "never/<1 day 5 hours 8 mins>" }, { 0, "Novice Delays", yes_or_no }, { 0, "Old Mthreads Database", yes_or_no }, #ifdef SCAN_ART { 0, "*Article Scan Mode Options", 0 }, { 0, "Follow Threads", yes_or_no }, { 0, "Fold Subjects", yes_or_no }, { 0, "Re-fold Subjects", yes_or_no }, { 0, "Mark Without Moving", yes_or_no }, { 0, "VI Key Movement Allowed", yes_or_no }, { 0, "Display Item Numbers", yes_or_no }, { 0, "Display Article Number", yes_or_no }, { 0, "Display Author", yes_or_no }, { 0, "Display Score", yes_or_no }, { 0, "Display Subject Count", yes_or_no }, { 0, "Display Subject", yes_or_no }, { 0, "Display Summary", yes_or_no }, { 0, "Display Keywords", yes_or_no }, #endif #ifdef SCORE { 0, "*Scoring Options", 0 }, { 0, "Verbose scoring", yes_or_no }, #endif { 0, 0, 0 } }; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static char* hidden_list _((void)); static char* magic_list _((void)); static void set_header_list _((int,int,char*)); static int parse_mouse_buttons _((char**,char*)); static char* expand_mouse_buttons _((char*,int)); trn-4.0-test77/os2.h0000644000000000000000000000140407113133016012657 0ustar rootroot/* msdos.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #include #include #include #include #include FILE* popen(char*,char*); int pclose(FILE*); #define FILE_REF(s) (*(s)=='/'?'/':(isalpha(*s)&&(s)[1]==':'?(s)[2]:0)) #define chdir ChDir #define getenv GetEnv #define FOPEN_RB "rb" #define FOPEN_WB "wb" #define B19200 19200 #define B9600 9600 #define B4800 4800 #define B2400 2400 #define B1800 1800 #define B1200 1200 #define B600 600 #define B300 300 #define B200 200 #define B150 150 #define B134 134 #define B110 110 #define B75 75 #define B50 50 #define LIMITED_FILENAMES #define RESTORE_ORIGDIR #define NO_FILELINKS #define WINSOCK #define LAX_INEWS #define mkdir(dir,mode) mkdir(dir) trn-4.0-test77/parsedate.y0000644000000000000000000005144607113133016014160 0ustar rootroot%{ /* $Revision: 1.12 $ ** ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990. ** Further revised (removed obsolete constructs and cleaned up timezone ** names) in August, 1991, by Rich. Paul Eggert ** helped in September, 1992. ** ** This grammar has six shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ /* SUPPRESS 530 *//* Empty body for statement */ /* SUPPRESS 593 on yyerrlab *//* Label was not used */ /* SUPPRESS 593 on yynewstate *//* Label was not used */ /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */ #include #include #include #include "config.h" /*#include "config2.h"*/ #include #define yyparse date_parse #define yylex date_lex #define yyerror date_error /* See the LeapYears table in Convert. */ #define EPOCH 1970 #define END_OF_TIME 2038 /* Constants for general time calculations. */ #define DST_OFFSET 1 #define SECSPERDAY (24L * 60L * 60L) /* Readability for TABLE stuff. */ #define HOUR(x) (x * 60) #define LPAREN '(' #define RPAREN ')' #define IS7BIT(x) ((unsigned int)(x) < 0200) #define SIZEOF(array) ((int)(sizeof array / sizeof array[0])) #define ENDOF(array) (&array[SIZEOF(array)]) /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { char *name; int type; time_t value; } TABLE; /* ** Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of them by using a yacc ** union, but this is more efficient. (This routine predates the ** yacc %union construct.) */ static char *yyInput; static DSTMODE yyDSTmode; static int yyHaveDate; static int yyHaveRel; static int yyHaveTime; static time_t yyTimezone; static time_t yyDay; static time_t yyHour; static time_t yyMinutes; static time_t yyMonth; static time_t yySeconds; static time_t yyYear; static MERIDIAN yyMeridian; static time_t yyRelMonth; static time_t yyRelSeconds; extern struct tm *localtime(); static void date_error(); %} %union { time_t Number; enum _MERIDIAN Meridian; } %token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER %token tUNUMBER tZONE %type tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT %type tSNUMBER tUNUMBER tZONE numzone zone %type tMERIDIAN o_merid %% spec : /* NULL */ | spec item ; item : time { yyHaveTime++; #ifdef lint /* I am compulsive about lint natterings... */ if (yyHaveTime == -1) { YYERROR; } #endif /* lint */ } | time zone { yyHaveTime++; yyTimezone = $2; } | date { yyHaveDate++; } | rel { yyHaveRel = 1; } ; time : tUNUMBER o_merid { if ($1 < 100) { yyHour = $1; yyMinutes = 0; } else { yyHour = $1 / 100; yyMinutes = $1 % 100; } yySeconds = 0; yyMeridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = 0; yyMeridian = $4; } | tUNUMBER ':' tUNUMBER numzone { yyHour = $1; yyMinutes = $3; yyTimezone = $4; yyMeridian = MER24; yyDSTmode = DSToff; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyTimezone = $6; yyMeridian = MER24; yyDSTmode = DSToff; } ; zone : tZONE { $$ = $1; yyDSTmode = DSToff; } | tDAYZONE { $$ = $1; yyDSTmode = DSTon; } | tZONE numzone { /* Only allow "GMT+300" and "GMT-0800" */ if ($1 != 0) { YYABORT; } $$ = $2; yyDSTmode = DSToff; } | numzone { $$ = $1; yyDSTmode = DSToff; } ; numzone : tSNUMBER { int i; /* Unix and GMT and numeric timezones -- a little confusing. */ if ($1 < 0) { /* Don't work with negative modulus. */ $1 = -$1; if ($1 > 9999 || (i = $1 % 100) >= 60) { YYABORT; } $$ = ($1 / 100) * 60 + i; } else { if ($1 > 9999 || (i = $1 % 100) >= 60) { YYABORT; } $$ = -(($1 / 100) * 60 + i); } } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { if ($1 > 100) { yyYear = $1; yyMonth = $3; yyDay = $5; } else { yyMonth = $1; yyDay = $3; yyYear = $5; } } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyDay = $1; yyMonth = $2; } | tUNUMBER tMONTH tUNUMBER { yyDay = $1; yyMonth = $2; yyYear = $3; } | tDAY ',' tUNUMBER tMONTH tUNUMBER { yyDay = $3; yyMonth = $4; yyYear = $5; } ; rel : tSNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tUNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tSNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } ; o_merid : /* NULL */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ; %% /* Month and day table. */ static TABLE MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, { "april", tMONTH, 4 }, { "may", tMONTH, 5 }, { "june", tMONTH, 6 }, { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, /* The value of the day isn't used... */ { "sunday", tDAY, 0 }, { "monday", tDAY, 0 }, { "tuesday", tDAY, 0 }, { "wednesday", tDAY, 0 }, { "thursday", tDAY, 0 }, { "friday", tDAY, 0 }, { "saturday", tDAY, 0 }, }; /* Time units table. */ static TABLE UnitsTable[] = { { "year", tMONTH_UNIT, 12 }, { "month", tMONTH_UNIT, 1 }, { "week", tSEC_UNIT, 7L * 24 * 60 * 60 }, { "day", tSEC_UNIT, 1L * 24 * 60 * 60 }, { "hour", tSEC_UNIT, 60 * 60 }, { "minute", tSEC_UNIT, 60 }, { "min", tSEC_UNIT, 60 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, }; /* Timezone table. */ static TABLE TimezoneTable[] = { { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ { "ut", tZONE, HOUR( 0) }, /* Universal */ { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */ { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */ { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */ { "wet", tZONE, HOUR( 0) }, /* Western European */ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ { "cst", tZONE, HOUR( 6) }, /* Central Standard */ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */ { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */ { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */ { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */ { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */ { "mez", tZONE, -HOUR(1) }, /* Middle European */ { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { "cet", tZONE, -HOUR(1) }, /* Central European */ { "met", tZONE, -HOUR(1) }, /* Middle European */ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */ { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */ { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */ { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */ { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */ { "cct", tZONE, -HOUR(8) }, /* China Coast */ { "jst", tZONE, -HOUR(9) }, /* Japan Standard */ { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */ { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */ { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ /* For completeness we include the following entries. */ #if 0 /* Duplicate names. Either they conflict with a zone listed above * (which is either more likely to be seen or just been in circulation * longer), or they conflict with another zone in this section and * we could not reasonably choose one over the other. */ { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */ { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */ { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */ { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */ { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */ { "cst", tZONE, HOUR( 5) }, /* Chile Standard */ { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */ { "ast", tZONE, HOUR( 5) }, /* Acre Standard */ { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */ { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */ { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */ { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */ { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */ { "sst", tZONE, HOUR(11) }, /* Samoa Standard */ { "ist", tZONE, -HOUR(2) }, /* Israel Standard */ { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */ { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */ { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */ { "cst", tZONE, -HOUR(8) }, /* China Standard */ { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */ { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */ /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ { "wat", tZONE, -HOUR(1) }, /* West Africa */ { "at", tZONE, HOUR( 2) }, /* Azores */ { "gst", tZONE, -HOUR(10) }, /* Guam Standard */ { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ { "fwt", tZONE, -HOUR(1) }, /* French Winter */ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ { "bt", tZONE, -HOUR(3) }, /* Baghdad */ { "it", tZONE, -(HOUR(3)+30) }, /* Iran */ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ { "nst", tZONE, -HOUR(7) }, /* North Sumatra */ { "sst", tZONE, -HOUR(7) }, /* South Sumatra */ { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */ { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */ { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */ { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */ #endif /* 0 */ }; /* ARGSUSED */ static void date_error(s) char *s; { /* NOTREACHED */ } static time_t ToSeconds(Hours, Minutes, Seconds, Meridian) time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61) return -1; if (Meridian == MER24) { if (Hours < 0 || Hours > 23) return -1; } else { if (Hours < 1 || Hours > 12) return -1; if (Hours == 12) Hours = 0; if (Meridian == MERpm) Hours += 12; } return (Hours * 60L + Minutes) * 60L + Seconds; } static time_t Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst) time_t Month; time_t Day; time_t Year; time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; DSTMODE dst; { static int DaysNormal[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int DaysLeap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int LeapYears[] = { 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036 }; register int *yp; register int *mp; register time_t Julian; register int i; time_t tod; if (Year < 0) Year = -Year; if (Year < 100) Year += 1900; if (Year < EPOCH) Year += 100; for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++) if (Year == *yp) { mp = DaysLeap; break; } if (Year < EPOCH || Year > END_OF_TIME || Month < 1 || Month > 12 /* NOSTRICT *//* conversion from long may lose accuracy */ || Day < 1 || Day > mp[(int)Month]) return -1; Julian = Day - 1 + (Year - EPOCH) * 365; for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) if (Year <= *yp) break; for (i = 1; i < Month; i++) Julian += *++mp; Julian *= SECSPERDAY; Julian += yyTimezone * 60L; if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) return -1; Julian += tod; tod = Julian; if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst)) Julian -= DST_OFFSET * 60L * 60L; return Julian; } static time_t DSTcorrect(Start, Future) time_t Start; time_t Future; { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L; } static time_t RelativeMonth(Start, RelMonth) time_t Start; time_t RelMonth; { struct tm *tm; time_t Month; time_t Year; tm = localtime(&Start); Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, MER24, DSTmaybe)); } static int LookupWord(buff, length) char *buff; register int length; { register char *p; register char *q; register TABLE *tp; register int c; p = buff; c = p[0]; /* See if we have an abbreviation for a month. */ if (length == 3 || (length == 4 && p[3] == '.')) for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) { q = tp->name; if (c == q[0] && p[1] == q[1] && p[2] == q[2]) { yylval.Number = tp->value; return tp->type; } } else for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Try for a timezone. */ for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) if (c == tp->name[0] && p[1] == tp->name[1] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Try the units table. */ for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ if (--length > 0 && p[length] == 's') { p[length] = '\0'; for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { p[length] = 's'; yylval.Number = tp->value; return tp->type; } p[length] = 's'; } length++; /* Drop out any periods. */ for (p = buff, q = (char*)buff; *q; q++) if (*q != '.') *p++ = *q; *p = '\0'; /* Try the meridians. */ if (buff[1] == 'm' && buff[2] == '\0') { if (buff[0] == 'a') { yylval.Meridian = MERam; return tMERIDIAN; } if (buff[0] == 'p') { yylval.Meridian = MERpm; return tMERIDIAN; } } /* If we saw any periods, try the timezones again. */ if (p - buff != length) { c = buff[0]; for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) if (c == tp->name[0] && p[1] == tp->name[1] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } /* Unknown word -- assume GMT timezone. */ yylval.Number = 0; return tZONE; } int date_lex() { register char c; register char *p; char buff[20]; register int sign; register int i; register int nesting; for ( ; ; ) { /* Get first character after the whitespace. */ for ( ; ; ) { while (isspace(*yyInput)) yyInput++; c = *yyInput; /* Ignore RFC 822 comments, typically time zone names. */ if (c != LPAREN) break; for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; ) if (c == LPAREN) nesting++; else if (!IS7BIT(c) || c == '\0' || c == '\r' || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) /* Lexical error: bad comment. */ return '?'; yyInput++; } /* A number? */ if (isdigit(c) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; yyInput++; if (!isdigit(*yyInput)) /* Skip the plus or minus sign. */ continue; } else sign = 0; for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); ) i = 10 * i + c - '0'; yyInput--; yylval.Number = sign < 0 ? -i : i; return sign ? tSNUMBER : tUNUMBER; } /* A word? */ if (isalpha(c)) { for (p = buff; (c = *yyInput++) == '.' || isalpha(c); ) if (p < &buff[sizeof buff - 1]) *p++ = isupper(c) ? tolower(c) : c; *p = '\0'; yyInput--; return LookupWord(buff, p - buff); } return *yyInput++; } } time_t parsedate(p) char *p; { extern int date_parse(); time_t Start; yyInput = p; yyYear = 0; yyMonth = 0; yyDay = 0; yyTimezone = 0; yyDSTmode = DSTmaybe; yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; yyRelSeconds = 0; yyRelMonth = 0; yyHaveDate = 0; yyHaveRel = 0; yyHaveTime = 0; if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1) return -1; if (yyHaveDate || yyHaveTime) { Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, yyMeridian, yyDSTmode); if (Start < 0) return -1; } else return -1; Start += yyRelSeconds; if (yyRelMonth) Start += RelativeMonth(Start, yyRelMonth); /* Have to do *something* with a legitimate -1 so it's distinguishable * from the error return value. (Alternately could set errno on error.) */ return Start == -1 ? 0 : Start; } #ifdef TEST #if YYDEBUG extern int yydebug; #endif /* YYDEBUG */ /* ARGSUSED */ int main(ac, av) int ac; char *av[]; { char buff[128]; time_t d; #if YYDEBUG yydebug = 1; #endif /* YYDEBUG */ (void)printf("Enter date, or blank line to exit.\n\t> "); for ( ; ; ) { (void)printf("\t> "); (void)fflush(stdout); if (gets(buff) == NULL || buff[0] == '\n') break; #if YYDEBUG if (strcmp(buff, "yydebug") == 0) { yydebug = !yydebug; printf("yydebug = %s\n", yydebug ? "on" : "off"); continue; } #endif /* YYDEBUG */ d = parsedate(buff, (TIMEINFO *)NULL); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else (void)printf("%s", ctime(&d)); } exit(0); /* NOTREACHED */ } #endif /* TEST */ trn-4.0-test77/parsedate.h0000644000000000000000000000016107113133016014123 0ustar rootroot/* parsedate.h */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ time_t parsedate _((char*)); trn-4.0-test77/patchlevel.h0000644000000000000000000000005711437627215014323 0ustar rootroot#define PATCHLEVEL " 4.0-test77 (Sep 1, 2010)" trn-4.0-test77/popen.c0000644000000000000000000001447507113133016013304 0ustar rootroot/* popen/pclose: * * simple MS-DOS piping scheme to imitate UNIX pipes */ #include "EXTERN.h" #include "common.h" #include #include "util2.h" #include "util3.h" #ifndef _NFILE # define _NFILE 5 /* Number of open files */ #endif _NFILE #define READIT 1 /* Read pipe */ #define WRITEIT 2 /* Write pipe */ static char* prgname[_NFILE]; /* program name if write pipe */ static int pipetype[_NFILE]; /* 1=read 2=write */ static char* pipename[_NFILE]; /* pipe file name */ /* *------------------------------------------------------------------------ * run: Execute command via SHELL or COMSPEC *------------------------------------------------------------------------ */ static int run(char* command) { jmp_buf panic; /* How to recover from errors */ char* shell; /* Command processor */ char* s = NULL; /* Holds the command */ int s_is_malloced = 0; /* True if need to free 's' */ static char* command_com = "COMMAND.COM"; int status; /* Return codes */ char* shellpath; /* Full command processor path */ char* bp; /* Generic string pointer */ static char dash_c[] = "/c"; s = savestr(command); /* Determine the command processor */ if ((shell = getenv("SHELL")) == NULL && (shell = getenv("COMSPEC")) == NULL) shell = command_com; strupr(shell); shellpath = shell; /* Strip off any leading backslash directories */ shell = rindex(shellpath, '\\'); if (shell != NULL) shell++; else shell = shellpath; /* Strip off any leading slash directories */ bp = rindex(shell, '/'); if (bp != NULL) shell = ++bp; if (strcmp(shell, command_com) != 0) { /* MKS Shell needs quoted argument */ char* bp; bp = s = safemalloc(strlen(command) + 3); *bp++ = '\''; while ((*bp++ = *command++) != '\0') ; *(bp - 1) = '\''; *bp = '\0'; s_is_malloced = 1; } else s = command; /* Run the program */ status = spawnl(P_WAIT, shellpath, shell, dash_c, s, NULL); if (s_is_malloced) free(s); return status; } /* *------------------------------------------------------------------------ * uniquepipe: returns a unique file name *------------------------------------------------------------------------ */ static char* uniquepipe(void) { static char name[14]; static short int num = 0; (void) sprintf(name, "pipe%04d.tmp", num++); return name; } /* *------------------------------------------------------------------------ * resetpipe: Private routine to cancel a pipe *------------------------------------------------------------------------ */ static void resetpipe(int fd) { char* bp; if (fd >= 0 && fd < _NFILE) { pipetype[fd] = 0; if ((bp = pipename[fd]) != NULL) { (void) unlink(bp); free(bp); pipename[fd] = NULL; } if ((bp = prgname[fd]) != NULL) { free(bp); prgname[fd] = NULL; } } } /* *------------------------------------------------------------------------ * popen: open a pipe *------------------------------------------------------------------------ */ FILE* popen(prg, type) char* prg; /* The command to be run */ char* type; /* "w" or "r" */ { FILE* p = NULL; /* Where we open the pipe */ int ostdin; /* Where our stdin is now */ int pipefd = -1; /* fileno(p) -- for convenience */ char* tmpfile; /* Holds name of pipe file */ jmp_buf panic; /* Where to go if there's an error */ int lineno; /* Line number where panic happened */ /* Get a unique pipe file name */ tmpfile = filexp("%Y/"); strcat(tmpfile, uniquepipe()); if ((lineno = setjmp(panic)) != 0) { /* An error has occurred, so clean up */ int E = errno; if (p != NULL) (void) fclose(p); resetpipe(pipefd); errno = E; lineno = lineno; return NULL; } if (strcmp(type, "w") == 0) { /* for write style pipe, pclose handles program execution */ if ((p = fopen(tmpfile, "w")) != NULL) { pipefd = fileno(p); pipetype[pipefd] = WRITEIT; pipename[pipefd] = savestr(tmpfile); prgname[pipefd] = savestr(prg); } } else if (strcmp(type, "r") == 0) { /* read pipe must create tmp file, set up stdout to point to the temp * file, and run the program. note that if the pipe file cannot be * opened, it'll return a condition indicating pipe failure, which is * fine. */ if ((p = fopen(tmpfile, "w")) != NULL) { int ostdout; pipefd = fileno(p); pipetype[pipefd]= READIT; pipename[pipefd] = savestr(tmpfile); /* Redirect stdin for the new command */ ostdout = dup(fileno(stdout)); if (dup2(fileno(stdout), pipefd) < 0) { int E = errno; (void) dup2(fileno(stdout), ostdout); errno = E; longjmp(panic, __LINE__); } if (run(prg) != 0) longjmp(panic, __LINE__); if (dup2(fileno(stdout), ostdout) < 0) longjmp(panic, __LINE__); if (fclose(p) < 0) longjmp(panic, __LINE__); if ((p = fopen(tmpfile, "r")) == NULL) longjmp(panic, __LINE__); } } else { /* screwy call or unsupported type */ errno = EINVFNC; longjmp(panic, __LINE__); } return p; } /* close a pipe */ int pclose(p) FILE* p; { int pipefd = -1; /* Fildes where pipe is opened */ int ostdout; /* Where our stdout points now */ int ostdin; /* Where our stdin points now */ jmp_buf panic; /* Context to return to if error */ int lineno; /* Line number where panic happened */ if ((lineno = setjmp(panic)) != 0) { /* An error has occurred, so clean up and return */ int E = errno; if (p != NULL) (void) fclose(p); resetpipe(pipefd); errno = E; lineno = lineno; return -1; } pipefd = fileno(p); if (fclose(p) < 0) longjmp(panic, __LINE__); switch (pipetype[pipefd]) { case WRITEIT: /* open the temp file again as read, redirect stdin from that * file, run the program, then clean up. */ if ((p = fopen(pipename[pipefd],"r")) == NULL) longjmp(panic, __LINE__); ostdin = dup(fileno(stdin)); if (dup2(fileno(stdin), fileno(p)) < 0) longjmp(panic, __LINE__); if (run(prgname[pipefd]) != 0) longjmp(panic, __LINE__); if (dup2(fileno(stdin), ostdin) < 0) longjmp(panic, __LINE__); if (fclose(p) < 0) longjmp(panic, __LINE__); resetpipe(pipefd); break; case READIT: /* close the temp file and remove it */ resetpipe(pipefd); break; default: errno = EINVFNC; longjmp(panic, __LINE__); /*NOTREACHED*/ } return 0; } trn-4.0-test77/rcln.c0000644000000000000000000003563407113133016013121 0ustar rootroot/* rcln.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "util2.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "rcstuff.h" #include "term.h" #include "INTERN.h" #include "rcln.h" #define MAX_DIGITS 7 void rcln_init() { ; } #ifdef CATCHUP void catch_up(np, leave_count, output_level) NGDATA* np; int leave_count; int output_level; { char tmpbuf[128]; if (leave_count) { if (output_level) { #ifdef VERBOSE IF(verbose) printf("\nMarking all but %d articles in %s as read.\n", leave_count,np->rcline) FLUSH; ELSE #endif #ifdef TERSE printf("\nAll but %d marked as read.\n",leave_count) FLUSH; #endif } checkexpired(np, getngsize(np) - leave_count + 1); set_toread(np, ST_STRICT); } else { if (output_level) { #ifdef VERBOSE IF(verbose) printf("\nMarking %s as all read.\n",np->rcline) FLUSH; ELSE #endif #ifdef TERSE fputs("\nMarked read\n",stdout) FLUSH; #endif } sprintf(tmpbuf,"%s: 1-%ld", np->rcline,(long)getngsize(np)); free(np->rcline); np->rcline = savestr(tmpbuf); *(np->rcline + np->numoffset - 1) = '\0'; if (ng_min_toread > TR_NONE && np->toread > TR_NONE) newsgroup_toread--; np->toread = TR_NONE; } np->rc->flags |= RF_RCCHANGED; if (!write_newsrcs(multirc)) get_anything(); } #endif /* add an article number to a newsgroup, if it isn't already read */ int addartnum(dp,artnum,ngnam) DATASRC* dp; ART_NUM artnum; char* ngnam; { register NGDATA* np; register char* s; register char* t; register char* maxt = NULL; ART_NUM min = 0, max = -1, lastnum = 0; char* mbuf; bool_int morenum; if (!artnum) return 0; np = find_ng(ngnam); if (np == NULL) /* not found in newsrc? */ return 0; if (dp != np->rc->datasrc) { /* punt on cross-host xrefs */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) printf("Cross-host xref to group %s ignored.\n",ngnam) FLUSH; #endif return 0; } if (!np->numoffset) return 0; #ifndef ANCIENT_NEWS if (!np->abs1st) { /* Trim down the list due to expires if we haven't done so yet. */ set_toread(np, ST_LAX); } #endif #if 0 if (artnum > np->ngmax + 200) { /* allow for incoming articles */ printf("\nCorrupt Xref line!!! %ld --> %s(1..%ld)\n", artnum,ngnam, np->ngmax) FLUSH; paranoid = TRUE; /* paranoia reigns supreme */ return -1; /* hope this was the first newsgroup */ } #endif if (np->toread == TR_BOGUS) return 0; if (artnum > np->ngmax) { if (np->toread > TR_NONE) np->toread += artnum - np->ngmax; np->ngmax = artnum; } #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%ld->\n%s%c%s\n",(long)artnum,np->rcline, np->subscribechar, np->rcline + np->numoffset) FLUSH; } #endif s = np->rcline + np->numoffset; while (*s == ' ') s++; /* skip spaces */ t = s; while (isdigit(*s) && artnum >= (min = atol(s))) { /* while it might have been read */ for (t = s; isdigit(*t); t++) ; /* skip number */ if (*t == '-') { /* is it a range? */ t++; /* skip to next number */ if (artnum <= (max = atol(t))) return 0; /* it is in range => already read */ lastnum = max; /* remember it */ maxt = t; /* remember position in case we */ /* want to overwrite the max */ while (isdigit(*t)) t++; /* skip second number */ } else { if (artnum == min) /* explicitly a read article? */ return 0; lastnum = min; /* remember what the number was */ maxt = NULL; /* last one was not a range */ } while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ s = t; } /* we have not read it, so insert the article number before s */ morenum = isdigit(*s); /* will it need a comma after? */ *(np->rcline + np->numoffset - 1) = np->subscribechar; mbuf = safemalloc((MEM_SIZE)(strlen(s)+(s - np->rcline)+MAX_DIGITS+2+1)); strcpy(mbuf,np->rcline); /* make new rc line */ if (maxt && lastnum && artnum == lastnum+1) /* can we just extend last range? */ t = mbuf + (maxt - np->rcline); /* then overwrite previous max */ else { t = mbuf + (t - np->rcline); /* point t into new line instead */ if (lastnum) { /* have we parsed any line? */ if (!morenum) /* are we adding to the tail? */ *t++ = ','; /* supply comma before */ if (!maxt && artnum == lastnum+1 && *(t-1) == ',') /* adjacent singletons? */ *(t-1) = '-'; /* turn them into a range */ } } if (morenum) { /* is there more to life? */ if (min == artnum+1) { /* can we consolidate further? */ bool range_before = (*(t-1) == '-'); bool range_after; char* nextmax; for (nextmax = s; isdigit(*nextmax); nextmax++) ; range_after = *nextmax++ == '-'; if (range_before) *t = '\0'; /* artnum is redundant */ else sprintf(t,"%ld-",(long)artnum);/* artnum will be new min */ if (range_after) s = nextmax; /* *s is redundant */ /* else s = s */ /* *s is new max */ } else sprintf(t,"%ld,",(long)artnum); /* put the number and comma */ } else sprintf(t,"%ld",(long)artnum); /* put the number there (wherever) */ strcat(t,s); /* copy remainder of line */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) printf("%s\n",mbuf) FLUSH; #endif free(np->rcline); np->rcline = mbuf; /* pull the switcheroo */ *(np->rcline + np->numoffset - 1) = '\0'; /* wipe out : or ! */ if (np->toread > TR_NONE) /* lest we turn unsub into bogus */ np->toread--; return 0; } /* delete an article number from a newsgroup, if it is there */ #ifdef MCHASE void subartnum(dp,artnum,ngnam) DATASRC* dp; register ART_NUM artnum; char* ngnam; { register NGDATA* np; register char* s; register char* t; register ART_NUM min, max; char* mbuf; int curlen; if (!artnum) return; np = find_ng(ngnam); if (np == NULL) /* not found in newsrc? */ return; if (dp != np->rc->datasrc) /* punt on cross-host xrefs */ return; if (!np->numoffset) return; #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%ld<-\n%s%c%s\n",(long)artnum,np->rcline,np->subscribechar, np->rcline + np->numoffset) FLUSH; } #endif s = np->rcline + np->numoffset; while (*s == ' ') s++; /* skip spaces */ /* a little optimization, since it is almost always the last number */ for (t=s; *t; t++) ; /* find end of string */ curlen = t - np->rcline; for (t--; isdigit(*t); t--) ; /* find previous delim */ if (*t == ',' && atol(t+1) == artnum) { *t = '\0'; if (np->toread >= TR_NONE) ++np->toread; #ifdef DEBUG if (debug & DEB_XREF_MARKER) printf("%s%c %s\n",np->rcline,np->subscribechar,s) FLUSH; #endif return; } /* not the last number, oh well, we may need the length anyway */ while (isdigit(*s) && artnum >= (min = atol(s))) { /* while it might have been read */ for (t = s; isdigit(*t); t++) ; /* skip number */ if (*t == '-') { /* is it a range? */ t++; /* skip to next number */ max = atol(t); while (isdigit(*t)) t++; /* skip second number */ if (artnum <= max) { /* it is in range => already read */ if (artnum == min) { min++; artnum = 0; } else if (artnum == max) { max--; artnum = 0; } *(np->rcline + np->numoffset - 1) = np->subscribechar; mbuf = safemalloc((MEM_SIZE)(curlen+(artnum?(MAX_DIGITS+1)*2+1:1+1))); *s = '\0'; strcpy(mbuf,np->rcline); /* make new rc line */ s = mbuf + (s - np->rcline); /* point s into mbuf now */ if (artnum) { /* split into two ranges? */ prange(s,min,artnum-1); s += strlen(s); *s++ = ','; prange(s,artnum+1,max); } else /* only one range */ prange(s,min,max); strcat(s,t); /* copy remainder over */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s\n",mbuf) FLUSH; } #endif free(np->rcline); np->rcline = mbuf; /* pull the switcheroo */ *(np->rcline + np->numoffset - 1) = '\0'; /* wipe out : or ! */ if (np->toread >= TR_NONE) np->toread++; return; } } else { if (artnum == min) { /* explicitly a read article? */ if (*t == ',') /* pick a comma, any comma */ t++; else if (s[-1] == ',') s--; else if (s[-2] == ',') /* (in case of space) */ s -= 2; strcpy(s,t); /* no need to realloc */ if (np->toread >= TR_NONE) np->toread++; #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s%c%s\n",np->rcline,np->subscribechar, np->rcline + np->numoffset) FLUSH; } #endif return; } } while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ s = t; } } void prange(where,min,max) char* where; ART_NUM min,max; { if (min == max) sprintf(where,"%ld",(long)min); else sprintf(where,"%ld-%ld",(long)min,(long)max); } #endif /* calculate the number of unread articles for a newsgroup */ void set_toread(np, lax_high_check) register NGDATA* np; bool_int lax_high_check; { register char* s; register char* c; register char* h; char tmpbuf[64]; char* mybuf = tmpbuf; char* nums; int length; bool virgin_ng = (!np->abs1st); ART_NUM ngsize = getngsize(np); ART_NUM unread = ngsize; ART_NUM newmax; if (ngsize == TR_BOGUS) { if (!toread_quiet) { printf("\nInvalid (bogus) newsgroup found: %s\n",np->rcline) FLUSH; } paranoid = TRUE; if (virgin_ng || np->toread >= ng_min_toread) { newsgroup_toread--; missing_count++; } np->toread = TR_BOGUS; return; } if (virgin_ng) { sprintf(tmpbuf," 1-%ld",(long)ngsize); if (strNE(tmpbuf,np->rcline+np->numoffset)) checkexpired(np,np->abs1st); /* this might realloc rcline */ } nums = np->rcline + np->numoffset; length = strlen(nums); if (length+MAX_DIGITS+1 > sizeof tmpbuf) mybuf = safemalloc((MEM_SIZE)(length+MAX_DIGITS+1)); strcpy(mybuf,nums); mybuf[length++] = ','; mybuf[length] = '\0'; for (s = mybuf; isspace(*s); s++) ; for ( ; (c = index(s,',')) != NULL ; s = ++c) { /* for each range */ *c = '\0'; /* keep index from running off */ if ((h = index(s,'-')) != NULL) /* find - in range, if any */ unread -= (newmax = atol(h+1)) - atol(s) + 1; else if ((newmax = atol(s)) != 0) unread--; /* recalculate length */ if (newmax > ngsize) { /* paranoia check */ if (!lax_high_check && newmax > ngsize) { unread = -1; break; } else { unread += newmax - ngsize; np->ngmax = ngsize = newmax; } } } if (unread < 0) { /* SOMEONE RESET THE NEWSGROUP!!! */ unread = (ART_UNREAD)ngsize; /* assume nothing carried over */ if (!toread_quiet) { printf("\nSomebody reset %s -- assuming nothing read.\n", np->rcline) FLUSH; } *(np->rcline + np->numoffset) = '\0'; paranoid = TRUE; /* enough to make a guy paranoid */ np->rc->flags |= RF_RCCHANGED; } if (np->subscribechar == NEGCHAR) unread = TR_UNSUB; if (unread >= ng_min_toread) { if (!virgin_ng && np->toread < ng_min_toread) newsgroup_toread++; } else if (unread <= 0) { if (np->toread > ng_min_toread) { newsgroup_toread--; if (virgin_ng) missing_count++; } } np->toread = (ART_UNREAD)unread; /* remember how many are left */ if (mybuf != tmpbuf) free(mybuf); } /* make sure expired articles are marked as read */ void checkexpired(np,a1st) register NGDATA* np; register ART_NUM a1st; { register char* s; register ART_NUM num, lastnum = 0; char* mbuf; char* cp; int len; if (a1st<=1) return; #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("1-%ld->\n%s%c%s\n",(long)(a1st-1),np->rcline,np->subscribechar, np->rcline + np->numoffset) FLUSH; } #endif for (s = np->rcline + np->numoffset; isspace(*s); s++) ; while (*s && (num = atol(s)) <= a1st) { while (isdigit(*s)) s++; while (*s && !isdigit(*s)) s++; lastnum = num; } len = strlen(s); if (len && s[-1] == '-') { /* landed in a range? */ if (lastnum != 1) { if (3+len <= (int)strlen(np->rcline+np->numoffset)) mbuf = np->rcline; else { mbuf = safemalloc((MEM_SIZE)(np->numoffset+3+len+1)); strcpy(mbuf, np->rcline); } cp = mbuf + np->numoffset; *cp++ = ' '; *cp++ = '1'; *cp++ = '-'; safecpy(cp, s, len+1); if (np->rcline != mbuf) { free(np->rcline); np->rcline = mbuf; } np->rc->flags |= RF_RCCHANGED; } } else { /* s now points to what should follow the first range */ char numbuf[32]; int nlen; sprintf(numbuf," 1-%ld",(long)(a1st - (lastnum != a1st))); nlen = strlen(numbuf) + (len != 0); if (s - np->rcline >= np->numoffset + nlen) mbuf = np->rcline; else { mbuf = safemalloc((MEM_SIZE)(np->numoffset+nlen+len+1)); strcpy(mbuf,np->rcline); } cp = mbuf + np->numoffset; strcpy(cp, numbuf); cp += nlen; if (len) { cp[-1] = ','; if (cp != s) safecpy(cp,s,len+1); } if (!checkflag && np->rcline == mbuf) np->rcline = saferealloc(np->rcline, (MEM_SIZE)(cp-mbuf+len+1)); else { if (!checkflag) free(np->rcline); np->rcline = mbuf; } np->rc->flags |= RF_RCCHANGED; } #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s%c%s\n",np->rcline,np->subscribechar, np->rcline + np->numoffset) FLUSH; } #endif } /* Returns TRUE if article is marked as read or does not exist */ /* could use a better name */ bool was_read_group(dp,artnum,ngnam) DATASRC* dp; ART_NUM artnum; char* ngnam; { register NGDATA* np; register char* s; register char* t; register char* maxt = NULL; ART_NUM min = 0, max = -1, lastnum = 0; if (!artnum) return TRUE; np = find_ng(ngnam); if (np == NULL) /* not found in newsrc? */ return TRUE; if (!np->numoffset) /* no numbers on line */ return FALSE; #if 0 /* consider this code later */ if (!np->abs1st) { /* Trim down the list due to expires if we haven't done so yet. */ set_toread(np, ST_LAX); } #endif if (np->toread == TR_BOGUS) return TRUE; if (artnum > np->ngmax) { return FALSE; /* probably doesn't exist, however */ } s = np->rcline + np->numoffset; while (*s == ' ') s++; /* skip spaces */ t = s; while (isdigit(*s) && artnum >= (min = atol(s))) { /* while it might have been read */ for (t = s; isdigit(*t); t++) ; /* skip number */ if (*t == '-') { /* is it a range? */ t++; /* skip to next number */ if (artnum <= (max = atol(t))) return TRUE; /* it is in range => already read */ lastnum = max; /* remember it */ maxt = t; /* remember position in case we */ /* want to overwrite the max */ while (isdigit(*t)) t++; /* skip second number */ } else { if (artnum == min) /* explicitly a read article? */ return TRUE; lastnum = min; /* remember what the number was */ maxt = NULL; /* last one was not a range */ } while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ s = t; } /* we have not read it, so return FALSE */ return FALSE; } trn-4.0-test77/rcln.h0000644000000000000000000000117607113133016013120 0ustar rootroot/* rcln.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* if TRUE, silence is golden (universal scan mode) */ EXT bool toread_quiet INIT(FALSE); #define ST_STRICT 0 #define ST_LAX 1 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void rcln_init _((void)); #ifdef CATCHUP void catch_up _((NGDATA*,int,int)); #endif int addartnum _((DATASRC*,ART_NUM,char*)); #ifdef MCHASE void subartnum _((DATASRC*,ART_NUM,char*)); #endif void prange _((char*,ART_NUM,ART_NUM)); void set_toread _((NGDATA*,bool_int)); void checkexpired _((NGDATA*,ART_NUM)); bool was_read_group _((DATASRC*,ART_NUM,char*)); trn-4.0-test77/rcstuff.c0000644000000000000000000010273211437640112013635 0ustar rootroot/* rcstuff.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "util.h" #include "util2.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "term.h" #include "final.h" #include "trn.h" #include "env.h" #include "init.h" #include "intrp.h" #include "only.h" #include "rcln.h" #include "last.h" #include "autosub.h" #include "rt-select.h" #include "rt-page.h" #include "INTERN.h" #include "rcstuff.h" #include "rcstuff.ih" static bool foundany; bool rcstuff_init() { MULTIRC* mptr = NULL; int i; multirc_list = new_list(0,0,sizeof(MULTIRC),20,LF_ZERO_MEM|LF_SPARSE,NULL); if (trnaccess_mem) { NEWSRC* rp; char* s; char* section; char* cond; char** vals = prep_ini_words(rcgroups_ini); s = trnaccess_mem; while ((s = next_ini_section(s,§ion,&cond)) != NULL) { if (*cond && !check_ini_cond(cond)) continue; if (strncaseNE(section, "group ", 6)) continue; i = atoi(section+6); if (i < 0) i = 0; s = parse_ini_section(s, rcgroups_ini); if (!s) break; rp = new_newsrc(vals[RI_ID],vals[RI_NEWSRC],vals[RI_ADDGROUPS]); if (rp) { MULTIRC* mp; NEWSRC* prev_rp; mp = multirc_ptr(i); prev_rp = mp->first; if (!prev_rp) mp->first = rp; else { while (prev_rp->next) prev_rp = prev_rp->next; prev_rp->next = rp; } mp->num = i; if (!mptr) mptr = mp; /*rp->flags |= RF_USED;*/ } /*else ;$$complain?*/ } free((char*)vals); free(trnaccess_mem); } if (UseNewsrcSelector && !checkflag) return TRUE; foundany = FALSE; if (mptr && !use_multirc(mptr)) use_next_multirc(mptr); if (!multirc) { mptr = multirc_ptr(0); mptr->first = new_newsrc("default",NULL,NULL); if (!use_multirc(mptr)) { printf("Couldn't open any newsrc groups. Is your access file ok?\n"); finalize(1); } } if (checkflag) /* were we just checking? */ finalize(foundany); /* tell them what we found */ return foundany; } NEWSRC* new_newsrc(name,newsrc,add_ok) char* name; char* newsrc; char* add_ok; { char tmpbuf[CBUFLEN]; NEWSRC* rp; DATASRC* dp; if (!name || !*name) return NULL; if (!newsrc || !*newsrc) { newsrc = getenv("NEWSRC"); if (!newsrc) newsrc = RCNAME; } dp = get_datasrc(name); if (!dp) return NULL; rp = (NEWSRC*)safemalloc(sizeof (NEWSRC)); bzero((char*)rp, sizeof (NEWSRC)); rp->datasrc = dp; rp->name = savestr(filexp(newsrc)); sprintf(tmpbuf, RCNAME_OLD, rp->name); rp->oldname = savestr(tmpbuf); sprintf(tmpbuf, RCNAME_NEW, rp->name); rp->newname = savestr(tmpbuf); switch (add_ok? *add_ok : 'y') { case 'n': case 'N': break; default: if (dp->flags & DF_ADD_OK) rp->flags |= RF_ADD_NEWGROUPS; /* FALL THROUGH */ case 'm': case 'M': rp->flags |= RF_ADD_GROUPS; break; } return rp; } bool use_multirc(mp) MULTIRC* mp; { NEWSRC* rp; bool had_trouble = FALSE; bool had_success = FALSE; for (rp = mp->first; rp; rp = rp->next) { if ((rp->datasrc->flags & DF_UNAVAILABLE) || !lock_newsrc(rp) || !open_datasrc(rp->datasrc) || !open_newsrc(rp)) { unlock_newsrc(rp); had_trouble = TRUE; } else { rp->datasrc->flags |= DF_ACTIVE; rp->flags |= RF_ACTIVE; had_success = TRUE; } } if (had_trouble) get_anything(); if (!had_success) return FALSE; multirc = mp; #ifdef NO_FILELINKS if (!write_newsrcs(multirc)) get_anything(); #endif return TRUE; } void unuse_multirc(mptr) MULTIRC* mptr; { NEWSRC* rp; if (!mptr) return; write_newsrcs(mptr); for (rp = mptr->first; rp; rp = rp->next) { unlock_newsrc(rp); rp->flags &= ~RF_ACTIVE; rp->datasrc->flags &= ~DF_ACTIVE; } if (ngdata_list) { close_cache(); hashdestroy(newsrc_hash); walk_list(ngdata_list, clear_ngitem, 0); delete_list(ngdata_list); ngdata_list = NULL; first_ng = NULL; last_ng = NULL; ngptr = NULL; current_ng = NULL; recent_ng = NULL; starthere = NULL; sel_page_np = NULL; } ngdata_cnt = 0; newsgroup_cnt = 0; newsgroup_toread = 0; multirc = NULL; } bool use_next_multirc(mptr) MULTIRC* mptr; { register MULTIRC* mp = multirc_ptr(mptr->num); unuse_multirc(mptr); for (;;) { mp = multirc_next(mp); if (!mp) mp = multirc_low(); if (mp == mptr) { use_multirc(mptr); return FALSE; } if (use_multirc(mp)) break; } return TRUE; } bool use_prev_multirc(mptr) MULTIRC* mptr; { register MULTIRC* mp = multirc_ptr(mptr->num); unuse_multirc(mptr); for (;;) { mp = multirc_prev(mp); if (!mp) mp = multirc_high(); if (mp == mptr) { use_multirc(mptr); return FALSE; } if (use_multirc(mp)) break; } return TRUE; } char* multirc_name(mp) register MULTIRC* mp; { char* cp; if (mp->first->next) return ""; if ((cp = rindex(mp->first->name, '/')) != NULL) return cp+1; return mp->first->name; } static bool clear_ngitem(cp, arg) char* cp; int arg; { NGDATA* ncp = (NGDATA*)cp; if (ncp->rcline != NULL) { if (!checkflag) free(ncp->rcline); ncp->rcline = NULL; } return 0; } /* make sure there is no trn out there reading this newsrc */ static bool lock_newsrc(rp) NEWSRC* rp; { long processnum = 0; char* runninghost = "(Unknown)"; char* s; if (checkflag) return TRUE; s = filexp(RCNAME); if (strEQ(rp->name, s)) rp->lockname = savestr(filexp(LOCKNAME)); else { sprintf(buf, RCNAME_INFO, rp->name); rp->infoname = savestr(buf); sprintf(buf, RCNAME_LOCK, rp->name); rp->lockname = savestr(buf); } tmpfp = fopen(rp->lockname,"r"); if (tmpfp != NULL) { if (fgets(buf,LBUFLEN,tmpfp)) { processnum = atol(buf); if (fgets(buf,LBUFLEN,tmpfp) && *buf && *(s = buf + strlen(buf) - 1) == '\n') { *s = '\0'; runninghost = buf; } } fclose(tmpfp); } if (processnum) { #ifndef MSDOS #ifdef VERBOSE IF(verbose) printf("\nThe requested newsrc is locked by process %ld on host %s.\n", processnum, runninghost) FLUSH; ELSE #endif #ifdef TERSE printf("\nNewsrc locked by %ld on host %s.\n",processnum,runninghost) FLUSH; #endif termdown(2); if (strNE(runninghost,localhost)) { #ifdef VERBOSE IF(verbose) printf("\n\ Since that's not the same host as this one (%s), we must\n\ assume that process still exists. To override this check, remove\n\ the lock file: %s\n", localhost, rp->lockname) FLUSH; ELSE #endif #ifdef TERSE printf("\nThis host (%s) doesn't match.\nCan't unlock %s.\n", localhost, rp->lockname) FLUSH; #endif termdown(2); if (bizarre) resetty(); finalize(0); } if (processnum == our_pid) { #ifdef VERBOSE IF(verbose) printf("\n\ Hey, that *my* pid! Your access file is trying to use the same newsrc\n\ more than once.\n") FLUSH; ELSE #endif #ifdef TERSE printf("\nAccess file error (our pid detected).\n") FLUSH; #endif termdown(2); return FALSE; } if (kill(processnum, 0) != 0) { /* Process is apparently gone */ sleep(2); #ifdef VERBOSE IF(verbose) fputs("\n\ That process does not seem to exist anymore. The count of read articles\n\ may be incorrect in the last newsgroup accessed by that other (defunct)\n\ process.\n\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nProcess crashed.\n",stdout) FLUSH; #endif if (lastngname) { #ifdef VERBOSE IF(verbose) printf("(The last newsgroup accessed was %s.)\n\n", lastngname) FLUSH; ELSE #endif #ifdef TERSE printf("(In %s.)\n\n",lastngname) FLUSH; #endif } termdown(2); get_anything(); newline(); } else { #ifdef VERBOSE IF(verbose) printf("\n\ It looks like that process still exists. To override this, remove\n\ the lock file: %s\n", rp->lockname) FLUSH; ELSE #endif #ifdef TERSE printf("\nCan't unlock %s.\n", rp->lockname) FLUSH; #endif termdown(2); if (bizarre) resetty(); finalize(0); } #endif } tmpfp = fopen(rp->lockname,"w"); if (tmpfp == NULL) { printf(cantcreate,rp->lockname) FLUSH; sig_catcher(0); } fprintf(tmpfp,"%ld\n%s\n",our_pid,localhost); fclose(tmpfp); return TRUE; } static void unlock_newsrc(rp) NEWSRC* rp; { safefree0(rp->infoname); if (rp->lockname) { UNLINK(rp->lockname); free(rp->lockname); rp->lockname = NULL; } } static bool open_newsrc(rp) NEWSRC* rp; { register NGDATA* np; NGDATA* prev_np; char* some_buf; long length; FILE* rcfp; HASHDATUM data; /* make sure the .newsrc file exists */ if ((rcfp = fopen(rp->name,"r")) == NULL) { rcfp = fopen(rp->name,"w+"); if (rcfp == NULL) { printf("\nCan't create %s.\n", rp->name) FLUSH; termdown(2); return FALSE; } some_buf = SUBSCRIPTIONS; #ifdef SUPPORT_NNTP if ((rp->datasrc->flags & DF_REMOTE) && nntp_list("SUBSCRIPTIONS",nullstr,0) == 1) { do { fputs(ser_line,rcfp); fputc('\n',rcfp); if (nntp_gets(ser_line, sizeof ser_line) < 0) break; } while (!nntp_at_list_end(ser_line)); } #endif ElseIf (*some_buf && (tmpfp = fopen(filexp(some_buf),"r")) != NULL) { while (fgets(buf,sizeof buf,tmpfp)) fputs(buf,rcfp); fclose(tmpfp); } fseek(rcfp, 0L, 0); } else { /* File exists; if zero length and backup isn't, complain */ if (fstat(fileno(rcfp),&filestat) < 0) { perror(rp->name); return FALSE; } if (filestat.st_size == 0 && stat(rp->oldname,&filestat) >= 0 && filestat.st_size > 0) { printf("Warning: %s is zero length but %s is not.\n", rp->name,rp->oldname); printf("Either recover your newsrc or else remove the backup copy.\n"); termdown(2); return FALSE; } /* unlink backup file name and backup current name */ UNLINK(rp->oldname); #ifndef NO_FILELINKS safelink(rp->name,rp->oldname); #endif } if (!ngdata_list) { /* allocate memory for rc file globals */ ngdata_list = new_list(0, 0, sizeof (NGDATA), 200, 0, init_ngnode); newsrc_hash = hashcreate(3001, rcline_cmp); } if (ngdata_cnt) prev_np = ngdata_ptr(ngdata_cnt-1); else prev_np = NULL; /* read in the .newsrc file */ while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != NULL) { length = len_last_line_got; /* side effect of get_a_line */ if (length <= 1) /* only a newline??? */ continue; np = ngdata_ptr(ngdata_cnt++); if (prev_np) prev_np->next = np; else first_ng = np; np->prev = prev_np; prev_np = np; np->rc = rp; newsgroup_cnt++; if (some_buf[length-1] == '\n') some_buf[--length] = '\0'; /* wipe out newline */ if (some_buf == buf) np->rcline = savestr(some_buf); /* make semipermanent copy */ else { /*NOSTRICT*/ #ifndef lint some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1)); #endif np->rcline = some_buf; } if (*some_buf == ' ' || *some_buf == '\t' || strnEQ(some_buf,"options",7)) { /* non-useful line? */ np->toread = TR_JUNK; np->subscribechar = ' '; np->numoffset = 0; continue; } parse_rcline(np); data = hashfetch(newsrc_hash, np->rcline, np->numoffset - 1); if (data.dat_ptr) { np->toread = TR_IGNORE; continue; } if (np->subscribechar == NEGCHAR) { np->toread = TR_UNSUB; sethash(np); continue; } newsgroup_toread++; /* now find out how much there is to read */ if (!inlist(buf) || (suppress_cn && foundany && !paranoid)) np->toread = TR_NONE; /* no need to calculate now */ else set_toread(np, ST_LAX); if (np->toread > TR_NONE) { /* anything unread? */ if (!foundany) { starthere = np; foundany = TRUE; /* remember that fact*/ } if (suppress_cn) { /* if no listing desired */ if (checkflag) /* if that is all they wanted */ finalize(1); /* then bomb out */ } else { #ifdef VERBOSE IF(verbose) printf("Unread news in %-40s %5ld article%s\n", np->rcline,(long)np->toread,PLURAL(np->toread)) FLUSH; ELSE #endif #ifdef TERSE printf("%s: %ld article%s\n", np->rcline,(long)np->toread,PLURAL(np->toread)) FLUSH; #endif termdown(1); if (int_count) { countdown = 1; int_count = 0; } if (countdown) { if (!--countdown) { fputs("etc.\n",stdout) FLUSH; if (checkflag) finalize(1); suppress_cn = TRUE; } } } } sethash(np); } if (prev_np) { prev_np->next = NULL; last_ng = prev_np; } fclose(rcfp); /* close .newsrc */ #ifdef NO_FILELINKS UNLINK(rp->oldname); RENAME(rp->name,rp->oldname); rp->flags |= RF_RCCHANGED; #endif if (rp->infoname) { if ((tmpfp = fopen(rp->infoname,"r")) != NULL) { if (fgets(buf,sizeof buf,tmpfp) != NULL) { long actnum, descnum; char* s; buf[strlen(buf)-1] = '\0'; if ((s = index(buf, ':')) != NULL && s[1] == ' ' && s[2]) { safefree0(lastngname); lastngname = savestr(s+2); } if (fscanf(tmpfp,"New-Group-State: %ld,%ld,%ld", &lastnewtime,&actnum,&descnum) == 3) { rp->datasrc->act_sf.recent_cnt = actnum; rp->datasrc->desc_sf.recent_cnt = descnum; } } } } else { readlast(); #ifdef SUPPORT_NNTP if (rp->datasrc->flags & DF_REMOTE) { rp->datasrc->act_sf.recent_cnt = lastactsiz; rp->datasrc->desc_sf.recent_cnt = lastextranum; } else #endif { rp->datasrc->act_sf.recent_cnt = lastextranum; rp->datasrc->desc_sf.recent_cnt = 0; } } rp->datasrc->lastnewgrp = lastnewtime; if (paranoid && !checkflag) cleanup_newsrc(rp); return TRUE; } /* Initialize the memory for an entire node's worth of article's */ static void init_ngnode(list, node) LIST* list; LISTNODE* node; { register ART_NUM i; register NGDATA* np; bzero(node->data, list->items_per_node * list->item_size); for (i = node->low, np = (NGDATA*)node->data; i <= node->high; i++, np++) np->num = i; } static void parse_rcline(np) register NGDATA* np; { char* s; int len; for (s=np->rcline; *s && *s!=':' && *s!=NEGCHAR && !isspace(*s); s++) ; len = s - np->rcline; if ((!*s || isspace(*s)) && !checkflag) { #ifndef lint np->rcline = saferealloc(np->rcline,(MEM_SIZE)len + 3); #endif s = np->rcline + len; strcpy(s, ": "); } if (*s == ':' && s[1] && s[2] == '0') { np->flags |= NF_UNTHREADED; s[2] = '1'; } np->subscribechar = *s; /* salt away the : or ! */ np->numoffset = len + 1; /* remember where the numbers are */ *s = '\0'; /* null terminate newsgroup name */ } void abandon_ng(np) NGDATA* np; { char* some_buf = NULL; FILE* rcfp; /* open newsrc backup copy and try to find the prior value for the group. */ if ((rcfp = fopen(np->rc->oldname, "r")) != NULL) { int length = np->numoffset - 1; while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != NULL) { if (len_last_line_got <= 0) continue; some_buf[len_last_line_got-1] = '\0'; /* wipe out newline */ if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR) && strnEQ(np->rcline, some_buf, length)) { break; } if (some_buf != buf) free(some_buf); } fclose(rcfp); } else if (errno != ENOENT) { printf("Unable to open %s.\n", np->rc->oldname) FLUSH; termdown(1); return; } if (some_buf == NULL) { some_buf = np->rcline + np->numoffset; if (*some_buf == ' ') some_buf++; *some_buf = '\0'; np->abs1st = 0; /* force group to be re-calculated */ } else { free(np->rcline); if (some_buf == buf) np->rcline = savestr(some_buf); else { /*NOSTRICT*/ #ifndef lint some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got)); #endif /* lint */ np->rcline = some_buf; } } parse_rcline(np); if (np->subscribechar == NEGCHAR) np->subscribechar = ':'; np->rc->flags |= RF_RCCHANGED; set_toread(np, ST_LAX); } /* try to find or add an explicitly specified newsgroup */ /* returns TRUE if found or added, FALSE if not. */ /* assumes that we are chdir'ed to NEWSSPOOL */ bool get_ng(what, flags) char* what; int flags; { char* ntoforget; char promptbuf[128]; int autosub; #ifdef VERBOSE IF(verbose) ntoforget = "Type n to forget about this newsgroup.\n"; ELSE #endif #ifdef TERSE ntoforget = "n to forget it.\n"; #endif if (index(what,'/')) { dingaling(); printf("\nBad newsgroup name.\n") FLUSH; termdown(2); check_fuzzy_match: #ifdef EDIT_DISTANCE if (fuzzyGet && (flags & GNG_FUZZY)) { flags &= ~GNG_FUZZY; if (find_close_match()) what = ngname; else return FALSE; } else #endif return FALSE; } set_ngname(what); ngptr = find_ng(ngname); if (ngptr == NULL) { /* not in .newsrc? */ NEWSRC* rp; for (rp = multirc->first; rp; rp = rp->next) { if (!ALLBITS(rp->flags, RF_ADD_GROUPS | RF_ACTIVE)) continue; /*$$ this may scan a datasrc multiple times... */ if (find_actgrp(rp->datasrc,buf,ngname,ngname_len,(ART_NUM)0)) break; /*$$ let them choose which server */ } if (!rp) { dingaling(); #ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo %s!\n",ngname) FLUSH; #endif termdown(2); if (novice_delays) sleep(2); goto check_fuzzy_match; } if (mode != 'i' || !(autosub = auto_subscribe(ngname))) autosub = addnewbydefault; if (autosub) { if (append_unsub) { printf("(Adding %s to end of your .newsrc %ssubscribed)\n", ngname, (autosub == ADDNEW_SUB)? nullstr : "un") FLUSH; termdown(1); ngptr = add_newsgroup(rp, ngname, autosub); } else { if (autosub == ADDNEW_SUB) { printf("(Subscribing to %s)\n", ngname) FLUSH; termdown(1); ngptr = add_newsgroup(rp, ngname, autosub); } else { printf("(Ignoring %s)\n", ngname) FLUSH; termdown(1); return FALSE; } } flags &= ~GNG_RELOC; } else { #ifdef VERBOSE IF(verbose) sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname); ELSE #endif #ifdef TERSE sprintf(promptbuf,"\nSubscribe %s?",ngname); #endif reask_add: in_char(promptbuf,'A',"ynYN"); #ifdef VERIFY printcmd(); #endif newline(); if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { printf("Type y or SP to subscribe to %s.\n\ Type Y to subscribe to this and all remaining new groups.\n\ Type N to leave all remaining new groups unsubscribed.\n", ngname) FLUSH; termdown(3); } ELSE #endif #ifdef TERSE { fputs("\ y or SP to subscribe, Y to subscribe all new groups, N to unsubscribe all\n", stdout) FLUSH; termdown(1); } #endif fputs(ntoforget,stdout) FLUSH; termdown(1); goto reask_add; } else if (*buf == 'n' || *buf == 'q') { if (append_unsub) ngptr = add_newsgroup(rp, ngname, NEGCHAR); return FALSE; } else if (*buf == 'y') { ngptr = add_newsgroup(rp, ngname, ':'); flags |= GNG_RELOC; } else if (*buf == 'Y') { addnewbydefault = ADDNEW_SUB; if (append_unsub) printf("(Adding %s to end of your .newsrc subscribed)\n", ngname) FLUSH; else printf("(Subscribing to %s)\n", ngname) FLUSH; termdown(1); ngptr = add_newsgroup(rp, ngname, ':'); flags &= ~GNG_RELOC; } else if (*buf == 'N') { addnewbydefault = ADDNEW_UNSUB; if (append_unsub) { printf("(Adding %s to end of your .newsrc unsubscribed)\n", ngname) FLUSH; termdown(1); ngptr = add_newsgroup(rp, ngname, NEGCHAR); flags &= ~GNG_RELOC; } else { printf("(Ignoring %s)\n", ngname) FLUSH; termdown(1); return FALSE; } } else { fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); goto reask_add; } } } else if (mode == 'i') /* adding new groups during init? */ return FALSE; else if (ngptr->subscribechar == NEGCHAR) {/* unsubscribed? */ #ifdef VERBOSE IF(verbose) sprintf(promptbuf, "\nNewsgroup %s is unsubscribed -- resubscribe?",ngname) FLUSH; ELSE #endif #ifdef TERSE sprintf(promptbuf,"\nResubscribe %s?",ngname) FLUSH; #endif reask_unsub: in_char(promptbuf,'R',"yn"); #ifdef VERIFY printcmd(); #endif newline(); if (*buf == 'h') { #ifdef VERBOSE IF(verbose) printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH; ELSE #endif #ifdef TERSE fputs("y or SP to resubscribe.\n",stdout) FLUSH; #endif fputs(ntoforget,stdout) FLUSH; termdown(2); goto reask_unsub; } else if (*buf == 'n' || *buf == 'q') { return FALSE; } else if (*buf == 'y') { register char* cp; cp = ngptr->rcline + ngptr->numoffset; ngptr->flags = (*cp && cp[1] == '0' ? NF_UNTHREADED : 0); ngptr->subscribechar = ':'; ngptr->rc->flags |= RF_RCCHANGED; flags &= ~GNG_RELOC; } else { fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); goto reask_unsub; } } /* now calculate how many unread articles in newsgroup */ set_toread(ngptr, ST_STRICT); #ifdef RELOCATE if (flags & GNG_RELOC) { if (!relocate_newsgroup(ngptr,-1)) return FALSE; } #endif return ngptr->toread >= TR_NONE; } /* add a newsgroup to the newsrc file (eventually) */ static NGDATA* add_newsgroup(rp, ngn, c) NEWSRC* rp; char* ngn; char_int c; { register NGDATA* np; np = ngdata_ptr(ngdata_cnt++); np->prev = last_ng; if (last_ng) last_ng->next = np; else first_ng = np; np->next = NULL; last_ng = np; newsgroup_cnt++; np->rc = rp; np->numoffset = strlen(ngn) + 1; np->rcline = safemalloc((MEM_SIZE)(np->numoffset + 2)); strcpy(np->rcline,ngn); /* and copy over the name */ strcpy(np->rcline + np->numoffset, " "); np->subscribechar = c; /* subscribe or unsubscribe */ if (c != NEGCHAR) newsgroup_toread++; np->toread = TR_NONE; /* just for prettiness */ sethash(np); /* so we can find it again */ rp->flags |= RF_RCCHANGED; return np; } #ifdef RELOCATE bool relocate_newsgroup(move_np,newnum) NGDATA* move_np; NG_NUM newnum; { NGDATA* np; int i; char* dflt = (move_np!=current_ng ? "$^.Lq" : "$^Lq"); int save_sort = sel_sort; if (sel_newsgroupsort != SS_NATURAL) { if (newnum < 0) { /* ask if they want to keep the current order */ in_char("Sort newsrc(s) using current sort order?", 'D', "yn"); /*$$ !'D' */ #ifdef VERIFY printcmd(); #endif newline(); if (*buf == 'y') set_selector(SM_NEWSGROUP, SS_NATURAL); else { sel_sort = SS_NATURAL; sel_direction = 1; sort_newsgroups(); } } else { sel_sort = SS_NATURAL; sel_direction = 1; sort_newsgroups(); } } starthere = NULL; /* Disable this optimization */ if (move_np != last_ng) { if (move_np->prev) move_np->prev->next = move_np->next; else first_ng = move_np->next; move_np->next->prev = move_np->prev; move_np->prev = last_ng; move_np->next = NULL; last_ng->next = move_np; last_ng = move_np; } /* Renumber the groups according to current order */ for (np = first_ng, i = 0; np; np = np->next, i++) np->num = i; move_np->rc->flags |= RF_RCCHANGED; if (newnum < 0) { reask_reloc: unflush_output(); /* disable any ^O in effect */ #ifdef VERBOSE IF(verbose) printf("\nPut newsgroup where? [%s] ", dflt); ELSE #endif #ifdef TERSE printf("\nPut where? [%s] ", dflt); #endif fflush(stdout); termdown(1); reinp_reloc: eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') /* if return from stop signal */ goto reask_reloc; /* give them a prompt again */ setdef(buf,dflt); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { printf("\n\n\ Type ^ to put the newsgroup first (position 0).\n\ Type $ to put the newsgroup last (position %d).\n", newsgroup_cnt-1); printf("\ Type . to put it before the current newsgroup.\n"); printf("\ Type -newsgroup name to put it before that newsgroup.\n\ Type +newsgroup name to put it after that newsgroup.\n\ Type a number between 0 and %d to put it at that position.\n", newsgroup_cnt-1); printf("\ Type L for a listing of newsgroups and their positions.\n\ Type q to abort the current action.\n") FLUSH; } ELSE #endif #ifdef TERSE { printf("\n\n\ ^ to put newsgroup first (pos 0).\n\ $ to put last (pos %d).\n", newsgroup_cnt-1); printf("\ . to put before current newsgroup.\n"); printf("\ -newsgroup to put before newsgroup.\n\ +newsgroup to put after.\n\ number in 0-%d to put at that pos.\n", newsgroup_cnt-1); printf("\ L for list of newsrc.\n\ q to abort\n") FLUSH; } #endif termdown(10); goto reask_reloc; } else if (*buf == 'q') return FALSE; else if (*buf == 'L') { newline(); list_newsgroups(); goto reask_reloc; } else if (isdigit(*buf)) { if (!finish_command(TRUE)) /* get rest of command */ goto reinp_reloc; newnum = atol(buf); if (newnum < 0) newnum = 0; if (newnum >= newsgroup_cnt) newnum = newsgroup_cnt-1; } else if (*buf == '^') { newline(); newnum = 0; } else if (*buf == '$') { newnum = newsgroup_cnt-1; } else if (*buf == '.') { newline(); newnum = current_ng->num; } else if (*buf == '-' || *buf == '+') { if (!finish_command(TRUE)) /* get rest of command */ goto reinp_reloc; np = find_ng(buf+1); if (np == NULL) { fputs("Not found.",stdout) FLUSH; goto reask_reloc; } newnum = np->num; if (*buf == '+') newnum++; } else { printf("\n%s",hforhelp) FLUSH; termdown(2); settle_down(); goto reask_reloc; } } if (newnum < newsgroup_cnt-1) { for (np = first_ng; np; np = np->next) if (np->num >= newnum) break; if (!np || np == move_np) return FALSE; /* This can't happen... */ last_ng = move_np->prev; last_ng->next = NULL; move_np->prev = np->prev; move_np->next = np; if (np->prev) np->prev->next = move_np; else first_ng = move_np; np->prev = move_np; move_np->num = newnum++; for (; np; np = np->next, newnum++) np->num = newnum; } if (sel_newsgroupsort != SS_NATURAL) { sel_sort = sel_newsgroupsort; sort_newsgroups(); sel_sort = save_sort; } return TRUE; } #endif /* RELOCATE */ /* List out the newsrc with annotations */ void list_newsgroups() { register NGDATA* np; register NG_NUM i; char tmpbuf[2048]; static char* status[] = {"(READ)","(UNSUB)","(DUP)","(BOGUS)","(JUNK)"}; page_start(); print_lines(" # Status Newsgroup\n",STANDOUT); for (np = first_ng, i = 0; np && !int_count; np = np->next, i++) { if (np->toread >= 0) set_toread(np, ST_LAX); *(np->rcline + np->numoffset - 1) = np->subscribechar; if (np->toread > 0) sprintf(tmpbuf,"%3d %6ld ",i,(long)np->toread); else sprintf(tmpbuf,"%3d %7s ",i,status[-np->toread]); safecpy(tmpbuf+13, np->rcline, sizeof tmpbuf - 13); *(np->rcline + np->numoffset - 1) = '\0'; if (print_lines(tmpbuf,NOMARKING) != 0) break; } int_count = 0; } /* find a newsgroup in any newsrc */ NGDATA* find_ng(ngnam) char* ngnam; { HASHDATUM data; data = hashfetch(newsrc_hash, ngnam, strlen(ngnam)); return (NGDATA*)data.dat_ptr; } void cleanup_newsrc(rp) NEWSRC* rp; { register NGDATA* np; register NG_NUM bogosity = 0; #ifdef VERBOSE IF(verbose) printf("Checking out '%s' -- hang on a second...\n",rp->name) FLUSH; ELSE #endif #ifdef TERSE printf("Checking '%s' -- hang on...\n",rp->name) FLUSH; #endif termdown(1); for (np = first_ng; np; np = np->next) { /*#ifdef CHECK_ALL_BOGUS $$ what is this? */ if (np->toread >= TR_UNSUB) set_toread(np, ST_LAX); /* this may reset the group or declare it bogus */ /*#endif*/ if (np->toread == TR_BOGUS) bogosity++; } for (np = last_ng; np && np->toread == TR_BOGUS; np = np->prev) bogosity--; /* discount already moved ones */ if (newsgroup_cnt > 5 && bogosity > newsgroup_cnt / 2) { fputs( "It looks like the active file is messed up. Contact your news administrator,\n\ ",stdout); fputs( "leave the \"bogus\" groups alone, and they may come back to normal. Maybe.\n\ ",stdout) FLUSH; termdown(2); } #ifdef RELOCATE else if (bogosity) { NGDATA* prev_np; #ifdef VERBOSE IF(verbose) printf("Moving bogus newsgroups to the end of '%s'.\n",rp->name) FLUSH; ELSE #endif #ifdef TERSE fputs("Moving boguses to the end.\n",stdout) FLUSH; #endif termdown(1); while (np) { prev_np = np->prev; if (np->toread == TR_BOGUS) relocate_newsgroup(np, newsgroup_cnt-1); np = prev_np; } rp->flags |= RF_RCCHANGED; #ifdef DELBOGUS reask_bogus: in_char("Delete bogus newsgroups?", 'D', "ny"); #ifdef VERIFY printcmd(); #endif newline(); if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { fputs("\ Type y to delete bogus newsgroups.\n\ Type n or SP to leave them at the end in case they return.\n\ ",stdout) FLUSH; termdown(2); } ELSE #endif #ifdef TERSE { fputs("y to delete, n to keep\n",stdout) FLUSH; termdown(1); } #endif goto reask_bogus; } else if (*buf == 'n' || *buf == 'q') ; else if (*buf == 'y') { for (np = last_ng; np && np->toread == TR_BOGUS; np = np->prev) { hashdelete(newsrc_hash, np->rcline, np->numoffset - 1); clear_ngitem((char*)np,0); newsgroup_cnt--; } rp->flags |= RF_RCCHANGED; /*$$ needed? */ last_ng = np; if (np) np->next = NULL; else first_ng = NULL; if (current_ng && !current_ng->rcline) current_ng = first_ng; if (recent_ng && !recent_ng->rcline) recent_ng = first_ng; if (ngptr && !ngptr->rcline) ngptr = first_ng; if (sel_page_np && !sel_page_np->rcline) sel_page_np = NULL; } else { fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); goto reask_bogus; } #endif /* DELBOGUS */ } #else /* !RELOCATE */ #ifdef VERBOSE IF(verbose) printf("You should edit bogus newsgroups out of '%s'.\n",rp->name) FLUSH; ELSE #endif #ifdef TERSE printf("Edit boguses from '%s'.\n",rp->name) FLUSH; #endif termdown(1); #endif /* !RELOCATE */ paranoid = FALSE; } /* make an entry in the hash table for the current newsgroup */ void sethash(np) NGDATA* np; { HASHDATUM data; data.dat_ptr = (char*)np; data.dat_len = np->numoffset - 1; hashstore(newsrc_hash, np->rcline, data.dat_len, data); } static int rcline_cmp(key, keylen, data) char* key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, ((NGDATA*)data.dat_ptr)->rcline, keylen); } /* checkpoint the newsrc(s) */ void checkpoint_newsrcs() { #ifdef DEBUG if (debug & DEB_CHECKPOINTING) { fputs("(ckpt)",stdout); fflush(stdout); } #endif if (doing_ng) bits_to_rc(); /* do not restore M articles */ if (!write_newsrcs(multirc)) get_anything(); #ifdef DEBUG if (debug & DEB_CHECKPOINTING) { fputs("(done)",stdout); fflush(stdout); } #endif } /* write out the (presumably) revised newsrc(s) */ bool write_newsrcs(mptr) MULTIRC* mptr; { NEWSRC* rp; register NGDATA* np; int save_sort = sel_sort; FILE* rcfp; bool total_success = TRUE; if (!mptr) return TRUE; if (sel_newsgroupsort != SS_NATURAL) { sel_sort = SS_NATURAL; sel_direction = 1; sort_newsgroups(); } for (rp = mptr->first; rp; rp = rp->next) { if (!(rp->flags & RF_ACTIVE)) continue; if (rp->infoname) { if ((tmpfp = fopen(rp->infoname, "w")) != NULL) { fprintf(tmpfp,"Last-Group: %s\nNew-Group-State: %ld,%ld,%ld\n", ngname? ngname : nullstr,rp->datasrc->lastnewgrp, rp->datasrc->act_sf.recent_cnt, rp->datasrc->desc_sf.recent_cnt); fclose(tmpfp); } } else { readlast(); #ifdef SUPPORT_NNTP if (rp->datasrc->flags & DF_REMOTE) { lastactsiz = rp->datasrc->act_sf.recent_cnt; lastextranum = rp->datasrc->desc_sf.recent_cnt; } else #endif lastextranum = rp->datasrc->act_sf.recent_cnt; lastnewtime = rp->datasrc->lastnewgrp; writelast(); } if (!(rp->flags & RF_RCCHANGED)) continue; rcfp = fopen(rp->newname, "w"); if (rcfp == NULL) { printf(cantrecreate,rp->name) FLUSH; total_success = FALSE; continue; } #ifndef MSDOS if (stat(rp->name,&filestat)>=0) { /* preserve permissions */ chmod(rp->newname,filestat.st_mode&0666); chown(rp->newname,filestat.st_uid,filestat.st_gid); } #endif /* write out each line*/ for (np = first_ng; np; np = np->next) { register char* delim; if (np->rc != rp) continue; if (np->numoffset) { delim = np->rcline + np->numoffset - 1; *delim = np->subscribechar; if ((np->flags & NF_UNTHREADED) && delim[2] == '1') delim[2] = '0'; } else delim = NULL; #ifdef DEBUG if (debug & DEB_NEWSRC_LINE) { printf("%s\n",np->rcline) FLUSH; termdown(1); } #endif if (fprintf(rcfp,"%s\n",np->rcline) < 0) { fclose(rcfp); /* close new newsrc */ goto write_error; } if (delim) { *delim = '\0'; /* might still need this line */ if ((np->flags & NF_UNTHREADED) && delim[2] == '0') delim[2] = '1'; } } fflush(rcfp); /* fclose is the only sure test for full disks via NFS */ if (ferror(rcfp)) { fclose(rcfp); goto write_error; } if (fclose(rcfp) == EOF) { write_error: printf(cantrecreate,rp->name) FLUSH; UNLINK(rp->newname); total_success = FALSE; continue; } rp->flags &= ~RF_RCCHANGED; UNLINK(rp->name); RENAME(rp->newname,rp->name); } if (sel_newsgroupsort != SS_NATURAL) { sel_sort = sel_newsgroupsort; sort_newsgroups(); sel_sort = save_sort; } return total_success; } void get_old_newsrcs(mptr) MULTIRC* mptr; { NEWSRC* rp; if (mptr) { for (rp = mptr->first; rp; rp = rp->next) { if (rp->flags & RF_ACTIVE) { UNLINK(rp->newname); RENAME(rp->name,rp->newname); RENAME(rp->oldname,rp->name); } } } } trn-4.0-test77/rcstuff.h0000644000000000000000000000506207113133016013634 0ustar rootroot/* rcstuff.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #define TR_ONE ((ART_UNREAD) 1) #define TR_NONE ((ART_UNREAD) 0) #define TR_UNSUB ((ART_UNREAD) -1) /* keep this one as -1, some tests use >= TR_UNSUB */ #define TR_IGNORE ((ART_UNREAD) -2) #define TR_BOGUS ((ART_UNREAD) -3) #define TR_JUNK ((ART_UNREAD) -4) #define NF_SEL 0x01 #define NF_DEL 0x02 #define NF_DELSEL 0x04 #define NF_INCLUDED 0x10 #define NF_UNTHREADED 0x40 #define NF_VISIT 0x80 #define ADDNEW_SUB ':' #define ADDNEW_UNSUB '!' #define GNG_RELOC 0x0001 #define GNG_FUZZY 0x0002 EXT HASHTABLE* newsrc_hash INIT(NULL); struct newsrc { NEWSRC* next; DATASRC* datasrc; char* name; /* the name of the associated newsrc */ char* oldname; /* the backup of the newsrc */ char* newname; /* our working newsrc file */ char* infoname; /* the time/size info file */ char* lockname; /* the lock file we created */ int flags; }; #define RF_ADD_NEWGROUPS 0x0001 #define RF_ADD_GROUPS 0x0002 #define RF_OPEN 0x0100 #define RF_ACTIVE 0x0200 #define RF_RCCHANGED 0x0400 struct multirc { NEWSRC* first; int num; int flags; }; #define MF_SEL 0x0001 #define MF_INCLUDED 0x0010 EXT MULTIRC* sel_page_mp; EXT MULTIRC* sel_next_mp; #define multirc_ptr(n) ((MULTIRC*)listnum2listitem(multirc_list,(long)(n))) #define multirc_low() ((MULTIRC*)listnum2listitem(multirc_list,existing_listnum(multirc_list,0L,1))) #define multirc_high() ((MULTIRC*)listnum2listitem(multirc_list,existing_listnum(multirc_list,multirc_list->high,-1))) #define multirc_next(p) ((MULTIRC*)next_listitem(multirc_list,(char*)(p))) #define multirc_prev(p) ((MULTIRC*)prev_listitem(multirc_list,(char*)(p))) EXT LIST* multirc_list; /* a list of all MULTIRCs */ EXT MULTIRC* multirc; /* the current MULTIRC */ EXT bool paranoid INIT(FALSE); /* did we detect some inconsistency in .newsrc? */ EXT int addnewbydefault INIT(0); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool rcstuff_init _((void)); NEWSRC* new_newsrc _((char*,char*,char*)); bool use_multirc _((MULTIRC*)); void unuse_multirc _((MULTIRC*)); bool use_next_multirc _((MULTIRC*)); bool use_prev_multirc _((MULTIRC*)); char* multirc_name _((MULTIRC*)); void abandon_ng _((NGDATA*)); bool get_ng _((char*,int)); #ifdef RELOCATE bool relocate_newsgroup _((NGDATA*,NG_NUM)); #endif void list_newsgroups _((void)); NGDATA* find_ng _((char*)); void cleanup_newsrc _((NEWSRC*)); void sethash _((NGDATA*)); void checkpoint_newsrcs _((void)); bool write_newsrcs _((MULTIRC*)); void get_old_newsrcs _((MULTIRC*)); trn-4.0-test77/rcstuff.ih0000644000000000000000000000130307113133016013777 0ustar rootroot/* rcstuff.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ #define RI_ID 1 #define RI_NEWSRC 2 #define RI_ADDGROUPS 3 INI_WORDS rcgroups_ini[] = { { 0, "RCGROUPS", 0 }, { 0, "ID", 0 }, { 0, "Newsrc", 0 }, { 0, "Add Groups", 0 }, { 0, 0, 0 } }; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static bool clear_ngitem _((char*,int)); static bool lock_newsrc _((NEWSRC*)); static void unlock_newsrc _((NEWSRC*)); static bool open_newsrc _((NEWSRC*)); static void init_ngnode _((LIST*,LISTNODE*)); static void parse_rcline _((NGDATA*)); static NGDATA* add_newsgroup _((NEWSRC*,char*,char_int)); static int rcline_cmp _((char*,int,HASHDATUM)); trn-4.0-test77/respond.c0000644000000000000000000006067307232647706013660 0ustar rootroot/* respond.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "intrp.h" #include "hash.h" #include "cache.h" #include "head.h" #include "term.h" #include "mime.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "ng.h" #include "env.h" #include "util.h" #include "util2.h" #include "trn.h" #include "art.h" #include "artio.h" #include "search.h" #include "artstate.h" #include "final.h" #include "decode.h" #include "uudecode.h" #include "charsubst.h" #include "INTERN.h" #include "respond.h" #include "respond.ih" static char nullart[] = "\nEmpty article.\n"; void respond_init() { } int save_article() { bool_int use_pref; register char* s; register char* c; char altbuf[CBUFLEN]; int i; bool interactive = (buf[1] == FINISHCMD); char cmd = *buf; if (!finish_command(interactive)) /* get rest of command */ return SAVE_ABORT; if ((use_pref = isupper(cmd)) != 0) cmd = tolower(cmd); parseheader(art); mime_SetArticle(); clear_artbuf(); savefrom = (cmd == 'w' || cmd == 'e')? htype[PAST_HEADER].minpos : 0; if (artopen(art,savefrom) == NULL) { #ifdef VERBOSE IF(verbose) fputs("\nCan't save an empty article.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif termdown(2); return SAVE_DONE; } if (chdir(cwd)) { printf(nocd,cwd) FLUSH; sig_catcher(0); } if (cmd == 'e') { /* is this an extract command? */ static bool custom_extract = FALSE; char* cmdstr; int partOpt = 0, totalOpt = 0; s = buf+1; /* skip e */ while (*s == ' ') s++; /* skip leading spaces */ if (*s == '-' && isdigit(s[1])) { partOpt = atoi(s+1); do s++; while (isdigit(*s)); if (*s == '/') { totalOpt = atoi(s+1); do s++; while (isdigit(*s)); while (*s == ' ') s++; } else totalOpt = partOpt; } safecpy(altbuf,filexp(s),sizeof altbuf); s = altbuf; if (*s) { cmdstr = cpytill(buf,s,'|'); /* check for | */ s = buf + strlen(buf)-1; while (*s == ' ') s--; /* trim trailing spaces */ *++s = '\0'; if (*cmdstr) { s = cmdstr+1; /* skip | */ while (*s == ' ') s++; if (*s) { /* if new command, use it */ safefree(extractprog); extractprog = savestr(s); /* put extracter in %e */ } else cmdstr = extractprog; } else cmdstr = NULL; s = buf; } else { if (extractdest) strcpy(s, extractdest); if (custom_extract) cmdstr = extractprog; else cmdstr = NULL; } custom_extract = (cmdstr != 0); if (FILE_REF(s) != '/') { /* relative path? */ c = (s==buf ? altbuf : buf); interp(c, (sizeof buf), getval("SAVEDIR",SAVEDIR)); if (makedir(c,MD_DIR)) /* ensure directory exists */ strcpy(c,cwd); if (*s) { while (*c) c++; *c++ = '/'; strcpy(c,s); /* add filename */ } s = (s==buf ? altbuf : buf); } if (FILE_REF(s) != '/') { /* path still relative? */ c = (s==buf ? altbuf : buf); sprintf(c, "%s/%s", cwd, s); s = c; /* absolutize it */ } safefree(extractdest); s = extractdest = savestr(s); /* make it handy for %E */ if (makedir(s, MD_DIR)) { /* ensure directory exists */ int_count++; return SAVE_DONE; } if (chdir(s)) { printf(nocd,s) FLUSH; sig_catcher(0); } c = trn_getwd(buf, sizeof(buf)); /* simplify path for output */ if (custom_extract) { printf("Extracting article into %s using %s\n",c,extractprog) FLUSH; termdown(1); interp(cmd_buf, sizeof cmd_buf, getval("CUSTOMSAVER",CUSTOMSAVER)); invoke(cmd_buf, (char*)NULL); } else if (is_mime) { printf("Extracting MIME article into %s:\n", c) FLUSH; termdown(1); mime_DecodeArticle(FALSE); } else { char* filename; int part, total; int decode_type = 0; int cnt = 0; /* Scan subject for filename and part number information */ filename = decode_subject(art, &part, &total); if (partOpt) part = partOpt; if (totalOpt) total = totalOpt; for (artpos = savefrom; readart(art_line,sizeof art_line) != NULL; artpos = tellart()) { if (*art_line <= ' ') continue; /* Ignore empty or initially-whitespace lines */ if (((*art_line == '#' || *art_line == ':') && (strnEQ(art_line+1, "! /bin/sh", 9) || strnEQ(art_line+1, "!/bin/sh", 8) || strnEQ(art_line+2, "This is ", 8))) #if 0 || strnEQ(art_line, "sed ", 4) || strnEQ(art_line, "cat ", 4) || strnEQ(art_line, "echo ", 5) #endif ) { savefrom = artpos; decode_type = 1; break; } else if (uue_prescan(art_line,&filename,&part,&total)) { savefrom = artpos; seekart(savefrom); decode_type = 2; break; } else if (++cnt == 300) break; }/* for */ switch (decode_type) { case 1: printf("Extracting shar into %s:\n", c) FLUSH; termdown(1); interp(cmd_buf,(sizeof cmd_buf),getval("SHARSAVER",SHARSAVER)); invoke(cmd_buf, (char*)NULL); break; case 2: printf("Extracting uuencoded file into %s:\n", c) FLUSH; termdown(1); mime_section->type = IMAGE_MIME; safefree(mime_section->filename); mime_section->filename = filename? savestr(filename) : NULL; mime_section->encoding = MENCODE_UUE; mime_section->part = part; mime_section->total = total; if (!decode_piece((MIMECAP_ENTRY*)NULL,(char*)NULL) && *msg) { newline(); fputs(msg,stdout); } newline(); break; default: printf("Unable to determine type of file.\n") FLUSH; termdown(1); break; } }/* if */ } else if ((s = index(buf,'|')) != NULL) { /* is it a pipe command? */ s++; /* skip the | */ while (*s == ' ') s++; safecpy(altbuf,filexp(s),sizeof altbuf); safefree(savedest); savedest = savestr(altbuf); #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) nntp_finishbody(FB_SILENT); #endif interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER)); /* then set up for command */ termlib_reset(); resetty(); /* restore tty state */ if (use_pref) /* use preferred shell? */ doshell((char*)NULL,cmd_buf); /* do command with it */ else doshell(sh,cmd_buf); /* do command with sh */ noecho(); /* and stop echoing */ crmode(); /* and start cbreaking */ termlib_init(); } else { /* normal save */ bool there, mailbox; char* savename = getval("SAVENAME",SAVENAME); s = buf+1; /* skip s or S */ if (*s == '-') { /* if they are confused, skip - also */ #ifdef VERBOSE IF(verbose) fputs("Warning: '-' ignored. This isn't readnews.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("'-' ignored.\n",stdout) FLUSH; #endif termdown(1); s++; } for (; *s == ' '; s++); /* skip spaces */ safecpy(altbuf,filexp(s),sizeof altbuf); s = altbuf; if (!FILE_REF(s)) { interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR)); if (makedir(buf,MD_DIR)) /* ensure directory exists */ strcpy(buf,cwd); if (*s) { for (c = buf; *c; c++) ; *c++ = '/'; strcpy(c,s); /* add filename */ } s = buf; } for (i = 0; (there = stat(s,&filestat) >= 0) && S_ISDIR(filestat.st_mode); i++) { /* is it a directory? */ c = (s+strlen(s)); *c++ = '/'; /* put a slash before filename */ interp(c, s==buf?(sizeof buf):(sizeof altbuf), i ? "News" : savename ); /* generate a default name somehow or other */ } makedir(s,MD_FILE); if (FILE_REF(s) != '/') { /* relative path? */ c = (s==buf ? altbuf : buf); sprintf(c, "%s/%s", cwd, s); s = c; /* absolutize it */ } safefree(savedest); s = savedest = savestr(s); /* doesn't move any more */ /* make it handy for %b */ tmpfp = NULL; if (!there) { if (mbox_always) mailbox = TRUE; else if (norm_always) mailbox = FALSE; else { char* dflt = (instr(savename,"%a", TRUE) ? "nyq" : "ynq"); sprintf(cmd_buf, "\nFile %s doesn't exist--\n use mailbox format?",s); reask_save: in_char(cmd_buf, 'M', dflt); newline(); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) printf("\n\ Type y to create %s as a mailbox.\n\ Type n to create it as a normal file.\n\ Type q to abort the save.\n\ ",s) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ y to create mailbox.\n\ n to create normal file.\n\ q to abort.\n\ ",stdout) FLUSH; #endif termdown(4); goto reask_save; } else if (*buf == 'n') { mailbox = FALSE; } else if (*buf == 'y') { mailbox = TRUE; } else if (*buf == 'q') { goto s_bomb; } else { fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); goto reask_save; } } } else if (S_ISCHR(filestat.st_mode)) mailbox = FALSE; else { tmpfp = fopen(s,"r+"); if (!tmpfp) mailbox = FALSE; else { if (fread(buf,1,LBUFLEN,tmpfp)) { c = buf; if (!isspace(MBOXCHAR)) /* if non-zero, */ while (isspace(*c)) /* check the first character */ c++; mailbox = (*c == MBOXCHAR); } else mailbox = mbox_always; /* if zero length, recheck -M */ } } s = getenv(mailbox ? "MBOXSAVER" : "NORMSAVER"); if (s) { if (tmpfp) fclose(tmpfp); safecpy(cmd_buf, filexp(s), sizeof cmd_buf); #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) nntp_finishbody(FB_SILENT); #endif termlib_reset(); resetty(); /* make terminal behave */ i = doshell(use_pref?(char*)NULL:SH,cmd_buf); termlib_init(); noecho(); /* make terminal do what we want */ crmode(); } else if (tmpfp != NULL || (tmpfp = fopen(savedest, "a")) != NULL) { bool quote_From = FALSE; fseek(tmpfp,0,2); if (mailbox) { #if MBOXCHAR == '\001' fprintf(tmpfp,"\001\001\001\001\n"); #else interp(cmd_buf, sizeof cmd_buf, "From %t %`LANG= date`\n"); fputs(cmd_buf, tmpfp); quote_From = TRUE; #endif } if (savefrom == 0 && art != 0) fprintf(tmpfp,"Article: %ld of %s\n", (long)art, ngname); seekart(savefrom); while (readart(buf,LBUFLEN) != NULL) { if (quote_From && strncaseEQ(buf,"from ",5)) putc('>', tmpfp); fputs(buf, tmpfp); } fputs("\n\n", tmpfp); #if MBOXCHAR == '\001' if (mailbox) fprintf(tmpfp,"\001\001\001\001\n"); #endif fclose(tmpfp); i = 0; /*$$ set non-zero on write error */ } else i = 1; if (i) fputs("Not saved",stdout); else { printf("%s to %s %s", there? "Appended" : "Saved", mailbox? "mailbox" : "file", savedest); } if (interactive) newline(); } s_bomb: chdir_newsdir(); return SAVE_DONE; } int view_article() { parseheader(art); mime_SetArticle(); clear_artbuf(); savefrom = htype[PAST_HEADER].minpos; if (artopen(art,savefrom) == NULL) { #ifdef VERBOSE IF(verbose) fputs("\nNo attatchments on an empty article.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif termdown(2); return SAVE_DONE; } printf("Processing attachments...\n") FLUSH; termdown(1); if (is_mime) mime_DecodeArticle(TRUE); else { char* filename; int part, total; int cnt = 0; /* Scan subject for filename and part number information */ filename = decode_subject(art, &part, &total); for (artpos = savefrom; readart(art_line,sizeof art_line) != NULL; artpos = tellart()) { if (*art_line <= ' ') continue; /* Ignore empty or initially-whitespace lines */ if (uue_prescan(art_line, &filename, &part, &total)) { MIMECAP_ENTRY* mc = mime_FindMimecapEntry("image/jpeg",0); /*$$ refine this */ savefrom = artpos; seekart(savefrom); mime_section->type = UNHANDLED_MIME; safefree(mime_section->filename); mime_section->filename = filename? savestr(filename) : NULL; mime_section->encoding = MENCODE_UUE; mime_section->part = part; mime_section->total = total; if (mc && !decode_piece(mc,(char*)NULL) && *msg) { newline(); fputs(msg,stdout); } newline(); cnt = 0; } else if (++cnt == 300) break; }/* for */ if (cnt) { printf("Unable to determine type of file.\n") FLUSH; termdown(1); } } chdir_newsdir(); return SAVE_DONE; } int cancel_article() { char hbuf[5*LBUFLEN]; char* ngs_buf; char* from_buf; char* reply_buf; int myuid = getuid(); int r = -1; if (artopen(art,(ART_POS)0) == NULL) { #ifdef VERBOSE IF(verbose) fputs("\nCan't cancel an empty article.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif termdown(2); return r; } reply_buf = fetchlines(art,REPLY_LINE); from_buf = fetchlines(art,FROM_LINE); ngs_buf = fetchlines(art,NGS_LINE); if (strcaseNE(getval("FROM",""),from_buf) && (!instr(from_buf,hostname,FALSE) || (!instr(from_buf,loginName,TRUE) && !instr(reply_buf,loginName,TRUE) #ifdef NEWS_ADMIN && myuid != newsuid #endif && myuid != ROOTID))) { #ifdef DEBUG if (debug) { printf("\n%s@%s != %s\n",loginName,hostname,from_buf) FLUSH; printf("%s != %s\n",getval("FROM",""),from_buf) FLUSH; termdown(3); } #endif #ifdef VERBOSE IF(verbose) fputs("\nYou can't cancel someone else's article\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNot your article\n",stdout) FLUSH; #endif termdown(2); } else { tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == NULL) { printf(cantcreate,headname) FLUSH; termdown(1); goto done; } interp(hbuf, sizeof hbuf, getval("CANCELHEADER",CANCELHEADER)); fputs(hbuf,tmpfp); fclose(tmpfp); fputs("\nCanceling...\n",stdout) FLUSH; termdown(2); export_nntp_fds = TRUE; r = doshell(sh,filexp(getval("CANCEL",CALL_INEWS))); export_nntp_fds = FALSE; } done: free(ngs_buf); free(from_buf); free(reply_buf); return r; } int supersede_article() /* Supersedes: */ { char hbuf[5*LBUFLEN]; char* ngs_buf; char* from_buf; char* reply_buf; int myuid = getuid(); int r = -1; bool incl_body = (*buf == 'Z'); if (artopen(art,(ART_POS)0) == NULL) { #ifdef VERBOSE IF(verbose) fputs("\nCan't supersede an empty article.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif termdown(2); return r; } reply_buf = fetchlines(art,REPLY_LINE); from_buf = fetchlines(art,FROM_LINE); ngs_buf = fetchlines(art,NGS_LINE); if (strcaseNE(getval("FROM",""),from_buf) && (!instr(from_buf,hostname,FALSE) || (!instr(from_buf,loginName,TRUE) && !instr(reply_buf,loginName,TRUE) #ifdef NEWS_ADMIN && myuid != newsuid #endif && myuid != ROOTID))) { #ifdef DEBUG if (debug) { printf("\n%s@%s != %s\n",loginName,hostname,from_buf) FLUSH; printf("%s != %s\n",getval("FROM",""),from_buf) FLUSH; termdown(3); } #endif #ifdef VERBOSE IF(verbose) fputs("\nYou can't supersede someone else's article\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNot your article\n",stdout) FLUSH; #endif termdown(2); } else { tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == NULL) { printf(cantcreate,headname) FLUSH; termdown(1); goto done; } interp(hbuf, sizeof hbuf, getval("SUPERSEDEHEADER",SUPERSEDEHEADER)); fputs(hbuf,tmpfp); if (incl_body && artfp != NULL) { parseheader(art); seekart(htype[PAST_HEADER].minpos); while (readart(buf,LBUFLEN) != NULL) fputs(buf,tmpfp); } fclose(tmpfp); follow_it_up(); r = 0; } done: free(ngs_buf); free(from_buf); free(reply_buf); return r; } static void follow_it_up() { safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)), sizeof cmd_buf); if (invoke(cmd_buf,origdir) == 42) { int ret; #ifdef SUPPORT_NNTP if ((datasrc->flags & DF_REMOTE) && (nntp_command("DATE") <= 0 || (nntp_check() < 0 && atoi(ser_line) != NNTP_BAD_COMMAND_VAL))) ret = 1; else #endif { export_nntp_fds = TRUE; ret = invoke(filexp(CALL_INEWS),origdir); export_nntp_fds = FALSE; } if (ret) { int appended = 0; char* deadart = filexp("%./dead.article"); FILE* fp_in; FILE* fp_out; if ((fp_out = fopen(deadart, "a")) != NULL) { if ((fp_in = fopen(headname, "r")) != NULL) { while (fgets(cmd_buf, sizeof cmd_buf, fp_in)) fputs(cmd_buf, fp_out); fclose(fp_in); appended = 1; } fclose(fp_out); } if (appended) printf("Article appended to %s\n", deadart) FLUSH; else printf("Unable to append article to %s\n", deadart) FLUSH; } } } void reply() { char hbuf[5*LBUFLEN]; bool incl_body = (*buf == 'R' && art); char* maildoer = savestr(getval("MAILPOSTER",MAILPOSTER)); artopen(art,(ART_POS)0); tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == NULL) { printf(cantcreate,headname) FLUSH; termdown(1); goto done; } interp(hbuf, sizeof hbuf, getval("MAILHEADER",MAILHEADER)); fputs(hbuf,tmpfp); if (!instr(maildoer,"%h",TRUE)) { #ifdef VERBOSE IF(verbose) printf("\n%s\n(Above lines saved in file %s)\n",buf,headname) FLUSH; ELSE #endif #ifdef TERSE printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH; #endif termdown(3); } if (incl_body && artfp != NULL) { char* s; char* t; interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID)); fprintf(tmpfp,"%s\n",buf); parseheader(art); mime_SetArticle(); clear_artbuf(); seekart(htype[PAST_HEADER].minpos); wrapped_nl = '\n'; while ((s = readartbuf(FALSE)) != NULL) { if ((t = index(s, '\n')) != NULL) *t = '\0'; #ifdef CHARSUBST strcharsubst(hbuf,s,sizeof hbuf,*charsubst); fprintf(tmpfp,"%s%s\n",indstr,hbuf); #else fprintf(tmpfp,"%s%s\n",indstr,s); #endif if (t) *t = '\0'; } fprintf(tmpfp,"\n"); wrapped_nl = WRAPPED_NL; } fclose(tmpfp); safecpy(cmd_buf,filexp(maildoer),sizeof cmd_buf); invoke(cmd_buf,origdir); done: free(maildoer); } void forward() { char hbuf[5*LBUFLEN]; char* maildoer = savestr(getval("FORWARDPOSTER",FORWARDPOSTER)); #ifdef REGEX_WORKS_RIGHT COMPEX mime_compex; #else char* s; char* eol; #endif char* mime_boundary; #ifdef REGEX_WORKS_RIGHT init_compex(&mime_compex); #endif artopen(art,(ART_POS)0); tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == NULL) { printf(cantcreate,headname) FLUSH; termdown(1); goto done; } interp(hbuf, sizeof hbuf, getval("FORWARDHEADER",FORWARDHEADER)); fputs(hbuf,tmpfp); #ifdef REGEX_WORKS_RIGHT if (!compile(&mime_compex,"Content-Type: multipart/.*; *boundary=\"\\([^\"]*\\)\"",TRUE,TRUE) && execute(&mime_compex,hbuf) != NULL) mime_boundary = getbracket(&mime_compex,1); else mime_boundary = NULL; #else mime_boundary = NULL; for (s = hbuf; s; s = eol) { eol = index(s, '\n'); if (eol) eol++; if (*s == 'C' && strncaseEQ(s, "Content-Type: multipart/", 24)) { s += 24; for (;;) { for ( ; *s && *s != ';'; s++) { if (*s == '\n' && !isspace(s[1])) break; } if (*s != ';') break; while (*++s == ' ') ; if (*s == 'b' && strncaseEQ(s, "boundary=\"", 10)) { mime_boundary = s+10; if ((s = index(mime_boundary, '"')) != NULL) *s = '\0'; mime_boundary = savestr(mime_boundary); if (s) *s = '"'; break; } } } } #endif if (!instr(maildoer,"%h",TRUE)) { #ifdef VERBOSE IF(verbose) printf("\n%s\n(Above lines saved in file %s)\n",hbuf,headname) FLUSH; ELSE #endif #ifdef TERSE printf("\n%s\n(Header in %s)\n",hbuf,headname) FLUSH; #endif termdown(3); } if (artfp != NULL) { interp(buf, sizeof buf, getval("FORWARDMSG",FORWARDMSG)); if (mime_boundary) { if (*buf && strncaseNE(buf, "Content-", 8)) strcpy(buf, "Content-Type: text/plain\n"); fprintf(tmpfp,"--%s\n%s\n[Replace this with your comments.]\n\n--%s\nContent-Type: message/rfc822\n\n", mime_boundary,buf,mime_boundary); } else if (*buf) fprintf(tmpfp,"%s\n",buf); parseheader(art); seekart((ART_POS)0); while (readart(buf,sizeof buf) != NULL) { if (!mime_boundary && *buf == '-') { putchar('-'); putchar(' '); } fprintf(tmpfp,"%s",buf); } if (mime_boundary) fprintf(tmpfp,"\n--%s--\n",mime_boundary); else { interp(buf, (sizeof buf), getval("FORWARDMSGEND",FORWARDMSGEND)); if (*buf) fprintf(tmpfp,"%s\n",buf); } } fclose(tmpfp); safecpy(cmd_buf,filexp(maildoer),sizeof cmd_buf); invoke(cmd_buf,origdir); done: free(maildoer); #ifdef REGEX_WORKS_RIGHT free_compex(&mime_compex); #else safefree(mime_boundary); #endif } void followup() { char hbuf[5*LBUFLEN]; bool incl_body = (*buf == 'F' && art); ART_NUM oldart = art; if (!incl_body && art <= lastart) { termdown(2); in_answer("\n\nAre you starting an unrelated topic? [ynq] ", 'F'); setdef(buf,"y"); if (*buf == 'q') /*TODO: need to add 'h' also */ return; if (*buf != 'n') art = lastart + 1; } artopen(art,(ART_POS)0); tmpfp = fopen(headname,"w"); if (tmpfp == NULL) { printf(cantcreate,headname) FLUSH; termdown(1); art = oldart; return; } interp(hbuf, sizeof hbuf, getval("NEWSHEADER",NEWSHEADER)); fputs(hbuf,tmpfp); if (incl_body && artfp != NULL) { char* s; char* t; #ifdef VERBOSE if (verbose) fputs("\n\ (Be sure to double-check the attribution against the signature, and\n\ trim the quoted article down as much as possible.)\n\ ",stdout) FLUSH; #endif interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION)); fprintf(tmpfp,"%s\n",buf); parseheader(art); mime_SetArticle(); clear_artbuf(); seekart(htype[PAST_HEADER].minpos); wrapped_nl = '\n'; while ((s = readartbuf(FALSE)) != NULL) { if ((t = index(s, '\n')) != NULL) *t = '\0'; #ifdef CHARSUBST strcharsubst(hbuf,s,sizeof hbuf,*charsubst); fprintf(tmpfp,"%s%s\n",indstr,hbuf); #else fprintf(tmpfp,"%s%s\n",indstr,s); #endif if (t) *t = '\0'; } fprintf(tmpfp,"\n"); wrapped_nl = WRAPPED_NL; } fclose(tmpfp); follow_it_up(); art = oldart; } int invoke(cmd,dir) char* cmd; char* dir; { char oldmode = mode; int ret = -1; #ifdef SUPPORT_NNTP if (datasrc->flags & DF_REMOTE) nntp_finishbody(FB_SILENT); #endif #ifdef DEBUG if (debug) printf("\nInvoking command: %s\n",cmd); #endif if (dir) { if (chdir(dir)) { printf(nocd,dir) FLUSH; return ret; } } set_mode(gmode,'x'); termlib_reset(); resetty(); /* make terminal well-behaved */ ret = doshell(sh,cmd); /* do the command */ noecho(); /* set no echo */ crmode(); /* and cbreak mode */ termlib_init(); set_mode(gmode,oldmode); if (dir) chdir_newsdir(); return ret; } /* ** cut_line() determines if a line is meant as a "cut here" marker. ** Some examples that we understand: ** ** BEGIN--cut here--cut here ** ** ------------------ tear at this line ------------------ ** ** #----cut here-----cut here-----cut here-----cut here----# */ #if 0 static bool cut_line(str) char* str; { char* cp; char got_flag; char word[80]; int dash_cnt, equal_cnt, other_cnt; /* Disallow any single-/double-quoted, parenthetical or c-commented ** string lines. Make sure it has the cut-phrase and at least six ** '-'s or '='s. If only four '-'s are present, check for a duplicate ** of the cut phrase. If over 20 unknown characters are encountered, ** assume it isn't a cut line. If we succeed, return TRUE. */ for (cp = str, dash_cnt = equal_cnt = other_cnt = 0; *cp; cp++) { switch (*cp) { case '-': dash_cnt++; break; case '=': equal_cnt++; break; case '/': if (*(cp+1) != '*') break; case '"': case '\'': case '(': case ')': case '[': case ']': case '{': case '}': return FALSE; default: other_cnt++; break; } } if (dash_cnt < 4 && equal_cnt < 6) return FALSE; got_flag = 0; for (*(cp = word) = '\0'; *str; str++) { if (islower(*str)) *cp++ = *str; else if (isupper(*str)) *cp++ = tolower(*str); else { if (*word) { *cp = '\0'; switch (got_flag) { case 2: if (strEQ(word, "line") || strEQ(word, "here")) if ((other_cnt -= 4) <= 20) return TRUE; break; case 1: if (strEQ(word, "this")) { got_flag = 2; other_cnt -= 4; } else if (strEQ(word, "here")) { other_cnt -= 4; if ((dash_cnt >= 6 || equal_cnt >= 6) && other_cnt <= 20) return TRUE; dash_cnt = 6; got_flag = 0; } break; case 0: if (strEQ(word, "cut") || strEQ(word, "snip") || strEQ(word, "tear")) { got_flag = 1; other_cnt -= strlen(word); } break; } *(cp = word) = '\0'; } } } /* for *str */ return FALSE; } #endif trn-4.0-test77/respond.h0000644000000000000000000000120507113133016013625 0ustar rootroot/* respond.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* savedest INIT(NULL); /* value of %b */ EXT char* extractdest INIT(NULL); /* value of %E */ EXT char* extractprog INIT(NULL); /* value of %e */ EXT ART_POS savefrom INIT(0); /* value of %B */ #define SAVE_ABORT 0 #define SAVE_DONE 1 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void respond_init _((void)); int save_article _((void)); int view_article _((void)); int cancel_article _((void)); int supersede_article _((void)); void reply _((void)); void forward _((void)); void followup _((void)); int invoke _((char*,char*)); trn-4.0-test77/respond.ih0000644000000000000000000000035107113133016013777 0ustar rootroot/* respond.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void follow_it_up _((void)); #if 0 static bool cut_line _((char*)); #endif trn-4.0-test77/rt-mt.c0000644000000000000000000004417307113133016013224 0ustar rootroot/* rt-mt.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "intrp.h" #include "trn.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "kfile.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "ng.h" #include "rcln.h" #include "util.h" #include "util2.h" #include "rthread.h" #include "rt-process.h" #include "INTERN.h" #include "rt-mt.h" #include "rt-mt.ih" static FILE* fp; static bool word_same, long_same; static BMAP my_bmap, mt_bmap; static char* strings = NULL; static WORD* author_cnts = 0; static WORD* ids = 0; static ARTICLE** article_array = 0; static SUBJECT** subject_array = 0; static char** author_array = 0; static TOTAL total; static PACKED_ROOT p_root; static PACKED_ARTICLE p_article; /* Initialize our thread code by determining the byte-order of the thread ** files and our own current byte-order. If they differ, set flags to let ** the read code know what we'll need to translate. */ bool mt_init() { int i; long size; bool success = TRUE; datasrc->flags &= ~DF_TRY_THREAD; word_same = long_same = TRUE; #ifdef SUPPORT_XTHREAD if (!datasrc->thread_dir) { if (nntp_command("XTHREAD DBINIT") <= 0) return FALSE; size = nntp_readcheck(); if (size >= 0) size = nntp_read((char*)&mt_bmap, (long)sizeof (BMAP)); } else #endif { if ((fp = fopen(filexp(DBINIT), FOPEN_RB)) != NULL) size = fread((char*)&mt_bmap, 1, sizeof (BMAP), fp); else size = 0; } if (size >= (long)(sizeof (BMAP)) - 1) { if (mt_bmap.version != DB_VERSION) { printf("\nMthreads database is the wrong version -- ignoring it.\n") FLUSH; return FALSE; } mybytemap(&my_bmap); for (i = 0; i < sizeof (LONG); i++) { if (i < sizeof (WORD)) { if (my_bmap.w[i] != mt_bmap.w[i]) word_same = FALSE; } if (my_bmap.l[i] != mt_bmap.l[i]) long_same = FALSE; } } else success = FALSE; #ifdef SUPPORT_XTHREAD if (!datasrc->thread_dir) { while (nntp_read(ser_line, (long)sizeof ser_line)) ; /* trash any extraneous bytes */ } else #endif if (fp != NULL) fclose(fp); if (success) datasrc->flags |= DF_TRY_THREAD; return success; } /* Open and process the data in the group's thread file. Returns TRUE unless ** we discovered a bogus thread file, destroyed the cache, and re-built it. */ int mt_data() { int ret = 1; #ifdef SUPPORT_XTHREAD /* use remote thread file? */ long size; if (!datasrc->thread_dir) { if (nntp_command("XTHREAD THREAD") <= 0) return 0; size = nntp_readcheck(); if (size < 0) return 0; #ifdef VERBOSE IF(verbose) printf("\nGetting thread file."), fflush(stdout); #endif if (nntp_read((char*)&total, (long)sizeof (TOTAL)) < sizeof (TOTAL)) goto exit; } else #endif { if ((fp = fopen(mt_name(ngname), FOPEN_RB)) == NULL) return 0; #ifdef VERBOSE IF(verbose) printf("\nReading thread file."), fflush(stdout); #endif if (fread((char*)&total, 1, sizeof (TOTAL), fp) < sizeof (TOTAL)) goto exit; } lp_bmap(&total.first, 4); wp_bmap(&total.root, 5); if (!total.root) { tweak_data(); goto exit; } #ifdef SUPPORT_NNTP if (!datasrc->thread_dir && total.last > lastart) total.last = lastart; #endif if (read_authors() && read_subjects() && read_roots() && read_articles() && read_ids()) { tweak_data(); first_cached = absfirst; last_cached = (total.last < absfirst ? absfirst-1: total.last); cached_all_in_range = TRUE; goto exit; } /* Something failed. Safefree takes care of checking if some items ** were already freed. Any partially-allocated structures were freed ** before we got here. All other structures are cleaned up now. */ close_cache(); safefree0(strings); safefree0(article_array); safefree0(subject_array); safefree0(author_array); safefree0(ids); datasrc->flags &= ~DF_TRY_THREAD; build_cache(); datasrc->flags |= DF_TRY_THREAD; ret = -1; exit: #ifdef SUPPORT_XTHREAD if (!datasrc->thread_dir) { while (nntp_read(ser_line, (long)sizeof ser_line)) ; /* trash any extraneous bytes */ } else #endif fclose(fp); return ret; } /* Change a newsgroup name into the name of the thread data file. We ** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES ** is defined), prepend the path, and append the '/.thread' or '.th' on to ** the end. */ static char* mt_name(group) char* group; { #ifdef LONG_THREAD_NAMES sprintf(buf, "%s/%s", datasrc->thread_dir, group); #else register char* cp; cp = strcpy(buf, datasrc->thread_dir) + strlen(datasrc->thread_dir); *cp++ = '/'; strcpy(cp, group); while ((cp = index(cp, '.'))) *cp = '/'; if (datasrc->thread_dir == datasrc->spool_dir) strcat(buf, MT_FILE_NAME); else strcat(buf, ".th"); #endif return buf; } static char* subject_strings, *string_end; /* The author information is an array of use-counts, followed by all the ** null-terminated strings crammed together. The subject strings are read ** in at the same time, since they are appended to the end of the author ** strings. */ static int read_authors() { register int count; register char* string_ptr; register char** author_ptr; if (!read_item((char**)&author_cnts, (MEM_SIZE)total.author*sizeof (WORD))) return 0; safefree0(author_cnts); /* we don't need these */ if (!read_item(&strings, (MEM_SIZE)total.string1)) return 0; string_ptr = strings; string_end = string_ptr + total.string1; if (string_end[-1] != '\0') { /*error("first string table is invalid.\n");*/ return 0; } /* We'll use this array to point each article at its proper author ** (the packed values were saved as indexes). */ author_array = (char**)safemalloc(total.author * sizeof (char*)); author_ptr = author_array; for (count = total.author; count; count--) { if (string_ptr >= string_end) break; *author_ptr++ = string_ptr; string_ptr += strlen(string_ptr) + 1; } subject_strings = string_ptr; if (count) { /*error("author unpacking failed.\n");*/ return 0; } return 1; } /* The subject values consist of the crammed-together null-terminated strings ** (already read in above) and the use-count array. They were saved in the ** order that the roots require while being unpacked. */ static int read_subjects() { register int count; register char* string_ptr; register SUBJECT** subj_ptr; WORD* subject_cnts; if (!read_item((char**)&subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD))) { /* (Error already logged.) */ return 0; } free((char*)subject_cnts); /* we don't need these */ /* Use this array when unpacking the article's subject offset. */ subject_array = (SUBJECT**)safemalloc(total.subject * sizeof (SUBJECT*)); subj_ptr = subject_array; string_ptr = subject_strings; /* string_end is already set */ for (count = total.subject; count; count--) { int len; ARTICLE arty; if (string_ptr >= string_end) break; len = strlen(string_ptr); arty.subj = 0; set_subj_line(&arty, string_ptr, len); if (len == 72) arty.subj->flags |= SF_SUBJTRUNCED; arty.subj->thread_link = NULL; string_ptr += len + 1; *subj_ptr++ = arty.subj; } if (count || string_ptr != string_end) { /*error("subject data is invalid.\n");*/ return 0; } return 1; } /* Read in the packed root structures to set each subject's thread article ** offset. This gets turned into a real pointer later. */ static int read_roots() { register SUBJECT** subj_ptr; register int i; SUBJECT* sp; SUBJECT* prev_sp; int count; int ret; subj_ptr = subject_array; for (count = total.root; count--; ) { #ifdef SUPPORT_XTHREAD if (!datasrc->thread_dir) ret = nntp_read((char*)&p_root, (long)sizeof (PACKED_ROOT)); else #endif ret = fread((char*)&p_root, 1, sizeof (PACKED_ROOT), fp); if (ret != sizeof (PACKED_ROOT)) { /*error("failed root read -- %d bytes instead of %d.\n", ret, sizeof (PACKED_ROOT));*/ return 0; } wp_bmap(&p_root.articles, 3); /* converts subject_cnt too */ if (p_root.articles < 0 || p_root.articles >= total.article) { /*error("root has invalid values.\n");*/ return 0; } i = p_root.subject_cnt; if (i <= 0 || (subj_ptr - subject_array) + i > total.subject) { /*error("root has invalid values.\n");*/ return 0; } for (prev_sp = *subj_ptr; i--; prev_sp = sp, subj_ptr++) { sp = *subj_ptr; if (sp->thread_link == NULL) { sp->thread_link = prev_sp->thread_link; prev_sp->thread_link = sp; } else { while (sp != prev_sp && sp->thread_link != *subj_ptr) sp = sp->thread_link; if (sp == prev_sp) continue; sp->thread_link = prev_sp->thread_link; prev_sp->thread_link = *subj_ptr; } } } return 1; } static bool invalid_data; /* A simple routine that checks the validity of the article's subject value. ** A -1 means that it is NULL, otherwise it should be an offset into the ** subject array we just unpacked. */ static SUBJECT* the_subject(num) int num; { if (num == -1) return NULL; if (num < 0 || num >= total.subject) { /*printf("Invalid subject in thread file: %d [%ld]\n", num, art_num);*/ invalid_data = TRUE; return NULL; } return subject_array[num]; } /* Ditto for author checking. */ static char* the_author(num) int num; { if (num == -1) return NULL; if (num < 0 || num >= total.author) { /*error("invalid author in thread file: %d [%ld]\n", num, art_num);*/ invalid_data = TRUE; return NULL; } return savestr(author_array[num]); } /* Our parent/sibling information is a relative offset in the article array. ** zero for none. Child values are always found in the very next array ** element if child_cnt is non-zero. */ static ARTICLE* the_article(relative_offset, num) int relative_offset; int num; { union { ARTICLE* ap; int num; } uni; if (!relative_offset) return NULL; num += relative_offset; if (num < 0 || num >= total.article) { /*error("invalid article offset in thread file.\n");*/ invalid_data = TRUE; return NULL; } uni.num = num+1; return uni.ap; /* slip them an offset in disguise */ } /* Read the articles into their trees. Point everything everywhere. */ static int read_articles() { register int count; register ARTICLE* article; register ARTICLE** art_ptr; int ret; /* Build an array to interpret interlinkages of articles. */ article_array = (ARTICLE**)safemalloc(total.article * sizeof (ARTICLE*)); art_ptr = article_array; invalid_data = FALSE; for (count = 0; count < total.article; count++) { #ifdef SUPPORT_XTHREAD if (!datasrc->thread_dir) ret = nntp_read((char*)&p_article, (long)sizeof (PACKED_ARTICLE)); else #endif ret = fread((char*)&p_article, 1, sizeof (PACKED_ARTICLE), fp); if (ret != sizeof (PACKED_ARTICLE)) { /*error("failed article read -- %d bytes instead of %d.\n", ret, sizeof (PACKED_ARTICLE));*/ return 0; } lp_bmap(&p_article.num, 2); wp_bmap(&p_article.subject, 8); #ifdef SUPPORT_NNTP article = *art_ptr++ = allocate_article(p_article.num > lastart? 0 : p_article.num); #else article = *art_ptr++ = allocate_article(p_article.num); #endif article->date = p_article.date; #ifndef DBM_XREFS if (olden_days < 2 && !(p_article.flags & HAS_XREFS)) article->xrefs = nullstr; #endif article->from = the_author(p_article.author); article->parent = the_article(p_article.parent, count); article->child1 = the_article((WORD)(p_article.child_cnt?1:0), count); article->sibling = the_article(p_article.sibling, count); article->subj = the_subject(p_article.subject); if (invalid_data) { /* (Error already logged.) */ return 0; } /* This is OK because parent articles precede their children */ if (article->parent) { union { ARTICLE* ap; int num; } uni; uni.ap = article->parent; article->parent = article_array[uni.num-1]; } else article->sibling = NULL; if (article->subj) { article->flags |= AF_FROMTRUNCED | AF_THREADED | ((p_article.flags & ROOT_ARTICLE)? 0 : AF_HAS_RE); /* Give this subject to any faked parent articles */ while (article->parent && !article->parent->subj) { article->parent->subj = article->subj; article = article->parent; } } else article->flags |= AF_FAKE; } /* We're done with most of the pointer arrays at this point. */ safefree0(subject_array); safefree0(author_array); safefree0(strings); return 1; } /* Read the message-id strings and attach them to each article. The data ** format consists of the mushed-together null-terminated strings (a domain ** name followed by all its unique-id prefixes) and then the article offsets ** to which they belong. The first domain name was omitted, as it is a null ** domain for those truly weird message-id's without '@'s. */ static int read_ids() { register ARTICLE* article; register char* string_ptr; register int i, count, len, len2; if (!read_item(&strings, (MEM_SIZE)total.string2) || !read_item((char**)&ids, (MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD))) { return 0; } wp_bmap(ids, total.article + total.domain + 1); string_ptr = strings; string_end = string_ptr + total.string2; if (string_end[-1] != '\0') { /*error("second string table is invalid.\n");*/ return 0; } for (i = 0, count = total.domain + 1; count--; i++) { if (i) { if (string_ptr >= string_end) { /*error("error unpacking domain strings.\n");*/ return 0; } sprintf(buf, "@%s", string_ptr); len = strlen(string_ptr) + 1; string_ptr += len; } else { *buf = '\0'; len = 0; } if (ids[i] != -1) { if (ids[i] < 0 || ids[i] >= total.article) { /*error("error in id array.\n");*/ return 0; } article = article_array[ids[i]]; for (;;) { if (string_ptr >= string_end) { /*error("error unpacking domain strings.\n");*/ return 0; } len2 = strlen(string_ptr); article->msgid = safemalloc(len2 + len + 2 + 1); sprintf(article->msgid, "<%s%s>", string_ptr, buf); string_ptr += len2 + 1; if (msgid_hash) { HASHDATUM data; data = hashfetch(msgid_hash, article->msgid, len2+len+2); if (data.dat_len) { article->autofl = data.dat_len&(AUTO_SELS|AUTO_KILLS); if ((data.dat_len & KF_AGE_MASK) == 0) article->autofl |= AUTO_OLD; else kf_changethd_cnt++; data.dat_len = 0; free(data.dat_ptr); } data.dat_ptr = (char*)article; hashstorelast(data); } if (++i >= total.article + total.domain + !count) { /*error("overran id array unpacking domains.\n");*/ return 0; } if (ids[i] != -1) { if (ids[i] < 0 || ids[i] >= total.article) return 0; article = article_array[ids[i]]; } else break; } } } safefree0(ids); safefree0(strings); return 1; } /* And finally, turn all the links into real pointers and mark missing ** articles as read. */ static void tweak_data() { register int count; register ARTICLE* ap; register ARTICLE** art_ptr; union { ARTICLE* ap; int num; } uni; int fl; art_ptr = article_array; for (count = total.article; count--; ) { ap = *art_ptr++; if (ap->child1) { uni.ap = ap->child1; ap->child1 = article_array[uni.num-1]; } if (ap->sibling) { uni.ap = ap->sibling; ap->sibling = article_array[uni.num-1]; } if (!ap->parent) link_child(ap); } art_ptr = article_array; for (count = total.article; count--; ) { ap = *art_ptr++; if (ap->subj && !(ap->flags & AF_FAKE)) cache_article(ap); else onemissing(ap); } art_ptr = article_array; for (count = total.article; count--; ) { ap = *art_ptr++; if ((fl = ap->autofl) != 0) perform_auto_flags(ap, fl, fl, fl); } safefree0(article_array); } /* A shorthand for reading a chunk of the file into a malloc'ed array. */ static int read_item(dest, len) char** dest; MEM_SIZE len; { long ret; *dest = safemalloc(len); #ifdef SUPPORT_XTHREAD if (!datasrc->thread_dir) ret = nntp_read(*dest, (long)len); else #endif ret = fread(*dest, 1, (int)len, fp); if (ret != len) { free(*dest); *dest = NULL; return 0; } putchar('.'); fflush(stdout); return 1; } /* Determine this machine's byte map for WORDs and LONGs. A byte map is an ** array of BYTEs (sizeof (WORD) or sizeof (LONG) of them) with the 0th BYTE ** being the byte number of the high-order byte in my , and so forth. */ static void mybytemap(map) BMAP* map; { union { BYTE b[sizeof (LONG)]; WORD w; LONG l; } u; register BYTE *mp; register int i, j; mp = &map->w[sizeof (WORD)]; u.w = 1; for (i = sizeof (WORD); i > 0; i--) { for (j = 0; j < sizeof (WORD); j++) { if (u.b[j] != 0) break; } if (j == sizeof (WORD)) goto bad_news; *--mp = j; while (u.b[j] != 0 && u.w) u.w <<= 1; } mp = &map->l[sizeof (LONG)]; u.l = 1; for (i = sizeof (LONG); i > 0; i--) { for (j = 0; j < sizeof (LONG); j++) { if (u.b[j] != 0) break; } if (j == sizeof (LONG)) { bad_news: /* trouble -- set both to *something* consistent */ for (j = 0; j < sizeof (WORD); j++) map->w[j] = j; for (j = 0; j < sizeof (LONG); j++) map->l[j] = j; return; } *--mp = j; while (u.b[j] != 0 && u.l) u.l <<= 1; } } /* Transform each WORD's byte-ordering in a buffer of the designated length. */ static void wp_bmap(buf, len) WORD* buf; int len; { union { BYTE b[sizeof (WORD)]; WORD w; } in, out; register int i; if (word_same) return; while (len--) { in.w = *buf; for (i = 0; i < sizeof (WORD); i++) out.b[my_bmap.w[i]] = in.b[mt_bmap.w[i]]; *buf++ = out.w; } } /* Transform each LONG's byte-ordering in a buffer of the designated length. */ static void lp_bmap(buf, len) LONG* buf; int len; { union { BYTE b[sizeof (LONG)]; LONG l; } in, out; register int i; if (long_same) return; while (len--) { in.l = *buf; for (i = 0; i < sizeof (LONG); i++) out.b[my_bmap.l[i]] = in.b[mt_bmap.l[i]]; *buf++ = out.l; } } trn-4.0-test77/rt-mt.h0000644000000000000000000000030307113133016013214 0ustar rootroot/* rt-mt.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool mt_init _((void)); int mt_data _((void)); trn-4.0-test77/rt-mt.ih0000644000000000000000000000273407113133016013377 0ustar rootroot/* rt-mt.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ #define DB_VERSION 2 typedef Uchar BYTE; typedef short WORD; #ifndef __alpha typedef long LONG; #else typedef int LONG; #endif #define ROOT_ARTICLE 0x0001 /* article flag definitions */ #define HAS_XREFS 0x0004 /* article has an xref line */ struct packed_root { LONG root_num; WORD articles; WORD article_cnt; WORD subject_cnt; WORD pad_hack; }; struct packed_article { LONG num; LONG date; WORD subject, author; WORD flags; WORD child_cnt; WORD parent; WORD padding; WORD sibling; WORD root; }; struct total { LONG first, last; LONG string1; LONG string2; WORD root; WORD article; WORD subject; WORD author; WORD domain; WORD pad_hack; }; struct bmap { BYTE l[sizeof (LONG)]; BYTE w[sizeof (WORD)]; BYTE version; BYTE pad_hack; }; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static char* mt_name _((char*)); static int read_authors _((void)); static int read_subjects _((void)); static int read_roots _((void)); static SUBJECT* the_subject _((int)); static char* the_author _((int)); static ARTICLE* the_article _((int,int)); static int read_articles _((void)); static int read_ids _((void)); static void tweak_data _((void)); static int read_item _((char**,MEM_SIZE)); static void mybytemap _((BMAP*)); static void wp_bmap _((WORD*,int)); static void lp_bmap _((LONG*,int)); trn-4.0-test77/rt-ov.c0000644000000000000000000002744311437640112013235 0ustar rootroot/* rt-ov.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "head.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "util.h" #include "util2.h" #include "env.h" #include "ng.h" #include "term.h" #include "intrp.h" #include "final.h" #include "rthread.h" #include "rt-process.h" #include "rt-util.h" #include "parsedate.h" #include "INTERN.h" #include "rt-ov.h" #include "rt-ov.ih" bool ov_init() { bool has_overview_fmt; Uchar* fieldnum = datasrc->fieldnum; Uchar* fieldflags = datasrc->fieldflags; datasrc->flags &= ~DF_TRY_OVERVIEW; #ifdef SUPPORT_NNTP if (!datasrc->over_dir) { int ret; /* Check if the server is XOVER compliant */ if (nntp_command("XOVER") <= 0) return FALSE; if (nntp_check() < 0) return FALSE;/*$$*/ if (atoi(ser_line) == NNTP_BAD_COMMAND_VAL) return FALSE; /* Just in case... */ if (*ser_line == NNTP_CLASS_OK) nntp_finish_list(); if ((ret = nntp_list("overview.fmt",nullstr,0)) < -1) return FALSE; has_overview_fmt = ret > 0; } else #endif { has_overview_fmt = datasrc->over_fmt != NULL && (tmpfp = fopen(datasrc->over_fmt, "r")) != NULL; } if (has_overview_fmt) { int i; fieldnum[0] = OV_NUM; fieldflags[OV_NUM] = FF_HAS_FIELD; for (i = 1;;) { #ifdef SUPPORT_NNTP if (!datasrc->over_dir) { if (nntp_gets(buf, sizeof buf) < 0) break;/*$$*/ if (nntp_at_list_end(buf)) break; } #endif ElseIf (!fgets(buf, sizeof buf, tmpfp)) { fclose(tmpfp); break; } if (*buf == '#') continue; if (i < OV_MAX_FIELDS) { char *s = index(buf,':'); fieldnum[i] = ov_num(buf,s); fieldflags[fieldnum[i]] = FF_HAS_FIELD | ((s && strncaseEQ("full",s+1,4))? FF_HAS_HDR : 0); i++; } } if (!fieldflags[OV_SUBJ] || !fieldflags[OV_MSGID] || !fieldflags[OV_FROM] || !fieldflags[OV_DATE]) return FALSE; if (i < OV_MAX_FIELDS) { int j; for (j = OV_MAX_FIELDS; j--; ) { if (!fieldflags[j]) break; } while (i < OV_MAX_FIELDS) fieldnum[i++] = j; } } else { int i; for (i = 0; i < OV_MAX_FIELDS; i++) { fieldnum[i] = i; fieldflags[i] = FF_HAS_FIELD; } fieldflags[OV_XREF] = FF_CHECK4FIELD | FF_CHECK4HDR; } datasrc->flags |= DF_TRY_OVERVIEW; return TRUE; } int ov_num(hdr,end) char* hdr; char* end; { if (!end) end = hdr + strlen(hdr); switch (set_line_type(hdr,end)) { case SUBJ_LINE: return OV_SUBJ; case AUTHOR_LINE: /* This hack is for the Baen NNTP server */ case FROM_LINE: return OV_FROM; case DATE_LINE: return OV_DATE; case MSGID_LINE: return OV_MSGID; case REFS_LINE: return OV_REFS; case BYTES_LINE: return OV_BYTES; case LINES_LINE: return OV_LINES; case XREF_LINE: return OV_XREF; } return 0; } /* Process the data in the group's news-overview file. */ bool ov_data(first, last, cheating) ART_NUM first, last; bool_int cheating; { ART_NUM artnum, an; char* line; char* last_buf = buf; MEM_SIZE last_buflen = LBUFLEN; bool success = TRUE; ART_NUM real_first = first; #ifdef SUPPORT_NNTP ART_NUM real_last = last; int line_cnt; int ov_chunk_size = cheating? OV_CHUNK_SIZE : OV_CHUNK_SIZE * 8; #endif time_t started_request; bool remote = !datasrc->over_dir; #ifdef SUPPORT_NNTP beginning: #endif for (;;) { artnum = article_first(first); if (artnum > first || !(article_ptr(artnum)->flags & AF_CACHED)) break; spin_todo--; first++; } if (first > last) goto exit; #ifdef SUPPORT_NNTP if (remote) { if (last - first > ov_chunk_size + ov_chunk_size/2 - 1) { last = first + ov_chunk_size - 1; line_cnt = 0; } } #endif started_request = time((time_t*)NULL); for (;;) { artnum = article_last(last); if (artnum < last || !(article_ptr(artnum)->flags & AF_CACHED)) break; spin_todo--; last--; } #ifdef SUPPORT_NNTP if (remote) { sprintf(ser_line, "XOVER %ld-%ld", (long)first, (long)last); if (nntp_command(ser_line) <= 0 || nntp_check() <= 0) { success = FALSE; goto exit; } # ifdef VERBOSE IF(verbose && !first_subject && !datasrc->ov_opened) printf("\nGetting overview file."), fflush(stdout); # endif } #endif ElseIf (datasrc->ov_opened < started_request - 60*60) { ov_close(); if ((datasrc->ov_in = fopen(ov_name(ngname), "r")) == NULL) return FALSE; #ifdef VERBOSE IF(verbose && !first_subject) printf("\nReading overview file."), fflush(stdout); #endif } if (!datasrc->ov_opened) { if (cheating) setspin(SPIN_BACKGROUND); else { #ifdef SUPPORT_NNTP int lots2do = ((datasrc->flags & DF_REMOTE)? netspeed : 20) * 100; #else int lots2do = 20 * 100; #endif if (spin_estimate > spin_todo) spin_estimate = spin_todo; setspin(spin_estimate > lots2do? SPIN_BARGRAPH : SPIN_FOREGROUND); } datasrc->ov_opened = started_request; } artnum = first-1; for (;;) { #ifdef SUPPORT_NNTP if (remote) { line = nntp_get_a_line(last_buf,last_buflen,last_buf!=buf); if (nntp_at_list_end(line)) break; line_cnt++; } #endif ElseIf (!(line = get_a_line(last_buf,last_buflen,last_buf!=buf,datasrc->ov_in))) break; last_buf = line; last_buflen = buflen_last_line_got; an = atol(line); if (an < first) continue; if (an > last) { artnum = last; #ifdef SUPPORT_NNTP if (remote) continue; #endif break; } spin_todo -= an - artnum - 1; ov_parse(line, artnum = an, remote); if (int_count) { int_count = 0; success = FALSE; #ifdef SUPPORT_NNTP if (!remote) #endif break; } if (!remote && cheating) { if (input_pending()) { success = FALSE; break; } if (curr_artp != sentinel_artp) { pushchar('\f' | 0200); success = FALSE; break; } } } #ifdef SUPPORT_NNTP if (remote && line_cnt == 0 && last < real_last) { an = nntp_find_real_art(last); if (an > 0) { last = an - 1; spin_todo -= last - artnum; artnum = last; } } if (remote) { int cachemask = (ThreadedGroup? AF_THREADED : AF_CACHED); ARTICLE* ap; for (ap = article_ptr(article_first(real_first)); ap && article_num(ap) <= artnum; ap = article_nextp(ap)) { if (!(ap->flags & cachemask)) onemissing(ap); } spin_todo -= last - artnum; } #endif if (artnum > last_cached && artnum >= first) last_cached = artnum; exit: if (int_count || !success) { int_count = 0; success = FALSE; } #ifdef SUPPORT_NNTP else if (remote) { if (cheating && curr_artp != sentinel_artp) { pushchar('\f' | 0200); success = FALSE; } else if (last < real_last) { if (!cheating || !input_pending()) { long elapsed_time = time((time_t*)NULL) - started_request; long expected_time = cheating? 2 : 10; int max_chunk_size = cheating? 500 : 2000; ov_chunk_size += (expected_time - elapsed_time) * OV_CHUNK_SIZE; if (ov_chunk_size <= OV_CHUNK_SIZE / 2) ov_chunk_size = OV_CHUNK_SIZE / 2 + 1; else if (ov_chunk_size > max_chunk_size) ov_chunk_size = max_chunk_size; first = last+1; last = real_last; goto beginning; } success = FALSE; } } #endif if (!cheating && datasrc->ov_in) fseek(datasrc->ov_in, 0L, 0); /* rewind it for the cheating phase */ if (success && real_first <= first_cached) { first_cached = real_first; cached_all_in_range = TRUE; } setspin(SPIN_POP); if (last_buf != buf) free(last_buf); return success; } static void ov_parse(line, artnum, remote) register char* line; ART_NUM artnum; bool_int remote; { register ARTICLE* article; register int i; int fn; Uchar* fieldnum = datasrc->fieldnum; Uchar* fieldflags = datasrc->fieldflags; char* fields[OV_MAX_FIELDS]; char* cp; char* tab; article = article_ptr(artnum); if (article->flags & AF_THREADED) { spin_todo--; return; } if (len_last_line_got > 0 && line[len_last_line_got-1] == '\n') { #ifdef SUPPORT_NNTP if (len_last_line_got > 1 && line[len_last_line_got-2] == '\r') line[len_last_line_got-2] = '\0'; else #endif line[len_last_line_got-1] = '\0'; } cp = line; bzero((char*)fields, sizeof fields); for (i = 0; cp && i < OV_MAX_FIELDS; cp = tab) { if ((tab = index(cp, '\t')) != NULL) *tab++ = '\0'; fn = fieldnum[i]; if (!(fieldflags[fn] & (FF_HAS_FIELD | FF_CHECK4FIELD))) break; if (fieldflags[fn] & (FF_HAS_HDR | FF_CHECK4HDR)) { char* s = index(cp, ':'); if (fieldflags[fn] & FF_CHECK4HDR) { if (s) fieldflags[fn] |= FF_HAS_HDR; fieldflags[fn] &= ~FF_CHECK4HDR; } if (fieldflags[fn] & FF_HAS_HDR) { if (!s) break; if (s - cp != htype[hdrnum[fn]].length || strncaseNE(cp,htype[hdrnum[fn]].name,htype[hdrnum[fn]].length)) continue; cp = s; while (*++cp == ' ') ; } } fields[fn] = cp; i++; } if (!fields[OV_SUBJ] || !fields[OV_MSGID] || !fields[OV_FROM] || !fields[OV_DATE]) return; /* skip this line if it's too short */ if (!article->subj) set_subj_line(article, fields[OV_SUBJ], strlen(fields[OV_SUBJ])); if (!article->msgid) set_cached_line(article, MSGID_LINE, savestr(fields[OV_MSGID])); if (!article->from) set_cached_line(article, FROM_LINE, savestr(fields[OV_FROM])); if (!article->date) article->date = parsedate(fields[OV_DATE]); #ifdef USE_FILTER if (!article->refs && fields[OV_REFS]) set_cached_line(article, REFS_LINE, *fields[OV_REFS]? savestr(fields[OV_REFS]) : nullstr); #endif if (!article->bytes && fields[OV_BYTES]) set_cached_line(article, BYTES_LINE, fields[OV_BYTES]); if (!article->lines && fields[OV_LINES]) set_cached_line(article, LINES_LINE, fields[OV_LINES]); if (fieldflags[OV_XREF] & (FF_HAS_FIELD | FF_CHECK4FIELD)) { if (!article->xrefs && fields[OV_XREF]) { /* Exclude an xref for just this group */ cp = index(fields[OV_XREF], ':'); if (cp && index(cp+1, ':')) article->xrefs = savestr(fields[OV_XREF]); } if (fieldflags[OV_XREF] & FF_HAS_FIELD) { if (!article->xrefs) article->xrefs = nullstr; } else if (fields[OV_XREF]) { ART_NUM an; ARTICLE* ap; for (an=article_first(absfirst); anxrefs) ap->xrefs = nullstr; } fieldflags[OV_XREF] |= FF_HAS_FIELD; } } if (remote) article->flags |= AF_EXISTS; if (ThreadedGroup) { if (valid_article(article)) thread_article(article, fields[OV_REFS]); } else if (!(article->flags & AF_CACHED)) cache_article(article); if (article->flags & AF_UNREAD) check_poster(article); spin(100); } /* Change a newsgroup name into the name of the overview data file. We ** subsitute any '.'s in the group name into '/'s, prepend the path, and ** append the '/.overview' or '.ov') on to the end. */ static char* ov_name(group) char* group; { register char* cp; strcpy(buf, datasrc->over_dir); cp = buf + strlen(buf); *cp++ = '/'; strcpy(cp, group); while ((cp = index(cp, '.'))) *cp = '/'; strcat(buf, OV_FILE_NAME); return buf; } void ov_close() { if (datasrc && datasrc->ov_opened) { if (datasrc->ov_in) { (void) fclose(datasrc->ov_in); datasrc->ov_in = NULL; } datasrc->ov_opened = 0; } } char* ov_fieldname(num) int num; { return htype[hdrnum[num]].name; } char* ov_field(ap, num) ARTICLE* ap; int num; { char* s; int fn; fn = datasrc->fieldnum[num]; if (!(datasrc->fieldflags[fn] & (FF_HAS_FIELD | FF_CHECK4FIELD))) return NULL; if (fn == OV_NUM) { sprintf(cmd_buf, "%ld", (long)ap->num); return cmd_buf; } if (fn == OV_DATE) { sprintf(cmd_buf, "%ld", (long)ap->date); return cmd_buf; } s = get_cached_line(ap, hdrnum[fn], TRUE); return s? s : nullstr; } trn-4.0-test77/rt-ov.h0000644000000000000000000000051507113133016013225 0ustar rootroot/* rt-ov.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool ov_init _((void)); int ov_num _((char*,char*)); bool ov_data _((ART_NUM,ART_NUM,bool_int)); void ov_close _((void)); char* ov_fieldname _((int)); char* ov_field _((ARTICLE*,int)); trn-4.0-test77/rt-ov.ih0000644000000000000000000000123407113133016013375 0ustar rootroot/* rt-ov.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* The usual order for the overview data fields. */ #define OV_NUM 0 #define OV_SUBJ 1 #define OV_FROM 2 #define OV_DATE 3 #define OV_MSGID 4 #define OV_REFS 5 #define OV_BYTES 6 #define OV_LINES 7 #define OV_XREF 8 /* How many overview lines to read with one NNTP call */ #define OV_CHUNK_SIZE 40 static int hdrnum[] = { 0, SUBJ_LINE, FROM_LINE, DATE_LINE, MSGID_LINE, REFS_LINE, BYTES_LINE, LINES_LINE, XREF_LINE }; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void ov_parse _((char*,ART_NUM,bool_int)); static char* ov_name _((char*)); trn-4.0-test77/rt-page.c0000644000000000000000000012772411437640112013530 0ustar rootroot/* rt-page.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "cache.h" #include "term.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "trn.h" #include "env.h" #include "util.h" #include "util2.h" #include "opt.h" #include "only.h" #include "addng.h" #include "rcln.h" #include "rcstuff.h" #include "rthread.h" #include "rt-select.h" #include "rt-util.h" #include "univ.h" #include "color.h" #include "INTERN.h" #include "rt-page.h" #include "rt-page.ih" bool set_sel_mode(ch) char_int ch; { switch (ch) { case 'a': set_selector(sel_defaultmode = SM_ARTICLE, 0); break; case 's': set_selector(sel_defaultmode = SM_SUBJECT, 0); break; case 't': if (in_ng && !ThreadedGroup) { bool always_save = thread_always; ThreadedGroup = TRUE; thread_always = TRUE; if (sel_rereading) firstart = absfirst; printf("\nThreading the group. "), fflush(stdout); termdown(1); thread_open(); thread_always = always_save; if (last_cached < lastart) ThreadedGroup = FALSE; } /* FALL THROUGH */ case 'T': set_selector(sel_defaultmode = SM_THREAD, 0); break; default: set_selector(sel_defaultmode, 0); return FALSE; } return TRUE; } char* get_sel_order(smode) int smode; { int save_sel_mode = sel_mode; set_selector(smode, 0); sprintf(buf,"%s%s", sel_direction < 0? "reverse " : nullstr, sel_sort_string); sel_mode = save_sel_mode; set_selector(0, 0); return buf; } bool set_sel_order(smode,str) int smode; char* str; { bool reverse = 0; char ch; if (*str == 'r' || *str == 'R') { while (*str && *str != ' ') str++; while (*str == ' ') str++; reverse = 1; } ch = *str; if (reverse) ch = islower(ch)? toupper(ch) : ch; else ch = isupper(ch)? tolower(ch) : ch; return set_sel_sort(smode,ch); } bool set_sel_sort(smode,ch) int smode; char_int ch; { int save_sel_mode = sel_mode; int ssort; switch (ch) { case 'd': case 'D': ssort = SS_DATE; break; case 's': case 'S': ssort = SS_STRING; break; case 'a': case 'A': ssort = SS_AUTHOR; break; case 'c': case 'C': ssort = SS_COUNT; break; case 'n': case 'N': ssort = SS_NATURAL; break; case 'g': case 'G': ssort = SS_GROUPS; break; case 'l': case 'L': ssort = SS_LINES; break; case 'p': case 'P': ssort = SS_SCORE; break; default: return FALSE; } sel_mode = smode; if (isupper(ch)) set_selector(0, -ssort); else set_selector(0, ssort); if (sel_mode != save_sel_mode) { sel_mode = save_sel_mode; set_selector(0, 0); } return TRUE; } void set_selector(smode, ssort) int smode; int ssort; { if (smode == 0) { if (sel_mode == SM_SUBJECT) sel_mode = sel_threadmode; smode = sel_mode; } else sel_mode = smode; if (!ssort) { switch (sel_mode) { case SM_MULTIRC: ssort = SS_NATURAL; break; case SM_ADDGROUP: ssort = sel_addgroupsort; break; case SM_NEWSGROUP: ssort = sel_newsgroupsort; break; case SM_OPTIONS: ssort = SS_NATURAL; break; case SM_THREAD: case SM_SUBJECT: ssort = sel_threadsort; break; case SM_ARTICLE: ssort = sel_artsort; break; case SM_UNIVERSAL: ssort = sel_univsort; break; } } if (ssort > 0) { sel_direction = 1; sel_sort = ssort; } else { sel_direction = -1; sel_sort = -ssort; } if (sel_mode == SM_THREAD && !ThreadedGroup) sel_mode = SM_SUBJECT; switch (sel_mode) { case SM_MULTIRC: sel_mode_string = "a newsrc group"; break; case SM_ADDGROUP: sel_mode_string = "a newsgroup to add"; sel_addgroupsort = ssort; break; case SM_NEWSGROUP: sel_mode_string = "a newsgroup"; sel_newsgroupsort = ssort; break; case SM_OPTIONS: sel_mode_string = "an option to change"; break; case SM_UNIVERSAL: sel_mode_string = "an item"; sel_univsort = ssort; break; case SM_THREAD: sel_mode_string = "threads"; sel_threadmode = smode; sel_threadsort = ssort; goto thread_subj_sort; case SM_SUBJECT: sel_mode_string = "subjects"; sel_threadmode = smode; sel_threadsort = ssort; thread_subj_sort: if (sel_sort == SS_AUTHOR || sel_sort == SS_GROUPS || sel_sort == SS_NATURAL) sel_sort = SS_DATE; break; case SM_ARTICLE: sel_mode_string = "articles"; sel_artsort = ssort; if (sel_sort == SS_COUNT) sel_sort = SS_DATE; break; } switch (sel_sort) { case SS_DATE: sel_sort_string = "date"; break; case SS_STRING: sel_sort_string = "subject"; break; case SS_AUTHOR: sel_sort_string = "author"; break; case SS_COUNT: sel_sort_string = "count"; break; case SS_LINES: sel_sort_string = "lines"; break; case SS_NATURAL: sel_sort_string = "natural"; break; case SS_GROUPS: if (sel_mode == SM_NEWSGROUP) sel_sort_string = "group name"; else sel_sort_string = "SubjDate"; break; case SS_SCORE: sel_sort_string = "points"; break; } } static void sel_page_init() { sel_max_line_cnt = tc_LINES - (tc_COLS - mousebar_width < 50? 6 : 5); sel_chars = getval("SELECTCHARS", SELECTCHARS); /* The numeric option of up to 99 lines will require many adaptations * to be able to switch from a large numeric page (more than * strlen(sel_chars) lines) to an alphanumeric page. XXX */ if (UseSelNum) sel_max_per_page = 99; else sel_max_per_page = strlen(sel_chars); if (sel_max_per_page > MAX_SEL) sel_max_per_page = MAX_SEL; if (sel_max_per_page > sel_max_line_cnt) sel_max_per_page = sel_max_line_cnt; sel_page_obj_cnt = 0; sel_page_item_cnt = 0; } void init_pages(fill_last_page) bool_int fill_last_page; { SEL_UNION no_search; no_search.op = -1; sel_page_init(); try_again: sel_prior_obj_cnt = sel_total_obj_cnt = 0; switch (sel_mode) { case SM_MULTIRC: { MULTIRC* mp; for (mp = multirc_low(); mp; mp = multirc_next(mp)) { if (mp->first) { mp->flags |= MF_INCLUDED; sel_total_obj_cnt++; } else mp->flags &= ~MF_INCLUDED; } if (sel_page_mp == NULL) (void) first_page(); break; } case SM_NEWSGROUP: { NGDATA* np; bool save_the_rest = FALSE; group_init_done = TRUE; sort_newsgroups(); selected_count = 0; obj_count = 0; for (np = first_ng; np; np = np->next) { if (sel_page_np == np) sel_prior_obj_cnt = sel_total_obj_cnt; np->flags &= ~NF_INCLUDED; if (np->toread < TR_NONE) continue; if (!inlist(np->rcline)) continue; if (np->abs1st) ; else if (save_the_rest) { group_init_done = FALSE; np->toread = !sel_rereading; } else { /*ngptr = np; ??*/ /*set_ngname(np->rcline);*/ set_toread(np, ST_LAX); if (!np->rc->datasrc->act_sf.fp) save_the_rest = (sel_rereading ^ (np->toread > TR_NONE)); } if (paranoid) { current_ng = ngptr = np; /* this may move newsgroups around */ cleanup_newsrc(np->rc); goto try_again; } if (!(np->flags & sel_mask) && (sel_rereading? np->toread != TR_NONE : np->toread < ng_min_toread)) continue; obj_count++; if (!sel_exclusive || (np->flags & sel_mask)) { if (np->flags & sel_mask) selected_count++; else if (sel_rereading) np->flags |= NF_DEL; np->flags |= NF_INCLUDED; sel_total_obj_cnt++; } } if (!sel_total_obj_cnt) { if (sel_exclusive) { sel_exclusive = FALSE; sel_page_np = NULL; goto try_again; } if (maxngtodo) { end_only(); fputs(msg, stdout); newline(); if (fill_last_page) get_anything(); sel_page_np = NULL; goto try_again; } } if (!sel_page_np) (void) first_page(); else if (sel_page_np == last_ng) (void) last_page(); else if (sel_prior_obj_cnt && fill_last_page) { calc_page(no_search); if (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt) (void) last_page(); } break; } case SM_ADDGROUP: { ADDGROUP* gp; sort_addgroups(); obj_count = 0; for (gp = first_addgroup; gp; gp = gp->next) { if (sel_page_gp == gp) sel_prior_obj_cnt = sel_total_obj_cnt; gp->flags &= ~AGF_INCLUDED; if (!sel_rereading ^ !(gp->flags & AGF_EXCLUDED)) continue; if (!sel_exclusive || (gp->flags & sel_mask)) { if (sel_rereading && !(gp->flags & sel_mask)) gp->flags |= AGF_DEL; gp->flags |= AGF_INCLUDED; sel_total_obj_cnt++; } obj_count++; } if (!sel_total_obj_cnt && sel_exclusive) { sel_exclusive = FALSE; sel_page_gp = NULL; goto try_again; } if (sel_page_gp == NULL) (void) first_page(); else if (sel_page_gp == last_addgroup) (void) last_page(); else if (sel_prior_obj_cnt && fill_last_page) { calc_page(no_search); if (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt) (void) last_page(); } break; } case SM_UNIVERSAL: { UNIV_ITEM* ui; bool ui_elig; obj_count = 0; sort_univ(); for (ui = first_univ; ui; ui = ui->next) { if (sel_page_univ == ui) sel_prior_obj_cnt = sel_total_obj_cnt; ui->flags &= ~UF_INCLUDED; ui_elig = TRUE; switch (ui->type) { case UN_GROUP_DESEL: case UN_VGROUP_DESEL: case UN_DELETED: case UN_VGROUP: /* first-pass item */ /* always ineligible items */ ui_elig = FALSE; break; case UN_NEWSGROUP: { NGDATA* np; if (!ui->data.group.ng) { ui_elig = FALSE; break; } np = find_ng(ui->data.group.ng); if (!np) { ui_elig = FALSE; break; } if (!np->abs1st) { toread_quiet = TRUE; set_toread(np, ST_LAX); toread_quiet = FALSE; } if (!(sel_rereading ^ (np->toread>TR_NONE))) { ui_elig = FALSE; } break; } case UN_ARTICLE: /* later: use the datasrc of the newsgroup */ ui_elig = !was_read_group(datasrc, ui->data.virt.num, ui->data.virt.ng); if (sel_rereading) ui_elig = !ui_elig; break; default: ui_elig = !sel_rereading; break; } if (!ui_elig) continue; if (!sel_exclusive || (ui->flags & sel_mask)) { if (sel_rereading && !(ui->flags & sel_mask)) ui->flags |= UF_DEL; ui->flags |= UF_INCLUDED; sel_total_obj_cnt++; } obj_count++; } if (!sel_total_obj_cnt && sel_exclusive) { sel_exclusive = FALSE; sel_page_univ = NULL; goto try_again; } if (sel_page_univ == NULL) (void) first_page(); else if (sel_page_univ == last_univ) (void) last_page(); else if (sel_prior_obj_cnt && fill_last_page) { calc_page(no_search); if (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt) (void) last_page(); } break; } case SM_OPTIONS: { int op; int included = 0; obj_count = 0; for (op = 1; options_ini[op].checksum; op++) { if (sel_page_op == op) sel_prior_obj_cnt = sel_total_obj_cnt; if (*options_ini[op].item == '*') { included = (option_flags[op] & OF_SEL); sel_total_obj_cnt++; option_flags[op] |= OF_INCLUDED; } else if (included) { sel_total_obj_cnt++; option_flags[op] |= OF_INCLUDED; } else option_flags[op] &= ~OF_INCLUDED; obj_count++; } #if 0 if (!sel_total_obj_cnt && sel_exclusive) { sel_exclusive = FALSE; sel_page_op = NULL; goto try_again; } #endif if (sel_page_op < 1) (void) first_page(); else if (sel_page_op >= obj_count) (void) last_page(); else if (sel_prior_obj_cnt && fill_last_page) { calc_page(no_search); if (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt) (void) last_page(); } break; } case SM_ARTICLE: { ARTICLE* ap; ARTICLE** app; ARTICLE** limit; if (sel_page_app) { int desired_flags = (sel_rereading? AF_EXISTS:(AF_EXISTS|AF_UNREAD)); limit = artptr_list + artptr_list_size; ap = NULL; for (app = sel_page_app; app < limit; app++) { ap = *app; if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == desired_flags) break; } sort_articles(); if (ap == NULL) sel_page_app = artptr_list + artptr_list_size; else { for (app = artptr_list; app < limit; app++) { if (*app == ap) { sel_page_app = app; break; } } } } else sort_articles(); while (sel_page_sp && sel_page_sp->misc == 0) sel_page_sp = sel_page_sp->next; /* The artptr_list contains only unread or read articles, never both */ limit = artptr_list + artptr_list_size; for (app = artptr_list; app < limit; app++) { ap = *app; if (sel_rereading && !(ap->flags & sel_mask)) ap->flags |= AF_DEL; if (sel_page_app == app || (!sel_page_app && ap->subj == sel_page_sp)) { sel_page_app = app; sel_prior_obj_cnt = sel_total_obj_cnt; } if (!sel_exclusive || (ap->flags & sel_mask)) { sel_total_obj_cnt++; ap->flags |= AF_INCLUDED; } else ap->flags &= ~AF_INCLUDED; } if (sel_exclusive && !sel_total_obj_cnt) { sel_exclusive = FALSE; sel_page_app = NULL; goto try_again; } if (!sel_page_app) (void) first_page(); else if (sel_page_app >= limit) (void) last_page(); else if (sel_prior_obj_cnt && fill_last_page) { calc_page(no_search); if (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt) (void) last_page(); } break; } default: { SUBJECT* sp; SUBJECT* group_sp; int group_arts; if (sel_page_sp) { while (sel_page_sp && sel_page_sp->misc == 0) sel_page_sp = sel_page_sp->next; sort_subjects(); if (!sel_page_sp) sel_page_sp = last_subject; } else sort_subjects(); for (sp = first_subject; sp; sp = sp->next) { if (sel_rereading && !(sp->flags & sel_mask)) sp->flags |= SF_DEL; group_sp = sp; group_arts = sp->misc; if (!sel_exclusive || (sp->flags & sel_mask)) sp->flags |= SF_INCLUDED; else sp->flags &= ~SF_INCLUDED; if (sel_page_sp == group_sp) sel_prior_obj_cnt = sel_total_obj_cnt; if (sel_mode == SM_THREAD) { while (sp->next && sp->next->thread == sp->thread) { sp = sp->next; if (sp == sel_page_sp) { sel_prior_obj_cnt = sel_total_obj_cnt; sel_page_sp = group_sp; } sp->flags &= ~SF_INCLUDED; if (sp->flags & sel_mask) group_sp->flags |= SF_INCLUDED; else if (sel_rereading) sp->flags |= SF_DEL; group_arts += sp->misc; } } if (group_sp->flags & SF_INCLUDED) sel_total_obj_cnt += group_arts; } if (sel_exclusive && !sel_total_obj_cnt) { sel_exclusive = FALSE; sel_page_sp = NULL; goto try_again; } if (!sel_page_sp) (void) first_page(); else if (sel_page_sp == last_subject) (void) last_page(); else if (sel_prior_obj_cnt && fill_last_page) { calc_page(no_search); if (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt) (void) last_page(); } } } } bool first_page() { sel_prior_obj_cnt = 0; switch (sel_mode) { case SM_MULTIRC: { MULTIRC* mp; for (mp = multirc_low(); mp; mp = multirc_next(mp)) { if (mp->flags & MF_INCLUDED) { if (sel_page_mp != mp) { sel_page_mp = mp; return TRUE; } break; } } break; } case SM_NEWSGROUP: { NGDATA* np; for (np = first_ng; np; np = np->next) { if (np->flags & NF_INCLUDED) { if (sel_page_np != np) { sel_page_np = np; return TRUE; } break; } } break; } case SM_ADDGROUP: { ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) { if (gp->flags & AGF_INCLUDED) { if (sel_page_gp != gp) { sel_page_gp = gp; return TRUE; } break; } } break; } case SM_UNIVERSAL: { UNIV_ITEM* ui; for (ui = first_univ; ui; ui = ui->next) { if (ui->flags & UF_INCLUDED) { if (sel_page_univ != ui) { sel_page_univ = ui; return TRUE; } break; } } break; } case SM_OPTIONS: { if (sel_page_op != 1) { sel_page_op = 1; return TRUE; } break; } case SM_ARTICLE: { ARTICLE** app; ARTICLE** limit; limit = artptr_list + artptr_list_size; for (app = artptr_list; app < limit; app++) { if ((*app)->flags & AF_INCLUDED) { if (sel_page_app != app) { sel_page_app = app; return TRUE; } break; } } break; } default: { SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_INCLUDED) { if (sel_page_sp != sp) { sel_page_sp = sp; return TRUE; } break; } } break; } } return FALSE; } bool last_page() { sel_prior_obj_cnt = sel_total_obj_cnt; switch (sel_mode) { case SM_MULTIRC: { MULTIRC* mp = sel_page_mp; sel_page_mp = NULL; if (!prev_page()) sel_page_mp = mp; else if (mp != sel_page_mp) return TRUE; break; } case SM_NEWSGROUP: { NGDATA* np = sel_page_np; sel_page_np = NULL; if (!prev_page()) sel_page_np = np; else if (np != sel_page_np) return TRUE; break; } case SM_ADDGROUP: { ADDGROUP* gp = sel_page_gp; sel_page_gp = NULL; if (!prev_page()) sel_page_gp = gp; else if (gp != sel_page_gp) return TRUE; break; } case SM_UNIVERSAL: { UNIV_ITEM* ui = sel_page_univ; sel_page_univ = NULL; if (!prev_page()) sel_page_univ = ui; else if (ui != sel_page_univ) return TRUE; break; } case SM_OPTIONS: { int op = sel_page_op; sel_page_op = obj_count+1; if (!prev_page()) sel_page_op = op; else if (op != sel_page_op) return TRUE; break; } case SM_ARTICLE: { ARTICLE** app = sel_page_app; sel_page_app = artptr_list + artptr_list_size; if (!prev_page()) sel_page_app = app; else if (app != sel_page_app) return TRUE; break; } default: { SUBJECT* sp = sel_page_sp; sel_page_sp = NULL; if (!prev_page()) sel_page_sp = sp; else if (sp != sel_page_sp) return TRUE; break; } } return FALSE; } bool next_page() { switch (sel_mode) { case SM_MULTIRC: { if (sel_next_mp) { sel_page_mp = sel_next_mp; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } case SM_NEWSGROUP: { if (sel_next_np) { sel_page_np = sel_next_np; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } case SM_ADDGROUP: { if (sel_next_gp) { sel_page_gp = sel_next_gp; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } case SM_UNIVERSAL: { if (sel_next_univ) { sel_page_univ = sel_next_univ; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } case SM_OPTIONS: { if (sel_next_op <= obj_count) { sel_page_op = sel_next_op; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } case SM_ARTICLE: { if (sel_next_app < artptr_list + artptr_list_size) { sel_page_app = sel_next_app; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } default: { if (sel_next_sp) { sel_page_sp = sel_next_sp; sel_prior_obj_cnt += sel_page_obj_cnt; return TRUE; } break; } } return FALSE; } bool prev_page() { int item_cnt = 0; /* Scan the items in reverse to go back a page */ switch (sel_mode) { case SM_MULTIRC: { MULTIRC* mp = sel_page_mp; MULTIRC* page_mp = sel_page_mp; if (!mp) mp = multirc_high(); else mp = multirc_prev(multirc_ptr(mp->num)); while (mp) { if (mp->flags & MF_INCLUDED) { page_mp = mp; sel_prior_obj_cnt--; if (++item_cnt >= sel_max_per_page) break; } mp = multirc_prev(mp); } if (sel_page_mp != page_mp) { sel_page_mp = page_mp; return TRUE; } break; } case SM_NEWSGROUP: { NGDATA* np = sel_page_np; NGDATA* page_np = sel_page_np; if (!np) np = last_ng; else np = np->prev; while (np) { if (np->flags & NF_INCLUDED) { page_np = np; sel_prior_obj_cnt--; if (++item_cnt >= sel_max_per_page) break; } np = np->prev; } if (sel_page_np != page_np) { sel_page_np = page_np; return TRUE; } break; } case SM_ADDGROUP: { ADDGROUP* gp = sel_page_gp; ADDGROUP* page_gp = sel_page_gp; if (!gp) gp = last_addgroup; else gp = gp->prev; while (gp) { if (gp->flags & AGF_INCLUDED) { page_gp = gp; sel_prior_obj_cnt--; if (++item_cnt >= sel_max_per_page) break; } gp = gp->prev; } if (sel_page_gp != page_gp) { sel_page_gp = page_gp; return TRUE; } break; } case SM_UNIVERSAL: { UNIV_ITEM* ui = sel_page_univ; UNIV_ITEM* page_ui = sel_page_univ; if (!ui) ui = last_univ; else ui = ui->prev; while (ui) { if (ui->flags & UF_INCLUDED) { page_ui = ui; sel_prior_obj_cnt--; if (++item_cnt >= sel_max_per_page) break; } ui = ui->prev; } if (sel_page_univ != page_ui) { sel_page_univ = page_ui; return TRUE; } break; } case SM_OPTIONS: { int op = sel_page_op; int page_op = sel_page_op; while (--op > 0) { if (option_flags[op] & OF_INCLUDED) { page_op = op; sel_prior_obj_cnt--; if (++item_cnt >= sel_max_per_page) break; } } if (sel_page_op != page_op) { sel_page_op = page_op; return TRUE; } break; } case SM_ARTICLE: { ARTICLE** app; ARTICLE** page_app = sel_page_app; for (app = sel_page_app; --app >= artptr_list; ) { if ((*app)->flags & AF_INCLUDED) { page_app = app; sel_prior_obj_cnt--; if (++item_cnt >= sel_max_per_page) break; } } if (sel_page_app != page_app) { sel_page_app = page_app; return TRUE; } } default: { SUBJECT* sp; SUBJECT* page_sp = sel_page_sp; int line_cnt, item_arts, line; line = 2; for (sp = (!page_sp? last_subject : page_sp->prev); sp; sp=sp->prev) { item_arts = sp->misc; if (sel_mode == SM_THREAD) { while (sp->prev && sp->prev->thread == sp->thread) { sp = sp->prev; item_arts += sp->misc; } line_cnt = count_thread_lines(sp, (int*)NULL); } else line_cnt = count_subject_lines(sp, (int*)NULL); if (!(sp->flags & SF_INCLUDED) || !line_cnt) continue; if (line_cnt > sel_max_line_cnt) line_cnt = sel_max_line_cnt; line += line_cnt; if (line > sel_max_line_cnt + 2) { sp = page_sp; break; } sel_prior_obj_cnt -= item_arts; page_sp = sp; if (++item_cnt >= sel_max_per_page) break; } if (sel_page_sp != page_sp) { sel_page_sp = (page_sp? page_sp : first_subject); return TRUE; } break; } } return FALSE; } /* Return TRUE if we had to change pages to find the object */ bool calc_page(u) SEL_UNION u; { int ret = FALSE; if (u.op != -1) sel_item_index = -1; try_again: sel_page_obj_cnt = 0; sel_page_item_cnt = 0; term_line = 2; switch (sel_mode) { case SM_MULTIRC: { MULTIRC* mp = sel_page_mp; if (mp) (void) multirc_ptr(mp->num); for (; mp && sel_page_item_cntflags & MF_INCLUDED) sel_page_item_cnt++; } sel_page_obj_cnt = sel_page_item_cnt; sel_next_mp = mp; break; } case SM_NEWSGROUP: { NGDATA* np = sel_page_np; for (; np && sel_page_item_cnt < sel_max_per_page; np = np->next) { if (np == u.np) sel_item_index = sel_page_item_cnt; if (np->flags & NF_INCLUDED) sel_page_item_cnt++; } sel_page_obj_cnt = sel_page_item_cnt; sel_next_np = np; break; } case SM_ADDGROUP: { ADDGROUP* gp = sel_page_gp; for (; gp && sel_page_item_cnt < sel_max_per_page; gp = gp->next) { if (gp == u.gp) sel_item_index = sel_page_item_cnt; if (gp->flags & AGF_INCLUDED) sel_page_item_cnt++; } sel_page_obj_cnt = sel_page_item_cnt; sel_next_gp = gp; break; } case SM_UNIVERSAL: { UNIV_ITEM* ui = sel_page_univ; for (; ui && sel_page_item_cnt < sel_max_per_page; ui = ui->next) { if (ui == u.un) sel_item_index = sel_page_item_cnt; if (ui->flags & UF_INCLUDED) sel_page_item_cnt++; } sel_page_obj_cnt = sel_page_item_cnt; sel_next_univ = ui; break; } case SM_OPTIONS: { int op = sel_page_op; for (; op <= obj_count && sel_page_item_cnt < sel_max_per_page; op++) { if (op == u.op) sel_item_index = sel_page_item_cnt; if (option_flags[op] & OF_INCLUDED) sel_page_item_cnt++; } sel_page_obj_cnt = sel_page_item_cnt; sel_next_op = op; break; } case SM_ARTICLE: { ARTICLE** app = sel_page_app; ARTICLE** limit = artptr_list + artptr_list_size; for (; app < limit && sel_page_item_cnt < sel_max_per_page; app++) { if (*app == u.ap) sel_item_index = sel_page_item_cnt; if ((*app)->flags & AF_INCLUDED) sel_page_item_cnt++; } sel_page_obj_cnt = sel_page_item_cnt; sel_next_app = app; break; } default: { SUBJECT* sp = sel_page_sp; int line_cnt, sel; for (; sp && sel_page_item_cnt < sel_max_per_page; sp = sp->next) { if (sp == u.sp) sel_item_index = sel_page_item_cnt; if (sp->flags & SF_INCLUDED) { if (sel_mode == SM_THREAD) line_cnt = count_thread_lines(sp, &sel); else line_cnt = count_subject_lines(sp, &sel); if (line_cnt) { if (line_cnt > sel_max_line_cnt) line_cnt = sel_max_line_cnt; if (term_line + line_cnt > sel_max_line_cnt+2) break; sel_page_obj_cnt += sp->misc; sel_page_item_cnt++; } } else line_cnt = 0; if (sel_mode == SM_THREAD) { while (sp->next && sp->next->thread == sp->thread) { sp = sp->next; if (!line_cnt || !sp->misc) continue; sel_page_obj_cnt += sp->misc; } } } sel_next_sp = sp; break; } } if (u.op != -1 && sel_item_index < 0) { if (!ret) { if (first_page()) { ret = TRUE; goto try_again; } } if (next_page()) { ret = TRUE; goto try_again; } first_page(); sel_item_index = 0; } return ret; } void display_page_title(home_only) bool_int home_only; { if (home_only || (erase_screen && erase_each_line)) home_cursor(); else clear(); if (sel_mode == SM_MULTIRC) color_string(COLOR_HEADING,"Newsrcs"); else if (sel_mode == SM_OPTIONS) color_string(COLOR_HEADING,"Options"); else if (sel_mode == SM_UNIVERSAL) { color_object(COLOR_HEADING, 1); printf("[%d] %s",univ_level, univ_title? univ_title : "Universal Selector"); color_pop(); } else if (in_ng) color_string(COLOR_NGNAME, ngname); else if (sel_mode == SM_ADDGROUP) color_string(COLOR_HEADING,"ADD newsgroups"); else { int len; NEWSRC* rp; color_object(COLOR_HEADING, 1); printf("Newsgroups"); for (rp = multirc->first, len = 0; rp && len < 34; rp = rp->next) { if (rp->flags & RF_ACTIVE) { sprintf(buf+len, ", %s", rp->datasrc->name); len += strlen(buf+len); } } if (rp) strcpy(buf+len, ", ..."); if (strNE(buf+2,"default")) printf(" (group #%d: %s)",multirc->num, buf+2); color_pop(); /* of COLOR_HEADING */ } if (home_only || (erase_screen && erase_each_line)) erase_eol(); if (sel_mode == SM_MULTIRC) ; else if (sel_mode == SM_UNIVERSAL) ; else if (sel_mode == SM_OPTIONS) printf(" (Press 'S' to save changes, 'q' to abandon, or TAB to use.)"); else if (in_ng) { printf(" %ld %sarticle%s", (long)sel_total_obj_cnt, sel_rereading? "read " : nullstr, sel_total_obj_cnt == 1 ? nullstr : "s"); if (sel_exclusive) printf(" out of %ld", (long)obj_count); fputs(moderated,stdout); } else { printf(" %s%ld group%s",group_init_done? nullstr : "~", (long)sel_total_obj_cnt, PLURAL(sel_total_obj_cnt)); if (sel_exclusive) printf(" out of %ld", (long)obj_count); if (maxngtodo) printf(" (Restriction)"); } home_cursor(); newline(); maybe_eol(); if (in_ng && redirected && redirected != nullstr) printf("\t** Please start using %s **", redirected); newline(); } void display_page() { int sel; display_page_title(FALSE); try_again: sel_page_init(); if (!sel_total_obj_cnt) ; else if (sel_mode == SM_MULTIRC) { MULTIRC* mp = sel_page_mp; NEWSRC* rp; int len; if (mp) (void) multirc_ptr(mp->num); for (; mp && sel_page_item_cntflags & MF_INCLUDED)) continue; sel = !!(mp->flags & sel_mask); sel_items[sel_page_item_cnt].u.mp = mp; sel_items[sel_page_item_cnt].line = term_line; sel_items[sel_page_item_cnt].sel = sel; sel_page_obj_cnt++; maybe_eol(); for (rp = mp->first, len = 0; rp && len < 34; rp = rp->next) { sprintf(buf+len, ", %s", rp->datasrc->name); len += strlen(buf+len); } if (rp) strcpy(buf+len, ", ..."); output_sel(sel_page_item_cnt, sel, FALSE); printf("%5d %s\n", mp->num, buf+2); termdown(1); sel_page_item_cnt++; } if (!sel_page_obj_cnt) { if (last_page()) goto try_again; } sel_next_mp = mp; } else if (sel_mode == SM_NEWSGROUP) { NGDATA* np; int max_len = 0; int outputting = (*sel_grp_dmode != 'l'); start_of_loop: for (np = sel_page_np; np; np = np->next) { if (np == ngptr) sel_item_index = sel_page_item_cnt; if (!(np->flags & NF_INCLUDED)) continue; if (!np->abs1st) { set_toread(np, ST_LAX); if (paranoid) { newline(); current_ng = ngptr = np; /* this may move newsgroups around */ cleanup_newsrc(np->rc); init_pages(PRESERVE_PAGE); display_page_title(FALSE); goto try_again; } if (sel_rereading? (np->toread != TR_NONE) : (np->toread < ng_min_toread)) { np->flags &= ~NF_INCLUDED; sel_total_obj_cnt--; newsgroup_toread--; missing_count++; continue; } } if (sel_page_item_cnt >= sel_max_per_page) break; if (outputting) { sel = !!(np->flags & sel_mask) + (np->flags & NF_DEL); sel_items[sel_page_item_cnt].u.np = np; sel_items[sel_page_item_cnt].line = term_line; sel_items[sel_page_item_cnt].sel = sel; sel_page_obj_cnt++; maybe_eol(); output_sel(sel_page_item_cnt, sel, FALSE); printf("%5ld ", (long)np->toread); display_group(np->rc->datasrc,np->rcline,np->numoffset-1,max_len); } else if (np->numoffset >= max_len) max_len = np->numoffset + 1; sel_page_item_cnt++; } if (!outputting) { outputting = 1; sel_page_init(); goto start_of_loop; } if (!sel_page_obj_cnt) { if (last_page()) goto try_again; } sel_next_np = np; if (!group_init_done) { for (; np; np = np->next) { if (!np->abs1st) break; } if (!np) { int line = term_line; group_init_done = TRUE; display_page_title(TRUE); goto_xy(0,line); } } } else if (sel_mode == SM_ADDGROUP) { ADDGROUP* gp = sel_page_gp; int max_len = 0; if (*sel_grp_dmode == 'l') { int i = 0; int len; for (; gp && i < sel_max_per_page; gp = gp->next) { if (!(gp->flags & AGF_INCLUDED)) continue; len = strlen(gp->name)+2; if (len > max_len) max_len = len; i++; } gp = sel_page_gp; } for (; gp && sel_page_item_cnt < sel_max_per_page; gp = gp->next) { #if 0 if (gp == xx) sel_item_index = sel_page_item_cnt; #endif if (!(gp->flags & AGF_INCLUDED)) continue; sel = !!(gp->flags & sel_mask) + (gp->flags & AGF_DEL); sel_items[sel_page_item_cnt].u.gp = gp; sel_items[sel_page_item_cnt].line = term_line; sel_items[sel_page_item_cnt].sel = sel; sel_page_obj_cnt++; maybe_eol(); output_sel(sel_page_item_cnt, sel, FALSE); printf("%5ld ", (long)gp->toread); display_group(gp->datasrc, gp->name, strlen(gp->name), max_len); sel_page_item_cnt++; } if (!sel_page_obj_cnt) { if (last_page()) goto try_again; } sel_next_gp = gp; } else if (sel_mode == SM_UNIVERSAL) { UNIV_ITEM* ui = sel_page_univ; for (; ui && sel_page_item_cnt < sel_max_per_page; ui = ui->next) { #if 0 if (ui == xx) sel_item_index = sel_page_item_cnt; #endif if (!(ui->flags & UF_INCLUDED)) continue; sel = !!(ui->flags & sel_mask) + (ui->flags & UF_DEL); sel_items[sel_page_item_cnt].u.un = ui; sel_items[sel_page_item_cnt].line = term_line; sel_items[sel_page_item_cnt].sel = sel; sel_page_obj_cnt++; maybe_eol(); output_sel(sel_page_item_cnt, sel, FALSE); putchar(' '); display_univ(ui); sel_page_item_cnt++; } if (!sel_page_obj_cnt) { if (last_page()) goto try_again; } sel_next_univ = ui; } else if (sel_mode == SM_OPTIONS) { int op = sel_page_op; for (; op <= obj_count && sel_page_item_cntflags & AF_INCLUDED)) continue; sel = !!(ap->flags & sel_mask) + (ap->flags & AF_DEL); sel_items[sel_page_item_cnt].u.ap = ap; sel_items[sel_page_item_cnt].line = term_line; sel_items[sel_page_item_cnt].sel = sel; sel_page_obj_cnt++; /* Output the article, with optional author */ display_article(ap, sel_page_item_cnt, sel); sel_page_item_cnt++; } if (!sel_page_obj_cnt) { if (last_page()) goto try_again; } sel_next_app = app; } else { SUBJECT* sp; int line_cnt; int etc = 0; int ix = -1; /* item # or -1 */ sp = sel_page_sp; for (; sp && !etc && sel_page_item_cntnext) { if (sp == sel_last_sp) sel_item_index = sel_page_item_cnt; if (sp->flags & SF_INCLUDED) { /* Compute how many lines we need to display this group */ if (sel_mode == SM_THREAD) line_cnt = count_thread_lines(sp, &sel); else line_cnt = count_subject_lines(sp, &sel); if (line_cnt) { /* If this item is too long to fit on the screen all by ** itself, trim it to fit and set the "etc" flag. */ if (line_cnt > sel_max_line_cnt) { etc = line_cnt; line_cnt = sel_max_line_cnt; } /* If it doesn't fit, save it for the next page */ if (term_line + line_cnt > sel_max_line_cnt + 2) break; sel_items[sel_page_item_cnt].u.sp = sp; sel_items[sel_page_item_cnt].line = term_line; sel_items[sel_page_item_cnt].sel = sel; sel_page_obj_cnt += sp->misc; ix = sel_page_item_cnt; sel = sel_items[sel_page_item_cnt].sel; sel_page_item_cnt++; if (sp->misc) { display_subject(sp, ix, sel); ix = -1; } } } else line_cnt = 0; if (sel_mode == SM_THREAD) { while (sp->next && sp->next->thread == sp->thread) { sp = sp->next; if (!line_cnt || !sp->misc) continue; if (term_line < sel_max_line_cnt + 2) display_subject(sp, ix, sel); ix = -1; sel_page_obj_cnt += sp->misc; } } if (etc) printf(" ... etc. [%d lines total]", etc); } if (!sel_page_obj_cnt) { if (last_page()) goto try_again; } sel_next_sp = sp; } sel_last_ap = NULL; sel_last_sp = NULL; sel_at_end = (sel_prior_obj_cnt + sel_page_obj_cnt == sel_total_obj_cnt); maybe_eol(); newline(); sel_last_line = term_line; } void update_page() { SEL_UNION u; int sel; int j; for (j = 0; j < sel_page_item_cnt; j++) { u = sel_items[j].u; switch (sel_mode) { case SM_MULTIRC: sel = !!(u.mp->flags & sel_mask); break; case SM_NEWSGROUP: sel = !!(u.np->flags & sel_mask) + (u.np->flags & NF_DEL); break; case SM_ADDGROUP: sel = !!(u.gp->flags & sel_mask) + (u.gp->flags & AGF_DEL); break; case SM_UNIVERSAL: sel = !!(u.un->flags & sel_mask) + (u.un->flags & UF_DEL); break; case SM_OPTIONS: if (*options_ini[u.op].item == '*') sel = !!(option_flags[u.op] & OF_SEL); else sel = (INI_VALUE(options_ini,u.op)? 1 : (option_saved_vals[u.op]? 3 : (option_def_vals[u.op]? 0 : 2))); break; case SM_ARTICLE: sel = !!(u.ap->flags & sel_mask) + (u.ap->flags & AF_DEL); break; case SM_THREAD: (void) count_thread_lines(u.sp, &sel); break; default: (void) count_subject_lines(u.sp, &sel); break; } if (sel == sel_items[j].sel) continue; goto_xy(0,sel_items[j].line); sel_item_index = j; output_sel(sel_item_index, sel, TRUE); } if (++sel_item_index == sel_page_item_cnt) sel_item_index = 0; } void output_sel(ix, sel, update) int ix; int sel; bool_int update; { if (ix < 0) { if (UseSelNum) putchar(' '); putchar(' '); putchar(' '); return; } if (UseSelNum) { /* later consider no-leading-zero option */ printf("%02d",ix+1); } else putchar(sel_chars[ix]); switch (sel) { case 1: /* '+' */ color_object(COLOR_PLUS, 1); break; case 2: /* '-' */ color_object(COLOR_MINUS, 1); break; case 3: /* '*' */ color_object(COLOR_STAR, 1); break; default: color_object(COLOR_DEFAULT, 1); break; } putchar(sel_disp_char[sel]); color_pop(); /* of COLOR_PLUS/MINUS/STAR/DEFAULT */ if (update) sel_items[ix].sel = sel; } /* Counts the number of lines needed to output a subject, including ** optional authors. */ static int count_subject_lines(subj, selptr) SUBJECT* subj; int* selptr; { register ARTICLE* ap; register int sel; if (subj->flags & SF_DEL) sel = 2; else if (subj->flags & sel_mask) { sel = 1; for (ap = subj->articles; ap; ap = ap->subj_next) { if ((!!(ap->flags&AF_UNREAD) ^ sel_rereading) && !(ap->flags & sel_mask)) { sel = 3; break; } } } else sel = 0; if (selptr) *selptr = sel; if (*sel_art_dmode == 'l') return subj->misc; if (*sel_art_dmode == 'm') return (subj->misc <= 4? subj->misc : (subj->misc - 4) / 3 + 4); return (subj->misc != 0); } /* Counts the number of lines needed to output a thread, including ** optional authors. */ static int count_thread_lines(subj, selptr) SUBJECT* subj; int* selptr; { register int total = 0; register ARTICLE* thread = subj->thread; int sel = -1, subj_sel; do { if (subj->misc) { total += count_subject_lines(subj, &subj_sel); if (sel < 0) sel = subj_sel; else if (sel != subj_sel) sel = 3; } } while ((subj = subj->next) != NULL && subj->thread == thread); if (selptr) *selptr = (sel < 0? 0 : sel); return total; } /* Display an article, perhaps with its author. */ static void display_article(ap, ix, sel) register ARTICLE* ap; int ix; int sel; { int subj_width = tc_COLS - 5 - UseSelNum; int from_width = tc_COLS / 5; int date_width = tc_COLS / 5; maybe_eol(); if (subj_width < 32) subj_width = 32; output_sel(ix, sel, FALSE); if (*sel_art_dmode == 's' || from_width < 8) printf(" %s\n",compress_subj(ap->subj->articles,subj_width)) FLUSH; else if (*sel_art_dmode == 'd') { printf("%s %s\n", compress_date(ap, date_width), compress_subj(ap, subj_width - date_width)) FLUSH; } else { printf("%s %s\n", compress_from(ap->from, from_width), compress_subj(ap, subj_width - from_width)) FLUSH; } termdown(1); } /* Display the given subject group, with optional authors. */ static void display_subject(subj, ix, sel) SUBJECT* subj; int ix; int sel; { register ARTICLE* ap; register int j, i; int subj_width = tc_COLS - 8 - UseSelNum; int from_width = tc_COLS / 5; int date_width = tc_COLS / 5; maybe_eol(); if (subj_width < 32) subj_width = 32; j = subj->misc; output_sel(ix, sel, FALSE); if (*sel_art_dmode == 's' || from_width < 8) { printf("%3d %s\n",j,compress_subj(subj->articles,subj_width)) FLUSH; termdown(1); } else { ARTICLE* first_ap; /* Find the first unread article so we get the author right */ if ((first_ap = subj->thread) != NULL && (first_ap->subj != subj || first_ap->from == NULL || (!(first_ap->flags&AF_UNREAD) ^ sel_rereading))) first_ap = NULL; for (ap = subj->articles; ap; ap = ap->subj_next) { if (!!(ap->flags&AF_UNREAD) ^ sel_rereading) break; } if (!first_ap) first_ap = ap; if (*sel_art_dmode == 'd') { printf("%s%3d %s\n", compress_date(first_ap, date_width), j, compress_subj(first_ap, subj_width - date_width)) FLUSH; } else { printf("%s%3d %s\n", compress_from(first_ap? first_ap->from : NULL, from_width), j, compress_subj(first_ap, subj_width - from_width)) FLUSH; } termdown(1); i = -1; if (*sel_art_dmode != 'd' && --j && ap) { for ( ; ap && j; ap = ap->subj_next) { if ((!(ap->flags&AF_UNREAD) ^ sel_rereading) || ap == first_ap) continue; j--; if (i < 0) i = 0; else if (*sel_art_dmode == 'm') { if (!j) { if (i) newline(); } else { if (i == 3 || !i) { if (i) newline(); if (term_line >= sel_max_line_cnt + 2) return; maybe_eol(); i = 1; } else i++; if (UseSelNum) putchar(' '); printf(" %s ", compress_from(ap? ap->from : NULL, from_width)) FLUSH; continue; } } if (term_line >= sel_max_line_cnt + 2) return; maybe_eol(); if (UseSelNum) putchar(' '); printf(" %s\n", compress_from(ap? ap->from : NULL, from_width)) FLUSH; termdown(1); } } } } void display_option(op,item_index) int op; int item_index; { int len; char* pre; char* item; char* post; char* val; if (*options_ini[op].item == '*') { len = strlen(options_ini[op].item+1); pre = "=="; item = options_ini[op].item+1; post = "=================================="; val = nullstr; } else { len = (options_ini[op].checksum & 0xff); pre = " "; item = options_ini[op].item; post = ".................................."; val = INI_VALUE(options_ini,op); if (!val) val = quote_string(option_value(op)); } output_sel(item_index, sel_items[item_index].sel, FALSE); printf(" %s%s%s %.39s\n", pre, item, post + len, val); termdown(1); } static void display_univ(ui) UNIV_ITEM* ui; { if (!ui) { fputs("****EMPTY****",stdout); } else { switch(ui->type) { case UN_NEWSGROUP: { NGDATA* np; /* later error check the UI? */ np = find_ng(ui->data.group.ng); if (!np) printf("!!!!! could not find %s",ui->data.group.ng); else { int numarts; /* XXX set_toread() can print sometimes... */ if (!np->abs1st) set_toread(np, ST_LAX); numarts = np->toread; if (numarts >= 0) printf("%5ld ",(long)numarts); else if (numarts == TR_UNSUB) printf("UNSUB "); else printf("***** "); fputs(ui->data.group.ng,stdout); } newline(); break; } case UN_ARTICLE: printf(" %s",ui->desc? ui->desc : univ_article_desc(ui)); newline(); break; case UN_HELPKEY: printf(" Help on the %s",univ_keyhelp_modestr(ui)); newline(); break; default: printf(" %s",ui->desc? ui->desc : "[No Description]"); newline(); break; } } } static void display_group(dp, group, len, max_len) DATASRC* dp; char* group; int len; int max_len; { if (*sel_grp_dmode == 's') fputs(group, stdout); else { char* end; char* cp = find_grpdesc(dp, group); if (*cp != '?' && (end = index(cp, '\n')) != NULL && end != cp) { char ch; if (end - cp > tc_COLS - max_len - 8 - 1 - UseSelNum) end = cp + tc_COLS - max_len - 8 - 1 - UseSelNum; ch = *end; *end = '\0'; if (*sel_grp_dmode == 'm') fputs(cp, stdout); else { int i = max_len - len; fputs(group, stdout); do { putchar(' '); } while (--i > 0); fputs(cp, stdout); } *end = ch; } else fputs(group, stdout); } newline(); } trn-4.0-test77/rt-page.h0000644000000000000000000000257407113133016013524 0ustar rootroot/* rt-page.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #define PRESERVE_PAGE 0 #define FILL_LAST_PAGE 1 EXT int sel_total_obj_cnt; EXT int sel_prior_obj_cnt; EXT int sel_page_obj_cnt; EXT int sel_page_item_cnt; EXT int sel_max_per_page; EXT int sel_max_line_cnt; EXT ARTICLE** sel_page_app; EXT ARTICLE** sel_next_app; EXT ARTICLE* sel_last_ap; EXT SUBJECT* sel_page_sp; EXT SUBJECT* sel_next_sp; EXT SUBJECT* sel_last_sp; EXT char* sel_grp_dmode INIT("*slm"); EXT char* sel_art_dmode INIT("*lmds"); EXT bool group_init_done INIT(TRUE); union sel_union { ARTICLE* ap; SUBJECT* sp; ADDGROUP* gp; MULTIRC* mp; NGDATA* np; UNIV_ITEM* un; int op; }; struct sel_item { SEL_UNION u; int line; int sel; }; #define MAX_SEL 99 EXT SEL_ITEM sel_items[MAX_SEL]; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool set_sel_mode _((char_int)); char* get_sel_order _((int)); bool set_sel_order _((int,char*)); bool set_sel_sort _((int,char_int)); void set_selector _((int,int)); void init_pages _((bool_int)); bool first_page _((void)); bool last_page _((void)); bool next_page _((void)); bool prev_page _((void)); bool calc_page _((SEL_UNION)); void display_page_title _((bool_int)); void display_page _((void)); void update_page _((void)); void output_sel _((int,int,bool_int)); void display_option _((int,int)); trn-4.0-test77/rt-page.ih0000644000000000000000000000075007113133016013667 0ustar rootroot/* rt-page.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void sel_page_init _((void)); static int count_subject_lines _((SUBJECT*,int*)); static int count_thread_lines _((SUBJECT*,int*)); static void display_article _((ARTICLE*,int,int)); static void display_subject _((SUBJECT*,int,int)); static void display_univ _((UNIV_ITEM*)); static void display_group _((DATASRC*,char*,int,int)); trn-4.0-test77/rt-process.c0000644000000000000000000003434107116177123014270 0ustar rootroot/* rt-process.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "intrp.h" #include "trn.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "final.h" #include "ng.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "rcln.h" #include "util.h" #include "util2.h" #include "kfile.h" #include "rthread.h" #include "rt-select.h" #include "INTERN.h" #include "rt-process.h" #include "rt-process.ih" /* 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) article = article_ptr(artnum); else { article = (ARTICLE*)safemalloc(sizeof (ARTICLE)); bzero((char*)article, sizeof (ARTICLE)); article->flags |= AF_FAKE|AF_TMPMEM; } return article; } static void fix_msgid(msgid) char* msgid; { register char* cp; if ((cp = index(msgid, '@')) != NULL) { while (*++cp) { if (isupper(*cp)) { *cp = tolower(*cp); /* lower-case domain portion */ } } } } int msgid_cmp(key, keylen, data) char* key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ if (data.dat_len) return bcmp(key, data.dat_ptr, keylen); return bcmp(key, ((ARTICLE*)data.dat_ptr)->msgid, keylen); } SUBJECT* fake_had_subj; /* the fake-turned-real article had this subject */ bool valid_article(article) ARTICLE* article; { ARTICLE* ap; ARTICLE* fake_ap; char* msgid = article->msgid; HASHDATUM data; if (msgid) { fix_msgid(msgid); data = hashfetch(msgid_hash, msgid, strlen(msgid)); if (data.dat_len) { safefree0(data.dat_ptr); article->autofl = data.dat_len & (AUTO_SELS | AUTO_KILLS); if ((data.dat_len & KF_AGE_MASK) == 0) article->autofl |= AUTO_OLD; else kf_changethd_cnt++; data.dat_len = 0; } if ((fake_ap = (ARTICLE*)data.dat_ptr) == NULL) { data.dat_ptr = (char*)article; hashstorelast(data); fake_had_subj = NULL; return TRUE; } if (fake_ap == article) { fake_had_subj = NULL; 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 && (fake_ap->flags & AF_TMPMEM)) { article->parent = fake_ap->parent; article->child1 = fake_ap->child1; article->sibling = fake_ap->sibling; fake_had_subj = fake_ap->subj; if (fake_ap->autofl) { article->autofl |= fake_ap->autofl; kf_state |= kfs_thread_change_set; } 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) != NULL) { if (ap->child1 == fake_ap) ap->child1 = article; else { ap = ap->child1; /* This sibling-search code is duplicated below */ while (ap->sibling) { if (ap->sibling == fake_ap) { ap->sibling = article; break; } ap = ap->sibling; } /* End of slibling-search code */ } } 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 { /* This sibling-search code is duplicated above */ while (ap->sibling) { if (ap->sibling == fake_ap) { ap->sibling = article; break; } ap = ap->sibling; } /* End of slibling-search code */ } } for (ap = article->child1; ap; ap = ap->sibling) ap->parent = article; clear_article(fake_ap); free((char*)fake_ap); data.dat_ptr = (char*)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 (data.dat_len) { article = allocate_article(0); article->autofl = data.dat_len & (AUTO_SELS | AUTO_KILLS); if ((data.dat_len & KF_AGE_MASK) == 0) article->autofl |= AUTO_OLD; else kf_changethd_cnt++; article->msgid = data.dat_ptr; data.dat_ptr = (char*)article; data.dat_len = 0; hashstorelast(data); } else if (!(article = (ARTICLE*)data.dat_ptr)) { 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,references) ARTICLE* article; char* references; { register ARTICLE* ap; register ARTICLE* prev; register char* cp; register char* end; int chain_autofl = (article->autofl | (article->subj->articles? article->subj->articles->autofl : 0)); int thread_autofl, subj_autofl = 0; int rethreading = article->flags & AF_THREADED; /* 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) { ARTICLE* stopper; if (fake_had_subj->thread != article->subj->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) && !ap->child1->sibling) { prev = ap; ap = ap->parent; } stopper = ap; unlink_child(article); /* 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 != stopper; ap = prev) { unlink_child(ap); prev = 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 = NULL; /* neaten up */ article->sibling = NULL; } /* 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 && *references) { prev = article; ap = NULL; if ((cp = rindex(references, '<')) == NULL || (end = index(cp+1, ' ')) == NULL) end = references + strlen(references) - 1; while (cp) { while (end >= cp && end > references && (*(unsigned char*)end <= ' ' || *end == ',')) { end--; } if (end <= cp) break; 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'; chain_autofl |= ap->autofl; if (ap->subj == article->subj) subj_autofl |= ap->autofl; /* 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 = prev) == article) ap = NULL; goto next; } /* When we're doing late processing of In-Reply-To: lines, we may ** have to move an article from an old position. */ if (rethreading && prev->subj) unlink_child(prev); prev->parent = ap; link_child(prev); if (ap->subj) break; ap->date = article->date; prev = ap; next: if (cp > references) end = cp-1; else end = cp; cp = rindex(references, '<'); } 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 = NULL; 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. Don't ** touch it at all unless this is the first attempt at threading it. */ if (!rethreading) link_child(article); } if (!(article->flags & AF_CACHED)) cache_article(article); thread_autofl = chain_autofl; if (sel_mode == SM_THREAD) { SUBJECT* sp = article->subj->thread_link; while (sp != article->subj) { if (sp->articles) thread_autofl |= sp->articles->autofl; sp = sp->thread_link; } } subj_autofl |= article->subj->articles->autofl; perform_auto_flags(article, thread_autofl, subj_autofl, chain_autofl); } void rover_thread(article, s) ARTICLE* article; char* s; { ARTICLE* prev = article; char* end; char ch; for (;;) { while (*++s == ' ') ; if (isdigit(*s)) { article = article_ptr(atol(s)); prev->parent = article; link_child(prev); break; } end = index(s, '>'); if (!end) return; /* Impossible! */ ch = end[1]; end[1] = '\0'; article = get_article(s); prev->parent = article; link_child(prev); if (!ch) break; end[1] = ch; s = end; } } /* Check if the string we've found looks like a valid message-id reference. */ static char* valid_message_id(start, end) register char* start; register char* 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, '@')) == NULL || 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 && last->sibling != child) last = last->sibling; if (last) 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. */ 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. */ void merge_threads(s1, s2) SUBJECT* s1; SUBJECT* s2; { register SUBJECT* sp; register ARTICLE* t1; register ARTICLE* t2; t1 = s1->thread; t2 = s2->thread; /* Change all of t2's thread pointers to a common lead article */ sp = s2; do { sp->thread = t1; 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; /* If thread mode is set, ensure the subjects are adjacent in the list. */ /* Don't do this if the selector is active, because it gets messed up. */ if (sel_mode == SM_THREAD && gmode != 's') { for (sp = s2; sp->prev && sp->prev->thread == t1; ) { sp = sp->prev; if (sp == s1) goto artlink; } while (s2->next && s2->next->thread == t1) { s2 = s2->next; if (s2 == s1) goto artlink; } /* Unlink the s2 chunk of subjects from the list */ if (!sp->prev) first_subject = s2->next; else sp->prev->next = s2->next; if (!s2->next) last_subject = sp->prev; else s2->next->prev = sp->prev; /* Link the s2 chunk after s1 */ sp->prev = s1; s2->next = s1->next; if (!s1->next) last_subject = s2; else s1->next->prev = s2; s1->next = sp; } artlink: /* 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 */ } } trn-4.0-test77/rt-process.h0000644000000000000000000000070607113133016014261 0ustar rootroot/* rt-process.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ ARTICLE* allocate_article _((ART_NUM)); int msgid_cmp _((char*,int,HASHDATUM)); bool valid_article _((ARTICLE*)); ARTICLE* get_article _((char*)); void thread_article _((ARTICLE*,char*)); void rover_thread _((ARTICLE*,char*)); void link_child _((ARTICLE*)); void merge_threads _((SUBJECT*,SUBJECT*)); trn-4.0-test77/rt-process.ih0000644000000000000000000000042407113133016014427 0ustar rootroot/* rt-process.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void fix_msgid _((char*)); static char* valid_message_id _((char*,char*)); static void unlink_child _((ARTICLE*)); trn-4.0-test77/rt-select.c0000644000000000000000000020276011437640112014065 0ustar rootroot/* rt-select.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "term.h" #include "final.h" #include "util.h" #include "util2.h" #include "help.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "artsrch.h" #include "ng.h" #include "opt.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "nntp.h" #include "ngstuff.h" #include "ngsrch.h" #include "rcstuff.h" #include "rcln.h" #include "kfile.h" #include "intrp.h" #include "search.h" #include "rthread.h" #include "univ.h" #include "rt-page.h" #include "rt-util.h" #include "color.h" #include "only.h" #ifdef USE_TK #include "tkstuff.h" #include "tktree.h" #endif #include "INTERN.h" #include "rt-select.h" #include "rt-select.ih" static char sel_ret; static char page_char, end_char; static int disp_status_line; static bool clean_screen; static int removed_prompt; static int force_sel_pos; #define START_SELECTOR(new_mode)\ char save_mode = mode;\ char save_gmode = gmode;\ bos_on_stop = TRUE;\ set_mode('s',new_mode) #define END_SELECTOR()\ bos_on_stop = FALSE;\ set_mode(save_gmode,save_mode) #define PUSH_SELECTOR()\ int save_sel_mode = sel_mode;\ bool save_sel_rereading = sel_rereading;\ bool save_sel_exclusive = sel_exclusive;\ ART_UNREAD save_selected_count = selected_count;\ int (*save_extra_commands) _((char_int)) = extra_commands #define POP_SELECTOR()\ sel_exclusive = save_sel_exclusive;\ sel_rereading = save_sel_rereading;\ selected_count = save_selected_count;\ extra_commands = save_extra_commands;\ bos_on_stop = TRUE;\ if (sel_mode != save_sel_mode) {\ sel_mode = save_sel_mode;\ set_selector(0, 0);\ save_sel_mode = 0;\ } #define PUSH_UNIV_SELECTOR()\ UNIV_ITEM* save_first_univ = first_univ;\ UNIV_ITEM* save_last_univ = last_univ;\ UNIV_ITEM* save_page_univ = sel_page_univ;\ UNIV_ITEM* save_next_univ = sel_next_univ;\ char* save_univ_fname = univ_fname;\ char* save_univ_label = univ_label;\ char* save_univ_title = univ_title;\ char* save_univ_tmp_file = univ_tmp_file;\ char save_sel_ret = sel_ret;\ HASHTABLE* save_univ_ng_hash = univ_ng_hash;\ HASHTABLE* save_univ_vg_hash = univ_vg_hash #define POP_UNIV_SELECTOR()\ first_univ = save_first_univ;\ last_univ = save_last_univ;\ sel_page_univ = save_page_univ;\ sel_next_univ = save_next_univ;\ univ_fname = save_univ_fname;\ univ_label = save_univ_label;\ univ_title = save_univ_title;\ univ_tmp_file = save_univ_tmp_file;\ sel_ret = save_sel_ret;\ univ_ng_hash = save_univ_ng_hash;\ univ_vg_hash = save_univ_vg_hash static int (*extra_commands) _((char_int)); /* Display a menu of threads/subjects/articles for the user to choose from. ** If "cmd" is '+' we display all the unread items and allow the user to mark ** them as selected and perform various commands upon them. If "cmd" is 'U' ** the list consists of previously-read items for the user to mark as unread. */ char article_selector(cmd) char_int cmd; { bool save_selected_only; START_SELECTOR('t'); sel_rereading = (cmd == 'U'); art = lastart+1; extra_commands = article_commands; keep_the_group_static = (keep_the_group_static == 1); sel_mode = SM_ARTICLE; set_sel_mode(cmd); if (!cache_range(sel_rereading? absfirst : firstart, lastart)) { sel_ret = '+'; goto sel_exit; } sel_restart: /* Setup for selecting articles to read or set unread */ if (sel_rereading) { end_char = 'Z'; page_char = '>'; sel_page_app = NULL; sel_page_sp = NULL; sel_mask = AF_DELSEL; } else { end_char = NewsSelCmds[0]; page_char = NewsSelCmds[1]; if (curr_artp) { sel_last_ap = curr_artp; sel_last_sp = curr_artp->subj; } sel_mask = AF_SEL; } save_selected_only = selected_only; selected_only = TRUE; count_subjects(cmd? CS_UNSEL_STORE : CS_NORM); init_pages(FILL_LAST_PAGE); sel_item_index = 0; *msg = '\0'; if (added_articles) { register long i = added_articles, j; for (j = lastart-i+1; j <= lastart; j++) { if (!article_unread(j)) i--; } if (i == added_articles) sprintf(msg, "** %ld new article%s arrived ** ", (long)added_articles, PLURAL(added_articles)); else sprintf(msg, "** %ld of %ld new articles unread ** ", i, (long)added_articles); disp_status_line = 1; } added_articles = 0; if (cmd && selected_count) { sprintf(msg+strlen(msg), "%ld article%s selected.", (long)selected_count, selected_count == 1? " is" : "s are"); disp_status_line = 1; } cmd = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; sel_cleanup(); newline(); if (mousebar_cnt) clear_rest(); sel_exit: if (sel_ret == '\033') sel_ret = '+'; else if (sel_ret == '`') sel_ret = 'Q'; if (sel_rereading) { sel_rereading = FALSE; sel_mask = AF_SEL; } if (sel_mode != SM_ARTICLE || sel_sort == SS_GROUPS || sel_sort == SS_STRING) { if (artptr_list) { free((char*)artptr_list); artptr_list = sel_page_app = NULL; sort_subjects(); } artptr = NULL; #ifdef ARTSEARCH if (!ThreadedGroup) srchahead = -1; #endif } #ifdef ARTSEARCH else srchahead = 0; #endif selected_only = (selected_count != 0); if (sel_ret == '+') { selected_only = save_selected_only; count_subjects(CS_RESELECT); } else count_subjects(CS_UNSELECT); if (sel_ret == '+') { art = curr_art; artp = curr_artp; } else top_article(); END_SELECTOR(); return sel_ret; } static void sel_dogroups() { NGDATA* np; int ret; int save_selected_count = selected_count; for (np = first_ng; np; np = np->next) { if (!(np->flags & NF_VISIT)) continue; do_group: if (np->flags & NF_SEL) { np->flags &= ~NF_SEL; save_selected_count--; } set_ng(np); if (np != current_ng) { recent_ng = current_ng; current_ng = np; } ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("Entering %s:", ngname); #ifdef SCAN_ART if (sel_ret == ';') { ret = do_newsgroup(savestr(";")); } else #endif ret = do_newsgroup(nullstr); switch (ret) { case NG_NORM: case NG_SELNEXT: set_ng(np->next); break; case NG_NEXT: set_ng(np->next); goto loop_break; case NG_ERROR: case NG_ASK: goto loop_break; case NG_SELPRIOR: while ((np = np->prev) != NULL) { if (np->flags & NF_VISIT) goto do_group; } (void) first_page(); goto loop_break; case NG_MINUS: np = recent_ng; #if 0 /* CAA: I'm not sure why I wrote this originally--it seems unnecessary */ np->flags |= NF_VISIT; #endif goto do_group; #ifdef SUPPORT_NNTP case NG_NOSERVER: nntp_server_died(np->rc->datasrc); (void) first_page(); break; #endif /* CAA extensions */ case NG_GO_ARTICLE: np = ng_go_ngptr; goto do_group; /* later: possible go-to-newsgroup (applicable?) */ } } loop_break: selected_count = save_selected_count; } char multirc_selector() { START_SELECTOR('c'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_MULTIRC, 0); sel_restart: end_char = NewsrcSelCmds[0]; page_char = NewsrcSelCmds[1]; sel_mask = MF_SEL; extra_commands = multirc_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='Z' || sel_ret=='\t') { MULTIRC* mp; NEWSRC* rp; PUSH_SELECTOR(); for (mp = multirc_low(); mp; mp = multirc_next(mp)) { if (mp->flags & MF_SEL) { mp->flags &= ~MF_SEL; save_selected_count--; for (rp = mp->first; rp; rp = rp->next) rp->datasrc->flags &= ~DF_UNAVAILABLE; if (use_multirc(mp)) { find_new_groups(); do_multirc(); unuse_multirc(mp); } else mp->flags &= ~MF_INCLUDED; } } POP_SELECTOR(); goto sel_restart; } END_SELECTOR(); return sel_ret; } char newsgroup_selector() { START_SELECTOR('w'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_NEWSGROUP, 0); sel_restart: if (*sel_grp_dmode != 's') { NEWSRC* rp; for (rp = multirc->first; rp; rp = rp->next) { if ((rp->flags & RF_ACTIVE) && !rp->datasrc->desc_sf.hp) { find_grpdesc(rp->datasrc, "control"); #ifdef SUPPORT_NNTP if (rp->datasrc->desc_sf.fp) rp->datasrc->flags |= DF_NOXGTITLE; /*$$ ok?*/ else rp->datasrc->desc_sf.refetch_secs = 0; #endif } } } end_char = NewsgroupSelCmds[0]; page_char = NewsgroupSelCmds[1]; if (sel_rereading) { sel_mask = NF_DELSEL; sel_page_np = NULL; } else sel_mask = NF_SEL; extra_commands = newsgroup_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='Z' || sel_ret=='\t' #ifdef SCAN_ART || sel_ret==';' #endif ) { NGDATA* np; PUSH_SELECTOR(); for (np = first_ng; np; np = np->next) { if ((np->flags & NF_INCLUDED) && (!selected_count || (np->flags & sel_mask))) np->flags |= NF_VISIT; else np->flags &= ~NF_VISIT; } sel_dogroups(); save_selected_count = selected_count; POP_SELECTOR(); if (multirc) goto sel_restart; sel_ret = 'q'; } sel_cleanup(); END_SELECTOR(); end_only(); return sel_ret; } char addgroup_selector(flags) int flags; { START_SELECTOR('j'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_ADDGROUP, 0); sel_restart: if (*sel_grp_dmode != 's') { NEWSRC* rp; for (rp = multirc->first; rp; rp = rp->next) { if ((rp->flags & RF_ACTIVE) && !rp->datasrc->desc_sf.hp) { find_grpdesc(rp->datasrc, "control"); #ifdef SUPPORT_NNTP if (!rp->datasrc->desc_sf.fp) rp->datasrc->desc_sf.refetch_secs = 0; #endif } } } end_char = AddSelCmds[0]; page_char = AddSelCmds[1]; /* Setup for selecting articles to read or set unread */ if (sel_rereading) sel_mask = AGF_DELSEL; else sel_mask = AGF_SEL; sel_page_gp = NULL; extra_commands = addgroup_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; selected_count = 0; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='Z' || sel_ret=='\t') { ADDGROUP *gp; int i; addnewbydefault = ADDNEW_SUB; for (gp = first_addgroup, i = 0; gp; gp = gp->next, i++) { if (gp->flags & NF_SEL) { gp->flags &= ~NF_SEL; get_ng(gp->name,flags); } } addnewbydefault = 0; } sel_cleanup(); END_SELECTOR(); return sel_ret; } char option_selector() { int i; char** vals = INI_VALUES(options_ini); START_SELECTOR('l'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; parse_ini_section(nullstr, options_ini); set_selector(SM_OPTIONS, 0); sel_restart: end_char = OptionSelCmds[0]; page_char = OptionSelCmds[1]; if (sel_rereading) sel_mask = AF_DELSEL; else sel_mask = AF_SEL; sel_page_op = -1; extra_commands = option_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R' || sel_ret=='\r' || sel_ret=='\n') goto sel_restart; selected_count = 0; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='Z' || sel_ret=='\t' || sel_ret == 'S') { set_options(vals); if (sel_ret == 'S') save_options(ini_file); } for (i = 1; options_ini[i].checksum; i++) { if (vals[i]) { if (option_saved_vals[i] && strEQ(vals[i],option_saved_vals[i])) { if (option_saved_vals[i] != option_def_vals[i]) free(option_saved_vals[i]); option_saved_vals[i] = NULL; } free(vals[i]); vals[i] = NULL; } } END_SELECTOR(); return sel_ret; } /* returns a command to do */ static int univ_read(ui) UNIV_ITEM* ui; { int exit_code = UR_NORM; char ch; univ_follow_temp = FALSE; if (!ui) { printf("NULL UI passed to reader!\n") FLUSH; sleep(5); return exit_code; } printf("\n") FLUSH; /* prepare for output msgs... */ switch (ui->type) { case UN_DEBUG1: { char* s; s = ui->data.str; if (s && *s) { printf("Not implemented yet (%s)\n",s) FLUSH; sleep(5); return exit_code; } break; } case UN_TEXTFILE: { char* s; s = ui->data.textfile.fname; if (s && *s) { /* later have some way of getting a return code back */ univ_page_file(s); } break; } case UN_ARTICLE: { int ret; NGDATA* np; if (in_ng) { /* XXX whine: can't recurse at this time */ break; } if (!ui->data.virt.ng) break; /* XXX whine */ np = find_ng(ui->data.virt.ng); if (!np) { printf("Universal: newsgroup %s not found!", ui->data.virt.ng) FLUSH; sleep(5); return exit_code; } set_ng(np); if (np != current_ng) { recent_ng = current_ng; current_ng = np; } ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("Virtual: Entering %s:\n", ngname) FLUSH; ng_go_artnum = ui->data.virt.num; univ_read_virtflag = TRUE; ret = do_newsgroup(nullstr); univ_read_virtflag = FALSE; switch (ret) { case NG_NORM: /* handle more cases later */ case NG_SELNEXT: case NG_NEXT: /* just continue reading */ break; case NG_SELPRIOR: /* not implemented yet */ /* FALL THROUGH */ case NG_ERROR: case NG_ASK: exit_code = UR_BREAK; return exit_code; case NG_MINUS: /* not implemented */ break; default: break; } break; } case UN_GROUPMASK: { univ_mask_load(ui->data.gmask.masklist,ui->data.gmask.title); ch = universal_selector(); switch (ch) { case 'q': exit_code = UR_BREAK; break; default: exit_code = UR_NORM; break; } return exit_code; } case UN_CONFIGFILE: { univ_file_load(ui->data.cfile.fname,ui->data.cfile.title, ui->data.cfile.label); ch = universal_selector(); switch (ch) { case 'q': exit_code = UR_BREAK; break; default: exit_code = UR_NORM; break; } return exit_code; } case UN_NEWSGROUP: { int ret; NGDATA* np; if (in_ng) { /* XXX whine: can't recurse at this time */ break; } if (!ui->data.group.ng) break; /* XXX whine */ np = find_ng(ui->data.group.ng); if (!np) { printf("Universal: newsgroup %s not found!", ui->data.group.ng) FLUSH; sleep(5); return exit_code; } do_group: set_ng(np); if (np != current_ng) { recent_ng = current_ng; current_ng = np; } ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("Entering %s:", ngname) FLUSH; #ifdef SCAN_ART if (sel_ret == ';') ret = do_newsgroup(savestr(";")); else #endif ret = do_newsgroup(nullstr); switch (ret) { case NG_NORM: /* handle more cases later */ case NG_SELNEXT: case NG_NEXT: /* just continue reading */ break; case NG_SELPRIOR: /* not implemented yet */ /* FALL THROUGH */ case NG_ERROR: case NG_ASK: exit_code = UR_BREAK; return exit_code; case NG_MINUS: np = recent_ng; goto do_group; #ifdef SUPPORT_NNTP case NG_NOSERVER: /* Eeep! */ break; #endif } break; } case UN_HELPKEY: if (another_command(univ_key_help(ui->data.i))) pushchar(sel_ret | 0200); break; default: break; } return exit_code; } char universal_selector() { START_SELECTOR('v'); /* kind of like 'v'irtual... */ sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_UNIVERSAL, 0); selected_count = 0; sel_restart: /* make options */ end_char = 'Z'; page_char = '>'; /* Setup for selecting articles to read or set unread */ if (sel_rereading) sel_mask = UF_DELSEL; else sel_mask = UF_SEL; sel_page_univ = NULL; extra_commands = universal_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='\t' #ifdef SCAN_ART || sel_ret==';' #endif || sel_ret=='Z') { UNIV_ITEM *ui; int i; for (ui = first_univ, i = 0; ui; ui = ui->next, i++) { int ret; if (ui->flags & UF_SEL) { PUSH_SELECTOR(); PUSH_UNIV_SELECTOR(); ui->flags &= ~UF_SEL; save_selected_count--; ret = univ_read(ui); POP_UNIV_SELECTOR(); POP_SELECTOR(); if (ret == UR_ERROR || ret == UR_BREAK) { sel_ret = ' '; /* don't leave completely. */ break; /* jump out of for loop */ } } } } /*univ_loop_break:*/ /* restart the selector unless the user explicitly quits. * possibly later have an option for 'Z' to quit levels>1. */ if (sel_ret != 'q' && (sel_ret != 'Q')) goto sel_restart; sel_cleanup(); univ_close(); END_SELECTOR(); return sel_ret; } static void sel_display() { /* Present a page of items to the user */ display_page(); if (erase_screen && erase_each_line) erase_line(1); if (sel_item_index >= sel_page_item_cnt) sel_item_index = 0; if (disp_status_line == 1) { newline(); fputs(msg, stdout); term_col = strlen(msg); disp_status_line = 2; } } static void sel_status_msg(cp) char* cp; { if (can_home) goto_xy(0,sel_last_line+1); else newline(); fputs(cp, stdout); term_col = strlen(cp); goto_xy(0,sel_items[sel_item_index].line); fflush(stdout); /* CAA: otherwise may not be visible */ disp_status_line = 2; } static char sel_input() { register int j; int ch, action; char* in_select; int got_dash, got_goto; int sel_number; int ch_num1; /* CAA: TRN proudly continues the state machine traditions of RN. * April 2, 1996: 28 gotos in this function. Conversion to * structured programming is left as an exercise for the reader. */ /* If one immediately types a goto command followed by a dash ('-'), * the following will be the default action. */ action = '+'; reask_selector: /* Prompt the user */ sel_prompt(); position_selector: got_dash = got_goto = 0; force_sel_pos = -1; if (removed_prompt & 1) { draw_mousebar(tc_COLS,0); removed_prompt &= ~1; } if (can_home) goto_xy(0,sel_items[sel_item_index].line); #ifdef USE_TK /* Allow Tk to do something with the current positioning */ if (ttk_running) { SEL_UNION u; u = sel_items[sel_item_index].u; switch (sel_mode) { case SM_THREAD: case SM_SUBJECT: ttcl_eval("wipetree"); if (sel_page_item_cnt != 0) ttk_draw_tree(u.sp->thread, 0, 0); break; case SM_ARTICLE: case SM_MULTIRC: case SM_ADDGROUP: case SM_NEWSGROUP: case SM_OPTIONS: case SM_UNIVERSAL: default: break; } } #endif reinp_selector: if (removed_prompt & 1) goto position_selector; /* (CAA: TRN considered harmful? :-) */ /* Grab some commands from the user */ fflush(stdout); eat_typeahead(); if (UseSelNum) spin_char = '0' + (sel_item_index+1)/10; /* first digit */ else spin_char = sel_chars[sel_item_index]; cache_until_key(); getcmd(buf); if (*buf == ' ') setdef(buf, sel_at_end? &end_char : &page_char); ch = *buf; if (errno) ch = Ctl('l'); if (disp_status_line == 2) { if (can_home) { goto_xy(0,sel_last_line+1); erase_line(0); if (term_line == tc_LINES-1) removed_prompt |= 1; } disp_status_line = 0; } if (ch == '-' && sel_page_item_cnt) { got_dash = 1; got_goto = 0; /* right action is not clear if both are true */ if (can_home) { if (!input_pending()) { j = (sel_item_index > 0? sel_item_index : sel_page_item_cnt); if (UseSelNum) sprintf(msg,"Range: %d-", j); else sprintf(msg,"Range: %c-", sel_chars[j-1]); sel_status_msg(msg); } } else { putchar('-'); fflush(stdout); } goto reinp_selector; } /* allow the user to back out of a range or a goto with erase char */ if (ch == ERASECH || ch == KILLCH) { /* later consider dingaling() if neither got_{dash,goto} is true */ got_dash = 0; got_goto = 0; /* following if statement should be function */ if (disp_status_line == 2) { /* status was printed */ if (can_home) { goto_xy(0,sel_last_line+1); erase_line(0); if (term_line == tc_LINES-1) removed_prompt |= 1; } disp_status_line = 0; } goto position_selector; } if (ch == Ctl('g')) { got_goto = 1; got_dash = 0; /* right action is not clear if both are true */ if (!input_pending()) { if (UseSelNum) sel_status_msg("Go to number?"); else sel_status_msg("Go to letter?"); } goto reinp_selector; } if (sel_mode == SM_OPTIONS && (ch == '\r' || ch == '\n')) ch = '.'; in_select = index(sel_chars, ch); if (UseSelNum && ch >= '0' && ch <= '9') { ch_num1 = ch; /* would be *very* nice to use wait_key_pause() here */ if (!input_pending()) { if (got_dash) { if (sel_item_index > 0) { j = sel_item_index; /* -1, +1 to print */ } else /* wrap around from the bottom */ j = sel_page_item_cnt; sprintf(msg,"Range: %d-%c", j, ch); } else { if (got_goto) { sprintf(msg,"Go to number: %c", ch); } else { sprintf(msg,"%c", ch); } } sel_status_msg(msg); } /* Consider cache_until_key() here. The middle of typing a * number is a lousy time to delay, however. */ getcmd(buf); ch = *buf; if (disp_status_line == 2) { /* status was printed */ if (can_home) { goto_xy(0,sel_last_line+1); erase_line(0); if (term_line == tc_LINES-1) removed_prompt |= 1; } disp_status_line = 0; } if (ch == KILLCH) { /* kill whole command in progress */ got_goto = 0; got_dash = 0; goto position_selector; } if (ch == ERASECH) { /* Erase any first digit printed, but allow complex * commands to continue. Spaces at end of message are * there to wipe out old first digit. */ if (got_dash) { if (sel_item_index > 0) { j = sel_item_index; /* -1, +1 to print */ } else /* wrap around from the bottom */ j = sel_page_item_cnt; sprintf(msg,"Range: %d- ", j); sel_status_msg(msg); goto reinp_selector; } if (got_goto) { sel_status_msg("Go to number? "); goto reinp_selector; } goto position_selector; } if (ch >= '0' && ch <= '9') sel_number = ((ch_num1 - '0') * 10) + (ch - '0'); else { pushchar(ch); /* for later use */ sel_number = (ch_num1 - '0'); } j = sel_number-1; if ((j < 0) || (j >= sel_page_item_cnt)) { dingaling(); sprintf(msg, "No item %c%c on this page.", ch_num1, ch); sel_status_msg(msg); goto position_selector; } else if (got_goto || (SelNumGoto && !got_dash)) { /* (but only do always-goto if there was not a dash) */ sel_item_index = j; goto position_selector; } else if (got_dash) ; else if (sel_items[j].sel == 1) action = (sel_rereading? 'k' : '-'); else action = '+'; } else if (in_select && !UseSelNum) { j = in_select - sel_chars; if (j >= sel_page_item_cnt) { dingaling(); sprintf(msg, "No item '%c' on this page.", ch); sel_status_msg(msg); goto position_selector; } else if (got_goto) { sel_item_index = j; goto position_selector; } else if (got_dash) ; else if (sel_items[j].sel == 1) action = (sel_rereading? 'k' : '-'); else action = '+'; } else if (ch == '*' && sel_mode == SM_ARTICLE) { register ARTICLE* ap; if (!sel_page_item_cnt) dingaling(); else { ap = sel_items[sel_item_index].u.ap; if (sel_items[sel_item_index].sel) deselect_subject(ap->subj); else select_subject(ap->subj, 0); update_page(); } goto position_selector; } else if (ch == 'y' || ch == '.' || ch == '*' || ch == Ctl('t')) { j = sel_item_index; if (sel_page_item_cnt && sel_items[j].sel == 1) action = (sel_rereading? 'k' : '-'); else action = '+'; if (ch == Ctl('t')) force_sel_pos = j; } else if (ch == 'k' || ch == 'j' || ch == ',') { j = sel_item_index; action = 'k'; } else if (ch == 'm' || ch == '|') { j = sel_item_index; action = '-'; } else if (ch == 'M' && in_ng) { j = sel_item_index; action = 'M'; } else if (ch == '@') { sel_item_index = 0; j = sel_page_item_cnt-1; got_dash = 1; action = '@'; } else if (ch == '[' || ch == 'p') { if (--sel_item_index < 0) sel_item_index = sel_page_item_cnt? sel_page_item_cnt-1 : 0; goto position_selector; } else if (ch == ']' || ch == 'n') { if (++sel_item_index >= sel_page_item_cnt) sel_item_index = 0; goto position_selector; } else { sel_ret = ch; switch (sel_command(ch)) { case DS_ASK: if (!clean_screen) { sel_display(); goto reask_selector; } if (removed_prompt & 2) { carriage_return(); goto reask_selector; } goto position_selector; case DS_DISPLAY: sel_display(); goto reask_selector; case DS_UPDATE: if (!clean_screen) { sel_display(); goto reask_selector; } if (disp_status_line == 1) { newline(); fputs(msg,stdout); term_col = strlen(msg); if (removed_prompt & 1) { draw_mousebar(tc_COLS,0); removed_prompt &= ~1; } disp_status_line = 2; } update_page(); if (can_home) goto_xy(0,sel_last_line); goto reask_selector; case DS_RESTART: return 'R'; /*Restart*/ case DS_QUIT: return 'Q'; /*Quit*/ case DS_STATUS: disp_status_line = 1; if (!clean_screen) { sel_display(); goto reask_selector; } sel_status_msg(msg); if (!can_home) newline(); if (removed_prompt & 2) goto reask_selector; goto position_selector; } } if (!sel_page_item_cnt) { dingaling(); goto position_selector; } /* The user manipulated one of the letters -- handle it. */ if (!got_dash) sel_item_index = j; else { if (j < sel_item_index) { ch = sel_item_index-1; sel_item_index = j; j = ch; } } if (++j == sel_page_item_cnt) j = 0; do { register int sel = sel_items[sel_item_index].sel; if (can_home) goto_xy(0,sel_items[sel_item_index].line); if (action == '@') { if (sel == 2) ch = (sel_rereading? '+' : ' '); else if (sel_rereading) ch = 'k'; else if (sel == 1) ch = '-'; else ch = '+'; } else ch = action; switch (ch) { case '+': if (select_item(sel_items[sel_item_index].u)) output_sel(sel_item_index, 1, TRUE); if (term_line >= sel_last_line) { sel_display(); goto reask_selector; } break; case '-': case 'k': case 'M': { bool sel_reread_save = sel_rereading; if (ch == 'M') delay_return_item(sel_items[sel_item_index].u); if (ch == '-') sel_rereading = FALSE; else sel_rereading = TRUE; if (deselect_item(sel_items[sel_item_index].u)) output_sel(sel_item_index, ch == '-' ? 0 : 2, TRUE); sel_rereading = sel_reread_save; if (term_line >= sel_last_line) { sel_display(); goto reask_selector; } break; } } if (can_home) carriage_return(); fflush(stdout); if (++sel_item_index == sel_page_item_cnt) sel_item_index = 0; } while (sel_item_index != j); if (force_sel_pos >= 0) sel_item_index = force_sel_pos; goto position_selector; } static void sel_prompt() { draw_mousebar(tc_COLS,0); if (can_home) goto_xy(0,sel_last_line); #ifdef MAILCALL setmail(FALSE); #endif if (sel_at_end) sprintf(cmd_buf, "%s [%c%c] --", (!sel_prior_obj_cnt? "All" : "Bot"), end_char, page_char); else sprintf(cmd_buf, "%s%ld%% [%c%c] --", (!sel_prior_obj_cnt? "Top " : nullstr), (long)((sel_prior_obj_cnt+sel_page_obj_cnt)*100 / sel_total_obj_cnt), page_char, end_char); interp(buf, sizeof buf, mailcall); sprintf(msg, "%s-- %s %s (%s%s order) -- %s", buf, sel_exclusive && in_ng? "SELECTED" : "Select", sel_mode_string, sel_direction<0? "reverse " : nullstr, sel_sort_string, cmd_buf); color_string(COLOR_CMD,msg); term_col = strlen(msg); removed_prompt = 0; } static bool select_item(u) SEL_UNION u; { switch (sel_mode) { case SM_MULTIRC: if (!(u.mp->flags & sel_mask)) selected_count++; u.mp->flags = (u.mp->flags /*& ~MF_DEL*/) | sel_mask; break; case SM_ADDGROUP: if (!(u.gp->flags & sel_mask)) selected_count++; u.gp->flags = (u.gp->flags & ~AGF_DEL) | sel_mask; break; case SM_NEWSGROUP: if (!(u.np->flags & sel_mask)) selected_count++; u.np->flags = (u.np->flags & ~NF_DEL) | sel_mask; break; case SM_OPTIONS: if (!select_option(u.op) || !INI_VALUE(options_ini,u.op)) return FALSE; break; case SM_THREAD: select_thread(u.sp->thread, 0); break; case SM_SUBJECT: select_subject(u.sp, 0); break; case SM_ARTICLE: select_article(u.ap, 0); break; case SM_UNIVERSAL: if (!(u.un->flags & sel_mask)) selected_count++; u.un->flags = (u.un->flags & ~UF_DEL) | sel_mask; break; } return TRUE; } static bool delay_return_item(u) SEL_UNION u; { switch (sel_mode) { case SM_MULTIRC: case SM_ADDGROUP: case SM_NEWSGROUP: case SM_OPTIONS: case SM_UNIVERSAL: return FALSE; case SM_ARTICLE: delay_unmark(u.ap); break; default: { register ARTICLE* ap; if (sel_mode == SM_THREAD) { for (ap = first_art(u.sp); ap; ap = next_art(ap)) if (!!(ap->flags & AF_UNREAD) ^ sel_rereading) delay_unmark(ap); } else { for (ap = u.sp->articles; ap; ap = ap->subj_next) if (!!(ap->flags & AF_UNREAD) ^ sel_rereading) delay_unmark(ap); } break; } } return TRUE; } static bool deselect_item(u) SEL_UNION u; { switch (sel_mode) { case SM_MULTIRC: if (u.mp->flags & sel_mask) { u.mp->flags &= ~sel_mask; selected_count--; } #if 0 if (sel_rereading) u.mp->flags |= MF_DEL; #endif break; case SM_ADDGROUP: if (u.gp->flags & sel_mask) { u.gp->flags &= ~sel_mask; selected_count--; } if (sel_rereading) u.gp->flags |= AGF_DEL; break; case SM_NEWSGROUP: if (u.np->flags & sel_mask) { u.np->flags &= ~sel_mask; selected_count--; } if (sel_rereading) u.np->flags |= NF_DEL; break; case SM_OPTIONS: if (!select_option(u.op) || INI_VALUE(options_ini,u.op)) return FALSE; break; case SM_THREAD: deselect_thread(u.sp->thread); break; case SM_SUBJECT: deselect_subject(u.sp); break; case SM_UNIVERSAL: if (u.un->flags & sel_mask) { u.un->flags &= ~sel_mask; selected_count--; } if (sel_rereading) u.un->flags |= UF_DEL; break; default: deselect_article(u.ap, 0); break; } return TRUE; } static bool select_option(i) int i; { bool changed = FALSE; char** vals = INI_VALUES(options_ini); char* val; char* oldval; if (*options_ini[i].item == '*') { option_flags[i] ^= OF_SEL; init_pages(FILL_LAST_PAGE); term_line = sel_last_line; return FALSE; } goto_xy(0,sel_last_line); erase_line(mousebar_cnt > 0); /* erase the prompt */ color_object(COLOR_CMD, 1); printf("Change `%s' (%s)",options_ini[i].item, options_ini[i].help_str); color_pop(); /* of COLOR_CMD */ newline(); *buf = '\0'; oldval = savestr(quote_string(option_value(i))); val = vals[i]? vals[i] : oldval; clean_screen = in_choice("> ", val, options_ini[i].help_str, 'z'); if (strNE(buf,val)) { char* to = buf; char* from = buf; parse_string(&to, &from); changed = TRUE; if (vals[i]) { free(vals[i]); selected_count--; } if (val != oldval && strEQ(buf,oldval)) vals[i] = NULL; else { vals[i] = savestr(buf); selected_count++; } } free(oldval); if (clean_screen) { up_line(); erase_line(1); sel_prompt(); goto_xy(0,sel_items[sel_item_index].line); if (changed) { erase_line(0); display_option(i,sel_item_index); up_line(); } } else return FALSE; return TRUE; } static void sel_cleanup() { switch (sel_mode) { case SM_MULTIRC: break; case SM_ADDGROUP: if (sel_rereading) { ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) { if (gp->flags & AGF_DELSEL) { if (!(gp->flags & AGF_SEL)) selected_count++; gp->flags = (gp->flags&~(AGF_DELSEL|AGF_EXCLUDED))|AGF_SEL; } gp->flags &= ~AGF_DEL; } } else { ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) { if (gp->flags & AGF_DEL) gp->flags = (gp->flags & ~AGF_DEL) | AGF_EXCLUDED; } } break; case SM_NEWSGROUP: if (sel_rereading) { NGDATA* np; for (np = first_ng; np; np = np->next) { if (np->flags & NF_DELSEL) { if (!(np->flags & NF_SEL)) selected_count++; np->flags = (np->flags & ~NF_DELSEL) | NF_SEL; } np->flags &= ~NF_DEL; } } else { NGDATA* np; for (np = first_ng; np; np = np->next) { if (np->flags & NF_DEL) { np->flags &= ~NF_DEL; catch_up(np, 0, 0); } } } break; case SM_OPTIONS: break; /* should probably be expanded... */ case SM_UNIVERSAL: break; default: if (sel_rereading) { /* Turn selections into unread selected articles. Let ** count_subjects() fix the counts after we're through. */ register SUBJECT* sp; sel_last_ap = NULL; sel_last_sp = NULL; for (sp = first_subject; sp; sp = sp->next) unkill_subject(sp); } else { if (sel_mode == SM_ARTICLE) article_walk(mark_DEL_as_READ, 0); else { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_DEL) { sp->flags &= ~SF_DEL; if (sel_mode == SM_THREAD) kill_thread(sp->thread, AFFECT_UNSEL); else kill_subject(sp, AFFECT_UNSEL); } } } } break; } } static bool mark_DEL_as_READ(ptr, arg) char* ptr; int arg; { register ARTICLE* ap = (ARTICLE*)ptr; if (ap->flags & AF_DEL) { ap->flags &= ~AF_DEL; set_read(ap); } return 0; } static int sel_command(ch) char_int ch; { int ret; if (can_home) goto_xy(0,sel_last_line); clean_screen = TRUE; term_scrolled = 0; page_line = 1; if (sel_mode == SM_NEWSGROUP) { if (sel_item_index < sel_page_item_cnt) set_ng(sel_items[sel_item_index].u.np); else ngptr = NULL; } do_command: *buf = ch; buf[1] = FINISHCMD; output_chase_phrase = TRUE; switch (ch) { case '>': sel_item_index = 0; if (next_page()) return DS_DISPLAY; break; case '<': sel_item_index = 0; if (prev_page()) return DS_DISPLAY; break; case '^': case Ctl('r'): sel_item_index = 0; if (first_page()) return DS_DISPLAY; break; case '$': sel_item_index = 0; if (last_page()) return DS_DISPLAY; break; case Ctl('l'): return DS_DISPLAY; case Ctl('^'): erase_line(0); /* erase the prompt */ removed_prompt |= 2; #ifdef MAILCALL setmail(TRUE); /* force a mail check */ #endif break; case '\r': case '\n': if (!selected_count && sel_page_item_cnt) { if (sel_rereading || sel_items[sel_item_index].sel != 2) select_item(sel_items[sel_item_index].u); } return DS_QUIT; case 'Z': case '\t': return DS_QUIT; case 'q': case 'Q': case '+': case '`': return DS_QUIT; case Ctl('Q'): case '\033': sel_ret = '\033'; return DS_QUIT; case '\b': case '\177': return DS_QUIT; case Ctl('k'): edit_kfile(); return DS_DISPLAY; case '&': case '!': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { PUSH_SELECTOR(); one_command = TRUE; perform(buf, FALSE); one_command = FALSE; if (term_line != sel_last_line+1 || term_scrolled) clean_screen = FALSE; POP_SELECTOR(); if (!save_sel_mode) return DS_RESTART; if (clean_screen) { erase_line(0); return DS_ASK; } } if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; case 'v': newline(); trn_version(); if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; case '\\': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (sel_mode == SM_NEWSGROUP) printf("[%s] Cmd: ", ngptr? ngptr->rcline : "*End*"); else fputs("Cmd: ", stdout); fflush(stdout); getcmd(buf); if (*buf == '\\') goto the_default; if (*buf != ' ' && *buf != '\n' && *buf != '\r') { ch = *buf; goto do_command; } if (clean_screen) { erase_line(0); break; } if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; default: the_default: ret = extra_commands(ch); switch (ret) { case DS_ERROR: break; case DS_DOCOMMAND: ch = sel_ret; goto do_command; default: return ret; } strcpy(msg,"Type ? for help."); settle_down(); if (clean_screen) return DS_STATUS; printf("\n%s\n",msg); if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; } return DS_ASK; } static bool sel_perform_change(cnt, obj_type) long cnt; char* obj_type; { int ret; carriage_return(); if (page_line == 1) { disp_status_line = 1; if (term_line != sel_last_line+1 || term_scrolled) clean_screen = FALSE; } else clean_screen = FALSE; if (error_occurred) { print_lines(msg,NOMARKING); clean_screen = error_occurred = FALSE; } ret = perform_status_end(cnt, obj_type); if (ret) disp_status_line = 1; if (clean_screen) { if (ret != 2) { up_line(); return TRUE; } } else if (disp_status_line == 1) { print_lines(msg,NOMARKING); disp_status_line = 0; } init_pages(PRESERVE_PAGE); return FALSE; } #ifdef SCAN_ART #define SPECIAL_CMD_LETTERS "<+>^$!?&:/\\hDEJLNOPqQRSUXYZ\n\r\t\033;" #else #define SPECIAL_CMD_LETTERS "<+>^$!?&:/\\hDEJLNOPqQRSUXYZ\n\r\t\033" #endif static char another_command(ch) char_int ch; { bool skip_q = !ch; if (ch < 0) return 0; if (ch > 1) { read_tty(buf,1); ch = *buf; } else ch = pause_getcmd(); if (ch != 0 && ch != '\n' && ch != '\r' && (!skip_q || ch != 'q')) { if (ch > 0) { /* try to optimize the screen update for some commands. */ if (!index(sel_chars, ch) && (index(SPECIAL_CMD_LETTERS, ch) || ch == Ctl('k'))) { sel_ret = ch; return ch; } pushchar(ch | 0200); } } return '\0'; } static int article_commands(ch) char_int ch; { switch (ch) { case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_sp = NULL; sel_page_app = NULL; if (!cache_range(sel_rereading? absfirst : firstart, lastart)) sel_rereading = !sel_rereading; return DS_RESTART; case '#': if (sel_page_item_cnt) { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) sp->flags &= ~SF_SEL; selected_count = 0; deselect_item(sel_items[sel_item_index].u); select_item(sel_items[sel_item_index].u); if (!keep_the_group_static) keep_the_group_static = 2; } return DS_QUIT; case 'N': case 'P': return DS_QUIT; case 'L': switch_dmode(&sel_art_dmode); /* sets msg */ return DS_DISPLAY; case 'Y': if (!dmcount) { strcpy(msg,"No marked articles to yank back."); return DS_STATUS; } yankback(); if (!sel_rereading) sel_cleanup(); disp_status_line = 1; count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(PRESERVE_PAGE); return DS_DISPLAY; case '=': if (!sel_rereading) sel_cleanup(); if (sel_mode == SM_ARTICLE) { set_selector(sel_threadmode, 0); sel_page_sp = sel_page_app? sel_page_app[0]->subj : NULL; } else { set_selector(SM_ARTICLE, 0); sel_page_app = 0; } count_subjects(CS_NORM); sel_item_index = 0; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'S': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_output: in_char("Selector mode: Threads, Subjects, Articles?", 'o', "tsa"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h' || *buf == 'H') { #ifdef VERBOSE IF(verbose) fputs("\n\ Type t or SP to display/select thread groups (threads the group, if needed).\n\ Type s to display/select subject groups.\n\ Type a to display/select individual articles.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ t or SP selects thread groups (threads the group too).\n\ s selects subject groups.\n\ a selects individual articles.\n\ q does nothing.\n\n\ ",stdout) FLUSH; #endif clean_screen = FALSE; goto reask_output; } else if (*buf == 'q') { if (can_home) erase_line(0); return DS_ASK; } if (isupper(*buf)) *buf = tolower(*buf); set_sel_mode(*buf); count_subjects(CS_NORM); init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: if (sel_mode == SM_ARTICLE) in_char( "Order by Date,Subject,Author,Number,subj-date Groups,Points?", 'q', "dsangpDSANGP"); else in_char("Order by Date, Subject, Count, Lines, or Points?", 'q', "dsclpDSCLP"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h' || *buf == 'H') { #ifdef VERBOSE IF(verbose) { fputs("\n\ Type d or SP to order the displayed items by date.\n\ Type s to order the items by subject.\n\ Type p to order the items by score points.\n\ ",stdout) FLUSH; if (sel_mode == SM_ARTICLE) fputs("\ Type a to order the items by author.\n\ Type n to order the items by number.\n\ Type g to order the items in subject-groups by date.\n\ ",stdout) FLUSH; else fputs("\ Type c to order the items by count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ d or SP sorts by date.\n\ s sorts by subject.\n\ p sorts by points.\n\ ",stdout) FLUSH; if (sel_mode == SM_ARTICLE) fputs("\ a sorts by author.\n\ g sorts in subject-groups by date.\n\ ",stdout) FLUSH; else fputs("\ c sorts by count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; } else if (*buf == 'q') { if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'R': if (!sel_rereading) sel_cleanup(); set_selector(sel_mode, sel_sort * -sel_direction); count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { if (sel_mode == SM_ARTICLE) { register ARTICLE* ap; register ARTICLE** app; register ARTICLE** limit; limit = artptr_list + artptr_list_size; if (ch == 'D') app = sel_page_app; else app = artptr_list; while (app < limit) { ap = *app; if ((!(ap->flags & AF_SEL) ^ (ch == 'J')) || (ap->flags & AF_DEL)) if (ch == 'J' || !sel_exclusive || (ap->flags & AF_INCLUDED)) { set_read(ap); } app++; if (ch == 'D' && app == sel_next_app) break; } } else { register SUBJECT* sp; if (ch == 'D') sp = sel_page_sp; else sp = first_subject; while (sp) { if (((!(sp->flags & SF_SEL) ^ (ch == 'J')) && sp->misc) || (sp->flags & SF_DEL)) { if (ch == 'J' || !sel_exclusive || (sp->flags & SF_INCLUDED)) { kill_subject(sp, ch=='J'? AFFECT_ALL:AFFECT_UNSEL); } } sp = sp->next; if (ch == 'D' && sp == sel_next_sp) break; } } count_subjects(CS_UNSELECT); if (obj_count && (ch == 'J' || (ch == 'D' && !selected_count))) { init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; } if (artptr_list && obj_count) sort_articles(); } else if (ch == 'J') { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) deselect_subject(sp); selected_subj_cnt = selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case 'T': if (!ThreadedGroup) { strcpy(msg,"Group is not threaded."); return DS_STATUS; } /* FALL THROUGH */ case 'A': if (!sel_page_item_cnt) { dingaling(); break; } erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (sel_mode == SM_ARTICLE) artp = sel_items[sel_item_index].u.ap; else { register SUBJECT* sp = sel_items[sel_item_index].u.sp; if (sel_mode == SM_THREAD) { while (!sp->misc) sp = sp->thread_link; } artp = sp->articles; } art = article_num(artp); /* This call executes the action too */ switch (ask_memorize(ch)) { case 'J': case 'j': case 'K': case ',': count_subjects(sel_rereading? CS_NORM : CS_UNSELECT); init_pages(PRESERVE_PAGE); strcpy(msg,"Kill memorized."); disp_status_line = 1; return DS_DISPLAY; case '+': case '.': case 'S': case 'm': strcpy(msg,"Selection memorized."); disp_status_line = 1; return DS_UPDATE; case 'c': case 'C': strcpy(msg,"Auto-commands cleared."); disp_status_line = 1; return DS_UPDATE; case 'q': return DS_UPDATE; case 'Q': break; } if (can_home) erase_line(0); break; #ifdef SCAN_ART case ';': sel_ret = ';'; return DS_QUIT; #endif case ':': if (sel_page_item_cnt) { if (sel_mode == SM_ARTICLE) artp = sel_items[sel_item_index].u.ap; else { register SUBJECT* sp = sel_items[sel_item_index].u.sp; if (sel_mode == SM_THREAD) { while (!sp->misc) sp = sp->thread_link; } artp = sp->articles; } art = article_num(artp); } else art = 0; /* FALL THROUGH */ case '/': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { if (ch == ':') { thread_perform(); if (!sel_rereading) { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_DEL) { sp->flags = 0; if (sel_mode == SM_THREAD) kill_thread(sp->thread, AFFECT_UNSEL); else kill_subject(sp, AFFECT_UNSEL); } } } } else { /* Force the search to begin at absfirst or firstart, ** depending upon whether they specified the 'r' option. */ art = lastart+1; switch (art_search(buf, sizeof buf, FALSE)) { case SRCH_ERROR: case SRCH_ABORT: break; case SRCH_INTR: errormsg("Interrupted"); break; case SRCH_DONE: case SRCH_SUBJDONE: case SRCH_FOUND: break; case SRCH_NOTFOUND: errormsg("Not found."); break; } } sel_item_index = 0; /* Recount, in case something has changed. */ count_subjects(sel_rereading? CS_NORM : CS_UNSELECT); if (sel_perform_change(ngptr->toread, "article")) return DS_UPDATE; if (clean_screen) return DS_DISPLAY; } if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; case 'c': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if ((ch = ask_catchup()) == 'y' || ch == 'u') { count_subjects(CS_UNSELECT); if (ch != 'u' && obj_count) { sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; } sel_ret = 'Z'; return DS_QUIT; } if (ch != 'N') return DS_DISPLAY; if (can_home) erase_line(0); break; case 'h': case '?': univ_help(UHELP_ARTSEL); return DS_RESTART; case 'H': newline(); if (another_command(help_artsel())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int newsgroup_commands(ch) char_int ch; { switch (ch) { case Ctl('n'): case Ctl('p'): return DS_QUIT; case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_np = NULL; return DS_RESTART; case 'L': switch_dmode(&sel_grp_dmode); /* sets msg */ if (*sel_grp_dmode != 's' && !multirc->first->datasrc->desc_sf.hp) { newline(); return DS_RESTART; } return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { register NGDATA* np; if (ch == 'D') np = sel_page_np; else np = first_ng; while (np) { if (((!(np->flags&NF_SEL) ^ (ch=='J')) && np->toread!=TR_UNSUB) || (np->flags & NF_DEL)) { if (ch == 'J' || (np->flags & NF_INCLUDED)) catch_up(np, 0, 0); np->flags &= ~(NF_DEL|NF_SEL); } np = np->next; if (ch == 'D' && np == sel_next_np) break; } if (ch == 'J' || (ch == 'D' && !selected_count)) { init_pages(FILL_LAST_PAGE); if (sel_total_obj_cnt) { sel_item_index = 0; return DS_DISPLAY; } } } else if (ch == 'J') { register NGDATA* np; for (np = first_ng; np; np = np->next) np->flags &= ~NF_DELSEL; selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case '=': { NGDATA* np; sel_cleanup(); missing_count = 0; for (np = first_ng; np; np = np->next) { if (np->toread > TR_UNSUB && np->toread < ng_min_toread) newsgroup_toread++; np->abs1st = 0; } erase_line(0); #ifdef SUPPORT_NNTP check_active_refetch(TRUE); #endif return DS_RESTART; } case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: in_char("Order by Newsrc, Group name, or Count?", 'q', "ngcNGC"); #ifdef VERIFY printcmd(); #endif switch (*buf) { case 'n': case 'N': break; case 'g': case 'G': *buf += 's' - 'g'; /* Group name == SS_STRING */ break; case 'c': case 'C': break; case 'h': case 'H': #ifdef VERBOSE IF(verbose) { fputs("\n\ Type n or SP to order the newsgroups in the .newsrc order.\n\ Type g to order the items by group name.\n\ Type c to order the items by count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ n or SP sorts by .newsrc.\n\ g sorts by group name.\n\ c sorts by count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; default: if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); sel_page_np = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'R': if (!sel_rereading) sel_cleanup(); set_selector(sel_mode, sel_sort * -sel_direction); sel_page_np = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_np = NULL; init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; case ':': #if 0 if (ngptr != current_ng) { recent_ng = current_ng; current_ng = ngptr; } /* FALL THROUGH */ #endif case '/': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { if (ch == ':') { ngsel_perform(); } else { #ifdef NGSEARCH ngptr = NULL; switch (ng_search(buf,FALSE)) { case NGS_ERROR: case NGS_ABORT: break; case NGS_INTR: errormsg("Interrupted"); break; case NGS_FOUND: case NGS_NOTFOUND: case NGS_DONE: break; } ngptr = current_ng; #else notincl("/"); #endif } sel_item_index = 0; if (sel_perform_change(newsgroup_toread, "newsgroup")) return DS_UPDATE; if (clean_screen) return DS_DISPLAY; } if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; case 'c': if (sel_item_index < sel_page_item_cnt) set_ng(sel_items[sel_item_index].u.np); else { strcpy(msg, "No newsgroup to catchup."); disp_status_line = 1; return DS_UPDATE; } if (ngptr != current_ng) { recent_ng = current_ng; current_ng = ngptr; } erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if ((ch = ask_catchup()) == 'y' || ch == 'u') return DS_DISPLAY; if (ch != 'N') return DS_DISPLAY; if (can_home) erase_line(0); break; case 'h': case '?': univ_help(UHELP_NGSEL); return DS_RESTART; case 'H': newline(); if (another_command(help_ngsel())) return DS_DOCOMMAND; return DS_DISPLAY; #ifdef SCAN_ART case ';': sel_ret = ';'; return DS_QUIT; #endif default: { SEL_UNION u; int ret; bool was_at_top = !sel_prior_obj_cnt; PUSH_SELECTOR(); if (!(removed_prompt & 2)) { erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; printf("[%s] Cmd: ", ngptr? ngptr->rcline : "*End*"); fflush(stdout); } dfltcmd = "\\"; set_mode('r','n'); if (ch == '\\') { putchar(ch); fflush(stdout); } else pushchar(ch | 0200); do { ret = input_newsgroup(); } while (ret == ING_INPUT); set_mode('s','w'); POP_SELECTOR(); switch (ret) { #ifdef SUPPORT_NNTP case ING_NOSERVER: if (multirc) { if (!was_at_top) (void) first_page(); return DS_RESTART; } /* FALL THROUGH */ #endif case ING_QUIT: sel_ret = 'q'; return DS_QUIT; case ING_ERROR: return DS_ERROR; case ING_ERASE: if (clean_screen) { erase_line(0); return DS_ASK; } break; default: if (!save_sel_mode) return DS_RESTART; if (term_line == sel_last_line) newline(); if (term_line != sel_last_line+1 || term_scrolled) clean_screen = FALSE; break; } sel_item_index = 0; init_pages(PRESERVE_PAGE); if (ret == ING_SPECIAL && ngptr && ngptr->toread < ng_min_toread){ ngptr->flags |= NF_INCLUDED; sel_total_obj_cnt++; ret = ING_DISPLAY; } u.np = ngptr; if ((calc_page(u) || ret == ING_DISPLAY) && clean_screen) return DS_DISPLAY; if (ret == ING_MESSAGE) { clean_screen = 0; return DS_STATUS; } if (was_at_top) (void) first_page(); if (clean_screen) return DS_ASK; newline(); if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; } } return DS_ASK; } static int addgroup_commands(ch) char_int ch; { switch (ch) { case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: in_char("Order by Natural-order, Group name, or Count?", 'q', "ngcNGC"); #ifdef VERIFY printcmd(); #endif switch (*buf) { case 'n': case 'N': break; case 'g': case 'G': *buf += 's' - 'g'; /* Group name == SS_STRING */ break; case 'c': case 'C': break; case 'h': case 'H': #ifdef VERBOSE IF(verbose) { fputs("\n\ Type n or SP to order the items in their naturally occurring order.\n\ Type g to order the items by newsgroup name.\n\ Type c to order the items by article count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ n or SP sorts by natural order.\n\ g sorts by newsgroup name.\n\ c sorts by article count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; default: if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); sel_page_np = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_gp = NULL; return DS_RESTART; case 'R': if (!sel_rereading) sel_cleanup(); set_selector(sel_mode, sel_sort * -sel_direction); sel_page_gp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_gp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'L': switch_dmode(&sel_grp_dmode); /* sets msg */ if (*sel_grp_dmode != 's' && !datasrc->desc_sf.hp) { newline(); return DS_RESTART; } return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { register ADDGROUP* gp; if (ch == 'D') gp = sel_page_gp; else gp = first_addgroup; while (gp) { if ((!(gp->flags&AGF_SEL) ^ (ch=='J')) || (gp->flags & AGF_DEL)) { if (ch == 'J' || (gp->flags & AGF_INCLUDED)) gp->flags |= AGF_EXCLUDED; gp->flags &= ~(AGF_DEL|AGF_SEL); } gp = gp->next; if (ch == 'D' && gp == sel_next_gp) break; } if (ch == 'J' || (ch == 'D' && !selected_count)) { init_pages(FILL_LAST_PAGE); if (sel_total_obj_cnt) { sel_item_index = 0; return DS_DISPLAY; } } } else if (ch == 'J') { register ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) gp->flags &= ~AGF_DELSEL; selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case ':': case '/': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { if (ch == ':') { addgrp_sel_perform(); } else { #ifdef NGSEARCH switch (ng_search(buf,FALSE)) { case NGS_ERROR: case NGS_ABORT: break; case NGS_INTR: errormsg("Interrupted"); break; case NGS_FOUND: case NGS_NOTFOUND: case NGS_DONE: break; } #else notincl("/"); #endif } sel_item_index = 0; if (sel_perform_change(newsgroup_toread, "newsgroup")) return DS_UPDATE; if (clean_screen) return DS_DISPLAY; } if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; case 'h': case '?': univ_help(UHELP_ADDSEL); return DS_RESTART; case 'H': newline(); if (another_command(help_addsel())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int multirc_commands(ch) char_int ch; { switch (ch) { case 'R': set_selector(sel_mode, sel_sort * -sel_direction); sel_page_mp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_mp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case '/': /*$$$$*/ break; case 'h': case '?': univ_help(UHELP_MULTIRC); return DS_RESTART; case 'H': newline(); if (another_command(help_multirc())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int option_commands(ch) char_int ch; { switch (ch) { case 'R': set_selector(sel_mode, sel_sort * -sel_direction); sel_page_op = 1; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_op = 1; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'S': return DS_QUIT; case '/': { extern COMPEX optcompex; SEL_UNION u; char* s; char* pattern; int i, j; erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) /* get rest of command */ break; s = cpytill(buf,buf+1,'/'); for (pattern = buf; *pattern == ' '; pattern++) ; if ((s = compile(&optcompex,pattern,TRUE,TRUE)) != NULL) { strcpy(msg,s); return DS_STATUS; } i = j = sel_items[sel_item_index].u.op; do { if (++i > obj_count) i = 1; if (*options_ini[i].item == '*') continue; if (execute(&optcompex,options_ini[i].item)) break; } while (i != j); u.op = i; if (!(option_flags[i] & OF_INCLUDED)) { for (j = i-1; *options_ini[j].item != '*'; j--) ; option_flags[j] |= OF_SEL; init_pages(FILL_LAST_PAGE); calc_page(u); return DS_DISPLAY; } if (calc_page(u)) return DS_DISPLAY; return DS_ASK; } case 'h': case '?': univ_help(UHELP_OPTIONS); return DS_RESTART; case 'H': newline(); if (another_command(help_options())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int universal_commands(ch) char_int ch; { switch (ch) { case 'R': set_selector(sel_mode, sel_sort * -sel_direction); sel_page_univ = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_univ = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; #ifdef SCAN_ART case ';': sel_ret = ';'; return DS_QUIT; #endif case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_univ = NULL; return DS_RESTART; case Ctl('e'): univ_edit(); univ_redofile(); sel_cleanup(); sel_page_univ = NULL; return DS_RESTART; case 'h': case '?': univ_help(UHELP_UNIV); return DS_RESTART; case 'H': newline(); if (another_command(help_univ())) return DS_DOCOMMAND; return DS_DISPLAY; #ifdef SCORE case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: in_char("Order by Natural, or score Points?", 'q', "npNP"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h' || *buf == 'H') { #ifdef VERBOSE IF(verbose) { fputs("\n\ Type n or SP to order the items in the natural order.\n\ Type p to order the items by score points.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ n or SP sorts by natural order.\n\ p sorts by score.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; } else if (*buf == 'q' || (tolower(*buf) != 'n' && tolower(*buf) != 'p')) { if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); sel_page_univ = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; #endif case '~': univ_virt_pass(); sel_cleanup(); sel_page_univ = NULL; return DS_RESTART; default: break; } return DS_ERROR; } static void switch_dmode(dmode_cpp) char** dmode_cpp; { char* s; if (!*++*dmode_cpp) { do { --*dmode_cpp; } while ((*dmode_cpp)[-1] != '*'); } switch (**dmode_cpp) { case 's': s = "short"; break; case 'm': s = "medium"; break; case 'd': s = "date"; break; case 'l': s = "long"; break; } sprintf(msg,"(%s display style)",s); disp_status_line = 1; } static int find_line(y) int y; { int i; for (i = 0; i < sel_page_item_cnt; i++) { if (sel_items[i].line > y) break; } if (i > 0) i--; return i; } /* On click: * btn = 0 (left), 1 (middle), or 2 (right) + 4 if double-clicked; * x = 0 to tc_COLS-1; y = 0 to tc_LINES-1; * btn_clk = 0, 1, or 2 (no 4); x_clk = x; y_clk = y. * On release: * btn = 3; x = release's x; y = release's y; * btn_clk = click's 0, 1, or 2; x_clk = click's x; y_clk = click's y. */ void selector_mouse(btn, x,y, btn_clk, x_clk,y_clk) int btn; int x, y; int btn_clk; int x_clk, y_clk; { if (check_mousebar(btn, x,y, btn_clk, x_clk,y_clk)) return; if (btn != 3) { /* Handle button-down event */ switch (btn_clk) { case 0: case 1: if (y > 0 && y < sel_last_line) { if (btn & 4) { pushchar(btn_clk == 0? '\n' : 'Z'); mouse_is_down = FALSE; } else { force_sel_pos = find_line(y); if (UseSelNum) { pushchar(('0' + (force_sel_pos+1) % 10) | 0200); pushchar(('0' + (force_sel_pos+1)/10) | 0200); } else { pushchar(sel_chars[force_sel_pos] | 0200); } if (btn == 1) pushchar(Ctl('g') | 0200); } } break; case 2: break; } } else { /* Handle the button-up event */ switch (btn_clk) { case 0: if (!y) pushchar('<' | 0200); else if (y >= sel_last_line) pushchar(' '); else { int i = find_line(y); if (sel_items[i].line != term_line) { if (UseSelNum) { pushchar(('0' + (i+1) % 10) | 0200); pushchar(('0' + (i+1) / 10) | 0200); } else { pushchar(sel_chars[i] | 0200); } pushchar('-' | 0200); force_sel_pos = i; } } break; case 1: if (!y) pushchar('<' | 0200); else if (y >= sel_last_line) pushchar('>' | 0200); break; case 2: /* move forward or backwards a page: * if cursor in top half: backwards * if cursor in bottom half: forwards */ if (y < tc_LINES/2) pushchar('<' | 0200); else pushchar('>' | 0200); break; } } } /* Icky placement, but the PUSH/POP stuff is local to this file */ int univ_visit_group(gname) char* gname; { PUSH_SELECTOR(); univ_visit_group_main(gname); POP_SELECTOR(); return 0; /* later may have some error return values */ } /* later consider returning universal_selector() value */ void univ_visit_help(where) int where; { PUSH_SELECTOR(); PUSH_UNIV_SELECTOR(); univ_help_main(where); (void)universal_selector(); POP_UNIV_SELECTOR(); POP_SELECTOR(); } trn-4.0-test77/rt-select.h0000644000000000000000000000376007113133016014065 0ustar rootroot/* rt-select.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT bool sel_rereading INIT(0); EXT char sel_disp_char[] INIT(" +-*"); #define SM_THREAD 1 #define SM_SUBJECT 2 #define SM_ARTICLE 3 #define SM_NEWSGROUP 4 #define SM_ADDGROUP 5 #define SM_MULTIRC 6 #define SM_OPTIONS 7 #define SM_UNIVERSAL 8 EXT int sel_mode; EXT int sel_defaultmode INIT(SM_THREAD); EXT int sel_threadmode INIT(SM_THREAD); #define SS_DATE 1 #define SS_STRING 2 #define SS_AUTHOR 3 #define SS_COUNT 4 #define SS_NATURAL 5 #define SS_GROUPS 6 #define SS_LINES 7 /* NOTE: The score order is still valid even without scoring enabled. */ /* (The real order is then something like natural or date.) */ #define SS_SCORE 8 EXT char* sel_mode_string; EXT int sel_sort; EXT int sel_artsort INIT(SS_GROUPS); EXT int sel_threadsort INIT(SS_DATE); EXT int sel_newsgroupsort INIT(SS_NATURAL); EXT int sel_addgroupsort INIT(SS_NATURAL); EXT int sel_univsort INIT(SS_NATURAL); EXT char* sel_sort_string; EXT int sel_direction INIT(1); EXT bool sel_exclusive INIT(FALSE); EXT int sel_mask INIT(1); EXT bool selected_only INIT(FALSE); EXT ART_UNREAD selected_count INIT(0); EXT int selected_subj_cnt INIT(0); EXT int added_articles INIT(0); EXT char* sel_chars; EXT int sel_item_index; EXT int sel_last_line; EXT bool sel_at_end; EXT bool art_sel_ilock INIT(FALSE); #define DS_ASK 1 #define DS_UPDATE 2 #define DS_DISPLAY 3 #define DS_RESTART 4 #define DS_STATUS 5 #define DS_QUIT 6 #define DS_DOCOMMAND 7 #define DS_ERROR 8 #define UR_NORM 1 #define UR_BREAK 2 /* request return to selector */ #define UR_ERROR 3 /* non-normal return */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ char article_selector _((char_int)); char multirc_selector _((void)); char newsgroup_selector _((void)); char addgroup_selector _((int)); char option_selector _((void)); char universal_selector _((void)); void selector_mouse _((int,int,int,int,int,int)); int univ_visit_group _((char*)); void univ_visit_help _((int)); trn-4.0-test77/rt-select.ih0000644000000000000000000000206007113133016014226 0ustar rootroot/* rt-select.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void sel_dogroups _((void)); static int univ_read _((UNIV_ITEM*)); static void sel_display _((void)); static void sel_status_msg _((char*)); static char sel_input _((void)); static void sel_prompt _((void)); static bool select_item _((SEL_UNION)); static bool delay_return_item _((SEL_UNION)); static bool deselect_item _((SEL_UNION)); static bool select_option _((int)); static void sel_cleanup _((void)); static bool mark_DEL_as_READ _((char*,int)); static int sel_command _((char_int)); static bool sel_perform_change _((long,char*)); static char another_command _((char_int)); static int article_commands _((char_int)); static int newsgroup_commands _((char_int)); static int addgroup_commands _((char_int)); static int multirc_commands _((char_int)); static int option_commands _((char_int)); static int universal_commands _((char_int)); static void switch_dmode _((char**)); static int find_line _((int)); trn-4.0-test77/rt-util.c0000644000000000000000000004110007211517254013555 0ustar rootroot/* rt-util.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "cache.h" #include "ngdata.h" #include "artio.h" #include "rthread.h" #include "rt-select.h" #include "term.h" #include "nntpclient.h" #include "charsubst.h" #include "datasrc.h" #include "nntp.h" #include "intrp.h" #include "ng.h" #include "util.h" #include "util2.h" #ifdef USE_TK #include "tkstuff.h" #endif #include "INTERN.h" #include "rt-util.h" #include "rt-util.ih" /* Name-munging routines written by Ross Ridge. ** Enhanced by Wayne Davison. */ /* Extract the full-name part of an email address, returning NULL if not ** found. */ char* extract_name(name) char* name; { char* s; char* lparen; char* rparen; char* langle; while (isspace(*name)) name++; lparen = index(name, '('); rparen = rindex(name, ')'); langle = index(name, '<'); if (!lparen && !langle) return NULL; else if (langle && (!lparen || !rparen || lparen>langle || rparen max && mid != s) { if (len - namelen <= max && ((mid[1] != '.' && (!name[1] || (name[1] == '.' && !name[2]))) || (*mid == '"' && *s == '"'))) { len -= namelen; name = mid; namelen = midlen; mid = 0; } else if (*mid == '"' && *s == '"') { if (midlen > max) { name = mid+1; *s = '\0'; goto try_again; } len = midlen; last = mid; namelen = 0; mid = 0; } } } s[1] = '\0'; if (mid && len > max) { /* Turn middle names into intials */ len -= s - mid + 2; d = s = mid; while (*s) { if (isalpha(*s)) { if (d != mid) *d++ = ' '; *d++ = *s++; } while (*s && !isspace(*s)) s++; while (isspace(*s)) s++; } if (d != mid) { *d = '\0'; midlen = d - mid + 1; len += midlen; } else mid = 0; } if (len > max) { /* If the first name fits without the middle initials, drop them */ if (mid && len - midlen <= max) { len -= midlen; mid = 0; } else if (namelen > 0) { /* Turn the first name into an initial */ len -= namelen - 2; name[1] = '\0'; namelen = 2; if (len > max) { /* Dump the middle initials (if present) */ if (mid) { len -= midlen; mid = 0; } if (len > max) { /* Finally just truncate the last name */ last[max - 2] = '\0'; } } } else namelen = 0; } /* Paste the names back together */ d = name + namelen; if (namelen) d[-1] = ' '; if (mid) { if (d != mid) strcpy(d, mid); d += midlen; d[-1] = ' '; } safecpy(d, last, max); /* "max - (d-name)" would be overkill */ return name; } /* Compress an email address, trying to keep as much of the local part of ** the addresses as possible. The order of precence is @ ! %, but ** @ % ! may be better... */ char* compress_address(name, max) char* name; int max; { char* s; char* at; char* bang; char* hack; char* start; int len; /* Remove white space from both ends. */ while (isspace(*name)) name++; if ((len = strlen(name)) == 0) return name; s = name + len - 1; while (isspace(*s)) s--; s[1] = '\0'; if (*name == '<') { name++; if (*s == '>') *s-- = '\0'; } if ((len = s - name + 1) <= max) return name; at = bang = hack = NULL; for (s = name + 1; *s; s++) { /* If there's whitespace in the middle then it's probably not ** really an email address. */ if (isspace(*s)) { name[max] = '\0'; return name; } switch (*s) { case '@': if (at == NULL) { at = s; } break; case '!': if (at == NULL) { bang = s; hack = NULL; } break; case '%': if (at == NULL && hack == NULL) hack = s; break; } } if (at == NULL) at = name + len; if (hack != NULL) { if (bang != NULL) { if (at - bang - 1 >= max) start = bang + 1; else if (at - name >= max) start = at - max; else start = name; } else start = name; } else if (bang != NULL) { if (at - name >= max) start = at - max; else start = name; } else start = name; if (len - (start - name) > max) start[max] = '\0'; return start; } /* Fit the author name in chars. Uses the comment portion if present ** and pads with spaces. */ char* compress_from(from, size) char* from; int size; { static char lbuf[LBUFLEN]; char* s = from? from : nullstr; int len; #ifdef CHARSUBST strcharsubst(lbuf, s, sizeof lbuf, *charsubst); #else safecpy(lbuf, s, sizeof lbuf); #endif if ((s = extract_name(lbuf)) != NULL) s = compress_name(s, size); else s = compress_address(lbuf, size); len = strlen(s); if (!len) { strcpy(s,"NO NAME"); len = 7; } while (len < size) s[len++] = ' '; s[size] = '\0'; return s; } /* Fit the date in chars. */ char* compress_date(ap, size) ARTICLE* ap; int size; { char* s; char* t; strncpy(t = cmd_buf, ctime(&ap->date), size); if ((s = index(t, '\n')) != NULL) *s = '\0'; t[size] = '\0'; return t; } #define EQ(x,y) ((isupper(x) ? tolower(x) : (x)) == (y)) /* Parse the subject to look for any "Re[:^]"s at the start. ** Returns TRUE if a Re was found. If strp is non-NULL, it ** will be set to the start of the interesting characters. */ bool subject_has_Re(str, strp) register char* str; char** strp; { bool has_Re = 0; while (*str && AT_GREY_SPACE(str)) str++; while (EQ(str[0], 'r') && EQ(str[1], 'e')) { /* check for Re: */ register char* cp = str + 2; if (*cp == '^') { /* allow Re^2: */ while (*++cp <= '9' && *cp >= '0') ; } if (*cp != ':') break; while (*++cp && AT_GREY_SPACE(cp)) ; str = cp; has_Re = 1; } if (strp) *strp = str; return has_Re; } /* Output a subject in chars. Does intelligent trimming that tries to ** save the last two words on the line, excluding "(was: blah)" if needed. */ char* compress_subj(ap, max) ARTICLE* ap; int max; { register char* cp; register int len; ARTICLE* first; if (!ap) return ""; /* Put a preceeding '>' on subjects that are replies to other articles */ cp = buf; first = (ThreadedGroup? ap->subj->thread : ap->subj->articles); if (ap != first || (ap->flags & AF_HAS_RE) || (!(ap->flags&AF_UNREAD) ^ sel_rereading)) *cp++ = '>'; #ifdef CHARSUBST strcharsubst(cp, ap->subj->str + 4, (sizeof buf) - (cp-buf), *charsubst); #else safecpy(cp, ap->subj->str + 4, (sizeof buf) - (cp-buf)); #endif /* Remove "(was: oldsubject)", because we already know the old subjects. ** Also match "(Re: oldsubject)". Allow possible spaces after the ('s. */ for (cp = buf; (cp = index(cp+1, '(')) != NULL;) { while (*++cp == ' ') ; if (EQ(cp[0], 'w') && EQ(cp[1], 'a') && EQ(cp[2], 's') && (cp[3] == ':' || cp[3] == ' ')) { *--cp = '\0'; break; } if (EQ(cp[0], 'r') && EQ(cp[1], 'e') && ((cp[2]==':' && cp[3]==' ') || (cp[2]=='^' && cp[4]==':'))) { *--cp = '\0'; break; } } len = strlen(buf); if (!unbroken_subjects && len > max) { char* last_word; /* Try to include the last two words on the line while trimming */ if ((last_word = rindex(buf, ' ')) != NULL) { char* next_to_last; *last_word = '\0'; if ((next_to_last = rindex(buf, ' ')) != NULL) { if (next_to_last-buf >= len - max + 3 + 10-1) cp = next_to_last; else cp = last_word; } else cp = last_word; *last_word = ' '; if (cp-buf >= len - max + 3 + 10-1) { char* s = buf + max - (len-(cp-buf)+3); *s++ = '.'; *s++ = '.'; *s++ = '.'; safecpy(s, cp + 1, max); len = max; } } } if (len > max) buf[max] = '\0'; return buf; } /* Modified version of a spinner originally found in Clifford Adams' strn. */ static char *spinchars; static int spin_level INIT(0); /* used to allow non-interfering nested spins */ static int spin_mode; static int spin_place; /* represents place in spinchars array */ static int spin_pos; /* the last spinbar position we drew */ static ART_NUM spin_art; static ART_POS spin_tell; void setspin(mode) int mode; { switch (mode) { case SPIN_FOREGROUND: case SPIN_BACKGROUND: case SPIN_BARGRAPH: if (!spin_level++) { if ((spin_art = openart) != 0 && artfp) spin_tell = tellart(); spin_count = 0; spin_place = 0; } if (spin_mode == SPIN_BARGRAPH) mode = SPIN_BARGRAPH; if (mode == SPIN_BARGRAPH) { if (spin_mode != SPIN_BARGRAPH) { int i; #ifdef VERBOSE spin_marks = (verbose? 25 : 10); #else spin_marks = 25; #endif printf(" [%*s]", spin_marks, nullstr); for (i = spin_marks + 1; i--; ) backspace(); fflush(stdout); } spin_pos = 0; } spinchars = "|/-\\"; spin_mode = mode; break; case SPIN_POP: case SPIN_OFF: if (spin_mode == SPIN_BARGRAPH) { spin_level = 1; spin(10000); if (spin_count >= spin_todo) spin_char = ']'; spin_count--; spin_mode = SPIN_FOREGROUND; } if (mode == SPIN_POP && --spin_level > 0) break; spin_level = 0; if (spin_place) { /* we have spun at least once */ putchar(spin_char); /* get rid of spin character */ backspace(); fflush(stdout); spin_place = 0; } if (spin_art) { artopen(spin_art,spin_tell); /* do not screw up the pager */ spin_art = 0; } spin_mode = SPIN_OFF; spin_char = ' '; break; } } void spin(count) int count; /* modulus for the spin... */ { if (!spin_level) return; switch (spin_mode) { case SPIN_BACKGROUND: if (!bkgnd_spinner) return; if (!(++spin_count % count)) { putchar(spinchars[++spin_place % 4]); backspace(); fflush(stdout); #ifdef USE_TK if (ttk_running) ttk_do_waiting_events(); #endif } break; case SPIN_FOREGROUND: if (!(++spin_count % count)) { putchar('.'); fflush(stdout); #ifdef USE_TK if (ttk_running) ttk_do_waiting_events(); #endif } break; case SPIN_BARGRAPH: { int new_pos; if (spin_todo == 0) break; /* bail out rather than crash */ new_pos = (int)((long)spin_marks * ++spin_count / spin_todo); if (spin_pos < new_pos && spin_count <= spin_todo+1) { do { putchar('*'); } while (++spin_pos < new_pos); spin_place = 0; fflush(stdout); #ifdef USE_TK if (ttk_running) ttk_do_waiting_events(); #endif } else if (!(spin_count % count)) { putchar(spinchars[++spin_place % 4]); backspace(); fflush(stdout); #ifdef USE_TK if (ttk_running) ttk_do_waiting_events(); #endif } break; } } } bool inbackground() { return spin_mode == SPIN_BACKGROUND; } static int prior_perform_cnt; static time_t prior_now; static long ps_sel; static long ps_cnt; static long ps_missing; void perform_status_init(cnt) long cnt; { perform_cnt = 0; error_occurred = FALSE; subjline = NULL; page_line = 1; performed_article_loop = TRUE; prior_perform_cnt = 0; prior_now = 0; ps_sel = selected_count; ps_cnt = cnt; ps_missing = missing_count; spin_count = 0; spin_place = 0; spinchars = "v>^<"; } void perform_status(cnt, spin) long cnt; int spin; { long kills, sels, missing; time_t now; if (!(++spin_count % spin)) { putchar(spinchars[++spin_place % 4]); backspace(); fflush(stdout); } if (perform_cnt == prior_perform_cnt) return; now = time((time_t*)NULL); if (now - prior_now < 2) return; prior_now = now; prior_perform_cnt = perform_cnt; missing = missing_count - ps_missing; kills = ps_cnt - cnt - missing; sels = selected_count - ps_sel; if (!(kills | sels)) return; carriage_return(); if (perform_cnt != sels && perform_cnt != -sels && perform_cnt != kills && perform_cnt != -kills) printf("M:%d ", perform_cnt); if (kills) printf("K:%ld ", kills); if (sels) printf("S:%ld ", sels); #if 0 if (missing > 0) printf("(M: %ld) ", missing); #endif erase_eol(); fflush(stdout); } static char* output_change(cp, num, obj_type, modifier, action) char* cp; long num; char* obj_type; char* modifier; char* action; { bool neg; char* s; if (num < 0) { num *= -1; neg = 1; } else neg = 0; if (cp != msg) { *cp++ = ','; *cp++ = ' '; } sprintf(cp, "%ld ", num); if (obj_type) sprintf(cp+=strlen(cp), "%s%s ", obj_type, PLURAL(num)); cp += strlen(cp); if ((s = modifier) != NULL) { *cp++ = ' '; if (num != 1) while (*s++ != '|') ; while (*s && *s != '|') *cp++ = *s++; *cp++ = ' '; } s = action; if (!neg) while (*s++ != '|') ; while (*s && *s != '|') *cp++ = *s++; s++; if (neg) while (*s++ != '|') ; while (*s) *cp++ = *s++; *cp = '\0'; return cp; } int perform_status_end(cnt, obj_type) long cnt; char* obj_type; { long kills, sels, missing; char* cp = msg; bool article_status = (*obj_type == 'a'); if (perform_cnt == 0) { sprintf(msg, "No %ss affected.", obj_type); return 0; } missing = missing_count - ps_missing; kills = ps_cnt - cnt - missing; sels = selected_count - ps_sel; if (!performed_article_loop) cp = output_change(cp, (long)perform_cnt, sel_mode == SM_THREAD? "thread" : "subject", (char*)NULL, "ERR|match|ed"); else if (perform_cnt != sels && perform_cnt != -sels && perform_cnt != kills && perform_cnt != -kills) { cp = output_change(cp, (long)perform_cnt, obj_type, (char*)NULL, "ERR|match|ed"); obj_type = NULL; } if (kills) { cp = output_change(cp, kills, obj_type, (char*)NULL, article_status? "un||killed" : "more|less|"); obj_type = NULL; } if (sels) { cp = output_change(cp, sels, obj_type, (char*)NULL, "de||selected"); obj_type = NULL; } if (article_status && missing > 0) { *cp++ = '('; cp = output_change(cp, missing, obj_type, "was|were", "ERR|missing|"); *cp++ = ')'; } strcpy(cp, "."); /* If we only selected/deselected things, return 1, else 2 */ return (kills | missing) == 0? 1 : 2; } trn-4.0-test77/rt-util.h0000644000000000000000000000206007113133016013553 0ustar rootroot/* rt-util.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char spin_char INIT(' '); /* char to put back when we're done spinning */ EXT long spin_estimate; /* best guess of how much work there is */ EXT long spin_todo; /* the max word to do (might decrease) */ EXT int spin_count; /* counter for when to spin */ EXT int spin_marks INIT(25); /* how many bargraph marks we want */ #define SPIN_OFF 0 #define SPIN_POP 1 #define SPIN_FOREGROUND 2 #define SPIN_BACKGROUND 3 #define SPIN_BARGRAPH 4 EXT bool performed_article_loop; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ char* extract_name _((char*)); char* compress_name _((char*,int)); char* compress_address _((char*,int)); char* compress_from _((char*,int)); char* compress_date _((ARTICLE*,int)); bool subject_has_Re _((char*,char**)); char* compress_subj _((ARTICLE*,int)); void setspin _((int)); void spin _((int)); bool inbackground _((void)); void perform_status_init _((long)); void perform_status _((long,int)); int perform_status_end _((long,char*)); trn-4.0-test77/rt-util.ih0000644000000000000000000000032507113133016013726 0ustar rootroot/* rt-util.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static char* output_change _((char*,long,char*,char*,char*)); trn-4.0-test77/rt-wumpus.c0000644000000000000000000003452611437640112014151 0ustar rootroot/* rt-wumpus.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "cache.h" #include "ng.h" #include "head.h" #include "util.h" #include "util2.h" #include "term.h" #include "final.h" #include "ngdata.h" #include "artio.h" #include "artstate.h" #include "backpage.h" #include "rthread.h" #include "rt-select.h" #include "charsubst.h" #include "color.h" #include "INTERN.h" #include "rt-wumpus.h" #include "rt-wumpus.ih" static char tree_indent[] = { ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0 }; char letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+"; static ARTICLE* tree_article; static int max_depth, max_line = -1; static int first_depth, first_line; static int my_depth, my_line; static bool node_on_line; static int node_line_cnt; static int line_num; static int header_indent; static char* tree_lines[11]; static char tree_buff[128]; static char* str; /* Prepare tree display for inclusion in the article header. */ void init_tree() { ARTICLE* thread; SUBJECT* sp; int num; while (max_line >= 0) /* free any previous tree data */ free(tree_lines[max_line--]); if (!(tree_article = curr_artp) || !tree_article->subj) return; if (!(thread = tree_article->subj->thread)) return; /* Enumerate our subjects for display */ sp = thread->subj; num = 0; do { sp->misc = num++; sp = sp->thread_link; } while (sp != thread->subj); max_depth = max_line = my_depth = my_line = node_line_cnt = 0; find_depth(thread, 0); if (max_depth <= 5) first_depth = 0; else { if (my_depth+2 > max_depth) first_depth = max_depth - 5; else if ((first_depth = my_depth - 3) < 0) first_depth = 0; max_depth = first_depth + 5; } if (--max_line < max_tree_lines) first_line = 0; else { if (my_line + max_tree_lines/2 > max_line) first_line = max_line - (max_tree_lines-1); else if ((first_line = my_line - (max_tree_lines-1)/2) < 0) first_line = 0; max_line = first_line + max_tree_lines-1; } str = tree_buff; /* initialize first line's data */ *str++ = ' '; node_on_line = FALSE; line_num = 0; /* cache our portion of the tree */ cache_tree(thread, 0, tree_indent); max_depth = (max_depth-first_depth+1) * 5; /* turn depth into char width */ max_line -= first_line; /* turn max_line into count */ /* shorten tree if lower lines aren't visible */ if (node_line_cnt < max_line) max_line = node_line_cnt + 1; } /* A recursive routine to find the maximum tree extents and where we are. */ static void find_depth(article, depth) ARTICLE* article; int depth; { if (depth > max_depth) max_depth = depth; for (;;) { if (article == tree_article) { my_depth = depth; my_line = max_line; } if (article->child1) find_depth(article->child1, depth+1); else max_line++; if (!(article = article->sibling)) break; } } /* Place the tree display in a maximum of 11 lines x 6 nodes. */ static void cache_tree(ap, depth, cp) ARTICLE* ap; int depth; char* cp; { int depth_mode; cp[1] = ' '; if (depth >= first_depth && depth <= max_depth) { cp += 5; depth_mode = 1; } else if (depth+1 == first_depth) depth_mode = 2; else { cp = tree_indent; depth_mode = 0; } for (;;) { switch (depth_mode) { case 1: { char ch; *str++ = ((ap->flags & AF_HAS_RE) || ap->parent) ? '-' : ' '; if (ap == tree_article) *str++ = '*'; if (!(ap->flags & AF_UNREAD)) { *str++ = '('; ch = ')'; } else if (!selected_only || (ap->flags & AF_SEL)) { *str++ = '['; ch = ']'; } else { *str++ = '<'; ch = '>'; } if (ap == recent_artp && ap != tree_article) *str++ = '@'; *str++ = thread_letter(ap); *str++ = ch; if (ap->child1) *str++ = (ap->child1->sibling? '+' : '-'); if (ap->sibling) *cp = '|'; else *cp = ' '; node_on_line = TRUE; break; } case 2: *tree_buff = (!ap->child1)? ' ' : (ap->child1->sibling)? '+' : '-'; break; default: break; } if (ap->child1) { cache_tree(ap->child1, depth+1, cp); cp[1] = '\0'; } else { if (!node_on_line && first_line == line_num) first_line++; if (line_num >= first_line) { if (str[-1] == ' ') str--; *str = '\0'; tree_lines[line_num-first_line] = safemalloc(str-tree_buff + 1); strcpy(tree_lines[line_num - first_line], tree_buff); if (node_on_line) { node_line_cnt = line_num - first_line; } } line_num++; node_on_line = FALSE; } if (!(ap = ap->sibling) || line_num > max_line) break; if (!ap->sibling) *cp = '\\'; if (!first_depth) tree_indent[5] = ' '; strcpy(tree_buff, tree_indent+5); str = tree_buff + strlen(tree_buff); } } static int find_artp_y; ARTICLE* get_tree_artp(x,y) int x; int y; { ARTICLE* ap; if (!tree_article || !tree_article->subj) return NULL; ap = tree_article->subj->thread; x -= tc_COLS-1 - max_depth; if (x < 0 || y > max_line || !ap) return NULL; x = (x-(x==max_depth))/5 + first_depth; find_artp_y = y + first_line; ap = find_artp(ap, x); return ap; } /* A recursive routine to find the maximum tree extents and where we are. */ static ARTICLE* find_artp(article, x) ARTICLE* article; int x; { for (;;) { if (!x && !find_artp_y) return article; if (article->child1) { ARTICLE* ap = find_artp(article->child1, x-1); if (ap) return ap; } else if (!find_artp_y--) return NULL; if (!(article = article->sibling)) break; } return NULL; } /* Output a header line with possible tree display on the right hand side. ** Does automatic wrapping of lines that are too long. */ int tree_puts(orig_line, header_line, is_subject) char* orig_line; ART_LINE header_line; int is_subject; { char* tmpbuf; register char* line; register char* cp; register char* end; int pad_cnt, wrap_at; ART_LINE start_line = header_line; int i, len; char ch; /* Make a modifiable copy of the line */ if ((cp = index(orig_line, '\n')) != NULL) len = cp - orig_line; else len = strlen(orig_line); /* Copy line, filtering encoded and control characters. */ #ifdef CHARSUBST if (HEADER_CONV()) { tmpbuf = safemalloc(len * 2 + 2); line = tmpbuf + len + 1; } else #endif line = tmpbuf = safemalloc(len + 2); /* yes, I mean "2" */ if (do_hiding) end = line + decode_header(line, orig_line, len); else { safecpy(line, orig_line, len+1); dectrl(line); end = line + len; } #ifdef CHARSUBST if (HEADER_CONV()) { end = tmpbuf + strcharsubst(tmpbuf, line, len*2+2, *charsubst); line = tmpbuf; } #endif if (!*line) { strcpy(line, " "); end = line+1; } color_object(COLOR_HEADER, 1); /* If this is the first subject line, output it with a preceeding [1] */ if (is_subject && !isspace(*line)) { if (ThreadedGroup) { color_object(COLOR_TREE_MARK, 1); putchar('['); putchar(thread_letter(curr_artp)); putchar(']'); color_pop(); putchar(' '); header_indent = 4; } else { fputs("Subject: ", stdout); header_indent = 9; } i = 0; } else { if (*line != ' ') { /* A "normal" header line -- output keyword and set header_indent ** _except_ for the first line, which is a non-standard header. */ if (!header_line || !(cp = index(line, ':')) || *++cp != ' ') header_indent = 0; else { *cp = '\0'; fputs(line, stdout); putchar(' '); header_indent = ++cp - line; line = cp; if (!*line) *--line = ' '; } i = 0; } else { /* Skip whitespace of continuation lines and prepare to indent */ while (*++line == ' ') ; i = header_indent; } } for ( ; *line; i = header_indent) { maybe_eol(); if (i) { putchar('+'); while (--i) putchar(' '); } term_col = header_indent; /* If no (more) tree lines, wrap at tc_COLS-1 */ if (max_line < 0 || header_line > max_line+1) wrap_at = tc_COLS-1; else wrap_at = tc_COLS - max_depth - 3; /* Figure padding between header and tree output, wrapping long lines */ pad_cnt = wrap_at - (end - line + header_indent); if (pad_cnt <= 0) { cp = line + (int)(wrap_at - header_indent - 1); pad_cnt = 1; while (cp > line && *cp != ' ') { if (*--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!') { cp++; break; } pad_cnt++; } if (cp == line) { cp += wrap_at - header_indent; pad_cnt = 0; } ch = *cp; *cp = '\0'; /* keep rn's backpager happy */ vwtary(artline, vrdary(artline - 1)); artline++; } else { cp = end; ch = '\0'; } if (is_subject) color_string(COLOR_SUBJECT, line); else if (header_indent == 0 && *line != '+') color_string(COLOR_ARTLINE1, line); else fputs(line, stdout); *cp = ch; /* Skip whitespace in wrapped line */ while (*cp == ' ') cp++; line = cp; /* Check if we've got any tree lines to output */ if (wrap_at != tc_COLS-1 && header_line <= max_line) { char* cp1; char* cp2; do { putchar(' '); } while (pad_cnt--); term_col = wrap_at; /* Check string for the '*' flagging our current node ** and the '@' flagging our prior node. */ cp = tree_lines[header_line]; cp1 = index(cp, '*'); cp2 = index(cp, '@'); if (cp1 != NULL) *cp1 = '\0'; if (cp2 != NULL) *cp2 = '\0'; color_object(COLOR_TREE, 1); fputs(cp, stdout); /* Handle standout output for '*' and '@' marked nodes, then ** continue with the rest of the line. */ while (cp1 || cp2) { color_object(COLOR_TREE_MARK, 1); if (cp1 && (!cp2 || cp1 < cp2)) { cp = cp1; cp1 = NULL; *cp++ = '*'; putchar(*cp++); putchar(*cp++); } else { cp = cp2; cp2 = NULL; *cp++ = '@'; } putchar(*cp++); color_pop(); /* of COLOR_TREE_MARK */ if (*cp) fputs(cp, stdout); }/* while */ color_pop(); /* of COLOR_TREE */ }/* if */ newline(); header_line++; }/* for remainder of line */ /* free allocated copy of line */ free(tmpbuf); color_pop(); /* of COLOR_HEADER */ /* return number of lines displayed */ return header_line - start_line; } /* Output any parts of the tree that are left to display. Called at the ** end of each header. */ int finish_tree(last_line) ART_LINE last_line; { ART_LINE start_line = last_line; while (last_line <= max_line) { artline++; last_line += tree_puts("+", last_line, 0); vwtary(artline, artpos); /* keep rn's backpager happy */ } return last_line - start_line; } /* Output the entire article tree for the user. */ void entire_tree(ap) ARTICLE* ap; { ARTICLE* thread; SUBJECT* sp; int num; if (!ap) { #ifdef VERBOSE IF (verbose) fputs("\nNo article tree to display.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNo tree.\n", stdout) FLUSH; #endif termdown(2); return; } if (!ThreadedGroup) { ThreadedGroup = TRUE; printf("Threading the group. "), fflush(stdout); thread_open(); if (!ThreadedGroup) { printf("*failed*\n") FLUSH; termdown(1); return; } count_subjects(CS_NORM); newline(); } if (!(ap->flags & AF_THREADED)) parseheader(article_num(ap)); if (check_page_line()) return; newline(); thread = ap->subj->thread; /* Enumerate our subjects for display */ sp = thread->subj; num = 0; do { if (check_page_line()) return; printf("[%c] %s\n",letters[num>9+26+26? 9+26+26:num],sp->str+4) FLUSH; termdown(1); sp->misc = num++; sp = sp->thread_link; } while (sp != thread->subj); if (check_page_line()) return; newline(); if (check_page_line()) return; putchar(' '); buf[3] = '\0'; display_tree(thread, tree_indent); if (check_page_line()) return; newline(); } /* A recursive routine to output the entire article tree. */ static void display_tree(article, cp) ARTICLE* article; char* cp; { if (cp - tree_indent > tc_COLS || page_line < 0) return; cp[1] = ' '; cp += 5; color_object(COLOR_TREE, 1); for (;;) { putchar(((article->flags&AF_HAS_RE) || article->parent) ? '-' : ' '); if (!(article->flags & AF_UNREAD)) { buf[0] = '('; buf[2] = ')'; } else if (!selected_only || (article->flags & AF_SEL)) { buf[0] = '['; buf[2] = ']'; } else { buf[0] = '<'; buf[2] = '>'; } buf[1] = thread_letter(article); if (article == curr_artp) { color_string(COLOR_TREE_MARK,buf); } else if (article == recent_artp) { putchar(buf[0]); color_object(COLOR_TREE_MARK, 1); putchar(buf[1]); color_pop(); /* of COLOR_TREE_MARK */ putchar(buf[2]); } else fputs(buf, stdout); if (article->sibling) { *cp = '|'; } else *cp = ' '; if (article->child1) { putchar((article->child1->sibling)? '+' : '-'); color_pop(); /* of COLOR_TREE */ display_tree(article->child1, cp); color_object(COLOR_TREE, 1); cp[1] = '\0'; } else newline(); if (!(article = article->sibling)) break; if (!article->sibling) *cp = '\\'; tree_indent[5] = ' '; if (check_page_line()) { color_pop(); return; } fputs(tree_indent+5, stdout); } color_pop(); /* of COLOR_TREE */ } /* Calculate the subject letter representation. "Place-holder" nodes ** are marked with a ' ', others get a letter in the sequence: ** ' ', '1'-'9', 'A'-'Z', 'a'-'z', '+' */ char thread_letter(ap) register ARTICLE* ap; { int subj = ap->subj->misc; if (!(ap->flags & AF_CACHED) && (absfirst < first_cached || last_cached < lastart || !cached_all_in_range)) return '?'; if (!(ap->flags & AF_EXISTS)) return ' '; return letters[subj > 9+26+26 ? 9+26+26 : subj]; } trn-4.0-test77/rt-wumpus.h0000644000000000000000000000053707113133016014145 0ustar rootroot/* rt-wumpus.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void init_tree _((void)); ARTICLE* get_tree_artp _((int,int)); int tree_puts _((char*,ART_LINE,int)); int finish_tree _((ART_LINE)); void entire_tree _((ARTICLE*)); char thread_letter _((ARTICLE*)); trn-4.0-test77/rt-wumpus.ih0000644000000000000000000000051607113133016014313 0ustar rootroot/* rt-wumpus.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void find_depth _((ARTICLE*,int)); static void cache_tree _((ARTICLE*,int,char*)); static ARTICLE* find_artp _((ARTICLE*,int)); static void display_tree _((ARTICLE*,char*)); trn-4.0-test77/rthread.c0000644000000000000000000011573307212000755013616 0ustar rootroot/* rthread.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "intrp.h" #include "trn.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "ng.h" #include "rcln.h" #include "search.h" #include "artstate.h" #include "rcstuff.h" #include "final.h" #include "kfile.h" #include "head.h" #include "util.h" #include "util2.h" #include "rt-mt.h" #include "rt-ov.h" #ifdef SCORE #include "score.h" #endif #include "rt-page.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #include "rt-wumpus.h" #include "INTERN.h" #include "rthread.h" #include "rthread.ih" void thread_init() { } /* Generate the thread data we need for this group. We must call ** thread_close() before calling this again. */ void thread_open() { bool find_existing = !first_subject; time_t save_ov_opened; ARTICLE* ap; if (!msgid_hash) msgid_hash = hashcreate(1999, msgid_cmp); /*TODO: pick a better size */ if (ThreadedGroup) { /* Parse input and use msgid_hash for quick article lookups. */ /* If cached but not threaded articles exist, set up to thread them. */ if (first_subject) { first_cached = firstart; last_cached = firstart - 1; parsed_art = 0; } } if (sel_mode == SM_ARTICLE) set_selector(sel_mode, sel_artsort); else set_selector(sel_threadmode, sel_threadsort); if ((datasrc->flags & DF_TRY_THREAD) && !first_subject) { if (mt_data() < 0) return; } if ((datasrc->flags & DF_TRY_OVERVIEW) && !cached_all_in_range) { if (thread_always) { spin_todo = spin_estimate = lastart - absfirst + 1; (void) ov_data(absfirst, lastart, FALSE); if (datasrc->ov_opened && find_existing && datasrc->over_dir == NULL) { mark_missing_articles(); rc_to_bits(); find_existing = FALSE; } } else { spin_todo = lastart - firstart + 1; spin_estimate = ngptr->toread; if (firstart > lastart) { /* If no unread articles, see if ov. exists as fast as possible */ (void) ov_data(absfirst, absfirst, FALSE); cached_all_in_range = FALSE; } else (void) ov_data(firstart, lastart, FALSE); } } if (find_existing) { find_existing_articles(); mark_missing_articles(); rc_to_bits(); } for (ap = article_ptr(article_first(firstart)); ap && article_num(ap) <= last_cached; ap = article_nextp(ap)) { if ((ap->flags & (AF_EXISTS|AF_CACHED)) == AF_EXISTS) (void) parseheader(article_num(ap)); } if (last_cached > lastart) { ngptr->toread += (ART_UNREAD)(last_cached-lastart); /* ensure getngsize() knows the new maximum */ ngptr->ngmax = lastart = last_cached; } article_list->high = lastart; for (ap = article_ptr(article_first(absfirst)); ap && article_num(ap) <= lastart; ap = article_nextp(ap)) { if (ap->flags & AF_CACHED) check_poster(ap); } save_ov_opened = datasrc->ov_opened; datasrc->ov_opened = 0; /* avoid trying to call ov_data twice for high arts */ thread_grow(); /* thread any new articles not yet in the database */ datasrc->ov_opened = save_ov_opened; added_articles = 0; sel_page_sp = 0; sel_page_app = 0; } /* Update the group's thread info. */ void thread_grow() { added_articles += lastart - last_cached; if (added_articles && thread_always) cache_range(last_cached + 1, lastart); count_subjects(CS_NORM); if (artptr_list) sort_articles(); else sort_subjects(); } void thread_close() { curr_artp = artp = NULL; init_tree(); /* free any tree lines */ update_thread_kfile(); if (msgid_hash) hashwalk(msgid_hash, cleanup_msgid_hash, 0); sel_page_sp = 0; sel_page_app = 0; sel_last_ap = 0; sel_last_sp = 0; selected_only = FALSE; sel_exclusive = 0; ov_close(); } static int cleanup_msgid_hash(keylen, data, extra) int keylen; HASHDATUM* data; int extra; { register ARTICLE* ap = (ARTICLE*)data->dat_ptr; int ret = -1; if (ap) { if (data->dat_len) return 0; if ((kf_state & KFS_GLOBAL_THREADFILE) && ap->autofl) { data->dat_ptr = ap->msgid; data->dat_len = ap->autofl; ret = 0; ap->msgid = NULL; } if (ap->flags & AF_TMPMEM) { clear_article(ap); free((char*)ap); } } return ret; } void top_article() { art = lastart+1; artp = NULL; inc_art(selected_only, FALSE); if (art > lastart && last_cached < lastart) art = firstart; } ARTICLE* first_art(sp) register SUBJECT* sp; { register ARTICLE* ap = (ThreadedGroup? sp->thread : sp->articles); if (ap && !(ap->flags & AF_EXISTS)) { oneless(ap); ap = next_art(ap); } return ap; } ARTICLE* last_art(sp) register SUBJECT* sp; { register ARTICLE* ap; if (!ThreadedGroup) { ap = sp->articles; while (ap->subj_next) ap = ap->subj_next; return ap; } ap = sp->thread; if (ap) { for (;;) { if (ap->sibling) ap = ap->sibling; else if (ap->child1) ap = ap->child1; else break; } if (!(ap->flags & AF_EXISTS)) { oneless(ap); ap = prev_art(ap); } } return ap; } /* Bump art/artp to the next article, wrapping from thread to thread. ** If sel_flag is TRUE, only stops at selected articles. ** If rereading is FALSE, only stops at unread articles. */ void inc_art(sel_flag, rereading) bool_int sel_flag, rereading; { register ARTICLE* ap = artp; int subj_mask = (rereading? 0 : SF_VISIT); /* Use the explicit article-order if it exists */ if (artptr_list) { ARTICLE** limit = artptr_list + artptr_list_size; if (!ap) artptr = artptr_list-1; else if (!artptr || *artptr != ap) { for (artptr = artptr_list; artptr < limit; artptr++) { if (*artptr == ap) break; } } do { if (++artptr >= limit) break; ap = *artptr; } while ((!rereading && !(ap->flags & AF_UNREAD)) || (sel_flag && !(ap->flags & AF_SEL))); if (artptr < limit) { artp = *artptr; art = article_num(artp); } else { artp = NULL; art = lastart+1; artptr = artptr_list; } return; } /* Use subject- or thread-order when possible */ if (ThreadedGroup || srchahead) { register SUBJECT* sp; if (ap) sp = ap->subj; else sp = next_subj((SUBJECT*)NULL, subj_mask); if (!sp) goto num_inc; do { if (ap) ap = next_art(ap); else ap = first_art(sp); while (!ap) { sp = next_subj(sp, subj_mask); if (!sp) break; ap = first_art(sp); } } while (ap && ((!rereading && !(ap->flags & AF_UNREAD)) || (sel_flag && !(ap->flags & AF_SEL)))); if ((artp = ap) != NULL) art = article_num(ap); else { if (art <= last_cached) art = last_cached+1; else art++; if (art <= lastart) artp = article_ptr(art); else art = lastart+1; } return; } /* Otherwise, just increment through the art numbers */ num_inc: if (!ap) art = firstart-1; for (;;) { art = article_next(art); if (art > lastart) { art = lastart+1; ap = NULL; break; } ap = article_ptr(art); if (!(ap->flags & AF_EXISTS)) oneless(ap); else if ((rereading || (ap->flags & AF_UNREAD)) && (!sel_flag || (ap->flags & AF_SEL))) break; } artp = ap; } /* Bump art/artp to the previous article, wrapping from thread to thread. ** If sel_flag is TRUE, only stops at selected articles. ** If rereading is FALSE, only stops at unread articles. */ void dec_art(sel_flag, rereading) bool_int sel_flag, rereading; { register ARTICLE* ap = artp; int subj_mask = (rereading? 0 : SF_VISIT); /* Use the explicit article-order if it exists */ if (artptr_list) { ARTICLE** limit = artptr_list + artptr_list_size; if (!ap) artptr = limit; else if (!artptr || *artptr != ap) { for (artptr = artptr_list; artptr < limit; artptr++) { if (*artptr == ap) break; } } do { if (artptr == artptr_list) break; ap = *--artptr; } while ((!rereading && !(ap->flags & AF_UNREAD)) || (sel_flag && !(ap->flags & AF_SEL))); artp = *artptr; art = article_num(artp); return; } /* Use subject- or thread-order when possible */ if (ThreadedGroup || srchahead) { register SUBJECT* sp; if (ap) sp = ap->subj; else sp = prev_subj((SUBJECT*)NULL, subj_mask); if (!sp) goto num_dec; do { if (ap) ap = prev_art(ap); else ap = last_art(sp); while (!ap) { sp = prev_subj(sp, subj_mask); if (!sp) break; ap = last_art(sp); } } while (ap && ((!rereading && !(ap->flags & AF_UNREAD)) || (sel_flag && !(ap->flags & AF_SEL)))); if ((artp = ap) != NULL) art = article_num(ap); else art = absfirst-1; return; } /* Otherwise, just decrement through the art numbers */ num_dec: for (;;) { art = article_prev(art); if (art < absfirst) { art = absfirst-1; ap = NULL; break; } ap = article_ptr(art); if (!(ap->flags & AF_EXISTS)) oneless(ap); else if ((rereading || (ap->flags & AF_UNREAD)) && (!sel_flag || (ap->flags & AF_SEL))) break; } artp = ap; } /* Bump the param to the next article in depth-first order. */ ARTICLE* bump_art(ap) register ARTICLE* ap; { if (ap->child1) return ap->child1; while (!ap->sibling) { if (!(ap = ap->parent)) return NULL; } return ap->sibling; } /* Bump the param to the next REAL article. Uses subject order in a ** non-threaded group; honors the breadth_first flag in a threaded one. */ ARTICLE* next_art(ap) register ARTICLE* ap; { try_again: if (!ThreadedGroup) { ap = ap->subj_next; goto done; } if (breadth_first) { if (ap->sibling) { ap = ap->sibling; goto done; } if (ap->parent) ap = ap->parent->child1; else ap = ap->subj->thread; } do { if (ap->child1) { ap = ap->child1; goto done; } while (!ap->sibling) { if (!(ap = ap->parent)) return NULL; } ap = ap->sibling; } while (breadth_first); done: if (ap && !(ap->flags & AF_EXISTS)) { oneless(ap); goto try_again; } return ap; } /* Bump the param to the previous REAL article. Uses subject order in a ** non-threaded group. */ ARTICLE* prev_art(ap) register ARTICLE* ap; { register ARTICLE* initial_ap; try_again: initial_ap = ap; if (!ThreadedGroup) { if ((ap = ap->subj->articles) == initial_ap) ap = NULL; else while (ap->subj_next != initial_ap) ap = ap->subj_next; goto done; } ap = (ap->parent ? ap->parent->child1 : ap->subj->thread); if (ap == initial_ap) { ap = ap->parent; goto done; } while (ap->sibling != initial_ap) ap = ap->sibling; while (ap->child1) { ap = ap->child1; while (ap->sibling) ap = ap->sibling; } done: if (ap && !(ap->flags & AF_EXISTS)) { oneless(ap); goto try_again; } return ap; } /* Find the next art/artp with the same subject as this one. Returns ** FALSE if no such article exists. */ bool next_art_with_subj() { register ARTICLE* ap = artp; if (!ap) return FALSE; do { ap = ap->subj_next; if (!ap) { if (!art) art = firstart; return FALSE; } } while (!ALLBITS(ap->flags, AF_EXISTS | AF_UNREAD) || (selected_only && !(ap->flags & AF_SEL))); artp = ap; art = article_num(ap); #ifdef ARTSEARCH srchahead = -1; #endif return TRUE; } /* Find the previous art/artp with the same subject as this one. Returns ** FALSE if no such article exists. */ bool prev_art_with_subj() { register ARTICLE* ap = artp; register ARTICLE* ap2; if (!ap) return FALSE; do { ap2 = ap->subj->articles; if (ap2 == ap) ap = NULL; else { while (ap2 && ap2->subj_next != ap) ap2 = ap2->subj_next; ap = ap2; } if (!ap) { if (!art) art = lastart; return FALSE; } } while (!(ap->flags & AF_EXISTS) || (selected_only && !(ap->flags & AF_SEL))); artp = ap; art = article_num(ap); return TRUE; } SUBJECT* next_subj(sp, subj_mask) register SUBJECT* sp; int subj_mask; { if (!sp) sp = first_subject; else if (sel_mode == SM_THREAD) { ARTICLE* ap = sp->thread; do { sp = sp->next; } while (sp && sp->thread == ap); } else sp = sp->next; while (sp && (sp->flags & subj_mask) != subj_mask) { sp = sp->next; } return sp; } SUBJECT* prev_subj(sp, subj_mask) register SUBJECT* sp; int subj_mask; { if (!sp) sp = last_subject; else if (sel_mode == SM_THREAD) { ARTICLE* ap = sp->thread; do { sp = sp->prev; } while (sp && sp->thread == ap); } else sp = sp->prev; while (sp && (sp->flags & subj_mask) != subj_mask) { sp = sp->prev; } return sp; } /* Select a single article. */ void select_article(ap, auto_flags) register ARTICLE* ap; int auto_flags; { int desired_flags = (sel_rereading? AF_EXISTS : (AF_EXISTS|AF_UNREAD)); #ifdef VERBOSE bool echo = (auto_flags & ALSO_ECHO) != 0; #endif auto_flags &= AUTO_SELS; if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == desired_flags) { if (!(ap->flags & sel_mask)) { selected_count++; #ifdef VERBOSE IF(verbose && echo && gmode != 's') fputs("\tSelected",stdout); #endif } ap->flags = (ap->flags & ~AF_DEL) | sel_mask; } if (auto_flags) change_auto_flags(ap, auto_flags); if (ap->subj) { if (!(ap->subj->flags & sel_mask)) selected_subj_cnt++; ap->subj->flags = (ap->subj->flags&~SF_DEL) | sel_mask | SF_VISIT; } selected_only = (selected_only || selected_count != 0); } /* Select this article's subject. */ void select_arts_subject(ap, auto_flags) register ARTICLE* ap; int auto_flags; { if (ap->subj && ap->subj->articles) select_subject(ap->subj, auto_flags); else select_article(ap, auto_flags); } /* Select all the articles in a subject. */ void select_subject(subj, auto_flags) SUBJECT* subj; int auto_flags; { register ARTICLE* ap; int desired_flags = (sel_rereading? AF_EXISTS : (AF_EXISTS|AF_UNREAD)); int old_count = selected_count; auto_flags &= AUTO_SELS; for (ap = subj->articles; ap; ap = ap->subj_next) { if ((ap->flags & (AF_EXISTS|AF_UNREAD|sel_mask)) == desired_flags) { ap->flags |= sel_mask; selected_count++; } if (auto_flags) change_auto_flags(ap, auto_flags); } if (selected_count > old_count) { if (!(subj->flags & sel_mask)) selected_subj_cnt++; subj->flags = (subj->flags & ~SF_DEL) | sel_mask | SF_VISIT | SF_WASSELECTED; selected_only = TRUE; } else subj->flags |= SF_WASSELECTED; } /* Select this article's thread. */ void select_arts_thread(ap, auto_flags) register ARTICLE* ap; int auto_flags; { if (ap->subj && ap->subj->thread) select_thread(ap->subj->thread, auto_flags); else select_arts_subject(ap, auto_flags); } /* Select all the articles in a thread. */ void select_thread(thread, auto_flags) register ARTICLE* thread; int auto_flags; { register SUBJECT* sp; sp = thread->subj; do { select_subject(sp, auto_flags); sp = sp->thread_link; } while (sp != thread->subj); } /* Select the subthread attached to this article. */ void select_subthread(ap, auto_flags) register ARTICLE* ap; int auto_flags; { register ARTICLE* limit; SUBJECT* subj; int desired_flags = (sel_rereading? AF_EXISTS : (AF_EXISTS|AF_UNREAD)); int old_count = selected_count; if (!ap) return; subj = ap->subj; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } auto_flags &= AUTO_SELS; for (; ap != limit; ap = bump_art(ap)) { if ((ap->flags & (AF_EXISTS|AF_UNREAD|sel_mask)) == desired_flags) { ap->flags |= sel_mask; selected_count++; } if (auto_flags) change_auto_flags(ap, auto_flags); } if (subj && selected_count > old_count) { if (!(subj->flags & sel_mask)) selected_subj_cnt++; subj->flags = (subj->flags & ~SF_DEL) | sel_mask | SF_VISIT; selected_only = TRUE; } } /* Deselect a single article. */ void deselect_article(ap, auto_flags) register ARTICLE* ap; int auto_flags; { #ifdef VERBOSE bool echo = (auto_flags & ALSO_ECHO) != 0; #endif auto_flags &= AUTO_SELS; if (ap->flags & sel_mask) { ap->flags &= ~sel_mask; if (!selected_count--) selected_count = 0; #ifdef VERBOSE IF(verbose && echo && gmode != 's') fputs("\tDeselected",stdout); #endif } if (sel_rereading && sel_mode == SM_ARTICLE) ap->flags |= AF_DEL; } /* Deselect this article's subject. */ void deselect_arts_subject(ap) register ARTICLE* ap; { if (ap->subj && ap->subj->articles) deselect_subject(ap->subj); else deselect_article(ap, 0); } /* Deselect all the articles in a subject. */ void deselect_subject(subj) SUBJECT* subj; { register ARTICLE* ap; for (ap = subj->articles; ap; ap = ap->subj_next) { if (ap->flags & sel_mask) { ap->flags &= ~sel_mask; if (!selected_count--) selected_count = 0; } } if (subj->flags & sel_mask) { subj->flags &= ~sel_mask; selected_subj_cnt--; } subj->flags &= ~(SF_VISIT | SF_WASSELECTED); if (sel_rereading) subj->flags |= SF_DEL; else subj->flags &= ~SF_DEL; } /* Deselect this article's thread. */ void deselect_arts_thread(ap) register ARTICLE* ap; { if (ap->subj && ap->subj->thread) deselect_thread(ap->subj->thread); else deselect_arts_subject(ap); } /* Deselect all the articles in a thread. */ void deselect_thread(thread) register ARTICLE* thread; { register SUBJECT* sp; sp = thread->subj; do { deselect_subject(sp); sp = sp->thread_link; } while (sp != thread->subj); } /* Deselect everything. */ void deselect_all() { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) deselect_subject(sp); selected_count = selected_subj_cnt = 0; sel_page_sp = 0; sel_page_app = 0; sel_last_ap = 0; sel_last_sp = 0; selected_only = FALSE; } /* Kill all unread articles attached to this article's subject. */ void kill_arts_subject(ap, auto_flags) register ARTICLE* ap; int auto_flags; { if (ap->subj && ap->subj->articles) kill_subject(ap->subj, auto_flags); else { if (auto_flags & SET_TORETURN) delay_unmark(ap); set_read(ap); auto_flags &= AUTO_KILLS; if (auto_flags) change_auto_flags(ap, auto_flags); } } /* Kill all unread articles attached to the given subject. */ void kill_subject(subj, auto_flags) SUBJECT* subj; int auto_flags; { register ARTICLE* ap; register int killmask = (auto_flags & AFFECT_ALL)? 0 : sel_mask; char toreturn = (auto_flags & SET_TORETURN) != 0; auto_flags &= AUTO_KILLS; for (ap = subj->articles; ap; ap = ap->subj_next) { if (toreturn) delay_unmark(ap); if ((ap->flags & (AF_UNREAD|killmask)) == AF_UNREAD) set_read(ap); if (auto_flags) change_auto_flags(ap, auto_flags); } subj->flags &= ~(SF_VISIT | SF_WASSELECTED); } /* Kill all unread articles attached to this article's thread. */ void kill_arts_thread(ap, auto_flags) register ARTICLE* ap; int auto_flags; { if (ap->subj && ap->subj->thread) kill_thread(ap->subj->thread, auto_flags); else kill_arts_subject(ap, auto_flags); } /* Kill all unread articles attached to the given thread. */ void kill_thread(thread, auto_flags) register ARTICLE* thread; int auto_flags; { register SUBJECT* sp; sp = thread->subj; do { kill_subject(sp, auto_flags); sp = sp->thread_link; } while (sp != thread->subj); } /* Kill the subthread attached to this article. */ void kill_subthread(ap, auto_flags) register ARTICLE* ap; int auto_flags; { register ARTICLE* limit; char toreturn = (auto_flags & SET_TORETURN) != 0; if (!ap) return; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } auto_flags &= AUTO_KILLS; for (; ap != limit; ap = bump_art(ap)) { if (toreturn) delay_unmark(ap); if (ALLBITS(ap->flags, AF_EXISTS | AF_UNREAD)) set_read(ap); if (auto_flags) change_auto_flags(ap, auto_flags); } } /* Unkill all the articles attached to the given subject. */ void unkill_subject(subj) SUBJECT* subj; { register ARTICLE* ap; int save_sel_count = selected_count; for (ap = subj->articles; ap; ap = ap->subj_next) { if (sel_rereading) { if (ALLBITS(ap->flags, AF_DELSEL | AF_EXISTS)) { if (!(ap->flags & AF_UNREAD)) ngptr->toread++; if (selected_only && !(ap->flags & AF_SEL)) selected_count++; ap->flags = (ap->flags & ~AF_DELSEL) | AF_SEL|AF_UNREAD; } else ap->flags &= ~(AF_DEL|AF_DELSEL); } else { if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == AF_EXISTS) onemore(ap); if (selected_only && (ap->flags & (AF_SEL|AF_UNREAD)) == AF_UNREAD) { ap->flags = (ap->flags & ~AF_DEL) | AF_SEL; selected_count++; } } } if (selected_count != save_sel_count && selected_only && !(subj->flags & SF_SEL)) { subj->flags |= SF_SEL | SF_VISIT | SF_WASSELECTED; selected_subj_cnt++; } subj->flags &= ~(SF_DEL|SF_DELSEL); } /* Unkill all the articles attached to the given thread. */ void unkill_thread(thread) register ARTICLE* thread; { register SUBJECT* sp; sp = thread->subj; do { unkill_subject(sp); sp = sp->thread_link; } while (sp != thread->subj); } /* Unkill the subthread attached to this article. */ void unkill_subthread(ap) register ARTICLE* ap; { register ARTICLE* limit; register SUBJECT* sp; if (!ap) return; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } sp = ap->subj; for (; ap != limit; ap = bump_art(ap)) { if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == AF_EXISTS) onemore(ap); if (selected_only && !(ap->flags & AF_SEL)) { ap->flags |= AF_SEL; selected_count++; } } if (!(sp->flags & sel_mask)) selected_subj_cnt++; sp->flags = (sp->flags & ~SF_DEL) | SF_SEL | SF_VISIT; selected_only = (selected_only || selected_count != 0); } /* Clear the auto flags in all unread articles attached to the given subject. */ void clear_subject(subj) SUBJECT* subj; { register ARTICLE* ap; for (ap = subj->articles; ap; ap = ap->subj_next) clear_auto_flags(ap); } /* Clear the auto flags in all unread articles attached to the given thread. */ void clear_thread(thread) register ARTICLE* thread; { register SUBJECT* sp; sp = thread->subj; do { clear_subject(sp); sp = sp->thread_link; } while (sp != thread->subj); } /* Clear the auto flags in the subthread attached to this article. */ void clear_subthread(ap) register ARTICLE* ap; { register ARTICLE* limit; if (!ap) return; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } for (; ap != limit; ap = bump_art(ap)) clear_auto_flags(ap); } ARTICLE* subj_art(sp) SUBJECT* sp; { register ARTICLE* ap = NULL; int art_mask = (selected_only? (AF_SEL|AF_UNREAD) : AF_UNREAD); bool TG_save = ThreadedGroup; ThreadedGroup = (sel_mode == SM_THREAD); ap = first_art(sp); while (ap && (ap->flags & art_mask) != art_mask) ap = next_art(ap); if (!ap) { reread = TRUE; ap = first_art(sp); if (selected_only) { while (ap && !(ap->flags & AF_SEL)) ap = next_art(ap); if (!ap) ap = first_art(sp); } } ThreadedGroup = TG_save; return ap; } /* Find the next thread (first if art > lastart). If articles are selected, ** only choose from threads with selected articles. */ void visit_next_thread() { register SUBJECT* sp; register ARTICLE* ap = artp; sp = (ap? ap->subj : NULL); while ((sp = next_subj(sp, SF_VISIT)) != NULL) { if ((ap = subj_art(sp)) != NULL) { art = article_num(ap); artp = ap; return; } reread = FALSE; } artp = NULL; art = lastart+1; forcelast = TRUE; } /* Find previous thread (or last if artp == NULL). If articles are selected, ** only choose from threads with selected articles. */ void visit_prev_thread() { register SUBJECT* sp; register ARTICLE* ap = artp; sp = (ap? ap->subj : NULL); while ((sp = prev_subj(sp, SF_VISIT)) != NULL) { if ((ap = subj_art(sp)) != NULL) { art = article_num(ap); artp = ap; return; } reread = FALSE; } artp = NULL; art = lastart+1; forcelast = TRUE; } /* Find artp's parent or oldest ancestor. Returns FALSE if no such ** article. Sets art and artp otherwise. */ bool find_parent(keep_going) bool_int keep_going; { register ARTICLE* ap = artp; if (!ap->parent) return FALSE; do { ap = ap->parent; } while (keep_going && ap->parent); artp = ap; art = article_num(ap); return TRUE; } /* Find artp's first child or youngest decendent. Returns FALSE if no ** such article. Sets art and artp otherwise. */ bool find_leaf(keep_going) bool_int keep_going; { register ARTICLE* ap = artp; if (!ap->child1) return FALSE; do { ap = ap->child1; } while (keep_going && ap->child1); artp = ap; art = article_num(ap); return TRUE; } /* Find the next "sibling" of artp, including cousins that are the ** same distance down the thread as we are. Returns FALSE if no such ** article. Sets art and artp otherwise. */ bool find_next_sib() { ARTICLE* ta; ARTICLE* tb; int ascent; ascent = 0; ta = artp; for (;;) { while (ta->sibling) { ta = ta->sibling; if ((tb = first_sib(ta, ascent)) != NULL) { artp = tb; art = article_num(tb); return TRUE; } } if (!(ta = ta->parent)) break; ascent++; } return FALSE; } /* A recursive routine to find the first node at the proper depth. This ** article is at depth 0. */ static ARTICLE* first_sib(ta, depth) ARTICLE* ta; int depth; { ARTICLE* tb; if (!depth) return ta; for (;;) { if (ta->child1 && (tb = first_sib(ta->child1, depth-1))) return tb; if (!ta->sibling) return NULL; ta = ta->sibling; } } /* Find the previous "sibling" of artp, including cousins that are ** the same distance down the thread as we are. Returns FALSE if no ** such article. Sets art and artp otherwise. */ bool find_prev_sib() { ARTICLE* ta; ARTICLE* tb; int ascent; ascent = 0; ta = artp; for (;;) { tb = ta; if (ta->parent) ta = ta->parent->child1; else ta = ta->subj->thread; if ((tb = last_sib(ta, ascent, tb)) != NULL) { artp = tb; art = article_num(tb); return TRUE; } if (!(ta = ta->parent)) break; ascent++; } return FALSE; } /* A recursive routine to find the last node at the proper depth. This ** article is at depth 0. */ static ARTICLE* last_sib(ta, depth, limit) ARTICLE* ta; int depth; ARTICLE* limit; { ARTICLE* tb; ARTICLE* tc; if (ta == limit) return NULL; if (ta->sibling) { tc = ta->sibling; if (tc != limit && (tb = last_sib(tc,depth,limit))) return tb; } if (!depth) return ta; if (ta->child1) return last_sib(ta->child1, depth-1, limit); return NULL; } /* Get each subject's article count; count total articles and selected ** articles (use sel_rereading to determine whether to count read or ** unread articles); deselect any subjects we find that are empty if ** CS_UNSELECT or CS_UNSEL_STORE is specified. If mode is CS_RESELECT ** is specified, the selections from the last CS_UNSEL_STORE are ** reselected. */ void count_subjects(cmode) int cmode; { register int count, sel_count; register ARTICLE* ap; register SUBJECT* sp; int desired_flags = (sel_rereading? AF_EXISTS : (AF_EXISTS|AF_UNREAD)); time_t subjdate; obj_count = selected_count = selected_subj_cnt = 0; if (last_cached >= lastart) firstart = lastart+1; switch (cmode) { case CS_RETAIN: break; case CS_UNSEL_STORE: for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_VISIT) sp->flags = (sp->flags & ~SF_VISIT) | SF_OLDVISIT; else sp->flags &= ~SF_OLDVISIT; } break; case CS_RESELECT: for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_OLDVISIT) sp->flags |= SF_VISIT; else sp->flags &= ~SF_VISIT; } break; default: for (sp = first_subject; sp; sp = sp->next) sp->flags &= ~SF_VISIT; } for (sp = first_subject; sp; sp = sp->next) { subjdate = 0; count = sel_count = 0; for (ap = sp->articles; ap; ap = ap->subj_next) { if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == desired_flags) { count++; if (ap->flags & sel_mask) sel_count++; if (!subjdate) subjdate = ap->date; if (article_num(ap) < firstart) firstart = article_num(ap); } } sp->misc = count; if (subjdate) sp->date = subjdate; else if (!sp->date && sp->articles) sp->date = sp->articles->date; obj_count += count; if (cmode == CS_RESELECT) { if (sp->flags & SF_VISIT) { sp->flags = (sp->flags & ~(SF_SEL|SF_DEL)) | sel_mask; selected_count += sel_count; selected_subj_cnt++; } else sp->flags &= ~sel_mask; } else { if (sel_count && (cmode >= CS_UNSEL_STORE || (sp->flags & sel_mask))) { sp->flags = (sp->flags & ~(SF_SEL|SF_DEL)) | sel_mask; selected_count += sel_count; selected_subj_cnt++; } else if (cmode >= CS_UNSELECT) sp->flags &= ~sel_mask; else if (sp->flags & sel_mask) { sp->flags &= ~SF_DEL; selected_subj_cnt++; } if (count && (!selected_only || (sp->flags & sel_mask))) { sp->flags |= SF_VISIT; } } } if (cmode != CS_RETAIN && cmode != CS_RESELECT && !obj_count && !selected_only) { for (sp = first_subject; sp; sp = sp->next) sp->flags |= SF_VISIT; } } static int subjorder_subject(spp1, spp2) register SUBJECT** spp1; register SUBJECT** spp2; { return strcaseCMP((*spp1)->str+4, (*spp2)->str+4) * sel_direction; } static int subjorder_date(spp1, spp2) register SUBJECT** spp1; register SUBJECT** spp2; { time_t eq = (*spp1)->date - (*spp2)->date; return eq? eq > 0? sel_direction : -sel_direction : 0; } static int subjorder_count(spp1, spp2) register SUBJECT** spp1; register SUBJECT** spp2; { short eq = (*spp1)->misc - (*spp2)->misc; return eq? eq > 0? sel_direction : -sel_direction : subjorder_date(spp1,spp2); } static int subjorder_lines(spp1, spp2) register SUBJECT** spp1; register SUBJECT** spp2; { long eq, l1, l2; l1 = (*spp1)->articles? (*spp1)->articles->lines : 0; l2 = (*spp2)->articles? (*spp2)->articles->lines : 0; eq = l1 - l2; return eq? eq > 0? sel_direction : -sel_direction : subjorder_date(spp1,spp2); } #ifdef SCORE /* for now, highest eligible article score */ static int subject_score_high(sp) SUBJECT* sp; { ARTICLE* ap; int desired_flags = (sel_rereading? AF_EXISTS : (AF_EXISTS|AF_UNREAD)); int hiscore = 0; int hiscore_found = 0; int sc; /* find highest score of desired articles */ for (ap = sp->articles; ap; ap = ap->subj_next) { if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == desired_flags) { sc = sc_score_art(article_num(ap),FALSE); if ((!hiscore_found) || (sc>hiscore)) { hiscore_found = 1; hiscore = sc; } } } return hiscore; } /* later make a subject-thread score routine */ static int subjorder_score(spp1, spp2) register SUBJECT** spp1; register SUBJECT** spp2; { int sc1, sc2; sc1 = subject_score_high(*spp1); sc2 = subject_score_high(*spp2); if (sc1 != sc2) return (sc2 - sc1) * sel_direction; return subjorder_date(spp1, spp2); } #endif static int threadorder_subject(spp1, spp2) SUBJECT** spp1; SUBJECT** spp2; { register ARTICLE* t1 = (*spp1)->thread; register ARTICLE* t2 = (*spp2)->thread; if (t1 != t2 && t1 && t2) return strcaseCMP(t1->subj->str+4, t2->subj->str+4) * sel_direction; return subjorder_date(spp1, spp2); } static int threadorder_date(spp1, spp2) SUBJECT** spp1; SUBJECT** spp2; { register ARTICLE* t1 = (*spp1)->thread; register ARTICLE* t2 = (*spp2)->thread; if (t1 != t2 && t1 && t2) { register SUBJECT* sp1; register SUBJECT* sp2; long eq; if (!(sp1 = t1->subj)->misc) for (sp1=sp1->thread_link; sp1 != t1->subj; sp1=sp1->thread_link) if (sp1->misc) break; if (!(sp2 = t2->subj)->misc) for (sp2=sp2->thread_link; sp2 != t2->subj; sp2=sp2->thread_link) if (sp2->misc) break; if (!(eq = sp1->date - sp2->date)) return strcaseCMP(sp1->str+4, sp2->str+4); return eq > 0? sel_direction : -sel_direction; } return subjorder_date(spp1, spp2); } static int threadorder_count(spp1, spp2) SUBJECT** spp1; SUBJECT** spp2; { register int size1 = (*spp1)->misc; register int size2 = (*spp2)->misc; if ((*spp1)->thread != (*spp2)->thread) { register SUBJECT* sp; for (sp = (*spp1)->thread_link; sp != *spp1; sp = sp->thread_link) size1 += sp->misc; for (sp = (*spp2)->thread_link; sp != *spp2; sp = sp->thread_link) size2 += sp->misc; } if (size1 != size2) return (size1 - size2) * sel_direction; return threadorder_date(spp1, spp2); } static int threadorder_lines(spp1, spp2) SUBJECT** spp1; SUBJECT** spp2; { register ARTICLE* t1 = (*spp1)->thread; register ARTICLE* t2 = (*spp2)->thread; if (t1 != t2 && t1 && t2) { register SUBJECT* sp1; register SUBJECT* sp2; long eq, l1, l2; if (!(sp1 = t1->subj)->misc) { for (sp1=sp1->thread_link; sp1 != t1->subj; sp1=sp1->thread_link) { if (sp1->misc) break; } } if (!(sp2 = t2->subj)->misc) { for (sp2=sp2->thread_link; sp2 != t2->subj; sp2=sp2->thread_link) { if (sp2->misc) break; } } l1 = sp1->articles? sp1->articles->lines : 0; l2 = sp2->articles? sp2->articles->lines : 0; if ((eq = l1 - l2) != 0) return eq > 0? sel_direction : -sel_direction; eq = sp1->date - sp2->date; return eq? eq > 0? sel_direction : -sel_direction : 0; } return subjorder_date(spp1, spp2); } #ifdef SCORE static int thread_score_high(tp) SUBJECT* tp; { SUBJECT* sp; int hiscore = 0; int hiscore_found = 0; int sc; for (sp = tp->thread_link; ; sp = sp->thread_link) { sc = subject_score_high(sp); if ((!hiscore_found) || (sc>hiscore)) { hiscore_found = 1; hiscore = sc; } /* break *after* doing the last item */ if (tp == sp) break; } return hiscore; } static int threadorder_score(spp1, spp2) SUBJECT** spp1; SUBJECT** spp2; { int sc1, sc2; sc1 = sc2 = 0; if ((*spp1)->thread != (*spp2)->thread) { sc1 = thread_score_high(*spp1); sc2 = thread_score_high(*spp2); } if (sc1 != sc2) return (sc2 - sc1) * sel_direction; return threadorder_date(spp1, spp2); } #endif /* Sort the subjects according to the chosen order. */ void sort_subjects() { register SUBJECT* sp; register int i; SUBJECT** lp; SUBJECT** subj_list; int (*sort_procedure)(); /* If we don't have at least two subjects, we're done! */ if (!first_subject || !first_subject->next) return; switch (sel_sort) { case SS_DATE: default: sort_procedure = (sel_mode == SM_THREAD? threadorder_date : subjorder_date); break; case SS_STRING: sort_procedure = (sel_mode == SM_THREAD? threadorder_subject : subjorder_subject); break; case SS_COUNT: sort_procedure = (sel_mode == SM_THREAD? threadorder_count : subjorder_count); break; case SS_LINES: sort_procedure = (sel_mode == SM_THREAD? threadorder_lines : subjorder_lines); break; #ifdef SCORE /* if SCORE is undefined, use the default above */ case SS_SCORE: sort_procedure = (sel_mode == SM_THREAD? threadorder_score : subjorder_score); break; #endif } subj_list = (SUBJECT**)safemalloc(subject_count * sizeof (SUBJECT*)); for (lp = subj_list, sp = first_subject; sp; sp = sp->next) *lp++ = sp; assert(lp - subj_list == subject_count); qsort(subj_list, subject_count, sizeof (SUBJECT*), sort_procedure); first_subject = sp = subj_list[0]; sp->prev = NULL; for (i = subject_count, lp = subj_list; --i; lp++) { lp[0]->next = lp[1]; lp[1]->prev = lp[0]; if (sel_mode == SM_THREAD) { if (lp[0]->thread && lp[0]->thread == lp[1]->thread) lp[0]->thread_link = lp[1]; else { lp[0]->thread_link = sp; sp = lp[1]; } } } last_subject = lp[0]; last_subject->next = NULL; if (sel_mode == SM_THREAD) last_subject->thread_link = sp; free((char*)subj_list); } static int artorder_date(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { long eq = (*art1)->date - (*art2)->date; return eq? eq > 0? sel_direction : -sel_direction : 0; } static int artorder_subject(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { if ((*art1)->subj == (*art2)->subj) return artorder_date(art1, art2); return strcaseCMP((*art1)->subj->str + 4, (*art2)->subj->str + 4) * sel_direction; } static int artorder_author(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { int eq = strcaseCMP((*art1)->from, (*art2)->from); return eq? eq * sel_direction : artorder_date(art1, art2); } static int artorder_number(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { ART_NUM eq = article_num(*art1) - article_num(*art2); return eq > 0? sel_direction : -sel_direction; } static int artorder_groups(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { long eq; #ifdef DEBUG assert((*art1)->subj != NULL); assert((*art2)->subj != NULL); #endif if ((*art1)->subj == (*art2)->subj) return artorder_date(art1, art2); eq = (*art1)->subj->date - (*art2)->subj->date; return eq? eq > 0? sel_direction : -sel_direction : 0; } static int artorder_lines(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { long eq = (*art1)->lines - (*art2)->lines; return eq? eq > 0? sel_direction : -sel_direction : artorder_date(art1,art2); } #ifdef SCORE static int artorder_score(art1, art2) register ARTICLE** art1; register ARTICLE** art2; { int eq = sc_score_art(article_num(*art2),FALSE) - sc_score_art(article_num(*art1),FALSE); return eq? eq > 0? sel_direction : -sel_direction : 0; } #endif /* Sort the articles according to the chosen order. */ void sort_articles() { int (*sort_procedure)(); build_artptrs(); /* If we don't have at least two articles, we're done! */ if (artptr_list_size < 2) return; switch (sel_sort) { case SS_DATE: default: sort_procedure = artorder_date; break; case SS_STRING: sort_procedure = artorder_subject; break; case SS_AUTHOR: sort_procedure = artorder_author; break; case SS_NATURAL: sort_procedure = artorder_number; break; case SS_GROUPS: sort_procedure = artorder_groups; break; case SS_LINES: sort_procedure = artorder_lines; break; #ifdef SCORE case SS_SCORE: sort_procedure = artorder_score; break; #endif } sel_page_app = 0; qsort(artptr_list, artptr_list_size, sizeof (ARTICLE*), sort_procedure); } static void build_artptrs() { ARTICLE** app; ARTICLE* ap; ART_NUM an; ART_NUM count = obj_count; int desired_flags = (sel_rereading? AF_EXISTS : (AF_EXISTS|AF_UNREAD)); if (!artptr_list || artptr_list_size != count) { artptr_list = (ARTICLE**)saferealloc((char*)artptr_list, (MEM_SIZE)count * sizeof (ARTICLE*)); artptr_list_size = count; } app = artptr_list; for (an = article_first(absfirst); count; an = article_next(an)) { ap = article_ptr(an); if ((ap->flags & (AF_EXISTS|AF_UNREAD)) == desired_flags) { *app++ = ap; count--; } } } trn-4.0-test77/rthread.h0000644000000000000000000000416307113133016013612 0ustar rootroot/* rthread.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT ART_NUM obj_count INIT(0); EXT int subject_count INIT(0); EXT bool output_chase_phrase; EXT HASHTABLE* msgid_hash INIT(NULL); /* Values to pass to count_subjects() */ #define CS_RETAIN 0 #define CS_NORM 1 #define CS_RESELECT 2 #define CS_UNSELECT 3 #define CS_UNSEL_STORE 4 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void thread_init _((void)); void thread_open _((void)); void thread_grow _((void)); void thread_close _((void)); void top_article _((void)); ARTICLE* first_art _((SUBJECT*)); ARTICLE* last_art _((SUBJECT*)); void inc_art _((bool_int,bool_int)); void dec_art _((bool_int,bool_int)); ARTICLE* bump_art _((ARTICLE*)); ARTICLE* next_art _((ARTICLE*)); ARTICLE* prev_art _((ARTICLE*)); bool next_art_with_subj _((void)); bool prev_art_with_subj _((void)); SUBJECT* next_subj _((SUBJECT*,int)); SUBJECT* prev_subj _((SUBJECT*,int)); void select_article _((ARTICLE*,int)); void select_arts_subject _((ARTICLE*,int)); void select_subject _((SUBJECT*,int)); void select_arts_thread _((ARTICLE*,int)); void select_thread _((ARTICLE*,int)); void select_subthread _((ARTICLE*,int)); void deselect_article _((ARTICLE*,int)); void deselect_arts_subject _((ARTICLE*)); void deselect_subject _((SUBJECT*)); void deselect_arts_thread _((ARTICLE*)); void deselect_thread _((ARTICLE*)); void deselect_all _((void)); void kill_arts_subject _((ARTICLE*,int)); void kill_subject _((SUBJECT*,int)); void kill_arts_thread _((ARTICLE*,int)); void kill_thread _((ARTICLE*,int)); void kill_subthread _((ARTICLE*,int)); void unkill_subject _((SUBJECT*)); void unkill_thread _((ARTICLE*)); void unkill_subthread _((ARTICLE*)); void clear_subject _((SUBJECT*)); void clear_thread _((ARTICLE*)); void clear_subthread _((ARTICLE*)); ARTICLE* subj_art _((SUBJECT*)); void visit_next_thread _((void)); void visit_prev_thread _((void)); bool find_parent _((bool_int)); bool find_leaf _((bool_int)); bool find_next_sib _((void)); bool find_prev_sib _((void)); void count_subjects _((int)); void sort_subjects _((void)); void sort_articles _((void)); trn-4.0-test77/rthread.ih0000644000000000000000000000254407113133016013764 0ustar rootroot/* rthread.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int cleanup_msgid_hash _((int,HASHDATUM*,int)); static ARTICLE* first_sib _((ARTICLE*,int)); static ARTICLE* last_sib _((ARTICLE*,int,ARTICLE*)); static int subjorder_subject _((SUBJECT**,SUBJECT**)); static int subjorder_date _((SUBJECT**,SUBJECT**)); static int subjorder_count _((SUBJECT**,SUBJECT**)); static int subjorder_lines _((SUBJECT**,SUBJECT**)); static int subject_score_high _((SUBJECT*)); static int subjorder_score _((SUBJECT**,SUBJECT**)); static int threadorder_subject _((SUBJECT**,SUBJECT**)); static int threadorder_date _((SUBJECT**,SUBJECT**)); static int threadorder_count _((SUBJECT**,SUBJECT**)); static int threadorder_lines _((SUBJECT**,SUBJECT**)); #ifdef SCORE static int thread_score_high _((SUBJECT*)); #endif static int threadorder_score _((SUBJECT**,SUBJECT**)); static int artorder_date _((ARTICLE**,ARTICLE**)); static int artorder_subject _((ARTICLE**,ARTICLE**)); static int artorder_author _((ARTICLE**,ARTICLE**)); static int artorder_number _((ARTICLE**,ARTICLE**)); static int artorder_groups _((ARTICLE**,ARTICLE**)); static int artorder_lines _((ARTICLE**,ARTICLE**)); #ifdef SCORE static int artorder_score _((ARTICLE**,ARTICLE**)); #endif static void build_artptrs _((void)); trn-4.0-test77/sacmd.c0000644000000000000000000004530307113133016013244 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sacmd.c * * main command loop */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "list.h" #include "hash.h" #include "cache.h" /* absfirst, lastart, oneless_artnum() */ #include "bits.h" #include "decode.h" #include "term.h" /* for LINES */ #include "head.h" #include "help.h" /* online help */ #include "ngdata.h" /* for ThreadedGroup */ #include "ng.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "ngstuff.h" #include "respond.h" #include "intrp.h" #include "scan.h" #include "scmd.h" #include "smisc.h" /* needed? */ #include "sorder.h" #include "spage.h" #include "sdisp.h" #include "scanart.h" #include "samain.h" #include "samisc.h" #include "sadisp.h" #include "sadesc.h" #include "sathread.h" #ifdef SCORE #include "score.h" #endif #include "util.h" #include "util2.h" #include "INTERN.h" #include "sacmd.h" bool sa_extract_start(); /* use this command on an extracted file */ /*$$static char* sa_extracted_use = NULL;*/ static char* sa_extract_dest = NULL; /* junk articles after extracting them */ static bool sa_extract_junk = FALSE; /* several basic commands are already done by s_docmd (Scan level) */ /* interprets command in buf, returning 0 to continue looping, * a condition code (negative #s) or an art# to read. Also responsible * for setting refresh flags if necessary. */ int sa_docmd() { long a; /* article pointed to */ long b; /* misc. artnum */ int i; /* for misc. purposes */ /*$$bool flag;*/ /* misc */ ART_NUM artnum; a = (long)page_ents[s_ptr_page_line].entnum; artnum = sa_ents[a].artnum; switch(*buf) { case '+': /* enter thread selector */ if (!ThreadedGroup) { s_beep(); return 0; } buf[0] = '+'; /* fake up command for return */ buf[1] = '\0'; sa_art = artnum; /* give it somewhere to point */ s_save_context(); /* for possible later changes */ return SA_FAKE; /* fake up the command. */ #ifdef SCORE case 'K': /* kill below a threshold */ *buf = ' '; /* for finish_cmd() */ if (!s_finish_cmd("Kill below or equal score:")) break; /* make **sure** that there is a number here */ i = atoi(buf+1); if (i == 0) { /* it might not be a number */ char* s; s = buf+1; if (*s != '0' && ((*s != '+' && *s != '-') || s[1] != '0')) { /* text was not a numeric 0 */ s_beep(); return 0; /* beep but do nothing */ } } sc_kill_threshold(i); s_refill = TRUE; s_ref_top = TRUE; /* refresh # of articles */ break; #endif /* SCORE */ case 'D': /* kill unmarked "on" page */ for (i = 0; i <= s_bot_ent; i++) /* This is a difficult decision, with no obviously good behavior. */ /* Do not kill threads with the first article marked, as it is probably * not what the user wanted. */ if (!sa_marked(page_ents[i].entnum) || !sa_mode_fold) (void)sa_art_cmd(sa_mode_fold,SA_KILL_UNMARKED, page_ents[i].entnum); /* consider: should it start reading? */ b = sa_readmarked_elig(); if (b) { sa_clearmark(b); return b; } s_ref_top = TRUE; /* refresh # of articles */ s_refill = TRUE; break; case 'J': /* kill marked "on" page */ /* If in "fold" mode, kill threads with the first article marked */ if (sa_mode_fold) { for (i = 0; i <= s_bot_ent; i++) { if (sa_marked(page_ents[i].entnum)) (void)sa_art_cmd(TRUE,SA_KILL,page_ents[i].entnum); } } else for (i = 0; i <= s_bot_ent; i++) (void)sa_art_cmd(FALSE,SA_KILL_MARKED,page_ents[i].entnum); s_refill = TRUE; s_ref_top = TRUE; /* refresh # of articles */ break; case 'X': /* kill unmarked (basic-eligible) in group */ *buf = '?'; /* for finish_cmd() */ if (!s_finish_cmd("Junk all unmarked articles")) break; if ((buf[1] != 'Y') && (buf[1] != 'y')) break; i = s_first(); if (!i) break; if (!sa_basic_elig(i)) while ((i = s_next(i)) != 0 && !sa_basic_elig(i)) ; /* New action: if in fold mode, will not delete an article which has a thread-prior marked article. */ for ( ; i; i = s_next(i)) { if (!sa_basic_elig(i)) continue; if (!sa_marked(i) && !was_read(sa_ents[i].artnum)) { if (sa_mode_fold) { /* new semantics */ long j; /* make j == 0 if no prior marked articles in the thread. */ for (j = i; j; j = sa_subj_thread_prev(j)) if (sa_marked(j)) break; if (j) /* there was a marked article */ continue; /* article selection loop */ } oneless_artnum(sa_ents[i].artnum); } } b = sa_readmarked_elig(); if (b) { sa_clearmark(b); return b; } s_refill = TRUE; s_ref_top = TRUE; /* refresh # of articles */ break; case 'c': /* catchup */ s_go_bot(); ask_catchup(); s_refill = TRUE; s_ref_all = TRUE; break; #ifdef SCORE case 'o': /* toggle between score and arrival orders */ s_rub_ptr(); if (sa_mode_order==1) sa_mode_order = 2; else sa_mode_order = 1; if (sa_mode_order == 2 && sc_delay) { sc_delay = FALSE; sc_init(TRUE); } if (sa_mode_order == 2 && !sc_initialized) /* score order */ sa_mode_order = 1; /* nope... (maybe allow later) */ /* if we go into score mode, make sure score is displayed */ if (sa_mode_order == 2 && !sa_mode_desc_score) sa_mode_desc_score = TRUE; s_sort(); s_go_top_ents(); s_refill = TRUE; s_ref_bot = TRUE; break; case 'O': /* change article sorting order */ if (sa_mode_order != 2) { /* not in score order */ s_beep(); break; } score_newfirst = !score_newfirst; s_sort(); s_go_top_ents(); s_refill = TRUE; break; case 'R': /* rescore articles */ if (!sc_initialized) break; /* clear to end of screen */ clear_rest(); s_ref_all = TRUE; /* refresh everything */ printf("\nRescoring articles...\n") FLUSH; sc_rescore(); s_sort(); s_go_top_ents(); s_refill = TRUE; eat_typeahead(); /* stay in control. */ break; case Ctl('e'): /* edit scorefile for group */ /* clear to end of screen */ clear_rest(); s_ref_all = TRUE; /* refresh everything */ sc_score_cmd("e"); /* edit scorefile */ eat_typeahead(); /* stay in control. */ break; #endif /* SCORE */ case '\t': /* TAB: toggle threadcount display */ sa_mode_desc_threadcount = !sa_mode_desc_threadcount; s_ref_desc = 0; break; case 'a': /* toggle author display */ sa_mode_desc_author = !sa_mode_desc_author; s_ref_desc = 0; break; case '%': /* toggle article # display */ sa_mode_desc_artnum = !sa_mode_desc_artnum; s_ref_desc = 0; break; case 'U': /* toggle unread/unread+read mode */ sa_mode_read_elig = !sa_mode_read_elig; /* maybe later use the flag to not do this more than once per newsgroup */ for (i = 1; i < sa_num_ents; i++) s_order_add(i); /* duplicates ignored */ if (sa_eligible(s_first()) || s_next_elig(s_first())) { #ifdef SCORE #ifdef PENDING if (sa_mode_read_elig) { sc_fill_read = TRUE; sc_fill_max = absfirst - 1; } if (!sa_mode_read_elig) sc_fill_read = FALSE; #endif #endif s_ref_top = TRUE; s_rub_ptr(); s_go_top_ents(); s_refill = TRUE; } else /* quit out: no articles */ return SA_QUIT; break; case 'F': /* Fold */ sa_mode_fold = !sa_mode_fold; s_refill = TRUE; s_ref_top = TRUE; break; case 'f': /* follow */ sa_follow = !sa_follow; s_ref_top = TRUE; break; case 'Z': /* Zero (wipe) selections... */ for (i = 1; i < sa_num_ents; i++) sa_ents[i].sa_flags = (sa_ents[i].sa_flags & 0xfd); s_ref_status = 0; if (!sa_mode_zoom) break; s_ref_all = TRUE; /* otherwise won't be refreshed */ /* if in zoom mode, turn it off... */ /* FALL THROUGH */ case 'z': /* zoom mode toggle */ sa_mode_zoom = !sa_mode_zoom; if (sa_unzoomrefold && !sa_mode_zoom) sa_mode_fold = TRUE; /* toggle mode again if no elibible articles left */ if (sa_eligible(s_first()) || s_next_elig(s_first())) { s_ref_top = TRUE; s_go_top_ents(); s_refill = TRUE; } else { s_beep(); sa_mode_zoom = !sa_mode_zoom; /* toggle it right back */ } break; case 'j': /* junk just this article */ (void)sa_art_cmd(FALSE,SA_KILL,a); /* later refill the page */ s_refill = TRUE; s_ref_top = TRUE; /* refresh # of articles */ break; case 'n': /* next art */ case ']': s_rub_ptr(); if (s_ptr_page_line<(s_bot_ent)) /* more on page... */ s_ptr_page_line +=1; else { if (!s_next_elig(page_ents[s_bot_ent].entnum)) { s_beep(); return 0; } s_go_next_page(); /* will jump to top too... */ } break; case 'k': /* kill subject thread */ case ',': /* might later clone TRN ',' */ (void)sa_art_cmd(TRUE,SA_KILL,a); s_refill = TRUE; s_ref_top = TRUE; /* refresh # of articles */ break; case Ctl('n'): /* follow subject forward */ i = sa_subj_thread(a); for (b = s_next_elig(a); b; b = s_next_elig(b)) if (i == sa_subj_thread(b)) break; if (!b) { /* no next w/ same subject */ s_beep(); return 0; } for (i = s_ptr_page_line+1; i <= s_bot_ent; i++) if (page_ents[i].entnum == b) { /* art is on same page */ s_rub_ptr(); s_ptr_page_line = i; return 0; } /* article is not on page... */ (void)s_fillpage_forward(b); /* fill forward (*must* work) */ s_ptr_page_line = 0; break; case Ctl('a'): /* next article with same author... */ /* good for scoring */ b = sa_wrap_next_author(a); /* rest of code copied from next-subject case above */ if (!b || (a == b)) { /* no next w/ same subject */ s_beep(); return 0; } for (i = 0; i <= s_bot_ent; i++) if (page_ents[i].entnum == b) { /* art is on same page */ s_rub_ptr(); s_ptr_page_line = i; return 0; } /* article is not on page... */ (void)s_fillpage_forward(b); /* fill forward (*must* work) */ s_ptr_page_line = 0; break; case Ctl('p'): /* follow subject backwards */ i = sa_subj_thread(a); for (b = s_prev_elig(a); b; b = s_prev_elig(b)) if (i == sa_subj_thread(b)) break; if (!b) { /* no next w/ same subject */ s_beep(); return 0; } for (i = s_ptr_page_line-1; i >= 0; i--) if (page_ents[i].entnum == b) { /* art is on same page */ s_rub_ptr(); s_ptr_page_line = i; return 0; } /* article is not on page... */ (void)s_fillpage_backward(b); /* fill backward (*must* work) */ s_ptr_page_line = s_bot_ent; /* go to bottom of page */ (void)s_refillpage(); /* make sure page is full */ s_ref_all = TRUE; /* make everything redrawn... */ break; case 'G': /* go to article number */ *buf = ' '; /* for finish_cmd() */ if (!s_finish_cmd("Goto article:")) break; /* make **sure** that there is a number here */ i = atoi(buf+1); if (i == 0) { /* it might not be a number */ char* s; s = buf+1; if (*s != '0' && ((*s != '+' && *s != '-') || s[1] != '0')) { /* text was not a numeric 0 */ s_beep(); return 0; /* beep but do nothing */ } } sa_art = (ART_NUM)i; return SA_READ; /* special code to really return */ case 'N': /* next newsgroup */ return SA_NEXT; case 'P': /* previous newsgroup */ return SA_PRIOR; case '`': return SA_QUIT_SEL; case 'q': /* quit newsgroup */ return SA_QUIT; case 'Q': /* start reading and quit SA mode */ sa_in = FALSE; /* FALL THROUGH */ case '\n': case ' ': b = sa_readmarked_elig(); if (b) { sa_clearmark(b); return b; } /* FALL THROUGH */ case 'r': /* read article... */ /* everything needs to be refreshed... */ s_ref_all = TRUE; return a; case 'm': /* toggle mark on one article */ case 'M': /* toggle mark on thread */ s_rub_ptr(); (void)sa_art_cmd((*buf == 'M'),SA_MARK,a); if (!sa_mark_stay) { /* go to next art on page or top of page if at bottom */ if (s_ptr_page_line < s_bot_ent) /* more on page */ s_ptr_page_line +=1; else s_go_top_page(); /* wrap around to top */ } break; case 's': /* toggle select1 on one article */ case 'S': /* toggle select1 on a thread */ if (!sa_mode_zoom) s_rub_ptr(); (void)sa_art_cmd((*buf == 'S'),SA_SELECT,a); /* if in zoom mode, selection will remove article(s) from the * page, so that moving the cursor down is unnecessary */ if (!sa_mark_stay && !sa_mode_zoom) { /* go to next art on page or top of page if at bottom */ if (s_ptr_page_line<(s_bot_ent)) /* more on page */ s_ptr_page_line +=1; else s_go_top_page(); /* wrap around to top */ } break; case 'e': /* extract marked articles */ #if 0 if (!sa_extract_start()) break; /* aborted */ if (!decode_fp) *decode_dest = '\0'; /* wipe old name */ a = s_first(); if (!s_eligible(a)) a = s_next_elig(a); flag = FALSE; /* have we found a marked one? */ for ( ; a; a = s_next_elig(a)) if (sa_marked(a)) { flag = TRUE; (void)sa_art_cmd(FALSE,SA_EXTRACT,a); } if (!flag) { /* none were marked */ a = page_ents[s_ptr_page_line].entnum; (void)sa_art_cmd(FALSE,SA_EXTRACT,a); } s_refill = TRUE; s_ref_top = TRUE; /* refresh # of articles */ (void)get_anything(); eat_typeahead(); #endif break; #if 0 case 'E': /* end extraction, do command on image */ s_ref_all = TRUE; s_go_bot(); if (decode_fp) { printf("\nIncomplete file: %s\n",decode_dest) FLUSH; printf("Continue with command? [ny]"); fflush(stdout); getcmd(buf); printf("\n") FLUSH; if (*buf == 'n' || *buf == ' ' || *buf == '\n') break; printf("Remove this file? [ny]"); fflush(stdout); getcmd(buf); printf("\n") FLUSH; if (*buf == 'y' || *buf == 'Y') { decode_end(); /* will remove file */ break; } fclose(decode_fp); decode_fp = 0; } if (!sa_extracted_use) { sa_extracted_use = safemalloc(LBUFLEN); /* later consider a variable for the default command */ *sa_extracted_use = '\0'; } if (!*decode_dest) { printf("\nTrn doesn't remember an extracted file name.\n") FLUSH; *buf = ' '; if (!s_finish_cmd("Please enter a file to use:")) break; if (!buf[1]) /* user just typed return */ break; safecpy(decode_dest,buf+1,MAXFILENAME); printf("\n") FLUSH; } if (sa_extract_dest == NULL) { sa_extract_dest = (char*)safemalloc(LBUFLEN); safecpy(sa_extract_dest,filexp("%p"),LBUFLEN); } if (*decode_dest != '/' && *decode_dest != '~' && *decode_dest != '%') { sprintf(buf,"%s/%s",sa_extract_dest,decode_dest); safecpy(decode_dest,buf,MAXFILENAME); } if (*sa_extracted_use) printf("Use command (default %s):\n",sa_extracted_use) FLUSH; else printf("Use command (no default):\n") FLUSH; *buf = ':'; /* cosmetic */ if (!s_finish_cmd(NULL)) break; /* command rubbed out */ if (buf[1] != '\0') /* typed in a command */ safecpy(sa_extracted_use,buf+1,LBUFLEN); if (*sa_extracted_use == '\0') /* no command */ break; sprintf(buf,"!%s %s",sa_extracted_use,decode_dest); printf("\n%s\n",buf+1) FLUSH; (void)escapade(); (void)get_anything(); eat_typeahead(); break; #endif #ifdef SCORE case '"': /* append to local SCORE file */ s_go_bot(); s_ref_all = TRUE; printf("Enter score append command or type RETURN for a menu\n"); buf[0] = ':'; buf[1] = FINISHCMD; if (!finish_command(FALSE)) break; printf("\n") FLUSH; sa_go_art(artnum); sc_append(buf+1); (void)get_anything(); eat_typeahead(); break; case '\'': /* execute scoring command */ s_go_bot(); s_ref_all = TRUE; printf("\nEnter scoring command or type RETURN for a menu\n"); buf[0] = ':'; buf[1] = FINISHCMD; if (!finish_command(FALSE)) break; printf("\n") FLUSH; sa_go_art(artnum); sc_score_cmd(buf+1); s_ref_all = TRUE; (void)get_anything(); eat_typeahead(); break; #endif default: s_beep(); return 0; } /* switch */ return 0; } bool sa_extract_start() { if (sa_extract_dest == NULL) { sa_extract_dest = (char*)safemalloc(LBUFLEN); safecpy(sa_extract_dest,filexp("%p"),LBUFLEN); } s_go_bot(); printf("To directory (default %s)\n",sa_extract_dest) FLUSH; *buf = ':'; /* cosmetic */ if (!s_finish_cmd(NULL)) return FALSE; /* command rubbed out */ s_ref_all = TRUE; /* if the user typed something, copy it to the destination */ if (buf[1] != '\0') safecpy(sa_extract_dest,filexp(buf+1),LBUFLEN); /* set a mode for this later? */ printf("\nMark extracted articles as read? [yn]"); fflush(stdout); getcmd(buf); printf("\n") FLUSH; if (*buf == 'y' || *buf == ' ' || *buf == '\n') sa_extract_junk = TRUE; else sa_extract_junk = FALSE; return TRUE; } /* sa_art_cmd primitive: actually does work on an article */ void sa_art_cmd_prim(cmd,a) int cmd; long a; { ART_NUM artnum; artnum = sa_ents[a].artnum; /* do more onpage status refreshes when in unread+read mode? */ switch (cmd) { case SA_KILL_MARKED: if (sa_marked(a)) { sa_clearmark(a); oneless_artnum(artnum); } break; case SA_KILL_UNMARKED: if (sa_marked(a)) break; /* end case early */ oneless_artnum(artnum); break; case SA_KILL: /* junk this article */ sa_clearmark(a); /* clearing should be fast */ oneless_artnum(artnum); break; case SA_MARK: /* mark this article */ if (sa_marked(a)) sa_clearmark(a); else sa_mark(a); s_ref_status_onpage(a); break; case SA_SELECT: /* select this article */ if (sa_selected1(a)) { sa_clearselect1(a); if (sa_mode_zoom) s_refill = TRUE; /* article is now ineligible */ } else sa_select1(a); s_ref_status_onpage(a); break; case SA_EXTRACT: sa_clearmark(a); art = artnum; *buf = 'e'; /* fake up the extract command */ safecpy(buf+1,sa_extract_dest,LBUFLEN); (void)save_article(); if (sa_extract_junk) oneless_artnum(artnum); break; } /* switch */ } /* return value is unused for now, but may be later... */ /* note: refilling after a kill is the caller's responsibility */ int sa_art_cmd(multiple,cmd,a) int multiple; /* follow the thread? */ int cmd; /* what to do */ long a; /* article # to affect or start with */ { long b; sa_art_cmd_prim(cmd,a); /* do the first article */ if (!multiple) return 0; /* no problem... */ b = a; while ((b = sa_subj_thread_next(b)) != 0) /* if this is basically eligible and the same subject thread# */ sa_art_cmd_prim(cmd,b); return 0; } /* XXX this needs a good long thinking session before re-activating */ long sa_wrap_next_author(a) long a; { #ifdef UNDEF long b; char* s; char* s2; s = (char*)sa_desc_author(a,20); /* 20 characters should be enough */ for (b = s_next_elig(a); b; b = s_next_elig(b)) if (STRSTR(get_from_line(b),s)) break; /* out of the for loop */ if (b) /* found it */ return b; /* search from first article (maybe return original art) */ b = s_first(); if (!sa_eligible(b)) b = s_next_elig(b); for ( ; b; b = s_next_elig(b)) if (STRSTR(get_from_line(b),s)) break; /* out of the for loop */ return b; #else return a; /* feature is disabled */ #endif } #endif /* SCAN */ trn-4.0-test77/sacmd.h0000644000000000000000000000070107113133016013242 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sacmd.h * * main command loop */ #define SA_KILL 1 #define SA_MARK 2 #define SA_SELECT 3 #define SA_KILL_UNMARKED 4 #define SA_KILL_MARKED 5 #define SA_EXTRACT 6 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int sa_docmd _((void)); bool sa_extract_start _((void)); void sa_art_cmd_prim _((int,long)); int sa_art_cmd _((int,int,long)); long sa_wrap_next_author _((long)); trn-4.0-test77/sadesc.c0000644000000000000000000001366511437640112013431 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sadesc.c * */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "list.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "head.h" /* currently used for fast author fetch when group is threaded */ #include "ngdata.h" #include "term.h" /* for standout */ #include "rthread.h" #include "rt-util.h" /* compress_from() */ #include "scanart.h" #include "samain.h" #include "sacmd.h" /* for sa_wrap_next_author() */ #include "sadisp.h" #include "sathread.h" #include "scan.h" #ifdef SCORE #include "score.h" #endif #include "INTERN.h" #include "sadesc.h" /* returns statchars in temp space... */ char* sa_get_statchars(a,line) long a; int line; /* which status line (1 = first) */ { static char char_buf[16]; /* Debug */ #if 0 printf("entry: sa_get_statchars(%d,%d)\n",(int)a,line) FLUSH; #endif #if 0 /* old 5-column status */ switch (line) { case 1: strcpy(char_buf,"....."); if (sa_marked(a)) char_buf[4] = 'x'; if (sa_selected1(a)) char_buf[3] = '+'; if (was_read(sa_ents[a].artnum)) char_buf[0] = '-'; else char_buf[0] = '+'; break; default: strcpy(char_buf," "); break; } /* switch */ #else switch (line) { case 1: strcpy(char_buf,"..."); if (sa_marked(a)) char_buf[2] = 'x'; if (sa_selected1(a)) char_buf[1] = '*'; if (was_read(sa_ents[a].artnum)) char_buf[0] = '-'; else char_buf[0] = '+'; break; default: strcpy(char_buf," "); break; } /* switch */ #endif return char_buf; } char* sa_desc_subject(e) long e; { char* s; char* s1; static char sa_subj_buf[256]; /* fetchlines saves its arguments */ s = fetchlines(sa_ents[e].artnum,SUBJ_LINE); if (!s || !*s) { if (s) free(s); sprintf(sa_subj_buf,"(no subject)"); return sa_subj_buf; } strncpy(sa_subj_buf,s,250); free(s); s1 = sa_subj_buf; if (*s1 == 'r' || *s1 == 'R') { if (*++s1 == 'e' || *s1 == 'E') { if (*++s1 ==':') { *s1 = '>'; /* more cosmetic "Re:" */ return s1; } } } return sa_subj_buf; } /* NOTE: should redesign later for the "menu" style... */ char* sa_get_desc(e,line,trunc) long e; /* entry number */ int line; bool_int trunc; /* should it be truncated? */ { static char desc_buf[1024]; char* s; bool use_standout; /* if TRUE, use stdout on line */ ART_NUM artnum; artnum = sa_ents[e].artnum; use_standout = FALSE; switch (line) { case 1: desc_buf[0] = '\0'; /* initialize the buffer */ if (sa_mode_desc_artnum) { sprintf(sa_buf,"%6d ",(int)artnum); strcat(desc_buf,sa_buf); } #ifdef SCORE if (sc_initialized && sa_mode_desc_score) { /* we'd like the score now */ sprintf(sa_buf,"[%4d] ",sc_score_art(artnum,TRUE)); strcat(desc_buf,sa_buf); } #endif /* SCORE */ if (sa_mode_desc_threadcount) { sprintf(sa_buf,"(%3d) ",sa_subj_thread_count(e)); strcat(desc_buf,sa_buf); } if (sa_mode_desc_author) { #if 0 if (trunc) sprintf(sa_buf,"%s ",padspaces(sa_desc_author(e,16),16)); else sprintf(sa_buf,"%s ",sa_desc_author(e,40)); strcat(desc_buf,sa_buf); #endif if (trunc) strcat(desc_buf,compress_from(article_ptr(artnum)->from,16)); else strcat(desc_buf,compress_from(article_ptr(artnum)->from,200)); strcat(desc_buf," "); } if (sa_mode_desc_subject) { sprintf(sa_buf,"%s",sa_desc_subject(e)); strcat(desc_buf,sa_buf); } break; case 2: /* summary line (test) */ s = fetchlines(artnum,SUMRY_LINE); if (s && *s) { /* we really have one */ int i; /* number of spaces to indent */ char* s2; /* for indenting */ /* include the following line to use standout mode */ #if 0 use_standout = TRUE; #endif i = 0; /* if variable widths used later, use them */ if (sa_mode_desc_artnum) i += 7; #ifdef SCORE if (sc_initialized && sa_mode_desc_score) i += 7; #endif if (sa_mode_desc_threadcount) i += 6; s2 = desc_buf; while (i--) *s2++ = ' '; #ifdef HAS_TERMLIB if (use_standout) sprintf(s2,"Summary: %s%s",tc_SO,s); else #endif sprintf(s2,"Summary: %s",s); break; } /* otherwise, we might have had a keyword */ /* FALL THROUGH */ case 3: /* Keywords (test) */ s = fetchlines(artnum,KEYW_LINE); if (s && *s) { /* we really have one */ int i; /* number of spaces to indent */ char* s2; /* for indenting */ /* include the following line to use standout mode */ #if 0 use_standout = TRUE; #endif i = 0; /* if variable widths used later, use them */ if (sa_mode_desc_artnum) i += 7; #ifdef SCORE if (sc_initialized && sa_mode_desc_score) i += 7; #endif if (sa_mode_desc_threadcount) i += 6; s2 = desc_buf; while (i--) *s2++ = ' '; #ifdef HAS_TERMLIB if (use_standout) sprintf(s2,"Keys: %s%s",tc_SO,s); else #endif sprintf(s2,"Keys: %s",s); break; } /* FALL THROUGH */ default: /* no line I know of */ /* later return NULL */ sprintf(desc_buf,"Entry %ld: Nonimplemented Description LINE",e); break; } /* switch (line) */ if (trunc) desc_buf[s_desc_cols] = '\0'; /* make sure it's not too long */ #ifdef HAS_TERMLIB if (use_standout) strcat(desc_buf,tc_SE); /* end standout mode */ #endif /* take out bad characters (replace with one space) */ for (s = desc_buf; *s; s++) switch (*s) { case Ctl('h'): case '\t': case '\n': case '\r': *s = ' '; } return desc_buf; } /* returns # of lines the article occupies in total... */ int sa_ent_lines(e) long e; /* the entry number */ { char* s; ART_NUM artnum; int num = 1; artnum = sa_ents[e].artnum; if (sa_mode_desc_summary) { s = fetchlines(artnum,SUMRY_LINE); if (s && *s) num++; /* just a test */ if (s) free(s); } if (sa_mode_desc_keyw) { s = fetchlines(artnum,KEYW_LINE); if (s && *s) num++; /* just a test */ if (s) free(s); } return num; } #endif /* SCAN */ trn-4.0-test77/sadesc.h0000644000000000000000000000043207113133016013416 0ustar rootroot/* This file Copyright 1992,1995 by Clifford A. Adams */ /* sadesc.h * */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ char* sa_get_statchars _((long,int)); char* sa_desc_subject _((long)); char* sa_get_desc _((long,int,bool_int)); int sa_ent_lines _((long)); trn-4.0-test77/sadisp.c0000644000000000000000000000400407113133016013431 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sadisp.c * * display stuff */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "hash.h" #include "cache.h" /* for absfirst declaration */ #include "bits.h" /* for mailcall */ #include "ng.h" /* for ngname */ #include "trn.h" #include "term.h" #include "util.h" #include "scanart.h" #include "samain.h" #include "sadesc.h" #include "samisc.h" #include "sathread.h" /* for fold mode flag */ #ifdef SCORE #include "score.h" #endif #include "scan.h" #include "sdisp.h" #include "color.h" #include "INTERN.h" #include "sadisp.h" void sa_refresh_top() { color_object(COLOR_SCORE, 1); printf("%s |",ngname); /* # of articles might be optional later */ printf(" %d",sa_number_arts()); if (sa_mode_read_elig) printf(" unread+read"); else printf(" unread"); if (sa_mode_zoom) printf(" zoom"); if (sa_mode_fold) printf(" Fold"); if (sa_follow) printf(" follow"); color_pop(); /* of COLOR_SCORE */ erase_eol(); printf("\n") FLUSH; } void sa_refresh_bot() { char* s; color_object(COLOR_SCORE, 1); s_mail_and_place(); printf("("); switch (sa_mode_order) { case 1: s = "arrival"; break; #ifdef SCORE case 2: if (score_newfirst) s = "score (new>old)"; else s = "score (old>new)"; break; #endif default: s = "unknown"; break; } printf("%s order",s); #ifdef SCORE printf(", %d%% scored",sc_percent_scored()); #endif printf(")"); color_pop(); /* of COLOR_SCORE */ fflush(stdout); } /* set up various screen dimensions */ void sa_set_screen() { /* One size fits all for now. */ /* these things here because they may vary by screen size later */ s_top_lines = 1; s_bot_lines = 1; s_status_cols = 3; s_cursor_cols = 2; if (s_itemnum) s_itemnum_cols = 3; else s_itemnum_cols = 0; /* (scr_width-1) keeps last character blank. */ s_desc_cols = (scr_width-1) -s_status_cols -s_cursor_cols -s_itemnum_cols; } #endif /* SCAN */ trn-4.0-test77/sadisp.h0000644000000000000000000000041707113133016013442 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sadisp.h * * article scan specific display functions */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sa_refresh_top _((void)); void sa_refresh_bot _((void)); void sa_set_screen _((void)); trn-4.0-test77/samain.c0000644000000000000000000001230707113133016013423 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* samain.c * * main working routines (may change later) */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "list.h" #include "hash.h" #include "cache.h" #include "ngdata.h" #include "bits.h" #include "head.h" /* for fetching misc header lines... */ #include "util.h" #ifdef SCORE #include "score.h" #endif #include "scan.h" #include "scmd.h" #include "sdisp.h" #include "smisc.h" /* needed? */ #include "sorder.h" #include "spage.h" #include "scanart.h" #include "samisc.h" #include "sacmd.h" #include "sadesc.h" #include "sadisp.h" #include "sathread.h" #include "INTERN.h" #include "samain.h" void sa_init() { sa_init_context(); if (lastart == 0 || absfirst > lastart) return; /* no articles */ if (s_initscreen()) /* If not able to init screen...*/ return; /* ...most likely dumb terminal */ sa_initmode(); /* mode differences */ sa_init_threads(); sa_mode_read_elig = FALSE; if (firstart > lastart) /* none unread */ sa_mode_read_elig = TRUE; /* unread+read in some situations */ if (!sa_initarts()) /* init article array(s) */ return; /* ... no articles */ #ifdef SCORE #ifdef PENDING if (sa_mode_read_elig) { sc_fill_read = TRUE; sc_fill_max = absfirst - 1; } #endif #endif s_save_context(); sa_initialized = TRUE; /* all went well... */ } void sa_init_ents() { sa_num_ents = sa_ents_alloc = 0; sa_ents = (SA_ENTRYDATA*)NULL; } void sa_clean_ents() { free(sa_ents); } /* returns entry number that was added */ long sa_add_ent(artnum) ART_NUM artnum; /* article number to be added */ { long cur; sa_num_ents++; if (sa_num_ents > sa_ents_alloc) { sa_ents_alloc += 100; if (sa_ents_alloc == 100) { /* newly allocated */ /* don't use number 0, just allocate it and skip it */ sa_num_ents = 2; sa_ents = (SA_ENTRYDATA*)safemalloc(sa_ents_alloc * sizeof (SA_ENTRYDATA)); } else { sa_ents = (SA_ENTRYDATA*)saferealloc((char*)sa_ents, sa_ents_alloc * sizeof (SA_ENTRYDATA)); } } cur = sa_num_ents-1; sa_ents[cur].artnum = artnum; sa_ents[cur].subj_thread_num = 0; sa_ents[cur].sa_flags = (char)0; s_order_add(cur); return cur; } void sa_cleanmain() { sa_clean_ents(); sa_mode_zoom = FALSE; /* doesn't survive across groups */ /* remove the now-unused scan-context */ s_delete_context(sa_scan_context); sa_context_init = FALSE; sa_scan_context = -1; /* no longer "in" article scan mode */ sa_mode_read_elig = FALSE; /* the default */ sa_in = FALSE; } void sa_growarts(oldlast,last) long oldlast,last; { int i; for (i = oldlast+1; i <= last; i++) (void)sa_add_ent(i); } /* Initialize the scan-context to enter article scan mode. */ void sa_init_context() { if (sa_context_init) return; /* already initialized */ if (sa_scan_context == -1) sa_scan_context = s_new_context(S_ART); s_change_context(sa_scan_context); } bool sa_initarts() { int a; sa_init_ents(); /* add all available articles to entry list */ for (a = article_first(absfirst); a <= lastart; a = article_next(a)) { if (article_exists(a)) (void)sa_add_ent(a); } sa_order_read = sa_mode_read_elig; return TRUE; } /* note: initscreen must be called before (for scr_width) */ void sa_initmode() { /* set up screen sizes */ sa_set_screen(); sa_mode_zoom = 0; /* reset zoom */ } int sa_mainloop() { int i; #ifdef SCORE /* Eventually, strn will need a variable in score.[ch] set when the * scoring initialization *failed*, so that strn could try to * initialize the scoring again or decide to disallow score operations.) */ /* If strn is in score mode but scoring is not initialized, * then try to initialize. * If that fails then strn will just use arrival ordering. */ if (!sc_initialized && sa_mode_order == 2) { sc_delay = FALSE; /* yes, actually score... */ sc_init(TRUE); /* wait for articles to score */ if (!sc_initialized) sa_mode_order = 1; /* arrival order */ } #endif /* redraw it *all* */ s_ref_all = TRUE; if (s_top_ent < 1) s_top_ent = s_first(); i = s_fillpage(); if (i == -1 || i == 0) { /* for now just quit if no page could be filled. */ return SA_QUIT; } i = s_cmdloop(); if (i == SA_READ) { i = SA_NORM; } if (i > 0) { sa_art = sa_ents[i].artnum; return SA_NORM; } /* something else (quit, return, etc...) */ return i; } /* do something useful until a key is pressed. */ void sa_lookahead() { #ifdef PENDING #ifdef SCORE sc_lookahead(TRUE,FALSE); /* do resorting now... */ #else /* !SCORE */ /* consider looking forward from the last article on the page... */ ; /* so the function isn't empty */ #endif /* SCORE */ #else /* !PENDING */ ; /* so the function isn't empty */ #endif } /* Returns first marked entry number, or 0 if no articles are marked. */ long sa_readmarked_elig() { long e; e = s_first(); if (!e) return 0L; for ( ; e; e = s_next(e)) { if (!sa_basic_elig(e)) continue; if (sa_marked(e)) return e; } /* This is possible since the marked articles might not be eligible. */ return 0; } #endif /* SCAN */ trn-4.0-test77/samain.h0000644000000000000000000000343107113133016013426 0ustar rootroot/* This file Copyright 1992,1993 by Clifford A. Adams */ /* samain.h * * Main routines for article-scan mode. */ /* sa_flags character bitmap: * 0: If set, the article is "marked" (for reading). * 1: If set, the article is "selected" (for zoom mode display). * 2: Secondary selection--not currently used. * 3: If set, the author of this article influenced its score. */ #define sa_mark(a) (sa_ents[a].sa_flags |= 1) #define sa_clearmark(a) (sa_ents[a].sa_flags &= 0xfe) #define sa_marked(a) (sa_ents[a].sa_flags & 1) #define sa_select1(a) (sa_ents[a].sa_flags |= 2) #define sa_clearselect1(a) (sa_ents[a].sa_flags &= 0xfd) #define sa_selected1(a) (sa_ents[a].sa_flags & 2) /* sa_select2 is not currently used */ #define sa_select2(a) (sa_ents[a].sa_flags |= 4) #define sa_clearselect2(a) (sa_ents[a].sa_flags &= 0xfb) #define sa_selected2(a) (sa_ents[a].sa_flags & 4) #define sa_setauthscored(a) (sa_ents[a].sa_flags |= 8) #define sa_clearauthscored(a) (sa_ents[a].sa_flags &= 0xf7) #define sa_authscored(a) (sa_ents[a].sa_flags & 8) /* misc. buffer */ EXT char sa_buf[LBUFLEN]; /* TRUE if in "zoom" (display only selected) mode */ EXT bool sa_mode_zoom INIT(FALSE); /* if TRUE, the already-read articles have been added to the order arrays */ EXT bool sa_order_read INIT(FALSE); /* contains the scan-context number for the current article scan */ EXT int sa_scan_context INIT(-1); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sa_init _((void)); void sa_init_ents _((void)); void sa_clean_ents _((void)); long sa_add_ent _((ART_NUM)); void sa_cleanmain _((void)); void sa_growarts _((long,long)); void sa_init_context _((void)); bool sa_initarts _((void)); void sa_initmode _((void)); int sa_mainloop _((void)); void sa_lookahead _((void)); long sa_readmarked_elig _((void)); trn-4.0-test77/samisc.c0000644000000000000000000001220107113133016013423 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* samisc.c * * lower-level routines */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "list.h" #include "hash.h" #include "cache.h" #include "artio.h" /* openart */ #include "bits.h" #include "final.h" /* assert's sig_catcher() */ #include "term.h" /* for "backspace" */ #include "util.h" #include "scanart.h" #include "samain.h" #include "sathread.h" #include "sorder.h" #include "trn.h" #include "ngdata.h" /* several */ #include "rcstuff.h" #include "ng.h" /* for "art" */ #include "head.h" /* fetchsubj */ #include "rthread.h" #include "rt-select.h" /* sel_mode */ #ifdef SCORE #include "score.h" #endif #include "INTERN.h" #include "samisc.h" #ifdef UNDEF /* use function for breakpoint debugging */ int check_article(a) long a; { if (a < absfirst || a > lastart) { printf("\nArticle %d out of range\n",a) FLUSH; return FALSE; } return TRUE; } #else /* note that argument is used twice. */ #define check_article(a) ((a) >= absfirst && (a) <= lastart) #endif /* ignoring "Fold" (or later recursive) mode(s), is this article eligible? */ bool sa_basic_elig(a) long a; { ART_NUM artnum; artnum = sa_ents[a].artnum; assert(check_article(artnum)); /* "run the gauntlet" style (:-) */ if (!sa_mode_read_elig && was_read(artnum)) return FALSE; if (sa_mode_zoom && !sa_selected1(a)) return FALSE; #ifdef SCORE if (sa_mode_order == 2) /* score order */ if (!SCORED(artnum)) return FALSE; #endif /* now just check availability */ if (is_unavailable(artnum)) { if (!was_read(artnum)) oneless_artnum(artnum); return FALSE; /* don't try positively unavailable */ } /* consider later positively checking availability */ return TRUE; /* passed all tests */ } bool sa_eligible(a) long a; { assert(check_article(sa_ents[a].artnum)); if (!sa_basic_elig(a)) return FALSE; /* must always be basic-eligible */ if (!sa_mode_fold) return TRUE; /* just use basic-eligible */ else { if (sa_subj_thread_prev(a)) return FALSE; /* there was an earlier match */ return TRUE; /* no prior matches */ } } /* given an article number, return the entry number for that article */ /* (There is no easy mapping between entry numbers and article numbers.) */ long sa_artnum_to_ent(artnum) ART_NUM artnum; { long i; for (i = 1; i < sa_num_ents; i++) if (sa_ents[i].artnum == artnum) return i; /* this had better not happen (complain?) */ return -1; } /* select1 the articles picked in the TRN thread selector */ void sa_selthreads() { register SUBJECT *sp; register ARTICLE *ap; bool want_unread; #if 0 /* this does not work now, but maybe it will help debugging? */ int subj_mask = (sel_mode == SM_THREAD? (SF_THREAD|SF_VISIT) : SF_VISIT); #endif int subj_mask = SF_VISIT; long art; long i; want_unread = !sa_mode_read_elig; /* clear any old selections */ for (i = 1; i < sa_num_ents; i++) sa_ents[i].sa_flags = (sa_ents[i].sa_flags & 0xfd); /* Loop through all (selected) articles. */ for (sp = first_subject; sp; sp = sp->next) { if ((sp->flags & subj_mask) == subj_mask) { for (ap = first_art(sp); ap; ap = next_art(ap)) { art = article_num(ap); if ((ap->flags & AF_SEL) && (!(ap->flags & AF_UNREAD) ^ want_unread)) { /* this was a trn-thread selected article */ sa_select1(sa_artnum_to_ent(art)); #ifdef SCORE /* if scoring, make sure that this article is scored... */ if (sa_mode_order == 2) /* score order */ sc_score_art(art,FALSE); #endif } }/* for all articles */ }/* if selected */ }/* for all threads */ s_sort(); } int sa_number_arts() { int total; long i; ART_NUM a; if (sa_mode_read_elig) i = absfirst; else i = firstart; total = 0; for (i = 1; i < sa_num_ents; i++) { a = sa_ents[i].artnum; if (is_unavailable(a)) continue; if (!article_unread(a) && !sa_mode_read_elig) continue; total++; } return total; } /* needed for a couple functions which act within the * scope of an article. */ void sa_go_art(a) long a; { art = a; (void)article_find(art); if (openart != art) artopen(art,(ART_POS)0); } int sa_compare(a,b) long a,b; /* the entry numbers to compare */ { long i,j; #ifdef SCORE if (sa_mode_order == 2) { /* score order */ /* do not score the articles here--move the articles to * the end of the list if unscored. */ if (!SCORED(sa_ents[a].artnum)) { /* a unscored */ if (!SCORED(sa_ents[b].artnum)) { /* a+b unscored */ if (a < b) /* keep ordering consistent */ return -1; return 1; } return 1; /* move unscored (a) to end */ } if (!SCORED(sa_ents[b].artnum)) /* only b unscored */ return -1; /* move unscored (b) to end */ i = sc_score_art(sa_ents[a].artnum,TRUE); j = sc_score_art(sa_ents[b].artnum,TRUE); if (i < j) return 1; if (i > j) return -1; /* i == j */ if (score_newfirst) { if (a < b) return 1; return -1; } else { if (a < b) return -1; return 1; } } #endif if (a < b) return -1; return 1; } #endif /* SCAN */ trn-4.0-test77/samisc.h0000644000000000000000000000064107113133016013435 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* samisc.h * * smaller functions */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef UNDEF int check_article _((long)); #endif bool sa_basic_elig _((long)); bool sa_eligible _((long)); long sa_artnum_to_ent _((ART_NUM)); void sa_selthreads _((void)); int sa_number_arts _((void)); void sa_go_art _((long)); int sa_compare _((long,long)); trn-4.0-test77/sathread.c0000644000000000000000000000500707113133016013745 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sathread.c * */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "list.h" #include "hash.h" #include "cache.h" #include "bits.h" /* for absfirst */ #include "head.h" /* hc_setspin() */ #include "ngdata.h" #include "mempool.h" #include "scanart.h" #include "sadesc.h" /* sa_desc_subject() */ #include "samain.h" #include "samisc.h" #include "sorder.h" #include "util.h" #include "INTERN.h" #include "sathread.h" static long sa_num_threads = 0; static HASHTABLE* sa_thread_hash = 0; void sa_init_threads() { mp_free(MP_SATHREAD); sa_num_threads = 0; if (sa_thread_hash) { hashdestroy(sa_thread_hash); sa_thread_hash = 0; } } /* called only if the macro didn't find a value */ /* XXX: dependent on hash feature that data.dat_len is not used in * the default comparison function, so it can be used for a number. * later: write a custom comparison function. */ long sa_get_subj_thread(e) long e; /* entry number */ { HASHDATUM data; char* s; bool old_untrim; char* p; old_untrim = untrim_cache; untrim_cache = TRUE; s = sa_desc_subject(e); untrim_cache = old_untrim; if (!s || !*s) return -2; if ((*s == '>') && (s[1] == ' ')) s += 2; if (!sa_thread_hash) { sa_thread_hash = hashcreate(401, HASH_DEFCMPFUNC); } data = hashfetch(sa_thread_hash,s,strlen(s)); if (data.dat_ptr) { return (long)(data.dat_len); } p = mp_savestr(s,MP_SATHREAD); data = hashfetch(sa_thread_hash,p,strlen(s)); data.dat_ptr = p; data.dat_len = (unsigned)(sa_num_threads+1); hashstorelast(data); sa_num_threads++; sa_ents[e].subj_thread_num = sa_num_threads; return sa_num_threads; } int sa_subj_thread_count(a) long a; { int count; long b; count = 1; b = a; while ((b = sa_subj_thread_next(b)) != 0) if (sa_basic_elig(b)) count++; return count; } /* returns basic_elig previous subject thread */ long sa_subj_thread_prev(a) long a; { int i,j; i = sa_subj_thread(a); while ((a = s_prev(a)) != 0) { if (!sa_basic_elig(a)) continue; if (!(j = sa_ents[a].subj_thread_num)) j = sa_subj_thread(a); if (i == j) return a; } return 0L; } long sa_subj_thread_next(a) long a; { int i,j; i = sa_subj_thread(a); while ((a = s_next(a)) != 0) { if (!sa_basic_elig(a)) continue; if (!(j = sa_ents[a].subj_thread_num)) j = sa_subj_thread(a); if (i == j) return a; } return 0L; } #endif /* SCAN */ trn-4.0-test77/sathread.h0000644000000000000000000000074707113133016013760 0ustar rootroot/* This file Copyright 1992,1995 by Clifford A. Adams */ /* sathread.h * */ /* this define will save a *lot* of function calls. */ #define sa_subj_thread(e) \ (sa_ents[e].subj_thread_num? sa_ents[e].subj_thread_num : \ sa_get_subj_thread(e)) /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sa_init_threads _((void)); long sa_get_subj_thread _((long)); int sa_subj_thread_count _((long)); long sa_subj_thread_prev _((long)); long sa_subj_thread_next _((long)); trn-4.0-test77/scan.c0000644000000000000000000001126407113133016013100 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* scan.c * * Scan initialization/cleanup and context control. */ /* CLEANUP: make a scontext file for the scan context stuff. */ #include "EXTERN.h" #include "common.h" #ifdef SCAN #include "final.h" /* for assert statements */ #include "util.h" /* allocation */ #include "INTERN.h" #include "scan.h" #include "EXTERN.h" #include "sorder.h" void s_init_context(cnum,type) int cnum,type; { SCONTEXT *p; int i; /* s_num_contexts not incremented until last moment */ if (cnum < 0 || cnum > s_num_contexts) { printf("s_init_context: illegal context number %d!\n",cnum) FLUSH; assert(FALSE); } p = s_contexts + cnum; p->type = type; p->ent_sort = (long*)NULL; p->ent_sort_max = -1; p->ent_sorted_max = -1; p->ent_index = (long*)NULL; p->ent_index_max = -1; p->page_size = MAX_PAGE_SIZE; p->top_ent = -1; p->bot_ent = -1; p->refill = TRUE; p->ref_all = TRUE; p->ref_top = TRUE; p->ref_bot = TRUE; p->ref_status = -1; p->ref_desc = -1; /* next ones should be reset later */ p->top_lines = 0; p->bot_lines = 0; p->status_cols = 0; p->cursor_cols = 0; p->desc_cols = 0; p->itemnum_cols = 0; p->ptr_page_line = 0; p->flags = 0; /* clear the page entries */ for (i = 0; i < MAX_PAGE_SIZE; i++) { p->page_ents[i].entnum = 0; p->page_ents[i].lines = 0; p->page_ents[i].start_line = 0; p->page_ents[i].pageflags = (char)0; } } /* allocate a new context number and initialize it */ int /* context number */ s_new_context(type) int type; /* context type */ { int i; /* check for deleted contexts */ for (i = 0; i < s_num_contexts; i++) if (s_contexts[i].type == 0) /* deleted context */ break; if (i < s_num_contexts) { /* a deleted one was found */ s_init_context(i,type); return i; } /* none deleted, so allocate a new one */ i = s_num_contexts; i++; if (i == 1) { /* none allocated before */ s_contexts = (SCONTEXT*)safemalloc(sizeof (SCONTEXT)); } else { s_contexts = (SCONTEXT*)saferealloc((char*)s_contexts, i * sizeof (SCONTEXT)); } s_contexts[i-1].page_ents = (PAGE_ENT*)safemalloc(MAX_PAGE_SIZE*sizeof(PAGE_ENT)); s_init_context(i-1,type); s_num_contexts++; /* now safe to increment */ return s_num_contexts-1; } /* saves the current context */ void s_save_context() { SCONTEXT *p; p = s_contexts + s_cur_context; p->type = s_cur_type; p->page_ents = page_ents; p->ent_sort = s_ent_sort; p->ent_sort_max = s_ent_sort_max; p->ent_sorted_max = s_ent_sorted_max; p->ent_index = s_ent_index; p->ent_index_max = s_ent_index_max; p->page_size = s_page_size; p->top_ent = s_top_ent; p->bot_ent = s_bot_ent; p->refill = s_refill; p->ref_all = s_ref_all; p->ref_top = s_ref_top; p->ref_bot = s_ref_bot; p->ref_status = s_ref_status; p->ref_desc = s_ref_desc; p->top_lines = s_top_lines; p->bot_lines = s_bot_lines; p->status_cols = s_status_cols; p->cursor_cols = s_cursor_cols; p->desc_cols = s_desc_cols; p->itemnum_cols = s_itemnum_cols; p->ptr_page_line = s_ptr_page_line; p->flags = s_flags; } void s_change_context(newcontext) int newcontext; /* context number to activate */ { SCONTEXT *p; if (newcontext < 0 || newcontext >= s_num_contexts) { printf("s_change_context: bad context number %d!\n",newcontext) FLUSH; assert(FALSE); } s_cur_context = newcontext; p = s_contexts + newcontext; s_cur_type = p->type; page_ents = p->page_ents; s_ent_sort = p->ent_sort; s_ent_sort_max = p->ent_sort_max; s_ent_sorted_max = p->ent_sorted_max; s_ent_index = p->ent_index; s_ent_index_max = p->ent_index_max; s_page_size = p->page_size; s_top_ent = p->top_ent; s_bot_ent = p->bot_ent; s_refill = p->refill; s_ref_all = p->ref_all; s_ref_top = p->ref_top; s_ref_bot = p->ref_bot; s_ref_status = p->ref_status; s_ref_desc = p->ref_desc; /* next ones should be reset later */ s_top_lines = p->top_lines; s_bot_lines = p->bot_lines; s_status_cols = p->status_cols; s_cursor_cols = p->cursor_cols; s_desc_cols = p->desc_cols; s_itemnum_cols = p->itemnum_cols; s_ptr_page_line = p->ptr_page_line; s_flags = p->flags; } /* implement later? */ void s_clean_contexts() { } void s_delete_context(cnum) int cnum; /* context number to delete */ { if (cnum < 0 || cnum >= s_num_contexts) { printf("s_delete_context: illegal context number %d!\n",cnum) FLUSH; assert(FALSE); } s_order_clean(); /* mark the context as empty */ s_contexts[cnum].type = 0; } #endif /* SCAN */ trn-4.0-test77/scan.h0000644000000000000000000001041507113133016013102 0ustar rootroot/* This file Copyright 1992,1993 by Clifford A. Adams */ /* scan.h * * (Mostly scan context declarations.) */ /* return codes. First two should be the same as article scan codes */ #define S_QUIT (-1) #define S_ERR (-2) /* command was not found in common scan subset */ #define S_NOTFOUND (-3) /* number of entries allocated for a page */ #define MAX_PAGE_SIZE 256 /* different context types */ #define S_ART 1 #define S_GROUP 2 #define S_HELP 3 #define S_VIRT 4 struct page_ent { long entnum; /* entry (article/message/newsgroup) number */ short lines; /* how many screen lines to describe? */ short start_line; /* screen line (0 = line under top status bar) */ char pageflags; /* not currently used. */ }; struct scontext { int type; /* context type */ /* ordering information */ long* ent_sort; /* sorted list of entries in the context */ long ent_sort_max; /* maximum index of sorted array */ long ent_sorted_max; /* maximum index *that is sorted* */ long* ent_index; /* indexes into ent_sorted */ long ent_index_max; /* maximum entry number added */ int page_size; /* number of entries allocated for page */ /* (usually fixed, > max screen lines) */ PAGE_ENT* page_ents; /* array of entries on page */ /* -1 means not initialized for top and bottom entry */ long top_ent; /* top entry on page */ long bot_ent; /* bottom entry (note change) */ bool refill; /* does the page need refilling? */ /* refresh entries */ bool ref_all; /* refresh all on page */ bool ref_top; /* top status bar */ bool ref_bot; /* bottom status bar */ /* -1 for the next two entries means don't refresh */ short ref_status; /* line to start refreshing status from */ short ref_desc; /* line to start refreshing descript. from */ /* screen sizes */ short top_lines; /* lines for top status bar */ short bot_lines; /* lines for bottom status bar */ short status_cols; /* characters for status column */ short cursor_cols; /* characters for cursor column */ short itemnum_cols; /* characters for item number column */ short desc_cols; /* characters for description column */ /* pointer info */ short ptr_page_line; /* page_ent index */ long flags; }; /* the current values */ long* s_ent_sort; /* sorted list of entries in the context */ long s_ent_sort_max; /* maximum index of sorted array */ long s_ent_sorted_max; /* maximum index *that is sorted* */ long* s_ent_index; /* indexes into ent_sorted */ long s_ent_index_max; /* maximum entry number added */ int s_page_size; /* number of entries allocated for page */ /* (usually fixed, > max screen lines) */ PAGE_ENT* page_ents; /* array of entries on page */ /* -1 means not initialized for top and bottom entry */ long s_top_ent; /* top entry on page */ long s_bot_ent; /* bottom entry (note change) */ bool s_refill; /* does the page need refilling? */ /* refresh entries */ bool s_ref_all; /* refresh all on page */ bool s_ref_top; /* top status bar */ bool s_ref_bot; /* bottom status bar */ /* -1 for the next two entries means don't refresh */ short s_ref_status; /* line to start refreshing status from */ short s_ref_desc; /* line to start refreshing descript. from */ /* screen sizes */ short s_top_lines; /* lines for top status bar */ short s_bot_lines; /* lines for bottom status bar */ short s_status_cols; /* characters for status column */ short s_cursor_cols; /* characters for cursor column */ short s_itemnum_cols; /* characters for item number column */ short s_desc_cols; /* characters for description column */ /* pointer info */ short s_ptr_page_line; /* page_ent index */ long s_flags; /* misc. flags */ EXT int s_num_contexts INIT(0); /* array of context structures */ EXT SCONTEXT* s_contexts INIT((SCONTEXT*)NULL); /* current context number */ EXT int s_cur_context INIT(0); /* current context type (for fast switching) */ int s_cur_type; /* options */ /* show item numbers by default */ EXT int s_itemnum INIT(TRUE); EXT int s_mode_vi INIT(0); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void s_init_context _((int,int)); int s_new_context _((int)); void s_save_context _((void)); void s_change_context _((int)); void s_clean_contexts _((void)); void s_delete_context _((int)); trn-4.0-test77/scanart.c0000644000000000000000000000550007113133016013603 0ustar rootroot/* This file is Copyright 1992, 1993 by Clifford A. Adams */ /* scanart.c * * article scan mode: screen oriented article interface * based loosely on the older "SubScan" mode. * Interface routines to the rest of trn */ #include "EXTERN.h" #include "common.h" #ifdef SCAN_ART #include "hash.h" #include "cache.h" #include "ng.h" /* variable art, the next article to read. */ #include "ngdata.h" #include "term.h" /* macro to clear... */ #include "bits.h" /* absfirst */ #include "artsrch.h" /* needed for artstate.h */ #include "artstate.h" /* for reread */ #include "rt-select.h" /* selected_only */ #include "scan.h" #include "smisc.h" #include "spage.h" #include "INTERN.h" #include "scanart.h" /* ordering dependency */ #include "EXTERN.h" #include "samain.h" #include "samisc.h" int sa_main() { char sa_oldmode; /* keep mode of caller */ int i; sa_in = TRUE; sa_go = FALSE; /* ...do not collect $200... */ s_follow_temp = FALSE; if (lastart < absfirst) { s_beep(); return SA_QUIT; } if (!sa_initialized) { sa_init(); if (!sa_initialized) /* still not working... */ return SA_ERR; /* we don't belong here */ sa_never_initialized = FALSE; /* we have entered at least once */ } else s_change_context(sa_scan_context); /* unless "explicit" entry, read any marked articles */ if (!sa_go_explicit) { long a; a = sa_readmarked_elig(); if (a) { /* there was an article */ art = sa_ents[a].artnum; reread = TRUE; sa_clearmark(a); /* trn 3.x won't read an unselected article if selected_only */ selected_only = FALSE; s_save_context(); return SA_NORM; } } sa_go_explicit = FALSE; /* If called from the trn thread-selector and articles/threads were * selected there, "select" the articles and enter the zoom mode. */ if (sa_do_selthreads) { sa_selthreads(); sa_do_selthreads = FALSE; sa_mode_zoom = TRUE; /* zoom in by default */ s_top_ent = -1; /* go to top of arts... */ } sa_oldmode = mode; /* save mode */ mode = 's'; /* for RN macros */ i = sa_mainloop(); mode = sa_oldmode; /* restore mode */ if (i == SA_NORM || i == SA_FAKE) { art = sa_art; /* trn 3.x won't read an unselected article if selected_only */ selected_only = FALSE; if (sa_mode_read_elig) reread = TRUE; } if (sa_scan_context >= 0) s_save_context(); return i; } /* called when more articles arrive */ void sa_grow(oldlast,last) ART_NUM oldlast,last; { if (!sa_initialized) return; sa_growarts(oldlast,last); } void sa_cleanup() { /* we might be called by other routines which aren't sure * about the scan status */ if (!sa_initialized) return; sa_cleanmain(); clear(); /* should something else clear the screen? */ sa_initialized = FALSE; /* goodbye... */ } #endif /* SCAN */ trn-4.0-test77/scanart.h0000644000000000000000000000561207113133016013614 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* scanart.h * * Interface to rest of [t]rn */ /* return codes for sa_main */ /* read article pointed to by art (always) */ #define SA_READ (-7) /* quit, and return to previous selector (backtick) */ #define SA_QUIT_SEL (-6) /* go to and enter prior newsgroup */ #define SA_PRIOR (-5) /* go to and enter next newsgroup */ #define SA_NEXT (-4) /* Fake a command (buf and art already set up) */ #define SA_FAKE (-3) /* error, quit out one level */ #define SA_ERR (-2) /* quit out one level and clean up... */ #define SA_QUIT (-1) /* do the normal thing (usually read article pointed to by art) */ #define SA_NORM 0 /* per-entry data */ struct sa_entrydata { ART_NUM artnum; long subj_thread_num; char sa_flags; /* status bitmap (marked, select, etc...) */ }; EXT SA_ENTRYDATA* sa_ents INIT((SA_ENTRYDATA*)0); EXT int sa_num_ents INIT(0); EXT int sa_ents_alloc INIT(0); EXT bool sa_initialized INIT(FALSE); /* Have we initialized? */ EXT bool sa_never_initialized INIT(TRUE); /* Have we ever initialized? */ /* note: sa_in should be checked for returning to SA */ EXT bool sa_in INIT(FALSE); /* Are we "in" SA? */ EXT bool sa_go INIT(FALSE); /* go to sa. Do not pass GO (:-) */ EXT bool sa_go_explicit INIT(FALSE); /* want to bypass read-next-marked */ EXT bool sa_context_init INIT(FALSE); /* has context been initialized? */ /* used to pass an article number to read soon */ EXT ART_NUM sa_art INIT(0); /* reimplement later */ /* select threads from TRN thread selector */ EXT bool sa_do_selthreads INIT(FALSE); /* TRUE if read articles are eligible */ /* in scanart.h for world-visibilty */ EXT bool sa_mode_read_elig INIT(FALSE); /* Options */ /* Display order variable: * * 1: Arrival order * 2: Descending score */ #ifdef SCORE EXT int sa_mode_order INIT(2); #else EXT int sa_mode_order INIT(1); #endif /* if TRUE, don't move the cursor after marking or selecting articles */ EXT bool sa_mark_stay INIT(FALSE); /* if TRUE, re-"fold" after an un-zoom operation. */ /* This flag is useful for very slow terminals */ EXT bool sa_unzoomrefold INIT(FALSE); /* TRUE if in "fold" mode */ EXT bool sa_mode_fold INIT(FALSE); /* Follow threads by default? */ EXT bool sa_follow INIT(TRUE); /* Options: what to display */ EXT bool sa_mode_desc_artnum INIT(FALSE); /* show art#s */ EXT bool sa_mode_desc_author INIT(TRUE); /* show author */ #ifdef SCORE EXT bool sa_mode_desc_score INIT(TRUE); /* show score */ #else EXT bool sa_mode_desc_score INIT(FALSE); /* show score */ #endif /* flags to determine whether to display various things */ EXT bool sa_mode_desc_threadcount INIT(FALSE); EXT bool sa_mode_desc_subject INIT(TRUE); EXT bool sa_mode_desc_summary INIT(FALSE); EXT bool sa_mode_desc_keyw INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int sa_main _((void)); void sa_grow _((ART_NUM,ART_NUM)); void sa_cleanup _((void)); trn-4.0-test77/scmd.c0000644000000000000000000002475411437640112013116 0ustar rootroot/* This file is Copyright 1993 by Clifford A. Adams */ /* scmd.c * * Scan command loop. * Does some simple commands, and passes the rest to context-specific routines. */ #include "EXTERN.h" #include "common.h" #ifdef SCAN #include "hash.h" #include "cache.h" #include "bits.h" #include "final.h" #include "help.h" #include "ng.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "ngstuff.h" #include "term.h" #include "util.h" #include "scan.h" #include "smisc.h" #include "sorder.h" #include "spage.h" #include "sdisp.h" #include "sacmd.h" /* sa_docmd */ #include "samain.h" #ifdef SCORE #include "score.h" #endif #include "univ.h" #include "INTERN.h" #include "scmd.h" void s_search(); void s_go_bot() { s_ref_bot = TRUE; /* help uses whole screen */ s_goxy(0,tc_LINES-s_bot_lines); /* go to bottom bar */ erase_eol(); /* erase to end of line */ s_goxy(0,tc_LINES-s_bot_lines); /* go (back?) to bottom bar */ } /* finishes a command on the bottom line... */ /* returns TRUE if command entered, FALSE if wiped out... */ int s_finish_cmd(string) char* string; { s_go_bot(); if (string && *string) { printf("%s",string); fflush(stdout); } buf[1] = FINISHCMD; return finish_command(FALSE); /* do not echo newline */ } /* returns an entry # selected, S_QUIT, or S_ERR */ int s_cmdloop() { int i; /* initialization stuff for entry into s_cmdloop */ s_ref_all = TRUE; eat_typeahead(); /* no typeahead before entry */ while(TRUE) { s_refresh(); s_place_ptr(); /* place article pointer */ bos_on_stop = TRUE; s_lookahead(); /* do something useful while waiting */ getcmd(buf); bos_on_stop = FALSE; eat_typeahead(); /* stay in control. */ /* check for window resizing and refresh */ /* if window is resized, refill and redraw */ if (s_resized) { char ch = *buf; i = s_fillpage(); if (i == -1 || i == 0) /* can't fillpage */ return S_QUIT; *buf = Ctl('l'); (void)s_docmd(); *buf = ch; s_resized = FALSE; /* dealt with */ } i = s_docmd(); if (i == S_NOTFOUND) { /* command not in common set */ switch (s_cur_type) { #ifdef SCAN_ART case S_ART: i = sa_docmd(); break; #endif default: i = 0; /* just keep looping */ break; } } if (i != 0) /* either an entry # or a return code */ return i; if (s_refill) { i = s_fillpage(); if (i == -1 || i == 0) /* can't fillpage */ return S_QUIT; } /* otherwise just keep on looping... */ } } void s_lookahead() { switch (s_cur_type) { #ifdef SCAN_ART case S_ART: sa_lookahead(); break; #endif default: break; } } /* Do some simple, common Scan commands for any mode */ /* Interprets command in buf, returning 0 to continue looping or * a condition code (negative #s). Responsible for setting refresh flags * if necessary. */ int s_docmd() { long a; /* entry pointed to */ bool flag; /* misc */ a = page_ents[s_ptr_page_line].entnum; if (*buf == '\f') /* map form feed to ^l */ *buf = Ctl('l'); switch(*buf) { case 'j': /* vi mode */ if (!s_mode_vi) return S_NOTFOUND; /* FALL THROUGH */ case 'n': /* next art */ case ']': s_rub_ptr(); if (s_ptr_page_line < s_bot_ent) /* more on page... */ s_ptr_page_line +=1; else { if (!s_next_elig(page_ents[s_bot_ent].entnum)) { s_beep(); s_refill = TRUE; break; } s_go_next_page(); /* will jump to top too... */ } break; case 'k': /* vi mode */ if (!s_mode_vi) return S_NOTFOUND; /* FALL THROUGH */ case 'p': /* previous art */ case '[': s_rub_ptr(); if (s_ptr_page_line > 0) /* more on page... */ s_ptr_page_line = s_ptr_page_line - 1; else { if (s_prev_elig(page_ents[0].entnum)) { s_go_prev_page(); s_ptr_page_line = s_bot_ent; /* go to page bot. */ } else { s_refill = TRUE; s_beep(); } } break; case 't': /* top of page */ s_rub_ptr(); s_go_top_page(); break; case 'b': /* bottom of page */ s_rub_ptr(); s_go_bot_page(); break; case '>': /* next page */ s_rub_ptr(); a = s_next_elig(page_ents[s_bot_ent].entnum); if (!a) { /* at end of articles */ s_beep(); break; } s_go_next_page(); /* will beep if no next page */ break; case '<': /* previous page */ s_rub_ptr(); if (!s_prev_elig(page_ents[0].entnum)) { s_beep(); break; } s_go_prev_page(); /* will beep if no prior page */ break; case 'T': /* top of ents */ case '^': s_rub_ptr(); flag = s_go_top_ents(); if (!flag) /* failure */ return S_QUIT; break; case 'B': /* bottom of ents */ case '$': s_rub_ptr(); flag = s_go_bot_ents(); if (!flag) return S_QUIT; break; case Ctl('r'): /* refresh screen */ case Ctl('l'): s_ref_all = TRUE; break; case Ctl('f'): /* refresh (mail) display */ #ifdef MAILCALL setmail(TRUE); #endif s_ref_bot = TRUE; break; case 'h': /* universal help */ s_go_bot(); s_ref_all = TRUE; univ_help(UHELP_SCANART); eat_typeahead(); break; case 'H': /* help */ s_go_bot(); s_ref_all = TRUE; /* any commands typed during help are unused. (might change) */ switch (s_cur_type) { #ifdef SCAN_ART case S_ART: (void)help_scanart(); break; #endif default: printf("No help available for this mode (yet).\n") FLUSH; printf("Press any key to continue.\n"); break; } (void)get_anything(); eat_typeahead(); break; case '!': /* shell command */ s_go_bot(); s_ref_all = TRUE; /* will need refresh */ if (!escapade()) (void)get_anything(); eat_typeahead(); break; #if 0 case '&': /* see/set switches... */ /* CAA 05/29/95: The new option stuff makes this potentially recursive. * Something similar to the 'H' (extended help) code needs to be done. * It may be necessary for this code to do the context saving. */ s_go_bot(); s_ref_all = TRUE; /* will need refresh */ if (!switcheroo()) /* XXX same semantics in trn4? */ (void)get_anything(); eat_typeahead(); break; #endif case '/': case '?': case 'g': /* goto (search for) group */ s_search(); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': s_jumpnum(*buf); break; case '#': /* Toggle item numbers */ if (s_itemnum) { /* turn off item numbers */ s_desc_cols += s_itemnum_cols; s_itemnum_cols = 0; s_itemnum = 0; } else { /* turn on item numbers */ s_itemnum_cols = 3; s_desc_cols -= s_itemnum_cols; s_itemnum = 1; } s_ref_all = TRUE; break; default: return S_NOTFOUND; /* not one of the simple commands */ } /* switch */ return 0; /* keep on looping! */ } static char search_text[LBUFLEN]; static char search_init INIT(FALSE); bool s_match_description(ent) long ent; { int i, lines; static char lbuf[LBUFLEN]; char* s; lines = s_ent_lines(ent); for (i = 1; i <= lines; i++) { strncpy(lbuf,s_get_desc(ent,i,FALSE),LBUFLEN); for (s = lbuf; *s; s++) if (isupper(*s)) *s = tolower(*s); /* convert to lower case */ if (STRSTR(lbuf,search_text)) return TRUE; } return FALSE; } long s_forward_search(ent) long ent; { if (ent) ent = s_next_elig(ent); else ent = s_first(); for ( ; ent; ent = s_next_elig(ent)) if (s_match_description(ent)) break; return ent; } long s_backward_search(ent) long ent; { if (ent) ent = s_prev_elig(ent); else ent = s_last(); for ( ; ent; ent = s_prev_elig(ent)) if (s_match_description(ent)) break; return ent; } /* perhaps later have a wraparound search? */ void s_search() { int i; int fill_type; /* 0: forward, 1: backward */ long ent; char* s; char* error_msg; if (!search_init) { search_init = TRUE; search_text[0] = '\0'; } s_rub_ptr(); buf[1] = '\0'; if (!s_finish_cmd(NULL)) return; if (buf[1]) { /* new text */ s = buf+1; /* make leading space skip an option later? */ /* (it isn't too important because substring matching is used) */ while (*s == ' ') s++; /* skip leading spaces */ strncpy(search_text,s,LBUFLEN); for (s = search_text; *s != '\0'; s++) if (isupper(*s)) *s = tolower(*s); /* convert to lower case */ } if (!*search_text) { s_beep(); printf("\nNo previous search string.\n") FLUSH; (void)get_anything(); s_ref_all = TRUE; return; } s_go_bot(); printf("Searching for %s",search_text); fflush(stdout); ent = page_ents[s_ptr_page_line].entnum; switch (*buf) { case '/': error_msg = "No matches forward from current point."; ent = s_forward_search(ent); fill_type = 0; /* forwards fill */ break; case '?': error_msg = "No matches backward from current point."; ent = s_backward_search(ent); fill_type = 1; /* backwards fill */ break; case 'g': ent = s_forward_search(ent); if (!ent) { ent = s_forward_search(0); /* from top */ /* did we just loop around? */ if (ent == page_ents[s_ptr_page_line].entnum) { ent = 0; error_msg = "No other entry matches."; } else error_msg = "No matches."; } fill_type = 0; /* forwards fill */ break; default: fill_type = 0; error_msg = "Internal error in s_search()"; break; } if (!ent) { s_beep(); printf("\n%s\n",error_msg) FLUSH; (void)get_anything(); s_ref_all = TRUE; return; } for (i = 0; i <= s_bot_ent; i++) if (page_ents[i].entnum == ent) { /* entry is on same page */ s_ptr_page_line = i; return; } /* entry is not on page... */ if (fill_type == 1) { (void)s_fillpage_backward(ent); s_go_bot_page(); s_refill = TRUE; s_ref_all = TRUE; } else { (void)s_fillpage_forward(ent); s_go_top_page(); s_ref_all = TRUE; } } void s_jumpnum(firstchar) char_int firstchar; { int value; bool jump_verbose; jump_verbose = TRUE; value = firstchar - '0'; s_rub_ptr(); #ifdef NICEBG wait_key_pause(10); if (input_pending()) jump_verbose = FALSE; #endif if (jump_verbose) { s_go_bot(); s_ref_bot = TRUE; printf("Jump to item: %c",firstchar); fflush(stdout); } getcmd(buf); if (*buf == ERASECH) return; switch (*buf) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (jump_verbose) { printf("%c",*buf); fflush(stdout); } value = value*10 + (*buf - '0'); break; default: pushchar(*buf); break; } if (value == 0 || value > s_bot_ent+1) { s_beep(); return; } s_ptr_page_line = value-1; } #endif /* SCAN */ trn-4.0-test77/scmd.h0000644000000000000000000000071407113133016013105 0ustar rootroot/* This file is Copyright 1993 by Clifford A. Adams */ /* scmd.h * * Scan command interpreter/router */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void s_go_bot _((void)); int s_finish_cmd _((char*)); int s_cmdloop _((void)); void s_lookahead _((void)); int s_docmd _((void)); bool s_match_description _((long)); long s_forward_search _((long)); long s_backward_search _((long)); void s_search _((void)); void s_jumpnum _((char_int)); trn-4.0-test77/score-easy.c0000644000000000000000000001214507113133016014225 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* score-easy.c * * Simple interactive menus for scorefile tasks. */ #include "EXTERN.h" #include "common.h" #ifdef SCORE #include "search.h" #include "term.h" #include "util.h" #include "score.h" #include "scorefile.h" #include "INTERN.h" #include "score-easy.h" /* new line to return to the caller. */ static char sc_e_newline[LBUFLEN]; /* returns new string or NULL to abort. */ char* sc_easy_append() { char* s; bool q_done; /* if TRUE, we are finished with current question */ char filechar; long score; char ch; filechar = '\0'; /* GCC warning avoidance */ s = sc_e_newline; printf("\nScorefile easy append mode.\n") FLUSH; q_done = FALSE; while (!q_done) { printf("0) Exit.\n") FLUSH; printf("1) List the current scorefile abbreviations.\n"); printf("2) Add an entry to the global scorefile.\n"); printf("3) Add an entry to this newsgroup's scorefile.\n"); printf("4) Add an entry to another scorefile.\n"); printf("5) Use a temporary scoring rule.\n"); ch = menu_get_char(); q_done = TRUE; switch (ch) { case '0': return NULL; case '1': strcpy(sc_e_newline,"?"); return sc_e_newline; case '2': filechar = '*'; break; case '3': filechar = '"'; break; case '4': filechar = '\0'; break; case '5': filechar = '!'; break; case 'h': printf("No help available (yet).\n") FLUSH; q_done = FALSE; break; default: q_done = FALSE; break; } } while (filechar == '\0') { /* choose one */ printf("Type the (single character) abbreviation of the scorefile:"); fflush(stdout); eat_typeahead(); getcmd(buf); printf("%c\n",*buf) FLUSH; filechar = *buf; /* If error checking is done later, then an error should set * filechar to '\0' and continue the while loop. */ } *s++ = filechar; *s++ = ' '; q_done = FALSE; while (!q_done) { printf("What type of line do you want to add?\n"); printf("0) Exit.\n"); printf("1) A scoring rule line.\n"); printf(" (for the current article's author/subject)\n"); printf("2) A command, comment, or other kind of line.\n"); printf(" (use this for any other kind of line)\n"); printf("\n[Other line formats will be supported later.]\n"); ch = menu_get_char(); q_done = TRUE; switch (ch) { case '0': return NULL; case '1': break; case '2': printf("Enter the line below:\n"); fflush(stdout); buf[0] = '>'; buf[1] = FINISHCMD; if (finish_command(TRUE)) { sprintf(s,"%s",buf+1); return sc_e_newline; } printf("\n"); q_done = FALSE; break; case 'h': printf("No help available (yet).\n") FLUSH; q_done = FALSE; break; default: q_done = FALSE; break; } } q_done = FALSE; while (!q_done) { printf("Enter a score amount (like 10 or -6):"); fflush(stdout); buf[0] = ' '; buf[1] = FINISHCMD; if (finish_command(TRUE)) { score = atoi(buf+1); if (score == 0) if (buf[1] != '0') continue; /* the while loop */ sprintf(s,"%ld",score); s = sc_e_newline+strlen(sc_e_newline); /* point at terminator */ *s++ = ' '; q_done = TRUE; } else printf("\n") FLUSH; } q_done = FALSE; while (!q_done) { printf("Do you want to:\n"); printf("0) Exit.\n"); printf("1) Give the score to the current subject.\n"); printf("2) Give the score to the current author.\n"); /* add some more options here later */ /* perhaps fold regular-expression question here? */ ch = menu_get_char(); q_done = TRUE; switch (ch) { case '0': return NULL; case '1': *s++ = 'S'; *s++ = '\0'; return sc_e_newline; case '2': *s++ = 'F'; *s++ = '\0'; return sc_e_newline; case 'h': printf("No help available (yet).\n") FLUSH; q_done = FALSE; break; default: q_done = FALSE; break; } } /* later ask for headers, pattern-matching, etc... */ return NULL; } /* returns new string or NULL to abort. */ char* sc_easy_command() { char* s; bool q_done; /* if TRUE, we are finished with current question */ char ch; s = sc_e_newline; printf("\nScoring easy command mode.\n") FLUSH; q_done = FALSE; while (!q_done) { printf("0) Exit.\n"); printf("1) Add something to a scorefile.\n"); printf("2) Rescore the articles in the current newsgroup.\n"); printf("3) Explain the current article's score.\n"); printf(" (show the rules that matched this article)\n"); printf("4) Edit this newsgroup's scoring rule file.\n"); /* later add an option to edit an arbitrary file */ printf("5) Continue scoring unscored articles.\n"); ch = menu_get_char(); q_done = TRUE; switch (ch) { case '0': return NULL; case '1': return "\""; /* do an append command */ case '2': return "r"; case '3': return "s"; case '4': /* add more later */ return "e"; case '5': return "f"; case 'h': printf("No help available (yet).\n") FLUSH; q_done = FALSE; break; default: q_done = FALSE; break; } } return NULL; } #endif /* SCORE */ trn-4.0-test77/score-easy.h0000644000000000000000000000031507113133016014226 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* score-easy.h * */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ char* sc_easy_append _((void)); char* sc_easy_command _((void)); trn-4.0-test77/score.c0000644000000000000000000003333207113133016013267 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* score.c * */ #include "EXTERN.h" #include "common.h" #ifdef SCORE /* if SCORE is undefined, no code should be compiled */ /* sort the following includes later */ #include "list.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "artio.h" /* for openart var.*/ #include "final.h" /* int_count */ #include "head.h" /* ? */ #include "intrp.h" /* for filexp */ #include "ng.h" /* art */ #include "ngdata.h" #include "search.h" /* for regex */ #include "rt-util.h" /* spinner */ #include "term.h" /* input_pending() */ #include "trn.h" /* ngname */ #include "util.h" /* several */ #ifdef SCAN #include "scan.h" #include "sorder.h" #include "scanart.h" #ifdef SCAN_ART #include "samain.h" #include "samisc.h" #endif #endif #include "INTERN.h" #include "score.h" #include "EXTERN.h" #include "scorefile.h" #include "scoresave.h" #include "score-easy.h" /* interactive menus and such */ #ifdef USE_FILTER #include "filter.h" #endif #include "INTERN.h" /* not currently needed, but safer */ #if defined(USE_FILTER) && defined(FILTER_DEBUG) FILE* filter_error_file; #endif void sc_init(pend_wait) bool_int pend_wait; /* if true, enter pending mode when scoring... */ { int i; ART_NUM a; if (lastart == 0 || lastart < absfirst) { #if 0 printf("No articles exist to be scored.\n") FLUSH; #endif return; } sc_sf_force_init = TRUE; /* generally force initialization */ if (sc_delay) /* requested delay? */ return; sc_sf_delay = FALSE; /* Consider the relationships between scoring and article scan mode. * Should one be able to initialize the other? How much can they depend on * each other when both are #defined? * Consider this especially in a later redesign or porting these systems * to other newsreaders. */ kill_thresh_active = FALSE; /* kill thresholds are generic */ /* July 24, 1993: changed default of sc_savescores to TRUE */ sc_savescores = TRUE; /* CONSIDER: (for sc_init callers) is lastart properly set yet? */ sc_fill_max = absfirst - 1; #ifdef SCAN_ART if (sa_mode_read_elig || firstart > lastart) sc_fill_read = TRUE; else #endif sc_fill_read = FALSE; if (sf_verbose) { printf("\nScoring articles..."); fflush(stdout); /* print it *now* */ } sc_initialized = TRUE; /* little white lie for lookahead */ /* now is a good time to load a saved score-list which may exist */ if (!sc_rescoring) { /* don't load if rescoring */ sc_load_scores(); /* will be quiet if non-existent */ i = firstart; if (sc_fill_read) i = absfirst; if (sc_sf_force_init) i = lastart+1; /* skip loop */ for (i = article_first(i); i <= lastart; i = article_next(i)) { if (!SCORED(i) && (sc_fill_read || article_unread(i))) break; } if (i == lastart) /* all scored */ sc_sf_delay = TRUE; } if (sc_sf_force_init) sc_sf_delay = FALSE; if (!sc_sf_delay) sf_init(); /* initialize the scorefile code */ sc_do_spin = FALSE; for (i = article_last(lastart); i >= absfirst; i = article_prev(i)) { if (SCORED(i)) break; } if (i < absfirst) { /* none scored yet */ /* score one article, or give up */ for (a = article_last(lastart); a >= absfirst; a = article_prev(a)) { sc_score_art(a,TRUE); /* I want it *now* */ if (SCORED(a)) break; } if (a < absfirst) { /* no articles scored */ if (sf_verbose) printf("\nNo articles available for scoring\n"); sc_cleanup(); return; } } /* if no scoring rules/methods are present, score everything */ /* XXX will be bad if later methods are added. */ if (!sf_num_entries) { /* score everything really fast */ for (a = article_last(lastart); a >= absfirst; a = article_prev(a)) sc_score_art(a,TRUE); } if (pend_wait) { bool waitflag; /* if true, use key pause */ waitflag = TRUE; /* normal mode: wait for key first */ if (sf_verbose && waitflag) { #ifdef PENDING printf("(press key to start reading)"); #else printf("(interrupt to start reading)"); #endif fflush(stdout); } if (waitflag) { setspin(SPIN_FOREGROUND); sc_do_spin = TRUE; /* really do it */ } sc_lookahead(TRUE,waitflag); /* jump in *now* */ if (waitflag) { sc_do_spin = FALSE; setspin(SPIN_POP); } } if (sf_verbose) putchar('\n') FLUSH; sc_initialized = TRUE; } void sc_cleanup() { if (!sc_initialized) return; if (sc_savescores) sc_save_scores(); sc_loaded_count = 0; if (sf_verbose) { printf("\nCleaning up scoring..."); fflush(stdout); } if (!sc_sf_delay) sf_clean(); /* let the scorefile do whatever cleaning it needs */ sc_initialized = FALSE; if (sf_verbose) printf("Done.\n") FLUSH; } void sc_set_score(a,score) ART_NUM a; int score; { register ARTICLE* ap; if (is_unavailable(a)) /* newly unavailable */ return; if (kill_thresh_active && score <= kill_thresh && article_unread(a)) oneless_artnum(a); ap = article_ptr(a); ap->score = score; /* update the score */ ap->scoreflags |= SFLAG_SCORED; #ifdef SCAN s_order_changed = TRUE; /* resort */ #endif } /* Hopefully people will write more scoring routines later */ /* This is where you should add hooks for new scoring methods. */ void sc_score_art_basic(a) ART_NUM a; { int score; #ifdef USE_FILTER int sc; # ifdef FILTER_DEBUG ARTICLE* ap; # endif #endif score = 0; score += sf_score(a); /* get a score */ #ifdef USE_FILTER # ifdef FILTER_DEBUG filter_error_file = fopen("/tmp/score.log", "a"); # endif sc = filter(a); score += sc; # ifdef FILTER_DEBUG ap = article_find(a); if (ap && ap->refs) fprintf(filter_error_file, "%s: article %ld got score %ld\n", current_ng->rcline, (long)a, (long)sc); fclose(filter_error_file); # endif /* FILTER_DEBUG */ #endif /* USE_FILTER */ if (sc_do_spin) /* appropriate to spin */ spin(20); /* keep the user amused */ sc_set_score(a,score); /* set the score */ } /* Returns an article's score, scoring it if necessary */ int sc_score_art(a,now) ART_NUM a; bool_int now; /* if TRUE, sort the scores if necessary... */ { if (a < absfirst || a > lastart) { #if 0 printf("\nsc_score_art: illegal article# %d\n",a) FLUSH; #endif return LOWSCORE; /* definitely unavailable */ } if (is_unavailable(a)) return LOWSCORE; if (sc_initialized == FALSE) { sc_delay = FALSE; sc_sf_force_init = TRUE; sc_init(FALSE); sc_sf_force_init = FALSE; } if (!SCORED(a)) { if (sc_sf_delay) { sf_init(); sc_sf_delay = FALSE; } sc_score_art_basic(a); } if (is_unavailable(a)) return LOWSCORE; return article_ptr(a)->score; } /* scores articles in a range */ /* CONSIDER: option for scoring only unread articles (obey sc_fill_unread?) */ void sc_fill_scorelist(first,last) ART_NUM first,last; { int i; for (i = article_first(first); i <= last; i = article_next(i)) (void)sc_score_art(i,FALSE); /* will be sorted later... */ } /* consider having this return a flag (is finished/is not finished) */ /* flag == TRUE means sort now, FALSE means wait until later (not used) * nowait == TRUE means start scoring immediately (group entry) * FALSE means use NICEBG if available */ void sc_lookahead(flag, nowait) bool_int flag; bool_int nowait; { ART_NUM oldart = openart; ART_POS oldartpos; if (!sc_initialized) return; /* no looking ahead now */ #ifdef PENDING if (input_pending()) return; /* delay as little as possible */ #endif if (!sc_initialized) return; /* don't score then... */ #ifdef PENDING #ifdef NICEBG if (sc_mode_nicebg && !nowait) if (wait_key_pause(10)) /* wait up to 1 second for a key */ return; #endif #endif if (oldart) /* Was there an article open? */ oldartpos = tellart(); /* where were we in it? */ #ifndef PENDING if (int_count) int_count = 0; /* clear the interrupt count */ #endif /* prevent needless looping below */ if (sc_fill_max < firstart && !sc_fill_read) sc_fill_max = article_first(firstart)-1; else sc_fill_max = article_first(sc_fill_max); while (sc_fill_max < lastart #ifdef PENDING && !input_pending() #endif ) { #ifndef PENDING if (int_count > 0) { int_count = 0; return; /* user requested break */ } #endif sc_fill_max = article_next(sc_fill_max); /* skip over some articles quickly */ while (sc_fill_max < lastart && (SCORED(sc_fill_max) || (!sc_fill_read && !article_unread(sc_fill_max)))) sc_fill_max = article_next(sc_fill_max); if (SCORED(sc_fill_max)) continue; if (!sc_fill_read) /* score only unread */ if (!article_unread(sc_fill_max)) continue; (void)sc_score_art(sc_fill_max,FALSE); } if (oldart) /* copied from cheat.c */ artopen(oldart,oldartpos); /* do not screw the pager */ } int sc_percent_scored() { int i,total,scored; if (!sc_initialized) return 0; /* none scored */ if (sc_fill_max == lastart) return 100; i = firstart; #ifdef SCAN_ART if (sa_mode_read_elig) i = absfirst; #endif total = scored = 0; for (i = article_first(i); i <= lastart; i = article_next(i)) { if (!article_exists(i)) continue; if (!article_unread(i) #ifdef SCAN_ART && !sa_mode_read_elig #endif ) continue; total++; if (SCORED(i)) scored++; } /* for */ if (total == 0) return 0; return (scored*100) / total; } void sc_rescore_arts() { ART_NUM a; bool old_spin; if (!sc_initialized) { if (sc_delay) { sc_delay = FALSE; sc_sf_force_init = TRUE; sc_init(TRUE); sc_sf_force_init = FALSE; } } else if (sc_sf_delay) { sf_init(); sc_sf_delay = FALSE; } if (!sc_initialized) { printf("\nScoring is not initialized, aborting command.\n") FLUSH; return; } /* I think sc_do_spin will always be false, but why take chances? */ old_spin = sc_do_spin; setspin(SPIN_FOREGROUND); sc_do_spin = TRUE; /* amuse the user */ for (a = article_first(absfirst); a <= lastart; a = article_next(a)) { if (article_exists(a)) sc_score_art_basic(a); /* rescore it then */ } sc_do_spin = old_spin; setspin(SPIN_POP); #ifdef SCAN_ART if (sa_in) { s_ref_all = TRUE; s_refill = TRUE; s_top_ent = 0; /* make sure the refill starts from top */ } #endif } /* Wrapper to isolate scorefile functions from the rest of the world */ /* corrupted (:-) 11/12/92 by CAA for online rescoring */ void sc_append(line) char* line; { char filechar; if (!line) /* empty line */ return; if (!sc_initialized) { if (sc_delay) { sc_delay = FALSE; sc_sf_force_init = TRUE; sc_init(TRUE); sc_sf_force_init = FALSE; } } else if (sc_sf_delay) { sf_init(); sc_sf_delay = FALSE; } if (!sc_initialized) { printf("\nScoring is not initialized, aborting command.\n") FLUSH; return; } if (!*line) { line = sc_easy_append(); if (!line) return; /* do nothing with empty string */ } filechar = *line; /* first char */ sf_append(line); if (filechar == '!') { printf("\nRescoring articles..."); fflush(stdout); sc_rescore_arts(); printf("Done.\n") FLUSH; #ifdef SCAN_ART if (sa_initialized) s_top_ent = -1; /* reset top of page */ #endif } } void sc_rescore() { sc_rescoring = TRUE; /* in case routines need to know */ sc_cleanup(); /* get rid of the old */ sc_init(TRUE); /* enter the new... (wait for rescore) */ #ifdef SCAN_ART if (sa_initialized) { s_top_ent = -1; /* reset top of page */ s_refill = TRUE; /* make sure a refill is done */ } #endif sc_rescoring = FALSE; } /* May have a very different interface in the user versions */ void sc_score_cmd(line) char* line; { long i, j; char* s; if (!sc_initialized) { if (sc_delay) { sc_delay = FALSE; sc_sf_force_init = TRUE; sc_init(TRUE); sc_sf_force_init = FALSE; } } else if (sc_sf_delay) { sf_init(); sc_sf_delay = FALSE; } if (!sc_initialized) { printf("\nScoring is not initialized, aborting command.\n") FLUSH; return; } if (!*line) { line = sc_easy_command(); if (!line) return; /* do nothing with empty string */ if (*line == '\"') { buf[0] = '\0'; sc_append(buf); return; } } switch (*line) { case 'f': /* fill (useful when PENDING is unavailable) */ printf("Scoring more articles..."); fflush(stdout); /* print it now */ setspin(SPIN_FOREGROUND); sc_do_spin = TRUE; sc_lookahead(TRUE,FALSE); sc_do_spin = FALSE; setspin(SPIN_POP); /* consider a "done" message later, * *if* lookahead did all the arts */ putchar('\n') FLUSH; break; case 'r': /* rescore */ printf("Rescoring articles...\n") FLUSH; sc_rescore(); break; case 's': /* verbose score for this article */ /* XXX CONSIDER: A VERBOSE-SCORE ROUTINE (instead of this hack) */ i = 0; /* total score */ sf_score_verbose = TRUE; j = sf_score(art); sf_score_verbose = FALSE; printf("Scorefile total score: %ld\n",j); i += j; j = sc_score_art(art,TRUE); if (i != j) { /* Consider resubmitting article to filter? */ printf("Other scoring total: %ld\n", j - i) FLUSH; } printf("Total score is %ld\n",i) FLUSH; break; case 'e': /* edit scorefile or other file */ for (s = line+1; *s == ' ' || *s == '\t'; s++) ; if (!*s) /* empty name for scorefile */ sf_edit_file("\""); /* edit local scorefile */ else sf_edit_file(s); break; default: printf("Unknown scoring command |%s|\n",line); } /* switch */ } void sc_kill_threshold(thresh) int thresh; /* kill all articles with this score or lower */ { ART_NUM a; for (a = article_first(firstart); a <= lastart; a = article_next(a)) { if (article_ptr(a)->score <= thresh && article_unread(a) #ifdef SCAN_ART /* CAA 6/19/93: this is needed for zoom mode */ && sa_basic_elig(sa_artnum_to_ent(a)) #endif ) oneless_artnum(a); } } #endif /* SCORE */ trn-4.0-test77/score.h0000644000000000000000000000406207113133016013272 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* score.h * */ /* RETHINK LOWSCORE: (especially for 16-bit scores?) */ /* score given to unavailable articles */ #define LOWSCORE (-999999) /* specific scoreflag meanings: (note: bad placement, but where else?) */ /* author has a score (match on FROM: line) */ #define SFLAG_AUTHOR 1 /* if TRUE, the article has been scored */ #define SFLAG_SCORED 16 #define SCORED(a) (article_ptr(a)->scoreflags & SFLAG_SCORED) EXT bool kill_thresh_active INIT(FALSE); EXT int kill_thresh INIT(LOWSCORE); /* KILL articles at or below this score */ EXT ART_NUM sc_fill_max; /* maximum art# scored by fill-routine */ EXT bool sc_fill_read INIT(FALSE); /* TRUE if also scoring read arts... */ /* has score been initialized (are we "in" scoring?) */ EXT bool sc_initialized INIT(FALSE); /* are we currently scoring an article (prevents loops) */ EXT bool sc_scoring INIT(FALSE); /* changes order of sorting (artnum comparison) when scores are equal */ EXT bool score_newfirst INIT(FALSE); /* if nice background available, use it */ EXT bool sc_mode_nicebg INIT(TRUE); /* If true, save the scores for this group on exit. */ EXT bool sc_savescores INIT(FALSE); /* If true, delay initialization of scoring until explicitly required */ EXT bool sc_delay INIT(FALSE); EXT bool sc_rescoring INIT(FALSE); /* are we rescoring now? */ EXT bool sc_do_spin INIT(FALSE); /* actually do the score spinner */ EXT bool sc_sf_delay INIT(FALSE); /* if TRUE, delay loading rule files */ EXT bool sc_sf_force_init INIT(FALSE); /* If true, always sf_init() */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sc_init _((bool_int)); void sc_cleanup _((void)); void sc_set_score _((ART_NUM,int)); void sc_score_art_basic _((ART_NUM)); int sc_score_art _((ART_NUM,bool_int)); void sc_fill_scorelist _((ART_NUM,ART_NUM)); void sc_lookahead _((bool_int,bool_int)); int sc_percent_scored _((void)); void sc_rescore_arts _((void)); void sc_append _((char*)); void sc_rescore _((void)); void sc_score_cmd _((char*)); void sc_kill_threshold _((int)); trn-4.0-test77/scorefile.c0000644000000000000000000010562507113133016014134 0ustar rootroot/* This file Copyright 1992, 1993 by Clifford A. Adams */ /* scorefile.c * * A simple "proof of concept" scoring file for headers. * (yeah, right. :) */ #include "EXTERN.h" #include "common.h" #ifdef SCORE /* if SCORE is undefined, no code should be compiled */ #include "list.h" #include "hash.h" #include "cache.h" #include "bits.h" /* absfirst */ #include "head.h" #include "search.h" /* regex matches */ #include "ngdata.h" #include "ng.h" #include "term.h" /* finish_command() */ #include "util.h" #include "util2.h" #include "env.h" /* getval */ #include "rt-util.h" #include "mempool.h" #include "score.h" /* shared stuff... */ #ifdef SCAN_ART #include "scanart.h" #include "samain.h" /* for sa_authscored macro */ #endif #include "url.h" #include "INTERN.h" #include "scorefile.h" #include "scorefile.ih" /* list of score array markers (in htype field of score entry) */ /* entry is a file marker. Score is the file level */ #define SF_FILE_MARK_START (-1) #define SF_FILE_MARK_END (-2) /* other misc. rules */ #define SF_KILLTHRESHOLD (-3) #define SF_NEWAUTHOR (-4) #define SF_REPLY (-5) static int sf_file_level INIT(0); /* how deep are we? */ static char sf_buf[LBUFLEN]; static char** sf_extra_headers = NULL; static int sf_num_extra_headers = 0; static bool sf_has_extra_headers; /* Must be called before any other sf_ routine (once for each group) */ void sf_init() { int i; char* s; int level; /* depth of newsgroup score file */ sf_num_entries = 0; level = 0; sf_extra_headers = NULL; sf_num_extra_headers = 0; /* initialize abbreviation list */ sf_abbr = (char**)safemalloc(256 * sizeof (char*)); bzero((char*)sf_abbr, 256 * sizeof (char*)); if (sf_verbose) printf("\nReading score files...\n") FLUSH; sf_file_level = 0; /* find # of levels */ strcpy(sf_buf,filexp("%C")); level = 0; for (s = sf_buf; *s; s++) if (*s == '.') level++; /* count dots in group name */ level++; /* the main read-in loop */ for (i = 0; i <= level; i++) if ((s = sf_get_filename(i)) != NULL) sf_do_file(s); /* do post-processing (set thresholds and detect extra header usage) */ sf_has_extra_headers = FALSE; /* set thresholds from the sf_entries */ reply_active = newauthor_active = kill_thresh_active = FALSE; for (i = 0; i < sf_num_entries; i++) { if (sf_entries[i].head_type >= HEAD_LAST) sf_has_extra_headers = TRUE; switch (sf_entries[i].head_type) { case SF_KILLTHRESHOLD: kill_thresh_active = TRUE; kill_thresh = sf_entries[i].score; if (sf_verbose) { int j; /* rethink? */ for (j = i+1; j < sf_num_entries; j++) if (sf_entries[j].head_type == SF_KILLTHRESHOLD) break; if (j == sf_num_entries) /* no later thresholds */ printf("killthreshold %d\n",kill_thresh) FLUSH; } break; case SF_NEWAUTHOR: newauthor_active = TRUE; newauthor = sf_entries[i].score; if (sf_verbose) { int j; /* rethink? */ for (j = i+1; j < sf_num_entries; j++) if (sf_entries[j].head_type == SF_NEWAUTHOR) break; if (j == sf_num_entries) /* no later newauthors */ printf("New Author score: %d\n",newauthor) FLUSH; } break; case SF_REPLY: reply_active = TRUE; reply_score = sf_entries[i].score; if (sf_verbose) { int j; /* rethink? */ for (j = i+1; j < sf_num_entries; j++) if (sf_entries[j].head_type == SF_REPLY) break; if (j == sf_num_entries) /* no later reply rules */ printf("Reply score: %d\n",reply_score) FLUSH; } break; } } } void sf_clean() { int i; for (i = 0; i < sf_num_entries; i++) { if (sf_entries[i].compex != NULL) { free_compex(sf_entries[i].compex); free(sf_entries[i].compex); } } mp_free(MP_SCORE1); /* free memory pool */ if (sf_abbr) { for (i = 0; i < 256; i++) if (sf_abbr[i]) { free(sf_abbr[i]); sf_abbr[i] = NULL; } free(sf_abbr); } if (sf_entries) free(sf_entries); sf_entries = NULL; for (i = 0; i < sf_num_extra_headers; i++) free(sf_extra_headers[i]); sf_num_extra_headers = 0; sf_extra_headers = NULL; } /* rename sf_num_entries (to ?) */ /* use macro instead of all the "sf_entries[sf_num_entries-1]"? * call it "sf_recent_entry" or "sf_last_entry"? */ void sf_grow() { int i; sf_num_entries++; if (sf_num_entries == 1) { sf_entries = (SF_ENTRY*)safemalloc(sizeof (SF_ENTRY)); } else { sf_entries = (SF_ENTRY*)saferealloc((char*)sf_entries, sf_num_entries * sizeof (SF_ENTRY)); } i = sf_num_entries-1; sf_entries[i].compex = NULL; /* init */ sf_entries[i].flags = 0; sf_entries[i].str1 = NULL; sf_entries[i].str2 = NULL; } /* Returns -1 if no matching extra header found, otherwise returns offset * into the sf_extra_headers array. */ int sf_check_extra_headers(head) char* head; /* header name, (without ':' character) */ { int i; char* s; static char lbuf[LBUFLEN]; /* convert to lower case */ safecpy(lbuf,head,sizeof lbuf - 1); for (s = lbuf; *s; s++) { if (isalpha(*s) && isupper(*s)) *s = tolower(*s); /* convert to lower case */ } for (i = 0; i < sf_num_extra_headers; i++) { if (strEQ(sf_extra_headers[i],lbuf)) return i; } return -1; } /* adds the header to the list of known extra headers if it is not already * known. */ void sf_add_extra_header(head) char* head; /* new header name, (without ':' character) */ { static char lbuf[LBUFLEN]; /* ick. */ int len; char* colonptr; /* points to ':' character */ char* s; char* s2; /* check to see if it's already known */ /* first see if it is a known system header */ safecpy(lbuf,head,sizeof lbuf - 2); len = strlen(lbuf); lbuf[len] = ':'; lbuf[len+1] = '\0'; colonptr = lbuf+len; if (set_line_type(lbuf,colonptr) != SOME_LINE) return; /* known types should be interpreted in normal way */ /* then check to see if it's a known extra header */ if (sf_check_extra_headers(head) >= 0) return; sf_num_extra_headers++; sf_extra_headers = (char**)saferealloc((char*)sf_extra_headers, sf_num_extra_headers * sizeof (char*)); s = savestr(head); for (s2 = s; *s2; s2++) { if (isalpha(*s2) && isupper(*s2)) *s2 = tolower(*s2); /* convert to lower case */ } sf_extra_headers[sf_num_extra_headers-1] = s; } char* sf_get_extra_header(art,hnum) ART_NUM art; /* article number to check */ int hnum; /* header number: offset into sf_extra_headers */ { char* s; char* head; /* header text */ int len; /* length of header */ static char lbuf[LBUFLEN]; parseheader(art); /* fast if already parsed */ head = sf_extra_headers[hnum]; len = strlen(head); for (s = headbuf; s && *s && *s != '\n'; s++) { if (strncaseEQ(head,s,len)) { s = index(s,':'); if (!s) return nullstr; s++; /* skip the colon */ while (*s == ' ' || *s == '\t') s++; if (!*s) return nullstr; head = s; /* now point to start of new text */ s = index(s,'\n'); if (!s) return nullstr; *s = '\0'; safecpy(lbuf,head,sizeof lbuf - 1); *s = '\n'; return lbuf; } s = index(s,'\n'); /* '\n' will be skipped on loop increment */ } return nullstr; } /* move to util.c ? */ /* Returns TRUE if text pointed to by s is a text representation of * the number 0. Used for error checking. * Note: does not check for trailing garbage ("+00kjsdfk" returns TRUE). */ bool is_text_zero(s) char* s; { return *s == '0' || ((*s == '+' || *s == '-') && s[1]=='0'); } /* keep this one outside the functions because it is shared */ static char sf_file[LBUFLEN]; /* filenames of type a/b/c/foo.bar.misc for group foo.bar.misc */ char* sf_get_filename(level) int level; { char* s; strcpy(sf_file,filexp(getval("SCOREDIR",DEFAULT_SCOREDIR))); strcat(sf_file,"/"); #ifdef SHORTSCORENAMES strcat(sf_file,filexp("%C")); s1 = rindex(sf_file,'/'); /* find last slash in filename */ if (!level) *s1 = '\0'; /* cut off slash for global */ i = level; while (i--) { *s1++ = '/'; while (*s1 != '.' && *s1 != '\0') s1++; if (*s1 == '\0' && i > 0) /* not enough levels exist */ return NULL; /* get_file2 wouldn't work either... */ *s1 = '\0'; } strcat(sf_file,"/SCORE"); #else /* !SHORTSCORENAMES */ if (!level) { /* allow environment variable later... */ strcat(sf_file,"global"); } else { strcat(sf_file,filexp("%C")); s = rindex(sf_file,'/'); /* maybe redo this logic later... */ while (level--) { if (*s == '\0') /* no more name to match */ return NULL; while (*s && *s != '.') s++; if (*s && level) s++; } *s = '\0'; /* cut end of score file */ } #endif /* SHORTSCORENAMES */ return sf_file; } /* given a string, if no slashes prepends SCOREDIR env. variable */ char* sf_cmd_fname(s) char* s; { static char lbuf[LBUFLEN]; char* s1; s1 = index(s,'/'); if (s1) return s; /* no slashes in this filename */ strcpy(lbuf,getval("SCOREDIR",DEFAULT_SCOREDIR)); strcat(lbuf,"/"); strcat(lbuf,s); return lbuf; } /* returns TRUE if good command, FALSE otherwise */ bool sf_do_command(cmd,check) char* cmd; /* text of command */ bool_int check; /* if TRUE, just check, don't execute */ { char* s; int i; char ch; if (strnEQ(cmd,"killthreshold",13)) { /* skip whitespace and = sign */ for (s = cmd+13; *s && (*s == ' ' || *s == '\t' || *s == '='); s++) ; /* make **sure** that there is a number here */ i = atoi(s); if (i == 0) /* it might not be a number */ if (!is_text_zero(s)) { printf("\nBad killthreshold: %s",cmd); return FALSE; /* continue looping */ } if (check) return TRUE; sf_grow(); sf_entries[sf_num_entries-1].head_type = SF_KILLTHRESHOLD; sf_entries[sf_num_entries-1].score = i; return TRUE; } if (strnEQ(cmd,"savescores",10)) { /* skip whitespace and = sign */ for (s = cmd+10; *s && (*s == ' ' || *s == '\t' || *s == '='); s++) ; if (strnEQ(s,"off",3)) { if (!check) sc_savescores = FALSE; return TRUE; } if (*s) { /* there is some argument */ if (check) return TRUE; sc_savescores = TRUE; return TRUE; } printf("Bad savescores command: |%s|\n",cmd) FLUSH; return FALSE; } if (strnEQ(cmd,"newauthor",9)) { /* skip whitespace and = sign */ for (s = cmd+9; *s && (*s == ' ' || *s == '\t' || *s == '='); s++) ; /* make **sure** that there is a number here */ i = atoi(s); if (i == 0) /* it might not be a number */ if (!is_text_zero(s)) { printf("\nBad newauthor: %s",cmd); return FALSE; /* continue looping */ } if (check) return TRUE; sf_grow(); sf_entries[sf_num_entries-1].head_type = SF_NEWAUTHOR; sf_entries[sf_num_entries-1].score = i; return TRUE; } if (strnEQ(cmd,"include",7)) { if (check) return TRUE; s = cmd+7; while (*s == ' ' || *s == '\t') s++; /* skip whitespace */ if (!*s) { printf("Bad include command (missing filename)\n"); return FALSE; } sf_do_file(filexp(sf_cmd_fname(s))); return TRUE; } if (strnEQ(cmd,"exclude",7)) { if (check) return TRUE; s = cmd+7; while (*s == ' ' || *s == '\t') s++; /* skip whitespace */ if (!*s) { printf("Bad exclude command (missing filename)\n"); return FALSE; } sf_exclude_file(filexp(sf_cmd_fname(s))); return TRUE; } if (strnEQ(cmd,"header",6)) { char* s2; s = cmd+7; while (*s == ' ' || *s == '\t') s++; /* skip whitespace */ for (s2 = s; *s2 && *s2 != ':'; s2++) ; if (!s2) { printf("\nBad header command (missing :)\n%s\n",cmd) FLUSH; return FALSE; } if (check) return TRUE; *s2 = '\0'; sf_add_extra_header(s); *s2 = ':'; return TRUE; } if (strnEQ(cmd,"begin",5)) { s = cmd+6; while (*s == ' ' || *s == '\t') s++; /* skip whitespace */ if (strnEQ(s,"score",5)) { /* do something useful later */ return TRUE; } return TRUE; } if (strnEQ(cmd,"reply",5)) { /* skip whitespace and = sign */ for (s = cmd+5; *s && (*s == ' ' || *s == '\t' || *s == '='); s++) ; /* make **sure** that there is a number here */ i = atoi(s); if (i == 0) /* it might not be a number */ if (!is_text_zero(s)) { printf("\nBad reply command: %s\n",cmd); return FALSE; /* continue looping */ } if (check) return TRUE; sf_grow(); sf_entries[sf_num_entries-1].head_type = SF_REPLY; sf_entries[sf_num_entries-1].score = i; return TRUE; } if (strnEQ(cmd,"file",4)) { if (check) return TRUE; s = cmd+4; while (*s == ' ' || *s == '\t') s++; /* skip whitespace */ if (!*s) { printf("Bad file command (missing parameters)\n"); return FALSE; } ch = *s++; while ((*s == ' ') || (*s == '\t')) s++; /* skip whitespace */ if (!*s) { printf("Bad file command (missing parameters)\n"); return FALSE; } if (sf_abbr[(int)ch]) free(sf_abbr[(int)ch]); sf_abbr[(int)ch] = savestr(sf_cmd_fname(s)); return TRUE; } if (strnEQ(cmd,"end",3)) { s = cmd+4; while (*s == ' ' || *s == '\t') s++; /* skip whitespace */ if (strnEQ(s,"score",5)) { /* do something useful later */ return TRUE; } return TRUE; } if (strnEQ(cmd,"newsclip",8)) { printf("Newsclip is no longer supported.\n") FLUSH; return FALSE; } /* no command matched */ printf("Unknown command: |%s|\n",cmd) FLUSH; return FALSE; } COMPEX* sf_compex INIT(NULL); char* sf_freeform(start1,end1) char* start1; /* points to first character of keyword */ char* end1; /* points to last character of keyword */ { char* s; bool error; char ch; error = FALSE; /* be optimistic :-) */ /* cases are # of letters in keyword */ switch (end1-start1+1) { case 7: if (strnEQ(start1,"pattern",7)) { sf_pattern_status = TRUE; break; } error = TRUE; break; case 4: #ifdef UNDEF /* here is an example of a hypothetical freeform key with an argument */ if (strnEQ(start1,"date",4)) { char* s1; int datenum; /* skip whitespace and = sign */ for (s = end1+1; *s && (*s == ' ' || *s == '\t'); s++) ; if (!*s) { /* ran out of line */ printf("freeform: date keyword: ran out of input\n"); return s; } datenum = atoi(s); printf("Date: %d\n",datenum) FLUSH; while (isdigit(*s)) s++; /* skip datenum */ end1 = s; /* end of key data */ break; } #endif error = TRUE; break; default: error = TRUE; break; } if (error) { s = end1+1; ch = *s; *s = '\0'; printf("Scorefile freeform: unknown key: |%s|\n",start1) FLUSH; *s = ch; return NULL; /* error indicated */ } /* no error, so skip whitespace at end of key */ for (s = end1+1; *s && (*s == ' ' || *s == '\t'); s++) ; return s; } bool sf_do_line(line,check) char* line; bool_int check; /* if TRUE, just check the line, don't act. */ { char ch; char* s; char* s2; int i,j; if (!line || !*line) return TRUE; /* very empty line */ s = line + strlen(line) - 1; if (*s == '\n') *s = '\0'; /* kill the newline */ ch = line[0]; if (ch == '#') /* comment */ return TRUE; /* reset any per-line bitflags */ sf_pattern_status = FALSE; if (isalpha(ch)) /* command line */ return sf_do_command(line,check); /* skip whitespace */ for (s = line; *s && (*s == ' ' || *s == '\t'); s++) ; if (!*s || *s == '#') return TRUE; /* line was whitespace or comment after whitespace */ /* convert line to lowercase (make optional later?) */ for (s2 = s; *s2 != '\0'; s2++) { if (isupper(*s2)) *s2 = tolower(*s2); /* convert to lower case */ } i = atoi(s); if (i == 0) { /* it might not be a number */ if (!is_text_zero(s)) { printf("\nBad scorefile line:\n|%s|\n",s); return FALSE; } } /* add the line as a scoring entry */ while (isdigit(*s) || *s == '+' || *s == '-' || *s == ' ' || *s == '\t') s++; /* skip score */ while (TRUE) { for (s2 = s; *s2 && !(*s2 == ' ' || *s2 == '\t'); s2++) ; s2--; if (*s2 == ':') /* did header */ break; /* go to set header routine */ s = sf_freeform(s,s2); if (!s || !*s) { /* used up all the line's text, or error */ printf("Scorefile entry error error (freeform parse). "); printf("Line was:\n|%s|\n",line) FLUSH; return FALSE; /* error */ } s2 = s; } /* while */ /* s is start of header name, s2 points to the ':' character */ j = set_line_type(s,s2); if (j == SOME_LINE) { *s2 = '\0'; j = sf_check_extra_headers(s); *s2 = ':'; if (j >= 0) j += HEAD_LAST; else { printf("Unknown score header type. Line follows:\n|%s|\n",line); return FALSE; } } /* skip whitespace */ for (s = ++s2; *s && (*s == ' ' || *s == '\t'); s++) ; if (!*s) { /* no pattern */ printf("Empty score pattern. Line follows:\n|%s|\n",line) FLUSH; return FALSE; } if (check) return TRUE; /* limits of check */ sf_grow(); /* acutally make an entry */ sf_entries[sf_num_entries-1].head_type = j; sf_entries[sf_num_entries-1].score = i; if (sf_pattern_status) { /* in pattern matching mode */ sf_entries[sf_num_entries-1].flags |= 1; sf_entries[sf_num_entries-1].str1 = mp_savestr(s,MP_SCORE1); sf_compex = (COMPEX*)safemalloc(sizeof (COMPEX)); init_compex(sf_compex); /* compile arguments: */ /* 1st is COMPEX to store compiled regex in */ /* 2nd is search string */ /* 3rd should be TRUE if the search string is a regex */ /* 4th is TRUE for case-insensitivity */ s2 = compile(sf_compex,s,TRUE,TRUE); if (s2 != NULL) { printf("Bad pattern : |%s|\n",s) FLUSH; printf("Compex returns: |%s|\n",s2) FLUSH; free_compex(sf_compex); free(sf_compex); sf_entries[sf_num_entries-1].compex = NULL; return FALSE; } else sf_entries[sf_num_entries-1].compex = sf_compex; } else { sf_entries[sf_num_entries-1].flags &= 0xfe; sf_entries[sf_num_entries-1].str2 = NULL; /* Note: consider allowing * wildcard on other header filenames */ if (j == FROM_LINE) { /* may have * wildcard */ if ((s2 = index(s,'*')) != NULL) { sf_entries[sf_num_entries-1].str2 = mp_savestr(s2+1,MP_SCORE1); *s2 = '\0'; } } sf_entries[sf_num_entries-1].str1 = mp_savestr(s,MP_SCORE1); } return TRUE; } void sf_do_file(fname) char* fname; { char* s; int sf_fp; int i; char* safefilename; #ifdef SCOREFILE_CACHE sf_fp = sf_open_file(fname); if (sf_fp < 0) return; #else fp = fopen(fname,"r"); if (!fp) return; #endif sf_file_level++; if (sf_verbose) { for (i = 1; i < sf_file_level; i++) printf("."); /* maybe later putchar... */ printf("Score file: %s\n",fname) FLUSH; } safefilename = savestr(fname); /* add end marker to scoring array */ sf_grow(); sf_entries[sf_num_entries-1].head_type = SF_FILE_MARK_START; /* file_level is 1 to n */ sf_entries[sf_num_entries-1].score = sf_file_level; sf_entries[sf_num_entries-1].str2 = NULL; sf_entries[sf_num_entries-1].str1 = savestr(safefilename); #ifdef SCOREFILE_CACHE while ((s = sf_file_getline(sf_fp)) != NULL) { strcpy(sf_buf,s); s = sf_buf; #else while ((s = fgets(sf_buf,1020,fp)) != NULL) { /* consider buffer size */ #endif (void)sf_do_line(s,FALSE); } #ifndef SCOREFILE_CACHE fclose(fp); #endif /* add end marker to scoring array */ sf_grow(); sf_entries[sf_num_entries-1].head_type = SF_FILE_MARK_END; /* file_level is 1 to n */ sf_entries[sf_num_entries-1].score = sf_file_level; sf_entries[sf_num_entries-1].str2 = NULL; sf_entries[sf_num_entries-1].str1 = savestr(safefilename); free(safefilename); sf_file_level--; } int score_match(str,ind) char* str; /* string to match on */ int ind; /* index into sf_entries */ { char* s1; char* s2; char* s3; s1 = sf_entries[ind].str1; s2 = sf_entries[ind].str2; if (sf_entries[ind].flags & 1) { /* pattern style match */ if (sf_entries[ind].compex != NULL) { /* we have a good pattern */ s2 = execute(sf_entries[ind].compex,str); if (s2 != NULL) return TRUE; } return FALSE; } /* default case */ if ((s3 = STRSTR(str,s1)) != NULL && (!s2 || STRSTR(s3+strlen(s1),s2))) return TRUE; return FALSE; } int sf_score(a) ART_NUM a; { int sum,i,j; int h; /* header type */ char* s; /* misc */ bool old_untrim; /* old value of untrim_cache */ if (is_unavailable(a)) return LOWSCORE; /* unavailable arts get low negative score. */ /* if there are no score entries, then the answer is real easy and quick */ if (sf_num_entries == 0) return 0; old_untrim = untrim_cache; untrim_cache = TRUE; sc_scoring = TRUE; /* loop prevention */ sum = 0; /* parse the header now if there are extra headers */ /* (This could save disk accesses.) */ if (sf_has_extra_headers) parseheader(a); for (i = 0; i < sf_num_entries; i++) { h = sf_entries[i].head_type; if (h <= 0) /* don't use command headers for scoring */ continue; /* the outer for loop */ /* if this head_type has been done before, this entry has already been done */ if (sf_entries[i].flags & 2) { /* rule has been applied */ sf_entries[i].flags &= 0xfd; /* turn off flag */ continue; /* ...with the next rule */ } /* sf_get_line will return ptr to buffer (already lowercased string) */ s = sf_get_line(a,h); if (!s || !*s) /* no such line for the article */ continue; /* with the sf_entries. */ /* do the matches for this header */ for (j = i; j < sf_num_entries; j++) { /* see if there is a match */ if (h == sf_entries[j].head_type) { if (j != i) { /* set flag only for future rules */ sf_entries[j].flags |= 2; /* rule has been applied. */ } if (score_match(s,j)) { sum = sum + sf_entries[j].score; if (h == FROM_LINE) article_ptr(a)->scoreflags |= SFLAG_AUTHOR; if (sf_score_verbose) sf_print_match(j); } } } } if (newauthor_active && !(article_ptr(a)->scoreflags & SFLAG_AUTHOR)) { sum = sum+newauthor; /* add new author bonus */ if (sf_score_verbose) { printf("New Author: %d\n",newauthor) FLUSH; /* consider: print which file the bonus came from */ } } if (reply_active) { /* should be in cache if a rule above used the subject */ s = fetchcache(a, SUBJ_LINE, TRUE); /* later: consider other possible reply forms (threading?) */ if (s && subject_has_Re(s,(char**)NULL)) { sum = sum+reply_score; if (sf_score_verbose) { printf("Reply: %d\n",reply_score); /* consider: print which file the bonus came from */ } } } untrim_cache = old_untrim; sc_scoring = FALSE; return sum; } /* returns changed score line or NULL if no changes */ char* sf_missing_score(line) char* line; { static char lbuf[LBUFLEN]; int i; char* s; /* save line since it is probably pointing at (the TRN-global) buf */ s = savestr(line); printf("Possibly missing score.\n\ Type a score now or delete the colon to abort this entry:\n") FLUSH; buf[0] = ':'; buf[1] = FINISHCMD; i = finish_command(TRUE); /* print the CR */ if (!i) { /* there was no score */ free(s); return NULL; } strcpy(lbuf,buf+1); i = strlen(lbuf); lbuf[i] = ' '; lbuf[i+1] = '\0'; strcat(lbuf,s); free(s); return lbuf; } /* Interprets the '\"' command for creating new score entries online */ /* consider using some external buffer rather than the 2 internal ones */ void sf_append(line) char* line; { char* scoreline; /* full line to add to scorefile */ char* scoretext; /* text after the score# */ char filechar; /* filename character from line */ char* filename; /* expanded filename */ static char filebuf[LBUFLEN]; FILE* fp; char ch; /* misc */ char* s; if (!line) return; /* do nothing with empty string */ filechar = *line; /* ch is file abbreviation */ if (filechar == '?') { /* list known file abbreviations */ int i; printf("List of abbreviation/file pairs\n") ; for (i = 0; i < 256; i++) if (sf_abbr[i]) printf("%c %s\n",(char)i,sf_abbr[i]) FLUSH; printf("\" [The current newsgroup's score file]\n") FLUSH; printf("* [The global score file]\n") FLUSH; return; } /* skip whitespace after filechar */ scoreline = line+1; while (*scoreline == ' ' || *scoreline == '\t') scoreline++; ch = *scoreline; /* first non-whitespace after filechar */ /* If the scorefile line does not begin with a number, and is not a valid command, request a score */ if (!isdigit(ch) && ch != '+' && ch != '-' && ch != ':' && ch != '!' && ch != '#') { if (!sf_do_line(scoreline,TRUE)) { /* just checking */ scoreline = sf_missing_score(scoreline); if (!scoreline) { /* no score typed */ printf("Score entry aborted.\n") FLUSH; return; } } } /* scoretext = first non-whitespace after score# */ for (scoretext = scoreline; isdigit(*scoretext) || *scoretext == '+' || *scoretext == '-' || *scoretext == ' ' || *scoretext == '\t'; scoretext++) { ; } /* special one-character shortcuts */ if (*scoretext && scoretext[1] == '\0') { static char lbuf[LBUFLEN]; switch(*scoretext) { case 'F': /* domain-shortened FROM line */ strcpy(lbuf,scoreline); lbuf[strlen(lbuf)-1] = '\0'; strcat(lbuf,filexp("from: %y")); scoreline = lbuf; break; case 'S': /* current subject */ strcpy(lbuf,scoreline); s = fetchcache(art,SUBJ_LINE,TRUE); if (!s || !*s) { printf("No subject: score entry aborted.\n"); return; } if (s[0] == 'R' && s[1] == 'e' && s[2] == ':' && s[3] == ' ') s += 4; /* change this next line if LBUFLEN changes */ sprintf(lbuf+(strlen(lbuf)-1),"subject: %.900s",s); scoreline = lbuf; break; default: printf("\nBad scorefile line: |%s| (not added)\n", line) FLUSH; return; } printf("%s\n",scoreline) FLUSH; } /* test the scoring line unless filechar is '!' (meaning do it now) */ if (!sf_do_line(scoreline,filechar!='!')) { printf("Bad score line (ignored)\n") FLUSH; return; } if (filechar == '!') return; /* don't actually append to file */ if (filechar == '"') { /* do local group */ /* Note: should probably be changed to use sf_ file functions */ strcpy(filebuf,getval("SCOREDIR",DEFAULT_SCOREDIR)); #ifdef SHORTSCORENAMES strcat(filebuf,"/%c/SCORE"); #else strcat(filebuf,"/%C"); #endif filename = filebuf; } else if (filechar == '*') { /* do global scorefile */ /* Note: should probably be changed to use sf_ file functions */ strcpy(filebuf,getval("SCOREDIR",DEFAULT_SCOREDIR)); strcat(filebuf,"/global"); filename = filebuf; } else if (!(filename = sf_abbr[(int)filechar])) { printf("\nBad file abbreviation: %c\n",filechar) FLUSH; return; } filename = filexp(sf_cmd_fname(filename)); /* allow shortcuts */ /* make sure directory exists... */ makedir(filename,MD_FILE); #ifdef SCOREFILE_CACHE sf_file_clear(); #endif if ((fp = fopen(filename,"a")) != NULL) { /* open (or create) for append */ fprintf(fp,"%s\n",scoreline); fclose(fp); } else /* unsuccessful in opening file */ printf("\nCould not open (for append) file %s\n",filename); return; } /* returns a lowercased copy of the header line type h in private buffer */ char* sf_get_line(a,h) ART_NUM a; int h; { static char sf_getline[LBUFLEN]; char* s; if (h <= SOME_LINE) { printf("sf_get_line(%d,%d): bad header type\n",(int)a,h) FLUSH; printf("(Internal error: header number too low)\n") FLUSH; *sf_getline = '\0'; return sf_getline; } if (h >= HEAD_LAST) { if (h-HEAD_LAST < sf_num_extra_headers) s = sf_get_extra_header(a,h-HEAD_LAST); else { printf("sf_get_line(%d,%d): bad header type\n",(int)a,h) FLUSH; printf("(Internal error: header number too high)\n") FLUSH; *sf_getline = '\0'; return sf_getline; } } else if (h == SUBJ_LINE) s = fetchcache(a,h,TRUE); /* get compressed copy */ else s = prefetchlines(a,h,FALSE); /* don't make a copy */ if (!s) *sf_getline = '\0'; else safecpy(sf_getline,s,sizeof sf_getline - 1); for (s = sf_getline; *s; s++) if (isupper(*s)) *s = tolower(*s); *s = tolower(*s); return sf_getline; } /* given an index into sf_entries, print information about that index */ void sf_print_match(indx) int indx; { int i,j,k; int level,tmplevel; /* level is initialized iff used */ char* head_name; char* pattern; for (i = indx; i >= 0; i--) { j = sf_entries[i].head_type; if (j == SF_FILE_MARK_START) /* found immediate inclusion. */ break; if (j == SF_FILE_MARK_END) { /* found included file, skip */ tmplevel = sf_entries[i].score; for (k = i; k >= 0; k--) { if (sf_entries[k].head_type == SF_FILE_MARK_START && sf_entries[k].score == tmplevel) break; /* inner for loop */ } i = k; /* will be decremented again */ } } if (i >= 0) level = sf_entries[i].score; /* print the file markers. */ for ( ; i >= 0; i--) { if (sf_entries[i].head_type == SF_FILE_MARK_START && sf_entries[i].score <= level) { level--; /* go out... */ for (k = 0; k < level; k++) printf("."); /* make putchar later? */ printf("From file: %s\n",sf_entries[i].str1); if (level == 0) /* top level */ break; /* out of the big for loop */ } } if (sf_entries[indx].flags & 1) /* regex type */ pattern = "pattern "; else pattern = ""; if (sf_entries[indx].head_type >= HEAD_LAST) head_name = sf_extra_headers[sf_entries[indx].head_type-HEAD_LAST]; else head_name = htype[sf_entries[indx].head_type].name; printf("%d %s%s: %s", sf_entries[indx].score,pattern,head_name, sf_entries[indx].str1); if (sf_entries[indx].str2) printf("*%s",sf_entries[indx].str2); printf("\n"); } void sf_exclude_file(fname) char* fname; { int start,end; int newnum; SF_ENTRY* tmp_entries; for (start = 0; start < sf_num_entries; start++) if (sf_entries[start].head_type == SF_FILE_MARK_START && strEQ(sf_entries[start].str1,fname)) break; if (start == sf_num_entries) { printf("Exclude: file |%s| was not included\n",fname) FLUSH; return; } for (end = start+1; end < sf_num_entries; end++) if (sf_entries[end].head_type==SF_FILE_MARK_END && strEQ(sf_entries[end].str1,fname)) break; if (end == sf_num_entries) { printf("Exclude: file |%s| is incomplete at exclusion command\n", fname) FLUSH; /* insert more explanation later? */ return; } newnum = sf_num_entries-(end-start)-1; #ifdef UNDEF /* Deal with exclusion of all scorefile entries. * This cannot happen since the exclusion command has to be within a * file. Code kept in case online exclusions allowed later. */ if (newnum==0) { sf_num_entries = 0; free(sf_entries); sf_entries = NULL; return; } #endif tmp_entries = (SF_ENTRY*)safemalloc(newnum*sizeof(SF_ENTRY)); /* copy the parts into tmp_entries */ if (start > 0) bcopy((char*)sf_entries,(char*)tmp_entries,start * sizeof (SF_ENTRY)); if (end < sf_num_entries-1) bcopy((char*)(sf_entries+end+1), (char*)(tmp_entries+start), (sf_num_entries-end-1) * sizeof (SF_ENTRY)); free(sf_entries); sf_entries = tmp_entries; sf_num_entries = newnum; if (sf_verbose) printf("Excluded file: %s\n",fname) FLUSH; } void sf_edit_file(filespec) char* filespec; /* file abbrev. or name */ { char filebuf[LBUFLEN]; /* clean up buffers */ char filechar; /* which file to do? */ char* fname_noexpand; /* non-expanded filename */ if (!filespec || !*filespec) return; /* empty, do nothing (error later?) */ filechar = *filespec; /* if more than one character use as filename */ if (filespec[1]) strcpy(filebuf,filespec); else if (filechar == '"') { /* edit local group */ /* Note: should probably be changed to use sf_ file functions */ strcpy(filebuf,getval("SCOREDIR",DEFAULT_SCOREDIR)); #ifdef SHORTSCORENAMES strcat(filebuf,"/%c/SCORE"); #else strcat(filebuf,"/%C"); #endif } else if (filechar == '*') { /* edit global scorefile */ /* Note: should probably be changed to use sf_ file functions */ strcpy(filebuf,getval("SCOREDIR",DEFAULT_SCOREDIR)); strcat(filebuf,"/global"); } else { /* abbreviation */ if (!sf_abbr[(int)filechar]) { printf("\nBad file abbreviation: %c\n",filechar) FLUSH; return; } strcpy(filebuf,sf_abbr[(int)filechar]); } fname_noexpand = sf_cmd_fname(filebuf); strcpy(filebuf,filexp(fname_noexpand)); /* make sure directory exists... */ if (makedir(filebuf,MD_FILE) == 0) { (void)edit_file(fname_noexpand); #ifdef SCOREFILE_CACHE sf_file_clear(); #endif } else printf("Can't make %s\n",filebuf) FLUSH; } /* returns file number */ /* if file number is negative, the file does not exist or cannot be opened */ #ifdef SCOREFILE_CACHE static int sf_open_file(name) char* name; { FILE* fp; char* temp_name; char* s; int i; if (!name || !*name) return 0; /* unable to open */ for (i = 0; i < sf_num_files; i++) if (strEQ(sf_files[i].fname,name)) { if (sf_files[i].num_lines < 0) /* nonexistent */ return -1; /* no such file */ sf_files[i].line_on = 0; return i; } sf_num_files++; sf_files = (SF_FILE*)saferealloc((char*)sf_files, sf_num_files * sizeof (SF_FILE)); sf_files[i].fname = savestr(name); sf_files[i].num_lines = 0; sf_files[i].num_alloc = 0; sf_files[i].line_on = 0; sf_files[i].lines = NULL; temp_name = NULL; if (strncaseEQ(name,"URL:",4)) { #ifdef USEURL char lbuf[1024]; safecpy(lbuf,name,sizeof lbuf - 4); name = lbuf; temp_name = temp_filename(); if (!url_get(name+4,temp_name)) name = NULL; else name = temp_name; #else printf("\nThis copy of strn does not have URL support.\n") FLUSH; name = NULL; #endif } if (!name) { sf_files[i].num_lines = -1; return -1; } fp = fopen(name,"r"); if (!fp) { sf_files[i].num_lines = -1; return -1; } while ((s = fgets(sf_buf,LBUFLEN-4,fp)) != NULL) { if (sf_files[i].num_lines >= sf_files[i].num_alloc) { sf_files[i].num_alloc += 100; sf_files[i].lines = (char**)saferealloc((char*)sf_files[i].lines, sf_files[i].num_alloc*sizeof(char**)); } /* CAA: I kind of like the next line in a twisted sort of way. */ sf_files[i].lines[sf_files[i].num_lines++] = mp_savestr(s,MP_SCORE2); } fclose(fp); if (temp_name) UNLINK(temp_name); return i; } #endif /* SCOREFILE_CACHE */ #ifdef SCOREFILE_CACHE static void sf_file_clear() { int i; for (i = 0; i < sf_num_files; i++) { if (sf_files[i].fname) free(sf_files[i].fname); if (sf_files[i].num_lines > 0) { /* memory pool takes care of freeing line contents */ free(sf_files[i].lines); } } mp_free(MP_SCORE2); if (sf_files) free(sf_files); sf_files = (SF_FILE*)NULL; sf_num_files = 0; } #endif /* SCOREFILE_CACHE */ #ifdef SCOREFILE_CACHE static char* sf_file_getline(fnum) int fnum; { if (fnum < 0 || fnum >= sf_num_files) return NULL; if (sf_files[fnum].line_on >= sf_files[fnum].num_lines) return NULL; /* past end of file, or empty file */ /* below: one of the more twisted lines of my career (:-) */ return sf_files[fnum].lines[sf_files[fnum].line_on++]; } #endif /* SCOREFILE_CACHE */ #endif /* SCORE */ trn-4.0-test77/scorefile.h0000644000000000000000000000445607113133016014141 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* scorefile.h * */ #define DEFAULT_SCOREDIR "%+/scores" struct sf_entry { int head_type; /* header # (see head.h) */ int score; /* score change */ char* str1; /* first string part */ char* str2; /* second string part */ COMPEX* compex; /* regular expression ptr */ char flags; /* 1: regex is valid * 2: rule has been applied to the current article. * 4: use faster rule checking (later) */ }; /* note that negative header #s are used to indicate special entries... */ EXT int sf_num_entries INIT(0); /* # of entries */ EXT SF_ENTRY* sf_entries; /* array of entries */ #ifdef SCOREFILE_CACHE /* for cached score rules */ struct sf_file { char* fname; int num_lines; int num_alloc; long line_on; char** lines; }; EXT SF_FILE *sf_files INIT((SF_FILE*)NULL); EXT int sf_num_files INIT(0); #endif EXT char **sf_abbr; /* abbreviations */ /* when true, the scoring routine prints lots of info... */ EXT int sf_score_verbose INIT(FALSE); EXT bool sf_verbose INIT(TRUE); /* if true print more stuff while loading */ /* if TRUE, only header types that are cached are scored... */ EXT bool cached_rescore INIT(FALSE); /* if TRUE, newauthor is active */ EXT bool newauthor_active INIT(FALSE); /* bonus score given to a new (unscored) author */ EXT int newauthor INIT(0); /* if TRUE, reply_score is active */ EXT bool reply_active INIT(FALSE); /* score amount added to an article reply */ EXT int reply_score INIT(0); /* should we match by pattern? */ EXT int sf_pattern_status INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sf_init _((void)); void sf_clean _((void)); void sf_grow _((void)); int sf_check_extra_headers _((char*)); void sf_add_extra_header _((char*)); char* sf_get_extra_header _((ART_NUM,int)); bool is_text_zero _((char*)); char* sf_get_filename _((int)); char* sf_cmd_fname _((char*)); bool sf_do_command _((char*,bool_int)); char* sf_freeform _((char*,char*)); bool sf_do_line _((char*,bool_int)); void sf_do_file _((char*)); int score_match _((char*,int)); int sf_score _((ART_NUM)); char* sf_missing_score _((char*)); void sf_append _((char*)); char* sf_get_line _((ART_NUM,int)); void sf_print_match _((int)); void sf_exclude_file _((char*)); void sf_edit_file _((char*)); trn-4.0-test77/scorefile.ih0000644000000000000000000000044707113133016014306 0ustar rootroot/* scorefile.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef SCOREFILE_CACHE static int sf_open_file _((char*)); static void sf_file_clear _((void)); static char* sf_file_getline _((int)); #endif trn-4.0-test77/scoresave.c0000644000000000000000000002156307113133016014151 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* scoresave.c * * Saving/restoring scores from a file. */ #include "EXTERN.h" #include "common.h" #ifdef SCORE #include "list.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "intrp.h" /* for filexp */ #include "ng.h" /* art */ #include "ngdata.h" #include "util.h" /* several */ #include "util2.h" #include "env.h" /* getval */ #ifdef SCAN #include "scan.h" #endif #ifdef SCAN_ART #include "scanart.h" #include "samain.h" #include "samisc.h" #endif #include "score.h" #include "INTERN.h" #include "scoresave.h" static int num_lines = 0; static int lines_alloc = 0; static char** lines = NULL; static char lbuf[LBUFLEN]; static char lbuf2[LBUFLEN]; /* what's another buffer between... */ static int loaded; static int used; static int saved; static ART_NUM last; void sc_sv_add(str) char* str; { if (num_lines == lines_alloc) { lines_alloc += 100; lines = (char**)saferealloc((char*)lines,lines_alloc * sizeof (char*)); } lines[num_lines] = savestr(str); num_lines++; } void sc_sv_delgroup(gname) char* gname; { char* s; int i; int start; for (i = 0; i < num_lines; i++) { s = lines[i]; if (s && *s == '!' && strEQ(gname,s+1)) break; } if (i == num_lines) return; /* group not found */ start = i; free(lines[i]); lines[i] = NULL; for (i++; i < num_lines; i++) { s = lines[i]; if (s && *s == '!') break; if (s) { free(s); lines[i] = NULL; } } /* copy into the hole (if any) */ for ( ; i < num_lines; i++) lines[start++] = lines[i]; num_lines -= (i-start); } /* get the file containing scores into memory */ void sc_sv_getfile() { char* s; FILE* fp; num_lines = lines_alloc = 0; lines = NULL; s = getval("SAVESCOREFILE","%+/savedscores"); fp = fopen(filexp(s),"r"); if (!fp) { #if 0 printf("Could not open score save file for reading.\n") FLUSH; #endif return; } while (fgets(lbuf,LBUFLEN-2,fp)) { lbuf[strlen(lbuf)-1] = '\0'; /* strip \n */ sc_sv_add(lbuf); } fclose(fp); } /* save the memory into the score file */ void sc_sv_savefile() { char* s; FILE* tmpfp; char* savename; int i; if (num_lines == 0) return; waiting = TRUE; /* don't interrupt */ s = getval("SAVESCOREFILE","%+/savedscores"); savename = savestr(filexp(s)); strcpy(lbuf,savename); strcat(lbuf,".tmp"); tmpfp = fopen(lbuf,"w"); if (!tmpfp) { #if 0 printf("Could not open score save temp file %s for writing.\n", lbuf) FLUSH; #endif free(savename); waiting = FALSE; return; } for (i = 0; i < num_lines; i++) { if (lines[i]) fprintf(tmpfp,"%s\n",lines[i]); if (ferror(tmpfp)) { fclose(tmpfp); free(savename); printf("\nWrite error in temporary save file %s\n",lbuf) FLUSH; printf("(keeping old saved scores)\n"); UNLINK(lbuf); waiting = FALSE; return; } } fclose(tmpfp); UNLINK(savename); RENAME(lbuf,savename); waiting = FALSE; } /* returns the next article number (after the last one used) */ ART_NUM sc_sv_use_line(line,a) char* line; ART_NUM a; /* art number to start with */ { char* s; char* p; char c1,c2; int score; int x; score = 0; /* get rid of warning */ s = line; if (!s) return a; while (*s) { switch(*s) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': /* negative starting digit */ p = s; c1 = *s; *s = '0' + ('J' - *s); /* convert to first digit */ s++; while (isdigit(*s)) s++; c2 = *s; *s = '\0'; score = 0 - atoi(p); *p = c1; *s = c2; loaded++; if (is_available(a) && article_unread(a)) { sc_set_score(a,score); used++; } a++; break; case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': /* positive starting digit */ p = s; c1 = *s; *s = '0' + (*s - 'J'); /* convert to first digit */ s++; while (isdigit(*s)) s++; c2 = *s; *s = '\0'; score = atoi(p); *p = c1; *s = c2; loaded++; if (is_available(a) && article_unread(a)) { sc_set_score(a,score); used++; } a++; break; case 'r': /* repeat */ s++; p = s; if (!isdigit(*s)) { /* simple case, just "r" */ x = 1; } else { s++; while (isdigit(*s)) s++; c1 = *s; *s = '\0'; x = atoi(p); *s = c1; } for ( ; x; x--) { loaded++; if (is_available(a) && article_unread(a)) { sc_set_score(a,score); used++; } a++; } break; case 's': /* skip */ s++; p = s; if (!isdigit(*s)) { /* simple case, just "s" */ a += 1; } else { s++; while (isdigit(*s)) s++; c1 = *s; *s = '\0'; x = atoi(p); *s = c1; a += x; } break; } /* switch */ } /* while */ return a; } ART_NUM sc_sv_make_line(a) ART_NUM a; { char* s; bool lastscore_valid = FALSE; int num_output = 0; int score,lastscore; int i; bool neg_flag; s = lbuf; *s++ = '.'; lastscore = 0; for (a = article_first(a); a <= lastart && num_output < 50; a = article_next(a)) { if (article_unread(a) && SCORED(a)) { if (last != a-1) { if (last == a-2) { *s++ = 's'; num_output++; } else { sprintf(s,"s%ld",(a-last)-1); s = lbuf + strlen(lbuf); num_output++; } } /* print article's score */ score = article_ptr(a)->score; /* check for repeating scores */ if (score == lastscore && lastscore_valid) { a = article_next(a); for (i = 1; a <= lastart && article_unread(a) && SCORED(a) && article_ptr(a)->score == score; i++) a = article_next(a); a = article_prev(a); /* prepare for the for loop increment */ if (i == 1) { *s++ = 'r'; /* repeat one */ num_output++; } else { sprintf(s,"r%d",i); /* repeat >one */ s = lbuf + strlen(lbuf); num_output++; } saved += i-1; } else { /* not a repeat */ i = score; if (i < 0) { neg_flag = TRUE; i = 0 - i; } else neg_flag = FALSE; sprintf(s,"%d",i); i = (*s - '0'); if (neg_flag) *s++ = 'J' - i; else *s++ = 'J' + i; s = lbuf + strlen(lbuf); num_output++; lastscore_valid = TRUE; } lastscore = score; last = a; saved++; } /* if */ } /* for */ *s = '\0'; sc_sv_add(lbuf); return a; } void sc_load_scores() { /* lots of cleanup needed here */ ART_NUM a = 0; char* s; char* gname; int i; int total,scored; bool verbose; sc_save_new = -1; /* just in case we exit early */ loaded = used = 0; sc_loaded_count = 0; /* verbosity is only really useful for debugging... */ verbose = 0; if (num_lines == 0) sc_sv_getfile(); gname = savestr(filexp("%C")); for (i = 0; i < num_lines; i++) { s = lines[i]; if (s && *s == '!' && strEQ(s+1,gname)) break; } if (i == num_lines) return; /* no scores loaded */ i++; if (verbose) { printf("\nLoading scores..."); fflush(stdout); } while (i < num_lines) { s = lines[i++]; if (!s) continue; switch (*s) { case ':': a = atoi(s+1); /* set the article # */ break; case '.': /* longer score line */ a = sc_sv_use_line(s+1,a); break; case '!': /* group of shared file */ i = num_lines; break; case 'v': /* version number */ break; /* not used now */ case '\0': /* empty string */ case '#': /* comment */ break; default: /* don't even try to deal with it */ return; } /* switch */ } /* while */ sc_loaded_count = loaded; a = firstart; #ifdef SCAN_ART if (sa_mode_read_elig) a = absfirst; #endif total = scored = 0; for (a = article_first(a); a <= lastart; a = article_next(a)) { if (!article_exists(a)) continue; if (!article_unread(a) #ifdef SCAN_ART && !sa_mode_read_elig #endif ) continue; total++; if (SCORED(a)) scored++; } /* for */ /* sloppy plurals (:-) */ if (verbose) printf("(%d/%d/%d scores loaded/used/unscored)\n", loaded,used,total-scored) FLUSH; sc_save_new = total-scored; #ifdef SCAN if (sa_initialized) s_top_ent = -1; /* reset top of page */ #endif } void sc_save_scores() { ART_NUM a; char* gname; saved = 0; last = 0; waiting = TRUE; /* DON'T interrupt */ gname = savestr(filexp("%C")); /* not being able to open is OK */ if (num_lines > 0) { sc_sv_delgroup(gname); /* delete old group */ } else { /* there was no old file */ sc_sv_add("#STRN saved score file."); sc_sv_add("v1.0"); } sprintf(lbuf2,"!%s",gname); /* add the header */ sc_sv_add(lbuf2); a = firstart; sprintf(lbuf2,":%ld",a); sc_sv_add(lbuf2); last = a-1; while (a <= lastart) a = sc_sv_make_line(a); waiting = FALSE; } #endif /* SCORE */ trn-4.0-test77/scoresave.h0000644000000000000000000000102607113133016014146 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* scoresave.h * */ EXT long sc_save_new INIT(0); /* new articles (unloaded) */ EXT int sc_loaded_count INIT(0); /* how many articles were loaded? */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sc_sv_add _((char*)); void sc_sv_delgroup _((char*)); void sc_sv_getfile _((void)); void sc_sv_savefile _((void)); ART_NUM sc_sv_use_line _((char*,ART_NUM)); ART_NUM sc_sv_make_line _((ART_NUM)); void sc_load_scores _((void)); void sc_save_scores _((void)); trn-4.0-test77/sdisp.c0000644000000000000000000001331211437640112013276 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sdisp.c * * display stuff */ #include "EXTERN.h" #include "common.h" #ifdef SCAN #include "final.h" /* assert() */ #include "hash.h" #include "cache.h" #include "ng.h" /* mailcall */ #include "term.h" #include "scan.h" #include "sorder.h" #include "smisc.h" #ifdef SCAN_ART #include "scanart.h" #include "samain.h" #include "sadisp.h" #endif #include "INTERN.h" #include "sdisp.h" void s_goxy(x,y) int x,y; { char* tgoto(); tputs(tgoto(tc_CM, x, y), 1, putchr); } /* Print a string with the placing of the page and mail status. * sample: "(mail)-MIDDLE-" * Good for most bottom status bars. */ void s_mail_and_place() { bool previous,next; #ifdef MAILCALL setmail(FALSE); /* another chance to check mail */ printf("%s",mailcall); #endif /* MAILCALL */ /* print page status wrt all entries */ previous = (0 != s_prev_elig(page_ents[0].entnum)); next = (0 != s_next_elig(page_ents[s_bot_ent].entnum)); if (previous && next) printf("-MIDDLE-"); /* middle of entries */ else if (next && !previous) printf("-TOP-"); else if (previous && !next) printf("-BOTTOM-"); else /* !previous && !next */ printf("-ALL-"); } void s_refresh_top() { home_cursor(); switch (s_cur_type) { #ifdef SCAN_ART case S_ART: sa_refresh_top(); break; #endif } s_ref_top = FALSE; } void s_refresh_bot() { /* if bottom bar exists, then it is at least one character high... */ s_goxy(0,tc_LINES-s_bot_lines); switch (s_cur_type) { #ifdef SCAN_ART case S_ART: sa_refresh_bot(); break; #endif } s_ref_bot = FALSE; } /* refresh both status and description */ void s_refresh_entzone() { int i; int start; /* starting page_arts index to refresh... */ if (s_ref_status < s_ref_desc) { /* refresh status characters up to (not including) desc_line */ for (i = s_ref_status; i <= s_bot_ent && i < s_ref_desc; i++) s_refresh_description(i); start = i; } else { for (i = s_ref_desc; i <= s_bot_ent && i < s_ref_status; i++) s_refresh_status(i); start = i; } for (i = start; i <= s_bot_ent; i++) s_ref_entry(i,i==start); /* clear to end of screen */ clear_rest(); /* now we need to redraw the bottom status line */ s_ref_bot = TRUE; s_ref_status = s_ref_desc = -1; } void s_place_ptr() { s_goxy(s_status_cols, s_top_lines+page_ents[s_ptr_page_line].start_line); putchar('>'); fflush(stdout); } /* refresh the status line for an article on screen page */ /* note: descriptions will not (for now) be individually refreshable */ void s_refresh_status(line) int line; { int i,j; long ent; ent = page_ents[line].entnum; assert(line <= s_bot_ent); /* better be refreshing on-page */ s_goxy(0,s_top_lines+page_ents[line].start_line); j = page_ents[line].lines; for (i = 1; i <= j; i++) printf("%s\n",s_get_statchars(ent,i)); fflush(stdout); } void s_refresh_description(line) int line; { int i,j,startline; long ent; ent = page_ents[line].entnum; assert(line <= s_bot_ent); /* better be refreshing on-page */ startline = s_top_lines+page_ents[line].start_line; j = page_ents[line].lines; for (i = 1; i <= j; i++) { s_goxy(s_status_cols+s_cursor_cols,(i-1)+startline); /* allow flexible format later? */ if (s_itemnum_cols) { if (i == 1) { /* first description line */ if (line < 99) printf("%2d ",line+1); else printf("** "); /* too big */ } else printf(" "); } printf("%s",s_get_desc(ent,i,TRUE)); erase_eol(); putchar('\n'); } fflush(stdout); } void s_ref_entry(line,jump) int line; int jump; /* true means that the cursor should be positioned */ { int i,j; long ent; ent = page_ents[line].entnum; assert(line <= s_bot_ent); /* better be refreshing on-page */ if (jump) s_goxy(0,s_top_lines+page_ents[line].start_line); j = page_ents[line].lines; for (i = 1; i <= j; i++) { /* later replace middle with variable #spaces routine */ printf("%s%s",s_get_statchars(ent,i)," "); if (s_itemnum_cols) { if (i == 1) { /* first description line */ if (line < 99) printf("%2d ",line+1); else printf("** "); /* too big */ } else printf(" "); } printf("%s",s_get_desc(ent,i,TRUE)); erase_eol(); putchar('\n'); } } void s_rub_ptr() { rubout(); } void s_refresh() { int i; if (s_ref_all) { clear(); /* make a clean slate */ s_ref_desc = s_ref_status = 0; } if ((s_ref_all || s_ref_top) && s_top_lines>0) s_refresh_top(); if (s_ref_all || ((s_ref_status>=0) && (s_ref_desc>=0))) s_refresh_entzone(); else { if (s_ref_status>=0) { for (i = s_ref_status; i <= s_bot_ent; i++) s_refresh_status(i); } if (s_ref_desc >= 0) { for (i = s_ref_desc; i <= s_bot_ent; i++) s_refresh_description(i); } } s_ref_status = s_ref_desc = -1; if ((s_ref_all || s_ref_bot) && s_bot_lines > 0) s_refresh_bot(); s_ref_all = FALSE; } int s_initscreen() { /* check to see if term is too dumb: if so, return non-zero */ /* set scr_{height,width} */ /* return 0 if all went well */ scr_height = tc_LINES; scr_width = tc_COLS; if (scr_height > 2 && scr_width > 1) /* current dependencies */ return 0; /* everything is OK. */ return 1; /* we can't play with this... */ } /* screen-refresh the status if on-page */ void s_ref_status_onpage(ent) long ent; { int i; for (i = 0; i <= s_bot_ent; i++) if (page_ents[i].entnum == ent) s_refresh_status(i); } void s_resize_win() { #if 0 int i; i = s_initscreen(); /* later possibly use the return value for an error abort? */ s_resized = TRUE; #endif ; /* don't have an empty function */ } #endif /* SCAN */ trn-4.0-test77/sdisp.h0000644000000000000000000000142207113133016013276 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* sdisp.h * * scan display functions */ /* height of screen in characters */ EXT int scr_height INIT(0); /* width of screen in characters */ EXT int scr_width INIT(0); /* has the window been resized? */ EXT bool s_resized INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void s_goxy _((int,int)); void s_mail_and_place _((void)); void s_refresh_top _((void)); void s_refresh_bot _((void)); void s_refresh_entzone _((void)); void s_place_ptr _((void)); void s_refresh_status _((int)); void s_refresh_description _((int)); void s_ref_entry _((int,int)); void s_rub_ptr _((void)); void s_refresh _((void)); int s_initscreen _((void)); void s_ref_status_onpage _((long)); void s_resize_win _((void)); trn-4.0-test77/search.c0000644000000000000000000003156007113133016013422 0ustar rootroot/* search.c */ /* string search routines */ /* Copyright (c) 1981,1980 James Gosling */ /* Modified Aug. 12, 1981 by Tom London to include regular expressions as in ed. RE stuff hacked over by jag to correct a few major problems, mainly dealing with searching within the buffer rather than copying each line to a separate array. Newlines can now appear in RE's */ /* Ripped to shreds and glued back together to make a search package, * July 6, 1984, by Larry Wall. (If it doesn't work, it's probably my fault.) * Changes include: * Buffer, window, and mlisp stuff gone. * Translation tables reduced to 1 table. * Expression buffer is now dynamically allocated. * Character classes now implemented with a bitmap. * Modified by David Canzi, Apr 1997: * Check bounds on alternatives array. * Correct spurious matching, eg. /: re: .*\bfoo/ matched ": re: bar". */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "util2.h" #include "INTERN.h" #include "search.h" #ifndef BITSPERBYTE #define BITSPERBYTE 8 #endif #define BMAPSIZ (127 / BITSPERBYTE + 1) #define MNULL 64 /* Bit is set in a meta-character defn to indicate that the metacharacter can match a null string. advance() uses this. */ /* meta characters in the "compiled" form of a regular expression */ #define CBRA (2|MNULL) /* \( -- begin bracket */ #define CCHR 4 /* a vanilla character */ #define CDOT 6 /* . -- match anything except a newline */ #define CCL 8 /* [...] -- character class */ #define NCCL 10 /* [^...] -- negated character class */ #define CDOL (12|MNULL) /* $ -- matches the end of a line */ #define CEND (14|MNULL) /* The end of the pattern */ #define CKET (16|MNULL) /* \) -- close bracket */ #define CBACK (18|MNULL) /* \N -- backreference to the Nth bracketed string */ #define CIRC (20|MNULL) /* ^ matches the beginning of a line */ #define WORD 32 /* matches word character \w */ #define NWORD 34 /* matches non-word characer \W */ #define WBOUND (36|MNULL) /* matches word boundary \b */ #define NWBOUND (38|MNULL) /* matches non-(word boundary) \B */ #define STAR 01 /* * -- Kleene star, repeats the previous REas many times as possible; the value ORs with the other operator types */ #define ASCSIZ 256 typedef Uchar TRANSTABLE[ASCSIZ]; static TRANSTABLE trans; static bool folding = FALSE; static int err; static char* FirstCharacter; void search_init() { register int i; for (i = 0; i < ASCSIZ; i++) trans[i] = i; } void init_compex(compex) register COMPEX* compex; { /* the following must start off zeroed */ compex->eblen = 0; compex->brastr = NULL; } void free_compex(compex) register COMPEX* compex; { if (compex->eblen) { free(compex->expbuf); compex->eblen = 0; } if (compex->brastr) { free(compex->brastr); compex->brastr = NULL; } } static char* gbr_str = NULL; static int gbr_siz = 0; char* getbracket(compex,n) register COMPEX* compex; int n; { int length = compex->braelist[n] - compex->braslist[n]; if (!compex->nbra) return NULL; if (n > compex->nbra || !compex->braelist[n] || length < 0) return nullstr; growstr(&gbr_str, &gbr_siz, length+1); safecpy(gbr_str, compex->braslist[n], length+1); return gbr_str; } void case_fold(which) int which; { register int i; if (which != folding) { if (which) { for (i = 'A'; i <= 'Z'; i++) trans[i] = tolower(i); } else { for (i = 'A'; i <= 'Z'; i++) trans[i] = i; } folding = which; } } /* Compile the given regular expression into a [secret] internal format */ char* compile(compex, strp, RE, fold) register COMPEX* compex; register char* strp; int RE; int fold; { register int c; register char* ep; char* lastep; char bracket[NBRA]; char* bracketp; char** alt = compex->alternatives; char* retmes = "Badly formed search string"; case_fold(compex->do_folding = fold); if (!compex->eblen) { compex->expbuf = safemalloc(84); compex->eblen = 80; } ep = compex->expbuf; /* point at expression buffer */ *alt++ = ep; /* first alternative starts here */ bracketp = bracket; /* first bracket goes here */ if (*strp == 0) { /* nothing to compile? */ if (*ep == 0) /* nothing there yet? */ return "Null search string"; return NULL; /* just keep old expression */ } compex->nbra = 0; /* no brackets yet */ lastep = 0; for (;;) { if (ep + 4 - compex->expbuf >= compex->eblen) ep = grow_eb(compex, ep, alt); c = *strp++; /* fetch next char of pattern */ if (c == 0) { /* end of pattern? */ if (bracketp != bracket) { /* balanced brackets? */ #ifdef VERBOSE retmes = "Unbalanced parens"; #endif goto cerror; } *ep++ = CEND; /* terminate expression */ *alt++ = 0; /* terminal alternative list */ return NULL; /* return success */ } if (c != '*') lastep = ep; if (!RE) { /* just a normal search string? */ *ep++ = CCHR; /* everything is a normal char */ *ep++ = c; } else /* it is a regular expression */ switch (c) { case '\\': /* meta something */ switch (c = *strp++) { case '(': if (compex->nbra >= NBRA) { #ifdef VERBOSE retmes = "Too many parens"; #endif goto cerror; } *bracketp++ = ++compex->nbra; *ep++ = CBRA; *ep++ = compex->nbra; break; case '|': if (bracketp>bracket) { #ifdef VERBOSE retmes = "No \\| in parens"; /* Alas! */ #endif goto cerror; } *ep++ = CEND; *alt++ = ep; if (alt > compex->alternatives + NALTS) { #ifdef VERBOSE retmes = "Too many alternatives in reg ex"; #endif goto cerror; } break; case ')': if (bracketp <= bracket) { #ifdef VERBOSE retmes = "Unmatched right paren"; #endif goto cerror; } *ep++ = CKET; *ep++ = *--bracketp; break; case 'w': *ep++ = WORD; break; case 'W': *ep++ = NWORD; break; case 'b': *ep++ = WBOUND; break; case 'B': *ep++ = NWBOUND; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *ep++ = CBACK; *ep++ = c - '0'; break; default: *ep++ = CCHR; if (c == '\0') goto cerror; *ep++ = c; break; } break; case '.': *ep++ = CDOT; continue; case '*': if (lastep == 0 || *lastep == CBRA || *lastep == CKET || *lastep == CIRC || (*lastep&STAR)|| *lastep>NWORD) goto defchar; *lastep |= STAR; continue; case '^': if (ep != compex->expbuf && ep[-1] != CEND) goto defchar; *ep++ = CIRC; continue; case '$': if (*strp != 0 && (*strp != '\\' || strp[1] != '|')) goto defchar; *ep++ = CDOL; continue; case '[': { /* character class */ register int i; if (ep - compex->expbuf >= compex->eblen - BMAPSIZ) ep = grow_eb(compex, ep, alt); /* reserve bitmap */ for (i = BMAPSIZ; i; --i) ep[i] = 0; if ((c = *strp++) == '^') { c = *strp++; *ep++ = NCCL; /* negated */ } else *ep++ = CCL; /* normal */ i = 0; /* remember oldchar */ do { if (c == '\0') { #ifdef VERBOSE retmes = "Missing ]"; #endif goto cerror; } if (*strp == '-' && *(++strp) != ']' && *strp) i = *strp++; else i = c; while (c <= i) { ep[c / BITSPERBYTE] |= 1 << (c % BITSPERBYTE); if (fold && isalpha(c)) ep[(c ^ 32) / BITSPERBYTE] |= 1 << ((c ^ 32) % BITSPERBYTE); /* set the other bit too */ c++; } } while ((c = *strp++) != ']'); ep += BMAPSIZ; continue; } defchar: default: *ep++ = CCHR; *ep++ = c; } } cerror: compex->expbuf[0] = 0; compex->nbra = 0; return retmes; } char* grow_eb(compex, epp, alt) register COMPEX* compex; char* epp; char** alt; { register char* oldbuf = compex->expbuf; register char** altlist = compex->alternatives; compex->eblen += 80; compex->expbuf = saferealloc(compex->expbuf, (MEM_SIZE)compex->eblen + 4); if (compex->expbuf != oldbuf) { /* realloc can change expbuf! */ epp += compex->expbuf - oldbuf; while (altlist != alt) *altlist++ += compex->expbuf - oldbuf; } return epp; } char* execute(compex, addr) register COMPEX* compex; char* addr; { register char* p1 = addr; register Uchar* trt = trans; register int c; if (addr == NULL || compex->expbuf == NULL) return NULL; if (compex->nbra) { /* any brackets? */ for (c = 0; c <= compex->nbra; c++) compex->braslist[c] = compex->braelist[c] = NULL; if (compex->brastr) free(compex->brastr); compex->brastr = savestr(p1); /* in case p1 is not static */ p1 = compex->brastr; /* ! */ } case_fold(compex->do_folding); /* make sure table is correct */ FirstCharacter = p1; /* for ^ tests */ if (compex->expbuf[0] == CCHR && !compex->alternatives[1]) { c = trt[*(Uchar*)(compex->expbuf+1)]; /* fast check for first char */ do { if (trt[*(Uchar*)p1] == c && advance(compex, p1, compex->expbuf)) return p1; p1++; } while (*p1 && !err); if (err) err = 0; return NULL; } else { /* regular algorithm */ do { register char** alt = compex->alternatives; while (*alt) { if (advance(compex, p1, *alt++)) return p1; } p1++; } while (*p1 && !err); if (err) err = 0; return NULL; } /*NOTREACHED*/ } /* advance the match of the regular expression starting at ep along the string lp, simulates an NDFSA */ bool advance(compex, lp, ep) register COMPEX* compex; register char* ep; register char* lp; { register char* curlp; register Uchar* trt = trans; register int i; while (*lp || (*ep & (STAR|MNULL))) { switch (*ep++) { case CCHR: if (trt[*(Uchar*)ep++] != trt[*(Uchar*)lp]) return FALSE; lp++; continue; case CDOT: if (*lp == '\n') return FALSE; lp++; continue; case CDOL: if (!*lp || *lp == '\n') continue; return FALSE; case CIRC: if (lp == FirstCharacter || lp[-1]=='\n') continue; return FALSE; case WORD: if (isalnum(*lp)) { lp++; continue; } return FALSE; case NWORD: if (!isalnum(*lp)) { lp++; continue; } return FALSE; case WBOUND: if ((lp == FirstCharacter || !isalnum(lp[-1])) != (!*lp || !isalnum(*lp)) ) continue; return FALSE; case NWBOUND: if ((lp == FirstCharacter || !isalnum(lp[-1])) == (!*lp || !isalnum(*lp))) continue; return FALSE; case CEND: return TRUE; case CCL: if (cclass(ep, *lp, 1)) { ep += BMAPSIZ; lp++; continue; } return FALSE; case NCCL: if (cclass(ep, *lp, 0)) { ep += BMAPSIZ; lp++; continue; } return FALSE; case CBRA: compex->braslist[(unsigned char)*ep++] = lp; continue; case CKET: i = *ep++; compex->braelist[i] = lp; compex->braelist[0] = lp; compex->braslist[0] = compex->braslist[i]; continue; case CBACK: if (compex->braelist[i = *ep++] == 0) { fputs("bad braces\n",stdout) FLUSH; err = TRUE; return FALSE; } if (backref(compex, i, lp)) { lp += compex->braelist[i] - compex->braslist[i]; continue; } return FALSE; case CBACK | STAR: if (compex->braelist[i = *ep++] == 0) { fputs("bad braces\n",stdout) FLUSH; err = TRUE; return FALSE; } curlp = lp; while (backref(compex, i, lp)) { lp += compex->braelist[i] - compex->braslist[i]; } while (lp >= curlp) { if (advance(compex, lp, ep)) return TRUE; lp -= compex->braelist[i] - compex->braslist[i]; } continue; case CDOT | STAR: curlp = lp; while (*lp++ && lp[-1] != '\n') ; goto star; case WORD | STAR: curlp = lp; while (*lp++ && isalnum(lp[-1])) ; goto star; case NWORD | STAR: curlp = lp; while (*lp++ && !isalnum(lp[-1])) ; goto star; case CCHR | STAR: curlp = lp; while (*lp++ && trt[*(Uchar*)(lp-1)] == trt[*(Uchar*)ep]) ; ep++; goto star; case CCL | STAR: case NCCL | STAR: curlp = lp; while (*lp++ && cclass(ep, lp[-1], ep[-1] == (CCL | STAR))) ; ep += BMAPSIZ; goto star; star: do { lp--; if (advance(compex, lp, ep)) return TRUE; } while (lp > curlp); return FALSE; default: fputs("Badly compiled pattern\n",stdout) FLUSH; err = TRUE; return -1; } } return FALSE; } bool backref(compex, i, lp) register COMPEX* compex; register int i; register char* lp; { register char* bp; bp = compex->braslist[i]; while (*lp && *bp == *lp) { bp++; lp++; if (bp >= compex->braelist[i]) return TRUE; } return FALSE; } bool cclass(set, c, af) register char* set; register int c; int af; { c &= 0177; #if BITSPERBYTE == 8 if (set[c >> 3] & 1 << (c & 7)) #else if (set[c / BITSPERBYTE] & 1 << (c % BITSPERBYTE)) #endif return af; return !af; } trn-4.0-test77/search.h0000644000000000000000000000225707113133016013430 0ustar rootroot/* search.h */ /* This software is copyrighted as detailed in the LICENSE file. */ #ifndef NBRA #define NBRA 10 /* the maximum number of meta-brackets in an RE -- \( \) */ #define NALTS 10 /* the maximum number of \|'s */ struct compex { char* expbuf; /* The compiled search string */ int eblen; /* Length of above buffer */ char* alternatives[NALTS+1];/* The list of \| seperated alternatives */ char* braslist[NBRA]; /* RE meta-bracket start list */ char* braelist[NBRA]; /* RE meta-bracket end list */ char* brastr; /* saved match string after execute() */ char nbra; /* The number of meta-brackets int the most recenlty compiled RE */ bool do_folding; /* fold upper and lower case? */ }; #endif /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void search_init _((void)); void init_compex _((COMPEX*)); void free_compex _((COMPEX*)); char* getbracket _((COMPEX*,int)); void case_fold _((int)); char* compile _((COMPEX*,char*,int,int)); char* grow_eb _((COMPEX*,char*,char**)); char* execute _((COMPEX*,char*)); bool advance _((COMPEX*,char*,char*)); bool backref _((COMPEX*,int,char*)); bool cclass _((char*,int,int)); trn-4.0-test77/smisc.c0000644000000000000000000000255007113133016013270 0ustar rootroot/* This file is Copyright 1993 by Clifford A. Adams */ /* smisc.c * * Lots of misc. stuff. */ #include "EXTERN.h" #include "common.h" #ifdef SCAN #include "hash.h" #include "cache.h" #include "rt-util.h" /* for from-compression stuff */ #include "intrp.h" #include "term.h" #include "util.h" #include "scan.h" #include "sdisp.h" #include "sorder.h" #include "charsubst.h" #ifdef SCAN_ART #include "samisc.h" #include "sadesc.h" #endif #include "INTERN.h" #include "smisc.h" bool s_eligible(ent) long ent; { switch (s_cur_type) { #ifdef SCAN_ART case S_ART: return sa_eligible(ent); #endif default: printf("s_eligible: current type is bad!\n") FLUSH; return FALSE; } } void s_beep() { putchar(7); fflush(stdout); } char* s_get_statchars(ent,line) long ent; int line; { if (s_status_cols == 0) return nullstr; switch (s_cur_type) { #ifdef SCAN_ART case S_ART: return sa_get_statchars(ent,line); #endif default: return NULL; } } char* s_get_desc(ent,line,trunc) long ent; int line; bool_int trunc; { switch (s_cur_type) { #ifdef SCAN_ART case S_ART: return sa_get_desc(ent,line,trunc); #endif default: return NULL; } } int s_ent_lines(ent) long ent; { switch (s_cur_type) { #ifdef SCAN_ART case S_ART: return sa_ent_lines(ent); #endif default: return 1; } } #endif /* SCAN */ trn-4.0-test77/smisc.h0000644000000000000000000000073607113133016013301 0ustar rootroot/* This file is Copyright 1993 by Clifford A. Adams */ /* smisc.h */ /* TRUE if the last command (run through setdef()) was the default */ EXT bool s_default_cmd INIT(FALSE); /* explicitly follow until end of thread */ EXT bool s_follow_temp INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool s_eligible _((long)); void s_beep _((void)); char* s_get_statchars _((long,int)); char* s_get_desc _((long,int,bool_int)); int s_ent_lines _((long)); trn-4.0-test77/sorder.c0000644000000000000000000001205607113133016013452 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* sorder.c * * scan ordering */ #include "EXTERN.h" #include "common.h" #ifdef SCAN #include "util.h" #include "scan.h" #include "smisc.h" #ifdef SCAN_ART #include "scanart.h" #include "samisc.h" #endif #include "INTERN.h" #include "sorder.h" #ifdef UNDEF int s_compare(a,b) long* a; long* b; /* pointers to the two entries to be compared */ { switch(s_cur_type) { #ifdef SCAN_ART case S_ART: return sa_compare(*a,*b); #endif default: return *a - *b; } } #endif int s_compare(a,b) long a; long b; /* the two entry numbers to be compared */ { switch(s_cur_type) { #ifdef SCAN_ART case S_ART: return sa_compare(a,b); #endif default: return a - b; } } /* sort offset--used so that the 1-offset algorithm is clear even * though the array is 0-offset. */ #define SOFF(a) ((a)-1) /* Uses a heapsort algorithm with the heap readjustment inlined. */ void s_sort_basic() { int i,n; int t1; int j; n = s_ent_sort_max + 1; if (n < 1) return; /* nothing to sort */ for (i = n/2; i >= 1; i--) { /* begin heap readjust */ t1 = s_ent_sort[SOFF(i)]; j = 2*i; while (j <= n) { if (j < n && s_compare(s_ent_sort[SOFF(j)],s_ent_sort[SOFF(j+1)]) < 0) j++; if (s_compare(t1,s_ent_sort[SOFF(j)]) > 0) break; /* out of while loop */ else { s_ent_sort[SOFF(j/2)] = s_ent_sort[SOFF(j)]; j = j*2; } } /* while */ s_ent_sort[SOFF(j/2)] = t1; /* end heap readjust */ } /* for */ for (i = n-1; i >= 1; i--) { t1 = s_ent_sort[SOFF(i+1)]; s_ent_sort[SOFF(i+1)] = s_ent_sort[SOFF(1)]; s_ent_sort[SOFF(1)] = t1; /* begin heap readjust */ j = 2; while (j <= i) { if (j < i && s_compare(s_ent_sort[SOFF(j)],s_ent_sort[SOFF(j+1)]) < 0) j++; if (s_compare(t1,s_ent_sort[SOFF(j)]) > 0) break; /* out of while */ else { s_ent_sort[SOFF(j/2)] = s_ent_sort[SOFF(j)]; j = j*2; } } /* while */ s_ent_sort[SOFF(j/2)] = t1; /* end heap readjust */ } /* for */ /* end of heapsort */ } void s_sort() { long i; #ifdef UNDEF qsort((void*)s_ent_sort,(s_ent_sort_max)+1,sizeof(long),s_compare); #endif s_sort_basic(); s_ent_sorted_max = s_ent_sort_max; /* whole array is now sorted */ s_order_changed = FALSE; /* rebuild the indexes */ for (i = 0; i <= s_ent_sort_max; i++) s_ent_index[s_ent_sort[i]] = i; } void s_order_clean() { if (s_ent_sort) free(s_ent_sort); if (s_ent_index) free(s_ent_index); s_ent_sort = NULL; s_contexts[s_cur_context].ent_sort = s_ent_sort; s_ent_index = (long*)0; s_contexts[s_cur_context].ent_index = s_ent_index; s_ent_sort_max = -1; s_ent_sorted_max = -1; s_ent_index_max = -1; } /* adds the entry number to the current context */ void s_order_add(ent) long ent; { long size; if (ent < s_ent_index_max && s_ent_index[ent] >= 0) return; /* entry is already in the list */ /* add entry to end of sorted list */ s_ent_sort_max += 1; if (s_ent_sort_max % 100 == 0) { /* be nice to realloc */ size = (s_ent_sort_max+100) * sizeof (long); s_ent_sort = (long*)saferealloc((char*)s_ent_sort,size); /* change the context too */ s_contexts[s_cur_context].ent_sort = s_ent_sort; } s_ent_sort[s_ent_sort_max] = ent; /* grow index list if needed */ if (ent > s_ent_index_max) { long old,i; old = s_ent_index_max; if (s_ent_index_max == -1) s_ent_index_max += 1; s_ent_index_max = (ent/100+1) * 100; /* round up */ size = (s_ent_index_max + 1) * sizeof (long); s_ent_index = (long*)saferealloc((char*)s_ent_index,size); /* change the context too */ s_contexts[s_cur_context].ent_index = s_ent_index; /* initialize new indexes */ for (i = old+1; i < s_ent_index_max; i++) s_ent_index[i] = -1; /* -1 == not a legal entry */ } s_ent_index[ent] = s_ent_sort_max; s_order_changed = TRUE; } long s_prev(ent) long ent; { long tmp; if (ent < 0 || ent > s_ent_index_max || s_ent_sorted_max < 0) return 0; if (s_order_changed) s_sort(); tmp = s_ent_index[ent]; if (tmp <= 0) return 0; return s_ent_sort[tmp-1]; } long s_next(ent) long ent; { long tmp; if (ent < 0 || ent > s_ent_index_max || s_ent_sorted_max < 0) return 0; if (s_order_changed) s_sort(); tmp = s_ent_index[ent]; if (tmp < 0 || tmp == s_ent_sorted_max) return 0; return s_ent_sort[tmp+1]; } /* given an entry, returns previous eligible entry */ /* returns 0 if no previous eligible entry */ long s_prev_elig(a) long a; { while ((a = s_prev(a)) != 0) if (s_eligible(a)) return a; return 0L; } /* given an entry, returns next eligible entry */ /* returns 0 if no next eligible entry */ long s_next_elig(a) long a; { while ((a = s_next(a)) != 0) if (s_eligible(a)) return a; return 0L; } long s_first() { if (s_order_changed) s_sort(); if (s_ent_sorted_max < 0) return 0; return s_ent_sort[0]; } long s_last() { if (s_order_changed) s_sort(); if (s_ent_sorted_max < 0) return 0; return s_ent_sort[s_ent_sorted_max]; } #endif /* SCAN */ trn-4.0-test77/sorder.h0000644000000000000000000000102507113133016013451 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* sorder.h * * scan ordering */ /* If true, resort next time order is considered */ EXT bool s_order_changed INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int s_compare _((long,long)); void s_sort_basic _((void)); void s_sort _((void)); void s_order_clean _((void)); void s_order_add _((long)); long s_prev _((long)); long s_next _((long)); long s_prev_elig _((long)); long s_next_elig _((long)); long s_first _((void)); long s_last _((void)); trn-4.0-test77/spage.c0000644000000000000000000003030707113133016013252 0ustar rootroot/* This file Copyright 1992 by Clifford A. Adams */ /* spage.c * */ #include "EXTERN.h" #include "common.h" #ifdef SCAN #include "list.h" #include "hash.h" #include "cache.h" #include "final.h" /* assert() */ #include "ngdata.h" #include "rt-util.h" /* spinner */ #include "scan.h" #include "sdisp.h" /* for display dimension sizes */ #include "smisc.h" #include "sorder.h" #ifdef SCAN_ART #include "scanart.h" #include "samain.h" #include "sathread.h" /* sa_mode_fold */ #endif #include "term.h" #include "INTERN.h" #include "spage.h" /* returns TRUE if sucessful */ bool s_fillpage_backward(end) long end; /* entry number to be last on page */ { int min_page_ents; /* current minimum */ int i,j; long a; /* misc entry number */ int page_lines; /* lines available on page */ int line_on; /* line # currently on: 0=line after top status bar */ /* Debug */ #if 0 printf("entry: s_fillpage_backward(%d)\n",end) FLUSH; #endif page_lines = scr_height - s_top_lines - s_bot_lines; min_page_ents = MAX_PAGE_SIZE-1; s_bot_ent = -1; /* none yet */ line_on = 0; /* whatever happens, the entry display will need a full refresh... */ s_ref_status = s_ref_desc = 0; /* refresh from top entry */ if (end < 1) /* start from end */ end = s_last(); if (end == 0) /* no entries */ return FALSE; if (s_eligible(end)) a = end; else a = s_prev_elig(end); if (!a) /* no eligible entries */ return FALSE; /* at this point we *know* that there is at least one eligible */ /* what if the "first" one for the page has a description too long? */ /* later make it shorten the descript. */ /* CONSIDER: make setspin conditional on context? */ setspin(SPIN_BACKGROUND); /* turn on spin on cache misses */ /* uncertain what next comment means now */ /* later do sheer paranoia check for min_page_ents */ while ((line_on+s_ent_lines(a)) <= page_lines) { page_ents[min_page_ents].entnum = a; i = s_ent_lines(a); page_ents[min_page_ents].lines = i; min_page_ents--; s_bot_ent += 1; line_on = line_on+i; a = s_prev_elig(a); if (!a) /* no more eligible */ break; /* get out of loop and finish up... */ } /* what if none on page? (desc. too long) Fix later */ setspin(SPIN_POP); /* turn off spin on cache misses */ /* replace the entries at the front of the page_ents array */ /* also set start_line entries */ j = 0; line_on = 0; for (i = min_page_ents+1; i < MAX_PAGE_SIZE; i++) { page_ents[j].entnum = page_ents[i].entnum; page_ents[j].pageflags = (char)0; page_ents[j].lines = page_ents[i].lines; page_ents[j].start_line = line_on; line_on = line_on + page_ents[j].lines; j++; } /* set new s_top_ent */ s_top_ent = page_ents[0].entnum; /* Now, suppose that the pointer position is off the page. That would * be bad, so lets make sure it doesn't happen. */ if (s_ptr_page_line > s_bot_ent) s_ptr_page_line = s_bot_ent; #ifdef SCAN_ART if (s_cur_type != S_ART) return TRUE; /* temporary fix. Under some conditions ineligible entries will * not be found until they are in the page. In this case just * refill the page. */ for (i = 0; i <= s_bot_ent; i++) if (is_unavailable(sa_ents[page_ents[i].entnum].artnum)) break; if (i <= s_bot_ent) return s_fillpage_backward(end); /* next time the unavail won't be chosen */ #endif return TRUE; /* we have a page... */ } /* fills the page array */ /* returns TRUE on success */ bool s_fillpage_forward(start) long start; /* entry to start filling with */ { int i; long a; int page_lines; /* lines available on page */ int line_on; /* line # currently on (0: line after top status bar */ /* Debug */ #if 0 printf("entry: s_fillpage_forward(%d)\n",start) FLUSH; #endif page_lines = scr_height - s_top_lines - s_bot_lines; s_bot_ent = -1; line_on = 0; /* whatever happens, the entry zone will need a full refresh... */ s_ref_status = s_ref_desc = 0; if (start < 0) /* fill from top */ start = s_first(); if (start == 0) /* no entries */ return FALSE; if (s_eligible(start)) a = start; else a = s_next_elig(start); if (!a) /* no eligible entries */ return FALSE; /* at this point we *know* that there is at least one eligible */ /* what if the first entry for the page has a description too long? */ /* later make it shorten the descript. */ setspin(SPIN_BACKGROUND); /* turn on spin on cache misses */ /* ? later do paranoia check for s_bot_ent */ while ((line_on+s_ent_lines(a)) <= page_lines) { s_bot_ent += 1; page_ents[s_bot_ent].entnum = a; page_ents[s_bot_ent].start_line = line_on; i = s_ent_lines(a); page_ents[s_bot_ent].lines = i; page_ents[s_bot_ent].pageflags = (char)0; line_on = line_on+i; a = s_next_elig(a); if (!a) /* no more eligible */ break; /* get out of loop and finish up... */ } setspin(SPIN_POP); /* turn off spin on cache misses */ /* Now, suppose that the pointer position is off the page. That would * be bad, so lets make sure it doesn't happen. */ /* set new s_top_ent */ s_top_ent = page_ents[0].entnum; if (s_ptr_page_line > s_bot_ent) s_ptr_page_line = s_bot_ent; #ifdef SCAN_ART if (s_cur_type != S_ART) return TRUE; /* temporary fix. Under some conditions ineligible entries will * not be found until they are in the page. In this case just * refill the page. */ for (i = 0; i <= s_bot_ent; i++) if (is_unavailable(sa_ents[page_ents[i].entnum].artnum)) break; if (i <= s_bot_ent) return s_fillpage_forward(start); /* next time the unavail won't be chosen */ #endif return TRUE; /* we have a page... */ } /* Given possible changes to which entries should be on the page, * fills the page with more/less entries. Tries to minimize refreshing * (the last entry on the page will be refreshed whether it needs it * or not.) */ /* returns TRUE on success */ bool s_refillpage() { int i,j; long a; int page_lines; /* lines available on page */ int line_on; /* line # currently on: 0=line after top status bar */ /* Debug */ #if 0 printf("entry: s_refillpage\n") FLUSH; #endif page_lines = scr_height - s_top_lines - s_bot_lines; /* if the top entry is not the s_top_ent, * or the top entry is not eligible, * or the top entry is not on the first line, * or the top entry has a different # of lines now, * just refill the whole page. */ if (s_bot_ent < 1 || s_top_ent < 1 || s_top_ent != page_ents[0].entnum || !s_eligible(page_ents[0].entnum) || page_ents[0].start_line != 0 || page_ents[0].lines != s_ent_lines(page_ents[0].entnum)) return s_fillpage_forward(s_top_ent); i = 1; /* CAA misc note: I used to have * a = page_ents[1]; * ...at this point. This caused a truly difficult to track bug... * (briefly, occasionally the entry in page_ents[1] would be * *before* (by display order) the entry in page_ents[0]. In * this case the start_line entry of page_ents[0] could be overwritten * causing big problems...) */ a = s_next_elig(page_ents[0].entnum); /* similar to the tests in the last loop... */ while (i <= s_bot_ent && s_eligible(page_ents[i].entnum) && page_ents[i].entnum == a && page_ents[i].lines == s_ent_lines(page_ents[i].entnum)) { i++; a = s_next_elig(a); } j = i-1; /* j is the last "good" entry */ s_bot_ent = j; line_on = page_ents[j].start_line + s_ent_lines(page_ents[j].entnum); a = s_next_elig(page_ents[j].entnum); setspin(SPIN_BACKGROUND); while (a && line_on+s_ent_lines(a) <= page_lines) { i = s_ent_lines(a); s_bot_ent += 1; page_ents[s_bot_ent].entnum = a; page_ents[s_bot_ent].lines = i; page_ents[s_bot_ent].start_line = line_on; page_ents[s_bot_ent].pageflags = (char)0; line_on = line_on+i; a = s_next_elig(a); } setspin(SPIN_POP); /* there are fairly good reasons to refresh the last good entry, such * as clearing the rest of the screen... */ s_ref_status = s_ref_desc = j; /* Now, suppose that the pointer position is off the page. That would * be bad, so lets make sure it doesn't happen. */ if (s_ptr_page_line > s_bot_ent) s_ptr_page_line = s_bot_ent; #ifdef SCAN_ART if (s_cur_type != S_ART) return TRUE; /* temporary fix. Under some conditions ineligible entries will * not be found until they are in the page. In this case just * refill the page. */ for (i = 0; i <= s_bot_ent; i++) if (is_unavailable(sa_ents[page_ents[i].entnum].artnum)) break; if (i <= s_bot_ent) return s_refillpage(); /* next time the unavail won't be chosen */ #endif return TRUE; /* we have a page... */ } /* fills a page from current position. * if that fails, backs up for a page. * if that fails, downgrade eligibility requirements and try again * returns positive # on normal fill, * negative # on special condition * 0 on failure */ int s_fillpage() { int i; s_refill = FALSE; /* we don't need one now */ if (s_top_ent < 1) /* set top to first entry */ s_top_ent = s_first(); if (s_top_ent == 0) /* no entries */ return 0; /* failure */ if (!s_refillpage()) /* try for efficient refill */ if (!s_fillpage_backward(s_top_ent)) { /* downgrade eligibility standards */ switch (s_cur_type) { #ifdef SCAN_ART case S_ART: /* article context */ if (sa_mode_zoom) { /* we were zoomed in */ s_ref_top = TRUE; /* for "FOLD" display */ sa_mode_zoom = FALSE; /* zoom out */ if (sa_unzoomrefold) sa_mode_fold = TRUE; (void)s_go_top_ents(); /* go to top (ents and page) */ return s_fillpage(); } return -1; /* there just aren't entries! */ #endif default: return -1; /* there just aren't entries! */ } /* switch */ } /* if */ /* temporary fix. Under some conditions ineligible entries will * not be found until they are in the page. In this case just * refill the page. */ for (i = 0; i <= s_bot_ent; i++) if (!s_eligible(page_ents[i].entnum)) return s_fillpage(); /* ineligible won't be chosen again */ #ifdef SCAN_ART if (s_cur_type != S_ART) return 1; /* be extra cautious about the article scan pages */ for (i = 0; i <= s_bot_ent; i++) if (is_unavailable(sa_ents[page_ents[i].entnum].artnum)) break; if (i <= s_bot_ent) return s_fillpage(); /* next time the unavail won't be chosen */ #endif return 1; /* I guess everything worked :-) */ } void s_cleanpage() { } void s_go_top_page() { s_ptr_page_line = 0; } void s_go_bot_page() { s_ptr_page_line = s_bot_ent; } bool s_go_top_ents() { s_top_ent = s_first(); if (!s_top_ent) printf("s_go_top_ents(): no first entry\n") FLUSH; assert(s_top_ent); /* be nicer later */ if (!s_eligible(s_top_ent)) /* this may save a redraw...*/ s_top_ent = s_next_elig(s_top_ent); if (!s_top_ent) { /* none eligible */ /* just go to the top of all the entries */ if (s_cur_type == S_ART) s_top_ent = absfirst; else s_top_ent = 1; /* not very nice coding */ } s_refill = TRUE; s_go_top_page(); return TRUE; /* successful */ } bool s_go_bot_ents() { bool flag; flag = s_fillpage_backward(s_last()); /* fill backwards */ if (!flag) return FALSE; s_go_bot_page(); return TRUE; } void s_go_next_page() { long a; bool flag; a = s_next_elig(page_ents[s_bot_ent].entnum); if (!a) return; /* no next page (we shouldn't have been called) */ /* the fill-page will set the refresh for the screen */ flag = s_fillpage_forward(a); assert(flag); /* I *must* be able to fill a page */ s_ptr_page_line = 0; /* top of page */ } void s_go_prev_page() { long a; bool flag; a = s_prev_elig(page_ents[0].entnum); if (!a) return; /* no prev. page (we shouldn't have been called) */ /* the fill-page will set the refresh for the screen */ flag = s_fillpage_backward(a); /* fill backward */ assert(flag); /* be nicer later... */ /* take care of partially filled previous pages */ flag = s_refillpage(); assert(flag); /* be nicer later... */ s_ref_status = s_ref_desc = 0; /* refresh from top */ s_ptr_page_line = 0; /* top of page */ } #endif /* SCAN */ trn-4.0-test77/spage.h0000644000000000000000000000100707113133016013252 0ustar rootroot/* This file Copyright 1992, 1993 by Clifford A. Adams */ /* spage.h * * functions to manage a page of entries. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool s_fillpage_backward _((long)); bool s_fillpage_forward _((long)); bool s_refillpage _((void)); int s_fillpage _((void)); void s_cleanpage _((void)); void s_go_top_page _((void)); void s_go_bot_page _((void)); bool s_go_top_ents _((void)); bool s_go_bot_ents _((void)); void s_go_next_page _((void)); void s_go_prev_page _((void)); trn-4.0-test77/strftime.c0000644000000000000000000001136307113133016014011 0ustar rootroot/* * strftime: print formatted information about a given time. * Adapted from the routine by Eric R. Smith, Michal Jaegermann, * Arnold Robins, and Paul Close. */ /* The authors 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. */ /* Configuration choices for %x and %X */ #undef LOCAL_DDMMYY /* choose DD/MM/YY instead of MM/DD/YY */ #undef LOCAL_DOTTIME /* choose HH.MM.SS instead of HH:MM:SS */ #include "EXTERN.h" #include "common.h" static char* mth_name[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static char* day_name[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; #ifdef HAS_FTIME # ifndef TM_ZONE char tznm[16] = ""; # endif #else extern char* tzname[]; #endif size_t strftime(str, maxsize, fmt, ts) char* str; size_t maxsize; CONST char* fmt; CONST struct tm* ts; { size_t num = 0, len; char ch; char* putstr; char tmpbuf[80]; if (maxsize-- <= 0) return 0; for (;;) { if (!(ch = *fmt++)) break; if (num == maxsize) { num = 0; break; } if (ch != '%') { *str++ = ch; num++; continue; } /* assume the finished product will be sprintf'ed into tmpbuf */ putstr = tmpbuf; switch (ch = *fmt++) { case 'A': case 'a': if (ts->tm_wday < 0 || ts->tm_wday > 6) putstr = "?"; else if (ch == 'A') putstr = day_name[ts->tm_wday]; else sprintf(tmpbuf, "%-.3s", day_name[ts->tm_wday]); break; case 'B': case 'b': case 'h': if (ts->tm_mon < 0 || ts->tm_mon > 11) putstr = "?"; else if (ch == 'B') putstr = mth_name[ts->tm_mon]; else sprintf(tmpbuf, "%-.3s", mth_name[ts->tm_mon]); break; case 'C': strftime(tmpbuf, sizeof tmpbuf, "%A, %B %e, %Y", ts); break; case 'c': strftime(tmpbuf, sizeof tmpbuf, "%x %X", ts); break; case 'D': #ifndef LOCAL_DDMMYY case 'x': #endif strftime(tmpbuf, sizeof tmpbuf, "%m/%d/%y", ts); break; case 'd': sprintf(tmpbuf, "%02d", ts->tm_mday); break; case 'e': /* day of month, blank padded */ sprintf(tmpbuf, "%2d", ts->tm_mday); break; case 'H': sprintf(tmpbuf, "%02d", ts->tm_hour); break; case 'I': { int n; n = ts->tm_hour; if (n == 0) n = 12; else if (n > 12) n -= 12; sprintf(tmpbuf, "%02d", n); break; } case 'j': sprintf(tmpbuf, "%03d", ts->tm_yday + 1); break; case 'm': sprintf(tmpbuf, "%02d", ts->tm_mon + 1); break; case 'M': sprintf(tmpbuf, "%02d", ts->tm_min); break; case 'p': putstr = (ts->tm_hour < 12) ? "AM" : "PM"; break; case 'r': strftime(tmpbuf, sizeof tmpbuf, "%I:%M:%S %p", ts); break; case 'R': strftime(tmpbuf, sizeof tmpbuf, "%H:%M", ts); break; case 'S': sprintf(tmpbuf, "%02d", ts->tm_sec); break; case 'T': #ifndef LOCAL_DOTTIME case 'X': #endif strftime(tmpbuf, sizeof tmpbuf, "%H:%M:%S", ts); break; case 'U': /* week of year - starting Sunday */ sprintf(tmpbuf, "%02d", (ts->tm_yday - ts->tm_wday + 10) / 7); break; case 'W': /* week of year - starting Monday */ sprintf(tmpbuf, "%02d", (ts->tm_yday - ((ts->tm_wday + 6) % 7) + 10) / 7); break; case 'w': sprintf(tmpbuf, "%d", ts->tm_wday); break; case 'y': sprintf(tmpbuf, "%02d", ts->tm_year % 100); break; #ifdef LOCAL_DOTTIME case 'X': strftime(tmpbuf, sizeof tmpbuf, "%H.%M.%S", ts); break; #endif #ifdef LOCAL_DDMMYY case 'x': strftime(tmpbuf, sizeof tmpbuf, "%d/%m/%y", ts); break; #endif case 'Y': sprintf(tmpbuf, "%d", ts->tm_year + 1900); break; case 'Z': #if defined(HAS_GETTIMEOFDAY) || defined(HAS_FTIME) # ifdef TM_ZONE strcpy(tmpbuf, ts->tm_zone); # else # ifdef HAS_GETTIMEOFDAY if (*tznm == '\0') { char* timezone(); struct timeval tv; struct timezone tz; (void) gettimeofday(&tv, &tz); strcpy(tznm, timezone(tz.tz_minuteswest, ts->tm_isdst)); } strcpy(tmpbuf, tznm); # else strcpy(tmpbuf, ts->tm_name); # endif # endif /* !HAS_GETTIMEOFDAY */ #else /* !HAS_GETTIMEOFDAY && !HAS_FTIME */ strcpy(tmpbuf, tzname[ts->tm_isdst]); #endif break; case '%': case '\0': putstr = "%"; break; case 'n': /* same as \n */ putstr = "\n"; break; case 't': /* same as \t */ putstr = "\t"; break; default: sprintf(tmpbuf, "%%%c", ch); break; } len = strlen(putstr); num += len; if (num > maxsize) { len -= num - maxsize; num = 0; ch = '\0'; } strncpy(str, putstr, len); str += len; if (!ch) break; } *str = '\0'; return num; } trn-4.0-test77/strn.10000644000000000000000000000177707113613623013077 0ustar rootroot.\" strn.1 .\" .\" This manual page is taken from the trn.1 page. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .de Ip .br .ie \\n.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH STRN 1 LOCAL .UC 6 .SH NAME strn - scanning threaded read news program .SH SYNOPSIS .B trn [options] [newsgroups] .SH DESCRIPTION .I Strn has been replaced by .I trn, version 4.0. (The last independent version of strn was version 0.9.2.) Trn is a threaded version of .I rn, which is a replacement for the readnews(1) program. .SH AUTHORS See the .I trn manual page. .SH SEE ALSO trn(1) .SH DIAGNOSTICS Generally self-documenting, as they say. .SH BUGS See the .I trn manual page. trn-4.0-test77/sw.c0000644000000000000000000002231111437640112012604 0ustar rootroot/* sw.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "env.h" #include "util.h" #include "util2.h" #include "cache.h" #include "head.h" #include "init.h" #include "opt.h" #include "only.h" #include "term.h" #include "intrp.h" #include "trn.h" #include "ngdata.h" #include "rt-page.h" #include "charsubst.h" #include "INTERN.h" #include "sw.h" void sw_file(tcbufptr) char** tcbufptr; { int initfd = open(*tcbufptr,0); if (initfd >= 0) { fstat(initfd,&filestat); if (filestat.st_size >= TCBUF_SIZE-1) *tcbufptr = saferealloc(*tcbufptr,(MEM_SIZE)filestat.st_size+1); if (filestat.st_size) { int len = read(initfd,*tcbufptr,(int)filestat.st_size); (*tcbufptr)[len] = '\0'; sw_list(*tcbufptr); } else **tcbufptr = '\0'; close(initfd); } } /* decode a list of space separated switches */ void sw_list(swlist) char* swlist; { register char* s; register char* p; register char inquote = 0; s = p = swlist; while (*s) { /* "String, or nothing" */ if (!inquote && isspace(*s)) { /* word delimiter? */ for (;;) { while (isspace(*s)) s++; if (*s != '#') break; while (*s && *s++ != '\n') ; } if (p != swlist) *p++ = '\0'; /* chop here */ } else if (inquote == *s) { s++; /* delete trailing quote */ inquote = 0; /* no longer quoting */ } else if (!inquote && (*s == '"' || *s == '\'')) { /* OK, I know when I am not wanted */ inquote = *s++; /* remember & del single or double */ } else if (*s == '\\') { /* quoted something? */ if (*++s != '\n') { /* newline? */ s = interp_backslash(p, s); p++; } s++; } else *p++ = *s++; /* normal char */ } *p++ = '\0'; *p = '\0'; /* put an extra null on the end */ if (inquote) { printf("Unmatched %c in switch\n",inquote) FLUSH; termdown(1); } for (p = swlist; *p; /* p += strlen(p)+1 */ ) { decode_switch(p); while (*p++) ; /* point at null + 1 */ } } /* decode a single switch */ void decode_switch(s) register char* s; { while (isspace(*s)) s++; /* ignore leading spaces */ #ifdef DEBUG if (debug) { printf("Switch: %s\n",s) FLUSH; termdown(1); } #endif if (*s != '-' && *s != '+') { /* newsgroup pattern */ setngtodo(s); if (mode == 'i') ng_min_toread = 0; } else { /* normal switch */ bool upordown = *s == '-' ? TRUE : FALSE; char tmpbuf[LBUFLEN]; switch (*++s) { #ifdef TERMMOD case '=': /*$$ fix this */ break; #endif #ifdef BAUDMOD case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /*$$ fix this */ break; #endif case '/': set_option(OI_AUTO_SAVE_NAME, YESorNO(upordown)); break; case '+': set_option(OI_USE_ADD_SEL, YESorNO(upordown)); set_option(OI_USE_NEWSGROUP_SEL, YESorNO(upordown)); if (upordown) set_option(OI_INITIAL_GROUP_LIST, YESorNO(0)); else set_option(OI_USE_NEWSRC_SEL, YESorNO(0)); break; case 'a': set_option(OI_BKGND_THREADING, YESorNO(!upordown)); break; case 'A': set_option(OI_AUTO_ARROW_MACROS, YESorNO(upordown)); break; case 'b': set_option(OI_READ_BREADTH_FIRST, YESorNO(upordown)); break; case 'B': set_option(OI_BKGND_SPINNER, YESorNO(upordown)); break; case 'c': checkflag = upordown; break; case 'C': if (*++s == '=') s++; set_option(OI_CHECKPOINT_NEWSRC_FREQUENCY, s); break; case 'd': { if (*++s == '=') s++; set_option(OI_SAVE_DIR, s); break; } case 'D': #ifdef DEBUG if (*++s == '=') s++; if (*s) { if (upordown) debug |= atoi(s); else debug &= ~atoi(s); } else { if (upordown) debug |= 1; else debug = 0; } #else printf("Trn was not compiled with -DDEBUG.\n") FLUSH; termdown(1); #endif break; case 'e': set_option(OI_ERASE_SCREEN, YESorNO(upordown)); break; case 'E': if (*++s == '=') s++; strcpy(tmpbuf,s); s = index(tmpbuf,'='); if (s) { *s++ = '\0'; s = export(tmpbuf,s) - (s-tmpbuf); if (mode == 'i') save_init_environment(s); } else { s = export(tmpbuf,nullstr) - strlen(tmpbuf) - 1; if (mode == 'i') save_init_environment(s); } break; case 'f': set_option(OI_NOVICE_DELAYS, YESorNO(!upordown)); break; case 'F': set_option(OI_CITED_TEXT_STRING, s+1); break; case 'g': #ifdef INNERSEARCH set_option(OI_GOTO_LINE_NUM, s+1); #else notincl("-g"); #endif break; case 'G': #ifdef EDIT_DISTANCE set_option(OI_FUZZY_NEWSGROUP_NAMES, YESorNO(upordown)); #else notincl("-G"); #endif break; case 'h': if (!s[1]) { /* Free old user_htype list */ while (user_htype_cnt > 1) free(user_htype[--user_htype_cnt].name); bzero((char*)user_htypeix, 26); } /* FALL THROUGH */ case 'H': if (checkflag) break; set_header(s+1,*s == 'h'? HT_HIDE : HT_MAGIC, upordown); break; case 'i': if (*++s == '=') s++; set_option(OI_INITIAL_ARTICLE_LINES, s); break; case 'I': set_option(OI_APPEND_UNSUBSCRIBED_GROUPS, YESorNO(upordown)); break; case 'j': set_option(OI_FILTER_CONTROL_CHARACTERS, YESorNO(!upordown)); break; case 'J': if (*++s == '=') s++; set_option(OI_JOIN_SUBJECT_LINES, upordown && *s? s : YESorNO(upordown)); break; case 'k': set_option(OI_IGNORE_THRU_ON_SELECT, YESorNO(upordown)); break; case 'K': set_option(OI_AUTO_GROW_GROUPS, YESorNO(!upordown)); break; case 'l': set_option(OI_MUCK_UP_CLEAR, YESorNO(upordown)); break; case 'L': set_option(OI_ERASE_EACH_LINE, YESorNO(upordown)); break; case 'M': if (upordown) set_option(OI_SAVEFILE_TYPE, "mail"); break; case 'm': set_option(OI_PAGER_LINE_MARKING, s+1); break; case 'N': if (upordown) set_option(OI_SAVEFILE_TYPE, "norm"); break; case 'o': if (*++s == '=') s++; set_option(OI_OLD_MTHREADS_DATABASE, s); break; case 'O': if (*++s == '=') s++; set_option(OI_NEWS_SEL_MODE, s); if (*++s) { char tmpbuf[4]; sprintf(tmpbuf, "%s%c", isupper(*s)? "r " : nullstr, *s); set_option(OI_NEWS_SEL_ORDER, tmpbuf); } break; case 'p': if (*++s == '=') s++; if (!upordown) s = YESorNO(0); else { switch (*s) { case '+': s = "thread"; break; case 'p': s = "parent"; break; default: s = "subthread"; break; } } set_option(OI_SELECT_MY_POSTS, s); break; case 'q': set_option(OI_NEWGROUP_CHECK, YESorNO(!upordown)); break; case 'Q': #ifdef CHARSUBST if (*++s == '=') s++; set_option(OI_CHARSET, s); #else notincl("-Q"); #endif break; case 'r': set_option(OI_RESTART_AT_LAST_GROUP, YESorNO(upordown)); break; case 's': if (*++s == '=') s++; set_option(OI_INITIAL_GROUP_LIST, isdigit(*s)? s : YESorNO(0)); break; case 'S': if (*++s == '=') s++; set_option(OI_SCANMODE_COUNT, s); break; case 't': set_option(OI_TERSE_OUTPUT, YESorNO(upordown)); break; case 'T': set_option(OI_EAT_TYPEAHEAD, YESorNO(!upordown)); break; case 'u': set_option(OI_COMPRESS_SUBJECTS, YESorNO(!upordown)); break; case 'U': unsafe_rc_saves = upordown; break; case 'v': set_option(OI_VERIFY_INPUT, YESorNO(upordown)); break; case 'V': if (mode == 'i') { tc_LINES = 1000; tc_COLS = 1000; erase_screen = FALSE; } trn_version(); newline(); if (mode == 'i') exit(0); break; case 'x': if (*++s == '=') s++; if (isdigit(*s)) { set_option(OI_ARTICLE_TREE_LINES, s); while (isdigit(*s)) s++; } if (*s) set_option(OI_NEWS_SEL_STYLES, s); set_option(OI_USE_THREADS, YESorNO(upordown)); break; case 'X': if (*++s == '=') s++; if (isdigit(*s)) { set_option(OI_USE_NEWS_SEL, s); while (isdigit(*s)) s++; } else set_option(OI_USE_NEWS_SEL, YESorNO(upordown)); if (*s) set_option(OI_NEWS_SEL_CMDS, s); break; case 'z': if (*++s == '=') s++; set_option(OI_DEFAULT_REFETCH_TIME, upordown && *s? s : YESorNO(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 termdown(2); break; } } } void save_init_environment(str) char* str; { if (init_environment_cnt >= init_environment_max) { init_environment_max += 32; init_environment_strings = (char**) saferealloc((char*)init_environment_strings, init_environment_max * sizeof (char*)); } init_environment_strings[init_environment_cnt++] = str; } void write_init_environment(fp) FILE* fp; { int i; char* s; for (i = 0; i < init_environment_cnt; i++) { s = index(init_environment_strings[i],'='); if (!s) continue; *s = '\0'; fprintf(fp, "%s=%s\n", init_environment_strings[i],quote_string(s+1)); *s = '='; } init_environment_cnt = init_environment_max = 0; free((char*)init_environment_strings); init_environment_strings = NULL; } trn-4.0-test77/sw.h0000644000000000000000000000066007113133016012610 0ustar rootroot/* sw.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char** init_environment_strings INIT(NULL); EXT int init_environment_cnt INIT(0); EXT int init_environment_max INIT(0); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void sw_file _((char**)); void sw_list _((char*)); void decode_switch _((char*)); void save_init_environment _((char*)); void write_init_environment _((FILE*)); trn-4.0-test77/term.c0000644000000000000000000015133411437640112013132 0ustar rootroot/* term.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "env.h" #include "util.h" #include "util2.h" #include "final.h" #include "help.h" #include "hash.h" #include "cache.h" #include "opt.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "intrp.h" #include "init.h" #include "art.h" #include "rt-select.h" #ifdef SCORE #include "score.h" /* for sc_lookahead */ #endif #ifdef SCAN #include "scan.h" #include "sdisp.h" #endif #ifdef SCAN_ART #include "scanart.h" #endif #ifdef USE_TK #include "tkstuff.h" #endif #include "univ.h" #include "color.h" #include "INTERN.h" #include "term.h" #include "term.ih" #ifdef u3b2 #undef TIOCGWINSZ #endif #undef USETITE /* use terminal init/exit seqences (not recommended) */ #undef USEKSKE /* use keypad start/end sequences */ char tcarea[TCSIZE]; /* area for "compiled" termcap strings */ static KEYMAP* topmap INIT(NULL); static char* lines_export = NULL; static char* cols_export = NULL; static int leftcost, upcost; static bool got_a_char = FALSE; /* TRUE if we got a char since eating */ /* guarantee capability pointer != NULL */ /* (I believe terminfo will ignore the &tmpaddr argument.) */ char* tgetstr(); #define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr) /* terminal initialization */ void term_init() { savetty(); /* remember current tty state */ #ifdef I_TERMIO outspeed = _tty.c_cflag & CBAUD; /* for tputs() */ ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */ KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */ if (tc_GT = ((_tty.c_oflag & TABDLY) != TAB3)) /* we have tabs, so that's OK */; else _tty.c_oflag &= ~TAB3; /* turn off kernel tabbing -- done in rn */ #else /* !I_TERMIO */ # ifdef I_TERMIOS outspeed = cfgetospeed(&_tty); /* for tputs() (output) */ ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */ KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */ #if 0 _tty.c_oflag &= ~OXTABS; /* turn off kernel tabbing-done in rn */ #endif # else /* !I_TERMIOS */ # ifdef I_SGTTY outspeed = _tty.sg_ospeed; /* for tputs() */ ERASECH = _tty.sg_erase; /* for finish_command() */ KILLCH = _tty.sg_kill; /* for finish_command() */ if (tc_GT = ((_tty.sg_flags & XTABS) != XTABS)) /* we have tabs, so that's OK */; else _tty.sg_flags &= ~XTABS; # else /* !I_SGTTY */ # ifdef MSDOS outspeed = B19200; ERASECH = '\b'; KILLCH = Ctl('u'); tc_GT = 1; # else ..."Don't know how to initialize the terminal!" # endif /* !MSDOS */ # endif /* !I_SGTTY */ # endif /* !I_TERMIOS */ #endif /* !I_TERMIO */ /* The following could be a table but I can't be sure that there isn't */ /* some degree of sparsity out there in the world. */ switch (outspeed) { /* 1 second of padding */ #ifdef BEXTA case BEXTA: just_a_sec = 1920; break; #else #ifdef B19200 case B19200: just_a_sec = 1920; break; #endif #endif case B9600: just_a_sec = 960; break; case B4800: just_a_sec = 480; break; case B2400: just_a_sec = 240; break; case B1800: just_a_sec = 180; break; case B1200: just_a_sec = 120; break; case B600: just_a_sec = 60; break; case B300: just_a_sec = 30; break; /* do I really have to type the rest of this??? */ case B200: just_a_sec = 20; break; case B150: just_a_sec = 15; break; case B134: just_a_sec = 13; break; case B110: just_a_sec = 11; break; case B75: just_a_sec = 8; break; case B50: just_a_sec = 5; break; default: just_a_sec = 960; break; /* if we are running detached I */ } /* don't want to know about it! */ } #ifdef PENDING # if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS) int devtty; # endif #endif /* set terminal characteristics */ void term_set(tcbuf) char* tcbuf; /* temp area for "uncompiled" termcap entry */ { char* tmpaddr; /* must not be register */ register char* tmpstr; char* s; int status; #ifdef TIOCGWINSZ struct winsize winsize; #endif #ifdef PENDING #if !defined (FIONREAD) && !defined (HAS_RDCHK) && !defined(MSDOS) /* do no delay reads on something that always gets closed on exit */ devtty = fileno(stdin); if (isatty(devtty)) { devtty = open("/dev/tty",0); if (devtty < 0) { printf(cantopen,"/dev/tty") FLUSH; finalize(1); } fcntl(devtty,F_SETFL,O_NDELAY); } #endif #endif /* get all that good termcap stuff */ #ifdef HAS_TERMLIB #ifdef MSDOS tc_BC = "\b"; tc_UP = "\033[A"; tc_CR = "\r"; tc_VB = nullstr; tc_CL = "\033[H\033[2J"; tc_CE = "\033[K"; tc_TI = nullstr; tc_TE = nullstr; tc_KS = nullstr; tc_KE = nullstr; tc_CM = "\033[%d;%dH"; tc_HO = "\033[H"; tc_IL = nullstr; /*$$*/ tc_CD = nullstr; /*$$*/ tc_SO = "\033[7m"; tc_SE = "\033[m"; tc_US = "\033[7m"; tc_UE = "\033[m"; tc_UC = nullstr; set_lines_and_cols(); tc_AM = TRUE; #else s = getenv("TERM"); status = tgetent(tcbuf,s? s : "dumb"); /* get termcap entry */ if (status < 1) { printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH; finalize(1); } tmpaddr = tcarea; /* set up strange tgetstr pointer */ s = Tgetstr("pc"); /* get pad character */ tc_PC = *s; /* get it where tputs wants it */ if (!tgetflag("bs")) { /* is backspace not used? */ tc_BC = Tgetstr("bc"); /* find out what is */ if (tc_BC == nullstr) { /* terminfo grok's 'bs' but not 'bc' */ tc_BC = Tgetstr("le"); if (tc_BC == nullstr) tc_BC = "\b"; /* better than nothing... */ } } else tc_BC = "\b"; /* make a backspace handy */ tc_UP = Tgetstr("up"); /* move up a line */ tc_CL = Tgetstr("cl"); /* get clear string */ tc_CE = Tgetstr("ce"); /* clear to end of line string */ tc_TI = Tgetstr("ti"); /* initialize display */ tc_TE = Tgetstr("te"); /* reset display */ tc_KS = Tgetstr("ks"); /* enter `keypad transmit' mode */ tc_KE = Tgetstr("ke"); /* exit `keypad transmit' mode */ tc_HO = Tgetstr("ho"); /* home cursor */ tc_IL = Tgetstr("al"); /* insert (add) line */ tc_CM = Tgetstr("cm"); /* cursor motion */ tc_CD = Tgetstr("cd"); /* clear to end of display */ if (!*tc_CE) tc_CE = tc_CD; tc_SO = Tgetstr("so"); /* begin standout */ tc_SE = Tgetstr("se"); /* end standout */ if ((tc_SG = tgetnum("sg"))<0) tc_SG = 0; /* blanks left by SG, SE */ tc_US = Tgetstr("us"); /* start underline */ tc_UE = Tgetstr("ue"); /* end underline */ if ((tc_UG = tgetnum("ug"))<0) tc_UG = 0; /* blanks left by US, UE */ if (*tc_US) tc_UC = nullstr; /* UC must not be NULL */ else tc_UC = Tgetstr("uc"); /* underline a character */ if (!*tc_US && !*tc_UC) { /* no underline mode? */ tc_US = tc_SO; /* substitute standout mode */ tc_UE = tc_SE; tc_UG = tc_SG; } tc_LINES = tgetnum("li"); /* lines per page */ tc_COLS = tgetnum("co"); /* columns on page */ #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) { tc_LINES = ws.ws_row; tc_COLS = ws.ws_col; } } #endif tc_AM = tgetflag("am"); /* terminal wraps automatically? */ tc_XN = tgetflag("xn"); /* then eats next newline? */ tc_VB = Tgetstr("vb"); if (!*tc_VB) tc_VB = "\007"; tc_CR = Tgetstr("cr"); if (!*tc_CR) { if (tgetflag("nc") && *tc_UP) { tc_CR = safemalloc((MEM_SIZE)strlen(tc_UP)+2); sprintf(tc_CR,"%s\r",tc_UP); } else tc_CR = "\r"; } #ifdef TIOCGWINSZ if (ioctl(1, TIOCGWINSZ, &winsize) >= 0) { if (winsize.ws_row > 0) tc_LINES = winsize.ws_row; if (winsize.ws_col > 0) tc_COLS = winsize.ws_col; } # endif #endif if (!*tc_UP) /* no UP string? */ marking = 0; /* disable any marking */ if (*tc_CM || *tc_HO) can_home = TRUE; if (!*tc_CD || !can_home) /* can we CE, CD, and home? */ erase_each_line = FALSE; /* no, so disable use of clear eol */ if (muck_up_clear) /* this is for weird HPs */ tc_CL = NULL; leftcost = strlen(tc_BC); upcost = strlen(tc_UP); #else /* !HAS_TERMLIB */ ..."Don't know how to set the terminal!" #endif /* !HAS_TERMLIB */ termlib_init(); line_col_calcs(); noecho(); /* turn off echo */ crmode(); /* enter cbreak mode */ sprintf(buf, "%d", tc_LINES); lines_export = export("LINES",buf); sprintf(buf, "%d", tc_COLS); cols_export = export("COLUMNS",buf); mac_init(tcbuf); } #ifdef MSDOS static void set_lines_and_cols() { gotoxy(132,1); if (wherex() == 132) tc_COLS = 132; else tc_COLS = 80; gotoxy(1,50); if (wherey() == 50) tc_LINES = 50; else { gotoxy(1,43); if (wherey() == 50) tc_LINES = 50; else tc_LINES = 25; } } #endif void set_macro(seq,def) char* seq; /* input sequence of keys */ char* def; /* definition */ { mac_line(def,seq,0); /* check for common (?) brain damage: ku/kd/etc sequence may be the * cursor move sequence instead of the input sequence. * (This happens on the local xterm definitions.) * Try to recognize and adjust for this case. */ if (seq[0] == '\033' && seq[1] == '[' && seq[2]) { char lbuf[LBUFLEN]; /* copy of possibly non-writable string */ strcpy(lbuf,seq); lbuf[1] = 'O'; mac_line(def,lbuf,0); } if (seq[0] == '\033' && seq[1] == 'O' && seq[2]) { char lbuf[LBUFLEN]; /* copy of possibly non-writable string */ strcpy(lbuf,seq); lbuf[1] = '['; mac_line(def,lbuf,0); } } char* up[] = { "^@", /* '(' at article or pager, '[' in thread sel, 'p' otherwise */ "%(%m=[ap]?\\(:%(%m=t?[:p))", /* '(' at article or pager, '[' in thread sel, 'p' otherwise */ "%(%m=[ap]?\\(:%(%m=t?[:p))" }; char* down[] = { "^@", /* ')' at article or pager, ']' in thread sel, 'n' otherwise */ "%(%m=[ap]?\\):%(%m=t?]:n))", /* ')' at article or pager, ']' in thread sel, 'n' otherwise */ "%(%m=[ap]?\\):%(%m=t?]:n))" }; char* left[] = { "^@", /* '[' at article or pager, 'Q' otherwise */ "%(%m=[ap]?\\[:Q)", /* '[' at article or pager, '<' otherwise */ "%(%m=[ap]?\\[:<)" }; char* right[] = { "^@", /* ']' at article or pager, CR otherwise */ "%(%m=[ap]?\\]:^j)", /* CR at newsgroups, ']' at article or pager, '>' otherwise */ "%(%m=n?^j:%(%m=[ap]?\\]:>))" }; /* Turn the arrow keys into macros that do some basic trn functions. ** Code provided by Clifford Adams. */ void arrow_macros(tmpbuf) char* tmpbuf; { #ifdef HAS_TERMLIB char lbuf[256]; /* should be long enough */ #ifndef MSDOS char* tmpaddr = tmpbuf; #endif register char* tmpstr; /* If arrows are defined as single keys, we probably don't * want to redefine them. (The tvi912c defines kl as ^H) */ #ifdef MSDOS strcpy(lbuf,"\035\110"); #else strcpy(lbuf,Tgetstr("ku")); /* up */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,up[auto_arrow_macros]); #ifdef MSDOS strcpy(lbuf,"\035\120"); #else strcpy(lbuf,Tgetstr("kd")); /* down */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,down[auto_arrow_macros]); #ifdef MSDOS strcpy(lbuf,"\035\113"); #else strcpy(lbuf,Tgetstr("kl")); /* left */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,left[auto_arrow_macros]); #ifdef MSDOS strcpy(lbuf,"\035\115"); #else strcpy(lbuf,Tgetstr("kr")); /* right */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,right[auto_arrow_macros]); if (*lbuf == '\033') set_macro("\033\033", "\033"); #endif } static void mac_init(tcbuf) char* tcbuf; { char tmpbuf[1024]; if (auto_arrow_macros) arrow_macros(tmpbuf); if (!use_threads || (tmpfp = fopen(filexp(getval("TRNMACRO",TRNMACRO)),"r")) == NULL) tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r"); if (tmpfp) { while (fgets(tcbuf,TCBUF_SIZE,tmpfp) != NULL) mac_line(tcbuf,tmpbuf,sizeof tmpbuf); fclose(tmpfp); } } void mac_line(line,tmpbuf,tbsize) char* line; char* tmpbuf; int tbsize; { register char* s; register char* m; register KEYMAP* curmap; register int ch; register int garbage = 0; static char override[] = "\nkeymap overrides string\n"; if (topmap == NULL) topmap = newkeymap(); if (*line == '#' || *line == '\n') return; if (line[ch = strlen(line)-1] == '\n') line[ch] = '\0'; /* A 0 length signifies we already parsed the macro into tmpbuf, ** so line is just the definition. */ if (tbsize) m = dointerp(tmpbuf,tbsize,line," \t",(char*)NULL); else m = line; if (!*m) return; while (*m == ' ' || *m == '\t') m++; for (s=tmpbuf,curmap=topmap; *s; s++) { ch = *s & 0177; if (s[1] == '+' && isdigit(s[2])) { s += 2; garbage = (*s & KM_GMASK) << KM_GSHIFT; } else garbage = 0; if (s[1]) { if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) { if (tbsize) { fputs(override,stdout) FLUSH; termdown(2); } free(curmap->km_ptr[ch].km_str); curmap->km_ptr[ch].km_str = NULL; } curmap->km_type[ch] = KM_KEYMAP + garbage; if (curmap->km_ptr[ch].km_km == NULL) curmap->km_ptr[ch].km_km = newkeymap(); curmap = curmap->km_ptr[ch].km_km; } else { if (tbsize && (curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP) { fputs(override,stdout) FLUSH; termdown(2); } else { curmap->km_type[ch] = KM_STRING + garbage; curmap->km_ptr[ch].km_str = savestr(m); } } } } static KEYMAP* newkeymap() { register int i; register KEYMAP* map; #ifndef lint map = (KEYMAP*)safemalloc(sizeof(KEYMAP)); #else map = NULL; #endif /* lint */ for (i = 127; i >= 0; i--) { map->km_ptr[i].km_km = NULL; map->km_type[i] = KM_NOTHIN; } return map; } void show_macros() { char prebuf[64]; if (topmap != NULL) { print_lines("Macros:\n",STANDOUT); *prebuf = '\0'; show_keymap(topmap,prebuf); } else { print_lines("No macros defined.\n", NOMARKING); } } static void show_keymap(curmap,prefix) register KEYMAP* curmap; char* prefix; { register int i; register char* next = prefix + strlen(prefix); register int kt; for (i = 0; i < 128; i++) { if ((kt = curmap->km_type[i]) != 0) { if (i < ' ') sprintf(next,"^%c",i+64); else if (i == ' ') strcpy(next,"\\040"); else if (i == 127) strcpy(next,"^?"); else sprintf(next,"%c",i); if ((kt >> KM_GSHIFT) & KM_GMASK) { sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK); strcat(next,cmd_buf); } switch (kt & KM_TMASK) { case KM_NOTHIN: sprintf(cmd_buf,"%s %c\n",prefix,i); print_lines(cmd_buf,NOMARKING); break; case KM_KEYMAP: show_keymap(curmap->km_ptr[i].km_km, prefix); break; case KM_STRING: sprintf(cmd_buf,"%s %s\n",prefix,curmap->km_ptr[i].km_str); print_lines(cmd_buf,NOMARKING); break; case KM_BOGUS: sprintf(cmd_buf,"%s BOGUS\n",prefix); print_lines(cmd_buf,STANDOUT); break; } } } } void set_mode(new_gmode, new_mode) char_int new_gmode; char_int new_mode; { if (gmode != new_gmode || mode != new_mode) { gmode = new_gmode; mode = new_mode; xmouse_check(); } } /* routine to pass to tputs */ int putchr(ch) register char_int ch; { putchar(ch); #ifdef lint ch = '\0'; ch = ch; #endif return 0; } int not_echoing = 0; void hide_pending() { not_echoing = 1; pushchar(0200); } bool finput_pending(check_term) bool_int check_term; { while (nextout != nextin) { if (circlebuf[nextout] != '\200') return 1; switch (not_echoing) { case 0: return 1; case 1: nextout++; nextout %= PUSHSIZE; not_echoing = 0; break; default: circlebuf[nextout] = '\n'; not_echoing = 0; return 1; } } #ifdef PENDING #ifdef USE_TK if (check_term && ttk_running) { ttk_do_waiting_events(); /* Update screen, process events. */ if (ttk_keys && *ttk_keys) return 1; } #endif if (check_term) { # ifdef FIONREAD long iocount; ioctl(0, FIONREAD, &iocount); return (int)iocount; # else /* !FIONREAD */ # ifdef HAS_RDCHK return rdchk(0); # else /* !HAS_RDCHK */ # ifdef MSDOS return kbhit(); # else /* !MSDOS */ return circfill(); # endif /* !MSDOS */ # endif /* !HAS_RDCHK */ # endif /* !FIONREAD */ } # endif /* !PENDING */ return 0; } /* input the 2nd and succeeding characters of a multi-character command */ /* returns TRUE if command finished, FALSE if they rubbed out first character */ int buflimit = LBUFLEN; bool finish_command(donewline) int donewline; { register char* s; char gmode_save = gmode; s = buf; if (s[1] != FINISHCMD) /* someone faking up a command? */ return TRUE; set_mode('i',mode); if (not_echoing) not_echoing = 2; do { s = edit_buf(s, buf); if (s == buf) { /* entire string gone? */ fflush(stdout); /* return to single char command mode */ set_mode(gmode_save,mode); return FALSE; } if (s - buf == buflimit) break; fflush(stdout); getcmd(s); if (errno || *s == '\f') { *s = Ctl('r'); /* force rewrite on CONT */ } } while (*s != '\r' && *s != '\n'); /* until CR or NL (not echoed) */ mouse_is_down = FALSE; while (s[-1] == ' ') s--; *s = '\0'; /* terminate the string nicely */ if (donewline) newline(); set_mode(gmode_save,mode); return TRUE; /* retrn success */ } static int echo_char(ch) char_int ch; { if (((Uchar)ch & 0x7F) < ' ') { putchar('^'); putchar((ch & 0x7F) | 64); return 2; } if (ch == '\177') { putchar('^'); putchar('?'); return 2; } putchar(ch); return 1; } static bool screen_is_dirty; /*$$ remove this? */ /* Process the character *s in the buffer buf returning the new 's' */ char* edit_buf(s, cmd) register char* s; char* cmd; { static bool quoteone = FALSE; if (quoteone) { quoteone = FALSE; if (s != buf) goto echo_it; } if (*s == '\033') { /* substitution desired? */ #ifdef ESCSUBS char tmpbuf[4], *cpybuf; tmpbuf[0] = '%'; read_tty(&tmpbuf[1],1); #ifdef RAWONLY tmpbuf[1] &= 0177; #endif tmpbuf[2] = '\0'; if (tmpbuf[1] == 'h') { (void) help_subs(); *s = '\0'; reprint(); } else if (tmpbuf[1] == '\033') { *s = '\0'; cpybuf = savestr(buf); interpsearch(buf, sizeof buf, cpybuf, cmd); free(cpybuf); s = buf + strlen(buf); reprint(); } else { interpsearch(s, sizeof buf - (s-buf), tmpbuf, cmd); fputs(s,stdout); s += strlen(s); } #else notincl("^["); *s = '\0'; reprint(); #endif return s; } else if (*s == ERASECH) { /* they want to rubout a char? */ if (s != buf) { rubout(); s--; /* discount the char rubbed out */ if (!AT_NORM_CHAR(s)) rubout(); } return s; } else if (*s == KILLCH) { /* wipe out the whole line? */ while (s != buf) { /* emulate that many ERASEs */ rubout(); s--; if (!AT_NORM_CHAR(s)) rubout(); } return s; } #ifdef WORDERASE else if (*s == Ctl('w')) { /* wipe out one word? */ if (s == buf) return s; *s-- = ' '; while (!isspace(*s) || isspace(s[1])) { rubout(); if (!AT_NORM_CHAR(s)) rubout(); if (s == buf) return buf; s--; } return s+1; } #endif else if (*s == Ctl('r')) { *s = '\0'; reprint(); return s; } else if (*s == Ctl('v')) { putchar('^'); backspace(); fflush(stdout); getcmd(s); } else if (*s == '\\') quoteone = TRUE; echo_it: if (!not_echoing) echo_char(*s); return s+1; } bool finish_dblchar() { bool ret; int buflimit_save = buflimit; int not_echoing_save = not_echoing; buflimit = 2; ret = finish_command(FALSE); buflimit = buflimit_save; not_echoing = not_echoing_save; return ret; } /* discard any characters typed ahead */ void eat_typeahead() { static double last_time = 0.; double this_time = current_time(); /* do not eat typeahead while creating virtual group */ if (univ_ng_virtflag) return; /* Don't eat twice before getting a character */ if (!got_a_char) return; got_a_char = FALSE; /* cancel only keyboard stuff */ if (!allow_typeahead && !mouse_is_down && !macro_pending() && this_time - last_time > 0.3) { #ifdef PENDING register KEYMAP* curmap = topmap; Uchar lc; int i, j; for (j = 0; input_pending(); ) { errno = 0; if (read_tty(&buf[j],1) < 0) { if (errno && errno != EINTR) { perror(readerr); sig_catcher(0); } continue; } lc = *(Uchar*)buf; if ((lc & 0200) || curmap == NULL) { curmap = topmap; j = 0; continue; } j++; for (i = (curmap->km_type[lc] >> KM_GSHIFT) & KM_GMASK; i; i--) { if (!input_pending()) goto dbl_break; read_tty(&buf[j++],1); } switch (curmap->km_type[lc] & KM_TMASK) { case KM_STRING: /* a string? */ case KM_NOTHIN: /* no entry? */ curmap = topmap; j = 0; continue; case KM_KEYMAP: /* another keymap? */ curmap = curmap->km_ptr[lc].km_km; break; } } dbl_break: if (j) { /* Don't delete a partial macro sequence */ buf[j] = '\0'; pushstring(buf,0); } #else /* this is probably v7 */ # ifdef I_SGTTY ioctl(_tty_ch,TIOCSETP,&_tty); # else # ifdef I_TERMIO ioctl(_tty_ch,TCSETAW,&_tty); # else # ifdef I_TERMIOS tcsetattr(_tty_ch,TCSAFLUSH,&_tty); # else ..."Don't know how to eat typeahead!" # endif # endif # endif #endif } last_time = this_time; } void save_typeahead(buf, len) char* buf; int len; { int cnt; while (input_pending()) { cnt = read_tty(buf, len); buf += cnt; len -= cnt; } *buf = '\0'; } void settle_down() { dingaling(); fflush(stdout); /*sleep(1);*/ nextout = nextin; /* empty circlebuf */ not_echoing = 0; eat_typeahead(); } #ifdef SUPPORT_NNTP bool ignore_EINTR = FALSE; Signal_t alarm_catcher(signo) int signo; { /*printf("\n*** In alarm catcher **\n"); $$*/ ignore_EINTR = TRUE; check_datasrcs(); sigset(SIGALRM,alarm_catcher); (void) alarm(DATASRC_ALARM_SECS); } #endif /* read a character from the terminal, with multi-character pushback */ int read_tty(addr,size) char* addr; int size; { if (macro_pending()) { *addr = circlebuf[nextout++]; nextout %= PUSHSIZE; return 1; } #ifdef USE_TK if (ttk_running) { ttk_wait_for_input(); /* handle events until input is available */ if (ttk_keys && *ttk_keys) { int len = strlen(ttk_keys); if (size > len) size = len; strncpy(addr, ttk_keys, size); /* return the first bit */ if (len > size) pushstring(ttk_keys+size,0); /* and push the rest */ free(ttk_keys); /* every byte counts... */ /* A plain NULL pointer will not work -- it is "\0" in TCL */ ttk_keys = savestr(nullstr); return size; } } #endif #ifdef MSDOS *addr = getch(); if (*addr == '\0') *addr = Ctl('\035'); size = 1; #else size = read(0,addr,size); #endif #ifdef RAWONLY *addr &= 0177; #endif got_a_char = TRUE; return size; } #ifdef PENDING # if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS) int circfill() { register int Howmany; errno = 0; Howmany = read(devtty,circlebuf+nextin,1); if (Howmany < 0 && (errno == EAGAIN || errno == EINTR)) Howmany = 0; if (Howmany) { nextin += Howmany; nextin %= PUSHSIZE; } return Howmany; } # endif /* FIONREAD */ #endif /* PENDING */ void pushchar(c) char_int c; { nextout--; if (nextout < 0) nextout = PUSHSIZE - 1; if (nextout == nextin) { fputs("\npushback buffer overflow\n",stdout) FLUSH; sig_catcher(0); } circlebuf[nextout] = c; } /* print an underlined string, one way or another */ void underprint(s) register char* s; { assert(tc_UC); if (*tc_UC) { /* char by char underline? */ while (*s) { if (!AT_NORM_CHAR(s)) { putchar('^'); backspace();/* back up over it */ underchar();/* and do the underline */ putchar((*s & 0x7F) | 64); backspace();/* back up over it */ underchar();/* and do the underline */ } else { putchar(*s); backspace();/* back up over it */ underchar();/* and do the underline */ } s++; } } else { /* start and stop underline */ underline(); /* start underlining */ while (*s) echo_char(*s++); un_underline(); /* stop underlining */ } } /* keep screen from flashing strangely on magic cookie terminals */ #ifdef NOFIREWORKS void no_sofire() { /* should we disable fireworks? */ if (!(fire_is_out & STANDOUT) && (term_line|term_col)==0 && *tc_UP && *tc_SE) { newline(); un_standout(); up_line(); carriage_return(); } } #endif #ifdef NOFIREWORKS void no_ulfire() { /* should we disable fireworks? */ if (!(fire_is_out & UNDERLINE) && (term_line|term_col)==0 && *tc_UP && *tc_US) { newline(); un_underline(); up_line(); carriage_return(); } } #endif /* get a character into a buffer */ void getcmd(whatbuf) register char* whatbuf; { register KEYMAP* curmap; register int i; bool no_macros; int times = 0; /* loop detector */ #ifdef SUPPORT_NNTP if (!input_pending()) { sigset(SIGALRM,alarm_catcher); (void) alarm(DATASRC_ALARM_SECS); } #endif tryagain: curmap = topmap; #ifdef OLD_RN_WAY no_macros = (whatbuf != buf && !macro_pending()); #else no_macros = (whatbuf != buf && !xmouse_is_on); #endif for (;;) { int_count = 0; errno = 0; #ifdef SUPPORT_NNTP ignore_EINTR = FALSE; #endif if (read_tty(whatbuf,1) < 0) { if (!errno) errno = EINTR; if (errno == EINTR) { #ifdef SUPPORT_NNTP if (ignore_EINTR) continue; (void) alarm(0); #endif return; } perror(readerr); sig_catcher(0); } lastchar = *(Uchar*)whatbuf; if (lastchar & 0200 || no_macros) { *whatbuf &= 0177; goto got_canonical; } if (curmap == NULL) goto got_canonical; for (i = (curmap->km_type[lastchar] >> KM_GSHIFT) & KM_GMASK; i; i--) read_tty(&whatbuf[i],1); switch (curmap->km_type[lastchar] & KM_TMASK) { case KM_NOTHIN: /* no entry? */ if (curmap == topmap) /* unmapped canonical */ goto got_canonical; settle_down(); goto tryagain; case KM_KEYMAP: /* another keymap? */ curmap = curmap->km_ptr[lastchar].km_km; assert(curmap != NULL); break; case KM_STRING: /* a string? */ pushstring(curmap->km_ptr[lastchar].km_str,0200); if (++times > 20) { /* loop? */ fputs("\nmacro loop?\n",stdout); termdown(2); settle_down(); } no_macros = FALSE; goto tryagain; } } got_canonical: /* This hack is for mouse support */ if (xmouse_is_on && *whatbuf == Ctl('c')) { mouse_input(whatbuf+1); times = 0; goto tryagain; } #ifdef I_SGTTY if (*whatbuf == '\r') *whatbuf = '\n'; #endif if (whatbuf == buf) whatbuf[1] = FINISHCMD; /* tell finish_command to work */ #ifdef SUPPORT_NNTP (void) alarm(0); #endif } void pushstring(str,bits) char* str; char_int bits; { register int i; char tmpbuf[PUSHSIZE]; register char* s = tmpbuf; assert(str != NULL); interp(tmpbuf,PUSHSIZE,str); for (i = strlen(s)-1; i >= 0; i--) pushchar(s[i] ^ bits); } int get_anything() { char tmpbuf[64]; char mode_save = mode; reask_anything: unflush_output(); /* disable any ^O in effect */ color_object(COLOR_MORE, 1); #ifdef VERBOSE IF(verbose) fputs("[Type space to continue] ",stdout); ELSE #endif #ifdef TERSE fputs("[MORE] ",stdout); #endif color_pop(); /* of COLOR_MORE */ fflush(stdout); eat_typeahead(); if (int_count) return -1; cache_until_key(); set_mode(gmode, 'K'); getcmd(tmpbuf); set_mode(gmode,mode_save); if (errno || *tmpbuf == '\f') { newline(); /* if return from stop signal */ goto reask_anything; /* give them a prompt again */ } if (*tmpbuf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\nType q to quit or space to continue.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nq to quit, space to continue.\n",stdout) FLUSH; #endif termdown(2); goto reask_anything; } else if (*tmpbuf != ' ' && *tmpbuf != '\n') { erase_line(0); /* erase the prompt */ return *tmpbuf == 'q' ? -1 : *tmpbuf; } if (*tmpbuf == '\n') { page_line = tc_LINES - 1; erase_line(0); } else { page_line = 1; if (erase_screen) /* -e? */ clear(); /* clear screen */ else erase_line(0); /* erase the prompt */ } return 0; } int pause_getcmd() { char mode_save = mode; unflush_output(); /* disable any ^O in effect */ color_object(COLOR_CMD, 1); #ifdef VERBOSE IF(verbose) fputs("[Type space or a command] ",stdout); ELSE #endif #ifdef TERSE fputs("[CMD] ",stdout); #endif color_pop(); /* of COLOR_CMD */ fflush(stdout); eat_typeahead(); if (int_count) return -1; cache_until_key(); set_mode(gmode,'K'); getcmd(buf); set_mode(gmode,mode_save); if (errno || *buf == '\f') return 0; /* if return from stop signal */ if (*buf != ' ') { erase_line(0); /* erase the prompt */ return *buf; } return 0; } void in_char(prompt, newmode, dflt) char* prompt; char_int newmode; char* dflt; { char mode_save = mode; char gmode_save = gmode; char* s; int newlines; for (newlines = 0, s = prompt; *s; s++) { if (*s == '\n') newlines++; } reask_in_char: unflush_output(); /* disable any ^O in effect */ printf("%s [%s] ", prompt, dflt); fflush(stdout); termdown(newlines); eat_typeahead(); set_mode('p',newmode); getcmd(buf); if (errno || *buf == '\f') { newline(); /* if return from stop signal */ goto reask_in_char; /* give them a prompt again */ } setdef(buf,dflt); set_mode(gmode_save,mode_save); } void in_answer(prompt, newmode) char* prompt; char_int newmode; { char mode_save = mode; char gmode_save = gmode; reask_in_answer: unflush_output(); /* disable any ^O in effect */ fputs(prompt,stdout); fflush(stdout); eat_typeahead(); set_mode('i',newmode); reinp_in_answer: getcmd(buf); if (errno || *buf == '\f') { newline(); /* if return from stop signal */ goto reask_in_answer; /* give them a prompt again */ } if (*buf == ERASECH) goto reinp_in_answer; if (*buf != '\n' && *buf != ' ') { if (!finish_command(FALSE)) goto reinp_in_answer; } else buf[1] = '\0'; newline(); set_mode(gmode_save,mode_save); } /* If this takes more than one line, return FALSE */ bool in_choice(prompt, value, choices, newmode) char* prompt; char* value; char* choices; char_int newmode; { char mode_save = mode; char gmode_save = gmode; char* cp; char* bp; char* s; char* prefix = NULL; int len, number_was = -1, any_val_OK = 0, value_changed; char tmpbuf[80], prefixes[80]; unflush_output(); /* disable any ^O in effect */ eat_typeahead(); set_mode('c',newmode); screen_is_dirty = FALSE; cp = choices; if (*cp == '[') { for (s = prefixes, cp++; *cp != ']'; ) { if (*cp == '/') { *s++ = '\0'; cp++; } else *s++ = *cp++; } *s++ = '\0'; *s = '\0'; if (*++cp == ' ') cp++; } else *prefixes = '\0'; for (s = tmpbuf; *cp; ) { if (*cp == '/') { *s++ = '\0'; cp++; } else if (*cp == '<') { do { *s++ = *cp; } while (*cp++ != '>'); any_val_OK = 1; /* flag that '<' was found */ } else *s++ = *cp++; } cp = s; *s++ = '\0'; *s = '\0'; strcpy(buf,value); reask_in_choice: len = strlen(buf); bp = buf; if (*prefixes != '\0') { s = prefix; for (prefix = prefixes; *prefix; prefix += strlen(prefix)) { if (*prefix == *buf) break; } if (*prefix) { for (bp = buf; *bp && *bp != ' '; bp++) ; while (*bp == ' ') bp++; } else prefix = NULL; value_changed = prefix != s; } else { prefix = NULL; value_changed = 0; } s = cp; for (;;) { cp += strlen(cp) + 1; if (!*cp) cp = tmpbuf; if (*cp == '<' && (*buf == '<' || cp[1] != '#' || isdigit(*buf) || !*s)) { prefix = NULL; break; } if (s == cp) { if (!value_changed) { if (prefix) prefix = NULL; else dingaling(); } break; } if (!*bp || strnEQ(cp,bp,any_val_OK? len : 1)) break; } if (*cp == '<') { if (*buf == '<' || cp[1] == '#') { if (number_was >= 0) sprintf(buf, "%d", number_was); else { for (s = buf; isdigit(*s); s++) ; *s = '\0'; } } } else { if (prefix) { sprintf(buf, "%s ", prefix); strcat(buf,cp); } else strcpy(buf,cp); } s = buf + strlen(buf); carriage_return(); erase_line(0); fputs(prompt,stdout); fputs(buf,stdout); len = strlen(prompt); number_was = -1; reinp_in_choice: if ((s-buf) + len >= tc_COLS) screen_is_dirty = TRUE; fflush(stdout); getcmd(s); if (errno || *s == '\f') /* if return from stop signal */ *s = '\n'; if (*s != '\n') { char ch = *s; if (*cp == '<' && ch != '\t' && (ch != ' ' || buf != s)) { if (cp[1] == '#') { s = edit_buf(s, (char*)NULL); if (s != buf) { if (isdigit(s[-1])) goto reinp_in_choice; else number_was = atoi(buf); } } else { s = edit_buf(s, (char*)NULL); goto reinp_in_choice; } } *s = '\0'; for (s = buf; *s && *s != ' '; s++) ; if (*s == ' ') s++; if (ch == ' ' || ch == '\t') { if (prefix) *s = '\0'; else *buf = '\0'; } else { char ch1 = buf[0]; if (prefix) { if (ch == ch1) ch = *s; else { ch1 = ch; ch = buf[0]; } } sprintf(buf,"%c %c",ch == ERASECH || ch == KILLCH? '<' : ch, ch1); } goto reask_in_choice; } *s = '\0'; set_mode(gmode_save,mode_save); return !screen_is_dirty; } int print_lines(what_to_print,hilite) char* what_to_print; int hilite; { register char* s; register int i; for (s=what_to_print; *s; ) { i = check_page_line(); if (i) return i; if (hilite == STANDOUT) { #ifdef NOFIREWORKS if (erase_screen) no_sofire(); #endif standout(); } else if (hilite == UNDERLINE) { #ifdef NOFIREWORKS if (erase_screen) no_ulfire(); #endif underline(); } for (i = 0; i < tc_COLS; i++) { if (!*s) break; if (AT_NORM_CHAR(s)) putchar(*s); else if (*s == '\t') { putchar(*s); i = ((i+8) & ~7) - 1; } else if (*s == '\n') { i = 32000; } else { i++; putchar('^'); putchar(*s + 64); } s++; } if (i) { if (hilite == STANDOUT) un_standout(); else if (hilite == UNDERLINE) un_underline(); if (tc_AM && i == tc_COLS) fflush(stdout); else newline(); } } return 0; } int check_page_line() { if (page_line < 0) return -1; if (page_line >= tc_LINES || int_count) { register int cmd = -1; if (int_count || (cmd = get_anything())) { page_line = -1; /* disable further printing */ if (cmd > 0) pushchar(cmd); return cmd; } } page_line++; return 0; } void page_start() { page_line = 1; if (erase_screen) clear(); else newline(); } void errormsg(str) char* str; { if (gmode == 's') { if (str != msg) strcpy(msg,str); error_occurred = TRUE; } else if (*str) { printf("\n%s\n", str) FLUSH; termdown(2); } } void warnmsg(str) char* str; { if (gmode != 's') { printf("\n%s\n", str) FLUSH; termdown(2); pad(just_a_sec/3); } } void pad(num) int num; { register int i; for (i = num; i; i--) putchar(tc_PC); fflush(stdout); } /* echo the command just typed */ #ifdef VERIFY void printcmd() { if (verify && buf[1] == FINISHCMD) { if (!AT_NORM_CHAR(buf)) { putchar('^'); putchar((*buf & 0x7F) | 64); backspace(); backspace(); } else { putchar(*buf); backspace(); } fflush(stdout); } } #endif void rubout() { backspace(); /* do the old backspace, */ putchar(' '); /* space, */ backspace(); /* backspace trick */ } void reprint() { register char* s; fputs("^R\n",stdout) FLUSH; termdown(1); for (s = buf; *s; s++) echo_char(*s); screen_is_dirty = TRUE; } void erase_line(to_eos) bool_int to_eos; { carriage_return(); if (to_eos) clear_rest(); else erase_eol(); carriage_return(); /* Resets kernel's tab column counter to 0 */ fflush(stdout); } void clear() { term_line = term_col = fire_is_out = 0; if (tc_CL) tputs(tc_CL,tc_LINES,putchr); else if (tc_CD) { home_cursor(); tputs(tc_CD,tc_LINES,putchr); } else { int i; for (i = 0; i < tc_LINES; i++) putchr('\n'); home_cursor(); } tputs(tc_CR,1,putchr) FLUSH; } void home_cursor() { char* tgoto(); if (!*tc_HO) { /* no home sequence? */ if (!*tc_CM) { /* no cursor motion either? */ fputs("\n\n\n", stdout); termdown(3); return; /* forget it. */ } tputs(tgoto(tc_CM, 0, 0), 1, putchr); /* go to home via CM */ } else { /* we have home sequence */ tputs(tc_HO, 1, putchr);/* home via HO */ } carriage_return(); /* Resets kernel's tab column counter to 0 */ term_line = term_col = 0; } void goto_xy(to_col,to_line) int to_col; int to_line; { char* tgoto(); char* str; int cmcost, xcost, ycost; if (term_col == to_col && term_line == to_line) return; if (*tc_CM && !muck_up_clear) { str = tgoto(tc_CM,to_col,to_line); cmcost = strlen(str); } else { str = NULL; cmcost = 9999; } if ((ycost = (to_line - term_line)) < 0) ycost = (upcost? -ycost * upcost : 7777); else if (ycost > 0) term_col = 0; if ((xcost = (to_col - term_col)) < 0) { if (!to_col && ycost+1 < cmcost) { carriage_return(); xcost = 0; } else xcost = -xcost * leftcost; } else if (xcost > 0 && cmcost < 9999) xcost = 9999; if (cmcost <= xcost + ycost) { tputs(str,1,putchr); term_line = to_line; term_col = to_col; return; } if (ycost == 7777) home_cursor(); if (to_line >= term_line) while(term_line < to_line) newline(); else while(term_line > to_line) up_line(); if (to_col >= term_col) while(term_col < to_col) term_col++, putchar(' '); else while(term_col > to_col) term_col--, backspace(); } static void line_col_calcs() { if (tc_LINES > 0) { /* is this a crt? */ if (!initlines || !option_def_vals[OI_INITIAL_ARTICLE_LINES]) { /* no -i or unreasonable value for initlines */ if (outspeed >= B9600) /* whole page at >= 9600 baud */ initlines = tc_LINES; else if (outspeed >= B4800) /* 16 lines at 4800 */ initlines = 16; else /* otherwise just header */ initlines = 8; } /* Check for initlines bigger than the screen and fix it! */ if (initlines > tc_LINES) initlines = tc_LINES; } else { /* not a crt */ tc_LINES = 30000; /* so don't page */ tc_CL = "\n\n"; /* put a couple of lines between */ if (!initlines || !option_def_vals[OI_INITIAL_ARTICLE_LINES]) initlines = 8; /* make initlines reasonable */ } if (tc_COLS <= 0) tc_COLS = 80; #ifdef SCAN s_resize_win(); /* let various parts know */ #endif } #ifdef SIGWINCH Signal_t winch_catcher(dummy) int dummy; { /* Reset signal in case of System V dain bramage */ sigset(SIGWINCH, winch_catcher); /* Come here if window size change signal received */ #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) { if (tc_LINES != ws.ws_row || tc_COLS != ws.ws_col) { tc_LINES = ws.ws_row; tc_COLS = ws.ws_col; line_col_calcs(); sprintf(lines_export, "%d", tc_LINES); sprintf(cols_export, "%d", tc_COLS); if (gmode == 's' || mode == 'a' || mode == 'p') { forceme("\f"); /* cause a refresh */ /* (defined only if TIOCSTI defined) */ } } } } #else /* Well, if SIGWINCH is defined, but TIOCGWINSZ isn't, there's */ /* almost certainly something wrong. Figure it out for yourself, */ /* because I don't know how to deal with it :-) */ ERROR! #endif } #endif void termlib_init() { #ifdef USETITE if (tc_TI && *tc_TI) { tputs(tc_TI,1,putchr); fflush(stdout); } #endif #ifdef USEKSKE if (tc_KS && *tc_KS) { tputs(tc_KS,1,putchr); fflush(stdout); } #endif term_line = tc_LINES-1; term_col = 0; term_scrolled = tc_LINES; } void termlib_reset() { #ifdef USETITE if (tc_TE && *tc_TE) { tputs(tc_TE,1,putchr); fflush(stdout); } #endif #ifdef USEKSKE if (tc_KE && *tc_KE) { tputs(tc_KE,1,putchr); fflush(stdout); } #endif } /* Nice-Background routines used to improve interactive performance with * background processing. When called, these routines will wait for a * keystroke or a time limit to expire. Calling this before a * background loop may significantly increase responsiveness when a * user types commands quickly. */ /* NOTE: If you have problems, uncomment this define for some debugging help. * (The time limit will be 5 seconds and lots of info will be printed). */ /* #define DEBUG_NICEBG */ #ifdef PENDING #ifdef NICEBG #ifdef NBG_SELECT #undef NBG_TERMIO #undef NBG_SIGIO #endif #ifdef NBG_TERMIO #undef NBG_SELECT #undef NBG_SIGIO #endif #ifdef NBG_SIGIO #undef NBG_TERMIO #undef NBG_SELECT #endif #ifdef NBG_SIGIO /* SIGIO style */ Signal_t waitkey_sig_handler(dummy) int dummy; { #ifdef DEBUG_NICEBG /* CAA: I doubt that printf is safe in a signal handler... */ printf("wait_key_pause signal caught!\n") FLUSH; fflush(stdout); #endif } #endif /* NBG_SIGIO */ /* /dev/tty file descriptor */ /* note: for now we let the system close it on exit... */ static int wait_ttyfd INIT(-1); static bool wait_initialized INIT(FALSE); static bool wait_fd_opened INIT(FALSE); #ifdef NBG_SELECT static struct timeval wait_time; static struct fd_set wait_fdset; static int wait_tbl_size; #endif /* returns TRUE if input received */ bool wait_key_pause(ticks) int ticks; /* tenths of seconds to wait */ { #ifdef DEBUG_NICEBG ticks = 50; /* debugging: wait 5 seconds */ #endif if (input_pending()) return TRUE; #ifdef DEBUG_NICEBG printf("entry: wait_key_pause\n") FLUSH; /* */ #endif /* common initialization */ if (!wait_fd_opened) { wait_ttyfd = open("/dev/tty",0); /*$$ possible cron prob */ /* CAA: the open shouldn't really be a problem now that the * open return value is checked below. If running from cron, * I hope that the open will simply fail. If the open succeeds, * however, then there should be no cause for complaint by the * routines below. (The select method may be safest.) Still, * it might be nice to have some flag which means "we are running * in the background". */ wait_fd_opened = TRUE; } if (wait_ttyfd < 0) { /* tty open failed. Don't wait around */ return input_pending(); } #ifdef NBG_TERMIO /* First try a nice standard way */ { #ifdef I_TERMIO struct termio save_tty, wait_tty; #else /* must be TERMIOS then */ struct termios save_tty, wait_tty; #endif char lbuf[1]; /* for the read command */ int nrd; /* number read */ #ifdef DEBUG_NICEBG printf("wait_key_pause: using termio(s)\n") FLUSH; #endif if (!wait_initialized) wait_initialized = TRUE; if (ioctl(wait_ttyfd,TCGETA,&save_tty) == -1) { printf("wait_key_pause (term.c): ioctl TCGETA error\n") FLUSH; assert(FALSE); } wait_tty = save_tty; wait_tty.c_lflag &= ~ICANON; wait_tty.c_lflag &= ~ECHO; wait_tty.c_cc[VMIN] = 0; wait_tty.c_cc[VTIME] = ticks; if (ioctl(wait_ttyfd,TCSETAF,&wait_tty) == -1) { printf("wait_key_pause (term.c): ioctl TCSETAF error\n") FLUSH; assert(FALSE); } nrd = read(wait_ttyfd,&lbuf,1); ioctl(wait_ttyfd,TCSETAF,&save_tty); /* put back the old info */ if (nrd == 1) { pushchar(lbuf[0]); /* put it back */ #ifdef DEBUG_NICEBG printf("early exit: wait_key_pause\n") FLUSH; /* */ #endif } #ifdef DEBUG_NICEBG printf("exit: wait_key_pause\n") FLUSH; /* */ #endif return input_pending(); } #endif /* NBG_TERMIO */ #ifdef NBG_SELECT #ifdef DEBUG_NICEBG printf("wait_key_pause: using select\n") FLUSH; #endif if (!wait_initialized) { wait_tbl_size = wait_ttyfd + 1; wait_initialized = TRUE; } FD_ZERO(&wait_fdset); FD_SET(wait_ttyfd,&wait_fdset); wait_time.tv_usec = (ticks%10)*1000000; wait_time.tv_sec = ticks/10; (void)select(wait_tbl_size,&wait_fdset,NULL,NULL,&wait_time); #ifdef DEBUG_NICEBG printf("exit: wait_key_pause\n") FLUSH; /* */ #endif return input_pending(); #endif /* NBG_SELECT */ #ifdef NBG_SIGIO /* SIGIO signal sent for each keystroke style */ /* Method of last resort. Surprisingly, this code has been quite * portable to several systems. */ { #ifdef DEBUG_NICEBG printf("wait_key_pause: using SIGIO style\n") FLUSH; #endif if (!wait_initialized) { /* not initialized yet? */ /* Portable? (probably not) */ wait_initialized = TRUE; sigset(SIGALRM,waitkey_sig_handler); sigset(SIGIO,waitkey_sig_handler); /* enable SIGIO (some systems need it). ifdef it? */ fcntl(wait_ttyfd,F_SETOWN,(int)getpid()); fcntl(wait_ttyfd,F_SETFL,FASYNC); } (void) alarm((ticks+9)/10); /* sleep if no key pressed */ /* NOTE: There is a race condition here, but not a very important one IMO. * If the alarm signal is sent *before* pause() is called, then this * function will simply wait until a key is pressed. In this case * no background processing will occur. * Noted by CAA, August 1992. */ /* note2: The race condition can be minimized by using a variable * which will tell if the alarm arrived before the pause (really before * the variable was set, which should be near-instant.) Look into * this later. */ pause(); /* wait for either ALRM or SIGIO signal */ (void) alarm(0); #ifdef DEBUG_NICEBG printf("exit: wait_key_pause\n") FLUSH; #endif return input_pending(); } #endif /* NBG_SIGIO */ /* if no nice-background methods are defined, return. */ /* Thanks to Kian-Tat Lim for finding an earlier bug here */ #ifndef NBG_SELECT #ifndef NBG_TERMIO #ifndef NBG_SIGIO /* really kind of an error, but a reasonable way to handle it */ /* Act as if no input was present */ #ifdef DEBUG_NICEBG printf("wait_key_pause: no handler style, returning FALSE.\n"); #endif return FALSE; /* don't worry if not reached */ #endif #endif #endif } #endif /* NICEBG */ #endif /* PENDING */ void xmouse_init(progname) char* progname; { char* s; if (!can_home || !use_threads) return; s = getenv("XTERMMOUSE"); if (s && *s) { interp(msg, sizeof msg, s); set_option(OI_USE_MOUSE, msg); } else if (progname[strlen(progname)-1] == 'x') { /* an 'x' at the end means enable Xterm mouse tracking */ set_option(OI_USE_MOUSE, "y"); } } void xmouse_check() { mousebar_cnt = 0; if (UseMouse) { bool turn_it_on; char mmode = mode; if (gmode == 'p') { turn_it_on = TRUE; mmode = '\0'; } else if (gmode == 'i' || gmode == 'p' || (muck_up_clear && gmode != 's')) turn_it_on = FALSE; else { interp(msg, sizeof msg, MouseModes); turn_it_on = (index(msg, mode) != NULL); } if (turn_it_on) { char* s; int i, j; switch (mmode) { case 'c': mousebar_btns = NewsrcSelBtns; mousebar_cnt = NewsrcSelBtnCnt; break; case 'j': mousebar_btns = AddSelBtns; mousebar_cnt = AddSelBtnCnt; break; case 'l': mousebar_btns = OptionSelBtns; mousebar_cnt = OptionSelBtnCnt; break; case 't': mousebar_btns = NewsSelBtns; mousebar_cnt = NewsSelBtnCnt; break; case 'w': mousebar_btns = NewsgroupSelBtns; mousebar_cnt = NewsgroupSelBtnCnt; break; case 'a': case 'p': mousebar_btns = ArtPagerBtns; mousebar_cnt = ArtPagerBtnCnt; break; case 'v': mousebar_btns = UnivSelBtns; mousebar_cnt = UnivSelBtnCnt; break; default: mousebar_btns = nullstr; /*mousebar_cnt = 0;*/ break; } s = mousebar_btns; mousebar_width = 0; for (i = 0; i < mousebar_cnt; i++) { j = strlen(s); if (*s == '[') { mousebar_width += j; s += j + 1; j = strlen(s); } else mousebar_width += (j < 2? 3+1 : (j == 2? 4+1 : (j < 5? j: 5+1))); s += j + 1; } xmouse_on(); } else xmouse_off(); mouse_is_down = FALSE; } } void xmouse_on() { if (!xmouse_is_on) { /* save old highlight mouse tracking and enable mouse tracking */ fputs("\033[?1001s\033[?1000h",stdout); fflush(stdout); xmouse_is_on = TRUE; } } void xmouse_off() { if (xmouse_is_on) { /* disable mouse tracking and restore old highlight mouse tracking */ fputs("\033[?1000l\033[?1001r",stdout); fflush(stdout); xmouse_is_on = FALSE; } } void draw_mousebar(limit,restore_cursor) int limit; bool_int restore_cursor; { int i; char* s; char* t; int save_col = term_col; int save_line = term_line; mousebar_width = 0; if (mousebar_cnt == 0) return; s = mousebar_btns; t = msg; for (i = 0; i < mousebar_cnt; i++) { if (*s == '[') { while (*++s) *t++ = *s; s++; } else { switch (strlen(s)) { case 0: *t++ = ' '; /* FALL THROUGH */ case 1: case 2: *t++ = ' '; while (*s) *t++ = *s++; *t++ = ' '; break; case 3: case 4: while (*s) *t++ = *s++; break; default: strncpy(t, s, 5); t += 5; break; } } s += strlen(s) + 1; *t++ = '\0'; } mousebar_width = t - msg; mousebar_start = 0; s = msg; while (mousebar_width > limit) { int len = strlen(s) + 1; s += len; mousebar_width -= len; mousebar_start++; } goto_xy(tc_COLS - mousebar_width - 1, tc_LINES-1); for (i = mousebar_start; i < mousebar_cnt; i++) { putchar(' '); color_string(COLOR_MOUSE,s); s += strlen(s) + 1; } term_col = tc_COLS-1; if (restore_cursor) goto_xy(save_col, save_line); } static void mouse_input(cp) char* cp; { static int last_btn; static int last_x, last_y; int btn; int x, y; if (cp[2] < ' ' || cp[2] > ' '+3) return; btn = (cp[2] & 3); x = cp[1] - 33; y = cp[0] - 33; if (btn != 3) { #if defined(HAS_GETTIMEOFDAY) || defined(HAS_FTIME) static double last_time = 0.; double this_time = current_time(); if (last_btn == btn && last_y == y && this_time - last_time <= 0.75 && (last_x == x || last_x == x-1 || last_x == x+1)) btn |= 4; last_time = this_time; #endif /* HAS_GETTIMEOFDAY || HAS_FTIME */ last_btn = (btn & 3); last_x = x; last_y = y; mouse_is_down = TRUE; } else { if (!mouse_is_down) return; mouse_is_down = FALSE; } if (gmode == 's') selector_mouse(btn, x,y, last_btn, last_x,last_y); else if (mode == 'a' || mode == 'p') pager_mouse(btn, x,y, last_btn, last_x,last_y); else pushchar(' '); } bool check_mousebar(btn, x,y, btn_clk, x_clk,y_clk) int btn; int x, y; int btn_clk; int x_clk, y_clk; { char* s = mousebar_btns; char* t; int i, j; int col = tc_COLS - mousebar_width; if (mousebar_width != 0 && btn_clk == 0 && y_clk == tc_LINES-1 && (x_clk -= col-1) > 0) { x -= col-1; for (i = 0; i < mousebar_start; i++) { if (*s == '[') s += strlen(s) + 1; s += strlen(s) + 1; } while (1) { i = strlen(s); t = s; if (*s == '[') { s += i + 1; j = 0; while (*++t == ' ') j++; } else if (i <= 2) { i = 3 + (i==2) + 1; j = 1; } else { i = (i < 5? i+1 : 5+1); j = 0; } if (x_clk < i) { int tcol = term_col; int tline = term_line; goto_xy(col+j,tc_LINES-1); if (btn == 3) color_object(COLOR_MOUSE, 1); if (s == t) { for (j = 0; j < 5 && *t; j++, t++) term_col++, putchar(*t); } else { for (; *t && *t != ' '; t++) term_col++, putchar(*t); } if (btn == 3) color_pop(); /* of COLOR_MOUSE */ goto_xy(tcol,tline); fflush(stdout); if (btn == 3 && x > 0 && x < i) pushstring(s,0); break; } if (!(x_clk -= i)) break; x -= i; col += i; s += strlen(s) + 1; } return TRUE; } return FALSE; } static int tc_string_cnt = 0; static struct { char* capability; /* name of capability, e.g. "forground red" */ char* string; /* escape sequence, e.g. "\033[31m" */ } tc_strings[TC_STRINGS]; /* Parse a line from the [termcap] section of trnrc. */ void add_tc_string(capability, string) char* capability; char* string; { int i; for (i = 0; i < tc_string_cnt; i++) { if (strEQ(capability, tc_strings[i].capability)) { free(tc_strings[i].string); break; } } if (i == tc_string_cnt) { if (tc_string_cnt == TC_STRINGS) { fprintf(stderr,"trn: too many colors in [termcap] section (max is %d).\n", TC_STRINGS); finalize(1); } tc_string_cnt++; tc_strings[i].capability = savestr(capability); } tc_strings[i].string = savestr(string); } /* Return the named termcap color capability's string. */ char* tc_color_capability(capability) char* capability; { int c; for (c = 0; c < tc_string_cnt; c++) { if (strEQ(tc_strings[c].capability, capability)) return tc_strings[c].string; } return NULL; } #ifdef MSDOS int tputs(str,num,func) char* str; int num; int (*func) _((char_int)); { printf(str,num); return 0; } #endif #ifdef MSDOS char* tgoto(str,x,y) char* str; int x; int y; { static char gbuf[32]; sprintf(gbuf,str,y+1,x+1); return gbuf; } #endif trn-4.0-test77/term.h0000644000000000000000000002216211437640112013133 0ustar rootroot/* term.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char ERASECH; /* rubout character */ EXT char KILLCH; /* line delete character */ EXT char circlebuf[PUSHSIZE]; EXT int nextin INIT(0); EXT int nextout INIT(0); EXT unsigned char lastchar; /* stuff wanted by terminal mode diddling routines */ #ifdef I_TERMIO EXT struct termio _tty, _oldtty; #else # ifdef I_TERMIOS EXT struct termios _tty, _oldtty; # else # ifdef I_SGTTY EXT struct sgttyb _tty; EXT int _res_flg INIT(0); # endif # endif #endif EXT int _tty_ch INIT(2); EXT bool bizarre INIT(FALSE); /* do we need to restore terminal? */ /* terminal mode diddling routines */ #ifdef I_TERMIO #define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty)) #define resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty)) #define unflush_output() #else /* !I_TERMIO */ # ifdef I_TERMIOS #define crmode() ((bizarre=1), _tty.c_lflag &= ~ICANON,_tty.c_cc[VMIN]=1,tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,tcsetattr(_tty_ch, TCSAFLUSH,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define savetty() (tcgetattr(_tty_ch, &_oldtty),tcgetattr(_tty_ch, &_tty)) #define resetty() ((bizarre=0),tcsetattr(_tty_ch, TCSAFLUSH, &_oldtty)) #define unflush_output() # else /* !I_TERMIOS */ # ifdef I_SGTTY #define raw() ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty)) #define noraw() ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty)) #define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty)) #define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty)) #define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty)) #define nl() ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty)) #define nonl() ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty)) #define savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags) #define resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty)) # ifdef LFLUSHO EXT int lflusho INIT(LFLUSHO); #define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho)) # else /*! LFLUSHO */ #define unflush_output() # endif # else # ifdef MSDOS #define crmode() (bizarre=1) #define nocrmode() (bizarre=1) #define echo() (bizarre=1) #define noecho() (bizarre=1) #define nl() (bizarre=1) #define nonl() (bizarre=1) #define savetty() #define resetty() (bizarre=0) #define unflush_output() # else /* !MSDOS */ ..."Don't know how to define the term macros!" # endif /* !MSDOS */ # endif /* !I_SGTTY */ # endif /* !I_TERMIOS */ #endif /* !I_TERMIO */ #ifdef TIOCSTI #define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */ #else #define forceme(c) #endif /* termcap stuff */ /* * NOTE: if you don't have termlib you'll either have to define these strings * and the tputs routine, or you'll have to redefine the macros below */ #ifdef HAS_TERMLIB EXT int tc_GT; /* hardware tabs */ EXT char* tc_BC INIT(NULL); /* backspace character */ EXT char* tc_UP INIT(NULL); /* move cursor up one line */ EXT char* tc_CR INIT(NULL); /* get to left margin, somehow */ EXT char* tc_VB INIT(NULL); /* visible bell */ EXT char* tc_CL INIT(NULL); /* home and clear screen */ EXT char* tc_CE INIT(NULL); /* clear to end of line */ EXT char* tc_TI INIT(NULL); /* initialize terminal */ EXT char* tc_TE INIT(NULL); /* reset terminal */ EXT char* tc_KS INIT(NULL); /* enter `keypad transmit' mode */ EXT char* tc_KE INIT(NULL); /* exit `keypad transmit' mode */ EXT char* tc_CM INIT(NULL); /* cursor motion */ EXT char* tc_HO INIT(NULL); /* home cursor */ EXT char* tc_IL INIT(NULL); /* insert line */ EXT char* tc_CD INIT(NULL); /* clear to end of display */ EXT char* tc_SO INIT(NULL); /* begin standout mode */ EXT char* tc_SE INIT(NULL); /* end standout mode */ EXT int tc_SG INIT(0); /* blanks left by SO and SE */ EXT char* tc_US INIT(NULL); /* start underline mode */ EXT char* tc_UE INIT(NULL); /* end underline mode */ EXT char* tc_UC INIT(NULL); /* underline a character, if that's how it's done */ EXT int tc_UG INIT(0); /* blanks left by US and UE */ EXT bool tc_AM INIT(FALSE); /* does terminal have automatic margins? */ EXT bool tc_XN INIT(FALSE); /* does it eat 1st newline after automatic wrap? */ EXT char tc_PC INIT(0); /* pad character for use by tputs() */ #ifdef _POSIX_SOURCE EXT speed_t outspeed INIT(0); /* terminal output speed, */ #else EXT long outspeed INIT(0); /* for use by tputs() */ #endif EXT int fire_is_out INIT(1); EXT int tc_LINES INIT(0), tc_COLS INIT(0);/* size of screen */ EXT int term_line, term_col; /* position of cursor */ EXT int term_scrolled; /* how many lines scrolled away */ EXT int just_a_sec INIT(960); /* 1 sec at current baud rate */ /* (number of nulls) */ /* define a few handy macros */ #define termdown(x) term_line+=(x), term_col=0 #define newline() term_line++, term_col=0, putchar('\n') FLUSH #define backspace() tputs(tc_BC,0,putchr) FLUSH #define erase_eol() tputs(tc_CE,1,putchr) FLUSH #define clear_rest() tputs(tc_CD,tc_LINES,putchr) FLUSH #define maybe_eol() if(erase_screen&&erase_each_line)tputs(tc_CE,1,putchr) FLUSH #define underline() tputs(tc_US,1,putchr) FLUSH #define un_underline() fire_is_out|=UNDERLINE, tputs(tc_UE,1,putchr) FLUSH #define underchar() tputs(tc_UC,0,putchr) FLUSH #define standout() tputs(tc_SO,1,putchr) FLUSH #define un_standout() fire_is_out|=STANDOUT, tputs(tc_SE,1,putchr) FLUSH #define up_line() term_line--, tputs(tc_UP,1,putchr) FLUSH #define insert_line() tputs(tc_IL,1,putchr) FLUSH #define carriage_return() term_col=0, tputs(tc_CR,1,putchr) FLUSH #define dingaling() tputs(tc_VB,1,putchr) FLUSH #else /* !HAS_TERMLIB */ ..."Don't know how to define the term macros!" #endif /* !HAS_TERMLIB */ #define input_pending() finput_pending(TRUE) #define macro_pending() finput_pending(FALSE) EXT int page_line INIT(1); /* line number for paging in print_line (origin 1) */ EXT bool error_occurred INIT(FALSE); EXT char* mousebar_btns; EXT int mousebar_cnt INIT(0); EXT int mousebar_start INIT(0); EXT int mousebar_width INIT(0); EXT bool xmouse_is_on INIT(FALSE); EXT bool mouse_is_down INIT(FALSE); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void term_init _((void)); void term_set _((char*)); void set_macro _((char*,char*)); void arrow_macros _((char*)); void mac_line _((char*,char*,int)); void show_macros _((void)); void set_mode _((char_int,char_int)); int putchr _((char_int)); void hide_pending _((void)); bool finput_pending _((bool_int)); bool finish_command _((int)); char* edit_buf _((char*,char*)); bool finish_dblchar _((void)); void eat_typeahead _((void)); void save_typeahead _((char*,int)); void settle_down _((void)); Signal_t alarm_catcher _((int)); int read_tty _((char*,int)); #if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS) int circfill _((void)); #endif void pushchar _((char_int)); void underprint _((char*)); #ifdef NOFIREWORKS void no_sofire _((void)); void no_ulfire _((void)); #endif void getcmd _((char*)); void pushstring _((char*,char_int)); int get_anything _((void)); int pause_getcmd _((void)); void in_char _((char*,char_int,char*)); void in_answer _((char*,char_int)); bool in_choice _((char*,char*,char*,char_int)); int print_lines _((char*,int)); int check_page_line _((void)); void page_start _((void)); void errormsg _((char*)); void warnmsg _((char*)); void pad _((int)); #ifdef VERIFY void printcmd _((void)); #endif void rubout _((void)); void reprint _((void)); void erase_line _((bool_int)); void clear _((void)); void home_cursor _((void)); void goto_xy _((int,int)); #ifdef SIGWINCH Signal_t winch_catcher _((int)); #endif void termlib_init _((void)); void termlib_reset _((void)); #ifdef NBG_SIGIO Signal_t waitkey_sig_handler _((int)); #endif bool wait_key_pause _((int)); void xmouse_init _((char*)); void xmouse_check _((void)); void xmouse_on _((void)); void xmouse_off _((void)); void draw_mousebar _((int,bool_int)); bool check_mousebar _((int,int,int,int,int,int)); void add_tc_string _((char*,char*)); char* tc_color_capability _((char*)); #ifdef MSDOS int tputs _((char*,int,int(*) _((char_int)))); char* tgoto _((char*,int,int)); #endif trn-4.0-test77/term.ih0000644000000000000000000000136107113133016013276 0ustar rootroot/* term.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ struct keymap { char km_type[128]; union km_union { KEYMAP* km_km; char* km_str; } km_ptr[128]; }; #define KM_NOTHIN 0 #define KM_STRING 1 #define KM_KEYMAP 2 #define KM_BOGUS 3 #define KM_TMASK 3 #define KM_GSHIFT 4 #define KM_GMASK 7 #define TC_STRINGS 48 /* number of colors we can keep track of */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef MSDOS static void set_lines_and_cols _((void)); #endif static void mac_init _((char*)); static KEYMAP* newkeymap _((void)); static void show_keymap _((KEYMAP*,char*)); static int echo_char _((char_int)); static void line_col_calcs _((void)); static void mouse_input _((char*)); trn-4.0-test77/tkstuff.c0000644000000000000000000001276307113133016013647 0ustar rootroot/* tkstuff.c */ #include "EXTERN.h" #include "common.h" #ifdef USE_TCL #include "term.h" #include "util.h" #include "util2.h" #include #ifdef USE_TK #include #include "tktree.h" #endif #include "INTERN.h" #include "tkstuff.h" #include "tkstuff.ih" Tcl_Interp* ttcl_interp; /* Interpreter for this application. */ #ifdef USE_TK Tk_Window mainWindow; /* The main window for the application. */ static char* ttk_idle_pending_buf; #endif /* all these separate ifdef blocks are to keep mkpro happy */ /* XXX clean up the waiting/get-input interaction... */ /* This needs to be made *fast* when nothing is waiting... */ #ifdef USE_TK static void ttk_waitidle_pending() { if (ttk_idle_flag) { (void)Tcl_Eval(ttcl_interp,ttk_idle_pending_buf); } } #endif #ifdef USE_TK void ttk_do_waiting_events() { if (ttk_running) { ttk_waitidle_pending(); if (ttk_do_waiting_flag) { while (Tk_DoOneEvent(TK_DONT_WAIT)) ; /* EMPTY */ } else { /* always at least do file events */ while (Tk_DoOneEvent(TK_FILE_EVENTS|TK_DONT_WAIT)) ; /* EMPTY */ } } } #endif /* later maybe have options to wait a number of milliseconds, then * start some background task... */ #ifdef USE_TK void ttk_wait_for_input() { if (ttk_running) { while (!finput_pending(1)) { ttk_waitidle_pending(); Tk_DoOneEvent(0); } } } #endif /* USE_TK */ void ttcl_init() { char* name; char* class; static char pending_buf[64]; /* we are *not* currently operating */ #ifdef USE_TK ttk_running = 0; #endif ttcl_running = 0; #ifdef USE_TK if (UseTk) UseTcl = 1; /* TCL is required for TK */ #endif /* do we really want to run TCL? */ if (!UseTcl) { return; } #ifdef USE_TK if (UseTk) { /* set up the idle buffer */ strcpy(pending_buf,"ttk_idlepending"); ttk_idle_pending_buf = pending_buf; /* plain NULL pointer does not work (it is "NULL" in TCL) */ ttk_keys = (char*)malloc(sizeof(char)); if (!ttk_keys) /* sheer paranoia */ return; *ttk_keys = '\0'; } #endif if (TCL_MAJOR_VERSION<7) { UseTcl = UseTk = 0; /* TCL is too old */ return; } #ifdef USE_TK if (TK_MAJOR_VERSION<4) { UseTk = 0; /* TK is too old. (We probably won't compile.) */ } #endif ttcl_interp = Tcl_CreateInterp(); #ifdef TCL_MEM_DEBUG Tcl_InitMemory(ttcl_interp); #endif #ifdef USE_TK ttk_running = 1; /* be optimistic */ #if TK_MINOR_VERSION == 0 if (UseTk && (TK_MINOR_VERSION==0)) { /* TK 4.0 initialization */ name = "tktrn"; class = (char*)ckalloc((unsigned) (strlen(name) + 1)); strcpy(class, name); class[0] = toupper((unsigned char) class[0]); mainWindow = Tk_CreateMainWindow(ttcl_interp, 0, name, class); ckfree(class); if (mainWindow == NULL) { #if 0 /* XXX handle error better */ fprintf(stderr, "%s\n", ttcl_interp->result); #endif ttk_running = 0; } } #endif #endif if (Tcl_Init(ttcl_interp) == TCL_ERROR) { #ifdef USE_TK ttk_running = 0; #endif return; } ttcl_running = 1; #ifdef USE_TK if (ttk_running) { if (Tk_Init(ttcl_interp) == TCL_ERROR) { ttk_running = 0; } #if TK_MINOR_VERSION > 0 /* TK 4.1 or higher initialization */ Tcl_StaticPackage(ttcl_interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL); #endif } #endif /* later: call initializations of extensions */ #ifdef USE_TK if (ttk_running) { /* misc. initialization done *before* the user tkinit file */ Tcl_LinkVar(ttcl_interp, "ttk_keys", (char*)&ttk_keys, TCL_LINK_STRING); ttk_idle_flag = 0; Tcl_LinkVar(ttcl_interp, "ttk_idle_flag", (char*)&ttk_idle_flag, TCL_LINK_INT); /* later make the article tree stuff optional, check value */ (void)ttk_tree_init(); } #endif /* Load the user TCL startup code */ if (Tcl_EvalFile(ttcl_interp, savestr(filexp("%+/tclinit"))) != TCL_OK) { /* XXX Later print some message about problem? */ /* This file is optional, so don't do anything */ ; } #ifdef USE_TK if (ttk_running) { if (Tcl_EvalFile(ttcl_interp, savestr(filexp("%+/tkinit"))) != TCL_OK) { /* XXX Later print some message about problem? */ ttk_running = 0; /* don't try to run */ } /* do simple initialization internally */ Tcl_Eval(ttcl_interp, "fileevent stdin readable { set ttk_keys \"$ttk_keys[read stdin 1]\" }"); ttk_do_waiting_events(); } #endif } /* might use status later */ void ttcl_finalize(status) int status; { #ifdef USE_TK if (ttk_running) (void)Tcl_Eval(ttcl_interp,"ttk_finalize"); #endif if (ttcl_running) (void)Tcl_Eval(ttcl_interp,"ttcl_finalize"); } void ttcl_set_int(varname,val) char* varname; int val; { static char buf[20]; sprintf(buf,"%d",val); /* check errors later? */ (void)Tcl_SetVar(ttcl_interp,varname,buf,0); } void ttcl_set_str(varname,val) char* varname; char* val; { (void)Tcl_SetVar(ttcl_interp,varname,val,0); } int ttcl_get_int(varname) char* varname; { char* s; int result = 0; /* check errors later? */ s = Tcl_GetVar(ttcl_interp,varname,0); if (!s) return 0; (void)Tcl_GetInt(ttcl_interp,s,&result); return result; } char* ttcl_get_str(varname) char* varname; { return Tcl_GetVar(ttcl_interp,varname,0); } void ttcl_eval(str) char* str; { static char buf[1024]; char* p; int len; if (ttcl_running) { if ((len = strlen(str)) > 1020) { p = savestr(str); } else { strcpy(buf,str); p = buf; } /* later do error checking */ (void)Tcl_Eval(ttcl_interp,p); if (len > 1020) { free(p); } } } #endif /* USE_TCL */ trn-4.0-test77/tkstuff.h0000644000000000000000000000126207113133016013644 0ustar rootroot/* tkstuff.h */ #ifdef USE_TK EXT char* ttk_keys; EXT int ttk_idle_flag; /* if true, we are really running Tk */ EXT int ttk_running INIT(0); /* if true, allow update via ttk_do_waiting_events() */ EXT int ttk_do_waiting_flag INIT(1); #endif /* if true, we are really running TCL */ EXT int ttcl_running INIT(0); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef USE_TK void ttk_do_waiting_events _((void)); void ttk_wait_for_input _((void)); #endif void ttcl_init _((void)); void ttcl_finalize _((int)); void ttcl_set_int _((char*,int)); void ttcl_set_str _((char*,char*)); int ttcl_get_int _((char*)); char* ttcl_get_str _((char*)); void ttcl_eval _((char*)); trn-4.0-test77/tkstuff.ih0000644000000000000000000000033007113133016014010 0ustar rootroot/* tkstuff.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifdef USE_TK static void ttk_waitidle_pending _((void)); #endif trn-4.0-test77/tktree.c0000644000000000000000000002631107113133016013451 0ustar rootroot/* tktree.c * * XXX tktree.c should become some other name, like tkmisc? * Was only article tree drawing. Now includes lots more. * Perhaps later allow much of it to be accessed from pure tcl. */ #include "EXTERN.h" #include "common.h" #ifdef USE_TK #include #include #include "hash.h" #include "cache.h" #include "rt-select.h" #include "rt-wumpus.h" #include "ng.h" #include "util.h" #include "tkstuff.h" #include "term.h" #ifdef SCORE #include "ngdata.h" /* absfirst */ #include "score.h" #endif #include "INTERN.h" #include "tktree.h" #include "tktree.ih" extern HASHTABLE* msgid_hash; extern Tcl_Interp* ttcl_interp; static int ttk_article_counter = 0; /* linked variable for passing a messageid */ static char* ttk_msgid = 0; /* variables for tree dimensions (x and y) */ static int ttk_tree_x = 0; static int ttk_tree_y = 0; typedef struct { ARTICLE* ap; } TTK_ART; static void ttk_article_delete(cd) ClientData cd; { safefree(cd); } /* this one is named "ttk__art0" */ static TTK_ART* ttk_fastart; static char ttk_letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+"; static void ttk_treeprep_helper(ap) ARTICLE* ap; { for (;;) { ap->flags2 &= ~(AF2_WASUNREAD|AF2_CHANGED|AF2_NODEDRAWN); if (ap->flags & AF_UNREAD) ap->flags2 |= AF2_WASUNREAD; if (ap->child1) ttk_treeprep_helper(ap->child1); if (!(ap = ap->sibling)) break; } } static void ttk_treechange_helper(ap) ARTICLE* ap; { int changed; /* later indicate num of changes? */ for (;;) { changed=0; if (ap->flags & AF_UNREAD) { if (!(ap->flags2 & AF2_WASUNREAD)) changed++; } else { if (ap->flags2 & AF2_WASUNREAD) changed++; } if (ap->flags2 & AF2_CHANGED) changed++; if (changed > 0) { ttk_fastart->ap = ap; ttcl_eval("eval $tree_nodechanged ttk__art0"); /* consider doing something with result */ } if (ap->flags & AF_UNREAD) ap->flags2 |= AF2_WASUNREAD; else ap->flags2 &= ~AF2_WASUNREAD; ap->flags2 &= ~AF2_CHANGED; if (ap->child1) ttk_treechange_helper(ap->child1); if (!(ap = ap->sibling)) break; } } static int ttk_article_objectcmd(cd, interp, argc, argv) ClientData cd; Tcl_Interp* interp; int argc; char* argv[]; { TTK_ART* p = (TTK_ART*)cd; ARTICLE* ap; char* cmd; char* s; HASHDATUM data; if (argc < 2) { interp->result = "not enough args"; return TCL_ERROR; } ap = p->ap; cmd = argv[1]; if (strEQ(cmd,"setid")) { if (argc != 3) { interp->result = "setid: needs id argument"; return TCL_ERROR; } data = hashfetch(msgid_hash, argv[2], strlen(argv[2])); if (!(ap = (ARTICLE*)data.dat_ptr) || data.dat_len) { interp->result = "0"; p->ap = 0; } else { interp->result = "1"; p->ap = ap; } return TCL_OK; } if (strEQ(cmd,"setcurrent")) { if (argc != 2) { interp->result = "setcurrent: too many arguments"; return TCL_ERROR; } ap = curr_artp; if (!ap) { interp->result = "0"; p->ap = 0; } else { interp->result = "1"; p->ap = ap; } return TCL_OK; } if (strEQ(cmd,"header")) { char* which; if (argc != 3) { interp->result = "header: needs header name argument"; return TCL_ERROR; } which = argv[2]; if (!ap) { s = "NO ARTICLE"; /* return error? */ } else if (strEQ(which,"from")) { s = "NO AUTHOR"; if (ap->from) { s = ap->from; } Tcl_AppendResult(interp, s, 0); return TCL_OK; } else if (strEQ(which,"subject")) { s = "NO SUBJECT"; if (ap->subj) { s = ap->subj->str +((ap->flags & AF_HAS_RE) ? 0 : 4); } } else if (strEQ(which,"msgid")) { s = "NO ID (***ERROR***)"; if (ap->msgid) { s = ap->msgid; } } else { /* not a known header */ Tcl_AppendResult(interp, "unknown header", 0); return TCL_ERROR; } Tcl_AppendResult(interp, s, 0); return TCL_OK; } if (strEQ(cmd,"move")) { char* dir; if (argc != 3) { interp->result = "move: needs direction"; return TCL_ERROR; } dir = argv[2]; if (!ap) { /* error later? */ interp->result = "0"; return TCL_OK; } /* later add threadtop and subject directions */ if (strEQ(dir,"parent")) { ap = ap->parent; } else if (strEQ(dir,"sibling")) { ap = ap->sibling; } else if (strEQ(dir,"child")) { ap = ap->child1; } else { interp->result = "move: invalid direction"; return TCL_ERROR; } p->ap = ap; if (ap) { interp->result = "1"; } else { interp->result = "0"; } return TCL_OK; } if (strEQ(cmd,"flag")) { char* flag; int status = 0; if (argc != 3) { interp->result = "move: needs direction"; return TCL_ERROR; } flag = argv[2]; if (!ap) { /* error later? (not error if checking existence?) */ interp->result = "0"; return TCL_OK; } if (strEQ(flag,"current")) { if (ap == curr_artp) status = 1; } else if (strEQ(flag,"exists")) { if (ap->flags & AF_EXISTS) status = 1; } else if (strEQ(flag,"unread")) { if (ap->flags & AF_UNREAD) status = 1; } else if (strEQ(flag,"selected")) { if (ap->flags & AF_SEL) status = 1; } else if (strEQ(flag,"wasunread")) { if (ap->flags2 & AF2_WASUNREAD) status = 1; } else if (strEQ(flag,"changed")) { if (ap->flags2 & AF2_CHANGED) status = 1; } else if (strEQ(flag,"parent")) { if (ap->parent) status = 1; } else if (strEQ(flag,"child")) { if (ap->child1) status = 1; } else if (strEQ(flag,"nextsibling")) { if (ap->sibling) status = 1; } else if (strEQ(flag,"priorsibling")) { if ((ap->parent) && (ap->parent->child1) && (ap != ap->parent->child1)) status = 1; } else if (strEQ(flag,"parentsubject")) { if ((ap->subj) && (ap->parent) && (ap->parent->subj) && (ap->subj == ap->parent->subj)) status = 1; } else if (strEQ(flag,"selectedonly")) { /* XXX Move this out to a trn-globals section */ if (selected_only) status = 1; } else { interp->result = "unknown flag"; return TCL_ERROR; } if (status) { interp->result = "1"; } else { interp->result = "0"; } return TCL_OK; } if (strEQ(cmd,"copy")) { TTK_ART* p2; if (argc != 2) { interp->result = "too many arguments"; return TCL_ERROR; } p2 = (TTK_ART*)safemalloc(sizeof(TTK_ART)); p2->ap = p->ap; sprintf(interp->result, "trn_article%d",ttk_article_counter++); Tcl_CreateCommand(interp,interp->result, ttk_article_objectcmd, p2,ttk_article_delete); return TCL_OK; } if (strEQ(cmd,"subjectletter")) { int subj = 0; ARTICLE* p; SUBJECT* sp; if ((!ap) || !(ap->flags & AF_EXISTS) || !(ap->subj)) { interp->result[0] = ' '; interp->result[1] = '\0'; return TCL_OK; } p = ap; while (p->parent) { p = p->parent; } sp = p->subj; while (sp != ap->subj) { subj++; sp = sp->thread_link; } interp->result[0] = ttk_letters[subj>9+26+26? 9+26+26:subj]; interp->result[1] = '\0'; return TCL_OK; } /* prepare article and all children for other tree usage */ if (strEQ(cmd,"treeprepare")) { ttk_tree_x=0; ttk_tree_y=0; if (ap) { ttk_treeprep_helper(ap); interp->result = "1"; } else { interp->result = "0"; } return TCL_OK; } /* call a function for all changed articles */ if (strEQ(cmd,"treechange")) { if (ap) { ttk_treechange_helper(ap); interp->result = "1"; } else { interp->result = "0"; } return TCL_OK; } /* get the article number for this article, or 0 if non-existant */ if (strEQ(cmd,"artnum")) { ART_NUM num; if (ap) { if (ap->flags & AF_EXISTS) { num = article_num(ap); if (num>0) { sprintf(interp->result,"%d",(int)num); return TCL_OK; } } } interp->result = "0"; return TCL_OK; } if (strEQ(cmd,"score")) { #ifdef SCORE /* Consider a quick query to see if the article is scored... */ ART_NUM num; int artscore; if (ap) { if (ap->flags & AF_EXISTS) { num = article_num(ap); if (num>0) { artscore = sc_score_art(num,TRUE); sprintf(interp->result,"%d",artscore); return TCL_OK; } } } interp->result = "0"; return TCL_OK; #else interp->result = "article scoring not compiled in this trn executable"; return TCL_ERROR; #endif } if (strEQ(cmd,"scorequick")) { #ifdef SCORE ART_NUM num; int artscore; if (ap) { if (ap->flags & AF_EXISTS) { num = article_num(ap); if (num>0) { if (SCORED(num)) { artscore = sc_score_art(num,TRUE); } else { artscore = 0; } sprintf(interp->result,"%d",artscore); return TCL_OK; } } } interp->result = "0"; return TCL_OK; #else interp->result = "article scoring not compiled in this trn executable"; return TCL_ERROR; #endif } interp->result = "unknown article command"; return TCL_ERROR; } /* XXX Cleanup the tree drawing, replace "wipetree" with special procedure */ /* Called from trn to draw an article tree. */ void ttk_draw_tree(ap,x,y) ARTICLE* ap; int x,y; /* starting X and Y positions */ { static char lbuf[100]; if (!ttk_running) return; /* no wasted time */ if (!(ap) || (!ap->subj) || (!ap->subj->thread) || (!ap->subj->thread->msgid)) { ttcl_eval("wipetree"); return; } ttk_msgid = ap->subj->thread->msgid; strcpy(lbuf,"trn_draw_article_tree 0 0"); Tcl_Eval(ttcl_interp,lbuf); } static int ttk_article(cd, interp, argc, argv) ClientData cd; Tcl_Interp* interp; int argc; char* argv[]; { TTK_ART* p; /* error checking later */ if (argc != 1) { interp->result = "wrong # args"; return TCL_ERROR; } p = (TTK_ART*)safemalloc(sizeof(TTK_ART)); p->ap = 0; sprintf(interp->result, "trn_article%d",ttk_article_counter++); Tcl_CreateCommand(interp,interp->result, ttk_article_objectcmd, p,ttk_article_delete); return TCL_OK; } /* XXX XXX Move this elsewhere. (general TRN stuff, not just article trees) */ static int ttk_trn(cd, interp, argc, argv) ClientData cd; Tcl_Interp* interp; int argc; char* argv[]; { char* cmd; static char lbuf[32]; /* long enough for integers... */ cmd = argv[1]; if (strEQ(cmd,"mode")) { lbuf[0] = mode; lbuf[1] = '\0'; Tcl_AppendResult(interp, lbuf, 0); return TCL_OK; } if (strEQ(cmd,"pending")) { ttk_do_waiting_flag = 0; /* do not update TK */ if (finput_pending(1)) { lbuf[0] = '1'; } else { lbuf[0] = '0'; } ttk_do_waiting_flag = 1; /* resume updating TK */ lbuf[1] = '\0'; Tcl_ResetResult(interp); Tcl_AppendResult(interp, lbuf, 0); return TCL_OK; } interp->result = "unknown trn_misc command"; return TCL_ERROR; } int ttk_tree_init() { char lbuf[20]; /* create the fast shared ArticlePointer (ap) ttk__art0 */ ttk_fastart = (TTK_ART*)safemalloc(sizeof(TTK_ART)); ttk_fastart->ap = 0; strcpy(lbuf,"ttk__art0"); Tcl_CreateCommand(ttcl_interp,lbuf,ttk_article_objectcmd, ttk_fastart,ttk_article_delete); Tcl_LinkVar(ttcl_interp, "ttk_msgid", (char*)&ttk_msgid, TCL_LINK_STRING); Tcl_LinkVar(ttcl_interp, "ttk_tree_x", (char*)&ttk_tree_x, TCL_LINK_INT); Tcl_LinkVar(ttcl_interp, "ttk_tree_y", (char*)&ttk_tree_y, TCL_LINK_INT); Tcl_CreateCommand(ttcl_interp, "trn_article", ttk_article, 0, 0); Tcl_CreateCommand(ttcl_interp, "trn_misc", ttk_trn, 0, 0); return TCL_OK; } #endif /* USE_TK */ trn-4.0-test77/tktree.h0000644000000000000000000000053507113133016013456 0ustar rootroot/* tktree.h */ /* * The authors 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. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void ttk_draw_tree _((ARTICLE*,int,int)); int ttk_tree_init _((void)); trn-4.0-test77/tktree.ih0000644000000000000000000000075107113133016013627 0ustar rootroot/* tktree.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void ttk_article_delete _((ClientData)); static void ttk_treeprep_helper _((ARTICLE*)); static void ttk_treechange_helper _((ARTICLE*)); static int ttk_article_objectcmd _((ClientData,Tcl_Interp*,int,char**)); static int ttk_article _((ClientData,Tcl_Interp*,int,char**)); static int ttk_trn _((ClientData,Tcl_Interp*,int,char**)); trn-4.0-test77/trn-artchk.c0000644000000000000000000001735307113133016014236 0ustar rootroot/* trn-artchk.c */ /* This software is copyrighted as detailed in the LICENSE file. */ /* A program to check an article's validity and print warnings if problems ** are found. ** ** Usage: trn-artchk
*/ #include "EXTERN.h" #include "common.h" #include "util2.h" #include "util3.h" #ifdef SUPPORT_NNTP #include "nntpclient.h" #include "nntpinit.h" #endif #define MAXNGS 100 #ifdef SUPPORT_NNTP int server_connection _((void)); int nntp_handle_timeout _((void)); char* server_name; char* nntp_auth_file; #endif /* SUPPORT_NNTP */ int debug = 0; char* homedir; char* dotdir; char nullstr[1] = ""; char ess[2] = "s"; int main(argc, argv) int argc; char* argv[]; { FILE* fp; FILE* fp_active = NULL; FILE* fp_ng = NULL; bool check_active = FALSE; bool check_ng = FALSE; char buff[LBUFLEN]; char* cp; char* cp2; char* ngptrs[MAXNGS]; int nglens[MAXNGS]; int foundactive[MAXNGS]; int i, col, max_col_len, line_num = 0, ngcnt = 0, ngleft; int found_newsgroups = 0; homedir = getenv("HOME"); if (homedir == NULL) homedir = getenv("LOGDIR"); dotdir = getenv("DOTDIR"); if (!dotdir) dotdir = homedir; if (argc != 5 || !(max_col_len = atoi(argv[2]))) { fprintf(stderr, "\ Usage: trn-artchk
\n"); exit(1); } if ((fp = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "trn-artchk: 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] != ' ' && cp[1] != '\0') { 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); foundactive[ngcnt] = 0; ngptrs[ngcnt] = safemalloc(nglens[ngcnt]+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++; col = strlen(buff)-1; if (buff[col] != '\n') printf("\n\ Warning: line %d has no trailing newline character and may get lost.\n", line_num); else buff[col] = '\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; } } #ifdef SUPPORT_NNTP cp = getenv("NNTPSERVER"); if (!cp) { cp = filexp(SERVER_NAME); if (FILE_REF(cp)) cp = nntp_servername(cp); } if (strNE(cp,"local")) { server_name = savestr(cp); cp = index(server_name, ';'); #ifndef DECNET if (!cp) cp = index(server_name, ':'); #endif if (cp) { *cp = '\0'; nntplink.port_number = atoi(cp+1); } nntp_auth_file = filexp(NNTP_AUTH_FILE); if ((cp = getenv("NNTP_FORCE_AUTH")) != NULL && (*cp == 'y' || *cp == 'Y')) nntplink.flags |= NNTP_FORCE_AUTH_NEEDED; if (init_nntp() < 0) server_name = NULL; } #endif if (ngcnt) { struct stat st; if (stat(argv[3], &st) != -1) check_ng = st.st_size > 0 && (fp_ng = fopen(argv[3], "r")) != NULL; #ifdef SUPPORT_NNTP else if (server_name && server_connection()) check_ng = TRUE; #endif if (stat(argv[4], &st) != -1) check_active = st.st_size > 0 && (fp_active = fopen(argv[4], "r")) != NULL; #ifdef SUPPORT_NNTP else if (server_name && server_connection()) check_active = TRUE; #endif } if (ngcnt && (check_ng || check_active)) { /* Print a note about each newsgroup */ printf("\nYour article's newsgroup%s:\n", PLURAL(ngcnt)); if (!check_active) { for (i = 0; i < ngcnt; i++) { foundactive[i] = 1; } } else if (fp_active != NULL) { ngleft = ngcnt; while (fgets(buff, LBUFLEN, fp_active)) { if (!ngleft) break; for (i = 0; i < ngcnt; i++) { if (!foundactive[i]) { if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ') && strnEQ(ngptrs[i], buff, nglens[i])) { foundactive[i] = 1; ngleft--; } } } } fclose(fp_active); } #ifdef SUPPORT_NNTP else if (server_name) { int listactive_works = 1; for (i = 0; i < ngcnt; i++) { if (listactive_works) { sprintf(ser_line, "list active %s", ngptrs[i]); if (nntp_command(ser_line) <= 0) break; if (nntp_check() > 0) { while (nntp_gets(ser_line, sizeof ser_line) >= 0) { if (nntp_at_list_end(ser_line)) break; foundactive[i] = 1; } } else if (*ser_line == NNTP_CLASS_FATAL) { listactive_works = FALSE; i--; } } else { sprintf(ser_line, "GROUP %s", ngptrs[i]); if (nntp_command(ser_line) <= 0) break; if (nntp_check() > 0) foundactive[i] = 1; } } } if (check_ng && fp_ng == NULL) { fp_ng = fopen(argv[3], "w+"); unlink(argv[3]); if (fp_ng != NULL) { for (i = 0; i < ngcnt; i++) { /* issue a description list command */ sprintf(ser_line, "XGTITLE %s", ngptrs[i]); if (nntp_command(ser_line) <= 0) break; /*$$ use list newsgroups if this fails...? */ if (nntp_check() > 0) { /* write results to fp_ng */ while (nntp_gets(ser_line, sizeof ser_line) >= 0) { if (nntp_at_list_end(ser_line)) break; fprintf(fp_ng, "%s\n", ser_line); } } } fseek(fp_ng, 0L, 0); } } #endif if (fp_ng != NULL) { ngleft = ngcnt; while (fgets(buff, LBUFLEN, fp_ng)) { if (!ngleft) break; for (i = 0; i < ngcnt; i++) { if (foundactive[i] && ngptrs[i]) { if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ') && strnEQ(ngptrs[i], buff, nglens[i])) { cp = &buff[nglens[i]]; *cp++ = '\0'; while (*cp == ' ' || *cp == '\t') cp++; if (cp[0] == '?' && cp[1] == '?') cp = "[no description available]\n"; printf("%-23s %s", buff, cp); free(ngptrs[i]); ngptrs[i] = 0; ngleft--; } } } } fclose(fp_ng); } for (i = 0; i < ngcnt; i++) { if (!foundactive[i]) { printf("%-23s ** invalid news group -- check spelling **\n", ngptrs[i]); free(ngptrs[i]); } else if (ngptrs[i]) { printf("%-23s [no description available]\n", ngptrs[i]); free(ngptrs[i]); } } } #ifdef SUPPORT_NNTP nntp_close(TRUE); if (server_name) cleanup_nntp(); #endif return 0; } #ifdef SUPPORT_NNTP int server_connection() { static int server_stat = 0; if (!server_stat) { if (nntp_connect(server_name,0) > 0) server_stat = 1; else server_stat = -1; } return server_stat == 1; } #endif #ifdef SUPPORT_NNTP int nntp_handle_timeout() { fputs("\n503 Server timed out.\n",stderr); return -2; } #endif trn-4.0-test77/trn.10000644000000000000000000030774311437640112012713 0ustar rootroot.\" trn.1 .\" .\" This software is copyrighted as detailed in the LICENSE file. .\" .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .de Ip .br .ie \\n.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .ds qq "" .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH TRN 1 LOCAL .UC 6 .SH NAME trn - threaded read news program .SH SYNOPSIS .B trn [options] [newsgroups] .SH DESCRIPTION .I Trn is a threaded version of .I rn, which is a replacement for the readnews(1) program. Being \*(L"threaded\*(R" means that the articles are interconnected in reply order. Each discussion thread is a tree of articles where all the reply (child) articles branch off from their respective originating (parent) articles. A representation of this tree (or a portion of it) is displayed in the article header as you are reading news. This gives you a better feel for how all the articles are related, and even lets you see at a glance when an article has replies \*(-- a good thing to check before posting. In addition, .I trn has a thread selector that allows you to quickly browse through a list of subjects and choose the ones you find interesting. This thread selector sorts articles according to various criteria and can be switched into various display modes that allows you to pick all the subjects separately (threads can have multiple subjects) or even pick individual articles. Any items you don't select can be saved for reading later or marked as read with a single keystroke. .PP If you are already familiar with .I trn you may just want to read the .IR "WHAT'S NEW" section. People upgrading from .I rn will probably want to pay attention to the sections on .IR "The Selector" , .IR "The Tree Display" , and the aforementioned .IR "WHAT'S NEW" . If you're impatient, just dive in and get started. All the regular commands will be familiar to an .I rn or .I trn user, and the on-line help will give you a quick run-down of what commands are available (just type \*(L'h\*(R' from any prompt). I'd also suggest using the command: .Sp trn \-x \-X .Sp to make sure some of the best features are turned on. .Sh "Starting Trn" If no newsgroups are specified, all the newsgroups which have unread news will be presented to the user in the order in which they occur in the .I .newsrc file. At the prompt for each group you can choose to read it, skip it, move it, etc. If a list of newsgroups is provided on the command line, .I trn will start up in \*(L"add\*(R" mode, using the list as a set of patterns to add new newsgroups and restrict which newsgroups are displayed (see also the discussion of the \*(L'a\*(R' command on the newsgroup-selection level). .PP .I Trn operates on four levels: the newsgroup-selection level, the thread selector, the article-reading level, and the paging level. Each level has its own set of commands, and its own help menu. At the paging level (the bottom level) .I trn behaves much like the .IR more (1) program. At the article-reading level articles are presented to you in the order of their replies, with the subjects being ordered by the date of the oldest unread article (though there are commands for changing the default display order). In the thread selector you are presented with the subjects and (usually) authors associated with each discussion thread, and given a chance to choose which ones you wish to read now, save for later, or manipulate in some way. At the newsgroup-selection level (the top level), you may specify which newsgroup you want next, or read them in the default order, which is the order that the newsgroups occur in your .I .newsrc file. (You will therefore want to rearrange your .I .newsrc file to put the most interesting newsgroups first. This can be done with the \*(L'm\*(R' command on the Newsgroup Selection level. WARNING: invoking readnews/vnews (the old user interface) in any way (including as a news checker in your login sequence!) will cause your .I .newsrc to be disarranged again.) .PP On any level, at ANY prompt, help is available by typing an \*(L'h\*(R'. This gives you a summary of available commands and what they do. Remember this command, you'll need it. .PP Typing space to any question means to do the normal thing. You will know what that is because every prompt has a list of several plausible commands enclosed in square brackets. The first command in the list is the one which will be done if you type a space. (All input is done in cbreak mode, so carriage returns should not be typed to terminate anything except certain multi-character commands. Those commands will be obvious in the discussion below because they take an argument.) .PP Upon startup, .I trn will do several things: .Ip 1. 4 It will look for your .I .newsrc file, which is your list of subscribed-to newsgroups. If .I trn doesn't find a .IR .newsrc , it will create one. If it does find one, it will back it up under the name \*(L".\|oldnewsrc\*(R". .Ip 2. 4 It will input your .I .newsrc file, listing out the first several newsgroups with unread news. .Ip 3. 4 It will perform certain consistency checks on your .IR .newsrc . If your .I .newsrc is out of date in any of several ways, .I trn will warn you and patch it up for you, but you may have to wait a little longer for it to start up. .Ip 4. 4 .I Trn will next check to see if any new newsgroups have been created, and give you the opportunity to add them to your .IR .newsrc . .Ip 5. 4 .I Trn goes into the top prompt level \*(-- the newsgroup-selection level. .Sh "Newsgroup Selection Level" In this section the words \*(L"next\*(R" and \*(L"previous\*(R" refer to the ordering of the newsgroups in your .I .newsrc file. On the newsgroup-selection level, the prompt looks like this: .Sp ====== 17 unread articles in talk.blurfl \*(-- read now? [ynq] .Sp unless the group is set for unthreaded reading, in which case the first six characters are \*(L"******\*(R". The following commands may be given at this level: .Ip \+ 8 Enter this newsgroup through the selector. .Ip y 8 Begin reading this newsgroup now. .Ip SP 8 Enter the newsgroup by executing the default command listed in []'s. .Ip .command 8 Do this newsgroup now, but execute .I command before displaying anything. The command will be interpreted as if typed on the article selection level. .Ip = 8 Start this newsgroup, but list subjects before displaying articles. .Ip U 8 Enter this newsgroup through the unkill-articles prompt. .Ip t 8 Toggle the newsgroup between threaded and unthreaded reading. The default is threaded, and the current setting is stored in your .newsrc. .Ip n 8 Go to the next newsgroup with unread news. .Ip N 8 Go to the next newsgroup. .Ip p 8 Go to the previous newsgroup with unread news. If there is none, stay at the current newsgroup. .Ip P 8 Go to the previous newsgroup. .Ip \- 8 Go to the previously displayed newsgroup (regardless of whether it is before or after the current one in the list). .Ip 1 8 Go to the first newsgroup. .Ip ^ 8 Go to the first newsgroup with unread news. .Ip $ 8 Go to the end of the newsgroups list. .Ip "g newsgroup" 8 Go to .IR newsgroup , which can be the group's name or a zero-relative number of the groups in your .newsrc (see the \*(L'L\*(R' command to list your .newsrc). If it isn't currently subscribed to, you will be asked if you want to subscribe. .Ip "/pattern" 8 Scan forward for a newsgroup matching .IR pattern . Patterns do globbing like filenames, i.\|e., use * to match any sequence of characters, and [] to specify a list of characters to match. Use . to match a single character. Unlike normal filename globbing, newsgroup-searching is not anchored to the front and back of the filename, i.\|e. \*(L"/ski\*(R" will find rec.skiing. You may use ^ or $ to anchor the front or back of the search: \*(L"/^test$\*(R" will find newsgroup test and nothing else If you want to include newsgroups with 0 unread articles, append /r. If the newsgroup is not found between the current newsgroup and the last newsgroup, the search will wrap around to the beginning. .Ip "?pattern" 8 Same as /, but search backwards. .Ip u 8 Unsubscribe from the current newsgroup. .Ip "l string" 8 List newsgroups not subscribed to which contain the string specified. .Ip L 8 13v Lists the current state of the .IR .newsrc , along with status information. .Sp .nf \h'|0.5i'Status \h'|2i'Meaning \h'|0.5i' \h'|2i'Count of unread articles in newsgroup. \h'|0.5i'READ \h'|2i'No unread articles in newsgroup. \h'|0.5i'UNSUB \h'|2i'Unsubscribed newsgroup. \h'|0.5i'BOGUS \h'|2i'Bogus newsgroup. \h'|0.5i'JUNK \h'|2i'Ignored line in .newsrc \h'|2i'(e.\|g. readnews \*(L"options\*(R" line). .fi .Sp (A bogus newsgroup is one that is not in the list of active newsgroups in the active file, which on most systems is /usr/lib/news/active unless you use NNTP.) .Ip "m {name}" 8 Move the named newsgroup somewhere else in the .IR .newsrc . If no name is given, the current newsgroup is moved. There are a number of ways to specify where you want the newsgroup \*(-- type h for help when it asks where you want to put it. .Ip c 8 Catch up \*(-- mark all unread articles in this newsgroup as read. .Ip A 8 Abandon the changes made to the current newsgroup since .I trn was started. Useful when you accidentally mark a group as read. .Ip "o {pattern}" 8 .Ip "O {pattern}" 8 Only display those newsgroups whose name matches .IR pattern . Patterns are the same as for the \*(L'/\*(R' command. Multiple patterns may be separated by spaces, just as on the command line. The restriction will remain in effect either until there are no articles left in the restricted set of newsgroups, or another restriction command is given. Since .I pattern is optional, \*(L'o\*(R' by itself will remove the restriction. Using \*(L'O\*(R' will omit empty groups from the cycle. .Ip "a pattern" 8 Add unsubscribed newsgroups matching .IR pattern . If any matching newsgroups are found, you will be asked for each one whether you would like to add it. If you want to add all the newsgroups, you can type \*(L'Y\*(R' and they will be added the the end of the .I .newsrc file. If you don't want to subscribe, all the remaining groups can be ignored by typing \*(L'N\*(R'. After any new newsgroups have been added, the \*(L'a\*(R' command also restricts the current set of newsgroups just like the \*(L'O\*(R' command does. .Ip & 8 Print out the current status of command-line switches and any newsgroup restrictions. .Ip "&switch {switch}" 8 Set additional command-line switches. .Ip && 8 Print out the current macro definitions. .Ip "&&keys commands" 8 Define additional macros. .Ip !command 8 Escape to a subshell. One exclamation mark (!) leaves you in your own news directory. A double exclamation mark (!!) leaves you in the spool directory for news, which is usually /usr/spool/news unless you're using NNTP to read news. The environment variable SHELL will be used if defined. If .I command is null, an interactive shell is started. .Ip v 8 Print the current version number and information on where to send bug reports. .Ip q 8 Quit. .Ip x 8 Quit, restoring .newsrc to its state at startup of .IR trn . The .newsrc you would have had if you had exited with \*(L'q\*(R' will be called .newnewsrc, in case you didn't really want to type \*(L'x\*(R'. .Ip ^K 8 Edit the global list of memorized commands (in the global KILL file) that you wish to be performed in every newsgroup as it is started up (that is, when it is selected at the newsgroup-selection level). This file contains commands (one per line) such as /subject/:j or /author/f:+ to kill or select articles based on the indicated search criteria. There is also a local list of commands for each newsgroup that can contain kill/selection commands tailored for each specific group. Because of the overhead involved in searching for articles to kill, it is better if possible to use a local list rather than the global one. Local memorized commands are usually maintained by using the \*(L'A\*(R' or \*(L'T\*(R' commands from the article/pager level or in the selector. There is also a K search modifier that appends any search command you desire to add. It is also possible to manually edit the file with the \*(L'^K\*(R' command from anywhere inside a newsgroup. If either of the environment variables VISUAL or EDITOR is set, the specified editor will be invoked; otherwise a default editor is invoked on the KILL file. .Sh "The Selector" Most people who don't have all day to read news will want to enter a newsgroup by way of the selector. This is accomplished by using the \*(L'+\*(R' command at the newsgroup-selection or article/pager levels. In fact, this may be the default command for entering a newsgroup, depending on how your version of .I trn was configured and your use of the .B \-X option. .PP The selector displays a list of articles by their subjects and (usually) authors. The articles are grouped into threads by default (which may list multiple subjects per selectable item if the subject has changed during the discussion) and ordered by the date of their oldest unread article. Thread or subject groups are also shown with a count of the number of articles in each group. Each selectable item is preceded by a letter or number that can be typed to toggle its selection. Items that are selected are flagged with a \*(L'+\*(R' after their letter. Groups that have only some of their articles selected are flagged with a \*(L'*\*(R'. You can change the selector's mode (to pick each subject separately or pick individual articles), order the list by a variety of sort criteria, and switch the author display between its long, medium and short styles using the commands detailed below. .PP The following commands are available in the selector: .Ip "a-z,0-9,A-Z" 8 Select/deselect the indicated item by its letter or number. There are quite a few letters omitted from the alpha characters to be typed as commands \*(-- see below. Also, the variable SELECTCHARS is available to customize which characters you want to be used as selection letters, overriding their command function. .Ip SP 8 Perform the default command. This is usually > for most pages, and Z on the last page (although D and X are also quite popular). .Ip CR 8 Begin reading. If no articles are selected, the current item is selected (unless you've marked it as killed). .Ip "Z,TAB" 8 Begin reading. If no articles are selected, read all unread articles. .Ip "\*(L'.\*(R'" 8 Toggle the current item's selection (the one under the cursor). .Ip * 8 Same as \*(L'.\*(R' except that it affects all articles with the same subject (useful in the article selector). .Ip # 8 Make an overriding selection that reads the current item only, temporarily ignoring all other selections. .Ip "k, \*(L',\*(R'" 8 Mark the current item as killed. .Ip "m, \e" 8 Unmark the current item. .Ip \- 8 Set a range, as in a \- k. Repeats the last marking action: selection, deselection, killing, or unmarking. .Ip @ 8 Toggle all visible selections. .Ip M 8 Mark the current item's article(s) to return on newsgroup exit and kill the item. .Ip Y 8 Yank back and select the marked-to-return articles, clearing their to-return status. .Ip E 8 Exclude all unselected items from the selection list (narrow the display). Press it again to pick from all available items. .Ip "n, ]" 8 Move down to the next item (try the down-arrow keypad key also). .Ip "p, [" 8 Move up to the previous item (try the up-arrow keypad key also). .Ip < 8 Go to previous page (try the left-arrow keypad key also). .Ip > 8 Go to next page (try the right-arrow keypad key also). .Ip ^ 8 Go to the first page. .Ip $ 8 Go to the last page. .Ip S 8 Set the items the selector displays: threads, subjects or articles. If the group is unthreaded setting this to threads will thread the group. .Ip = 8 Switch between the article selector and the subject/thread selector. .Ip O 8 Pick the order for the items: date, subject, author, item count (for thread/subject groups), and a subject-date grouping of individual articles. Typing the selection in lower-case will sort the articles in the default direction, while using upper-case will reverse the sort. There is a separate default sort order for the subject/thread selector and the article selector. See the .B \-O option to set your favorite selector mode and sort order as the default. .Ip R 8 Reverse the current sort order. .Ip L 8 Switch the selector's display between the long, medium and short display styles. See the .B \-x option to set your favorite style as the default. .Ip U 8 Switch between selecting unread/read articles. .Ip X 8 Mark all unselected articles as read and start reading. .Ip D 8 Mark unselected articles on the current page as read and begin reading if articles are selected, otherwise go to the next page. .Ip J 8 Mark all selected articles as read (useful after performing some action on them with the \*(L':\*(R' command). .Ip c 8 Catch up \*(-- marks ALL articles as read without affecting their cross-posted counterparts. .Ip A 8 Add a subject-search command to the memorized list (a.\|k.\|a. a KILL file) for this group. You are prompted to choose selection (+), junking (j), selection including all replies (.) or junking including all replies (,). If the thread has more than one subject the first subject is the one chosen for the memorized command. .Ip T 8 Add a thread-oriented command to the memorized list for this group. You are prompted to choose selecting the thread (+), junking the thread (j), or clearing the auto-selection/junking for the thread (c). (Note: there are three other options (\*(L'.\*(R', \*(L',\*(R', and \*(L'C\*(R') on the article-reading level \*(-- look there for an explanation of their use.) .Ip ^K 8 Edit the local list of memorized commands (a.\|k.\|a. a KILL file) for this newsgroup. A detailed description of memorized commands is found in the Article Selection section. .Ip ":command" 8 Apply a command to all the selected threads or their selected articles. You can also use ":E" to end a binary extraction or ":p" to post a new article. Use \*(L"::command\*(R" to apply it to all non-selected threads/articles. .Sp Applicable commands include \*(L'+\*(R'/\*(L'-\*(R' (select/deselect an article), \*(L"++\*(R"/\*(L"--\*(R" (select/deselect a thread), \&\*(L"T+\*(R" (auto-select the entire thread), \*(L"TJ\*(R" (auto-junk the entire thread), \*(L't\*(R' (display article tree), \*(L"s dest\*(R" (save article to a destination), \*(L"e dir\*(R" (extract to directory), \*(L'E\*(R' (end partial uudecode), as well as: S, |, w, W, m, M, j, = and \*(L',\*(R'. .Ip ":.command" 8 Apply a command to the current thread or the selected articles in the current thread. Use \*(L"::.command\*(R" to apply a command to the unselected articles in the current thread. .Ip "/pattern" 8 Scan all articles for a subject containing .I pattern and select it. .Ip "/pattern/modifiers:command{:command}" 8 Apply the commands listed to articles matching the search command (possibly with h, a, b, r, or K modifiers). The default action, if no command is specified, is to select the article's item in the selector (e.\|g. the entire thread (\*(L"++\*(R") in the thread selector). See the section on Regular Expressions and the description of pattern searching in the Article Selection section. .Sp One example: to scan all the unread articles looking for \*(L"topic\*(R" anywhere in the article and then select its group and save the articles to the files topic.1, topic.2, etc. use \*(L"/topic/a:++:s topic.%#\*(R". .Ip N 8 Go to the next newsgroup with unread news. .Ip P 8 Go to the previous newsgroup with unread news. .Ip & 8 Display or set the current status of command-line switches. .Ip && 8 Display or set the current macro definitions. .Ip !command 8 Escape to a subshell. .Ip q 8 Quit this group. .Ip "ESC,+" 8 Quit the selector to the article level. Note: ESC won't work if trn has mapped your arrow keys with default macros and the first character that your arrow keys send is an ESC. .Ip Q 8 Quit the current newsgroup and return to the newsgroup-selection prompt for this group. .Sh "Article-Reading Level" On the article-reading level, .I trn displays unread articles in thread sequence (reading each article and its replies before going on to another topic) unless threads are disabled for a particular group, in which case the default order is the order they arrived at your site (numeric sequence). In either case if you use the subject-search command (^N) you will switch to reading the articles in date order within each matching subject. (Making selections in the subject selector or using the .B \-S switch will automatically turn subject search mode on in an unthreaded group.) .Sp On the article-reading level you are .I not asked whether you want to read an article before the article is displayed; rather, .I trn simply displays the first page (or portion of a page, at low baud rates) of an article and asks if you want to continue. The normal article-reading prompt comes at the END of an article (although article-reading commands can also be given from within the middle of an article in addition to the pager level commands). The prompt at the end of an article looks like this: .Sp End of article 248 (of 257) \*(-- what next? [npq] .Sp The following are the options at this point: .Ip n,SP 8 Scan forward for next unread article. (Note: the \*(L'n\*(R' (next) command when typed at the end of an article does not mark the article as read, since an article is automatically marked as read after the last line of it is printed. It is therefore possible to type a sequence such as \*(L'mn\*(R' and leave the article marked as unread. The fact that an article is marked as read by typing n, N, ^N, F, R, e, s, S, |, w, or W within the MIDDLE of the article is in fact a special case.) .Ip N 8 Go to the next article. .Ip ^N 8 Find the next article with the same subject in date order. This also makes subject search mode (^N) the default command at the end of an article. .Ip p 8 Scan backward for previous unread article. If there is none, stay at the current article. .Ip P 8 Go to the previous article. .Ip \- 8 Go to the previously displayed article (regardless of whether that article is before or after this article in the normal sequence). .Ip ^P 8 Find the previous article with the same subject in date order. Makes subject search mode (^N) the default. .Ip _N 8 Go to the next article in numeric sequence. .Ip _P 8 Go to the previous article in numeric sequence. .Ip "<, >" 8 Browse the previous/next selected thread/subject. If no selections have been made, all the threads that had unread news when you entered the newsgroup (or last left the selector) are treated as selected. Entering an empty newsgroup makes all the already-read threads available for browsing. .Ip "[, ]" 8 Proceed to the left/right in the article tree. Visits already-read articles as well as empty nodes. Try using the left-/right-arrow keys also. .Ip "{, }" 8 Go to the root/leaf of the article tree, even if the node is already read or empty. Proceeds to the very first/last node if you're already at a root/leaf in a multi-root thread. .Ip "(, )" 8 Go to the previous/next sibling in the thread, including \*(L"cousin\*(R" siblings. Try using the up-/down-arrow keys also. .Ip t 8 Display the entire article tree and all its associated subjects. If the group is not currently threaded, it will become threaded to process this command. .Ip ^R 8 Restart the current article. .Ip v 8 Restart the current article verbosely, displaying the entire header. .Ip ^L 8 Refresh the screen. .Ip ^X 8 Restart the current article, and decrypt as a rot13 message. .Ip X 8 Refresh the screen, and decrypt as a rot13 message. .Ip b 8 Back up one page. .Ip ^E 8 Display the last page of the article. .Ip q 8 Quit this newsgroup and go back to the newsgroup-selection level. .Ip ^ 8 Go to the first unread article. .Ip $ 8 Go to the last article (actually, one past the last article). .Ip "number" 8 Go to the numbered article. .Ip _C 8 Switch to next available charset conversion. .Ip "range{,range}:command{:command}" 8 Apply a set of commands to a set of articles. A range consists of either
or \-. A dot \*(L'.\*(R' represents the current article, and a dollar sign \*(L'$\*(R' represents the last article. .Sp Applicable commands include \*(L'm\*(R' (mark as unread), \*(L'M\*(R' (mark as read-until-exit), \*(L'j\*(R' (mark as read), \*(L"s dest\*(R" (save to a destination), \*(L"e dir\*(R" (extract to directory), \&\*(L"!command\*(R" (shell escape), \*(L"=\*(R" (print the subject), \&\*(L'+\*(R'/\*(L'-\*(R' (select/deselect the article), \&\*(L'T+\*(R' (auto-select the entire thread), \*(L'TJ\*(R' (auto-junk the entire thread), \*(L"++\*(R"/\*(L"--\*(R" (select/deselect the associated thread), \*(L'C\*(R' (cancel), as well as S, |, w, W, and t. .Ip ":command" 8 Apply a command to all the selected threads or their selected articles. Use \*(L"::command\*(R" to apply it to all non-selected threads/articles. For applicable commands, see the discussion above for the range command. .Ip ":.command" 8 Apply a command to the current thread or the selected articles in the current thread. Use \*(L"::.command\*(R" to apply a command to the unselected articles in the current thread. .Ip j 8 Junk the current article (i.\|e. mark it as read). If this command is used from within an article, you are left at the end of the article, unlike \*(L'n\*(R', which looks for the next article. .Ip m 8 Mark the current article as still unread. (If you don't want to see this article for a while you're probably better off using M instead of m, otherwise this article might get picked again as the first available article sooner than you'd like.) .Ip M 8 Mark the current article to return on newsgroup exit. Until then, the current article will be marked as read. This is useful for returning to an article in another session. .Ip Y 8 Yank back the marked-to-return articles, clearing their to-return status. If you are reading selected articles, the yanked articles come back selected. .Ip /pattern 8 Scan forward for article containing .I pattern in the subject. See the Regular Expressions section. Together with the escape substitution facility described later, it becomes easy to search for various attributes of the current article, such as subject, article ID, author name, etc. The previous pattern can be recalled with ESC. If .I pattern is omitted, the previous pattern is assumed. .Ip /pattern/f 8 Scan forward for article containing .I pattern in the from line. If you are using thread files the article data you are matching against MAY contain only the real name of the user. If you want to always match the full from line, see the following header-matching option that will read in the full from-line data if it is not already available. .Ip /pattern/Hheader 8 Scan forward for article containing .I pattern in the indicated .I header. Because we scan the entire string up to the end of the modifiers, this modifier must be the last one. For example, \*(L"/jsmoe@somesite.com/rHfrom:m+\*(R" will mark all articles from \*(L"jsmoe@somesite.com\*(R" as unread and select them. Note that if the header line isn't one that trn recognizes in its header parser, you'll have to use the following full-header matching. .Ip /pattern/h 8 Scan forward for an article containing .I pattern in the header. .Ip /pattern/a 8 Scan forward for an article containing .I pattern anywhere in the article. .Ip /pattern/b 8 Scan forward for an article containing .I pattern in the body of the article, but not the signature. (The signature must be properly delimited to be ignored, however.) .Ip /pattern/B 8 Scan forward for an article containing .I pattern anywhere in the body of the article. .Ip /pattern/r 8 Scan read articles also. .Ip /pattern/c 8 Make search case sensitive. Ordinarily upper- and lower-case are considered the same. .Ip /pattern/t 8 Force the search to start at the top of the group (useful while reading the group, since the default is to start at the current article). .Ip /pattern/I 8 Force the search to ignore the THRU line when executed as a memorized command. If the command portion is a selection command (i.\|e. it starts with a \*(L'+\*(R' or a \*(L'.\*(R') this is the default behavior. .Ip /pattern/N 8 Force the search to NOT ignore the THRU line when executed as a memorized command (useful on selection commands -- see also \-k). .Ip "/pattern/modifiers:command{:command}" 8 Apply the commands listed to articles matching the search command (possibly with h, a, b, or r modifiers). Applicable commands include \*(L'm\*(R' (mark as UNread), \*(L'M\*(R' (mark as read-until-exit), \*(L'j\*(R' (junk -- mark as read in all groups), \&\*(L'x\*(R' (mark as read in this group), \&\*(L"s dest\*(R" (save to a dest), \*(L"e dir\*(R" (extract to dir), \&\*(L"!command\*(R" (shell escape), \*(L"=\*(R" (print the subject), \&\*(L'+\*(R' (select the article), \*(L'-\*(R' deselect the article, \&\*(L'T+\*(R' (auto-select the entire thread), \*(L'TJ\*(R' (auto-junk the entire thread), \*(L"++\*(R" (select the associated thread), \&\*(L"--\*(R" deselect the associated thread), and \*(L'C\*(R' (cancel). If the first command is \*(L'm\*(R' or \*(L'M\*(R', modifier r is assumed. A K may be included in the modifiers (not the commands) to cause the entire command (sans K) to be saved to the local KILL file, where it will be applied to every article that shows up in the newsgroup. .Sp For example, to save all articles in a given newsgroup to the line printer and mark them read, use \*(L"/^/\||\|lpr:j\*(R". If you type \*(L"/^/K\||\|lpr:j\*(R", this will happen every time you enter the newsgroup. .Ip ?pattern 8 Scan backward for article containing .I pattern in the subject. May be modified as the forward search is: ?pattern?modifiers[:commands]. It is likely that you will want an r modifier when scanning backward. .Ip k 8 Mark as read all articles with the same subject as the current article. (Note: there is no single character command to temporarily mark as read (M command) articles matching the current subject. That can be done with \*(L"/s/M\*(R", however.) .Ip \, 8 Mark the current article and all its replies as read. .Ip J 8 Junk all the articles in the current thread, even if it contains multiple subjects. .Ip A 8 Add a subject-search command to the memorized list for this group (in the KILL file). You are prompted to choose selection (+), junking (j), selection including all replies (.) or junking including all replies (,). .Ip K 8 This is a synonym for the command \*(L"Aj\*(R" which adds a command to junk the current subject to the memorized commands for the group. See also the K modifier on searches above. .Ip T 8 Add a thread-oriented command to the memorized list for this group. You are prompted to choose selection of entire thread (+), junking of entire thread (j), selection of an article and its replies (.), junking of an article and its replies (,), clearing the auto-selection/junking for this thread (c), or clearing the auto-selection/junking for an article and its replies (C). .Ip ^K 8 Edit the local list of memorized commands (a.\|k.\|a. a KILL file) for this newsgroup. Each line of the KILL file is either a subject-affecting command of the form /pattern/x or a thread-affecting command of the form Tx. The first line in the KILL file has the form \*(L"THRU \*(R", which tells .I trn the maximum article number that the KILL file has been applied to. The THRU value is usually only used to keep header or article searches from happening multiple times. Subject and from-line searches are quite fast if the group has cached data around (e.\|g. a .\|thread or .\|overview file). If it doesn't, the THRU line is used to set a lower boundary on the search to keep the startup time as short as possible. If trn skipped some selections (or you're not sure), wait for the group to finish being cached (e.\|g. visiting the selector forces the caching of all unread articles), quit the group, and re-enter. .Sp To see only newgroup articles in the control newsgroup, for instance, you might include the line .Sp /newgroup/:+ .Sp which selects all subjects containing \*(L"newgroup\*(R". You can add lines automatically via the A and T commands as well as the K search modifier, but editing is the only way to remove subject commands (thread commands die automatically as the thread dies). If either of the environment variables VISUAL or EDITOR is set, the specified editor will be invoked; otherwise a default editor (normally vi) is invoked on the KILL file. .Sp The KILL file may also contain switch-setting lines beginning with \*(L'&\*(R' (see the section on \*(L"Options\*(R") and special commands beginning with \*(L'*\*(R'. There are two such commands at the moment: \*(L"*j\*(R" (junk all articles from THRU to the end of the group) and \*(L"*X\*(R" (junk all .I unselected articles from THRU to the end of the group). Additionally, any line beginning with \*(L'X\*(R' is executed on exit from the newsgroup rather than on entrance. This can be used to set switches back to a default value. One use for this capability is to set your save directory to a custom value upon entry to a newsgroup and set it back on exit using the \-ESAVEDIR option. See also the .B \-/ option for another solution to multiple save directories without using KILL files. .Ip r 8 Reply through net mail. The environment variables MAILPOSTER and MAILHEADER may be used to modify the mailing behavior of .I trn (see the environment section). If the current article does not exist (such as the \*(L"End of newsgroup\*(R" pseudo-article you can get to with a \*(L'$\*(R' command), invokes the mailer to nobody in particular. .Ip R 8 Reply, including the current article in the header file generated. (See \*(L'F\*(R' command below). The YOUSAID environment variable controls the format of the attribution line. .Ip ^F 8 Forward the current article. .Ip f 8 Submit a follow-up article. If the current article does not exist (such as the \*(L"End of newsgroup\*(R" pseudo-article you can get to with a \*(L'$\*(R' command), posts an original (root) article. .Ip F 8 Submit a follow-up article, and include the old article, with lines prefixed either by \*(L">\*(R" or by the argument to the .B \-F switch. .I Trn will attempt to provide an attribution line in front of the quoted article, generated from the From: line of the article. Unfortunately, the From: line doesn't always contain the right name; you should double check it against the signature and change it if necessary, or you may have to apologize for quoting the wrong person. The environment variables NEWSPOSTER, NEWSHEADER and ATTRIBUTION may be used to modify the posting behavior of .I trn (see environment section). .Ip C 8 Cancel the current article, but only if you are the contributor or superuser. .Ip z 8 Supersede the current article, but only if you are the contributor. .Ip Z 8 Same as the \*(L'z\*(R' command, but you start with a copy of the original article to work with. .Ip c 8 Catch up in this newsgroup; i.\|e., mark all articles as read. .Ip U 8 Unkill articles. You can choose to unkill the current thread, sub-thread (the current article and its replies), all the articles, or start up the selector to choose specific articles to unkill. .Ip u 8 Unsubscribe from this newsgroup. .Ip "s destination" 8 Save to a filename or pipe using sh. If the first character of the destination is a vertical bar, the rest of the command is considered a shell command to which the article is passed through standard input. The command is subject to filename expansion. (See also the environment variable PIPESAVER.) If the destination does not begin with a vertical bar, the rest of the command is assumed to be a filename of some sort. An initial tilde \*(L'~\*(R' will be translated to the name of the home directory, and an initial environment variable substitution is also allowed. If only a directory name is specified, the environment variable SAVENAME is used to generate the actual name. If a non-absolute filename is specified, the environment variable SAVEDIR will be used to generate the actual directory. If nothing is specified, then obviously both variables will be used. Since the current directory for .I trn while doing a save command is your private news directory, typing \*(L"s ./filename\*(R" will force the file to your news directory. Save commands are also run through % interpretation, so that you can enter \*(L"s %O/filename\*(R" to save to the directory you were in when you ran .IR trn , and \*(L"s %t\*(R" to save to a filename consisting of the Internet address of the sender. .Sp After generating the full pathname of the file to save to, .I trn determines if the file exists already, and if so, appends to it. .I trn will attempt to determine if an existing file is a mailbox or a normal file, and save the article in the same format. If the output file does not yet exist, .I trn will by default ask you which format you want, or you can make it skip the question with either the .B \-M or .B \-N switch. If the article is to be saved in mailbox format, the command to do so is generated from the environment variable MBOXSAVER. Otherwise, NORMSAVER is used. .Ip "S destination" 8 Save to a filename or pipe using a preferred shell, such as csh. Which shell is used depends first on what you have the environment variable SHELL set to, and in the absence of that, on what your news administrator set for the preferred shell when he or she installed .IR trn . .Ip "| command" 8 Shorthand for \*(L"s | command\*(R". .Ip "w destination" 8 The same as \*(L"s destination\*(R", but saves without the header. .Ip "W destination" 8 The same as \*(L"S destination\*(R", but saves without the header. .Ip "e directory" 8 Extract a shell archive or uuencoded binary to the designated directory. The article is first scanned to try discover what type of data is encapsulated. If a \*(L"cut here\*(R" line is found, the first non-blank line after it must be either the start of a shar header, or the \*(L"begin\*(R" or \*(L"table\*(R" line of a uuencoded binary. The default for extracting shars is to send the data portion of the file to /bin/sh, but that can be overridden with the UNSHAR variable (see the ENVIRONMENT section). Uudecoding is done internally by a decoder that can handle the data being split up over multiple articles, and extracted one piece at a time. To decode a multi-article file, either execute the \*(L'e\*(R' command in each article in sequence, use an article range to execute the command, or use the \*(L":e\*(R" command to repeat the command for each of the currently selected articles. When the \*(L'e\*(R' command is not followed by any arguments, it will repeat the arguments from the last extraction. All directory specifications are relative to the value of SAVEDIR, so you can use the command \*(L"e .\*(R" to force an extraction to SAVEDIR itself. If a uudecoding is in progress (i.\|e. the last piece wasn't extracted yet) and you exit the group, the partial file will be removed. This also occurs if you start to extract a new uuencoded file before the previous one was finished. See also the \*(L'E\*(R' command for ending a multi-part uudecoding manually. .Sp There is one special case that is handled differently: if the first file in a recognizable shar file is a uuencoded binary that was packed with lines starting with an \*(L'X\*(R', we will not unshar the file but instead uudecode it. If this causes problems, you can override the default extraction method by following the directory with an explicit command to execute, as described below. .Ip "e directory|command" 8 This form of the \*(L'e\*(R' command allows you to extract other data formats than shar or uuencoded files or to override the decisions made by the automatic extraction selection described above. In normal operation, all data following what we recognize as a \*(L"cut here\*(R" line will be sent to the specified command. Additionally, the distinctive beginning of a shell archive is also recognized without a preceding cut line. When the command is run, the default directory will be set to the specified directory, or the value of SAVEDIR if unspecified. Entering the \*(L'e\*(R' command without arguments will repeat your previous extract command. You can use the command \*(L"e dir|\*(R" to extract to a new directory using the previously-specified command. .Ip E 8 This command ends any multi-part uuencoded file extraction that you began, but are unable (or unwilling) to complete. The partially extracted file is removed. .Ip & 8 Print out the current status of command-line switches. .Ip "&switch {switch}" 8 Set additional command-line switches. .Ip && 8 Print out current macro definitions. .Ip "&&keys commands" 8 Define an additional macro. .Ip !command 8 Escape to a subshell. One exclamation mark (!) leaves you in your own news directory. A double exclamation mark (!!) leaves you in the spool directory of the current newsgroup. The environment variable SHELL will be used if defined. If .I command is null, an interactive shell is started. .Sp You can use escape key substitutions described later to get to many run-time values. The command is also run through % interpretation, in case it is being called from a range or search command. .Ip \+ 8 Start the selector in the last-used mode. If the newsgroup is unthreaded and the default selector mode is threads, we temporarily switch to subject selection unless manually overridden. .Ip _a 8 Start the selector in article mode. .Ip _s 8 Start the selector in subject mode. .Ip _t 8 Start the selector in thread mode. .Ip _T 8 Start the selector in thread mode unless the group isn't threaded, in which case we settle for the subject selector. .Ip = 8 List subjects of unread articles. .Ip # 8 Print last article number. .Ip _+ 8 Select the entire thread associated with the current article. .Ip _- 8 Deselect the entire thread associated with the current article. .Sh "Pager Level" At the pager level (within an article), the prompt looks like this: .Sp \*(--MORE\*(--(17%) .Sp and a number of commands may be given: .Ip SP 8 Display next page. .Ip x 8 Display next page and decrypt as a rot13 message. .Ip d 8 Display half a page more. .Ip CR 8 Display one more line. .Ip q 8 Go to the end of the current article (don't mark it either read or unread). Leaves you at the \*(L"What next?\*(R" prompt. .Ip j 8 Junk the current article. Mark it read and go to the end of the article. .Ip ^L 8 Refresh the screen. .Ip X 8 Refresh the screen and decrypt as a rot13 message. .Ip b 8 Back up one page. .Ip ^E 8 Display the last page of the article. .Ip _C 8 Switch to next available charset conversion. .Ip t 8 Display the entire article tree, including its associated subjects, and continue reading. If the group is not currently threaded, it will be threaded first. .Ip gpattern 8 Goto (search forward for) .I pattern within current article. Note that there is no space between the command and the pattern. If the pattern is found, the page containing the pattern will be displayed. Where on the page the line matching the pattern goes depends on the value of the .B \-g switch. By default the matched line goes at the top of the screen. .Ip G 8 Search for g pattern again. .Ip ^G 8 This is a special version of the \*(L'g\*(R' command that is for skipping articles in a digest. It is equivalent to setting \*(L"\-g4\*(R" and then executing the command \*(L"g^Subject:\*(R". .Ip TAB 8 This is another special version of the \*(L'g\*(R' command that is for skipping inclusions of older articles. It is equivalent to setting \*(L"\-g4\*(R" and then executing the command \*(L"g^[^c]\*(R", where \fIc\fR is the first character of the last line on the screen. It searches for the first line that doesn't begin with the same character as the last line on the screen. .Ip !command 8 Escape to a subshell. .PP The following commands skip the rest of the current article, then behave just as if typed to the \*(L"What next?\*(R" prompt at the end of the article. See the documentation at the article selection level for these commands. .Sp # $ & / = ? A c C f F k K T ^K J , m M r R ^R u U v Y ^ .br p P ^P - < > [ ] { } number .br range{,range} command{:command} .Sp The following commands also skip to the end of the article, but have the additional effect of marking the current article as read: .Sp n N ^N e s S | w W .Sp .Sh "Miscellaneous facts about commands" An \*(L'n\*(R' typed at either the \*(L"Last newsgroup\*(R" prompt or a \*(L"Last article\*(R" prompt will cycle back to the top of the newsgroup or article list, whereas a \*(L'q\*(R' will quit the level. (Note that \*(L'n\*(R' does not mean \*(L"no\*(R", but rather \*(L"next\*(R".) A space will of course do whatever is shown as the default, which will vary depending on whether .I trn thinks you have more articles or newsgroups to read. .PP The \*(L'b\*(R' (backup page) command may be repeated until the beginning of the article is reached. If .I trn is suspended (via a ^Z), then when the job is resumed, a refresh (^L) will automatically be done (Berkeley-type systems only). If you type a command such as \*(L'!\*(R' or \*(L's\*(R' which takes you from the middle of the article to the end, you can always get back into the middle by typing \*(L'^L\*(R'. .PP In multi-character commands such as \*(L'!\*(R', \*(L's\*(R', \*(L'/\*(R', etc, you can interpolate various run-time values by typing escape and a character. To find out what you can interpolate, type escape and \*(L'h\*(R', or check out the single character % substitutions for environment variables in the Interpretation and Interpolation section, which are the same. Additionally, typing a double escape will cause any % substitutions in the string already typed in to be expanded. .Sh "The Tree Display" When reading a threaded newsgroup, .I trn displays a character representation of the article tree in the upper right corner of the header. For example, consider the following display: .Sp .nf .in +4n (1)+-(1)--(\fI2\fP)--\fI[2]\fP \h'\w'(1)'u'|-(1)+-<3> \h'\w'(1)'u'|\h'\w'-(1)'u'\\-[1] \h'\w'(1)'u'\\-(1)+-[1]--[1] \h'\w'(1)+-(1)'u'\\-[1] .in -4n .fi .Sp This tree represents an initial article that has three direct replies (the second column with three (1)'s). Each reply has further replies branching off from them. In two cases the subject line was altered in the reply, as indicated by the increasing numbers. .PP The third subject is not selected for reading, as indicated by the <>'s. Note you can always forcefully visit an unselected article with \*(L'N\*(R' and \*(L'P\*(R' as well as the thread-navagation commands (which are typically macro'ed to the arrow keys on your keypad). .PP When there is only one subject associated with a thread, all the nodes are marked with the number 1. When the first subject change arrives, it is marked with the number 2, and so on. If you were to look at this thread in the thread selector, the three subjects associated with it would be listed in the same order as the ascending digits. In those rare cases where more than 9 subjects are associated with each thread, the nodes are marked with the letters A-Z, and then by a-z. .PP The articles that have already been read are enclosed in ()'s, Unread articles are displayed in []'s, and unread-but-unselected articles are displayed in <>'s. The currently displayed article has its entire node highlighted in the display. The previously displayed article has only its number highlighted. If the group has not been completely threaded yet, some articles will appear as (?) until trn can determine if the referenced article truly exists or not. If you visit such an article and wait for trn to finish threading the group, the screen will refresh as soon as the presence or absence of the article is determined. .Sh "Options" .I Trn has a nice set of options to allow you to tailor the interaction to your liking. (You might like to know that the author swears by \*(L"\-x6ms \+e \-mu \-S -XX -N -B -p\*(R".) These options may be set on the command line, via the TRNINIT environment variable, via a file pointed to by the TRNINIT variable, or from within .I trn via the & command. Options may generally be unset by typing \*(L"+switch\*(R". Options include: .TP 5 .B \-a causes trn to always thread the unread articles on entry to a group. Without this option trn may enter a group in a partially-threaded state and process the unthreaded articles in the background. The down side of this is that the tree display may not be complete when it is first displayed and you may start out at an odd position in the first thread's article tree. .TP 5 .B \-A tells trn to attempt to create some default macros that will map your arrow keys to useful trn functions (this is the default). Use .B +A to turn this behavior off. .TP 5 .B \-b will force trn to read each thread in a breadth-first order, rather than depth-first. .TP 5 .B \-B will turn on a spinner that twirls when trn is doing background article-processing. A gizmo for those interested in what's going on behind the scenes. .TP 5 .B \-c checks for news without reading news. If a list of newsgroups is given on the command line, only those newsgroups will be checked; otherwise all subscribed-to newsgroups are checked. Whenever the .B \-c switch is specified, a non-zero exit status from .I trn means that there is unread news in one of the checked newsgroups. The .B \-c switch does not disable the printing of newsgroups with unread news; this is controlled by the .B \-s switch. (The .B \-c switch is not meaningful when given via the & command.) .TP 5 .B \-C tells .I trn how often to checkpoint the .IR .newsrc , in articles read. Actually, this number says when to start thinking about doing a checkpoint if the situation is right. If a reasonable check-pointing situation doesn't arise within 10 more articles, the .I .newsrc is check-pointed willy-nilly. .TP 5 .B \-d sets your private news directory to something other than ~/News. The directory name will be globbed (via csh) if necessary (and if possible). The value of SAVEDIR (where articles are saved) is initially set to this directory, but is often manipulated via the .B \-/ option or by manipulating SAVEDIR directly (perhaps via the memorized commands (the KILL file) for a group. Any KILL files (see the K command in the Article Selection section) also reside in this directory and its subdirectories, by default. In addition, shell escapes leave you in this directory. .TP 5 .B \-D enables debugging output. See common.h for flag values. Warning: normally .I trn attempts to restore your .I .newsrc when an unexpected signal or internal error occurs. This is disabled when any debugging flags are set. .TP 5 .B \-e causes each page within an article to be started at the top of the screen, not just the first page. (It is similar to the .B \-c switch of .IR more (1).) You never have to read scrolling text with this switch. This is helpful especially at certain baud rates because you can start reading the top of the next page without waiting for the whole page to be printed. It works nicely in conjunction with the .B \-m switch, especially if you use half-intensity for your highlight mode. See also the .B \-L switch. .TP 5 .B \-E= sets the environment variable to the value specified. Within .IR trn , \*(L"&\-ESAVENAME=%t\*(R" is similar to \*(L"setenv SAVENAME '%t'\*(R" in .IR csh , or \*(L"SAVENAME='%t'; export SAVENAME\*(R" in .IR sh . Any environment variables set with .B \-E will be inherited by subprocesses of .IR trn . .TP 5 .B \-f will make trn avoid various sleep calls and the prompt after the processing of the memorized commands that are intended to allow you time to read a message before the screen clears. This allows the advanced user to cruise along a little faster at the expense of readability. The \-t (terse) option turns on -f by default, but you can override this by specifying \+f after the \-t option. .TP 5 .B \-F sets the prefix string for the \*(L'F\*(R' follow-up command to use in prefixing each line of the quoted article. For example, \*(L"\-F\*(R" inserts a tab on the front of each line (which will cause long lines to wrap around, unfortunately), \*(L"\-F>>>>\*(R" inserts \*(L">>>>\*(R" on every line, and \*(L"\-F\*(R" by itself causes nothing to be inserted, in case you want to reformat the text, for instance. The initial default prefix is \*(L">\*(R". .TP 5 .B \-g tells .I trn which line of the screen you want searched-for strings to show up on when you search with the \*(L'g\*(R' command within an article. The lines are numbered starting with 1. The initial default is \*(L"\-g1\*(R", meaning the first line of the screen. Setting the line to less than 1 or more than the number of lines on the screen will set it to the last line of the screen. .TP 5 .B \-G selects the "fuzzy" processing on the go command when you don't type in a valid group name. With this option on trn will attempt to find the group you probably meant to type, but it can be a little slow about it, so it's not on by default. .TP 5 .B \-h hides (disables the printing of) all header lines beginning with .IR string . For instance, \-hx- will disable the printing of all \*(L"X-Foo:\*(R" headers. Case is not significant. The default for unrecognized headers can be set with the \-hunrecognized option. Alternately you could use \-h (no string) to disable all headers except the Subject line and then use .B +h to select only those lines you want to see. You may wish to use the baud-rate switch modifier below to hide more lines at lower baud rates. .TP 5 .B \-H works just like .B \-h except that instead of setting the hiding flag for a header line, it sets the magic flag for that header line. Certain header lines have magic behavior that can be controlled this way. At present, the following actions are caused by the flag for the particular line: the Date line prints the date in local time if the group is threaded; the From line will only print the commented portion of the user name; the Newsgroups line will only print when there are multiple newsgroups; the Subject line will be underlined and (when threaded) the keyword \*(L'Subject:\*(R' is replaced by its subject number (e.\|g. [1]); and the Expires line will always be suppressed if there is nothing on it. In fact, all of these actions are the default, and you must use .B +H to undo them. .TP 5 .B \-i= specifies how long (in lines) to consider the initial page of an article \*(-- normally this is determined automatically depending on baud rate. (Note that an entire article header will always be printed regardless of the specified initial page length. If you are working at low baud rate and wish to reduce the size of the headers, you may hide certain header lines with the .B \-h switch.) .TP 5 .B \-I tells trn to append all new, unsubscribed groups to the end of the .newsrc. .TP 5 .B \-j forces trn to leave control characters unmolested in messages. .TP 5 .B \-J{} causes trn to join similar subjects into a common thread if they are the same up to the indicated number of characters (the default is 30). You can turn this on and off for specific groups by putting the following lines into your kill file for the group(s): .Sp &-J30 .br X&+J .TP 5 .B \-k tells trn to ignore the THRU line when processing selection searches (i.\|e. searches with a command portion that starts with a \*(L'+\*(R' or a \*(L'.\*(R') in the memorized commands (aka kill files). This is turned on by default, so use .B +k if you want to turn it off. .TP 5 .B \-K is used to keep a trn from checking for new news while you're in the group. Use this when your kill-file processing is so slow that you don't want the group to expand while you're reading. If you only want specific groups to be affected, put these lines into your kill file for the group(s): .Sp &-K .br X&+K .TP 5 .B \-l disables the clearing of the screen at the beginning of each article, in case you have a bizarre terminal. .TP 5 .B \-L tells .I trn to leave information on the screen as long as possible by not blanking the screen between pages, and by using clear to end-of-line. (The .IR more (1) program does this.) This feature works only if you have the requisite termcap capabilities. The switch has no effect unless the .B \-e switch is set. .TP 5 .B \-m= enables the marking of the last line of the previous page printed, to help the user see where to continue reading. This is most helpful when less than a full page is going to be displayed. It may also be used in conjunction with the .B \-e switch, in which case the page is erased, and the first line (which is the last line of the previous page) is highlighted. If .B \-m=s is specified, the standout mode will be used, but if .B \-m=u is specified, underlining will be used. If neither .B =s or .B =u is specified, standout is the default. Use .B +m to disable highlighting. .TP 5 .B \-M forces mailbox format in creating new save files. Ordinarily you are asked which format you want. .TP 5 .B \-N forces normal (non-mailbox) format in creating new save files. Ordinarily you are asked which format you want. .TP 5 .B \-o will act like old versions of trn and not junk cross-referenced articles when using thread commands to junk articles in the current group (such as the selector's \*(L'X\*(R' command). .TP 5 .B \-O{} specifies the selector's mode and (optionally) the sort order. The modes are \*(L'a\*(R'rticle, \*(L's\*(R'ubject, or \*(L't\*(R'hread. The orders are \*(L'd\*(R'ate, \*(L's\*(R'ubject, \*(L'a\*(R'uthor, article \*(L'c\*(R'ount per group, \*(L'n\*(R'umeric, or subject-date \*(L'g\*(R'roups. The order can be capitalized to reverse the indicated order. For example, to choose the article selector in subject order specify \*(L"-Oas\*(R". .TP 5 .B \-p{opt} tells trn to auto-select your postings and their replies as it encounters them in the various groups you read. The optional parameter is either a \*(L'.\*(R', \*(L'p\*(R', or \*(L'+\*(R' (it defaults to \*(L'.\*(R' if omitted) and affects what command trn should execute when it encounters your postings. The default is to execute the command \*(L"T.\*(R" on each of your postings which tells trn to memorize the auto-selection of this article and all its replies. Using \-pp tells trn to use the same command, but start the selection with the parent article, so that you see any other replies to the same article. Using \-p+ tells trn to select the whole thread that contains your reply. .TP 5 .B \-q bypasses the automatic check for new newsgroups when starting .IR trn . .TP 5 .B \-Q defines the set of available charset conversions. This can be useful to restrict the available conversions to those your terminal can handle and/or to specify an alternate default. The first element of this set is taken as default for each article. .TP 5 .B \-r causes .I trn to restart in the last newsgroup read during a previous session with .IR trn . It is equivalent to starting up normally and then getting to the newsgroup with a g command. .TP 5 .B \-s with no argument suppresses the initial listing of newsgroups with unread news, whether .B \-c is specified or not. Thus .B \-c and .B \-s can be used together to test \*(L"silently\*(R" the status of news from within your .I .login file. If .B \-s is followed by a number, the initial listing is suppressed after that many lines have been listed. Presuming that you have your .I .newsrc sorted into order of interest, .B \-s5 will tell you the 5 most interesting newsgroups that have unread news. This is also a nice feature to use in your .I .login file, since it not only tells you whether there is unread news, but also how important the unread news is, without having to wade through the entire list of unread newsgroups. If no .B \-s switch is given .B \-s5 is assumed, so just putting \*(L"rn \-c\*(R" into your \&.login file is fine. .TP 5 .B \-S causes .I trn to enter subject search mode (^N) automatically whenever an unthreaded newsgroup is started up with unread articles or more. Additionally, it causes any \*(L'n\*(R' typed while in subject search mode to be interpreted as \*(L'^N\*(R' instead. (To get back out of subject search mode, the best command is probably \&\*(L'^\*(R'.) If is omitted, 3 is assumed. .TP 5 .B \-t puts .I trn into terse mode. This is more cryptic but useful for low baud rates. (Note that your system administrator may have compiled .I trn with either verbose or terse messages only to save memory.) You may wish to use the baud-rate switch modifier below to enable terse mode only at lower baud rates. .TP 5 .B \-T allows you to type ahead of trn. Ordinarily trn will eat typeahead to prevent your autorepeating space bar from doing a very frustrating thing when you accidentally hold it down. If you don't have a repeating space bar, or you are working at low baud rate, you can set this switch to prevent this behavior. You may wish to use the baud-rate switch modifier below to disable typeahead only at lower baud rates. .TP 5 .B \-u sets the unbroken-subject-line mode in the selector, which simply truncates subjects that are too long instead of dumping the middle portion prior to the last two words of the subject. .TP 5 .B \-U tells trn to not write the .newsrc file out after visiting each group. While this is \*(L"unsafe\*(R" it can be faster if you have a really huge .newsrc. .TP 5 .B \-v sets verification mode for commands. When set, the command being executed is displayed to give some feedback that the key has actually been typed. Useful when the system is heavily loaded and you give a command that takes a while to start up. .TP 5 .B \-V will output trn's version number and quit. .TP 5 .B \-x{}{} Enable the extended (threaded) features of .I trn beyond the .I rn compatibility mode (this may be the default on your system, use +x if you yearn for the good ol' days). The is the maximum number of article-tree lines (from 0 to 11) you want displayed in your header. Use the to choose which thread selector styles you like (\*(L's\*(R'hort, \*(L'm\*(R'edium, or \*(L'l\*(R'ong), and in what order they are selected with the \*(L'L\*(R' command. For example, use .B \-xms to start with the medium display mode and only switch between it and the short mode. You can omit either or both of the parameters, in which case a default of .B \-x6lms is assumed. .TP 5 .B \-X{}{} If you like using the selector, you'll probably want to use this option to make the selector command (+) the default when a newsgroup is started up with at least unread articles. (Your installer may have chosen to make -X0 the default on your system.) It is also used to select which commands you want to be the defaults while using the thread selector. For example, .B \-X2XD will make the thread selector the default command for entering a newsgroup with at least 2 unread articles, and set the default command for the LAST page of the thread selector to be the .B X command and the default command for all other pages to be the .B D command. Either or both parameters can be omitted, as well as the second default command (e.\|g. .B \-XX would change the default newsgroup entry to use the selector and the default command for the last page of the selector to be \*(L'X\*(R'). The default is .B \-X0Z> if just .B \-X is specified. To set the default selector commands without having \*(L'+\*(R' be the default entry into a newsgroup, specify a high number, like 9999. .TP 5 .B \-z sets the minimum number of minutes that must elapse before the active file is refetched to look for new articles. A value of 0 or using +z turns this off. .TP 5 .B \-/ sets SAVEDIR to \*(L"%p/%c\*(R" and SAVENAME to \*(L"%a\*(R", which means that by default articles are saved in a subdirectory of your private news directory corresponding to the name of the the current newsgroup, with the filename being the article number. .B +/ sets SAVEDIR to \*(L"%p\*(R" and SAVENAME to \*(L"%^C\*(R", which by default saves articles directly to your private news directory, with the filename being the name of the current newsgroup, first letter capitalized. (Either .B +/ or .B \-/ may be default on your system, depending on the feelings of your news administrator when he, she or it installed .IR trn .) You may, of course, explicitly set SAVEDIR and SAVENAME to other values \*(-- see discussion in the environment section. .PP Any switch may be selectively applied according to the current baud-rate. Simply prefix the switch with +speed to apply the switch at that speed or greater, and \%\-speed to apply the switch at that speed or less. Examples: \%\-1200\-hposted suppresses the Posted line at 1200 baud or less; \%+9600\-m enables marking at 9600 baud or more. You can apply the modifier recursively to itself also: \%+300\-1200\-t sets terse mode from 300 to 1200 baud. .PP Similarly, switches may be selected based on terminal type: .Sp \-=vt100+T set +T on vt100 .br \-=tvi920\-ETERM=mytvi get a special termcap entry .br \-=tvi920\-ERNMACRO=%./.rnmac.tvi .br set up special key-mappings .br +=paper\-v set verify mode if not hardcopy .PP Some switch arguments, such as environment variable values, may require spaces in them. Such spaces should be quoted via ", ', or \e in the conventional fashion, even when passed via TRNINIT or the & command. .Sh "Regular Expressions" The patterns used in article searching are regular expressions such as those used by .IR ed (1). In addition, \ew matches an alphanumeric character and \eW a non-alphanumeric. Word boundaries may be matched by \eb, and non-boundaries by \eB. The bracketing construct \e(\ ...\ \e) may also be used, and \edigit matches the digit'th substring, where digit can range from 1 to 9. \e0 matches whatever the last bracket match matched. Up to 10 alternatives may given in a pattern, separated by \e|, with the caveat that \e(\ ...\ \e|\ ...\ \e) is illegal. .Sh "Character Set Conversions" .I trn can use character set conversions when displaying articles. This helps users in non-English-speaking countries to display special characters on 7-bit displays. .I trn assumes that articles use the ISO-8859-1 character set and converts the special characters (e.g., \*(L"umlauts\*(R") to a string of ASCII characters. Currently the following conversions are supported (see the .I \-Q option): .TP 5 .B p Plain. No change. This is the default. .TP 5 .B a ISO->ASCII. Special characters are mapped to ASCII, e.g. the umlaut-o character becomes oe. .TP 5 .B m ISO->ASCII monospaced. Special characters are mapped to exactly one similar-looking ASCII character, e.g. umlaut-o becomes o. Used where correct spacing is more important than accuracy. .TP 5 .B t TeX->ISO. Assuming your display can handle the ISO-8859-1 charset, .I trn transforms umlauts in the TeX notation, which is commonly used in Germany, to real ISO characters, e.g. "a becomes umlaut-a. .PP The selected conversion, if different from .BR p , will be displayed in the article level and pager prompt. The conversion is also used when including original articles in a reply or followup. It is not used when saving articles to files. .Sh "Interpretation and Interpolation" Many of the strings that .I trn handles are subject to interpretations of several types. Under filename expansion, an initial \*(L"~/\*(R" is translated to the name of your home directory, and \*(L"~name\*(R" is translated to the login directory for the user specified. Filename expansion will also expand an initial environment variable, and also does the backslash, caret and percent expansion mentioned below. .PP All interpreted strings go through backslash, caret and percent interpretation. The backslash escapes are the normal ones (such as \en, \et, \e033, etc.). The caret escapes indicate control codes (such as ^i, ^l, etc.). If you wish to pass through a backslash or a caret it must be escaped with a backslash. The special percent escapes are similar to printf percent escapes. These cause the substitution of various run-time values into the string. The following are currently recognized: .Ip %a 8 Current article number. .Ip %A 8 Full name of current article (%P/%c/%a). .Ip %b 8 Destination of last save command, often a mailbox. .Ip %B 8 The byte offset to the beginning of the part of the article to be saved, set by the save command. The \*(L's\*(R' and \*(L'S\*(R' commands set it to 0, and the \*(L'w\*(R' and \*(L'W\*(R' commands set it to the byte offset of the body of the article. .Ip %c 8 Current newsgroup, directory form. .Ip %C 8 Current newsgroup, dot form. .Ip %d 8 Full name of newsgroup directory (%P/%c). .Ip %D 8 \*(L"Distribution:\*(R" line from the current article. .Ip %e 8 The last command executed to extract data from an article. .Ip %E 8 The last directory where an extracted file went. .Ip %f 8 \*(L"From:\*(R" line from the current article, or the \*(L"Reply-To:\*(R" line if there is one. This differs from %t in that comments (such as the full name) are not stripped out with %f. .Ip %F 8 \*(L"Newsgroups:\*(R" line for a new article, constructed from \*(L"Newsgroups:\*(R" and \*(L"Followup-To:\*(R" lines of current article. .Ip %g 8 The general mode of .I trn, for use in conditional macros. .Sp .nf I Init mode. s Selector mode. r Rn mode. i Input mode (newline terminated). p Prompt mode (single-character input). c Choice mode (multi-choice input). .fi .Ip %h 8 Name of the header file to pass to the mail or news poster, containing all the information that the poster program needs in the form of a message header. It may also contain a copy of the current article. The format of the header file is controlled by the MAILHEADER and NEWSHEADER environment variables. .Ip %H 8 Host name (your machine's name). .Ip %i 8 \*(L"Message-I.D.:\*(R" line from the current article, with <> guaranteed. .Ip %I 8 The reference indication mark for citing prior articles (see the .B \-F switch.) .Ip %l 8 The news administrator's login name, if any. .Ip %L 8 Login name (yours). .Ip %m 8 The current mode of .I trn, for use in conditional macros. .Sp .nf i Initializing. n Newsgroup-list level. f End (finis) of newsgroup-list level. t The thread/subject/article selector. c Newsrc selector. w Newsgroup selector. j Addgroup selector. l Option selector. a Article level ("What next?"). e End of the article level. p Pager level ("MORE" prompt). u Unkill prompt. d Selector mode prompt. o Selector order prompt. m Memorize thread command prompt. r Memorize subject command prompt. z Option edit prompt. k Processing memorized (KILL-file) commands. A Add this newsgroup? B Abandon confirmation. C Catchup confirmation. D Delete bogus newsgroups? F Is follow-up a new topic? M Use mailbox format? R Resubscribe to this newsgroup? K Press any key prompt. .fi .Sp Note that yes/no questions are all upper-case modes. If, for example, you wanted to disallow defaults on all yes/no questions, you could define the following macro: .Sp .nf \e040 %(%m=[A-Z]?h: ) .fi .Ip %M 8 The number of articles marked to return via the \*(L'M\*(R' command. If the same article is Marked multiple times, \*(L"%M\*(R" counts it multiple times in the current implementation. .Ip %n 8 \*(L"Newsgroups:\*(R" line from the current article. .Ip %N 8 Full name (yours). .Ip %o 8 Organization (yours). .Ip %O 8 Original working directory (where you ran trn from). .Ip %p 8 Your private news directory, normally ~/News. .Ip %P 8 Public news spool directory, normally /usr/spool/news on systems that don't use NNTP. .Ip %q 8 The value of the last \*(L"quoted\*(R" input string (see the %" interp). .Ip %r 8 Last reference on references line of current article (parent article id). .Ip %R 8 References list for a new article, constructed from the references and article ID of the current article. .Ip %s 8 Subject, with all Re's and (nf)'s stripped off. .Ip %S 8 Subject, with one \*(L"Re:\*(R" stripped off. .Ip %t 8 \*(L"To:\*(R" line derived from the \*(L"From:\*(R" and \*(L"Reply-To:\*(R" lines of the current article. This always returns an Internet format address. .Ip %T 8 \*(L"To:\*(R" line derived from the \*(L"Path:\*(R" line of the current article to produce a uucp path. .Ip %u 8 The number of unread articles in the current newsgroup. .Ip %U 8 The number of unread articles in the current newsgroup, not counting the the current article. When threads are selected, this count reflects only selected articles. .Ip %v 8 The number of unselected articles, not counting the current article if it is unselected. .Ip %w 8 The directory where mthreads keeps its tmp files. .Ip %W 8 The directory where thread files are placed. .Ip %x 8 The news library directory. .Ip %X 8 The trn library directory. .Ip %z 8 The length of the current article in bytes. .Ip %Z 8 The number of selected threads. .Ip %~ 8 Your home directory. .Ip %. 8 The directory containing your dot files, which is your home directory unless the environment variable DOTDIR is defined when trn is invoked. .Ip %# 8 The current count for a multi-file save, starting with 1. This value is incremented by one for each file saved or extracted within a single command. .Ip %$ 8 Current process number. .Ip %/ 8 Last search string. .Ip %? 8 A space unless the current interp string is > 79 characters, at which point it turns into a newline. .Ip %% 8 A percent sign. .Ip "%{name} or %{name\-default}" 8 The environment variable \*(L"name\*(R". .Ip %[name] 8 The value of header line \*(L"Name:\*(R" from the current article. The \*(L"Name:\ \*(R" is not included. For example \*(L"%D\*(R" and \*(L"%[distribution]\*(R" are equivalent. The name must be spelled out in full. .Ip %`command` 8 Inserts the output of the command, with any embedded newlines translated to space. .Ip %""prompt"" 8 Prints prompt on the terminal, then inputs one string, and inserts it. .Ip "%(test_text=pattern?then_text:else_text)" 8 If .I test_text matches .IR pattern , has the value .IR then_text , otherwise .IR else_text . The \*(L":else_text\*(R" is optional, and if absent, interpolates the null string. The = may be replaced with != to negate the test. To quote any of the meta-characters (\*(L'=\*(R', \*(L'?\*(R', \*(L':\*(R', or \*(L')\*(R'), precede with a backslash. .Ip %digit 8 The digits 1 through 9 interpolate the string matched by the nth bracket in the last pattern match that had brackets. If the last pattern had alternatives, you may not know the number of the bracket you want \*(-- %0 will give you the last bracket matched. .PP Modifiers: to capitalize the first letter, insert \*(L'^\*(R': \*(L"%^C\*(R" produces something like \*(L"Rec.humor\*(R". Inserting \*(L'_\*(R' causes the first letter following the last \&\*(L'/\*(R' to be capitalized: \*(L"%_c\*(R" produces \*(L"rec/Humor\*(R". .PP Inserting \*(L'\\\*(R' will insert a backslash before any characters that would be magic in a regular expression, including \*(L'%\*(R': \*(L"%\\C\*(R" produces \*(L"rec\\.humor\*(R". .PP Inserting \*(L"'\*(R" will insert a backslash before any single-quotes in the result, suitable for enclosing in single-quotes and sending to a shell: \*(L"'%'s'\*(R" might produce \*(L"'I'\\''m a subject'\*(R". .PP Inserting \*(L"''\*(R" will insert a backslash before any double-quotes in the result, suitable for enclosing in double-quotes and sending to a shell. .PP Inserting \*(L">\*(R" will strip out just the address portion of an address string such as the From line. .PP Inserting \*(L")\*(R" will strip out just the comment (real name) portion of an address string such as the From line. .PP Inserting \*(L":FMT\*(R" will format the result according to the printf-style FMT string: \*(L"%:-50.50s\*(R" left-justifies the subject into a 50 character field. .SH ENVIRONMENT The following environment variables are paid attention to by .IR trn . In general the default values assumed for these variables by .I trn are reasonable, so if you are using .I trn for the first time, you can safely ignore this section. Note that the defaults below may not correspond precisely to the defaults on your system. To find the actual defaults you would need to look in config.h and common.h in the trn source directory, and the file INIT in the trn library directory. .PP Those variables marked (%) are subject to % interpolation, and those marked (~) are subject to both % interpolation and ~ interpretation. .Ip "ATTRIBUTION (%)" 8 Gives the format of the attribution line in front of the quoted article included by an F command. .Sp Default: In article %i,%?%)f <%>f> wrote: .Ip "AUTOSUBSCRIBE" 8 When .I trn is checking for new newsgroups and finds one matching one of the patterns in AUTOSUBSCRIBE, the new group is automatically added to the end of the .newsrc, subscribed. Newsgroups not matching this or AUTOUNSUBSCRIBE, below, are offered to the user. .Sp AUTOSUBSCRIBE is a comma separated list of newsgroup patterns ala \&\*(L'o\*(R', \*(L'/\*(R', etc. It can also include \*(L"but not\*(R" entries preceded by \*(L'!\*(R'. \*(L"a,b,!c,d\*(R" is read as \*(L"matching a or b, unless it also matches c; matching d regardless\*(R". Another way to look at it is \*(L"(((a or b) and not c) or d)\*(R". To automatically subscribe to all local groups but be choosy about non-local groups, one might say \*(L"*,!*.*\*(R". .Sp Default: (none) .Ip "AUTOUNSUBSCRIBE" 8 AUTOUNSUBSCRIBE is very similar to AUTOSUBSCRIBE, above, but new newsgroups matching it are automatically added to the end of the .newsrc file, unsubscribed. If a newsgroup matches AUTOSUBSCRIBE, AUTOUNSUBSCRIBE is not consulted. .Sp Default: (none) .Ip "CANCEL (~)" 8 The shell command used to cancel an article. .Sp Default: inews \-h < %h .Ip "CANCELHEADER (%)" 8 13v The format of the file to pass to the CANCEL command in order to cancel an article. .Sp Default: .br Newsgroups: %n .br Subject: cmsg cancel %i .br References: %R .br Reply-To: %L@%H (%N) .br Distribution: %D .br Organization: %o .sp 1 %i cancelled from trn. .Ip DOTDIR 8 Where to find your dot files, if they aren't in your home directory. Can be interpolated using \*(L"%.\*(R". .Sp Default: $HOME .Ip "EDITOR (~)" 8 The name of your editor, if VISUAL is undefined. .Sp Default: whatever your news administrator compiled in, usually vi. .Ip "EXSAVER (%)" 8 The shell command to execute in order to extract data to either /bin/sh or a user-specified command. .Sp Default: tail +%Bc %A | %e .Ip "FIRSTLINE (%)" 8 Controls the format of the line displayed at the top of an article. Warning: this may go away. .Sp The default (ignoring the Marked to return display in unthreaded groups) is approximately: .Sp %C #%a%(%Z=^0$?%(%U!=^0$? (%U more\e)): (%U + %v more\e)) .Ip "FORWARDHEADER (%)" 8 The format of the header file for forwarding messages. See also FORWARDPOSTER. .Sp Default: .Sp To: .br Subject: %(%i=^$?:[subject] (fwd\e\e) .br %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} .br )Newsgroups: %n .br In-Reply-To: %i) .br %(%[references]=^$?:References: %[references] .br )Organization: %o .br Cc: .br Bcc: \en\en .Ip "FORWARDPOSTER (~)" 8 The shell command to be used by the forward command (^F) in order to allow you to edit and deliver the file. .I trn will not itself call upon an editor for replies \*(-- this is a function of the program referenced by FORWARDPOSTER. See also FORWARDHEADER and MAILPOSTER. .Sp Default: Rnmail \-h %h .Ip "FROM (%)" 8 What to put in the From: header of your posts, email replies, and email forwards, instead of whatever the default name and address are for your system. This will only work if you use the default settings for the NEWSHEADER, MAILHEADER, and FORWARDHEADER variables, or if your custom ones use FROM to set the From: header. .Sp Regardless of the settings of NEWSHEADER, MAILHEADER, and FORWARDHEADER, the setting of FROM is used to determine which articles may be cancelled or superseded. .Sp Default: undefined .Ip HIDELINE 8 If defined, contains a regular expression which matches article lines to be hidden, in order, for instance, to suppress quoted material. A recommended string for this purpose is \*(L"^>...\*(R", which \fIdoesn't\fR hide lines with only \*(L'>\*(R', to give some indication that quoted material is being skipped. If you want to hide more than one pattern, you can use \*(L"\||\|\*(R" to separate the alternatives. You can view the hidden lines by restarting the article with the \*(L'v\*(R' command. .Sp There is some overhead involved in matching each line of the article against a regular expression. You might wish to use a baud-rate modifier to enable this feature only at low baud rates. .Sp Default: undefined .Ip HOME 8 Your home directory. Affects ~ interpretation, and the location of your dot files if DOTDIR is not defined. .Sp Default: $LOGDIR .Ip "KILLGLOBAL (~)" 8 Where to find the KILL file to apply to every newsgroup. See the \*(L'^K\*(R' command at the newsgroup-selection level. .Sp Default: %p/KILL .Ip "KILLLOCAL (~)" 8 Where to find the KILL file for the current newsgroup. See the commands \*(L'K\*(R' and \*(L'^K\*(R' at the article selection level, and the search modifier \*(L'K\*(R'. .Sp Default: %p/%c/KILL .Ip LOGDIR 8 Your home directory if HOME is undefined. Affects ~ interpretation, and the location of your dot files if DOTDIR is not defined. .Sp Default: none. .Sp Explanation: you must have either $HOME or $LOGDIR. .Ip LOGNAME 8 Your login name, if USER is undefined. May be interpolated using \*(L"%L\*(R". .Sp Default: value of getlogin(). .Ip LOCALTIMEFMT 8 The format used by strftime() to print the local time. The Date line is only displayed in local time if the group is threaded (see the \-H option for more information on Date). .Sp Default: %a %b %e %X %Z %Y .Sp which is the same format as the .IR date (1) command. .Ip "MAILCALL (~)" 8 What to say when there is new mail. .Sp Default: (Mail) .Ip "MAILFILE (~)" 8 Where to check for mail. .Sp Default: /usr/spool/mail/%L .Ip "MAILHEADER (%)" 8 The format of the header file for replies. See also MAILPOSTER. .Sp Default: .Sp To: %t .br Subject: %(%i=^$?:Re: %S .br %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} .br )Newsgroups: %n .br In-Reply-To: %i) .br %(%[references]=^$?:References: %[references] .br )Organization: %o .br Cc: .br Bcc: \en\en .Ip "MAILPOSTER (~)" 8 The shell command to be used by the reply commands (r and R) in order to allow you to enter and deliver the response. .I trn will not itself call upon an editor for replies \*(-- this is a function of the program referenced by MAILPOSTER. See also MAILHEADER. .Sp Default: Rnmail \-h %h .Ip "MBOXSAVER (~)" 8 The shell command to save an article in mailbox format. .Sp Default: %X/mbox.saver %A %P %c %a %B %C "%b" \e .br "From %t %`date`" .Sp Explanation: the first seven arguments are the same as for NORMSAVER. The eighth argument to the shell script is the new From line for the article, including the posting date, derived either directly from the Posted: line, or not-so-directly from the Date: line. Header munging at its finest. .Ip MODSTRING 8 The string to insert in the group summary line, which heads each article, for a moderated group. See also NOPOSTRING. .Sp Default: " (moderated)" .Ip NAME 8 Your full name. May be interpolated using \*(L"%N\*(R". .Sp Default: name from /etc/passwd, or ~/.fullname. .Ip "NEWSHEADER (%)" 8 16v The format of the header file for follow-ups. See also NEWSPOSTER. .Sp Default: .Sp %(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n .br ))Newsgroups: %(%F=^$?%C:%F) .br Subject: %(%S=^$?%"\en\enSubject: ":Re: %S) .br Summary: .br Expires: .br %(%R=^$?:References: %R .br )Sender: .br Followup-To: .br %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} .br )Distribution: %(%i=^$?%"Distribution: ":%D) .br Organization: %o .br Keywords: %[keywords] .br Cc: \en\en .Ip NEWSORG 8 Either the name of your organization, or the name of a file containing the name of your organization. (For use at sites where the ORGANIZATION environmental variable is already in use. NEWSORG will override ORGANIZATION if both are present.) May be interpolated using \*(L"%o\*(R". .Sp Default: whatever your news administrator compiled in. .Ip "NEWSPOSTER (~)" 8 The shell command to be used by the follow-up commands (f and F) in order to allow you to enter and post a follow-up news article. If not set, .I trn handles the whole process and calls inews directly. See also NEWSHEADER. .Ip NNTPSERVER 8 The hostname of your NNTPSERVER. [This does not apply unless you are running the NNTP version of trn.] .Sp Default: the hostname listed in the server file, usually /usr/local/lib/rn/server. .Ip NOPOSTRING 8 The string to insert in the group summary line, which heads each article, for a group to which local posting is not allowed. See also MODSTRING. .Sp Default: " (no posting)" .Ip "NORMSAVER (~)" 8 The shell command to save an article in the normal (non-mailbox) format. .Sp Default: %X/norm.saver %A %P %c %a %B %C "%b" .Ip ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. (If NEWSORG is set, it will override ORGANIZATION.) May be interpolated using \*(L"%o\*(R". .Sp Default: whatever your news administrator compiled in. .Ip PAGESTOP 8 If defined, contains a regular expression which matches article lines to be treated as form-feeds. There are at least two things you might want to do with this. To cause page breaks between articles in a digest, you might define it as \*(L"^--------\*(R". To force a page break before a signature, you could define it as \*(L"^-- $\*(R". (Then, when you see \*(L"--\*(R" at the bottom of the page, you can skip the signature if you so desire by typing \*(L'n\*(R' instead of space.) To do both, you could use \*(L"^--\*(R". If you want to break on more than one pattern, you can use \*(L"\||\|\*(R" to separate the alternatives. .Sp There is some overhead involved in matching each line of the article against a regular expression. You might wish to use a baud-rate modifier to enable this feature only at low baud rates. .Sp Default: undefined .Ip "PIPESAVER (%)" 8 The shell command to execute in order to accomplish a save to a pipe (\*(L"s\ |\ command\*(R" or \*(L"w\ |\ command\*(R"). The command typed by the user is substituted in as %b. .Sp Default: %(%B=^0$?<%A:tail +%Bc %A |) %b .Sp Explanation: if %B is 0, the command is \*(L"<%A %b\*(R", otherwise the command is \*(L"tail +%Bc %A | %b\*(R". .Ip REPLYTO 8 The value of the \*(L"Reply-To:\*(R" header, if needed. .Sp Default: \*(L" \*(R". .Ip RNINIT 8 This variable is used when initializing trn in rn-compatibility mode (see the \-x switch) or when the TRNINIT variable isn't defined. See the TRNINIT variable for a description. .Ip "RNMACRO (~)" 8 The name of the file containing macros and key mappings when running trn as rn. See also the TRNMACRO variable and the CUSTOM MACROS section. .Sp Default: %./.rnmac .Ip "SAVEDIR (~)" 8 The name of the directory to save to, if the save command does not specify a directory name. .Sp Default: .br If .B \-/ is set: %p/%c .br If .B +/ is set: %p .Ip "SAVENAME (%)" 8 The name of the file to save to, if the save command contains only a directory name. .Sp Default: .br If .B \-/ is set: %a .br If .B +/ is set: %^C .Ip "SELECTCHARS" 8 The characters used by the thread selector to select the associated thread of discussion. You can specify up to 64 visible characters, including upper- and lower-case letters, numbers, and many punctuation characters. Selection characters override command characters in the selector, but are not excluded from macro expansion, so be careful. .br Default: abdefgijlorstuvwxyz1234567890BCFGHIKMVW .br (You'll notice various characters are omitted to allow them to be typed as commands in the selector.) .Ip SHELL 8 The name of your preferred shell. It will be used by the \*(L'!\*(R', \*(L'S\*(R' and \*(L'W\*(R' commands. .Sp Default: whatever your news administrator compiled in. .Ip "SUBJLINE (%)" 8 Controls the format of the lines displayed by the \*(L'=\*(R' command at the article selection level. .Sp Default: %s .Ip "SUPERSEDEHEADER (%)" 8 16v The format of the header file for a supersede article. .Sp Default: .Sp From: %L@%H (%N) .br Newsgroups: %n .br Subject: %S .br Distribution: %D .br Organization: %o .br Supersedes: %i .Ip TERM 8 Determines which termcap entry to use, unless TERMCAP contains the entry. .Ip TERMCAP 8 Holds either the name of your termcap file, or a termcap entry. .Sp Default: /etc/termcap, normally. .Ip TRNINIT 8 Default values for switches may be passed to .I trn by placing them in the TRNINIT variable (or RNINIT if you're starting trn in rn-compatibility mode). Any switch that is set in this way may be overruled on the command line, or via the \*(L'&\*(R' command from within .IR trn . Binary-valued switches that are set with \*(L"\-switch\*(R" may be unset using \*(L"+switch\*(R". .Sp If TRNINIT begins with a \*(L'/\*(R' it is assumed to be the name of a file containing switches. You can put comments in this file by preceding them with a \*(L'#\*(R' as long as this is the first character on a line or it follows some white-space (which delimits the switches in the file). If you want to set many environment variables but don't want to keep them all in your environment, or if the use of any of these variables conflicts with other programs, you can use this feature along with the .B \-E switch to set the environment variables upon startup. .Sp Default: \*(L" \*(R". .Ip "TRNMACRO (~)" 8 The name of the file containing macros and key mappings. If the file is not found, the RNMACRO variable is used to look for your rn macros. For information on what to put into this file, see the CUSTOM MACROS section. .Sp Default: %./.trn/macros .Ip "UNSHAR (~)" 8 The shell command to execute in order to accomplish the unshar'ing of a shell archive. .Sp Default: /bin/sh .Ip USER 8 Your login name. May be interpolated using \*(L"%L\*(R". .Sp Default: $LOGNAME .Ip "VISUAL (~)" 8 The name of your editor. .Sp Default: $EDITOR .Ip XTERMMOUSE 8 If you set this variable to \*(L'y\*(R' (yes), trn will enable the use of the xterm mouse in the selector if you are using an xterm. Once enabled left-clicking on an item selects it while middle-clicking an item will move to that item. If you click the top (header) line of the selector it moves up a page. If you click the bottom (footer) line of the selector it executes the default command for the page (left click) or goes down a page (middle click). You can also use the right mouse button to move up or down a page by clicking in the upper-half or lower-half of the screen, respectively. .Ip "YOUSAID (%)" 8 Gives the format of the attribution line in front of the quoted article included by an R command. .Sp Default: In article %i you write: .SH "AUTOMATIC MACROS" On startup .I trn attempts to build a set of macros that map your keypad arrow keys to useful functions. These default actions are mentioned in the prior description of each level's commands. If you don't like this (or trn gets it wrong), you can disable the automatic macros by using the .B \-A option. .SH "CUSTOM MACROS" When .I trn starts up it looks for a file containing macro definitions (see environment variables TRNMACRO and RNMACRO). Any sequence of commands may be bound to any sequence of keys, so you could re-map your entire keyboard if you desire. Blank lines or lines beginning with # in the macro file are considered comments; otherwise .I trn looks for two fields separated by white space. The first field gives the sequence of keystrokes that trigger the macro, and the second field gives the sequence of commands to execute. Both fields are subject to % interpolation, which will also translate backslash and caret sequences. (The keystroke field is interpreted at startup time, but the command field is interpreted at macro execution time so that you may refer to % values in a macro.) For example, if you want to reverse the roles of carriage return and space in .I trn .Sp ^J \e040 .br ^M \e040 .br \e040 ^J .Sp will do just that. By default, all characters in the command field are interpreted as the canonical .I trn characters, i.\|e. no macro expansion is done. Otherwise the above pair of macros would cause an infinite loop. To force macro expansion in the command field, enclose the macro call with ^( ... ^) thusly: .Sp @s |mysavescript .br @w w^(@s^) .Sp You can use the %() conditional construct to construct macros that work differently under different circumstances. In particular, the current mode (%m) of .I trn could be used to make a command that only works at a particular level. This is particularly vital for the selector which uses most of the lower-case letters to select the associated item in its display. For example, .Sp a %(%m=t?a:s art.hold\en) .Sp will return the original letter (a) in the selector, and the command \*(L"s art.hold\en\*(R" everywhere else. .Sp %(%{TERM}=vt100?^[[O) /^J .Sp will do the binding only if the terminal type is vt100, though if you have many of these it would be better to have separate files for each terminal. .Sp If you want to bind a macro to a function key that puts a common garbage character after the sequence (such as the carriage return on the end of Televideo 920 function sequences), DO NOT put the carriage return into all the sequences or you will waste a CONSIDERABLE amount of internal storage. Instead of \*(L"^AF^M\*(R", put \*(L"^AF+1\*(R", which indicates to .I trn that it should gobble up one character after the F. .SH "WHAT'S NEW" Here's a quick run-down of .IR trn 's features and commands aimed at the knowledgeable .I rn or .I trn user. .PP The addition of true reference-line threading is one of the biggest improvements over rn. This threading allows you to read a discussion in reply order with an article's replies being attached to the article that inspired them. Threads will encompass multiple subjects whenever a reply to an article in the thread arrives with a different subject. This is usually done to better indicate the topic in the reply when it diverges from the original subject. .PP Another big improvement is the selector, which is bound to the \*(L'+\*(R' key. The selector displays a list of threads, subjects, or individual articles to allow you to select the topics that interest you by typing their associated letter. The difference between the thread and the subject selector is that the subject selector displays all subjects with a separate selection letter, even those tied together via their references. This can be quite useful if you select some threads and desire to weed out some extraneous discussions: you could switch the selector into exclusive mode (\*(L'E\*(R' shows only selected threads) and then into subject mode (\*(L'Ss\*(R') to separate the threads into their component subjects and deselect or kill the subjects you don't care about. You don't have to go to all this trouble using the selector if you prefer to just hit the \*(L'k\*(R' key when you start reading a subject you're not interested in. The selector can also switch between showing unread articles and articles that have already been read, allowing you to selectively re-read discussions (this is the \*(L'U\*(R' command in the selector). .PP Another threaded addition is the article-tree display in the upper-right corner of the header. Looking at the tree gives you a feel for how the articles you are reading relate to each other, allowing you to see at a glance when there are lots of replies and decide if you want to junk an uninteresting set of replies or perhaps tough it out. .PP The header display has also been modified to hide a few more lines by default (e.\|g. References), but, as always, you can override these with \-h. There is also some more \*(L"magic\*(R" in the header: the From header can be trimmed to be just the comment portion (if available), and the Date header is displayed in local time (by default). Use \-H and +H to turn header magic on and off. .PP Once you begin reading articles, use the regular movement commands (n, N, p, P, etc.) as you normally would. You'll find that these commands track the reply order shown in the tree display. Then try using ^N and ^P, which follow a subject in the order the articles were posted. Finally, check out the [, ], (, ), {, and } commands to move around in the article tree a bit more directly. The first four commands should also be bound to your keypad's arrow keys, making them easier to type. For example, typing \*(L'[\*(R' (left) takes you to your parent article, even if it was already read, which is very useful for tracking down the cited portion of the article in its original context. .PP There are additional kill commands for the entire thread (J) and the current article and all its replies (,). .PP The KILL files have been extended and the commands inside them are now referred to memorized commands, since they are often used for selection rather than killing of articles. There are new, easier ways to add memorized commands using the \*(L'A\*(R'dd and \*(L'T\*(R'hread commands. The \*(L'A\*(R' command is subject-oriented, while the \*(L'T\*(R' command is article-oriented (meaning they affect a specific set of articles rather than any article that happens to have a matching subject). They both prompt you for what kind of command you want to add, making both auto-killing and auto-selecting just as easy. .PP There is also an easy way to skip around among the various threads with the < and > commands. Use them if you want to skip a set of article and read them later instead of junking them. .PP Note: your news administrator has the option of turning thread processing off for individual groups, and thus it is possible for some groups to not have any pre-processed thread information available for use. When .I trn encounters such a group, it generates the thread information on the fly while entering the group. For really large groups (or really slow systems), this can take an appreciable amount of time. If you can't talk your news administrator into pre-threading the group, you can turn off the threading on a group-by-group basis using the \*(L't\*(R' command at the newsgroup-selection level. Groups turned off in this way are read in the .I rn style \*(-- articles arranged in arrival order unless you specify the \-S option, which reads the articles in date order by subject. .PP Take note of the \*(L"e dir\*(R" command, which is used to extract a shell archive or uuencoded file into the specified directory. It is even possible to extract other data formats if you specify the appropriate filter command (e.\|g. \*(L"e dir|cmd\*(R". .PP Also, if you plan to use macro definitions, it is good to keep in mind that the selector uses most of the lower-case letters for selection, and thus it is a good idea to explicitly set the mode(s) in which a macro applies. For example, if you want to press \*(L'f\*(R' from the article pager/selector to forward the current article to the user \*(L"smith\*(R", you could define: .Sp .nf f %(%m=[pa]?|mail smith\en:f) .fi .Sp This checks the current mode (%m) and if it is \*(L'p\*(R' or \*(L'a\*(R' it expands it to the string \*(L"|mail smith\en\*(R", otherwise it returns the letter \*(L'f\*(R'. In some cases, you may simply wish to exclude the selector from a macro with the conditional \*(L"%m!=t\*(R". .PP Finally, you'll probably want to use the new options, .B \-x and .B \-X to ensure that all the newest features are available for use. These options might be on by default, depending on how your administrator decided to install .IR trn . .SH AUTHORS Rn was created by Larry Wall .br and is now under the direction of Stan Barber . .br Threaded version by Wayne Davison .br (Mail all bug reports for trn to Wayne.) .br Regular expression routines are borrowed from emacs, by James Gosling. .br Hashing routines are modified versions from Geoffrey Collyer. .SH FILES .Ip "%./.newsrc" 1.25i status of your news reading .Ip "%./.oldnewsrc" 1.25i backup copy of your .I .newsrc from start of session .Ip "%./.rnlock" 1.25i lock file so you don't screw up your .I .newsrc .Ip "%./.rnlast" 1.25i info from last run of trn .Ip "%./.rnsoft" 1.25i soft pointers into /usr/lib/news/active to speed startup, synchronous with .I .newsrc .Ip "%./.rnhead" 1.25i temporary header file to pass to a mailer or news poster .Ip "%./.[t]rnmac" 1.25i macro and keymap definitions .Ip "%p" 1.25i your news save directory, usually ~/News .Ip "%x/active" 1.25i the list of active newsgroups, usually /usr/lib/news/active on systems that don't use NNTP .Ip "%P" 1.25i the public news spool directory, usually /usr/spool/news on systems that don't use NNTP .Ip "%X/INIT" 1.25i system-wide default switches .SH SEE ALSO newsrc(5), more(1), readnews(1), Pnews(1), Rnmail(1) .SH DIAGNOSTICS Generally self-documenting, as they say. .SH BUGS The .B \-h switch can only hide header lines that .I trn knows about. .PP The \*(L'\-\*(R' command doesn't cross newsgroup boundaries, and only undoes the last article selection. .PP If you edit your .I .newsrc while .I trn is running, .I trn will happily wipe out your changes when it decides to write out the .I .newsrc file. .PP Marking of duplicate articles as read in cross-referenced newsgroups will not work unless the Xref patch is installed in inews. .PP If you get carried away with % or escape substitutions, you can overflow buffers. trn-4.0-test77/trn.c0000644000000000000000000005363607141206066012777 0ustar rootroot/* This software is copyrighted as detailed in the LICENSE file. */ /* trn -- threaded readnews program based on rn 4.4 * * You can request help from: trn-users@lists.sourceforge.net * Send bugs, suggestions, etc. to: trn-workers@lists.sourceforge.net * * Author/Maintainer of trn: trn@blorf.net (Wayne Davison) * Maintainer of rn: sob@bcm.tmc.edu (Stan Barber) * Original Author: lwall@sdcrdcf.UUCP (Larry Wall) * * History: * 01/14/83 - rn begun * 04/08/83 - rn 1.0 * 09/01/83 - rn 2.0 * 05/01/85 - rn 4.3 * 11/01/89 - rn/rrn integration * 11/25/89 - trn begun * 07/21/90 - trn 1.0 * 07/04/91 - rn 4.4 * 11/25/91 - trn 2.0 * 07/25/93 - trn 3.0 * ??/??/?? - trn 4.0 * * strn -- Scan(-mode)/Scoring TRN * * Author/Maintainer of strn: caadams@zynet.com (Clifford A. Adams) * * Strn history: * Dec. 90 - "Keyword RN" initial ideas, keyword entry prototype * 01/16/91 - "Scoring RN" initial design notes * Late 91 - cleaned up "Semicolon mode" RN patches from Todd Day * Early 92 - major additions to "STRN" * Mid 93 - first strn public release (version 0.8) * Sep. 94 - last beta release (version 0.9.3). * Late 95 - strn code ported to trn 4.0, universal selector started * May 96 - strn 1.0 release * */ #include "patchlevel.h" #include "INTERN.h" #include "common.h" #include "EXTERN.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "INTERN.h" #include "trn.h" #include "EXTERN.h" #include "term.h" #include "final.h" #include "search.h" #include "addng.h" #include "ngstuff.h" #include "rcstuff.h" #include "env.h" #include "util.h" #include "util2.h" #include "only.h" #include "ngsrch.h" #include "help.h" #include "last.h" #include "init.h" #include "intrp.h" #include "rcln.h" #include "sw.h" #include "opt.h" #include "cache.h" #include "ng.h" #include "kfile.h" #include "rt-select.h" #ifdef SCAN #include "scan.h" #endif /* SCAN */ #include "univ.h" void trn_init() { ; } static bool restore_old_newsrc = FALSE; static bool go_forward = TRUE; int main(argc,argv) int argc; char* argv[]; { bool foundany; char* s; /* Figure out our executable's name. */ #ifdef MSDOS strlwr(argv[0]); if ((s = rindex(argv[0],'\\')) != NULL) *s = '/'; #endif if ((s = rindex(argv[0],'/')) == NULL) s = argv[0]; else s++; #if !THREAD_INIT /* Default to threaded operation if our name starts with a 't' or 's'. */ if (*s == 't' || *s == 's') use_threads = TRUE; else UseNewsSelector = -1; #endif if (*s == 's') is_strn = TRUE; #ifdef USE_TK /*XXX remove this?*/ if (s[1] == 'k') UseTk = 1; else UseTk = 0; #endif foundany = initialize(argc,argv); if (UseNewsrcSelector) { multirc_selector(); finalize(0); } #ifdef FINDNEWNG if (find_new_groups()) { /* did we add any new groups? */ foundany = TRUE; starthere = NULL; /* start ng scan from the top */ } #endif if (maxngtodo) starthere = NULL; else if (!foundany) { /* nothing to do? */ #ifdef VERBOSE if (verbose) { fputs("\ No unread news in subscribed-to newsgroups. To subscribe to a new\n\ newsgroup use the g command.\n\ ",stdout) FLUSH; termdown(2); } #endif starthere = last_ng; } do_multirc(); finalize(0); /* NOT REACHED */ return 0; } void do_multirc() { bool special = FALSE; /* allow newsgroup with no unread news? */ char mode_save = mode; char gmode_save = gmode; if (UseUnivSelector) { char ch; univ_startup(); /* load startup file */ ch = universal_selector(); if (ch != 'Q') { /* section copied from bug_out below */ /* now write the newsrc(s) back out */ if (!write_newsrcs(multirc)) restore_old_newsrc = TRUE; /*$$ ask to retry! */ if (restore_old_newsrc) get_old_newsrcs(multirc); finalize(0); } } if (UseNewsgroupSelector) { ng_start_sel: switch (newsgroup_selector()) { case Ctl('n'): use_next_multirc(multirc); end_only(); goto ng_start_sel; case Ctl('p'): use_prev_multirc(multirc); end_only(); goto ng_start_sel; case 'q': goto bug_out; } starthere = ngptr; UseNewsgroupSelector = FALSE; } /* loop through all unread news */ restart: current_ng = first_ng; for (;;) { bool retry = FALSE; if (findlast > 0) { findlast = -1; starthere = NULL; if (lastngname) { if ((ngptr = find_ng(lastngname)) == NULL) ngptr = first_ng; else { set_ngname(lastngname); set_toread(ngptr, ST_LAX); if (ngptr->toread <= TR_NONE) ngptr = first_ng; } } } else if (starthere) { ngptr = starthere; starthere = NULL; } else ngptr = first_ng; for (;;) { /* for each newsgroup */ if (ngptr == NULL) { /* after the last newsgroup? */ set_mode('r','f'); if (maxngtodo) { if (retry) { #ifdef VERBOSE IF(verbose) printf("\nRestriction %s%s still in effect.\n", ngtodo[0], maxngtodo > 1 ? ", etc." : nullstr) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(\"Only\" mode.)\n",stdout) FLUSH; #endif termdown(2); } else { #ifdef VERBOSE IF(verbose) fputs("\nNo articles under restriction.", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNo \"only\" articles.",stdout) FLUSH; #endif termdown(2); end_only(); /* release the restriction */ printf("\n%s\n", msg) FLUSH; termdown(2); retry = TRUE; } } } else { bool shoe_fits; /* newsgroup matches restriction? */ set_mode('r','n'); if (ngptr->toread >= TR_NONE) { /* recalc toread? */ set_ngname(ngptr->rcline); shoe_fits = inlist(ngname); if (shoe_fits) set_toread(ngptr, ST_LAX); if (paranoid) { recent_ng = current_ng; current_ng = ngptr; cleanup_newsrc(ngptr->rc); /* this may move newsgroups around */ set_ng(current_ng); } } else shoe_fits = TRUE; if (ngptr->toread < (special? TR_NONE : ng_min_toread) || !shoe_fits) { /* unwanted newsgroup? */ if (go_forward) ngptr = ngptr->next; else { ngptr = ngptr->prev; if (ngptr == NULL) { ngptr = first_ng->next; go_forward = 1; } } continue; } } special = FALSE; /* go back to normal mode */ if (ngptr != current_ng) { recent_ng = current_ng; /* remember previous newsgroup */ current_ng = ngptr; /* remember current newsgroup */ } reask_newsgroup: unflush_output(); /* disable any ^O in effect */ if (ngptr == NULL) { dfltcmd = (retry ? "npq" : "qnp"); #ifdef VERBOSE IF(verbose) printf("\n****** End of newsgroups -- what next? [%s] ", dfltcmd); ELSE #endif #ifdef TERSE printf("\n**** End -- next? [%s] ", dfltcmd); #endif termdown(1); } else { ThreadedGroup = (use_threads && !(ngptr->flags&NF_UNTHREADED)); dfltcmd = (UseNewsSelector >= 0 && ngptr->toread >= (ART_UNREAD)UseNewsSelector? "+ynq":"ynq"); #ifdef VERBOSE IF(verbose) printf("\n%s %3ld unread article%s in %s -- read now? [%s] ", ThreadedGroup? "======" : "******", (long)ngptr->toread, PLURAL(ngptr->toread), ngname, dfltcmd); ELSE #endif #ifdef TERSE printf("\n%s %3ld in %s -- read? [%s] ", ThreadedGroup? "====" : "****", (long)ngptr->toread,ngname,dfltcmd); #endif termdown(1); } fflush(stdout); reinp_newsgroup: if (special || (ngptr && ngptr->toread > 0)) retry = TRUE; switch (input_newsgroup()) { case ING_ASK: goto reask_newsgroup; case ING_INPUT: case ING_ERASE: goto reinp_newsgroup; case ING_ERROR: printf("\n%s",hforhelp) FLUSH; termdown(2); settle_down(); goto reask_newsgroup; case ING_QUIT: goto bug_out; case ING_BREAK: goto loop_break; case ING_RESTART: goto restart; #ifdef SUPPORT_NNTP case ING_NOSERVER: if (multirc) goto restart; goto bug_out; #endif case ING_SPECIAL: special = TRUE; break; case ING_NORM: break; case ING_DISPLAY: newline(); break; case ING_MESSAGE: printf("\n%s\n", msg) FLUSH; termdown(2); break; } } loop_break:; #ifdef SUPPORT_NNTP check_active_refetch(FALSE); #endif } bug_out: /* now write the newsrc(s) back out */ if (!write_newsrcs(multirc)) restore_old_newsrc = TRUE; /*$$ ask to retry! */ if (restore_old_newsrc) get_old_newsrcs(multirc); set_mode(gmode_save,mode_save); } int input_newsgroup() { register char* s; ing_state = INGS_DIRTY; eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') { newline(); /* if return from stop signal */ return ING_ASK; } buf[2] = *buf; setdef(buf,dfltcmd); #ifdef VERIFY printcmd(); #endif if (ngptr != NULL) *buf = buf[2]; do_command: go_forward = TRUE; /* default to forward motion */ switch (*buf) { case 'P': /* goto previous newsgroup */ case 'p': /* find previous unread newsgroup */ if (!ngptr) ngptr = last_ng; else if (ngptr != first_ng) ngptr = ngptr->prev; go_forward = FALSE; /* go backward in the newsrc */ ing_state = INGS_CLEAN; if (*buf == 'P') return ING_SPECIAL; break; case '-': ngptr = recent_ng; /* recall previous newsgroup */ if (ngptr) { if (!get_ng(ngptr->rcline,0)) set_ng(current_ng); addnewbydefault = 0; } return ING_SPECIAL; case 'x': newline(); in_char("Confirm: exit and abandon newsrc changes?", 'A', "yn"); newline(); if (*buf != 'y') break; printf("\nThe abandoned changes are in %s.new.\n", multirc_name(multirc)) FLUSH; termdown(2); restore_old_newsrc = TRUE; return ING_QUIT; case 'q': case 'Q': /* quit? */ newline(); return ING_QUIT; case '^': if (gmode != 's') newline(); ngptr = first_ng; ing_state = INGS_CLEAN; break; case 'N': /* goto next newsgroup */ case 'n': /* find next unread newsgroup */ if (ngptr == NULL) { newline(); return ING_BREAK; } ngptr = ngptr->next; ing_state = INGS_CLEAN; if (*buf == 'N') return ING_SPECIAL; break; case '1': /* goto 1st newsgroup */ ngptr = first_ng; ing_state = INGS_CLEAN; return ING_SPECIAL; case '$': ngptr = NULL; /* go past last newsgroup */ ing_state = INGS_CLEAN; break; case 'L': list_newsgroups(); return ING_ASK; case '/': case '?': /* scan for newsgroup pattern */ #ifdef NGSEARCH switch (ng_search(buf,TRUE)) { case NGS_ERROR: set_ng(current_ng); return ING_ASK; case NGS_ABORT: set_ng(current_ng); return ING_INPUT; case NGS_INTR: #ifdef VERBOSE IF(verbose) fputs("\n(Interrupted)\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(Intr)\n",stdout) FLUSH; #endif termdown(2); set_ng(current_ng); return ING_ASK; case NGS_FOUND: return ING_SPECIAL; case NGS_NOTFOUND: #ifdef VERBOSE IF(verbose) fputs("\n\nNot found -- use a or g to add newsgroups\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\nNot found\n",stdout) FLUSH; #endif termdown(3); return ING_ASK; case NGS_DONE: return ING_ASK; } #else notincl("/"); #endif break; case 'm': #ifndef RELOCATE notincl("m"); break; #endif case 'g': /* goto named newsgroup */ if (!finish_command(FALSE)) return ING_INPUT; for (s = buf+1; *s == ' '; s++) ; /* skip leading spaces */ #ifdef RELOCATE if (!*s && *buf == 'm' && ngname && ngptr) strcpy(s,ngname); #endif { char* _s; for (_s=s; isdigit(*_s); _s++) ; if (*_s) /* found non-digit before hitting end */ set_ngname(s); else { int rcnum = atoi(s); for (ngptr = first_ng; ngptr; ngptr = ngptr->next) if (ngptr->num == rcnum) break; if (!ngptr) { ngptr = current_ng; printf("\nOnly %d groups. Try again.\n", newsgroup_cnt) FLUSH; termdown(2); return ING_ASK; } set_ngname(ngptr->rcline); } } /* try to find newsgroup */ #ifdef RELOCATE if (!get_ng(ngname,(*buf=='m'?GNG_RELOC:0) | GNG_FUZZY)) #else if (!get_ng(ngname,GNG_FUZZY)) #endif ngptr = current_ng; /* if not found, go nowhere */ addnewbydefault = 0; return ING_SPECIAL; #ifdef DEBUG case 'D': return ING_ASK; #endif case '!': /* shell escape */ if (escapade()) /* do command */ return ING_INPUT; return ING_ASK; case Ctl('k'): /* edit global KILL file */ edit_kfile(); return ING_ASK; case Ctl('n'): /* next newsrc list */ end_only(); use_next_multirc(multirc); goto display_multirc; case Ctl('p'): /* prev newsrc list */ end_only(); use_prev_multirc(multirc); display_multirc: { NEWSRC* rp; int len; for (rp = multirc->first, len = 0; rp && len < 66; rp = rp->next) { if (rp->flags & RF_ACTIVE) { sprintf(buf+len, ", %s", rp->datasrc->name); len += strlen(buf+len); } } if (rp) strcpy(buf+len, ", ..."); printf("\n\nUsing newsrc group #%d: %s.\n",multirc->num,buf+2); termdown(3); return ING_RESTART; } case 'c': /* catch up */ #ifdef CATCHUP if (ngptr) { ask_catchup(); if (ngptr->toread == TR_NONE) ngptr = ngptr->next; } #else notincl("c"); #endif break; case 't': if (!use_threads) printf("\n\nNot running in thread mode.\n"); else if (ngptr && ngptr->toread >= TR_NONE) { bool read_unthreaded = !(ngptr->flags&NF_UNTHREADED); ngptr->flags ^= NF_UNTHREADED; printf("\n\n%s will be read %sthreaded.\n", ngptr->rcline, read_unthreaded? "un" : nullstr) FLUSH; set_toread(ngptr, ST_LAX); } termdown(3); return ING_SPECIAL; case 'u': /* unsubscribe */ if (ngptr && ngptr->toread >= TR_NONE) {/* unsubscribable? */ newline(); printf(unsubto,ngptr->rcline) FLUSH; termdown(1); ngptr->subscribechar = NEGCHAR; /* unsubscribe it */ ngptr->toread = TR_UNSUB; /* and make line invisible */ ngptr->rc->flags |= RF_RCCHANGED; ngptr = ngptr->next; /* do an automatic 'n' */ newsgroup_toread--; } break; case 'h': univ_help(UHELP_NG); return ING_ASK; case 'H': /* help */ help_ng(); return ING_ASK; case 'A': if (!ngptr) break; reask_abandon: #ifdef VERBOSE IF(verbose) in_char("\nAbandon changes to current newsgroup?", 'B', "yn"); ELSE #endif #ifdef TERSE in_char("\nAbandon?", 'B', "ynh"); #endif #ifdef VERIFY printcmd(); #endif newline(); if (*buf == 'h') { #ifdef VERBOSE printf("Type y or SP to abandon the changes to this group since you started trn.\n"); printf("Type n to leave the group as it is.\n"); #else printf("y or SP to abandon changes to this group.\n"); printf("n to forget it.\n"); #endif termdown(2); goto reask_abandon; } else if (*buf != 'y' && *buf != 'n' && *buf != 'q') { fputs(hforhelp,stdout) FLUSH; termdown(1); settle_down(); goto reask_abandon; } else if (*buf == 'y') abandon_ng(ngptr); return ING_SPECIAL; case 'a': #ifndef FINDNEWNG notincl("a"); return ING_ASK; #else /* FALL THROUGH */ #endif case 'o': case 'O': { #ifdef FINDNEWNG bool doscan = (*buf == 'a'); #endif if (!finish_command(TRUE)) /* get rest of command */ return ING_INPUT; *msg = '\0'; end_only(); if (buf[1]) { bool minusd = instr(buf+1,"-d", TRUE) != NULL; sw_list(buf+1); if (minusd) cwd_check(); #ifdef FINDNEWNG if (doscan && maxngtodo) scanactive(TRUE); #endif ng_min_toread = *buf == empty_only_char && maxngtodo ? TR_NONE : TR_ONE; } ngptr = first_ng; /* simulate ^ */ if (*msg && !maxngtodo) return ING_MESSAGE; return ING_DISPLAY; } case '&': if (switcheroo()) /* get rest of command */ return ING_INPUT; /* if rubbed out, try something else */ return ING_ASK; case 'l': { /* list other newsgroups */ if (!finish_command(TRUE)) /* get rest of command */ return ING_INPUT; /* if rubbed out, try something else */ for (s = buf+1; *s == ' '; s++) ; /* skip leading spaces */ push_only(); if (*s) sw_list(s); page_start(); scanactive(FALSE); pop_only(); return ING_ASK; } case '`': case '\\': if (gmode == 's') return ING_ERASE; ng_start_sel: UseNewsgroupSelector = TRUE; switch (newsgroup_selector()) { case Ctl('n'): end_only(); use_next_multirc(multirc); goto ng_start_sel; case Ctl('p'): end_only(); use_prev_multirc(multirc); goto ng_start_sel; case 'q': return ING_QUIT; } UseNewsgroupSelector = FALSE; return ING_ASK; #ifdef SCAN_ART case ';': #endif case 'U': case '+': case '.': case '=': case 'y': case 'Y': case '\t': /* do normal thing */ case ' ': case '\r': case '\n': if (!ngptr) { fputs("\nNot on a newsgroup.",stdout) FLUSH; termdown(1); return ING_ASK; } /* CAA: *once*, the char* s was set to an illegal value * (it seemed to miss all the if statements below) * Just to be safe, make sure it is legal. */ s = nullstr; if (*buf == '.') { /* start command? */ if (!finish_command(FALSE)) /* get rest of command */ return ING_INPUT; s = savestr(buf+1); /* do_newsgroup will free it */ } else if (*buf == '+' || *buf == 'U' || *buf == '=' #ifdef SCAN_ART || *buf == ';' #endif ) { *buf = lastchar; /* restore 0200 if from a macro */ save_typeahead(buf+1, sizeof buf - 1); s = savestr(buf); } else if (*buf == ' ' || *buf == '\r' || *buf == '\n') s = nullstr; else s = NULL; redo_newsgroup: switch (do_newsgroup(s)) { case NG_NORM: case NG_NEXT: case NG_ERROR: ngptr = ngptr->next; break; case NG_ASK: return ING_ASK; case NG_SELPRIOR: *buf = 'p'; goto do_command; case NG_SELNEXT: *buf = 'n'; goto do_command; case NG_MINUS: ngptr = recent_ng; /* recall previous newsgroup */ return ING_SPECIAL; #ifdef SUPPORT_NNTP case NG_NOSERVER: nntp_server_died(ngptr->rc->datasrc); return ING_NOSERVER; #endif /* CAA extensions */ case NG_GO_ARTICLE: ngptr = ng_go_ngptr; s = savestr("y"); /* enter with minimal fuss */ goto redo_newsgroup; /* later: possible go-to-newsgroup */ } break; case ':': /* execute command on selected groups */ if (!ngsel_perform()) return ING_INPUT; page_line = 1; newline(); set_ng(current_ng); return ING_ASK; case 'v': newline(); trn_version(); return ING_ASK; default: if (*buf == ERASECH || *buf == KILLCH) return ING_ERASE; return ING_ERROR; } return ING_NORM; } #ifdef SUPPORT_NNTP void check_active_refetch(force) bool_int force; { DATASRC* dp; time_t now = time((time_t*)NULL); for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) { if (!ALLBITS(dp->flags, DF_OPEN | DF_ACTIVE)) continue; if (dp->act_sf.fp && dp->act_sf.refetch_secs && (force || now - dp->act_sf.lastfetch > dp->act_sf.refetch_secs)) actfile_hash(dp); } } #endif void trn_version() { page_start(); sprintf(msg,"Trn version:%s.\nConfigured for ",patchlevel); #ifdef SUPPORT_NNTP # ifdef HAS_LOCAL_SPOOL strcat(msg,"both NNTP and local news access.\n"); # else strcat(msg,"NNTP (plus individual local access).\n"); # endif #else strcat(msg,"local news access.\n"); #endif print_lines(msg, NOMARKING); if (multirc) { NEWSRC* rp; newline(); sprintf(msg,"News source group #%d:\n\n", multirc->num); print_lines(msg, NOMARKING); for (rp = multirc->first; rp; rp = rp->next) { if (!(rp->flags & RF_ACTIVE)) continue; sprintf(msg,"ID %s:\nNewsrc %s.\n",rp->datasrc->name,rp->name); print_lines(msg, NOMARKING); #ifdef SUPPORT_NNTP if (rp->datasrc->flags & DF_REMOTE) { sprintf(msg,"News from server %s.\n",rp->datasrc->newsid); print_lines(msg, NOMARKING); if (rp->datasrc->act_sf.fp) { if (rp->datasrc->flags & DF_TMPACTFILE) strcpy(msg,"Copy of remote active file"); else sprintf(msg,"Local active file: %s", rp->datasrc->extra_name); } else strcpy(msg,"Dynamic active file"); if (rp->datasrc->act_sf.refetch_secs) { char* cp = secs2text(rp->datasrc->act_sf.refetch_secs); if (*cp != 'n') sprintf(msg+strlen(msg), " (refetch%s %s)",*cp == 'm'? " if" : ":", cp); } strcat(msg,".\n"); } else #endif sprintf(msg,"News from %s.\nLocal active file %s.\n", rp->datasrc->spool_dir, rp->datasrc->newsid); print_lines(msg, NOMARKING); if (rp->datasrc->grpdesc) { #ifdef SUPPORT_NNTP if (!rp->datasrc->desc_sf.fp && rp->datasrc->desc_sf.hp) strcpy(msg,"Dynamic group desc. file"); else if (rp->datasrc->flags & DF_TMPGRPDESC) strcpy(msg,"Copy of remote group desc. file"); else #endif sprintf(msg,"Group desc. file: %s",rp->datasrc->grpdesc); #ifdef SUPPORT_NNTP if (rp->datasrc->desc_sf.refetch_secs) { char* cp = secs2text(rp->datasrc->desc_sf.refetch_secs); if (*cp != 'n') sprintf(msg+strlen(msg), " (refetch%s %s)",*cp == 'm'? " if" : ":", cp); } #endif strcat(msg,".\n"); print_lines(msg, NOMARKING); } if (rp->datasrc->flags & DF_TRY_OVERVIEW) { sprintf(msg,"Overview files from %s.\n", rp->datasrc->over_dir? rp->datasrc->over_dir : "the server"); print_lines(msg, NOMARKING); } if (rp->datasrc->flags & DF_TRY_THREAD) { sprintf(msg,"Thread files from %s.\n", rp->datasrc->thread_dir? rp->datasrc->thread_dir : "the server"); print_lines(msg, NOMARKING); } print_lines("\n", NOMARKING); } } print_lines("\ You can request help from: trn-users@lists.sourceforge.net\n\ Send bug reports, suggestions, etc. to: trn-workers@lists.sourceforge.net\n", NOMARKING); } void set_ngname(what) char* what; { if (ngname != what) { ngname_len = strlen(what); growstr(&ngname,&ngnlen,ngname_len+1); strcpy(ngname,what); } growstr(&ngdir,&ngdlen,ngname_len+1); strcpy(ngdir,getngdir(ngname)); } static char* myngdir; static int ngdirlen = 0; char* getngdir(ngnam) char* ngnam; { register char* s; growstr(&myngdir,&ngdirlen,strlen(ngnam)+1); strcpy(myngdir,ngnam); for (s = myngdir; *s; s++) if (*s == '.') *s = '/'; return myngdir; } trn-4.0-test77/trn.h0000644000000000000000000000247007113133016012763 0ustar rootroot/* trn.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT char* ngname INIT(NULL); /* name of current newsgroup */ EXT int ngnlen INIT(0); /* current malloced size of ngname */ EXT int ngname_len; /* length of current ngname */ EXT char* ngdir INIT(NULL); /* same thing in directory name form */ EXT int ngdlen INIT(0); /* current malloced size of ngdir */ #define ING_NORM 0 #define ING_ASK 1 #define ING_INPUT 2 #define ING_ERASE 3 #define ING_QUIT 4 #define ING_ERROR 5 #define ING_SPECIAL 6 #define ING_BREAK 7 #define ING_RESTART 8 #define ING_NOSERVER 9 #define ING_DISPLAY 10 #define ING_MESSAGE 11 EXT int ing_state; #define INGS_CLEAN 0 #define INGS_DIRTY 1 EXT bool write_less INIT(FALSE); /* write .newsrc less often */ EXT char* auto_start_cmd INIT(NULL); /* command to auto-start with */ EXT bool auto_started INIT(FALSE); /* have we auto-started? */ EXT bool is_strn INIT(FALSE); /* Is this "strn", or trn/rn? */ EXT char patchlevel[] INIT(PATCHLEVEL); /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void trn_init _((void)); int main _((int,char**)); void do_multirc _((void)); int input_newsgroup _((void)); #ifdef SUPPORT_NNTP void check_active_refetch _((bool_int)); #endif void trn_version _((void)); void set_ngname _((char*)); char* getngdir _((char*)); trn-4.0-test77/typedef.h0000644000000000000000000000463407113133016013624 0ustar rootroot/* typedef.h */ /* some important types */ typedef int NG_NUM; /* newsgroup number */ typedef long ART_NUM; /* article number */ typedef long ART_UNREAD; /* could be short to save space */ typedef long ART_POS; /* char position in article file */ typedef int ART_LINE; /* line position in article file */ typedef long ACT_POS; /* char position in active file */ typedef unsigned int MEM_SIZE; /* for passing to malloc */ typedef unsigned char Uchar; /* more space-efficient */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ /* addng.h */ typedef struct addgroup ADDGROUP; /* cache.h */ typedef struct subject SUBJECT; typedef struct article ARTICLE; /* color.ih */ typedef struct color_obj COLOR_OBJ; /* datasrc.h */ typedef struct srcfile SRCFILE; typedef struct datasrc DATASRC; /* hash.h */ typedef struct hashdatum HASHDATUM; /* hash.ih */ typedef struct hashent HASHENT; typedef struct hashtable HASHTABLE; /* head.h */ typedef struct headtype HEADTYPE; typedef struct user_headtype USER_HEADTYPE; /* list.h */ typedef struct listnode LISTNODE; typedef struct list LIST; /* mime.h */ typedef struct hblk HBLK; typedef struct mime_sect MIME_SECT; typedef struct html_tags HTML_TAGS; typedef struct mimecap_entry MIMECAP_ENTRY; /* ngdata.h */ typedef struct ngdata NGDATA; /* nntpclient.h */ typedef struct nntplink NNTPLINK; /* rcstuff.h */ typedef struct newsrc NEWSRC; typedef struct multirc MULTIRC; /* rt-mt.ih */ typedef struct packed_root PACKED_ROOT; typedef struct packed_article PACKED_ARTICLE; typedef struct total TOTAL; typedef struct bmap BMAP; /* rt-page.h */ typedef union sel_union SEL_UNION; typedef struct sel_item SEL_ITEM; /* scan.h */ typedef struct page_ent PAGE_ENT; typedef struct scontext SCONTEXT; /* scanart.h */ typedef struct sa_entrydata SA_ENTRYDATA; /* scorefile.h */ typedef struct sf_entry SF_ENTRY; typedef struct sf_file SF_FILE; /* search.h */ typedef struct compex COMPEX; /* term.ih */ typedef struct keymap KEYMAP; /* univ.h */ typedef struct univ_groupmask_data UNIV_GROUPMASK_DATA; typedef struct univ_configfile_data UNIV_CONFIGFILE_DATA; typedef struct univ_virt_data UNIV_VIRT_DATA; typedef struct univ_virt_group UNIV_VIRT_GROUP; typedef struct univ_newsgroup UNIV_NEWSGROUP; typedef struct univ_textfile UNIV_TEXTFILE; typedef union univ_data UNIV_DATA; typedef struct univ_item UNIV_ITEM; /* util.h */ typedef struct ini_words INI_WORDS; trn-4.0-test77/univ.c0000644000000000000000000007044007113133016013136 0ustar rootroot/* univ.c */ /* Universal selector * */ #include "EXTERN.h" #include "common.h" #include "env.h" #include "hash.h" #include "list.h" #include "ngdata.h" #include "util.h" #include "util2.h" #include "url.h" #include "term.h" #include "rcstuff.h" #include "ng.h" #include "cache.h" #include "head.h" #include "trn.h" #include "rt-select.h" #include "rt-util.h" #include "final.h" #include "help.h" #ifdef SCORE #include "score.h" #endif #include "INTERN.h" #include "univ.h" #include "univ.ih" /* TODO: * * Be friendlier when a file has no contents. * Implement virtual groups (largely done) * Help scan mode replacement * Lots more to do... */ static bool univ_virt_pass_needed INIT(FALSE); static int univ_item_counter INIT(1); static bool univ_done_startup INIT(FALSE); /* this score is part of the line format, so it is not ifdefed */ static int univ_min_score INIT(0); static bool univ_use_min_score INIT(FALSE); void univ_init() { univ_level = 0; univ_ever_init = 1; } void univ_startup() { bool sys_top_load; bool user_top_load; sys_top_load = FALSE; user_top_load = FALSE; /* later: make user top file an option or environment variable? */ if (!univ_file_load("%+/univ/top","Top Level",(char*)NULL)) { univ_open(); univ_title = savestr("Top Level"); univ_fname = savestr("%+/univ/usertop"); /* read in trn default top file */ (void)univ_include_file("%X/sitetop"); /* pure local */ sys_top_load = univ_include_file("%X/trn4top"); user_top_load = univ_use_file("%+/univ/usertop", univ_title, NULL); if (!(sys_top_load || user_top_load)) { /* last resort--all newsgroups */ univ_close(); univ_mask_load(savestr("*"),"All Newsgroups"); } if (user_top_load) { univ_usrtop = TRUE; } } else { univ_usrtop = TRUE; } univ_done_startup = TRUE; } void univ_open() { first_univ = last_univ = 0; sel_page_univ = sel_next_univ = 0; univ_fname = univ_title = univ_label = univ_tmp_file = NULL; univ_virt_pass_needed = FALSE; univ_ng_hash = univ_vg_hash = 0; univ_level++; } void univ_close() { UNIV_ITEM* node; UNIV_ITEM* nextnode; for (node = first_univ; node; node = nextnode) { univ_free_data(node); safefree(node->desc); nextnode = node->next; free((char*)node); } if (univ_tmp_file) { UNLINK(univ_tmp_file); free(univ_tmp_file); } safefree(univ_fname); safefree(univ_title); safefree(univ_label); if (univ_ng_hash) { hashdestroy(univ_ng_hash); univ_ng_hash = 0; } if (univ_vg_hash) { hashdestroy(univ_vg_hash); univ_vg_hash = 0; } first_univ = last_univ = 0; sel_page_univ = sel_next_univ = 0; univ_level--; } UNIV_ITEM* univ_add(type, desc) int type; char* desc; { UNIV_ITEM* node = first_univ; node = (UNIV_ITEM*)safemalloc(sizeof (UNIV_ITEM)); node->flags = 0; if (desc) node->desc = savestr(desc); else node->desc = NULL; node->type = type; node->num = univ_item_counter++; #ifdef SCORE node->score = 0; /* consider other default scores? */ #endif node->next = NULL; node->prev = last_univ; if (last_univ) last_univ->next = node; else first_univ = node; last_univ = node; return node; } static void univ_free_data(ui) UNIV_ITEM* ui; { if (!ui) return; switch (ui->type) { case UN_NONE: /* cases where nothing is needed. */ case UN_TXT: case UN_HELPKEY: break; case UN_DEBUG1: /* methods that use the string */ safefree(ui->data.str); break; case UN_GROUPMASK: /* methods that have custom data */ safefree(ui->data.gmask.title); safefree(ui->data.gmask.masklist); break; case UN_CONFIGFILE: safefree(ui->data.cfile.title); safefree(ui->data.cfile.fname); safefree(ui->data.cfile.label); break; case UN_NEWSGROUP: case UN_GROUP_DESEL: safefree(ui->data.group.ng); break; case UN_ARTICLE: safefree(ui->data.virt.ng); safefree(ui->data.virt.from); safefree(ui->data.virt.subj); break; case UN_VGROUP: case UN_VGROUP_DESEL: safefree(ui->data.vgroup.ng); break; case UN_TEXTFILE: safefree(ui->data.textfile.fname); break; case UN_DATASRC: /* unimplemented methods */ case UN_VIRTUAL1: default: break; } } /* not used now, but may be used later... */ int univ_lines(ui) UNIV_ITEM* ui; { /* later use the type and all that jazz... */ return 1; } /* not used now, but may be used later... */ char* univ_desc_line(ui,linenum) UNIV_ITEM* ui; /* universal item */ int linenum; /* which line to describe (0 base) */ { return ui->desc; } void univ_add_text(txt) char* txt; { /* later check text for bad things */ (void)univ_add(UN_TXT,txt); } /* temp for testing */ void univ_add_debug(desc,txt) char* desc; char* txt; { UNIV_ITEM* ui; /* later check text for bad things */ ui = univ_add(UN_DEBUG1,desc); ui->data.str = savestr(txt); } void univ_add_group(desc,grpname) char* desc; char* grpname; { UNIV_ITEM* ui; char* s; HASHDATUM data; s = grpname; if (!s) return; /* later check grpname for bad things? */ if (!univ_ng_hash) univ_ng_hash = hashcreate(701, HASH_DEFCMPFUNC); data = hashfetch(univ_ng_hash,grpname,strlen(grpname)); if (data.dat_ptr) { /* group was already added */ /* perhaps it is marked as deleted? */ for (ui = first_univ; ui; ui = ui->next) { if ((ui->type == UN_GROUP_DESEL) && ui->data.group.ng && strEQ(ui->data.group.ng,grpname)) { /* undelete the newsgroup */ ui->type = UN_NEWSGROUP; } } return; } ui = univ_add(UN_NEWSGROUP,desc); ui->data.group.ng = savestr(grpname); data.dat_ptr = ui->data.group.ng; hashstorelast(data); } void univ_add_mask(desc,mask) char* desc; char* mask; { UNIV_ITEM* ui; ui = univ_add(UN_GROUPMASK,desc); ui->data.gmask.masklist = savestr(mask); ui->data.gmask.title = savestr(desc); } void univ_add_file(desc,fname,label) char* desc; char* fname; /* May be URL */ char* label; { UNIV_ITEM* ui; ui = univ_add(UN_CONFIGFILE,desc); ui->data.cfile.title = savestr(desc); ui->data.cfile.fname = savestr(fname); if (label && *label) ui->data.cfile.label = savestr(label); else ui->data.cfile.label = NULL; } UNIV_ITEM* univ_add_virt_num(desc,grp,art) char* desc; char* grp; ART_NUM art; { UNIV_ITEM* ui; ui = univ_add(UN_ARTICLE,desc); ui->data.virt.ng = savestr(grp); ui->data.virt.num = art; ui->data.virt.subj = NULL; ui->data.virt.from = NULL; return ui; } void univ_add_textfile(desc,name) char* desc; char* name; { UNIV_ITEM* ui; char* s; char* p; static char lbuf[1024]; s = name; switch (*s) { /* later add URL handling */ case ':': s++; default: /* XXX later have error checking on length */ strcpy(lbuf,univ_fname); for (p = lbuf+strlen(lbuf); p > lbuf && *p != '/'; p--) ; if (p) { *p++ = '/'; *p = '\0'; strcat(lbuf,s); s = lbuf; } /* FALL THROUGH */ case '~': /* ...or full file names */ case '%': case '/': ui = univ_add(UN_TEXTFILE,desc); ui->data.textfile.fname = savestr(filexp(s)); break; } } /* mostly the same as the newsgroup stuff */ void univ_add_virtgroup(grpname) char* grpname; { UNIV_ITEM* ui; char* s; HASHDATUM data; s = grpname; if (!s) return; /* later check grpname for bad things? */ /* perhaps leave if group has no unread, or other factor */ if (!univ_vg_hash) univ_vg_hash = hashcreate(701, HASH_DEFCMPFUNC); univ_virt_pass_needed = TRUE; data = hashfetch(univ_vg_hash,grpname,strlen(grpname)); if (data.dat_ptr) { /* group was already added */ /* perhaps it is marked as deleted? */ for (ui = first_univ; ui; ui = ui->next) { if ((ui->type == UN_VGROUP_DESEL) && ui->data.vgroup.ng && strEQ(ui->data.vgroup.ng,grpname)) { /* undelete the newsgroup */ ui->type = UN_VGROUP; } } return; } ui = univ_add(UN_VGROUP,NULL); ui->data.vgroup.flags = (char)0; #ifdef SCORE if (univ_use_min_score) { ui->data.vgroup.flags |= UF_VG_MINSCORE; ui->data.vgroup.minscore = univ_min_score; } #endif ui->data.vgroup.ng = savestr(grpname); data.dat_ptr = ui->data.vgroup.ng; hashstorelast(data); } static bool univ_begin_found INIT(FALSE); /* label to start working with */ static char* univ_begin_label INIT(NULL); /* univ_DoMatch uses a modified Wildmat function which is * based on Rich $alz's wildmat, reduced to the simple case of * * and text. The complete version can be found in wildmat.c. */ /* an improbable number */ #define ABORT -42 /* ** Match text and p, return TRUE, FALSE, or ABORT. */ static int univ_DoMatch(text, p) register char* text; register char* p; { register int matched; for ( ; *p; text++, p++) { if (*p == '*') { while (*++p == '*') /* Consecutive stars act just like one. */ continue; if (*p == '\0') /* Trailing star matches everything. */ return TRUE; while (*text) if ((matched = univ_DoMatch(text++, p)) != FALSE) return matched; return ABORT; } if (*text != *p) { if (*text == '\0') return ABORT; return FALSE; } } return *text == '\0'; } /* type: 0=newsgroup, 1=virtual (more in future?) */ void univ_use_pattern(pattern,type) char* pattern; int type; { char* s = pattern; NGDATA* np; UNIV_ITEM* ui; if (!s || !*s) { printf("\ngroup pattern: empty regular expression\n") FLUSH; return; } /* XXX later: match all newsgroups in current datasrc to the pattern. */ /* XXX later do a quick check to see if the group is a simple one. */ if (*s == '!') { s++; switch (type) { case 0: for (ui = first_univ; ui; ui = ui->next) { if (ui->type == UN_NEWSGROUP && ui->data.group.ng && univ_DoMatch(ui->data.group.ng,s) == TRUE) { ui->type = UN_GROUP_DESEL; } } break; case 1: for (ui = first_univ; ui; ui = ui->next) { if (ui->type == UN_VGROUP && ui->data.vgroup.ng && univ_DoMatch(ui->data.vgroup.ng,s) == TRUE) { ui->type = UN_VGROUP_DESEL; } } break; } } else { switch (type) { case 0: for (np = first_ng; np; np = np->next) { if (univ_DoMatch(np->rcline,s) == TRUE) { univ_add_group(np->rcline,np->rcline); } } break; case 1: for (np = first_ng; np; np = np->next) { if (univ_DoMatch(np->rcline,s) == TRUE) { univ_add_virtgroup(np->rcline); } } break; } } } /* interprets a line of newsgroups, adding or subtracting each pattern */ /* Newsgroup patterns are separated by spaces and/or commas */ void univ_use_group_line(line,type) char* line; int type; { char* s; char* p; char ch; s = line; if (!s || !*s) return; /* newsgroup patterns will be separated by space(s) and/or comma(s) */ while (*s) { while (*s == ' ' || *s == ',') s++; for (p = s; *p && *p != ' ' && *p != ','; p++) ; ch = *p; *p = '\0'; univ_use_pattern(s,type); *p = ch; s = p; } } /* returns TRUE on success, FALSE otherwise */ static bool univ_use_file(fname,title,label) char* fname; char* title; char* label; { static char lbuf[LBUFLEN]; FILE* fp; char* s; char* p; char* open_name; bool save_temp; bool begin_top; /* if FALSE, look for "begin group" before interpreting */ save_temp = FALSE; begin_top = TRUE; /* default assumption (might be changed later) */ p = NULL; if (!fname) return FALSE; /* bad argument */ s = fname; open_name = s; /* open URLs and translate them into local temporary filenames */ if (strncaseEQ(fname,"URL:",4)) { #ifdef USEURL s = fname; open_name = temp_filename(); univ_tmp_file = open_name; if (!url_get(fname+4,open_name)) open_name = NULL; save_temp = TRUE; begin_top = FALSE; /* we will need a "begin group" */ #else /* !USEURL */ printf("This copy of trn does not have URL support.\n") FLUSH; open_name = NULL; #endif /* USEURL */ } else if (*s == ':') { /* relative to last file's directory */ printf("Colon filespec not supported for |%s|\n",s) FLUSH; open_name = NULL; } if (!open_name) return FALSE; univ_begin_found = begin_top; safefree0(univ_begin_label); if (label) univ_begin_label = savestr(label); fp = fopen(filexp(open_name),"r"); if (!fp) return FALSE; /* unsuccessful (XXX: complain) */ /* Later considerations: * 1. Long lines * 2. Backslash continuations */ while ((s = fgets(lbuf,sizeof lbuf,fp)) != NULL) { if (!s) /* end of file */ break; if (!univ_do_line(s)) break; /* end of useful file */ } fclose(fp); if (!univ_begin_found) printf("\"begin group\" not found.\n") FLUSH; if (univ_begin_label) printf("label not found: %s\n",univ_begin_label); if (univ_virt_pass_needed) { univ_virt_pass(); } sort_univ(); return TRUE; } static bool univ_include_file(fname) char* fname; { char* old_univ_fname; bool retval; old_univ_fname = univ_fname; univ_fname = savestr(fname); /* LEAK */ retval = univ_use_file(univ_fname,univ_title,NULL); univ_fname = old_univ_fname; return retval; } /* do the '$' extensions of the line. */ static void univ_do_line_ext1(desc,line) char* desc; char* line; /* may be temporarily edited */ { char* s; char* p; char* q; ART_NUM a; char ch; s = line; s++; switch (*s) { case 'v': s++; switch (*s) { case '0': /* test vector: "desc" $v0 */ s++; (void)univ_add_virt_num(desc? desc : s, "news.software.readers",(ART_NUM)15000); break; case '1': /* "desc" $v1 1500 news.admin */ /* XXX error checking */ s++; while (isspace(*s)) s++; p = s; while (isdigit(*p)) p++; ch = *p; *p = '\0'; a = (ART_NUM)atoi(s); *p = ch; if (*p) { p++; (void)univ_add_virt_num(desc ? desc : s, p, a); } break; case 'g': /* $vg [scorenum] news.* !news.foo.* */ p = s; p++; while (isspace(*p)) p++; q = p; if ((*p=='+') || (*p=='-')) p++; while (isdigit(*p)) p++; if (isspace(*p)) { *p = '\0'; univ_min_score = atoi(q); univ_use_min_score = TRUE; s = p; s++; } univ_use_group_line(s,1); univ_use_min_score = FALSE; break; } break; case 't': /* text file */ s++; switch (*s) { case '0': /* test vector: "desc" $t0 */ univ_add_textfile(desc? desc : s, "/home/c/caadams/ztext"); break; } break; default: break; } } /* if non-NULL, the description (printing name) of the entry */ static char* univ_line_desc; /* returns FALSE when no more lines should be interpreted */ static bool univ_do_line(line) char* line; { char* s; char* p; s = line + strlen(line)-1; if (*s == '\n') *s = '\0'; /* delete newline */ s = line; while (isspace(*s)) s++; if (*s == '\0') return TRUE; /* empty line */ if (!univ_begin_found) { if (strncaseNE(s,"begin group",11)) return TRUE; /* wait until "begin group" is found */ univ_begin_found = TRUE; } if (univ_begin_label) { if (*s == '>' && s[1] == ':' && strEQ(s+2,univ_begin_label)) { safefree0(univ_begin_label); /* interpret starting at next line */ } return TRUE; } safefree0(univ_line_desc); if (*s == '"') { /* description name */ p = cpytill(s,s+1,'"'); if (!*p) { printf("univ: unmatched quote in string:\n\"%s\"\n", s) FLUSH; return TRUE; } *p = '\0'; univ_line_desc = savestr(s); s = p+1; } while (isspace(*s)) s++; if (strncaseEQ(s,"end group",9)) return FALSE; if (strncaseEQ(s,"URL:",4)) { for (p = s; *p && *p != '>'; p++) ; if (*p) { p++; if (!*p) /* empty label */ p = NULL; /* XXX later do more error checking */ } else p = NULL; /* description defaults to name */ univ_add_file(univ_line_desc? univ_line_desc : s, s, p); } else { switch (*s) { case '#': /* comment */ break; case ':': /* relative to univ_fname */ /* XXX hack the variable and fall through */ if (univ_fname && strlen(univ_fname)+strlen(s) < 1020) { static char lbuf[1024]; strcpy(lbuf,univ_fname); for (p = lbuf+strlen(lbuf); p > lbuf && *p != '/'; p--) ; if (p) { *p++ = '/'; *p = '\0'; s++; strcat(lbuf,s); s = lbuf; } } /* XXX later have else which will complain */ /* FALL THROUGH */ case '~': /* ...or full file names */ case '%': case '/': for (p = s; *p && *p != '>'; p++) ; if (*p) { if (strlen(s) < 1020) { static char lbuf[1024]; strcpy(lbuf,s); s = lbuf; for (p = s; *p && *p != '>'; p++) ; /* XXX Ick! */ *p++ = '\0'; /* separate label */ if (!*p) /* empty label */ p = NULL; /* XXX later do more error checking */ } } else p = NULL; /* description defaults to name */ univ_add_file(univ_line_desc? univ_line_desc : s, filexp(s), p); break; case '-': /* label within same file */ s++; if (*s++ != '>') { /* XXX give an error message later */ break; } if (univ_tmp_file) p = univ_tmp_file; else p = univ_fname; univ_add_file(univ_line_desc? univ_line_desc : s, univ_fname, s); break; case '>': if (s[1] == ':') return FALSE; /* label found, end of previous block */ break; /* just ignore the line (print warning later?) */ case '@': /* virtual newsgroup file */ break; /* not used now */ case '&': /* text file shortcut (for help files) */ s++; univ_add_textfile(univ_line_desc? univ_line_desc : s, s); break; case '$': /* extension 1 */ univ_do_line_ext1(univ_line_desc,s); break; default: /* if there is a description, this must be a restriction list */ if (univ_line_desc) { univ_add_mask(univ_line_desc,s); break; } /* one or more newsgroups instead */ univ_use_group_line(s,0); break; } } return TRUE; /* continue reading */ } /* features to return later (?): * text files */ /* level generator */ bool univ_file_load(fname,title,label) char* fname; char* title; char* label; { bool flag; univ_open(); if (fname) univ_fname = savestr(fname); if (title) univ_title = savestr(title); if (label) univ_label = savestr(label); flag = univ_use_file(fname,title,label); if (!flag) { univ_close(); } if (int_count) { int_count = 0; } if (finput_pending(TRUE)) { ; /* later, *maybe* eat input */ } return flag; } /* level generator */ void univ_mask_load(mask,title) char* mask; char* title; { univ_open(); univ_use_group_line(mask,0); if (title) univ_title = savestr(title); if (int_count) { int_count = 0; } } void univ_redofile() { char* tmp_fname; char* tmp_title; char* tmp_label; tmp_fname = (univ_fname ? savestr(univ_fname) : 0); tmp_title = (univ_title ? savestr(univ_title) : 0); tmp_label = (univ_label ? savestr(univ_label) : 0); univ_close(); if (univ_level) (void)univ_file_load(tmp_fname,tmp_title,tmp_label); else univ_startup(); safefree(tmp_fname); safefree(tmp_title); safefree(tmp_label); } static char* univ_edit_new_userfile() { char* s; FILE* fp; s = savestr(filexp("%+/univ/usertop")); /* LEAK */ /* later, create a new user top file, and return its filename. * later perhaps ask whether to create or edit current file. * note: the user may need to restart in order to use the new file. * (trn could do a univ_redofile, but it may be confusing.) */ /* if the file exists, do not create a new one */ fp = fopen(s,"r"); if (fp) { fclose(fp); return univ_fname; /* as if this function was not called */ } makedir(s,MD_FILE); fp = fopen(s,"w"); if (!fp) { printf("Could not create new user file.\n"); printf("Editing current system file\n") FLUSH; (void)get_anything(); return univ_fname; } fprintf(fp,"# User Toplevel (Universal Selector)\n"); fclose(fp); printf("New User Toplevel file created.\n") FLUSH; printf("After editing this file, exit and restart trn to use it.\n") FLUSH; (void)get_anything(); univ_usrtop = TRUE; /* do not overwrite this file */ return s; } /* code adapted from edit_kfile in kfile.c */ /* XXX problem if elements expand to larger than cmd_buf */ void univ_edit() { char* s; if (univ_usrtop || !(univ_done_startup)) { if (univ_tmp_file) { s = univ_tmp_file; } else { s = univ_fname; } } else { s = univ_edit_new_userfile(); } /* later consider directory push/pop pair around editing */ (void)edit_file(s); } /* later use some internal pager */ void univ_page_file(fname) char* fname; { if (!fname || !*fname) return; sprintf(cmd_buf,"%s ", filexp(getval("HELPPAGER",getval("PAGER","more")))); strcat(cmd_buf, filexp(fname)); termdown(3); resetty(); /* make sure tty is friendly */ doshell(sh,cmd_buf); /* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ /* later: consider something else that will return the key, and * returning different codes based on the key. */ if (strnEQ(cmd_buf,"more ",5)) get_anything(); } static UNIV_ITEM* current_vg_ui; /* virtual newsgroup second pass function */ /* called from within newsgroup */ void univ_ng_virtual() { switch (current_vg_ui->type) { case UN_VGROUP: univ_vg_addgroup(); break; case UN_ARTICLE: /* get article number from message-id */ break; default: break; } /* later, get subjects and article numbers when needed */ /* also do things like check scores, add newsgroups, etc. */ } static void univ_vg_addart(a) ART_NUM a; { char* subj; char* from; char lbuf[70]; UNIV_ITEM* ui; int score; #ifdef SCORE score = sc_score_art(a,FALSE); if (univ_use_min_score && (scorescore = score; #endif ui->data.virt.subj = savestr(subj); ui->data.virt.from = savestr(from); } static void univ_vg_addgroup() { ART_NUM a; /* later: allow was-read articles, etc... */ for (a = article_first(firstart); a <= lastart; a = article_next(a)) { if (!article_unread(a)) continue; /* minimum score check */ univ_vg_addart(a); } } /* returns do_newsgroup() value */ int univ_visit_group_main(gname) char* gname; { int ret; NGDATA* np; bool old_threaded; if (!gname || !*gname) return NG_ERROR; np = find_ng(gname); if (!np) { printf("Univ/Virt: newsgroup %s not found!", gname) FLUSH; return NG_ERROR; } /* unsubscribed, bogus, etc. groups are not visited */ if (np->toread <= TR_UNSUB) return NG_ERROR; set_ng(np); if (np != current_ng) { /* probably unnecessary... */ recent_ng = current_ng; current_ng = np; } old_threaded = ThreadedGroup; ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("\nScanning newsgroup %s\n",gname); ret = do_newsgroup(nullstr); ThreadedGroup = old_threaded; return ret; } /* LATER: allow the loop to be interrupted */ void univ_virt_pass() { UNIV_ITEM* ui; univ_ng_virtflag = TRUE; univ_virt_pass_needed = FALSE; for (ui = first_univ; ui; ui = ui->next) { if (input_pending()) { /* later consider cleaning up the remains */ break; } switch (ui->type) { case UN_VGROUP: if (!ui->data.vgroup.ng) break; /* XXX whine */ current_vg_ui = ui; #ifdef SCORE if (ui->data.vgroup.flags & UF_VG_MINSCORE) { univ_use_min_score = TRUE; univ_min_score = ui->data.vgroup.minscore; } #endif (void)univ_visit_group(ui->data.vgroup.ng); univ_use_min_score = FALSE; /* later do something with return value */ univ_free_data(ui); safefree(ui->desc); ui->type = UN_DELETED; break; case UN_ARTICLE: /* if article number is not set, visit newsgroup with callback */ /* later also check for descriptions */ if ((ui->data.virt.num) && (ui->desc)) break; if (ui->data.virt.subj) break; current_vg_ui = ui; (void)univ_visit_group(ui->data.virt.ng); /* later do something with return value */ break; default: break; } } univ_ng_virtflag = FALSE; } static int univ_order_number(ui1, ui2) register UNIV_ITEM** ui1; register UNIV_ITEM** ui2; { return (int)((*ui1)->num - (*ui2)->num) * sel_direction; } #ifdef SCORE static int univ_order_score(ui1, ui2) register UNIV_ITEM** ui1; register UNIV_ITEM** ui2; { if ((*ui1)->score != (*ui2)->score) return (int)((*ui2)->score - (*ui1)->score) * sel_direction; else return (int)((*ui1)->num - (*ui2)->num) * sel_direction; } #endif void sort_univ() { int cnt,i; UNIV_ITEM* ui; UNIV_ITEM** lp; UNIV_ITEM** univ_sort_list; int (*sort_procedure)(); cnt = 0; for (ui = first_univ; ui; ui = ui->next) { cnt++; } if (cnt<=1) return; switch (sel_sort) { #ifdef SCORE case SS_SCORE: sort_procedure = univ_order_score; break; #endif case SS_NATURAL: default: sort_procedure = univ_order_number; break; } univ_sort_list = (UNIV_ITEM**)safemalloc(cnt*sizeof(UNIV_ITEM*)); for (lp = univ_sort_list, ui = first_univ; ui; ui = ui->next) *lp++ = ui; assert(lp - univ_sort_list == cnt); qsort(univ_sort_list, cnt, sizeof (UNIV_ITEM*), sort_procedure); first_univ = ui = univ_sort_list[0]; for (i = cnt, lp = univ_sort_list; --i; lp++) { lp[0]->next = lp[1]; lp[1]->prev = lp[0]; } last_univ = lp[0]; last_univ->next = NULL; free((char*)univ_sort_list); } /* return a description of the article */ /* do this better later, like the code in sadesc.c */ char* univ_article_desc(ui) UNIV_ITEM* ui; { char* s; char* f; static char dbuf[200]; static char sbuf[200]; static char fbuf[200]; s = ui->data.virt.subj; f = ui->data.virt.from; if (!f) { strcpy(fbuf," "); } else { safecpy(fbuf,compress_from(f,16),17); } if (!s) { strcpy(sbuf,""); } else { if ((s[0] == 'R') && (s[1] == 'e') && (s[2] == ':') && (s[3] == ' ')) { sbuf[0] = '>'; safecpy(sbuf+1,s+4,79); } else { safecpy(sbuf,s,80); } } fbuf[16] = '\0'; sbuf[55] = '\0'; #ifdef SCORE sprintf(dbuf,"[%3d] %16s %s",ui->score,fbuf,sbuf); #else sprintf(dbuf,"%16s %55s",fbuf,sbuf); #endif for (s = dbuf; *s; s++) { if ((*s==Ctl('h')) || (*s=='\t') || (*s=='\n') || (*s=='\r')) { *s = ' '; } } dbuf[70] = '\0'; return dbuf; } /* Help start */ /* later: add online help as a new item type, add appropriate item * to the new level */ void univ_help_main(where) int where; /* what context were we in--use later for key help? */ { UNIV_ITEM *ui; bool flag; univ_open(); univ_title = savestr("Extended Help"); /* first add help on current mode */ ui = univ_add(UN_HELPKEY, NULL); ui->data.i = where; /* later, do other mode sensitive stuff */ /* site-specific help */ univ_include_file("%X/sitehelp/top"); /* read in main help file */ univ_fname = savestr("%X/HelpFiles/top"); flag = univ_use_file(univ_fname,univ_title,univ_label); /* later: if flag is not true, then add message? */ } void univ_help(where) int where; { univ_visit_help(where); /* push old selector info to stack */ } char* univ_keyhelp_modestr(ui) UNIV_ITEM* ui; { switch (ui->data.i) { case UHELP_PAGE: return "Article Pager Mode"; case UHELP_ART: return "Article Display/Selection Mode"; case UHELP_NG: return "Newsgroup Browse Mode"; case UHELP_NGSEL: return "Newsgroup Selector"; case UHELP_ADDSEL: return "Add-Newsgroup Selector"; #ifdef ESCSUBS case UHELP_SUBS: return "Escape Substitutions"; #endif case UHELP_ARTSEL: return "Thread/Subject/Article Selector"; case UHELP_MULTIRC: return "Newsrc Selector"; case UHELP_OPTIONS: return "Option Selector"; #ifdef SCAN case UHELP_SCANART: return "Article Scan Mode"; #endif case UHELP_UNIV: return "Universal Selector"; default: return 0; } } trn-4.0-test77/univ.h0000644000000000000000000001035207113133016013137 0ustar rootroot/* univ.h */ /* Universal selector * */ #define UN_NONE 0 /* textual placeholder */ #define UN_TXT 1 #define UN_DATASRC 2 #define UN_NEWSGROUP 3 #define UN_GROUPMASK 4 /* an individual article */ #define UN_ARTICLE 5 /* filename for a configuration file */ #define UN_CONFIGFILE 6 /* Virtual newsgroup file (reserved for compatability with strn) */ #define UN_VIRTUAL1 7 /* virtual newsgroup marker (for pass 2) */ #define UN_VGROUP 8 /* text file */ #define UN_TEXTFILE 9 /* keystroke help functions from help.c */ #define UN_HELPKEY 10 /* quick debugging: just has data */ #define UN_DEBUG1 -1 /* group that is deselected (with !group) */ #define UN_GROUP_DESEL -2 /* virtual newsgroup deselected (with !group) */ #define UN_VGROUP_DESEL -3 /* generic deleted item -- no per-item memory */ #define UN_DELETED -4 /* selector flags */ #define UF_SEL 0x01 #define UF_DEL 0x02 #define UF_DELSEL 0x04 #define UF_INCLUDED 0x10 #define UF_EXCLUDED 0x20 /* virtual/merged group flags (UNIV_VIRT_GROUP.flags) */ /* articles use minimum score */ #define UF_VG_MINSCORE 0x01 /* articles use maximum score */ #define UF_VG_MAXSCORE 0x02 struct univ_groupmask_data { char* title; char* masklist; }; struct univ_configfile_data { char* title; char* fname; char* label; }; struct univ_virt_data { char* ng; char* id; char* from; char* subj; ART_NUM num; }; struct univ_virt_group { char* ng; #ifdef SCORE int minscore; int maxscore; #endif char flags; }; struct univ_newsgroup { char* ng; }; struct univ_textfile { char* fname; }; union univ_data { char* str; int i; UNIV_GROUPMASK_DATA gmask; UNIV_CONFIGFILE_DATA cfile; UNIV_NEWSGROUP group; UNIV_VIRT_DATA virt; UNIV_VIRT_GROUP vgroup; UNIV_TEXTFILE textfile; }; struct univ_item { UNIV_ITEM* next; UNIV_ITEM* prev; int num; /* natural order (for sort) */ int flags; /* for selector */ int type; /* what kind of object is it? */ char* desc; /* default description */ #ifdef SCORE int score; #endif UNIV_DATA data; /* describes the object */ }; /* have we ever been initialized? */ EXT int univ_ever_init; /* How deep are we in the tree? */ EXT int univ_level; /* if TRUE, we are in the "virtual group" second pass */ EXT bool univ_ng_virtflag INIT(FALSE); /* if TRUE, we are reading an article from a "virtual group" */ EXT bool univ_read_virtflag INIT(FALSE); /* "follow"-related stuff (virtual groups) */ EXT bool univ_default_cmd INIT(FALSE); EXT bool univ_follow INIT(TRUE); EXT bool univ_follow_temp INIT(FALSE); /* if TRUE, the user has loaded their own top univ. config file */ EXT bool univ_usrtop; /* items which must be saved in context */ EXT UNIV_ITEM* first_univ; EXT UNIV_ITEM* last_univ; EXT UNIV_ITEM* sel_page_univ; EXT UNIV_ITEM* sel_next_univ; EXT char* univ_fname; /* current filename (may be null) */ EXT char* univ_label; /* current label (may be null) */ EXT char* univ_title; /* title of current level */ EXT char* univ_tmp_file; /* temp. file (may be null) */ EXT HASHTABLE* univ_ng_hash INIT(0); EXT HASHTABLE* univ_vg_hash INIT(0); /* end of items that must be saved */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void univ_init _((void)); void univ_startup _((void)); void univ_open _((void)); void univ_close _((void)); UNIV_ITEM* univ_add _((int,char*)); int univ_lines _((UNIV_ITEM*)); char* univ_desc_line _((UNIV_ITEM*,int)); void univ_add_text _((char*)); void univ_add_debug _((char*,char*)); void univ_add_group _((char*,char*)); void univ_add_mask _((char*,char*)); void univ_add_file _((char*,char*,char*)); UNIV_ITEM* univ_add_virt_num _((char*,char*,ART_NUM)); void univ_add_textfile _((char*,char*)); void univ_add_virtgroup _((char*)); void univ_use_pattern _((char*,int)); void univ_use_group_line _((char*,int)); bool univ_file_load _((char*,char*,char*)); void univ_mask_load _((char*,char*)); void univ_redofile _((void)); void univ_edit _((void)); void univ_page_file _((char*)); void univ_ng_virtual _((void)); int univ_visit_group_main _((char*)); void univ_virt_pass _((void)); void sort_univ _((void)); char* univ_article_desc _((UNIV_ITEM*)); void univ_help_main _((int)); void univ_help _((int)); char* univ_keyhelp_modestr _((UNIV_ITEM*)); trn-4.0-test77/univ.ih0000644000000000000000000000124407113133016013310 0ustar rootroot/* univ.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void univ_free_data _((UNIV_ITEM*)); static int univ_DoMatch _((char*,char*)); static bool univ_use_file _((char*,char*,char*)); static bool univ_include_file _((char*)); static void univ_do_line_ext1 _((char*,char*)); static bool univ_do_line _((char*)); static char* univ_edit_new_userfile _((void)); static void univ_vg_addart _((ART_NUM)); static void univ_vg_addgroup _((void)); static int univ_order_number _((UNIV_ITEM**,UNIV_ITEM**)); #ifdef SCORE static int univ_order_score _((UNIV_ITEM**,UNIV_ITEM**)); #endif trn-4.0-test77/url.c0000644000000000000000000001350611437640112012763 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* url.c * * Routines for handling WWW URL references. */ #include "EXTERN.h" #include "common.h" #ifdef USEURL #include "term.h" #include "util.h" #include "util2.h" #include "INTERN.h" #include "url.h" #include "url.ih" /* Lower-level net routines grabbed from nntpinit.c. * The special cases (DECNET, EXCELAN, and NONETD) are not supported. */ /* NOTE: If running Winsock, NNTP must be enabled so that the Winsock * initialization will be done. (common.h will check for this) */ #ifdef WINSOCK #include WSADATA wsaData; #else #include #include #include #endif #ifndef WINSOCK unsigned long inet_addr _((char*)); struct servent* getservbyname(); struct hostent* gethostbyname(); #endif static char url_buf[1030]; /* XXX just a little bit larger than necessary... */ static char url_type[256]; static char url_host[256]; static int url_port; static char url_path[1024]; /* returns TRUE if successful */ bool fetch_http(host,port,path,outname) char* host; int port; char* path; char* outname; { int sock; FILE* fp_out; int len; sock = get_tcp_socket(host,port,"http"); /* XXX length check */ /* XXX later consider using HTTP/1.0 format (and user-agent) */ sprintf(url_buf, "GET %s\n",path); /* Should I be writing the 0 char at the end? */ if (write(sock, url_buf, strlen(url_buf)+1) < 0) { printf("\nError: writing on URL socket\n"); close(sock); return FALSE; } fp_out = fopen(outname,"w"); if (!fp_out) { printf("\nURL output file could not be opened.\n"); return FALSE; } /* XXX some kind of URL timeout would be really nice */ /* (the old nicebg code caused portability problems) */ /* later consider larger buffers, spinner */ while (1) { if ((len = read(sock, url_buf, 1024)) < 0) { printf("\nError: reading URL reply\n"); return FALSE; } if (len == 0) { break; /* no data, end connection */ } fwrite(url_buf,1,len,fp_out); } fclose(fp_out); close(sock); return TRUE; } /* add port support later? */ bool fetch_ftp(host,origpath,outname) char* host; char* origpath; char* outname; { #ifdef USEFTP static char cmdline[1024]; static char path[512]; /* use to make writable copy */ /* buffers used because because filexp overwrites previous call results */ static char username[128]; static char userhost[128]; char* p; int status; char* cdpath; int x,y,l; safecpy(path,origpath,510); p = rindex(path, '/'); /* p points to last slash or NULL*/ if (p == NULL) { printf("Error: URL:ftp path has no '/' character.\n") FLUSH; return FALSE; } if (p[1] == '\0') { printf("Error: URL:ftp path has no final filename.\n") FLUSH; return FALSE; } safecpy(username,filexp("%L"),120); safecpy(userhost,filexp("%H"),120); if (p != path) { /* not of form /foo */ *p = '\0'; cdpath = path; } else cdpath = "/"; sprintf(cmdline,"%s/ftpgrab %s ftp %s@%s %s %s %s", filexp("%X"),host,username,userhost,cdpath,p+1,outname); /* modified escape_shell_cmd code from NCSA HTTPD util.c */ /* serious security holes could result without this code */ l = strlen(cmdline); for (x = 0; cmdline[x]; x++) { if (index("&;`'\"|*?~<>^()[]{}$\\",cmdline[x])) { for (y = l+1; y > x; y--) cmdline[y] = cmdline[y-1]; l++; /* length has been increased */ cmdline[x] = '\\'; x++; /* skip the character */ } } #if 0 printf("ftpgrab command:\n|%s|\n",cmdline); #endif *p = '/'; status = doshell(NULL,cmdline); #if 0 printf("\nFTP command status is %d\n",status) FLUSH; while (!input_pending()) ; eat_typeahead(); #endif return TRUE; #else printf("\nThis copy of trn does not have URL:ftp support.\n"); return FALSE; #endif } /* right now only full, absolute URLs are allowed. */ /* use relative URLs later? */ /* later: pay more attention to long URLs */ bool parse_url(url) char* url; { char* s; char* p; /* consider using 0 as default to look up the service? */ url_port = 80; /* the default */ if (!url || !*url) { printf("Empty URL -- ignoring.\n") FLUSH; return FALSE; } p = url_type; for (s = url; *s && *s != ':'; *p++ = *s++) ; *p = '\0'; if (!*s) { printf("Incomplete URL: %s\n",url) FLUSH; return FALSE; } s++; if (strnEQ(s,"//",2)) { /* normal URL type, will have host (optional portnum) */ s += 2; p = url_host; /* check for address literal: news://[ip:v6:address]:port/ */ if (*s == '[') { while (*s && *s != ']') *p++ = *s++; if (!*s) { printf("Bad address literal: %s\n",url) FLUSH; return FALSE; } s++; /* skip ] */ } else while (*s && *s != '/' && *s != ':') *p++ = *s++; *p = '\0'; if (!*s) { printf("Incomplete URL: %s\n",url) FLUSH; return FALSE; } if (*s == ':') { s++; p = url_buf; /* temp space */ if (!isdigit(*s)) { printf("Bad URL (non-numeric portnum): %s\n",url) FLUSH; return FALSE; } while (isdigit(*s)) *p++ = *s++; *p = '\0'; url_port = atoi(url_buf); } } else { if (!strEQ(url_type,"news")) { printf("URL needs a hostname: %s\n",url); return FALSE; } } /* finally, just do the path */ if (*s != '/') { printf("Bad URL (path does not start with /): %s\n",url) FLUSH; return FALSE; } strcpy(url_path,s); return TRUE; } bool url_get(url,outfile) char* url; char* outfile; { bool flag; if (!parse_url(url)) return FALSE; if (strEQ(url_type,"http")) flag = fetch_http(url_host,url_port,url_path,outfile); else if (strEQ(url_type,"ftp")) flag = fetch_ftp(url_host,url_path,outfile); else { if (url_type) printf("\nURL type %s not supported (yet?)\n",url_type) FLUSH; flag = FALSE; } return flag; } #endif /* USEURL */ trn-4.0-test77/url.h0000644000000000000000000000047707113133016012767 0ustar rootroot/* This file Copyright 1993 by Clifford A. Adams */ /* url.h * * Routines for handling WWW URL references. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ bool fetch_http _((char*,int,char*,char*)); bool fetch_ftp _((char*,char*,char*)); bool parse_url _((char*)); bool url_get _((char*,char*)); trn-4.0-test77/url.ih0000644000000000000000000000022311437640112013131 0ustar rootroot/* url.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ trn-4.0-test77/util.c0000644000000000000000000005233707222736556013162 0ustar rootroot/* util.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "final.h" #include "term.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "nntpauth.h" #include "intrp.h" #include "env.h" #include "util2.h" #include "only.h" #include "search.h" #ifdef I_SYS_WAIT #include #endif #ifdef MSDOS #include #endif #ifdef SCAN #include "scan.h" #include "smisc.h" /* s_default_cmd */ #endif #include "univ.h" #include "INTERN.h" #include "util.ih" #include "util.h" #ifdef UNION_WAIT typedef union wait WAIT_STATUS; #else typedef int WAIT_STATUS; #endif #ifndef USE_DEBUGGING_MALLOC static char nomem[] = "trn: out of memory!\n"; #endif static char null_export[] = "_=X";/* Just in case doshell precedes util_init */ static char* newsactive_export = null_export + 2; static char* grpdesc_export = null_export + 2; static char* quotechars_export = null_export + 2; #ifdef SUPPORT_NNTP static char* nntpserver_export = null_export + 2; static char* nntpfds_export = null_export + 2; #ifdef USE_GENAUTH static char* nntpauth_export = null_export + 2; #endif static char* nntpforce_export = null_export + 2; #endif void util_init() { extern char patchlevel[]; char* cp; int i; for (i = 0, cp = buf; i < 512; i++) *cp++ = 'X'; *cp = '\0'; newsactive_export = export("NEWSACTIVE", buf); grpdesc_export = export("NEWSDESCRIPTIONS", buf); #ifdef SUPPORT_NNTP nntpserver_export = export("NNTPSERVER", buf); #endif buf[64] = '\0'; quotechars_export = export("QUOTECHARS",buf); #ifdef SUPPORT_NNTP nntpfds_export = export("NNTPFDS", buf); #ifdef USE_GENAUTH nntpauth_export = export("NNTP_AUTH_FDS", buf); #endif buf[3] = '\0'; nntpforce_export = export("NNTP_FORCE_AUTH", buf); #endif for (cp = patchlevel; isspace(*cp); cp++) ; export("TRN_VERSION", cp); } /* fork and exec a shell command */ int doshell(shell,s) char* shell; char* s; { #ifndef MSDOS WAIT_STATUS status; pid_t pid, w; #endif int ret; xmouse_off(); #ifdef SIGTSTP sigset(SIGTSTP,SIG_DFL); sigset(SIGTTOU,SIG_DFL); sigset(SIGTTIN,SIG_DFL); #endif #ifdef SUPPORT_NNTP if (datasrc && (datasrc->flags & DF_REMOTE)) { #ifdef USE_GENAUTH if (export_nntp_fds) { if (!nntplink.rd_fp) { if (nntp_command("DATE") <= 0 || nntp_check() < 0) finalize(1); /*$$*/ } sprintf(buf,"%d.%d.%d",(int)fileno(nntplink.rd_fp), (int)fileno(nntplink.wr_fp),nntplink.cookiefd); re_export(nntpauth_export, buf, 512); } else un_export(nntpauth_export); #endif if (!export_nntp_fds || !nntplink.rd_fp) un_export(nntpfds_export); else { sprintf(buf,"%d.%d",(int)fileno(nntplink.rd_fp), (int)fileno(nntplink.wr_fp)); re_export(nntpfds_export, buf, 64); } re_export(nntpserver_export,datasrc->newsid,512); if (datasrc->nntplink.flags & NNTP_FORCE_AUTH_NEEDED) re_export(nntpforce_export,"yes",3); else un_export(nntpforce_export); if (datasrc->auth_user) { int fd; if ((fd = open(nntp_auth_file, O_WRONLY|O_CREAT, 0600)) >= 0) { write(fd, datasrc->auth_user, strlen(datasrc->auth_user)); write(fd, "\n", 1); if (datasrc->auth_pass) { write(fd, datasrc->auth_pass, strlen(datasrc->auth_pass)); write(fd, "\n", 1); } close(fd); } } if (nntplink.port_number) { int len = strlen(nntpserver_export); sprintf(buf,";%d",nntplink.port_number); if (len + (int)strlen(buf) < 511) strcpy(nntpserver_export+len, buf); } if (datasrc->act_sf.fp) re_export(newsactive_export, datasrc->extra_name, 512); else re_export(newsactive_export, "none", 512); } else { #ifdef SUPPORT_NNTP un_export(nntpfds_export); #ifdef USE_GENAUTH un_export(nntpauth_export); #endif un_export(nntpserver_export); un_export(nntpforce_export); #endif if (datasrc) re_export(newsactive_export, datasrc->newsid, 512); else un_export(newsactive_export); } #else if (datasrc) re_export(newsactive_export, datasrc->newsid, 512); else un_export(newsactive_export); #endif if (datasrc) re_export(grpdesc_export, datasrc->grpdesc, 512); else un_export(grpdesc_export); interp(buf,64-1+2,"%I"); buf[strlen(buf)-1] = '\0'; re_export(quotechars_export, buf+1, 64); if (shell == NULL && (shell = getval("SHELL",NULL)) == NULL) shell = PREFSHELL; termlib_reset(); #ifdef MSDOS status = spawnl(P_WAIT, shell, shell, "/c", s, (char*)NULL); #else if ((pid = vfork()) == 0) { #ifdef SUPPORT_NNTP if (datasrc && (datasrc->flags & DF_REMOTE)) { int i; /* This is necessary to keep the bourne shell from puking */ for (i = 3; i < 10; ++i) { if (nntplink.rd_fp && (i == fileno(nntplink.rd_fp) || i == fileno(nntplink.wr_fp))) continue; #ifdef USE_GENAUTH if (i == nntplink.cookiefd) continue; #endif close(i); } } #endif /* SUPPORT_NNTP */ if (nowait_fork) { close(1); close(2); dup(open("/dev/null",1)); } if (*s) execl(shell, shell, "-c", s, (char*)NULL); else execl(shell, shell, (char*)NULL, (char*)NULL, (char*)NULL); _exit(127); } sigignore(SIGINT); #ifdef SIGQUIT sigignore(SIGQUIT); #endif waiting = TRUE; while ((w = wait(&status)) != pid) if (w == -1 && errno != EINTR) break; if (w == -1) ret = -1; else #ifdef USE_WIFSTAT ret = WEXITSTATUS(status); #else #ifdef UNION_WAIT ret = status.w_status >> 8; #else ret = status; #endif /* UNION_WAIT */ #endif /* USE_WIFSTAT */ #endif /* !MSDOS */ termlib_init(); xmouse_check(); waiting = FALSE; sigset(SIGINT,int_catcher); #ifdef SIGQUIT sigset(SIGQUIT,SIG_DFL); #endif #ifdef SIGTSTP sigset(SIGTSTP,stop_catcher); sigset(SIGTTOU,stop_catcher); sigset(SIGTTIN,stop_catcher); #endif #ifdef SUPPORT_NNTP if (datasrc && datasrc->auth_user) UNLINK(nntp_auth_file); #endif return ret; } /* paranoid version of malloc */ #ifndef USE_DEBUGGING_MALLOC char* safemalloc(size) MEM_SIZE size; { char* ptr; ptr = malloc(size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout) FLUSH; sig_catcher(0); } return ptr; } #endif /* paranoid version of realloc. If where is NULL, call malloc */ #ifndef USE_DEBUGGING_MALLOC char* saferealloc(where,size) char* where; MEM_SIZE size; { char* ptr; if (!where) ptr = malloc(size ? size : (MEM_SIZE)1); else ptr = realloc(where, size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout) FLUSH; sig_catcher(0); } return ptr; } #endif /* !USE_DEBUGGING_MALLOC */ /* safe version of string concatenate, with \n deletion and space padding */ char* safecat(to,from,len) char* to; register char* from; register int len; { register char* dest = to; len--; /* leave room for null */ if (*dest) { while (len && *dest++) len--; if (len) { len--; *(dest-1) = ' '; } } if (from) while (len && (*dest++ = *from++)) len--; if (len) dest--; if (*(dest-1) == '\n') dest--; *dest = '\0'; return to; } /* effective access */ #ifdef SETUIDGID int eaccess(filename, mod) char* filename; int mod; { int protection, euid; mod &= 7; /* remove extraneous garbage */ if (stat(filename, &filestat) < 0) return -1; euid = geteuid(); if (euid == ROOTID) return 0; protection = 7 & ( filestat.st_mode >> (filestat.st_uid == euid ? 6 : (filestat.st_gid == getegid() ? 3 : 0)) ); if ((mod & protection) == mod) return 0; errno = EACCES; return -1; } #endif /* * Get working directory */ char* trn_getwd(buf, buflen) char* buf; int buflen; { char* ret; #ifdef HAS_GETCWD ret = getcwd(buf, buflen); #else ret = trn_getcwd(buf, buflen); #endif if (!ret) { printf("Cannot determine current working directory!\n") FLUSH; finalize(1); } #ifdef MSDOS strlwr(buf); while ((buf = index(buf,'\\')) != NULL) *buf++ = '/'; #endif return ret; } #ifndef HAS_GETCWD static char* trn_getcwd(buf, len) char* buf; int len; { char* ret; #ifdef HAS_GETWD buf[len-1] = 0; ret = getwd(buf); if (buf[len-1]) { /* getwd() overwrote the end of the buffer */ printf("getwd() buffer overrun!\n") FLUSH; finalize(1); } #else FILE* popen(); FILE* pipefp; char* nl; if ((pipefp = popen("/bin/pwd","r")) == NULL) { printf("Can't popen /bin/pwd\n") FLUSH; return NULL; } buf[0] = 0; fgets(ret = buf, len, pipefp); if (pclose(pipefp) == EOF) { printf("Failed to run /bin/pwd\n") FLUSH; return NULL; } if (!buf[0]) { printf("/bin/pwd didn't output anything\n") FLUSH; return NULL; } if ((nl = index(buf, '\n')) != NULL) *nl = '\0'; #endif return ret; } #endif /* just like fgets but will make bigger buffer as necessary */ char* get_a_line(buffer,buffer_length,realloc_ok,fp) char* buffer; register int buffer_length; bool_int realloc_ok; FILE* fp; { register int bufix = 0; register int nextch; do { if (bufix >= buffer_length) { buffer_length *= 2; if (realloc_ok) { /* just grow in place, if possible */ buffer = saferealloc(buffer,(MEM_SIZE)buffer_length+1); } else { char* tmp = safemalloc((MEM_SIZE)buffer_length+1); strncpy(tmp,buffer,buffer_length/2); buffer = tmp; realloc_ok = TRUE; } } if ((nextch = getc(fp)) == EOF) { if (!bufix) return NULL; break; } buffer[bufix++] = (char)nextch; } while (nextch && nextch != '\n'); buffer[bufix] = '\0'; len_last_line_got = bufix; buflen_last_line_got = buffer_length; return buffer; } int makedir(dirname,nametype) register char* dirname; int nametype; { #ifdef MAKEDIR register char* end; register char* s; # ifdef HAS_MKDIR int status = 0; # else char tmpbuf[1024]; register char* tbptr = tmpbuf+5; # endif for (end = dirname; *end; end++) ; /* find the end */ if (nametype == MD_FILE) { /* not to create last component? */ for (--end; end != dirname && *end != '/'; --end) ; if (*end != '/') return 0; /* nothing to make */ *end = '\0'; /* isolate file name */ } # ifndef HAS_MKDIR strcpy(tmpbuf,"mkdir"); # endif s = end; for (;;) { if (stat(dirname,&filestat) >= 0 && S_ISDIR(filestat.st_mode)) { /* does this much exist as a dir? */ *s = '/'; /* mark this as existing */ break; } s = rindex(dirname,'/'); /* shorten name */ if (!s) /* relative path! */ break; /* hope they know what they are doing */ *s = '\0'; /* mark as not existing */ } for (s=dirname; s <= end; s++) { /* this is grody but efficient */ if (!*s) { /* something to make? */ # ifdef HAS_MKDIR status = status || mkdir(dirname,0777); # else sprintf(tbptr," %s",dirname); tbptr += strlen(tbptr); /* make it, sort of */ # endif *s = '/'; /* mark it made */ } } if (nametype == MD_DIR) /* don't need final slash unless */ *end = '\0'; /* a filename follows the dir name */ # ifdef HAS_MKDIR return status; # else return (tbptr==tmpbuf+5 ? 0 : doshell(sh,tmpbuf));/* exercise our faith */ # endif #else sprintf(cmd_buf,"%s %s %d", filexp(DIRMAKER), dirname, nametype); return doshell(sh,cmd_buf); #endif } void notincl(feature) char* feature; { printf("\nNo room for feature \"%s\" on this machine.\n",feature) FLUSH; } /* grow a static string to at least a certain length */ void growstr(strptr,curlen,newlen) char** strptr; int* curlen; int newlen; { if (newlen > *curlen) { /* need more room? */ if (*curlen) *strptr = saferealloc(*strptr,(MEM_SIZE)newlen); else *strptr = safemalloc((MEM_SIZE)newlen); *curlen = newlen; } } void setdef(buffer,dflt) char* buffer; char* dflt; { #ifdef SCAN s_default_cmd = FALSE; #endif univ_default_cmd = FALSE; if (*buffer == ' ' #ifndef STRICTCR || *buffer == '\n' || *buffer == '\r' #endif ) { #ifdef SCAN s_default_cmd = TRUE; #endif univ_default_cmd = TRUE; if (*dflt == '^' && isupper(dflt[1])) pushchar(Ctl(dflt[1])); else pushchar(*dflt); getcmd(buffer); } } #ifndef NO_FILELINKS void safelink(old, new) char* old; char* new; { #if 0 extern int sys_nerr; extern char* sys_errlist[]; #endif if (link(old,new)) { printf("Can't link backup (%s) to .newsrc (%s)\n", old, new) FLUSH; #if 0 if (errno>0 && errno= 24L * 60) { items = (int)(secs / (24*60)); secs = secs % (24*60); sprintf(s, "%d day%s, ", items, PLURAL(items)); s += strlen(s); } if (secs >= 60L) { items = (int)(secs / 60); secs = secs % 60; sprintf(s, "%d hour%s, ", items, PLURAL(items)); s += strlen(s); } if (secs) { sprintf(s, "%d minute%s, ", (int)secs, PLURAL(items)); s += strlen(s); } s[-2] = '\0'; return buf; } /* returns a saved string representing a unique temporary filename */ char* temp_filename() { static int tmpfile_num = 0; char tmpbuf[CBUFLEN]; extern long our_pid; sprintf(tmpbuf,"%s/trn%d.%ld",tmpdir,tmpfile_num++,our_pid); return savestr(tmpbuf); } #ifdef SUPPORT_NNTP char* get_auth_user() { return datasrc->auth_user; } #endif #ifdef SUPPORT_NNTP char* get_auth_pass() { return datasrc->auth_pass; } #endif #if defined(USE_GENAUTH) && defined(SUPPORT_NNTP) char* get_auth_command() { return datasrc->auth_command; } #endif char** prep_ini_words(words) INI_WORDS words[]; { register int checksum; char* cp = (char*)INI_VALUES(words); if (!cp) { int i; for (i = 1; words[i].item != NULL; i++) { if (*words[i].item == '*') { words[i].checksum = -1; continue; } checksum = 0; for (cp = words[i].item; *cp; cp++) checksum += (isupper(*cp)? tolower(*cp) : *cp); words[i].checksum = (checksum << 8) + (cp - words[i].item); } words[0].checksum = i; words[0].help_str = cp = safemalloc(i * sizeof (char*)); } bzero(cp, INI_LEN(words) * sizeof (char*)); return (char**)cp; } void unprep_ini_words(words) INI_WORDS words[]; { free((char*)INI_VALUES(words)); words[0].checksum = 0; words[0].help_str = NULL; } void prep_ini_data(cp,filename) char* cp; char* filename; { char* t = cp; #ifdef DEBUG if (debug & DEB_RCFILES) printf("Read %d bytes from %s\n",strlen(cp),filename); #endif while (*cp) { while (isspace(*cp)) cp++; if (*cp == '[') { char* s = t; do { *t++ = *cp++; } while (*cp && *cp != ']' && *cp != '\n'); if (*cp == ']' && t != s) { *t++ = '\0'; cp++; if (parse_string(&t, &cp)) cp++; while (*cp) { while (isspace(*cp)) cp++; if (*cp == '[') break; if (*cp == '#') s = cp; else { s = t; while (*cp && *cp != '\n') { if (*cp == '=') break; if (isspace(*cp)) { if (s == t || t[-1] != ' ') *t++ = ' '; cp++; } else *t++ = *cp++; } if (*cp == '=' && t != s) { while (t != s && isspace(t[-1])) t--; *t++ = '\0'; cp++; if (parse_string(&t, &cp)) s = NULL; else s = cp; } else s = cp; } cp++; if (s) for (cp = s; *cp && *cp++ != '\n'; ) ; } } else { *t = '\0'; printf("Invalid section in %s: %s\n", filename, s); t = s; while (*cp && *cp++ != '\n') ; } } else while (*cp && *cp++ != '\n') ; } *t = '\0'; } bool parse_string(to, from) char** to; char** from; { char inquote = 0; char* t = *to; char* f = *from; char* s; while (isspace(*f) && *f != '\n') f++; for (s = t; *f; f++) { if (inquote) { if (*f == inquote) { inquote = 0; s = t; continue; } } else if (*f == '\n') break; else if (*f == '\'' || *f == '"') { inquote = *f; continue; } else if (*f == '#') { while (*++f && *f != '\n') ; break; } if (*f == '\\') { if (*++f == '\n') continue; f = interp_backslash(t, f); t++; } else *t++ = *f; } #if 0 if (inquote) printf("Unbalanced quotes.\n"); #endif inquote = (*f != '\0'); while (t != s && isspace(t[-1])) t--; *t++ = '\0'; *to = t; *from = f; return inquote; /* return TRUE if the string ended with a newline */ } char* next_ini_section(cp,section,cond) char* cp; char** section; char** cond; { while (*cp != '[') { if (!*cp) return NULL; cp += strlen(cp) + 1; cp += strlen(cp) + 1; } *section = cp+1; cp += strlen(cp) + 1; *cond = cp; cp += strlen(cp) + 1; #ifdef DEBUG if (debug & DEB_RCFILES) printf("Section [%s] (condition: %s)\n",*section, **cond? *cond : ""); #endif return cp; } char* parse_ini_section(cp, words) char* cp; INI_WORDS words[]; { register int checksum; register char* s; char** values = prep_ini_words(words); int i; if (!*cp) return NULL; while (*cp && *cp != '[') { checksum = 0; for (s = cp; *s; s++) { if (isupper(*s)) *s = tolower(*s); checksum += *s; } checksum = (checksum << 8) + (s++ - cp); if (*s) { for (i = 1; words[i].checksum; i++) { if (words[i].checksum == checksum && strcaseEQ(cp,words[i].item)) { values[i] = s; break; } } if (!words[i].checksum) printf("Unknown option: `%s'.\n",cp); cp = s + strlen(s) + 1; } else cp = s + 1; } #ifdef DEBUG if (debug & DEB_RCFILES) { printf("Ini_words: %s\n", words[0].item); for (i = 1; words[i].checksum; i++) if (values[i]) printf("%s=%s\n",words[i].item,values[i]); } #endif return cp; } bool check_ini_cond(cond) char* cond; { int not, equal, upordown, num; char* s; cond = dointerp(buf,sizeof buf,cond,"!=<>",(char*)NULL); s = buf + strlen(buf); while (s != buf && isspace(s[-1])) s--; *s = '\0'; if ((not = (*cond == '!')) != 0) cond++; if ((upordown = (*cond=='<'? -1: (*cond=='>'? 1:0))) != 0) cond++; if ((equal = (*cond == '=')) != 0) cond++; while (isspace(*cond)) cond++; if (upordown) { num = atoi(cond) - atoi(buf); if (!((equal && !num) || (upordown * num < 0)) ^ not) return FALSE; } else if (equal) { COMPEX condcompex; init_compex(&condcompex); if ((s = compile(&condcompex,cond,TRUE,TRUE)) != NULL) { /*warning(s)*/; equal = FALSE; } else equal = execute(&condcompex,buf) != NULL; free_compex(&condcompex); return equal; } else return FALSE; return TRUE; } /* $$ might get replaced soonish... */ /* Ask for a single character (improve the prompt?) */ char menu_get_char() { printf("Enter your choice: "); fflush(stdout); eat_typeahead(); getcmd(buf); printf("%c\n",*buf) FLUSH; return(*buf); } /* NOTE: kfile.c uses its own editor function */ /* used in a few places, now centralized */ int edit_file(fname) char* fname; { int r = -1; if (!fname || !*fname) return r; /* XXX paranoia check on length */ sprintf(cmd_buf,"%s ", filexp(getval("VISUAL",getval("EDITOR",defeditor)))); strcat(cmd_buf, filexp(fname)); termdown(3); resetty(); /* make sure tty is friendly */ r = doshell(sh,cmd_buf);/* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ return r; } /* Consider a trn_pushdir, trn_popdir pair of functions */ trn-4.0-test77/util.h0000644000000000000000000000455107222733635013155 0ustar rootroot/* util.h */ /* This software is copyrighted as detailed in the LICENSE file. */ EXT bool waiting INIT(FALSE); /* waiting for subprocess (in doshell)? */ EXT bool nowait_fork INIT(FALSE); EXT bool export_nntp_fds INIT(FALSE); /* the strlen and the buffer length of "some_buf" after a call to: * some_buf = get_a_line(bufptr,bufsize,realloc,fp); */ EXT int len_last_line_got INIT(0); EXT MEM_SIZE buflen_last_line_got INIT(0); #define AT_GREY_SPACE(s) ((*(Uchar*)(s) & 0x7F) <= ' ' || *(s) == '\177') #define AT_NORM_CHAR(s) ((*(Uchar*)(s) & 0x7F) >= ' ' && *(s) != '\177') /* is the string for makedir a directory name or a filename? */ #define MD_DIR 0 #define MD_FILE 1 /* a template for parsing an ini file */ struct ini_words { int checksum; char* item; char* help_str; }; #define INI_LEN(words) (words)[0].checksum #define INI_VALUES(words) ((char**)(words)[0].help_str) #define INI_VALUE(words,num) INI_VALUES(words)[num] #define safefree(ptr) if (!ptr) ; else free((char*)(ptr)) #define safefree0(ptr) if (!ptr) ; else free((char*)(ptr)), (ptr)=0 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ void util_init _((void)); int doshell _((char*,char*)); #ifndef USE_DEBUGGING_MALLOC char* safemalloc _((MEM_SIZE)); char* saferealloc _((char*,MEM_SIZE)); #endif char* safecat _((char*,char*,int)); #ifdef SETUIDGID int eaccess _((char*,int)); #endif char* trn_getwd _((char*,int)); char* get_a_line _((char*,int,bool_int,FILE*)); int makedir _((char*,int)); void notincl _((char*)); void growstr _((char**,int*,int)); void setdef _((char*,char*)); #ifndef NO_FILELINKS void safelink _((char*,char*)); #endif #ifndef HAS_STRSTR char* trn_strstr _((char*,char*)); #endif void verify_sig _((void)); double current_time _((void)); time_t text2secs _((char*,time_t)); char* secs2text _((time_t)); char* temp_filename _((void)); #ifdef SUPPORT_NNTP char* get_auth_user _((void)); char* get_auth_pass _((void)); #endif #if defined(USE_GENAUTH) && defined(SUPPORT_NNTP) char* get_auth_command _((void)); #endif char** prep_ini_words _((INI_WORDS*)); void unprep_ini_words _((INI_WORDS*)); void prep_ini_data _((char*,char*)); bool parse_string _((char**,char**)); char* next_ini_section _((char*,char**,char**)); char* parse_ini_section _((char*,INI_WORDS*)); bool check_ini_cond _((char*)); char menu_get_char _((void)); int edit_file _((char*)); trn-4.0-test77/util.ih0000644000000000000000000000022207222736557013323 0ustar rootroot/* util.ih */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ #ifndef HAS_GETCWD static char* trn_getcwd _((char*,int)); #endif trn-4.0-test77/util2.c0000644000000000000000000001767407113133016013226 0ustar rootroot/* util2.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "hash.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "nntpauth.h" #include "util.h" #include "util3.h" #include "INTERN.h" #include "util2.h" #ifdef TILDENAME static char* tildename = NULL; static char* tildedir = NULL; #endif /* copy a string to a safe spot */ char* savestr(str) char* str; { register char* newaddr = safemalloc((MEM_SIZE)(strlen(str)+1)); strcpy(newaddr,str); return newaddr; } /* safe version of string copy */ char* safecpy(to,from,len) char* to; register char* from; register int len; { register char* dest = to; if (from) { while (--len && *from) *dest++ = *from++; } *dest = '\0'; return to; } /* copy a string up to some (non-backslashed) delimiter, if any */ char* cpytill(to,from,delim) register char* to; register char* from; register int delim; { while (*from) { if (*from == '\\' && from[1] == delim) from++; else if (*from == delim) break; *to++ = *from++; } *to = '\0'; return from; } /* 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 /* interpret any % escapes */ dointerp(filename,sizeof filename,s,(char*)NULL,(char*)NULL); #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 if (*s == '~' && (!s[1] || s[1] == '/')) { d = getenv("TRNPREFIX"); if (!d) d = INSTALLPREFIX; sprintf(scrbuf,"%s%s",d,s+1); #ifdef DEBUG if (debug & DEB_FILEXP) printf("~~ %s\n",scrbuf) FLUSH; #endif } 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); if (tildedir) free(tildedir); tildedir = NULL; tildename = savestr(scrbuf); #ifdef HAS_GETPWENT /* getpwnam() is not the paragon of efficiency */ { struct passwd* pwd = getpwnam(tildename); if (pwd == NULL) { printf("%s is an unknown user. Using default.\n",tildename) FLUSH; return NULL; } 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(filexp(PASSFILE),"r"); char tmpbuf[512]; int i; if (pfp) { while (fgets(tmpbuf,512,pfp) != NULL) { 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); } if (!tildedir) { printf("%s is an unknown user. Using default.\n",tildename) FLUSH; return NULL; } } #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 /* this might do some extra '%'s, but that's how the Mercedes Benz */ dointerp(filename,sizeof filename,scrbuf,(char*)NULL,(char*)NULL); } #ifdef DEBUG if (debug & DEB_FILEXP) printf("> %s\n",filename) FLUSH; #endif return filename; } /* return ptr to little string in big string, NULL if not found */ char* instr(big, little, case_matters) char* big; char* little; bool_int case_matters; { register char* t; register char* s; register char* x; for (t = big; *t; t++) { for (x=t,s=little; *s; x++,s++) { if (!*x) return NULL; if (case_matters == TRUE) { if (*s != *x) break; } else { register char c,d; if (isupper(*s)) c = tolower(*s); else c = *s; if (isupper(*x)) d = tolower(*x); else d = *x; if ( c != d ) break; } } if (!*s) return t; } return NULL; } #ifndef HAS_STRCASECMP static Uchar casemap[256] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }; #endif #ifndef HAS_STRCASECMP int trn_casecmp(s1, s2) register char* s1; register char* s2; { do { if (casemap[(Uchar)*s1++] != casemap[(Uchar)*s2]) return casemap[(Uchar)s1[-1]] - casemap[(Uchar)*s2]; } while (*s2++ != '\0'); return 0; } #endif #ifndef HAS_STRCASECMP int trn_ncasecmp(s1, s2, len) register char* s1; register char* s2; register int len; { while (len--) { if (casemap[(Uchar)*s1++] != casemap[(Uchar)*s2]) return casemap[(Uchar)s1[-1]] - casemap[(Uchar)*s2]; if (*s2++ == '\0') break; } return 0; } #endif #ifdef SUPPORT_NNTP char* read_auth_file(file, pass_ptr) char* file; char** pass_ptr; { FILE* fp; char* strptr[2]; char buf[1024]; strptr[0] = strptr[1] = NULL; if ((fp = fopen(file,"r")) != NULL) { int i; for (i = 0; i < 2; i++) { if (fgets(buf, sizeof buf, fp) != NULL) { char* cp = buf + strlen(buf) - 1; if (*cp == '\n') *cp = '\0'; strptr[i] = savestr(buf); } } fclose(fp); } *pass_ptr = strptr[1]; return strptr[0]; } #endif #ifdef MSDOS int ChDir(path) char* path; { if (isalpha(*path) && path[1] == ':') { setdisk(path[0]&0x1f); path += 2; } #undef chdir return chdir(path); } #endif #ifdef MSDOS int getuid() { return 2; } #endif trn-4.0-test77/util2.h0000644000000000000000000000104107113133016013210 0ustar rootroot/* util2.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ char* savestr _((char*)); char* safecpy _((char*,char*,int)); char* cpytill _((char*,char*,int)); char* filexp _((char*)); char* instr _((char*,char*,bool_int)); #ifndef HAS_STRCASECMP int trn_casecmp _((char*,char*)); int trn_ncasecmp _((char*,char*,int)); #endif #ifdef SUPPORT_NNTP char* read_auth_file _((char*,char**)); #endif #ifdef MSDOS int ChDir _((char*)); int getuid _((void)); #endif trn-4.0-test77/util3.c0000644000000000000000000000405407113133016013213 0ustar rootroot/* util3.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include #include "config.h" #include "config2.h" #include "typedef.h" #include "EXTERN.h" #include "config.h" #include "config2.h" #include "nntpclient.h" #include "util2.h" #include "INTERN.h" #include "util3.h" char* sh = NULL; bool export_nntp_fds = FALSE; #ifdef SUPPORT_NNTP char* nntp_password; #endif int doshell(sh,cmd) char* sh; char* cmd; { return system(cmd); } void finalize(num) int num; { #ifdef SUPPORT_NNTP nntp_close(TRUE); #endif exit(num); } static char nomem[] = "trn: out of memory!\n"; /* paranoid version of malloc */ #ifndef USE_DEBUGGING_MALLOC char* safemalloc(size) MEM_SIZE size; { char* ptr; ptr = malloc(size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout); finalize(1); } return ptr; } #endif /* paranoid version of realloc. If where is NULL, call malloc */ #ifndef USE_DEBUGGING_MALLOC char* saferealloc(where,size) char* where; MEM_SIZE size; { char* ptr; ptr = realloc(where, size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout); finalize(1); } return ptr; } #endif char* dointerp(dest, destsize, pattern, stoppers, cmd) char* dest; int destsize; char* pattern; char* stoppers; char* cmd; { extern char* dotdir; if (*pattern == '%' && pattern[1] == '.') { int len = strlen(dotdir); safecpy(dest, dotdir, destsize); if (len < destsize) safecpy(dest+len, pattern+2, destsize - len); } else safecpy(dest, pattern, destsize); return stoppers; /* This is wrong on purpose */ } #ifdef SUPPORT_NNTP int nntp_handle_nested_lists() { fputs("Programming error! Nested NNTP calls detected.\n",stderr); return -1; } #endif #ifdef SUPPORT_NNTP char* get_auth_user() { extern char* nntp_auth_file; return read_auth_file(nntp_auth_file, &nntp_password); } #endif #ifdef SUPPORT_NNTP char* get_auth_pass() { return nntp_password; } #endif #if defined(USE_GENAUTH) && defined(SUPPORT_NNTP) char* get_auth_command() { return NULL; } #endif trn-4.0-test77/util3.h0000644000000000000000000000127107113133016013216 0ustar rootroot/* util3.h */ /* This software is copyrighted as detailed in the LICENSE file. */ extern char* homedir; /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int doshell _((char*,char*)); void finalize _((int)) #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR >= 5) __attribute__((noreturn)) #endif ; #ifndef USE_DEBUGGING_MALLOC char* safemalloc _((MEM_SIZE)); char* saferealloc _((char*,MEM_SIZE)); #endif char* dointerp _((char*,int,char*,char*,char*)); #ifdef SUPPORT_NNTP int nntp_handle_nested_lists _((void)); char* get_auth_user _((void)); char* get_auth_pass _((void)); #endif #if defined(USE_GENAUTH) && defined(SUPPORT_NNTP) char* get_auth_command _((void)); #endif trn-4.0-test77/uudecode.c0000644000000000000000000001621407113133016013751 0ustar rootroot/* uudecode.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "respond.h" #include "artio.h" #include "mime.h" #include "term.h" #include "util2.h" #include "decode.h" #include "INTERN.h" #include "uudecode.h" #include "uudecode.ih" int uue_prescan(bp, filenamep, partp, totalp) char* bp; char** filenamep; int* partp; int* totalp; { char* s; char* tmpfilename; int tmppart, tmptotal; if (strnEQ(bp, "begin ", 6) && isdigit(bp[6]) && isdigit(bp[7]) && isdigit(bp[8]) && (bp[9] == ' ' || (bp[6] == '0' && isdigit(bp[9]) && bp[10] == ' '))) { if (*partp == -1) { *filenamep = NULL; *partp = 1; *totalp = 0; } return 1; } if (strnEQ(bp,"section ",8) && isdigit(bp[8])) { s = bp + 8; tmppart = atoi(s); if (tmppart == 0) return 0; while (isdigit(*s)) s++; if (strnEQ(s, " of ", 4)) { /* "section N of ... of file F ..." */ for (s += 4; *s && strnNE(s," of file ",9); s++) ; if (!*s) return 0; s += 9; tmpfilename = s; s = index(s, ' '); if (!s) return 0; *s = '\0'; *filenamep = tmpfilename; *partp = tmppart; *totalp = 0; return 1; } if (*s == '/' && isdigit(s[1])) { /* "section N/M file F ..." */ tmptotal = atoi(s); while (isdigit(*s)) s++; while (*s && isspace(*s)) s++; if (tmppart > tmptotal || strnNE(s,"file ",5)) return 0; tmpfilename = s+5; s = index(tmpfilename, ' '); if (!s) return 0; *s = '\0'; *filenamep = tmpfilename; *partp = tmppart; *totalp = tmptotal; return 1; } } if (strnEQ(bp, "POST V", 6)) { /* "POST Vd.d.d F (Part N/M)" */ s = index(bp+6, ' '); if (!s) return 0; tmpfilename = s+1; s = index(tmpfilename, ' '); if (!s || strnNE(s, " (Part ", 7)) return 0; *s = '\0'; s += 7; tmppart = atoi(s); while (isdigit(*s)) s++; if (tmppart == 0 || *s++ != '/') return 0; tmptotal = atoi(s); while (isdigit(*s)) s++; if (tmppart > tmptotal || *s != ')') return 0; *filenamep = tmpfilename; *partp = tmppart; *totalp = tmptotal; return 1; } if (strnEQ(bp, "File: ", 6)) { /* "File: F -- part N of M -- ... */ tmpfilename = bp+6; s = index(tmpfilename, ' '); if (!s || strnNE(s, " -- part ", 9)) return 0; *s = '\0'; s += 9; tmppart = atoi(s); while (isdigit(*s)) s++; if (tmppart == 0 || strnNE(s, " of ", 4)) return 0; s += 4; tmptotal = atoi(s); while (isdigit(*s)) s++; if (tmppart > tmptotal || strnNE(s, " -- ", 4)) return 0; *filenamep = tmpfilename; *partp = tmppart; *totalp = tmptotal; return 1; } if (strnEQ(bp, "[Section: ", 10)) { /* "[Section: N/M File: F ..." */ s = bp + 10; tmppart = atoi(s); if (tmppart == 0) return 0; while (isdigit(*s)) s++; tmptotal = atoi(++s); while (isdigit(*s)) s++; while (*s && isspace(*s)) s++; if (tmppart > tmptotal || strnNE(s, "File: ", 6)) return 0; tmpfilename = s+6; s = index(tmpfilename, ' '); if (!s) return 0; *s = '\0'; *filenamep = tmpfilename; *partp = tmppart; *totalp = tmptotal; return 1; } if (*filenamep && *partp > 0 && *totalp > 0 && *partp <= *totalp && (strnEQ(bp,"BEGIN",5) || strnEQ(bp,"--- BEGIN ---",12) || (bp[0] == 'M' && strlen(bp) == UULENGTH))) { /* Found the start of a section of uuencoded data * and have the part N of M information. */ return 1; } if (strncaseEQ(bp, "x-file-name: ", 13)) { for (s = bp + 13; *s && !isspace(*s); s++) ; *s = '\0'; safecpy(msg, bp+13, sizeof msg); *filenamep = msg; return 0; } if (strncaseEQ(bp, "x-part: ", 8)) { tmppart = atoi(bp+8); if (tmppart > 0) *partp = tmppart; return 0; } if (strncaseEQ(bp, "x-part-total: ", 14)) { tmptotal = atoi(bp+14); if (tmptotal > 0) *totalp = tmptotal; return 0; } return 0; } int uudecode(ifp, state) FILE* ifp; int state; { static FILE* ofp = NULL; static int line_length; char lastline[UULENGTH+1]; char* filename; char* p; if (state == DECODE_DONE) { all_done: if (ofp) { fclose(ofp); ofp = NULL; } return state; } while (ifp? fgets(buf, sizeof buf, ifp) : readart(buf, sizeof buf)) { if (!ifp && mime_EndOfSection(buf)) break; p = strchr(buf, '\r'); if (p) { p[0] = '\n'; p[1] = '\0'; } switch (state) { case DECODE_START: /* Looking for start of uuencoded file */ case DECODE_MAYBEDONE: if (strnNE(buf, "begin ", 6)) break; /* skip mode */ p = buf + 6; while (*p && !isspace(*p)) p++; while (*p && isspace(*p)) p++; filename = p; while (*p && (!isspace(*p) || *p == ' ')) p++; *p = '\0'; if (!*filename) return DECODE_ERROR; filename = decode_fix_fname(filename); /* Create output file and start decoding */ ofp = fopen(filename, FOPEN_WB); if (!ofp) return DECODE_ERROR; printf("Decoding %s\n", filename); termdown(1); state = DECODE_SETLEN; break; case DECODE_INACTIVE: /* Looking for uuencoded data to resume */ if (*buf != 'M' || strlen(buf) != line_length) { if (*buf == 'B' && strnEQ(buf, "BEGIN", 5)) state = DECODE_ACTIVE; break; } state = DECODE_ACTIVE; /* FALL THROUGH */ case DECODE_SETLEN: line_length = strlen(buf); state = DECODE_ACTIVE; /* FALL THROUGH */ case DECODE_ACTIVE: /* Decoding data */ if (*buf == 'M' && strlen(buf) == line_length) { uudecodeline(buf, ofp); break; } if ((int)strlen(buf) > line_length) { state = DECODE_INACTIVE; break; } /* May be nearing end of file, so save this line */ strcpy(lastline, buf); /* some encoders put the end line right after the last M line */ if (strnEQ(buf, "end", 3)) goto end; else if (*buf == ' ' || *buf == '`') state = DECODE_LAST; else state = DECODE_NEXT2LAST; break; case DECODE_NEXT2LAST:/* May be nearing end of file */ if (strnEQ(buf, "end", 3)) goto end; else if (*buf == ' ' || *buf == '`') state = DECODE_LAST; else state = DECODE_INACTIVE; break; case DECODE_LAST: /* Should be at end of file */ if (strnEQ(buf, "end", 3) && isspace(buf[3])) { /* Handle that last line we saved */ uudecodeline(lastline, ofp); end: if (ofp) { fclose(ofp); ofp = NULL; } state = DECODE_MAYBEDONE; } else state = DECODE_INACTIVE; break; case DECODE_DONE: break; } } if (state == DECODE_DONE || state == DECODE_MAYBEDONE) goto all_done; return DECODE_INACTIVE; } #define DEC(c) (((c) - ' ') & 077) /* Decode a uuencoded line to 'ofp' */ static void uudecodeline(line, ofp) char* line; FILE* ofp; { int c, len; /* Calculate expected length and pad if necessary */ if ((len = ((DEC(line[0]) + 2) / 3) * 4) > UULENGTH) len = UULENGTH; for (c = strlen(line) - 1; c <= len; c++) line[c] = ' '; len = DEC(*line++); while (len) { c = DEC(*line) << 2 | DEC(line[1]) >> 4; putc(c, ofp); if (--len) { c = DEC(line[1]) << 4 | DEC(line[2]) >> 2; putc(c, ofp); if (--len) { c = DEC(line[2]) << 6 | DEC(line[3]); putc(c, ofp); len--; } } line += 4; } } trn-4.0-test77/uudecode.h0000644000000000000000000000046107113133016013753 0ustar rootroot/* uudecode.h */ /* This software is copyrighted as detailed in the LICENSE file. */ /* Length of a normal uuencoded line, including newline */ #define UULENGTH 62 /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int uue_prescan _((char*,char**,int*,int*)); int uudecode _((FILE*,int)); trn-4.0-test77/uudecode.ih0000644000000000000000000000030307113133016014117 0ustar rootroot/* uudecode.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static void uudecodeline _((char*,FILE*)); trn-4.0-test77/wildmat.c0000644000000000000000000001114107113133016013607 0ustar rootroot/* $Revision: 1.1 $ ** ** Do shell-style pattern matching for ?, \, [], and * characters. ** Might not be robust in face of malformed patterns; e.g., "foo[a-" ** could cause a segmentation violation. It is 8bit clean. ** ** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. ** Rich $alz is now . ** April, 1991: Replaced mutually-recursive calls with in-line code ** for the star character. ** ** Special thanks to Lars Mathiesen for the ABORT code. ** This can greatly speed up failing wildcard patterns. For example: ** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* ** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 ** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 ** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without ** the ABORT, then it takes 22310 calls to fail. Ugh. The following ** explanation is from Lars: ** The precondition that must be fulfilled is that DoMatch will consume ** at least one character in text. This is true if *p is neither '*' nor ** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic ** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With ** FALSE, each star-loop has to run to the end of the text; with ABORT ** only the last one does. ** ** Once the control of one instance of DoMatch enters the star-loop, that ** instance will return either TRUE or ABORT, and any calling instance ** will therefore return immediately after (without calling recursively ** again). In effect, only one star-loop is ever active. It would be ** possible to modify the code to maintain this context explicitly, ** eliminating all recursive calls at the cost of some complication and ** loss of clarity (and the ABORT stuff seems to be unclear enough by ** itself). I think it would be unwise to try to get this into a ** released version unless you have a good test data base to try it out ** on. */ #include #include "config.h" #include "config2.h" #include "wildmat.h" #include "wildmat.ih" #define ABORT -1 /* What character marks an inverted character class? */ #define NEGATE_CLASS '^' /* Is "*" a common pattern? */ #define OPTIMIZE_JUST_STAR /* Do tar(1) matching rules, which ignore a trailing slash? */ #undef MATCH_TAR_PATTERN /* ** Match text and p, return TRUE, FALSE, or ABORT. */ static int DoMatch(text, p) register char* text; register char* p; { register int last; register int matched; register int reverse; for ( ; *p; text++, p++) { if (*text == '\0' && *p != '*') return ABORT; switch (*p) { case '\\': /* Literal match with following character. */ p++; /* FALLTHROUGH */ default: if (*text != *p) return FALSE; continue; case '?': /* Match anything. */ continue; case '*': while (*++p == '*') /* Consecutive stars act just like one. */ continue; if (*p == '\0') /* Trailing star matches everything. */ return TRUE; while (*text) if ((matched = DoMatch(text++, p)) != FALSE) return matched; return ABORT; case '[': reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; if (reverse) /* Inverted character class. */ p++; for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p) /* This next line requires a good C compiler. */ if (*p == '-' ? *text <= *++p && *text >= last : *text == *p) matched = TRUE; if (matched == reverse) return FALSE; continue; } } #ifdef MATCH_TAR_PATTERN if (*text == '/') return TRUE; #endif /* MATCH_TAR_ATTERN */ return *text == '\0'; } /* ** User-level routine. Returns TRUE or FALSE. */ int wildmat(text, p) char* text; char* p; { #ifdef OPTIMIZE_JUST_STAR if (p[0] == '*' && p[1] == '\0') return TRUE; #endif /* OPTIMIZE_JUST_STAR */ return DoMatch(text, p) == TRUE; } #ifdef TEST #include #endif #ifdef TEST int main() { /* Yes, we use gets not fgets. Sue me. */ extern char* gets(); char p[80]; char text[80]; printf("Wildmat tester. Enter pattern, then strings to test.\n"); printf("A blank line gets prompts for a new pattern; a blank pattern\n"); printf("exits the program.\n"); for ( ; ; ) { printf("\nEnter pattern: "); (void)fflush(stdout); if (gets(p) == NULL || p[0] == '\0') break; for ( ; ; ) { printf("Enter text: "); (void)fflush(stdout); if (gets(text) == NULL) exit(0); if (text[0] == '\0') /* Blank line; go back and get a new pattern. */ break; printf(" %s\n", wildmat(text, p) ? "YES" : "NO"); } } return 0; } #endif /* TEST */ trn-4.0-test77/wildmat.h0000644000000000000000000000022707113133016013617 0ustar rootroot/* wildmat.h */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ int wildmat _((char*,char*)); #ifdef TEST int main _((void)); #endif trn-4.0-test77/wildmat.ih0000644000000000000000000000027407113133016013772 0ustar rootroot/* wildmat.ih */ /* This software is copyrighted as detailed in the LICENSE file. */ /* DON'T EDIT BELOW THIS LINE OR YOUR CHANGES WILL BE LOST! */ static int DoMatch _((char*,char*)); trn-4.0-test77/msdos/config.sh0000644000000000000000000001005507113133243014735 0ustar rootroot#! /bin/sh # # This file was hand-generated, since Configure doesn't run under msdos. It # holds all the definitions for configuring trn. You should only modify # the values commented at the start of this file. To make use of this # file you need to have a bourne shell installed (/bin/sh) and various # un*x utilities, such as sed and awk. You also need a un*x compatible # make utility that understands VPATH, such as dmake. # srcdir='..' vincludes='-I..' vpath='..' cc='bcc' ccflags='-WX -ml -dc -w-par -w-stu -w-pia -DDEBUG' cppflags='' ldflags='-v -WX' lkflags='/c -v -Txe -Lc:\bc4\lib' optimize='-v' cpplast='' cppminus='' cpprun='' cppstdin='cppstdin' prefix='c:/trn' bin='~~/bin' privlib='~~/lib' newslib='c:/tmp' usrinc='\bc4\include' sharpbang='#! ' startsh='#! /bin/sh' orgname='Total lack thereof' inewsloc='inews' pager='more' defeditor='edit' ispell_options='-x' ispell_prg='ispell' mailer='mail' mailfile='%~/%L' phost='.' phostcmd='hostname' prefshell='command.com' trn_init='FALSE' trn_select='TRUE' # The default distributions orgdist='none' locdist='local' citydist='ba' statedist='ca' multistatedist='none' cntrydist='usa' contdist='na' d_genauth='undef' d_nntp='undef' d_xthread="$d_nntp" # # You could alternately use the access.default file in the trn library to # change the default data source for all users. # servername='local' active='/usr/lib/news/active' newsspool='/usr/spool/news' d_acttimes='define' acttimes="$active.times" overviewdir='remote' threaddir='remote' # # You shouldn't need to change any of these. # cf_by='' cf_time='' myuname='' osname='MSDOS' osvers='' filexp='Rnfilexp' direntrytype='struct dirent' i_dirent='define' i_stdlib='define' i_string='define' strings="$usrinc/string.h" c='-c' n='' d_bsd='undef' d_eunice='undef' d_xenix='undef' eunicefix=':' Mcc='Mcc' awk='awk' basename='basename' bash='bash' byacc='byacc' cat='cat' cp='cp' cpp='cpp' csh='csh' date='date' diff='diff' echo='echo' ed='ed' egrep='egrep' expr='expr' find='find' grep='grep' inews='inews' ispell='ispell' ksh='ksh' less='less' lint='lint' mail='mail' metamail='metamail' mhn='mhn' mkdir='mkdir' more='more' munpack='munpack' mv='mv' nroff='nroff' pg='pg' rm='rm' rmail='rmail' sed='sed' sendmail='sendmail' smail='smail' sort='sort' spell='spell' tail='tail' test='test' tr='tr' uname='uname' uniq='uniq' uuname='uuname' vi='vi' vspell='vspell' who='who' hint='hint' Id='Id' Log='Log' myactive="$active" binexp="$bin" installbin="$bin" gccversion='' contains="$grep" d_attribut='undef' d_ftime='define' aphostcmd='hostname' d_getdname='undef' d_gethname='undef' d_phostcmd='undef' d_resinit='undef' d_uname='undef' d_getpwent='undef' d_getcwd='define' d_getwd='undef' d_havetlib='define' termlib='' d_ignoreorg='undef' d_inews='define' d_internet='define' d_memcmp='define' d_memcpy='define' d_memset='define' d_mimeshow='undef' d_mimestore='undef' d_msdos='define' mimeshow="none" mimestore="none" d_mkdir='define' d_newsadm='undef' newsadmin='root' d_nolnbuf='undef' d_normsig='define' jobslib='' d_novoid='undef' void='' d_portable='define' d_rdchk='undef' d_rename='define' d_sigblock='undef' d_sighold='undef' d_sizet='undef' d_strccmp='undef' d_strchr='define' d_strftime='define' strftimec='' strftimeo='' d_libndir='undef' d_usendir='undef' libndir='' ndirc='' ndiro='' d_vfork='undef' usevfork='undef' d_voidsig='define' signal_t='void' d_dirnamlen='undef' i_ptem='undef' i_sysdir='undef' d_voidtty='undef' i_bsdioctl='undef' i_sysfilio='undef' i_sysioctl='undef' i_syssockio='undef' i_sysndir='undef' i_sgtty='undef' i_termio='undef' i_termios='undef' i_systime='undef' i_time='define' i_unistd='undef' i_vfork='undef' libc='' glibpth='' plibpth='' xlibpth='' libs='' installmansrc=' ' manext='1' mansrc=' ' mansrcexp=' ' mboxchar='F' d_berknames='undef' d_passnames='undef' d_usgnames='undef' nametype='other' newslibexp="$newslib" package='trn' spackage='Trn' installprivlib="$privlib" privlibexp="$privlib" rootid='0' so='so' shsharp='true' spitshell="$cat" sysman='/usr/man/man1' nm_opt='' runnm='false' usenm='false' incpath='' mips='' mips_type='' trn-4.0-test77/msdos/inews.def0000644000000000000000000000103307113133243014735 0ustar rootrootSTUB '16stub.exe' SEGMENTS _REAL_TEXT CLASS 'REALCODE' FIXED REAL PRELOAD _REAL_DATA CLASS 'REALDATA' FIXED REAL PRELOAD STACKSIZE 5120 IMPORTS WINSOCK.gethostbyname WINSOCK.socket WINSOCK.htons WINSOCK.connect WINSOCK.closesocket WINSOCK.ioctlsocket WINSOCK.recv WINSOCK.send WINSOCK.inet_addr WINSOCK.inet_ntoa WINSOCK.gethostname WINSOCK.getservbyname WINSOCK.WSAGetLastError WINSOCK.WSAStartup WINSOCK.WSACleanup WINSOCK.WSAAsyncSelect trn-4.0-test77/msdos/inews.lnk0000644000000000000000000000021607113133243014765 0ustar rootrootc0x.obj+ inews.obj+ env.obj+ nntpinit.obj+ nntpclient.obj+ nntpauth.obj+ util2.obj+ util3.obj inews.exe inews.map DPMI16+ EMUX+ CWL inews.def trn-4.0-test77/msdos/nntplist.def0000644000000000000000000000103307113133243015463 0ustar rootrootSTUB '16stub.exe' SEGMENTS _REAL_TEXT CLASS 'REALCODE' FIXED REAL PRELOAD _REAL_DATA CLASS 'REALDATA' FIXED REAL PRELOAD STACKSIZE 5120 IMPORTS WINSOCK.gethostbyname WINSOCK.socket WINSOCK.htons WINSOCK.connect WINSOCK.closesocket WINSOCK.ioctlsocket WINSOCK.recv WINSOCK.send WINSOCK.inet_addr WINSOCK.inet_ntoa WINSOCK.gethostname WINSOCK.getservbyname WINSOCK.WSAGetLastError WINSOCK.WSAStartup WINSOCK.WSACleanup WINSOCK.WSAAsyncSelect trn-4.0-test77/msdos/nntplist.lnk0000644000000000000000000000023607113133243015515 0ustar rootrootc0x.obj+ nntplist.obj+ nntpinit.obj+ nntpclient.obj+ nntpauth.obj+ wildmat.obj+ util2.obj+ util3.obj nntplist.exe nntplist.map DPMI16+ EMUX+ CWL nntplist.def trn-4.0-test77/msdos/trn-artc.def0000644000000000000000000000103307113133243015342 0ustar rootrootSTUB '16stub.exe' SEGMENTS _REAL_TEXT CLASS 'REALCODE' FIXED REAL PRELOAD _REAL_DATA CLASS 'REALDATA' FIXED REAL PRELOAD STACKSIZE 5120 IMPORTS WINSOCK.gethostbyname WINSOCK.socket WINSOCK.htons WINSOCK.connect WINSOCK.closesocket WINSOCK.ioctlsocket WINSOCK.recv WINSOCK.send WINSOCK.inet_addr WINSOCK.inet_ntoa WINSOCK.gethostname WINSOCK.getservbyname WINSOCK.WSAGetLastError WINSOCK.WSAStartup WINSOCK.WSACleanup WINSOCK.WSAAsyncSelect trn-4.0-test77/msdos/trn-artc.lnk0000644000000000000000000000022107113133243015366 0ustar rootrootc0x.obj+ trn-artc.obj+ nntpinit.obj+ nntpclient.obj+ nntpauth.obj+ util2.obj+ util3.obj trn-artc.exe trn-artc.map DPMI16+ EMUX+ CWL trn-artc.def trn-4.0-test77/msdos/trn.def0000644000000000000000000000103307113133243014413 0ustar rootrootSTUB '16stub.exe' SEGMENTS _REAL_TEXT CLASS 'REALCODE' FIXED REAL PRELOAD _REAL_DATA CLASS 'REALDATA' FIXED REAL PRELOAD STACKSIZE 5120 IMPORTS WINSOCK.gethostbyname WINSOCK.socket WINSOCK.htons WINSOCK.connect WINSOCK.closesocket WINSOCK.ioctlsocket WINSOCK.recv WINSOCK.send WINSOCK.inet_addr WINSOCK.inet_ntoa WINSOCK.gethostname WINSOCK.getservbyname WINSOCK.WSAGetLastError WINSOCK.WSAStartup WINSOCK.WSACleanup WINSOCK.WSAAsyncSelect trn-4.0-test77/msdos/trn.lnk0000644000000000000000000000122707113133243014446 0ustar rootrootc0x.obj+ addng.obj+ art.obj+ artio.obj+ artsrch.obj+ autosub.obj+ backpage.obj+ bits.obj+ cache.obj+ charsubst.obj+ datasrc.obj+ decode.obj+ edit_dist.obj+ env.obj+ final.obj+ hash.obj+ head.obj+ help.obj+ init.obj+ intrp.obj+ kfile.obj+ last.obj+ list.obj+ ng.obj+ ngdata.obj+ ngsrch.obj+ ngstuff.obj+ only.obj+ rcln.obj+ rcstuff.obj+ respond.obj+ rthread.obj+ rt-mt.obj+ rt-ov.obj+ rt-process.obj+ rt-page.obj+ rt-select.obj+ rt-util.obj+ rt-wumpus.obj+ search.obj+ sw.obj+ term.obj+ trn.obj+ util.obj+ util2.obj+ unship.obj+ uudecode.obj+ parsedate.obj+ nntpinit.obj+ nntpclient.obj+ nntpauth.obj+ nntp.obj+ popen.obj trn.exe trn.map DPMI16+ EMUX+ CWL trn.def trn-4.0-test77/os2/config.sh0000644000000000000000000000775307113133243014326 0ustar rootroot#! /bin/sh # # This file was hand-generated, since Configure doesn't run under os2. It # holds all the definitions for configuring trn. You should only modify # the values commented at the start of this file. To make use of this # file you need to have a bourne shell installed (/bin/sh) and various # un*x utilities, such as sed and awk. You also need a un*x compatible # make utility that understands VPATH, such as dmake. # srcdir='..' vincludes='-I..' vpath='..' cc='gcc' ccflags='-DDEBUG' cppflags='' ldflags='' lkflags='' optimize='-g' cpplast='' cppminus='' cpprun='' cppstdin='cppstdin' prefix='c:/trn' bin='~~/bin' privlib='~~/lib' newslib='c:/tmp' usrinc='\bc4\include' sharpbang='#! ' startsh='#! /bin/sh' orgname='Total lack thereof' inewsloc='inews' pager='more' defeditor='edit' ispell_options='-x' ispell_prg='ispell' mailer='mail' mailfile='%~/%L' phost='.' phostcmd='hostname' prefshell='command.com' trn_init='FALSE' trn_select='TRUE' # The default distributions orgdist='none' locdist='local' citydist='ba' statedist='ca' multistatedist='none' cntrydist='usa' contdist='na' d_genauth='undef' d_nntp='undef' d_xthread="$d_nntp" # # You could alternately use the access.default file in the trn library to # change the default data source for all users. # servername='local' active='/usr/lib/news/active' newsspool='/usr/spool/news' d_acttimes='define' acttimes="$active.times" overviewdir='remote' threaddir='remote' # # You shouldn't need to change any of these. # cf_by='' cf_time='' myuname='' osname='OS2' osvers='' filexp='Rnfilexp' direntrytype='struct dirent' i_dirent='define' i_stdlib='define' i_string='define' strings="$usrinc/string.h" c='-c' n='' d_bsd='undef' d_eunice='undef' d_xenix='undef' eunicefix=':' Mcc='Mcc' awk='awk' basename='basename' bash='bash' byacc='byacc' cat='cat' cp='cp' cpp='cpp' csh='csh' date='date' diff='diff' echo='echo' ed='ed' egrep='egrep' expr='expr' find='find' grep='grep' inews='inews' ispell='ispell' ksh='ksh' less='less' lint='lint' mail='mail' metamail='metamail' mhn='mhn' mkdir='mkdir' more='more' munpack='munpack' mv='mv' nroff='nroff' pg='pg' rm='rm' rmail='rmail' sed='sed' sendmail='sendmail' smail='smail' sort='sort' spell='spell' tail='tail' test='test' tr='tr' uname='uname' uniq='uniq' uuname='uuname' vi='vi' vspell='vspell' who='who' hint='hint' Id='Id' Log='Log' myactive="$active" binexp="$bin" installbin="$bin" gccversion='' contains="$grep" d_attribut='undef' d_ftime='define' aphostcmd='hostname' d_getdname='undef' d_gethname='undef' d_phostcmd='undef' d_resinit='undef' d_uname='undef' d_getpwent='undef' d_getcwd='define' d_getwd='undef' d_havetlib='define' termlib='' d_ignoreorg='undef' d_inews='define' d_internet='define' d_memcmp='define' d_memcpy='define' d_memset='define' d_mimeshow='undef' d_mimestore='undef' d_msdos='define' mimeshow="none" mimestore="none" d_mkdir='define' d_newsadm='undef' newsadmin='root' d_nolnbuf='undef' d_normsig='define' jobslib='' d_novoid='undef' void='' d_portable='define' d_rdchk='undef' d_rename='define' d_sigblock='undef' d_sighold='undef' d_sizet='undef' d_strccmp='undef' d_strchr='define' d_strftime='define' strftimec='' strftimeo='' d_libndir='undef' d_usendir='undef' libndir='' ndirc='' ndiro='' d_vfork='undef' usevfork='undef' d_voidsig='define' signal_t='void' d_dirnamlen='undef' i_ptem='undef' i_sysdir='undef' d_voidtty='undef' i_bsdioctl='undef' i_sysfilio='undef' i_sysioctl='undef' i_syssockio='undef' i_sysndir='undef' i_sgtty='undef' i_termio='undef' i_termios='undef' i_systime='undef' i_time='define' i_unistd='undef' i_vfork='undef' libc='' glibpth='' plibpth='' xlibpth='' libs='' installmansrc=' ' manext='1' mansrc=' ' mansrcexp=' ' mboxchar='F' d_berknames='undef' d_passnames='undef' d_usgnames='undef' nametype='other' newslibexp="$newslib" package='trn' spackage='Trn' installprivlib="$privlib" privlibexp="$privlib" rootid='0' so='so' shsharp='true' spitshell="$cat" sysman='/usr/man/man1' nm_opt='' runnm='false' usenm='false' incpath='' mips='' mips_type='' trn-4.0-test77/samples/mimecap0000644000000000000000000000057707113133243015021 0ustar rootroot# Xgif tends to load much faster than xv, so use it for .gif files. image/gif; xgif %s; description="A gif image" image/*; xv %s; description="A jpeg image" # Some other examples application/postscript; lpr %s; description="A Postscript File" application/applefile; recieveAppleSingle %s; compose="sendAppleSingle %s"; \ description="An Apple Macintosh file in AppleSingle format" trn-4.0-test77/samples/overview.fmt0000644000000000000000000000060207113133243016026 0ustar rootroot## overview.fmt: the format of the news overview database. ## Each line is either: ##
## or
:full ## ##
is a news article header. If ":full" appears, the header ## name will be prepended. ** Line-order is important! ** Subject: From: Date: Message-ID: References: Bytes: Lines: ## Many newsreaders get better performance if Xref is present. Xref:full trn-4.0-test77/samples/subscriptions0000644000000000000000000000011507113133243016301 0ustar rootrootgeneral news.announce.important news.announce.newusers news.software.readers trn-4.0-test77/samples/univ/top0000644000000000000000000000154707113133375015175 0ustar rootroot# This is a sample ~/.trn/univ/top file for controlling the Universal Selector # First, some groups to always keep an eye on news.announce.important #Some lists of newsgroups "All newsgroups" * "Computer groups" comp.* "WWW" *www* #This file isn't installed yet, but hopefully it will be soon # "Newsgroups by Hierarchies" %X/hier_groups # A file reference relative to the last file #(create a file "favorite" in the same directory as this file, containing # commands similar to this "top" file.) #"Favorite groups" :favorite # An individual article: #"Misc. Article" $v1 15000 news.software.readers #Include all articles with scores above 5 in news.software.*, except #for articles in news.software.nntp: #$vg 5 news.software.* !news.software.nntp #A URL reference (probably obsolete) #"URL strn.top" URL:http://www.zynet.com/~caadams/strnwww/strn.top end group trn-4.0-test77/samples/univ/hier_groups0000644000000000000000000002347407113133375016724 0ustar rootroot# This file is mostly a demo, but it could become more usable later. #Suggestions for improvement are welcome. #Last update: 12/06/93 begin group "All newsgroups" * "alt (alternative newsgroups)" ->alt "comp (computer-related)" ->comp "misc (miscellaneous topics)" ->misc "news (USENET maintenance/discussion)" ->news "rec (recreational/hobby)" ->rec "sci (science)" ->sci "soc (social)" ->soc "talk (controversial/political topics)" ->talk "bionet (biology related)" ->bionet "bit (BITNET)" ->bit "biz (business)" ->biz "clari (Clarinet: newswire feeds)" ->clari "gnu (GNU project software and discussion)" ->gnu "info (mostly moderated mailing lists)" ->info "k12 (Kindergarten--12th grade education)" ->k12 "vmsnet (VAX/VMS interests)" ->vmsnet "Regional hierarchies" ->regional "Other hierarchies" ->other_hier >:alt "alt.*" alt.* "alt.bbs.* (Bulletin Board Systems)" alt.bbs.* "alt.binaries.* (mostly picture or sound files)" alt.binaries.* "alt.books.*" alt.books.* "alt.comics.*" alt.comics.* "alt.comp.* (an altnet imitation of comp.*)" alt.comp.* "alt.culture.* (cultures, organized geographically)" alt.culture.* "alt.cyberpunk.*, alt.cyberspace" alt.cyberpunk alt.cyberpunk.* alt.cyberspace "alt.fan.* (for fans and anti-fans)" alt.fan.* "alt.flame.* (heated discussions)" alt.flame alt.flame.* "alt.folklore.* (stories and legends in various areas)" alt.folklore.* "alt.games.*" alt.games.* "alt.internet.* (information about internet services)" alt.internet.* "alt.music.*" alt.music.* "alt.personals.* (personal ads)" alt.personals.* "alt.politics.*" alt.politics.* "alt.pub.* (virtual gathering places)" alt.pub.* "alt.religion.* (mostly humorous groups)" alt.religion.* "alt.rock-n-roll.*" alt.rock-n-roll* "alt.sex.*" alt.sex alt.sex.* "alt.society.*" alt.society.* "alt.sources.* (source code for computer programs)" alt.sources.* "alt.sport.*, alt.sports.*" alt.sport.* alt.sports.* "alt.support.* (support groups)" alt.support alt.support.* "alt.sys.* (computer systems)" alt.sys.* "alt.tv.* (television shows)" alt.tv.* "Other alt.* groups" alt.* !alt.bbs.* !alt.binaries.* !alt.books.* !alt.comics.* !alt.comp.* !alt.culture.* !alt.cyberpunk !alt.cyberpunk.* !alt.cyberspace !alt.fan.* !alt.flame !alt.flame.* !alt.folklore.* !alt.games.* !alt.internet.* !alt.music.* !alt.personals.* !alt.politics.* !alt.pub.* !alt.religion.* !alt.rock-n-roll.* !alt.sex !alt.sex.* !alt.society.* !alt.sources.* !alt.sport.* !alt.sports.* !alt.support !alt.support.* !alt.sys.* !alt.tv.* >:comp "comp.*" comp.* "comp.ai.* (Artificial Intelligence)" comp.ai comp.ai.* "comp.binaries.* (binary files for specific systems)" comp.binaries.* "comp.databases.*" comp.databases comp.databases.* "comp.dcom.* (data communications)" comp.dcom.* "comp.graphics.*" comp.graphics comp.graphics.* "comp.infosystems.* (WWW,wais,gopher,etc...)" comp.infosystems.* "comp.lang.* (languages)" comp.lang.* "comp.mail.* (electronic mail)" comp.mail.* "comp.org.* (computer related organizations)" comp.org.* "comp.os.* (operating systems)" ->comp.os "comp.protocols.* (communications protocols)" comp.protocols.* "comp.security.*" comp.security.* "comp.society.*" comp.society.* "comp.soft-sys.* (software systems)" comp.soft-sys.* "comp.sources.* (source code postings)" comp.sources.* "comp.std.* (standards)" comp.std.* "comp.sys.* (systems by hardware (PC,Mac,Sun,...))" ->comp.sys "comp.text.* (text processing)" comp.text comp.text.* "comp.theory.*" comp.theory comp.theory.* "comp.unix.* (UNIX(tm) operating system topics)" comp.unix.* "comp.windows.* (windowing systems)" comp.windows.* "Other comp.* groups" comp.* !comp.ai !comp.ai.* !comp.binaries.* !comp.databases !comp.databases.* !comp.dcom.* !comp.graphics !comp.graphics.* !comp.infosystems.* !comp.lang.* !comp.mail.* !comp.org.* !comp.os.* !comp.protocols.* !comp.security.* !comp.society.* !comp.soft-sys.* !comp.sources.* !comp.std.* !comp.sys.* !comp.text !comp.text.* !comp.theory !comp.theory.* !comp.unix.* !comp.windows.* >:comp.os "comp.os.*" comp.os.* "comp.os.386bsd.* (free BSD UNIX topics)" comp.os.386bsd.* "comp.os.linux.* (free UNIX by Linus T.)" comp.os.linux comp.os.linux.* "comp.os.ms-windows.* (Microsoft Windows and NT)" comp.os.ms-windows.* "comp.os.msdos.* (other PC operating systems)" comp.os.msdos.* "comp.os.os2.* (IBM's OS/2)" comp.os.os2.* "Other comp.os.* groups" comp.os.* !comp.os.386bsd.* !comp.os.linux !comp.os.linux.* !comp.os.ms-windows.* !comp.os.msdos.* !comp.os.os2.* >:comp.sys "comp.sys.*" comp.sys.* "comp.sys.acorn.*" comp.sys.acorn comp.sys.acorn.* "comp.sys.amiga.*" comp.sys.amiga.* "comp.sys.apple2.*" comp.sys.apple2.* "comp.sys.atari.*" comp.sys.atari.* "comp.sys.dec.* (Digital Equipment Corporation)" comp.sys.dec comp.sys.dec.micro "comp.sys.hp.* (Hewlett-Packard)" comp.sys.hp comp.sys.hp.* "comp.sys.ibm.* (IBM PC and clones)" comp.sys.ibm.* "comp.sys.mac.* (Apple Macintosh)" comp.sys.mac.* "comp.sys.newton.* (Apple Newton)" comp.sys.newton.* "comp.sys.next.* (NeXT machines/software)" comp.sys.next.* "comp.sys.sgi.* (Silicon Graphics)" comp.sys.sgi.* "comp.sys.sun.* (Sun Microsystems)" comp.sys.sun.* "Other comp.sys.* groups" comp.sys.* !comp.sys.acorn !comp.sys.acorn.* !comp.sys.amiga.* !comp.sys.apple2.* !comp.sys.atari.* !comp.sys.dec !comp.sys.dec.* !comp.sys.hp !comp.sys.hp.* !comp.sys.ibm.* !comp.sys.mac.* !comp.sys.next.* !comp.sys.sgi.* !comp.sys.sun.* >:misc "misc.*" misc.* "misc.consumers.*" misc.consumers misc.consumers.* "misc.forsale.*" misc.forsale misc.forsale.* "misc.invest.*" misc.invest misc.invest.* "misc.jobs.*" misc.jobs misc.jobs.* "misc.legal.*" misc.legal misc.legal.* "Other misc.* groups" misc.* !misc.consumers.* !misc.forsale !misc.forsale.* !misc.invest !misc.invest.* !misc.jobs !misc.jobs.* !misc.legal !misc.legal.* >:news "news.*" news.* "news.admin.* (USENET administration)" news.admin.* "news.announce.* (important information)" news.announce.* "news.software.* (USENET software)" news.software.* "Other news.* groups" news.* !news.admin.* !news.announce.* !news.software.* >:rec "rec.*" rec.* "rec.arts.*" ->rec.arts "rec.audio.*" rec.audio rec.audio.* "rec.autos.* (automobiles)" rec.autos rec.autos.* "rec.aviation.*" rec.aviation.* "rec.bicycles.*" rec.bicycles.* "rec.crafts.*" rec.crafts.* "rec.food.* (food and drink)" rec.food.* "rec.games.*" ->rec.games "rec.humor.*" rec.humor rec.humor.* "rec.models.*" rec.models.* "rec.motorcycles.*" rec.motorcycles rec.motorcycles.* "rec.music.*" rec.music.* "rec.pets.*" rec.pets rec.pets.* "rec.radio.*" rec.radio.* "rec.sport.*" rec.sport.* "rec.travel.*" rec.travel rec.travel.* "rec.video.*" rec.video rec.video.* "Other rec.* groups" rec.* !rec.arts.* !rec.arts.* !rec.audio !rec.audio.* !rec.autos !rec.autos.* !rec.aviation.* !rec.bicycles.* !rec.crafts.* !rec.food.* !rec.games.* !rec.humor !rec.humor.* !rec.models.* !rec.models.* !rec.motorcycles !rec.motorcycles.* !rec.music.* !rec.pets !rec.pets.* !rec.radio.* !rec.sport.* !rec.travel !rec.travel.* !rec.video !rec.video.* >:rec.arts "rec.arts.*" rec.arts.* "rec.arts.anime.*" rec.arts.anime rec.arts.anime.* "rec.arts.comics.*" rec.arts.comics.* "rec.arts.sf.* (Science Fiction)" rec.arts.sf.* "rec.arts.startrek.*" rec.arts.startrek.* "Other rec.arts.* groups" rec.arts.* !rec.arts.anime !rec.arts.anime.* !rec.arts.comics.* !rec.arts.sf.* !rec.arts.startrek.* >:rec.games "rec.games.*" rec.games.* "rec.games.frp.* (Fantasy Role Playing)" rec.games.frp.* "rec.games.mud.* (Multi-User Dungeons)" rec.games.mud.* "rec.games.video.*" rec.games.video.* "Other rec.games.* groups" rec.games.* !rec.games.frp.* !rec.games.mud.* !rec.games.video.* >:sci "sci.*" sci.* "sci.astro.* (astronomy)" sci.astro sci.astro.* "sci.bio.* (biology)" sci.bio sci.bio.* "sci.engr.* (engineering)" sci.engr sci.engr.* "sci.geo.* (geography)" sci.geo sci.geo.* "sci.math.* (mathematics)" sci.math sci.math.* "sci.med.* (medicine)" sci.med sci.med.* "sci.physics.* (physics)" sci.physics sci.physics.* "sci.space.*" sci.space sci.space.* "sci.stat.* (statistics)" sci.stat.* "Other sci.* groups" sci.* !sci.astro !sci.astro.* !sci.bio !sci.bio.* !sci.engr !sci.engr.* !sci.geo !sci.geo.* !sci.math !sci.math.* !sci.med !sci.med.* !sci.physics !sci.physics.* !sci.space !sci.space.* !sci.stat.* >:soc "soc.*" soc.* "soc.college.*" soc.college.* "soc.culture.* (cultures, mostly by country)" soc.culture.* "soc.religion.*" soc.religion.* "Other soc.* groups" soc.* !soc.college.* !soc.culture.* !soc.religion.* >:talk "talk.*" talk.* "talk.politics.*" talk.politics.* "talk.religion.*" talk.religion.* "Other talk.* groups" talk.* !talk.politics.* !talk.religion.* >:bionet "bionet.*" bionet.* "bionet.molbio.*" bionet.molbio.* "bionet.software.*" bionet.software bionet.software.* "Other bionet.* groups" bionet.* !bionet.molbio.* !bionet.software !bionet.software.* >:bit bit.* >:biz biz.* >:clari clari.* >:gnu "gnu.*" gnu.* "gnu.emacs.*" gnu.emacs.* "gnu.g++.* (C++ compiler)" gnu.g++.* "gnu.gcc.* (ANSI C compiler)" gnu.gcc.* "Other gnu.* groups" gnu.* !gnu.emacs.* !gnu.g++.* !gnu.gcc.* >:info info.* >:k12 k12.* >:vmsnet vmsnet.* >:regional "aus (Australia)" ->aus. "de (Germany regional)" ->de. "fj (From Japan)" ->fj. "fr (France)" ->fr. "relcom (Russian-language groups)" ->relcom. "uk (United Kingdom)" ->uk. >:aus. aus.* >:de. de.* >:fj. fj.* >:fr. france >:relcom. "relcom.*" relcom.* "relcom.commerce.*" relcom.commerce.* "Other relcom.* groups" relcom.* !relcom.commerce.* >:uk. uk.* >:other_hier * !alt.* !comp.* !misc.* !news.* !rec.* !sci.* !soc.* !talk.* !bionet.* !bit.* !biz.* !clari.* !gnu.* !info.* !k12.* !vmsnet.* !aus.* !de.* !fj.* !fr.* !relcom.* !uk.* end group trn-4.0-test77/support/Score.pl0000644000000000000000000000476607113133243015147 0ustar rootroot# Score # # Functions for scoring articles externally ala trn4. require 5; %scores = (); $| = 1; $HOME = $ENV{HOME}; $FILTERDIR = $ENV{FILTERDIR} || "$HOME/News/Filters"; $debug = $ENV{FILTER_DEBUG}; sub next_cmd { my ($line, $cmd, $arg); my ($dummy); chomp ($line = <>); $debug && print LOG "recv: `", $line, "'\n"; ($cmd, $arg) = split (/\s+/, $line, 2); ($cmd, $arg); } sub article { my ($fields) = @_; my %art; my ($artid, $subj, $from, $date, $msgid, $refs, $bytes, $lines, $xref); ($artid, $subj, $from, $date, $msgid, $refs, $bytes, $lines, $xref) = split (/\t/, $fields); $debug && print STDERR "$artid..." if ($artid % 50 == 0); $scores{$artid} = 0; %art = ( "number" => $artid, "subject" => $subj, "from" => $from, "date" => $date, "message-id" => $msgid, "references" => $refs, "bytes" => $bytes, "lines" => $lines, "xref" => $xref ); \%art; } sub score_art { local ($art, $score) = @_; $scores{$art->{number}} += $score; } sub select_art { local ($art) = @_; $scores{$art->{number}} = 10000; } sub junk_art { local ($art) = @_; $scores{$art->{number}} = -10000; } sub thwack { my ($cmd, $arg); my ($art); my ($CurrentGroup); my ($num); while (($cmd, $arg) = &next_cmd) { if ($cmd eq "bye" || $cmd eq "") { exit; } elsif ($cmd eq "newsgroup") { &done if defined &done; $CurrentGroup = $arg; undef &local_score; undef @local_hdrs; undef &init; undef &done; do "$FILTERDIR/$CurrentGroup" if -T "$FILTERDIR/$CurrentGroup"; if (defined(&global_score) || defined(&local_score)) { # No need to sort or weed duplicates $_ = 'want'; $_ .= ' ' . join(' ', @global_hdrs) if defined(@global_hdrs); $_ .= ' ' . join(' ', @local_hdrs) if defined(@local_hdrs); $_ = 'overview' if $_ eq 'want'; print $_, "\n"; $debug && print LOG "\tsent: `$_'\n"; } else { print "skip\n"; $debug && print LOG "\tsent: `skip'\n"; } } elsif ($cmd eq "art") { $art = &article ($arg); &global_score ($art) if defined &global_score; &local_score ($art) if defined &local_score; } elsif ($cmd eq "scores") { $| = 1; foreach $num (keys %scores) { print "$num $scores{$num}\n"; $debug && print LOG "\tsent: `$num $scores{$num}'\n"; } undef %scores; print "done\n"; } else { $debug && print LOG "filter: received unknown command `$cmd' from newsreader\n"; } } } 1; trn-4.0-test77/support/filter0000644000000000000000000000043607113133243014735 0ustar rootroot#!/usr/bin/perl -- -*- Perl -*- require 5; require "Score.pl"; $debug = $ENV{FILTER_DEBUG}; if ($debug) { open (LOG, ">/tmp/filter.log"); select LOG; $| = 1; select STDOUT; print LOG "filter process starting\n"; } do "$FILTERDIR/global"; &thwack; $debug && close LOG; trn-4.0-test77/support/filter.README0000644000000000000000000002021607204617211015672 0ustar rootroot1 Oct 1996 ------------------------------ This file describes how to enable modular article filtering in trn. This is scanty documentation, and you will probably have questions that can be answered only by asking me or looking in the code. The code that's provided allows you to filter and score articles using Perl 5.000 and up. Some code for filtering articles in Tcl is also supplied. It's important to note, however, that using a different language to filter articles is a very simple matter, and requires no additional changes to the trn source. Technical details on how trn communicates with an external filtering process are at . 1. Before compiling, make sure that USE_FILTER is #defined in common.h and that you've told Configure that you want strn's scan and score features (define at least SCORE in your config.h file). These are presently turned on by default in trn4. Defining FILTER_DEBUG will produce voluminous diagnostic output in /tmp/filter.log, so don't do this unless you are really convinced you need to monitor the internal machinations of the filtering engine. If you go ahead and define this for some peculiar reason, you will have to remove /tmp/filter.log very frequently, since trn will never remove it on its own. 2. Compile trn. 3. If using the Perl filter script, make sure that support/Score.pl is somewhere in your Perl library path. (No such library is needed if you are using the Tcl filter.) 4. Copy support/filter (or support/filter.tcl) into your ~/.trn directory, make sure the interpreter's path is in the #! line, and make sure the script is executable. Trn will run this script in a subprocess when necessary and use it to calculate scores. The FILTER environment variable allows you to override the location of this script if you don't like the default (of %+/filter). You're all set. Now you'll need to write code to tell trn how you want articles to be scored. The rest of this document assumes that you are using Perl. The Tcl filter follows the same general model, however, so if you can follow this description you shouldn't have much trouble writing Tcl code. HOW TO WRITE FILTER SUBROUTINES ------------------------------- When trn calls upon the filter script to filter articles for, say, megabozo.general, the script checks your ~/News/Filters directory for a `megabozo.general' file containing filtering commands. That file should contain the following code: @local_hdrs = qw( subject from xref ); # the article headers we want sub init { # initialization code ... } sub local_score { # calculate a score for articles in megabozo.general ... } sub done { # cleanup code ... } The only real requirement is that the subroutine `local_score' must be defined. Everything else is optional. When trn needs a score for an article in megabozo.general, your filter script will call `local_score' with one argument: a reference to a hash containing the overview data for that article. `local_score' should calculate a score for this article, and record that score via calls to these routines: score_art (ARTICLE, SCORE) ARTICLE is an article object (the argument passed to local_score and global_score). SCORE is added to the current running score for the ARTICLE. E.g. the following calls would result in assigning a score of +150 to $article: score_art ($article, +200); score_art ($article, -50); select_art ARTICLE Set the current score for ARTICLE to an obscenely high value. Presently 10,000 (this should be adjustable). Obliterates whatever score ARTICLE previously held. junk_art ARTICLE Set the current score for ARTICLE to an obscenely dismal value. Presently 10,000. Obliterates whatever score ARTICLE previously held. In each case, ARTICLE is a reference to a hash which contains the overview data for that article. When `local_score' is called, it is passed a reference to this hash; you should use this reference whenever referring to the article. These functions may be called as many times as you like on the same article. No effects are permanent until local_score exits. If you know in advance that the code for a particular newsgroup will score articles based on only one or two headers, put the names of those headers in the @local_hdrs variable, like so: @local_hdrs = qw( subject xref ); Doing so will speed up the filtering mechanism. If you don't do this, trn will simply supply you with the complete overview record for each article. Here is an example. Suppose you want to filter comp.lang.c like so: * Any article crossposted to more than three groups gets a score of -50. * Any article crossposted to a non-comp.* group *also* gets a score of -50. This code would do that. Create this file and call it `~/News/Filters/comp.lang.c': # What headers are we interested in? @local_hdrs = qw( xref ); sub local_score { my ($article) = @_; my (@newsgroups); # Get a list of newsgroups from the Xref: header. @newsgroups = split (/\s+/, $article->{xref}); shift @newsgroups; # Articles crossposted to more than three groups get # -50. if (@newsgroups > 3) { score_art ($article, -50); } else { # Check each group in the @newsgroups array: if it's # not a comp.* group, score the article -50 then, too. foreach $n (@newsgroups) { next if $n =~ /^comp\./; score_art ($article, -50); last; } } } When you enter comp.lang.c, the scoring process will read the comp.lang.c file and call this `local_score' subroutine each time it scores an article. The `init' and `done' subroutines will be called upon newsgroup entry and exit, respectively. This makes it convenient to write filtering code that requires some kind of initialization or cleanup: for example, turning debugging on and off selectively for a particular group, or opening and closing DBM files referenced by your code. You can implement global scoring with `~/News/Filters/global': @global_hdrs = qw( subject from ); # headers wanted for every art sub global_score { # global scoring code here } The `global_score' subroutine will be called to calculate a score for every article you read. The location of these filtering scripts is defined by your FILTERDIR environment variable -- if this variable is not set, ~/News/Filters will be assumed. Note that percent escapes will *not* be honored by the FILTERDIR variable. (The reason is that percent escapes are all handled internally by the trn executable, but the local score files are managed independently by an external Perl process. Permitting the process to understand percent escapes is conceivable, but would be very complicated.) TCL FILTER DOCUMENTATION ------------------------ The mechanism for writing Tcl subroutines to filter your articles follows pretty much the same structure as the supplied Perl filter. The `filter.tcl' script defines the following procedures to make article scoring convenient: subject ARTICLE REGEXP from ARTICLE REGEXP date ARTICLE REGEXP message-id ARTICLE REGEXP references ARTICLE REGEXP bytes ARTICLE REGEXP lines ARTICLE REGEXP xref ARTICLE REGEXP Each of these fetches a header from the specified ARTICLE. If the optional argument REGEXP is supplied, it is matched against the contents of the desired header (case-insensitively), and 1 or 0 is returned depending on whether a match was found. If no REGEXP argument is supplied, the procedure merely returns the content of the header. header STRING ARTICLE Fetches the header whose name is STRING from the ARTICLE. For example, `subject $article' is the same thing as saying `header subject $article'. score_art ARTICLE SCORE select_art ARTICLE junk_art ARTICLE As in the Perl versions of these functions. For example, ~/News/Filters/news.software.readers might include code like the following: proc local_score { article } { if { [subject $article "rfc *1036"] && ! [from $article "nick knight"] } { select_art $article } } trn-4.0-test77/support/mhactive0000644000000000000000000000224507113133243015250 0ustar rootroot# feed this into perl eval 'exec perl -S $0 ${1+"$@"}' if $running_under_some_shell; # mhactive # Used to create an imitation "active" file for MH mail folders so that # trn can read them. To use this, create a news source like the following # in your .trn/access file and reference the mail ID in a group: # # [mail] # Active File = %`mhactive ~/Mail/active` # Spool Dir = ~/Mail # Active Times = none # # [group 2] # ID = mail # Newsrc = ~/Mail/mh.newsrc # # Adapted from Joe Edelman, 12 May 1995 by Raphael Manfredi open(TTY, '>/dev/tty'); select((select(TTY), $| = 1)[0]); print TTY "Building MH active file..."; ($active) = @ARGV; $active =~ s/^~/$ENV{'HOME'}/; die "Usage: mhactive active-file\n" unless $active; open(FILE, ">$active") || die "mhactive: can't create $active: $!\n"; foreach $folder (`folders -fast`) { chop($folder); opendir FOLDER, "$ENV{'HOME'}/Mail/$folder"; $first = 100000; $last = 0; MSG: foreach (readdir FOLDER) { /^\d*$/ or next MSG; $first = $_ if $_ < $first; $last = $_ if $_ > $last; } next if $last == 0; ($group = $folder) =~ s|/|.|g; print FILE "$group $last $first n\n"; } close FILE; print "$active\n"; print TTY "Done\n"; trn-4.0-test77/support/trnkill0000644000000000000000000000132007113133243015120 0ustar rootroot#!/bin/sh # trnkill - shell script to apply trn KILL files in the background # 14 Mar 89 created for rn by Jim Olsen # 10 Sep 93 modified for trn 3 (or 2) by Wayne Davison # Options: -d debug mode -- you see all gory action as it happens. # Visit all newsgroups (if trn asks about anything else, just say no) export TRNMACRO RNMACRO TERM TRNMACRO=/tmp/trnkill$$ TERM=dumb # support for trn 2.x RNMACRO=$TRNMACRO OPTIONS='-q -s -T -t -x +X ++' trap 'rm -f $TRNMACRO; exit' 1 2 3 15 cat >$TRNMACRO <<'EOF' z %(%m=[nf]?.q^J:n)^(z^) ^m ^(z^) ^j ^(z^) EOF if test X$1 = X-d; then echo "z" | trn $OPTIONS else echo "z" | trn $OPTIONS >/dev/null 2>&1 fi rm -f $TRNMACRO exit 0 trn-4.0-test77/support/unipatch.c0000644000000000000000000000274307113133243015507 0ustar rootroot/* A filter to turn a unified diff into a degenerate context diff (no '!'s) for patch. Version 1.1. Author: davison@borland.com */ #include #define ERR(a) {fputs(a,stderr);exit(1);} #define NUM(x) {for(x=0;*cp<='9'&&*cp>='0';)x=x*10+*cp++-'0';ch= *cp++;} struct Ln {struct Ln *lk; char t; char s[1];} r,*h,*ln; char bf[2048],*cp,ch,*malloc(); long os,ol,ns,nl,ne,lncnt; main() { for(;;){ for(;;){ if(!fgets(bf,sizeof bf,stdin)) exit(0); lncnt++; if(!strncmp(bf,"@@ -",4)) break; fputs(bf,stdout); } ol=nl=1, cp=bf+4; NUM(os) if(ch==',') NUM(ol) if(*cp++!='+') goto bad; NUM(ns) if(ch==',') NUM(nl) if(*cp!='@') goto bad; r.lk=0, h= &r, ne=ns+nl-1; printf("***************\n*** %ld,%ld ****\n",os,os+ol-(os>0)); while(ol||nl){ if(!fgets(bf,sizeof bf,stdin)){ if(nl>2) ERR("Unexpected end of file.\n") strcpy(bf," \n"); } lncnt++; if(*bf=='\t'||*bf=='\n') ch=' ', cp=bf; else ch= *bf, cp=bf+1; switch(ch){ case'-':if(!ol--) goto bad; printf("- %s",cp); break; case'=':ch=' '; case' ':if(!ol--) goto bad; printf(" %s",cp); case'+':if(!nl--) goto bad; ln = (struct Ln*)malloc(sizeof(*ln)+strlen(cp)); if(!ln) ERR("Out of memory!\n") ln->lk=0, ln->t=ch, strcpy(ln->s,cp); h->lk=ln, h=ln; break; default: bad: fprintf(stderr,"Malformed unified diff at line %ld: ",lncnt); ERR(bf) } } printf("--- %ld,%ld ----\n",ns,ne); for(ln=r.lk;ln;ln=h){ printf("%c %s",ln->t,ln->s); h=ln->lk; free(ln); } } } trn-4.0-test77/support/tk/README0000644000000000000000000000044207113133375015026 0ustar rootrootTo use Tktrn after you compile with USE_TK enabled, you need to put all the files from this dir (except this README) into your ~/.trn dir. This install process should probably be improved somehow... You must link the trn executable to the name tktrn and run that to try the tk extensions. trn-4.0-test77/support/tk/tkinit0000644000000000000000000000464507113133375015404 0ustar rootroot#startup script for trntk #XXX Later have some automatic directory? if [info exists env(DOTDIR) ] { set script_dir $env(DOTDIR)/.trn } else { set script_dir ~/.trn } set win_pos_file $script_dir/tkwinpos set tcl_interactive 0 ############### REQUIRED ############### set ttk_keymod 0 #Required procedures ttk_addkey and ttk_finalize #Add the key to the trn input if it has an ASCII code. proc ttk_addkey {akey codekey} { global ttk_keys ttk_keymod if {$codekey == "120"} { append ttk_keys "n" } elseif {$akey != "{}"} {append ttk_keys $akey} } #quasi-replacement for idle handlers. #Count is for debugging--to make sure I'm not spinning wildly... set ttk_idlepending_cnt 0 set ttk_idlepending_list {} #Use the dolist copy so that items can reschedule themselves proc ttk_idlepending {} { global ttk_idlepending_cnt ttk_idlepending_list ttk_idle_flag incr ttk_idlepending_cnt set dolist $ttk_idlepending_list set ttk_idlepending_list {} set ttk_idle_flag 0 foreach val $dolist { eval $val } } proc ttk_idle_add {cmd} { global ttk_idlepending_list ttk_idle_flag lappend ttk_idlepending_list $cmd set ttk_idle_flag 1 } #Called just before deleting the TCL interpreter #Later?: add a procedure that is called a little before the very end. proc ttk_finalize {} { global win_pos_file # puts "Bye from Tk!" catch {TrnModule_savesizes $win_pos_file} } #Default debug procedure: does nothing, is overridden if debug is sourced proc dbg {txt} { } ############# END REQUIRED ############# #Default behavior, add ASCII code to trn input stream. #Consider key_handled variable--if set to eventID, no other handlers #for the key will be called. bind all {ttk_addkey "%A" "%k"} #Don't show the default toplevel window wm withdraw . #object-oriented setup source $script_dir/obtcl.tcl source $script_dir/TrnModule.tcl #Show debug window #source $script_dir/debug.tcl #Old article tree (*incompatable with object-oriented version*) #source $script_dir/atree.tcl #New article tree (with object-oriented stuff) source $script_dir/objatree.tcl #New score display (object-oriented) #source $script_dir/ScoreBar.tcl #ScoreBar .scorebar -toplevel 1 #load and set old window sizes--to disable, comment the next 3 commands TrnModule_loadsizes $win_pos_file #Give the window manager a chance to create decorations... update #...and then set the window positions and sizes TrnModule_setsizes trn-4.0-test77/support/tk/atree.tcl0000644000000000000000000003606207113133375015761 0ustar rootroot#atree.tcl # # Simple article tree implementation. # Begun sometime around July 1995. # For more information, contact caadams@ist.net # # [Jan. 96] This file is no longer being worked on--it is replaced by the # object-oriented version of the article tree. # # Todo: # * consistent naming, pref. within a common prefix (ouch) # * allow multiple simultaneous trees? # * Factor out generic tree-drawing from article stuff? # # Consider: # * separate canvas->virtual array rather than $vnode(canvid.virt) #Vague attempt at encapsulation: set atree_top ".atree_top" #catch needed in case window position code is not loaded catch { ttk_topwin_register $atree_top } trash #default units are pixels set tree_def(units) "" set tree_def(xsize) 30 set tree_def(ysize) 30 #default shapesize is the radius of a circle, or half the side of a square... set tree_def(shapesize) 9 #size (radius) of decorative dots. 0 means no dot set tree_def(dotsize) 0 #Number of grid units to leave blank for text on right side of tree. set tree_def(rightblank) 8 proc reset_tree_units {} { global tree tree_def set tree(units) $tree_def(units) set tree(xsize) $tree_def(xsize) set tree(ysize) $tree_def(ysize) set tree(xoffset) [expr $tree(xsize)/2] set tree(yoffset) [expr $tree(ysize)/2] set tree(shapesize) $tree_def(shapesize) set tree(dotsize) $tree_def(dotsize) set tree(rightblank) $tree_def(rightblank) } reset_tree_units #virtual node (vnode) item counter #Always increases, independent of canvas objects. #First vnode returned will be 1 set vnode_count 0 proc new_vnode {} { global vnode_count incr vnode_count return $vnode_count } toplevel $atree_top wm title $atree_top "Article Tree" set atree $atree_top.c proc atree_scroll_cmd {which a b} { $which set $a $b #dbg "atree_scroll_cmd" atree_visible_update } canvas $atree -relief sunken -borderwidth 2 \ -xscrollcommand "atree_scroll_cmd $atree_top.hscroll" \ -yscrollcommand "atree_scroll_cmd $atree_top.vscroll" \ -xscrollincrement $tree(xsize) \ -yscrollincrement $tree(ysize) proc set_canv_scrollsize {x y} { global atree tree set sreg {0 0} lappend sreg [expr $tree(xsize) * $x] [expr $tree(ysize) * $y] $atree configure -scrollregion $sreg } set_canv_scrollsize 1 1 ###Tree enabling/disabling toggle (XXX: could be done better) #default state is on set atree_enabled 1 proc atree_enabled_text_set {} { global atree_enabled atree_enabled_text if {$atree_enabled} { set atree_enabled_text "Article tree is ON" } else { set atree_enabled_text "Article tree is OFF" } } #Set the initial text atree_enabled_text_set button $atree_top.enable -text $atree_enabled_text \ -command { set atree_enabled [expr !$atree_enabled] wipetree atree_enabled_text_set $atree_top.enable configure -text $atree_enabled_text } pack $atree_top.enable -side top -fill x set msgidlabel_default "<>" set msgidlabel $msgidlabel_default label $atree_top.msgid -textvariable msgidlabel pack $atree_top.msgid -side top -fill x scrollbar $atree_top.hscroll -orient horiz -command "$atree xview" pack $atree_top.hscroll -side bottom -fill x scrollbar $atree_top.vscroll -command "$atree yview" pack $atree_top.vscroll -side right -fill y pack $atree -expand yes -fill both bind $atree <2> "$atree scan mark %x %y" bind $atree { $atree scan dragto %x %y # atree_visible_update } #calculate visible rectangle set atree_vis_minx -1 set atree_vis_miny -1 set atree_vis_maxx -1 set atree_vis_maxy -1 #old visibility set atree_vis2_minx -1 set atree_vis2_miny -1 set atree_vis2_maxx -1 set atree_vis2_maxy -1 proc atree_visible_update {} { global atree_vis_minx atree_vis_miny atree_vis_maxx atree_vis_maxy global atree_vis2_minx atree_vis2_miny atree_vis2_maxx atree_vis2_maxy atree_visible_region if {($atree_vis_minx != $atree_vis2_minx) || \ ($atree_vis_miny != $atree_vis2_miny) || \ ($atree_vis_maxx != $atree_vis2_maxx) || \ ($atree_vis_maxy != $atree_vis2_maxy)} { #dbg "vis:($atree_vis_minx,$atree_vis_miny)->($atree_vis_maxx,$atree_vis_maxy)" #do complex things here set atree_vis2_minx $atree_vis_minx set atree_vis2_miny $atree_vis_miny set atree_vis2_maxx $atree_vis_maxx set atree_vis2_maxy $atree_vis_maxy } } proc atree_visible_region {} { global atree tree global atree_vis_minx atree_vis_miny atree_vis_maxx atree_vis_maxy global ttk_tree_x ttk_tree_y set tmp [$atree xview] set x0 [lindex $tmp 0] set x1 [lindex $tmp 1] set xwidth [expr $ttk_tree_x + $tree(rightblank)] set atree_vis_minx [expr floor($x0*$xwidth)] set atree_vis_maxx [expr ceil($x1*$xwidth)] set tmp [$atree yview] set y0 [lindex $tmp 0] set y1 [lindex $tmp 1] set yheight $ttk_tree_y set atree_vis_miny [expr floor($y0*$yheight)] set atree_vis_maxy [expr ceil($y1*$yheight)] } #This doesn't really work, as the visibility bounds were meant #to find which elements *might* **possibly** be visible. proc atree_make_visible {x y} { global atree tree global atree_vis_minx atree_vis_miny atree_vis_maxx atree_vis_maxy # dbg "make_vis: enter ($x,$y)" atree_visible_update while {$x<$atree_vis_minx} { $atree xview scroll -1 pages atree_visible_update } while {$x>$atree_vis_maxx} { $atree xview scroll 1 pages atree_visible_update } while {$y<$atree_vis_miny} { $atree yview scroll -1 pages atree_visible_update } while {$y>$atree_vis_maxy} { $atree yview scroll 1 pages atree_visible_update } } #coordinates are graphical. proc draw_parent_line {ap x y px py} { global atree tree set xmid [expr $px + (($x-$px)/2)] set tmpline [$atree create line $x $y $xmid $y] $atree lower $tmpline #later: optional dot at joint. if {![$ap flag nextsibling]} { #If this is the last sibling, draw the attachment #to the parent article... set tmpline [$atree create line $px $py $xmid $py $xmid $y] $atree lower $tmpline #later: optional dot at joint. } } # draws the "cursor" for the current article. Might later have other # effect, or even allow choice between effects. set atree_cursoritem1 -1 set atree_cursoritem2 -1 set atree_cursorexists 0 #coordinates are grid coords. proc plot_cursor {gx gy} { global atree node_msgid tree atree_cursoritem1 atree_cursorexists set sx $tree(xsize) set sy $tree(ysize) if {$atree_cursorexists} { unplot_cursor } set x [expr $gx * $tree(xsize)] set y [expr $gy * $tree(ysize)] set atree_cursoritem1 [$atree create rectangle $x $y \ [expr $x+$sx] [expr $y+$sy] -fill lightgreen] $atree lower $atree_cursoritem1 set atree_cursorexists 1 #XXX Make this auto-scroll optional # atree_make_visible $gx $gy } proc unplot_cursor {} { global atree node_msgid tree global atree_cursoritem1 atree_cursoritem2 atree_cursorexists if {$atree_cursoritem1>=0} { $atree delete $atree_cursoritem1 } if {$atree_cursoritem2>=0} { $atree delete $atree_cursoritem2 } set atree_cursoritem1 -1 set atree_cursoritem2 -1 set atree_cursorexists 0 } #returns the canvas itemnumber for the shape proc tree_makeshape {ap gx gy} { global tree atree set s $tree(shapesize) set x [expr $gx * $tree(xsize) + $tree(xoffset)] set y [expr $gy * $tree(ysize) + $tree(yoffset)] set shape rectangle set fillcol white if {![$ap flag exists]} { set fillcol gray75 } if {![$ap flag unread]} { set shape oval } #XXX testing set score [$ap score] if {$score <= -3} { set fillcol LightBlue } if {$score <= -6} { set fillcol DarkBlue } if {$score >= 4} { set fillcol LightPink } if {$score >= 8} { set fillcol HotPink } set new [$atree create $shape [expr $x-$s] [expr $y-$s] \ [expr $x+$s] [expr $y+$s] -outline black -width 3 \ -fill $fillcol -tags node] return $new } proc mkNode {ap gx gy px py} { global vnode atree node_msgid tree set letter " " #Get a new virtual node set vn [new_vnode] #Set grid coordinates: set vnode($vn.x) $gx set vnode($vn.y) $gy set x [expr $gx * $tree(xsize) + $tree(xoffset)] set y [expr $gy * $tree(ysize) + $tree(yoffset)] #uncomment the next line to see approximately how much time is taken by #actually drawing the items... #return set new [tree_makeshape $ap $gx $gy] set vnode($new.virt) $vn set vnode($vn.shape) $new #Selection code is not very usable now... # if {[$ap flag selectedonly] && ![$ap flag selected]} { # $atree itemconfigure $new -stipple gray25 # $atree itemconfigure $new -fill blue # } if {1} { #redo the letter code soon set letter [$ap subjectletter] set newtxt [$atree create text $x $y -text $letter -fill black \ -tags node] set vnode($newtxt.virt) $vn set vnode($vn.txt) $newtxt } set id [$ap header msgid] set vnode($vn.id) $id #Mapping from ID to virtual node (can allow quick update) #XXX What if there are bad IDs or shared ones? set vnode($id.virt) $vn #XXX consider making the parent lines part of a vnode... if {($px>=0) && [$ap flag parent]} { set px [expr $px * $tree(xsize) + $tree(xoffset)] set py [expr $py * $tree(ysize) + $tree(yoffset)] draw_parent_line $ap $x $y $px $py } #if subject is different than parent, show it to the right. if {![$ap flag parentsubject]} { #set x to next node location over to right set x [expr $x + $tree(xsize) - $tree(shapesize)] set subj [$ap header subject] set subjtxt [$atree create text $x $y -text $subj -fill black \ -anchor w -tags node] set vnode($subjtxt.virt) $vn set vnode($vn.subjtxt) $subjtxt } } set atree_cached "" proc wipetree {} { global atree vnode node_msgid global atree_vis2_minx atree_vis2_miny atree_vis2_maxx atree_vis2_maxy global msgidlabel msgidlabel_default global atree_cached set atree_cached "" set msgidlabel $msgidlabel_default $atree delete all catch { if {[info exists node_msgid]} { unset node_msgid } if {[info exists vnode]} { unset vnode #For now, do not reuse the count # set vnode_count 0 } } trash set_canv_scrollsize 1 1 unplot_cursor set atree_vis2_minx -1 set atree_vis2_miny -1 set atree_vis2_maxx -1 set atree_vis2_maxy -1 } $atree bind node { set curNode [$atree find withtag current] if {$curNode == ""} { break } #This array index must exist for all node-tagged canvas items set tmp_vn $vnode($curNode.virt) set tmp_shapenode $vnode($tmp_vn.shape) $atree itemconfigure $tmp_shapenode -outline red set msgidlabel $vnode($tmp_vn.id) } $atree bind node { set curNode [$atree find withtag current] if {$curNode == ""} { break } #This array index must exist for all node-tagged canvas items set tmp_vn $vnode($curNode.virt) set tmp_shapenode $vnode($tmp_vn.shape) $atree itemconfigure $tmp_shapenode -outline black set msgidlabel $msgidlabel_default } ####NOTE: this procedure is *required* by the treeprepare command! proc tree_nodesize {ap} { set n 0 if {![$ap flag parentsubject]} { incr n 1 } return $n } ####NOTE: this procedure is *required* by the treechange command! proc tree_nodechanged {ap} { global vnode atree set id [$ap header msgid] if {[info exists vnode($id.virt)]} { set vn $vnode($id.virt) } else { #if there is no virtual node, then there is nothing to update return } # dbg "nodechanged: id |$id| vnode |$vn|" #XXX later see if replacing the shape is always necessary? set oldshape $vnode($vn.shape) set oldoutline [$atree itemcget $oldshape -outline] #delete the old shape $atree delete $oldshape unset vnode($oldshape.virt) set gx $vnode($vn.x) set gy $vnode($vn.y) set txt $vnode($vn.txt) #re-make the shape and relink it set new [tree_makeshape $ap $gx $gy] $atree itemconfigure $new -outline $oldoutline set vnode($new.virt) $vn set vnode($vn.shape) $new #put the shape below the txt label $atree lower $new $txt # dbg "nodechanged: oldshape |$oldshape| newshape |$new|" } #mangles ap proc draw_tree_main {ap x y px py} { while 1 { mkNode $ap $x $y $px $py # update_node $ap $x $y $px $py set ns [tree_nodesize $ap] if {[$ap flag child]} { set apchild [$ap copy] $apchild move child set y [draw_tree_main $apchild [expr $x+1] [expr $y+$ns] $x $y] rename $apchild "" } elseif {$ns == 0} { incr y 1 } else { incr y $ns } set tmp [$ap move sibling] if {$tmp != 1} { #return y-coord. if no sibling return $y } } } #Use a single global item so that we don't have to keep creating/destroying #article objects for every cursor plot. set trn_current_cursor_ap [trn_article] proc trn_current_cursor {} { global vnode trn_current_cursor_ap set ap $trn_current_cursor_ap set tmp [$ap setcurrent] if {$tmp>0} { set vn 0 set id [$ap header msgid] if {[info exists vnode($id.virt)]} { set vn $vnode($id.virt) } if {$vn>0} { plot_cursor $vnode($vn.x) $vnode($vn.y) } } } proc draw_article_tree {ap x y} { global tree ttk_tree_x ttk_tree_y atree_enabled global atree_cached #testing... global atree #Check to see if tree article is same as last one... if {$atree_enabled} { if {[$ap header msgid] == $atree_cached} { atree_visible_region #call update procedure for changed articles. $ap treechange } else { set tmpap [$ap copy] wipetree $ap treeprepare # set_canv_scrollsize [expr $ttk_tree_x+$tree(rightblank)] \ # [expr $ttk_tree_y+1] # atree_visible_region draw_tree_main $tmpap $x $y -1 -1 set atree_cached [$ap header msgid] #Delete the temporary article copy rename $tmpap "" #testing... $atree configure -scrollregion [$atree bbox all] } trn_current_cursor } } proc trn_draw_article_tree {x y} { global ttk_msgid set ap [trn_article] $ap setid $ttk_msgid draw_article_tree $ap $x $y rename $ap "" } #XXX move this around somewhere else later... proc endgroup {} { wipetree update idletasks } #XXX testing... #later handle 's' (scan mode), 't' thread selector in appropriate ways... #Within the current group, attempt to go to an article #(if the article is<=0, do nothing) proc trn_go_article {anum} { global ttk_keys if {$anum>0} { #if the article number is good... set trn_mode [trn_misc mode] #dbg "mode: $trn_mode" if {(($trn_mode == "t"))} { #In the thread tree, so leave the tree append ttk_keys "+" } if {(($trn_mode == "p") || ($trn_mode == "a") || ($trn_mode == "e") || ($trn_mode == "t"))} { #if we are in the newsgroup append ttk_keys $anum append ttk_keys "\n" } } } set trn_treeclick_ap [trn_article] $atree bind node <1> { global trn_treeclick_ap atree vnode set curNode [$atree find withtag current] if {$curNode == ""} { break } #This array index must exist for all node-tagged canvas items set tmp_vn $vnode($curNode.virt) $trn_treeclick_ap setid $vnode($tmp_vn.id) #the following is safe even if the id is not found] set anum [$trn_treeclick_ap artnum] trn_go_article $anum #possibly later do things like update the tree, etc... } trn-4.0-test77/support/tk/objatree.tcl0000644000000000000000000000076507113133375016455 0ustar rootroot#object-oriented replacement for the old article tree code #XXX Later move tree outside... source $script_dir/Tree.tcl source $script_dir/ArtTree.tcl ArtTree .articleTree -toplevel 1 #XXX later this will be fixed up: .articleTree configure -title "Article Tree" #Procedures in the old style... proc trn_draw_article_tree {x y} { global ttk_msgid .articleTree draw $ttk_msgid } proc endgroup {} { .articleTree erase update idletasks } proc wipetree {} { .articleTree erase } trn-4.0-test77/support/tk/obtcl.tcl0000644000000000000000000005527207113133375015770 0ustar rootroot#This is a crunched version of obtcl 0.54 with the JLV patches. #The obtcl package was created by Patrik Floding (see the notice below) #The original patches were found at: # ftp://ftp.dynas.se/pub/tcl #The patches were found at: # http://www.osf.org/~loverso/tcl-tk/obTcl/ # #If you wish to play with obTcl, I highly recommend getting the distribution, #as its source code has extensive comments and even manual pages. # #The following copyright was included in the distribution: #[none of the tcl_cruncher files are included in trn] #Copyright (C) 1995 DynaSoft AB. # #This software is copyrighted by DynaSoft AB, Sweden. It is provided #courtesy of the author, Patrik Floding, and DynaSoft AB. #The copyright notice below applies to all included files except #the files under the directory 'tcl_cruncher', which are separately #copyrighted (see tcl_cruncher/LICENSE). # #The authors hereby grant permission to use, copy, modify, distribute, #and license this software and its documentation for any purpose, provided #that existing copyright notices are retained in all copies and that this #notice is included verbatim in any distributions. No written agreement, #license, or royalty fee is required for any of the authorized uses. #Modifications to this software may be copyrighted by their authors #and need not follow the licensing terms described here, provided that #the new terms are clearly indicated on the first page of each file where #they apply. # #IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY #FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES #ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY #DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE #POSSIBILITY OF SUCH DAMAGE. # #THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, #INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE #IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE #NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR #MODIFICATIONS. proc cmt { args } {} proc Nop {} {} proc setIfNew { var val } {global $var if ![info exists $var] {set $var $val}} proc crunch_skip { args } {} proc lappendUniq { v val } {upvar $v var if { [lsearch $var $val] != -1 } {return} lappend var $val} proc listMinus { a b } {set ret {} foreach i $a {set ArrA($i) 1} foreach i $b {set ArrB($i) 1} foreach i [array names ArrA] {if ![info exists ArrB($i)] {lappend ret $i}} return $ret} set _otReferenceSBD { global tkPriv set tkPriv(relief) [$w cget -activerelief] $w configure -activerelief sunken set element [$w identify $x $y] if {$element == "slider"} { tkScrollStartDrag $w $x $y } else { tkScrollSelect $w $element initial } } proc otTkScrollButtonDown {w x y} {global tkPriv set tkPriv(relief) [$w cget -activerelief] set element [$w identify $x $y] if {$element == "slider"} {tkScrollStartDrag $w $x $y} else {$w configure -activerelief sunken tkScrollSelect $w $element initial}} proc StrictMotif {} {global tk_version tk_strictMotif _otReferenceSBD set tk_strictMotif 1 if { $tk_version == 4.0 || [string compare [info body tkScrollButtonDown] [set _otReferenceSBD]] == 0 } {if {[info procs otTkScrollButtonDown] != ""} {rename tkScrollButtonDown {} rename otTkScrollButtonDown tkScrollButtonDown}}} proc dbputs { s } {} proc DOC { name rest } {} proc DOC_get_list {} {} set obtcl_version "0.54" proc instvar2global { name } {upvar 1 class class self self return _oIV_${class}:${self}:$name} proc classvar { args } {uplevel 1 "foreach _obTcl_i [list $args] { upvar #0 _oDCV_\${class}:\$_obTcl_i \$_obTcl_i }"} proc classvar_of_class { class args } {uplevel 1 "foreach _obTcl_i [list $args] { upvar #0 _oDCV_${class}:\$_obTcl_i \$_obTcl_i }"} proc iclassvar { args } {uplevel 1 "foreach _obTcl_i [list $args] { upvar #0 _oICV_\${iclass}:\$_obTcl_i \$_obTcl_i }"} proc instvar_of_class { class args } {uplevel 1 "foreach _obTcl_i [list $args] { upvar #0 _oIV_${class}:\${self}:\$_obTcl_i \$_obTcl_i }"} proc instvar { args } {uplevel 1 "foreach _obTcl_i [list $args] { upvar #0 _oIV_\${class}:\${self}:\$_obTcl_i \$_obTcl_i }"} proc import { class args } {uplevel 1 "foreach _obTcl_i [list $args] { upvar #0 _oIV_${class}:\${self}:\$_obTcl_i \$_obTcl_i }"} proc renamed_instvar { normal_name new_name } {uplevel 1 "upvar #0 _oIV_\${class}:\${self}:$normal_name $new_name"} proc is_object { name } {global _obTcl_Objects if [info exists _obTcl_Objects($name)] {return 1} else {return 0}} proc is_class { name } {global _obTcl_Classes if [info exists _obTcl_Classes($name)] {return 1} else {return 0}} proc otNew { iclass obj args } {global _obTcl_Objclass _obTcl_Objects set _obTcl_Objclass($iclass,$obj) $obj if ![info exists _obTcl_Objects($obj)] {catch {rename $obj ${obj}-cmd}} set _obTcl_Objects($obj) 1 proc $obj { cmd args } " set self $obj set iclass $iclass if \[catch {eval {$iclass::\$cmd} \$args} val\] { return -code error -errorinfo \"$obj: \$val\" \"$obj: \$val\" } else { return \$val } " set self $obj eval {$iclass::initialize} eval {$iclass::init} $args} proc otInstance { iclass obj args } {global _obTcl_Objclass _obTcl_Objects set _obTcl_Objclass($iclass,$obj) $obj if {[info procs ${obj}-cmd] == ""} {catch {rename $obj ${obj}-cmd}} set _obTcl_Objects($obj) 1 proc $obj { cmd args } " set self $obj set iclass $iclass if \[catch {eval {$iclass::\$cmd} \$args} val\] { return -code error -errorinfo \"$obj: \$val\" \"$obj: \$val\" } else { return \$val } " set self $obj eval {$iclass::initialize}} proc otFreeObj { obj } {global _obTcl_Objclass _obTcl_Objects otGetSelf catch {uplevel #0 "eval unset _obTcl_Objclass($iclass,$obj) _obTcl_Objects($obj) \[info vars _oIV_*:${self}:*\]"} catch {rename $obj {}}} setIfNew _obTcl_Classes() "" setIfNew _obTcl_NoClasses 0 proc class { class } {global _obTcl_NoClasses _obTcl_Classes _obTcl_Inherits if [info exists _obTcl_Classes($class)] {set self $class otClassDestroy $class} if [string match "*:*" $class] {puts stderr "class: Fatal Error:" puts stderr " class name `$class' contains reserved character `:'" return} incr _obTcl_NoClasses 1 set _obTcl_Classes($class) 1 set iclass $class set obj $class proc $class { cmd args } " set self $obj set iclass $iclass switch -glob \$cmd { .* { eval {$class::new \$cmd} \$args } new { eval {$class::new} \$args } method { eval {otMkMethod N $class} \$args} inherit { eval {otInherit $class} \$args} destroy { eval {otClassDestroy $class} \$args } init { return -code error -errorinfo \"$obj: Error: classes may not be init'ed!\" \"$obj: Error: classes may not be init'ed!\" } default { if \[catch {eval {$iclass::\$cmd} \$args} val\] { return -code error -errorinfo \"$obj: \$val\" \"$obj: \$val\" } else { return \$val } } } " if { "$class" != "Base" } {$class inherit "Base"} else {set _obTcl_Inherits($class) {}} return $class} proc otClassDestroy { class } {global _obTcl_NoClasses _obTcl_Classes otGetSelf if ![info exists _obTcl_Classes($class)] {return} otInvalidateCaches 0 $class [otClassInfoMethods $class] otDelAllMethods $class rename $class {} incr _obTcl_NoClasses -1 unset _obTcl_Classes($class) uplevel #0 " foreach _iii \[info vars _oICV_${class}:*\] { unset \$_iii } foreach _iii \[info vars _oDCV_${class}:*\] { unset \$_iii } catch {unset _iii} " otFreeObj $class} proc otGetSelf {} {uplevel 1 {upvar 1 self self iclass iclass Umethod method}} proc otMkMethod { mode class name params body } {otInvalidateCaches 0 $class $name if { "$name" == "unknown" } {set method ""} else {set method "set method $name"} proc $class::$name $params "upvar 1 self self iclass iclass Umethod method set class $class $method $body" if { $mode == "S" } {global _obTcl_SysMethod set _obTcl_SysMethod($class::$name) 1}} proc otRmMethod { class name } {global _obTcl_SysMethod if { "$name" == "unknown" } {otInvalidateCaches 0 $class *} else {otInvalidateCaches 0 $class $name} rename $class::$name {} catch {unset _obTcl_SysMethod($class::$name)}} proc otDelAllMethods { class } {global _obTcl_Cached foreach i [info procs $class::*] {if [info exists _obTcl_SysMethod($i)] {continue} if [info exists _obTcl_Cached($i)] {unset _obTcl_Cached($i)} rename $i {}}} proc otObjInfoVars { glob base { match "" } } {if { "$match" == "" } {set match "*"} set l [info globals ${glob}$match] set all {} foreach i $l {regsub "${base}(.*)" $i {\1} tmp lappend all $tmp} return $all} proc otObjInfoObjects { class } {global _obTcl_Objclass set l [array names _obTcl_Objclass $class,*] set all {} foreach i $l {regsub "${class},(.*)" $i {\1} tmp lappend all $tmp} return $all} proc otClassInfoBody { class method } {global _obTcl_Objclass _obTcl_Cached if [info exists _obTcl_Cached(${class}::$method)] {return} if [catch {set b [info body ${class}::$method]} ret] {return -code error -errorinfo "info body: Method '$method' not defined in class $class" "info body: Method '$method' not defined in class $class"} else {return $b}} proc otClassInfoArgs { class method } {global _obTcl_Objclass _obTcl_Cached if [info exists _obTcl_Cached(${class}::$method)] {return} if [catch {set b [info args ${class}::$method]} ret] {return -code error -errorinfo "info args: Method '$method' not defined in class $class" "info args: Method '$method' not defined in class $class"} else {return $b}} proc otClassInfoMethods+Cached { class } {global _obTcl_Objclass _obTcl_SysMethod set l [info procs ${class}::*] set all {} foreach i $l {regsub "${class}::(.*)" $i {\1} tmp if [info exists _obTcl_SysMethod($i)] {continue} lappend all $tmp} return $all} proc otClassInfoMethods { class } {global _obTcl_Objclass _obTcl_Cached _obTcl_SysMethod set l [info procs ${class}::*] set all {} foreach i $l {if [info exists _obTcl_Cached($i)] {continue} if [info exists _obTcl_SysMethod($i)] {continue} regsub "${class}::(.*)" $i {\1} tmp lappend all $tmp} return $all} proc otClassInfoSysMethods { class } {global _obTcl_Objclass _obTcl_Cached _obTcl_SysMethod set l [info procs ${class}::*] set all {} foreach i $l {if [info exists _obTcl_Cached($i)] {continue} if ![info exists _obTcl_SysMethod($i)] {continue} regsub "${class}::(.*)" $i {\1} tmp lappend all $tmp} return $all} proc otClassInfoCached { class } {global _obTcl_Objclass _obTcl_Cached _obTcl_SysMethod if ![array exists _obTcl_Cached] {return} set l [array names _obTcl_Cached $class::*] set all {} foreach i $l {regsub "${class}::(.*)" $i {\1} tmp if [info exists _obTcl_SysMethod($i)] {continue} lappend all $tmp} return $all} proc obtcl_mkindex {dir args} {global errorCode errorInfo set oldDir [pwd] cd $dir set dir [pwd] append index "# Tcl autoload index file, version 2.0\n" append index "# This file is generated by the \"obtcl_mkindex\" command\n" append index "# and sourced to set up indexing information for one or\n" append index "# more commands/classes. Typically each line is a command/class that\n" append index "# sets an element in the auto_index array, where the\n" append index "# element name is the name of a command/class and the value is\n" append index "# a script that loads the command/class.\n\n" foreach file [eval glob $args] {set f "" set error [catch { set f [open $file] while {[gets $f line] >= 0} { if [regexp {^(proc|class)[ ]+([^ ]*)} $line match dummy entityName] { append index "set [list auto_index($entityName)]" append index " \"source \$dir/$file\"\n" } } close $f } msg] if $error {set code $errorCode set info $errorInfo catch {close $f} cd $oldDir error $msg $info $code}} set f [open tclIndex w] puts $f $index nonewline close $f cd $oldDir} proc otPrInherits {} {global _obTcl_Classes foreach i [array names _obTcl_Classes] {puts "$i inherits from: [$i inherit]"}} proc otInherit { class args } {global _obTcl_Inherits if { "$args" == "" } {return [set _obTcl_Inherits($class)]} if { "$class" != "Base" && [lsearch $args "Base"] == -1 } {set args [concat $args "Base"]} if { [info exists _obTcl_Inherits($class)] == 1 } {otInvalidateCaches 0 $class [otClassInfoCached ${class}]} else {set _obTcl_Inherits($class) {}} set _obTcl_Inherits($class) $args} proc otInvalidateCaches { level class methods } {global _obTcl_CacheStop foreach i $methods {if {"$i" == "unknown" } {set i "*"} set _obTcl_CacheStop($i) 1} if [array exists _obTcl_CacheStop] {otDoInvalidate}} proc otDoInvalidate {} {global _obTcl_CacheStop _obTcl_Cached if ![array exists _obTcl_Cached] {unset _obTcl_CacheStop return} if [info exists _obTcl_CacheStop(*)] {set stoplist "*"} else {set stoplist [array names _obTcl_CacheStop]} foreach i $stoplist {set tmp [array names _obTcl_Cached *::$i] eval lappend tmp [array names _obTcl_Cached *::${i}_next] foreach k $tmp {catch { rename $k {} unset _obTcl_Cached($k) }}} if {[array size _obTcl_Cached] == 0} {unset _obTcl_Cached} unset _obTcl_CacheStop} if { [info procs otUnknown] == "" } {rename unknown otUnknown} proc otResolve { class func } {return [otGetFunc 0 $class $func]} setIfNew _obTcl_unknBarred() "" proc unknown args {global _obTcl_unknBarred set name [lindex $args 0] if [string match "*::*" $name] {set tmp [split $name :] set class [lindex $tmp 0] set func [join [lrange $tmp 2 end] :] set flist [otGetFunc 0 $class $func] if [string match "" $flist] {if [info exists _obTcl_unknBarred($name)] {return -code error} set flist [otGetFunc 0 $class "unknown"]} if ![string match "" $flist] {proc $name args " upvar 1 self self iclass iclass Umethod method set Umethod $func eval [lindex $flist 0] \$args"} else {proc $name args " return -code error -errorinfo \"Undefined method '$func' invoked\" \"Undefined method '$func' invoked\" "} global _obTcl_Cached set _obTcl_Cached(${class}::$func) $class global errorCode errorInfo set code [catch {uplevel $args} msg] if {$code == 1} {set new [split $errorInfo \n] set new [join [lrange $new 0 [expr [llength $new] - 6]] \n] return -code error -errorcode $errorCode -errorinfo $new $msg} else {return -code $code $msg}} else {uplevel [concat otUnknown $args]}} setIfNew _obTcl_Cnt 0 proc otChkCall { cmd var } {global _obTcl_Trace _obTcl_Cnt _obTcl_nextRet if [info exists _obTcl_Trace($cmd)] {return $_obTcl_nextRet} set _obTcl_Trace($cmd) 1 catch {uplevel 1 "uplevel 1 \"$cmd \[set $var\]\""} _obTcl_nextRet return $_obTcl_nextRet} proc otNextElse {} {uplevel 1 {set all [otGetNextFunc $class $method] foreach i $all {append tmp "otChkCall $i args\n"} if [info exists tmp] {proc $class::${method}_next args " $tmp "} else {proc $class::${method}_next args " return "} set _obTcl_Cached(${class}::${method}_next) $class incr _obTcl_Cnt 1 set ret [catch {uplevel 1 {${class}::${method}_next} $args} val] incr _obTcl_Cnt -1}} proc next args {global _obTcl_Cnt _obTcl_Cached _obTcl_nextRet upvar 1 self self method method class class iclass iclass if { $_obTcl_Cnt == 0 } {set _obTcl_nextRet ""} if [info exists _obTcl_Cached(${class}::${method}_next)] {incr _obTcl_Cnt 1 set ret [catch {uplevel 1 {${class}::${method}_next} $args} val] incr _obTcl_Cnt -1} else {otNextElse} if { $_obTcl_Cnt == 0 } {global _obTcl_Trace catch {unset _obTcl_Trace}} if { $ret != 0 } {return -code error -errorinfo "$self: $val" "$self: $val"} else {return $val}} proc otGetNextFunc { class func } {global _obTcl_Inherits set all "" foreach i [set _obTcl_Inherits($class)] {foreach k [otGetFunc 0 $i $func] {lappendUniq all $k}} return $all} proc otGetFunc { depth class func } {global _obTcl_Inherits _obTcl_Cached _obTcl_NoClasses _obTcl_Classes if { "$depth" > "$_obTcl_NoClasses" } {otGetFuncErr $depth $class $func return ""} incr depth set all "" if ![info exists _obTcl_Classes($class)] {if ![auto_load $class] {otGetFuncMissingClass $depth $class $func return ""}} if { [info procs $class::$func] != "" && ![info exists _obTcl_Cached(${class}::$func)] } {return "$class::$func"} foreach i [set _obTcl_Inherits($class)] {set ret [otGetFunc $depth $i $func] if { $ret != "" } {foreach i $ret {lappendUniq all $i}}} return $all} proc otGetFuncErr { depth class func } {puts stderr "GetFunc: depth=$depth, circular dependency!?" puts stderr " class=$class func=$func"} proc otGetFuncMissingClass { depth class func } {puts stderr "GetFunc: Unable to inherit from $class" puts stderr " $class not defined (and auto load failed)" puts stderr " Occurred while looking for $class::$func"} class Base Base method init args {} Base method destroy args {otFreeObj $self} Base method class args {return $iclass} Base method set args {set class $iclass set var [lindex $args 0] regexp -- {^([^(]*)\(.*\)$} $var m var instvar $var return [eval set $args]} Base method eval l {return [eval $l]} Base method info { cmd args } {switch $cmd { "instvars" {return [eval {otObjInfoVars _oIV_${iclass}:${self}: _oIV_${iclass}:${self}:} $args]} "iclassvars" {otObjInfoVars _oICV_${iclass}: _oICV_${iclass}: $args} "classvars" {otObjInfoVars _oDCV_${iclass}: _oDCV_${iclass}: $args} "objects" {otObjInfoObjects $iclass} "methods" {otClassInfoMethods $iclass} "sysmethods" {otClassInfoSysMethods $iclass} "cached" {otClassInfoCached $iclass} "body" {otClassInfoBody $iclass $args} "args" {otClassInfoArgs $iclass $args} "options" {$iclass::collectOptions values ret return [array get ret] } "defaults" {$iclass::collectOptions defaults ret return [array get ret] } default { return -code error -errorinfo "Undefined command 'info $cmd'" "Undefined command 'info $cmd'" } }} Base method unknown { args } {return -code error -errorinfo "Undefined method '$method' invoked" "Undefined method '$method' invoked"} Base method new { obj args } {eval {otNew $iclass $obj} $args} Base method instance { obj args } {eval {otInstance $iclass $obj} $args} Base method sys_method { args } {eval {otMkMethod S $iclass} $args} Base method method { args } {eval {otMkMethod N $iclass} $args} Base method del_method { args } {eval {otRmMethod $iclass} $args} Base method inherit { args } {eval {otInherit $iclass} $args} class AnonInst AnonInst method anonPrefix { p } { iclassvar _prefix set _prefix $p } AnonInst method new {{obj {}} args} { iclassvar _count _prefix if ![info exists _count] { set _count 0 } if ![info exists _prefix] { set _prefix "$iclass" } if [string match "" $obj] { set obj $_prefix[incr _count] } eval next {$obj} $args return $obj } proc otMkSectMethod { class name sect } {$class sys_method $name { args } " array set Opts \$args foreach i \[array names Opts\] { global _obTcl_unknBarred set _obTcl_unknBarred(\$class::${sect}:\$i) 1 if \[catch {\$class::$sect:\$i \$Opts(\$i)} err\] { if \[catch {\$class::${sect}_unknown \$i \$Opts(\$i)}\] { unset _obTcl_unknBarred(\$class::${sect}:\$i) error \"Unable to do '$sect \$i \$Opts(\$i)'\n \t\$err \" } } unset _obTcl_unknBarred(\$class::${sect}:\$i) } "} proc otMkOptHandl {} {uplevel 1 {$iclass sys_method "cget" { opt } " classvar classOptions if \[catch {$iclass::cget:\$opt} ret\] { if \[catch {\$class::cget_unknown \$opt} ret\] { error \"Unable to do 'cget \$opt'\" } } return \$ret " otMkSectMethod $iclass conf_init init $iclass sys_method initialize {} { next classvar optDefaults eval instvar [array names optDefaults] foreach i [array names optDefaults] { set $i $optDefaults($i) } } $iclass sys_method collectOptions { mode arr } { classvar classOptions optDefaults upvar 1 $arr ret next $mode ret eval instvar [array names optDefaults] foreach i [array names optDefaults] { if { $mode == "defaults" } { set ret(-$i) $optDefaults($i) } else { set ret(-$i) [set $classOptions(-$i)] } } } otMkSectMethod $iclass conf_verify verify otMkSectMethod $iclass configure configure set _optPriv(section,cget) 1 set _optPriv(section,init) 1 set _optPriv(section,initialize) 1 set _optPriv(section,verify) 1 set _optPriv(section,configure) 1}} otMkSectMethod Base configure configure Base method option { opt dflt args } {classvar_of_class $iclass optDefaults classOptions _optPriv set var [string range $opt 1 end] set optDefaults($var) $dflt set classOptions($opt) $var array set tmp $args if ![info exists _optPriv(initialize)] {otMkOptHandl set _optPriv(initialize) 1} foreach i [array names tmp] {if ![info exists _optPriv(section,$i)] {otMkSectMethod $iclass $i $i set _optPriv(section,$i) 1} $iclass sys_method "$i:$opt" _val " instvar $var set _old_val \$[set var] set $var \$_val set ret \[catch {$tmp($i)} res\] if {\$ret != 0 && \$ret != 2 } { set $var \$_old_val return -code \$ret -errorinfo \$res \$res } return \$res " set _optPriv($i:$opt) 1} if ![info exists _optPriv(cget:$opt)] {$iclass sys_method "cget:$opt" {} " instvar $var return \$[set var] " set _optPriv(cget:$opt) 1} if ![info exists tmp(verify)] {$iclass sys_method "verify:$opt" _val " instvar $var set $var \$_val " set _optPriv(verify:$opt) 1} if ![info exists tmp(configure)] {$iclass sys_method "configure:$opt" _val " instvar $var set $var \$_val " set _optPriv(configure:$opt) 1} if ![info exists tmp(init)] {$iclass sys_method "init:$opt" _val {} set _optPriv(init:$opt) 1}} Base sys_method init_unknown { opt val } {} Base sys_method verify_unknown { opt val } {} Base sys_method initialize {} {} Base sys_method conf_init {} {} class Widget Widget method init { args } {instvar win wincmd next set first "[lindex $args 0]" set c1 "[string index $first 0]" if { "$c1" == "" || "$c1" == "-" } {set type frame set cl "-class $iclass"} else {set type $first set args [lrange $args 1 end] set cl ""} if {[info commands $self-cmd] != ""} {set win $self set wincmd $self-cmd} else {if { [string index $self 0] == "." } {rename $self _ooTmp eval $type $self $cl $args rename $self $self-cmd rename _ooTmp $self set win $self set wincmd $self-cmd} else {eval $type .$self $cl $args set win .$self set wincmd .$self}} bind $win " if { \"%W\" == \"$self\" && !\[catch {info args $self}\] } { $self destroy -obj_only }" return $self} Widget sys_method configure { args } {instvar wincmd eval {$wincmd configure} $args} Widget sys_method cget { opt } {instvar wincmd eval {$wincmd cget} $opt} Widget sys_method configure_unknown { opt args } {instvar wincmd eval {$wincmd configure $opt} $args} Widget sys_method cget_unknown { opt } {instvar wincmd $wincmd cget $opt} Widget sys_method init_unknown { opt val } {puts "init_unknown: $opt $val (iclass=$iclass class=$class)"} Widget sys_method unknown args {instvar wincmd eval {$wincmd $method} $args} Widget method destroy { args } {instvar win wincmd set wp $win set w $wincmd otFreeObj $self catch {bind $w {}} if { $args != "-obj_only" } {if [string compare $w $wp] {rename $w $wp} if { $args != "-keepwin" } {destroy $wp}}} Widget sys_method set { args } {instvar wincmd eval {$wincmd set} $args} Widget sys_method base_set { args } {eval Base::set $args} trn-4.0-test77/support/tk/ArtTree.tcl0000644000000000000000000002321007113133375016216 0ustar rootrootclass ArtTree #Note that Widget is also inherited by TrnModule ArtTree inherit TrnModule #Instance variables #f frame or toplevel containing articletree #XXX document the rest later #If there are *no* options at all, the _unknown handlers do not work #Give these options to the contained Tree, not to the TrnModule #XXX deal with cget later... ArtTree option -width {0} verify { instvar tree_options lappend tree_options -width $width } ArtTree option -height {0} verify { instvar tree_options lappend tree_options -height $height } ArtTree option -enabled {1} ArtTree option -zoomfactor {0.75} ArtTree option -update_spin {10} ArtTree method init { args } { instvar f enable_state enable_button tree_options #puts "ArtTree init: begin" set tree_options {} eval $self conf_verify $args #set up our inheritance tree... set f [next] #do the main setup for the object $self setup } ArtTree method setup { args } { instvar tree enabled enable_button tree_options top_msgid \ cur_ap changes_ap cursor_ap update_count click_ap idlabel #puts "ArtTree setup: begin" set top_msgid "" set cur_ap [trn_article] set changes_ap [trn_article] set cursor_ap [trn_article] set click_ap [trn_article] set update_count 0 frame $self.bf1 set enable_button [button $self.bf1.enable_button \ -command "$self button"] $self button_text button $self.bf1.zout -text "<<" -command "$self do_soon zoom_out" button $self.bf1.zreset -text "1.0" -command "$self do_soon zoom_reset" button $self.bf1.zin -text ">>" -command "$self do_soon zoom_in" pack $enable_button \ $self.bf1.zout $self.bf1.zreset $self.bf1.zin \ -side left -fill x -expand yes frame $self.lf2 set idlabel [label $self.lf2.idlabel -width 30 -text "<>"] pack $self.lf2.idlabel -fill x lappend tree_options -entercommand "$self enternode" \ -leavecommand "$self leavenode" \ -clickcommand "$self clicknode" #dbg "Tree options: $tree_options" if {$tree_options != {}} { set tree [eval Tree $self.tree $tree_options] } else { set tree [Tree $self.tree] } pack $self.bf1 -side top -fill x pack $self.lf2 -side top -fill x pack $tree -side top -expand yes -fill both $self modulepack } ArtTree method nodesize {ap} { set n 0 if {![$ap flag parentsubject]} { incr n 1 } return $n } ArtTree method erase {} { instvar tree draw_background top_msgid id2vn vn2id draw_interrupt $self hidecursor set draw_background 0 set draw_interrupt 0 set top_msgid "" catch {unset id2vn} catch {unset vn2id} $tree erase } #draw article tree starting with article with message-ID msgid ArtTree method draw {msgid} { instvar enabled curx cury cur_ap draw_done pstackptr \ draw_background top_msgid tree draw_interrupt if {(!$enabled) || ($msgid=="")} { return } if {$top_msgid==$msgid} { ttk_idle_add "$self draw_continue" $self treechanges $self showcursor return } $self erase set curx 0 set cury 0 set draw_done 0 set pstackptr 0 set draw_interrupt 0 if {[$cur_ap setid $msgid]} { set draw_background 1 set top_msgid $msgid $cur_ap treeprepare ttk_idle_add "$self draw_continue" } else { dbg "ArtTree draw: invalid message-id" } } ArtTree method draw_done {} { instvar tree draw_done draw_background draw_interrupt $self showcursor $tree setscrollregion #Scroll tree to show current plotted article? set draw_done 1 set draw_background 0 set draw_interrupt 0 } #Later consider separating out some of these decisions (like colors) ArtTree method draw_node {} { instvar tree curx cury cur_ap id2vn vn2id set shape rectangle set fillcol white if {![$cur_ap flag exists]} { set fillcol gray75 } if {![$cur_ap flag unread]} { set shape oval } #XXX testing--icky colors. set score [$cur_ap scorequick] if {$score <= -3} { set fillcol LightBlue } if {$score <= -6} { set fillcol DarkBlue } if {$score >= 4} { set fillcol LightPink } if {$score >= 8} { set fillcol HotPink } set letter [$cur_ap subjectletter] set vn [$tree makenode $curx $cury $shape $fillcol $letter] if {![$cur_ap flag parentsubject]} { $tree draw_text $vn [$cur_ap header subject] } set msgid [$cur_ap header msgid] set id2vn($msgid) $vn set vn2id($vn) $msgid return $vn } ArtTree method redraw_node {ap vn} { instvar tree # dbg "redraw_node: updating node ap=$ap, vn=$vn" set shape rectangle if {![$ap flag unread]} { set shape oval } $tree changeshape $vn $shape } #Useful for debugging ArtTree method update_spin {} { instvar update_count update_spin tree incr update_count if {($update_count%$update_spin)==0} { $tree setscrollregion update return 1 } return 0 } #Consider whether some top items should show parent line. ArtTree method draw_continue {} { instvar tree curx cury cur_ap draw_done pstack pstackptr \ draw_background enabled draw_interrupt if {(!$enabled) || ($draw_done) || (!$draw_background)} { return } #dbg "draw_continue enter" update while (1) { $self update_spin if {$draw_interrupt} { set draw_interrupt 0 return } if {([trn_misc pending])} { ttk_idle_add "$self draw_continue" $tree setscrollregion return } #XXX make much more interesting (local method?) set vn [$self draw_node] if {$pstackptr>0} { set pvn $pstack($pstackptr) $tree draw_parentline $vn $pvn if {![$cur_ap flag nextsibling]} { $tree draw_childline $pvn $vn } } set ns [$self nodesize $cur_ap] incr cury $ns #later also check to see if we will draw the child. if {[$cur_ap flag child]} { incr curx incr pstackptr set pstack($pstackptr) $vn $cur_ap move child } else { if {$ns==0} { incr cury 1 } if {[$cur_ap flag nextsibling]} { $cur_ap move sibling } else { if {$pstackptr==0} { $self draw_done return } set foundsib 0 while {(!$foundsib) && ($pstackptr>0)} { incr curx -1 incr pstackptr -1 $cur_ap move parent if {[$cur_ap flag nextsibling]} { $cur_ap move sibling set foundsib 1 } } if {$foundsib==0} { $self draw_done return } } } } } ArtTree method nodeupdate {ap} { instvar id2vn set msgid [$ap header msgid] # dbg "updating node $ap: $msgid" if {[info exists id2vn($msgid)]} { $self redraw_node $ap $id2vn($msgid) } } ArtTree method treechanges {} { instvar changes_ap top_msgid #XXX work on the globals global tree_nodechanged if {[$changes_ap setid $top_msgid]} { set tree_nodechanged "$self nodeupdate" $changes_ap treechange } else { dbg "ArtTree treechanges: error setting id: |$top_msgid|" } } ArtTree method showcursor {} { instvar cursor_ap id2vn tree set tmp [$cursor_ap setcurrent] if {$tmp>0} { set msgid [$cursor_ap header msgid] if {[info exists id2vn($msgid)]} { $tree plot_cursor $id2vn($msgid) } } } ArtTree method hidecursor {} { instvar tree $tree unplot_cursor } #What to do when the button is pressed (arg is button path) ArtTree method button { } { instvar enabled enable_button $enable_button flash if {$enabled} { set enabled 0 $self erase } else { set enabled 1 } $self button_text } ArtTree method button_text {} { instvar enabled enable_button if {$enabled} { $enable_button configure -text "Tree is ON" } else { $enable_button configure -text "Tree is OFF" } } ArtTree method redraw {} { instvar top_msgid set safeid $top_msgid $self erase $self draw $safeid } ArtTree method zoom_in {} { instvar tree zoomfactor $tree zoomfactor [expr 1/$zoomfactor] $self redraw } ArtTree method zoom_out {} { instvar tree zoomfactor $tree zoomfactor $zoomfactor $self redraw } ArtTree method zoom_reset {} { instvar tree zoomfactor $tree zoomfactor reset $self redraw } #can interrupt drawing... ArtTree method do_soon {cmd} { instvar draw_interrupt ttk_idle_add "$self $cmd" set draw_interrupt 1 } ArtTree method enternode {vn} { instvar vn2id idlabel if {[info exists vn2id($vn)]} { $idlabel configure -text $vn2id($vn) } } ArtTree method leavenode {vn} { instvar idlabel $idlabel configure -text "<>" } #Within the current group, attempt to go to an article #later handle 's' (scan mode), 't' thread selector in appropriate ways... #(if the article is<=0, do nothing) ArtTree method goto_article {anum} { global ttk_keys if {$anum>0} { #if the article number is good... set trn_mode [trn_misc mode] if {(($trn_mode == "t"))} { append ttk_keys "+" } if {(($trn_mode == "p") || ($trn_mode == "a") || ($trn_mode == "e") || ($trn_mode == "t"))} { append ttk_keys "$anum\n" } elseif {$trn_mode == "s"} { append ttk_keys "G$anum\n" } } } ArtTree method clicknode {vn} { instvar click_ap vn2id if {[info exists vn2id($vn)]} { set msgid $vn2id($vn) $click_ap setid $msgid set anum [$click_ap artnum] } $self goto_article $anum } ################# Unknown options/methods #Unknown options and methods are redirected to the contained article tree ArtTree sys_method verify_unknown { opt args } { instvar tree tree_options #puts "ArtTree verify_unknown: $opt $args" lappend tree_options $opt $args } ArtTree sys_method configure_unknown { opt args } { instvar tree eval $tree configure $opt $args } ArtTree sys_method cget_unknown { opt } { instvar tree $tree cget $opt } ArtTree method unknown { args } { instvar tree eval {$tree $method} $args } trn-4.0-test77/support/tk/TrnModule.tcl0000644000000000000000000000524707113133375016573 0ustar rootroot#TrnModule: provides frames or toplevels to TrnModules--will later provide # common functionality of all modules. class TrnModule TrnModule inherit Widget #toplevel config is an error TrnModule option {-toplevel} {0} verify { if {$toplevel} { set toplevel 1 } else { set toplevel 0 } } TrnModule option {-width} {120} configure { instvar f $f config -width $width } TrnModule option {-height} {100} configure { instvar f $f config -height $height } #XXX clean this up, allow for initial setting TrnModule option {-title} {} configure { instvar f toplevel if {$toplevel} { wm title $f $title } } TrnModule method init { args } { instvar f toplevel height width eval $self conf_verify $args if {$toplevel} { set f [next toplevel -width $width -height $height] } else { set f [next -width $width -height $height] } $self setsize return $f } #If not a toplevel, pack the frame TrnModule method modulepack { args } { instvar f toplevel if {!$toplevel} { catch {pack $f -expand yes -fill both} } } #Later: save sizes of internal frames? # (might need to know resizing command, or make standard) #Set size if there is a default (and if we are a toplevel) #If there is no default, make the window saved later #Note: This code used to set the size and position immediately. # Unfortunately, the window would (usually) not be mapped, so the # window manager did not adjust for the decorations. TrnModule method setsize {} { instvar toplevel f global _TrnModule_winpos _TrnModule_winlist if {$toplevel} { set _TrnModule_winlist($f) 1 } } #consider special check if no data #Only currently mapped windows are saved. proc TrnModule_savesizes {fname} { global _TrnModule_winpos _TrnModule_winlist foreach w [array names _TrnModule_winlist] { catch { if {[winfo ismapped $w]} { set _TrnModule_winpos($w) [winfo geometry $w] } } } set data [array get _TrnModule_winpos] set file [open $fname "w"] puts $file $data close $file } proc TrnModule_loadsizes {fname} { global _TrnModule_winpos catch { set file [open $fname "r"] gets $file data close $file array set _TrnModule_winpos $data } } #Reset all windows to their saved sizes. proc TrnModule_setsizes {} { global _TrnModule_winpos _TrnModule_winlist foreach w [array names _TrnModule_winlist] { catch {wm geom $w $_TrnModule_winpos($w)} } } #Reset all windows to their natural sizes. proc TrnModule_naturalsizes {} { global _TrnModule_winpos _TrnModule_winlist foreach w [array names _TrnModule_winlist] { catch {wm geom $w $_TrnModule_winpos($w)} } } trn-4.0-test77/support/tk/ScoreBar.tcl0000644000000000000000000000126407113133375016355 0ustar rootroot#ScoreBar.tcl #Note that there is only one global score_curvar variable, so some of #this object-stuff is overkill... class ScoreBar ScoreBar inherit TrnModule #Option is not used yet, but things stop working with no options ArtTree option -enabled {1} ScoreBar method init { args } { instvar f eval $self conf_verify $args set f [next] $self setup } #Consider adding the label back in if we are *not* a toplevel ScoreBar method setup {} { instvar f global score_curvar $self configure -title "Current Score" scale $f.s \ -length 10c -orient horizontal -from -50 -to 50 -tickinterval 10 \ -variable score_curvar set score_curvar 0 pack $f.s } trn-4.0-test77/support/tk/Tree.tcl0000644000000000000000000002476607113133375015570 0ustar rootroot#todo: # * Make scrollbars optional? # * consider separating tree and virtual node info? # for outlines, allow 0 setting (or small enough scale?) to make no outline # Need to add margins to size # consider connecting line sizes? Should they be scaled? #Note: the Tree class does *not* pack the returned widget class Tree Tree inherit Widget Tree option {-width} {200} configure { instvar c $c config -width $width } Tree option {-height} {200} configure { instvar c $c config -height $height } #Scale for zoom/unzoom operations: 1.0 is normal Tree option {-scale} {1.0} configure { $self setscale } Tree option {-xgridsize} {30} Tree option {-ygridsize} {30} Tree option {-xshapesize} {9} Tree option {-yshapesize} {9} Tree option {-outlinewidth} {3} Tree option {-outline_normal_color} {black} Tree option {-outline_highlight_color} {red} Tree option {-entercommand} {} Tree option {-leavecommand} {} Tree option {-clickcommand} {} Tree method init { args } { instvar f c vnode vcnt #puts "Tree init: begin, args are |$args|" eval {$self conf_verify} $args #go up the inheritance tree and get our widget... set f [next] $self setup return $f } Tree method setup { args } { instvar height width instvar f c vnode c2v vcnt \ xgridsize ygridsize xshapesize yshapesize xgridoffset ygridoffset \ cursor_status cursor_items set vcnt 0 set cursor_status 0 set cursor_items {} #make more configurable later? #make scrolling command more flexible? #Reasonable defaults: set c [canvas $f.c -height $height -width $width \ -xscrollcommand "$f.sbh set" -yscrollcommand "$f.sbv set"] # set c [canvas $f.c -height $height -width $width \ # -xscrollcommand "$f.sbh set" -yscrollcommand "$self yscrollset"] $c configure -relief sunken -borderwidth 2 scrollbar $f.sbh -orient horizontal -command "$c xview" scrollbar $f.sbv -orient vertical -command "$c yview" # scrollbar $f.sbv -orient vertical -command "$self yview" pack $f.sbh -side bottom -fill x pack $f.sbv -side right -fill y pack $c -side top -expand yes -fill both $self setbindings #A reasonably safe scrollregion setting: $c configure -scrollregion {0 0 1 1} #Scale needs to be set late so that the canvas will have been created $self setscale } #Scrolling commands for debugging Tree method yscrollset {args} { instvar f puts "Yscroll: $args" eval $f.sbv set $args } Tree method yview {args} { instvar c puts "PreYview: $args |[$c yview]|" eval $c yview $args puts "PostYview: |[$c yview]|" } Tree method setbindings {} { instvar c vnode c2v bind $c <2> "$c scan mark %x %y" bind $c "$c scan dragto %x %y" $c bind node "$self node_enter" $c bind node "$self node_leave" $c bind node <1> "$self node_click" } #consider whether highlight changing should be in command, and what should #be passed upwards as the argument... #XXX: try catching error conditions... Tree method node_enter {} { instvar c c2v vnode outline_highlight_color entercommand set item [$c find withtag current] if {$item == ""} { break } set vn $c2v($item) $c itemconfigure $vnode($vn,shape) -outline $outline_highlight_color if {$entercommand != {}} { eval $entercommand $vn } } Tree method node_leave {} { instvar c c2v vnode outline_normal_color leavecommand set item [$c find withtag current] if {$item == ""} { break } set vn $c2v($item) $c itemconfigure $vnode($vn,shape) -outline $outline_normal_color if {$leavecommand != {}} { eval $leavecommand $vn } } Tree method node_click {} { instvar c c2v vnode clickcommand set item [$c find withtag current] if {$item == ""} { break } set vn $c2v($item) if {$clickcommand != {}} { eval $clickcommand $vn } } #XXX later add margins... Tree method setscrollregion {} { instvar c $c configure -scrollregion [$c bbox all] } #Scale grid, shape, and outline size to current dimensions: Tree method setscale {} { instvar c scale xgridsize ygridsize xgridoffset ygridoffset \ xshapesize yshapesize outlinewidth instvar xgridscale ygridscale xshapescale yshapescale outlinewidthscale #puts "Tree setscale: scale is $scale" set xgridscale [expr $xgridsize * $scale] set ygridscale [expr $xgridsize * $scale] set xgridoffset [expr $xgridscale/2] set ygridoffset [expr $ygridscale/2] set xshapescale [expr $xshapesize * $scale] set yshapescale [expr $yshapesize * $scale] set outlinewidthscale [expr $outlinewidth * $scale] $c configure -xscrollincrement $xgridscale -yscrollincrement $ygridscale } Tree method zoomfactor {zf} { instvar scale if {$zf=="reset"} { set scale 1.0 } else { set scale [expr $scale*$zf] } $self setscale } #returns virtual node number Tree method makenode {gx gy shape bcolor label} { instvar c vcnt vnode c2v instvar xgridoffset ygridoffset outline_normal_color scale instvar xgridscale ygridscale xshapescale yshapescale outlinewidthscale set vn [incr vcnt] set vnode($vn,gx) $gx set vnode($vn,gy) $gy set x [expr ($gx * $xgridscale) + $xgridoffset] set y [expr ($gy * $ygridscale) + $ygridoffset] #soon add other options #Later allow custom shape routines set t1 [$c create $shape [expr $x-$xshapescale] [expr $y-$yshapescale] \ [expr $x+$xshapescale] [expr $y+$yshapescale] \ -outline $outline_normal_color -width $outlinewidthscale \ -fill $bcolor -tags node] set c2v($t1) $vn set vnode($vn,shape) $t1 #XXX do not make label if scale too small (needs to be changable) if {$scale>=0.5} { set t1 [$c create text $x $y -text $label -fill black -tags node] set c2v($t1) $vn set vnode($vn,label) $t1 } return $vn } #vn is the virtual node #shape is the new shape Tree method changeshape {vn shape} { instvar c vnode c2v instvar xgridoffset ygridoffset outline_normal_color instvar xgridscale ygridscale xshapescale yshapescale outlinewidthscale set gx $vnode($vn,gx) set gy $vnode($vn,gy) set x [expr ($gx * $xgridscale) + $xgridoffset] set y [expr ($gy * $ygridscale) + $ygridoffset] set oldshape $vnode($vn,shape) set bcolor [$c itemcget $oldshape -fill] $c delete $oldshape unset c2v($oldshape) set t1 [$c create $shape [expr $x-$xshapescale] [expr $y-$yshapescale] \ [expr $x+$xshapescale] [expr $y+$yshapescale] \ -outline $outline_normal_color -width $outlinewidthscale \ -fill $bcolor -tags node] #make the shape appear below the label (if it exists) if {[info exists vnode($vn,label)]} { $c lower $t1 $vnode($vn,label) } set c2v($t1) $vn set vnode($vn,shape) $t1 } #Write out text to the right of the virtual node #Text is given as an arbitrary number of arguments #XXX font choice later, possible skip when nodes too small #XXX text color #Consider different tag? #Consider implementation that obeys grid? (one line per y-grid) Tree method draw_text {vn txt} { instvar c vnode c2v instvar xgridscale ygridscale xshapescale yshapescale outlinewidthscale \ xgridoffset ygridoffset scale #XXX hack: do not draw text if too small a scale if {$scale<0.5} { return } set gx [expr $vnode($vn,gx) + 1] set gy $vnode($vn,gy) set x [expr ($gx * $xgridscale) + $xgridoffset - $xshapescale] set y [expr ($gy * $ygridscale) + $ygridoffset - $yshapescale] set item [$c create text $x $y -text $txt -fill black -anchor nw \ -tags node] set c2v($item) $vn set vnode($vn,txt) $item } #XXX line color #later do fancier things... #Arguments are the current (child) virtual node and the parent virtual node Tree method draw_parentline {vn pvn} { instvar c vnode c2v instvar xgridscale ygridscale xshapescale yshapescale outlinewidthscale \ xgridoffset ygridoffset set gx $vnode($vn,gx) set gy $vnode($vn,gy) set x [expr ($gx * $xgridscale) + $xgridoffset] set y [expr ($gy * $ygridscale) + $ygridoffset] set pgx $vnode($pvn,gx) set pgy $vnode($pvn,gy) set px [expr ($pgx * $xgridscale) + $xgridoffset] set py [expr ($pgy * $ygridscale) + $ygridoffset] set xmid [expr $px + (($x-$px)/2)] set line [$c create line $x $y $xmid $y -tags pline] set c2v($line) $vn set vnode($vn,pline) $line $c lower $line if {$py!=$y} { #Later: draw dot at parent end of line if dots are enabled } } #XXX line color #later do fancier things... #Arguments are the current (parent) virtual node and the child virtual node #The child virtual node should be the *last* child. Tree method draw_childline {vn cvn} { instvar c vcnt vnode c2v instvar xgridscale ygridscale xshapescale yshapescale outlinewidthscale \ xgridoffset ygridoffset set gx $vnode($vn,gx) set gy $vnode($vn,gy) set x [expr ($gx * $xgridscale) + $xgridoffset] set y [expr ($gy * $ygridscale) + $ygridoffset] set cgx $vnode($cvn,gx) set cgy $vnode($cvn,gy) set cx [expr ($cgx * $xgridscale) + $xgridoffset] set cy [expr ($cgy * $ygridscale) + $ygridoffset] set xmid [expr $x + (($cx-$x)/2)] if {$cgy==$gy} { set line [$c create line $x $y $xmid $y -tags cline] } else { set line [$c create line $x $y $xmid $y $xmid $cy -tags cline] } set c2v($line) $vn set vnode($vn,cline) $line $c lower $line } #XXX cursor color #consider not plotting below a certain size? Tree method plot_cursor_loc {gx gy} { instvar c cursor_items cursor_status xgridscale ygridscale if {$cursor_status} { $self unplot_cursor } set cursor_status 1 set x [expr $gx * $xgridscale] set y [expr $gy * $ygridscale] #can place arbitrary number of items in list, for now, just one rectangle. lappend cursor_items [$c create rectangle $x $y \ [expr $x+$xgridscale] [expr $y+$ygridscale] -fill lightgreen] #later reverse list for lowering? foreach item $cursor_items { $c lower $item } } #Plot cursor on vnode vn Tree method plot_cursor {vn} { instvar vnode set gx $vnode($vn,gx) set gy $vnode($vn,gy) $self plot_cursor_loc $gx $gy } Tree method unplot_cursor {} { instvar c cursor_items cursor_status foreach item $cursor_items { $c delete $item } set cursor_items {} set cursor_status 0 } Tree method erase {} { instvar c vnode vcnt c2v $self unplot_cursor $c delete all catch {unset vnode} catch {unset c2v} #Should we reset the vnode counter? # set vcnt 0 #A reasonably safe scrollregion setting: $c configure -scrollregion {0 0 1 1} } #For compatability... Tree method wipetree {} { $self erase } trn-4.0-test77/support/tk/debug.tcl0000644000000000000000000000245307113133375015744 0ustar rootroot#debug.tcl #A sample of a non-object-oriented extension using TrnModule #later: # * horizontal scrollbar for text? #old way--not recommended in tktrn #set dbg_frame [toplevel .dbg] #New way--will allow window position to be saved, other benefits later. set dbg_frame [TrnModule .dbg -toplevel 1] text $dbg_frame.t -width 60 -height 12 \ -yscrollcommand "$dbg_frame.vscroll set" #No generic toplevel bindings bindtags $dbg_frame.t [list $dbg_frame.t Text $dbg_frame] entry $dbg_frame.eval -width 60 -relief sunken -textvariable dbg_eval bindtags $dbg_frame.eval [list $dbg_frame.eval Entry] bind $dbg_frame.eval {dbg [eval $dbg_eval]} label $dbg_frame.errinfo -textvariable errorInfo -justify left -height 3 \ -anchor nw bind $dbg_frame.errinfo <1> \ {dbg "error info follows:\n$errorInfo\n---end of error info---"} scrollbar $dbg_frame.vscroll -command "$dbg_frame.t yview" pack $dbg_frame.eval $dbg_frame.errinfo -side top -expand yes -fill both pack $dbg_frame.vscroll -side right -fill y pack $dbg_frame.t -expand yes -fill both proc dbg {txt} { global dbg_frame $dbg_frame.t insert end $txt $dbg_frame.t insert end "\n" } dbg "----start of debug output----" bind $dbg_frame.t { dbg "--------------------" } bind $dbg_frame.t { $dbg_frame.t delete 2.0 end } trn-4.0-test77/HelpFiles/top0000644000000000000000000000015307113133243014405 0ustar rootroot"Trn Quickstart" &quickstart "What's New?" &changelog "Helpful Hints" &hints "Configuration" :config/index trn-4.0-test77/HelpFiles/quickstart0000644000000000000000000000261707113133243016004 0ustar rootrootTrn Quickstart: An introduction for people who don't like reading documentation. This document is a very minimal introduction to most of the new features of trn. ** This is very preliminary! ** General: + Use the arrow keys to move around. + When you see a list of selections, use the letter/number keys to choose things, then press RETURN. + Typing 'q' will almost always exit what you are currently doing, even various command prompts. + Type 'h' (almost) anywhere to get help. In most places you'll enter the help selector, where you can read the trn documentation. + Type 'H' (almost) anywhere to get help on the current commands. Options: + Type '&' and a RETURN to enter the option selector. + Once you use the 'S'ave command, you'll have a ~/.trn/trnrc file that contains the current state of the options. Trn uses this file in preference to your TRNINIT variable _unless_ you modify the trnrc file manually to tell it to use both (or remove the trnrc file). Multiple News Servers: + Create an access file in your ~/.trn directory -- see the Sample Files for a starting point. + Use Ctrl-N and Ctrl-P to switch news servers from the newsgroup selector of the newsgroup browser (ala rn). + Optionally, turn on the Newsrc Selector to select a news source as you start trn (change the option, save, and restart trn). ...More stuff to come later... trn-4.0-test77/HelpFiles/changelog0000644000000000000000000006417311437640113015551 0ustar rootrootThis file will acquaint you with the user-visible changes in trn 4.x. If you're upgrading from trn 2.x see the discussion of the -a option, the 't' command (newsgroup selection level), and the 'T' command (article level & in kill files) for slight incompatibilities between trn 2.5 and later versions of trn. Changes from trn 3.6 to trn 4.0: o Added a newsgroup selector to make choosing news groups easier (use the ++ option to turn it off). o In the selector '\' now is used to execute a command, so if you had 'c' in your SELECTCHARS, you could still catch-up a group via \c. It also lets you enter the normal newsgroup commands from the newsgroup selector, such as unsubscribe (\u) and goto group (\g). If the command letter is both a selector command and a newsgroup command, use '\\' to get the newsgroup command (such as \\O or \\h). Use ':' to apply a (limited set) of commands to more than one group, such as ":u" (unsubscribe) or ":c" (catchup). o Use "\v" from any selector to see the current trn version. o Use the backtick (`) command to switch back and forth between the newsgroup selector and the rn-style newsgroup browser. You can also type backtick from inside a newsgroup to exit back to the newsgroup selection level. o Use the '=' command in the newsgroup selector to refresh the article counts (possibly refetching the active file). o Use the Ctrl-G command in the selector to go to a letter without affecting it (e.g. "Ctrl-G b" would go to letter 'b'). o Added an addgroup selector to ease the arrival of new groups. o Strn's article scan/scoring is now present in trn. o ** Describe the universal selector once it settles down ** o Enabled trn to use both nntp and local news sources, either separately or at the same time. Create a .trn/access file to customize this (start with a copy of /access.def). o Changed the format of trn's option files. Trn will create a .trn/trnrc file for you using your old trn options as soon as you edit and save them using the on-line option editor. Once this happens, the old TRNINIT variable is ignored. o When you specify a news source in the active file, you can tell trn to keep a local copy of the server's active and newsgroups files, and either re-grab them at regular intervals (however often you like), update them with new information we glean while running, or leave them alone (which is useful for an NFS mounted active file). o Added a newsrc selector to choose among the news sources and newsrc options in your .trn/access file. o Alternately, use Ctrl-N and Ctrl-P in the newsgroup selector or newsgroup browser to change from news source to news source. o Added an on-line option editor (type '&' ). Use the search command (/erase) to find the option you're looking for. o Much improved xterm mouse handling now lets you define a "mouse bar" of buttons to press, as well as allowing you to click on the tree nodes in the article tree display. Use the option selector to turn it on and customize it. See the /INIT file for an example of how to make the mouse code conditional on your terminal type. You can even double-click in the selector to start reading and select a range with click-drag-release. o Enhanced the mime code so that trn now knows how to handle basic mime types, and farm out the ones it doesn't understand. !!Not finished yet!! o Use the 'a' (attachments) command to view an article's attachments, including a uuencoded picture in a picture group. Even works with multi-part pieces if you use 'a' on all the parts (in any order). Use ":a" to view a group of selected articles (handy from the selector). Of course, "::a" affects all unselected articles. o The header hiding code now hides more headers by default, and can un-hide any header (prior trns could only hide known header names plus all unknown headers in a group). o You can now specify a custom IP port for an NNTP connection in your .trn/access file or the NNTPSERVER variable. o A bargraph (for percent done) pops up on slow tasks (such as fetching the active file over a slow link). o Changed the default "auto arrows" (-A) to be more web- browser-like in the selectors. Set option "Auto Arrow Macros" to "old-style" if you like the old style better. o A client ("mini") inews is now included with trn that is compatible with other news readers and a bit more functional. o Macro writers: each selector has a different mode letter (see the %m discussion in the man page for a list), but there's now an easy way to write a macro that affects all selectors test the "general mode": %g=s. See the man page for the other general modes. o Fixed the type-ahead eating to never eat just part of a macro sequence (such as a keypad key or a mouse click). o Trn now use X-Newsgroups instead of Newsgroups in mailed replies. It also uses X-Also-Posted-To when Cc'ing people while posting. o In-Reply-To lines are now used for threading as well as References lines, which makes reading gatewayed mailing lists a lot easier. Changes from trn 3.5 to trn 3.6: o Added support for slow network connections by: (1) making use of the LIST ACTIVE extension (if available), (2) beginning to display the received article as it is received from nntp, and (3) making the Pnews (posting) script query the group info more efficiently. o Changed nntplist's command-line syntax slightly so that it is more orthoganal and so that it can pass an argument to LIST ACTIVE or LIST NEWSGROUPS. ** If you use nntplist in your own scripts you'll need to switch them over to using the -o (output) option. ** o Added :p command to post from the selector. o Added the Ctrl-E command to display the end of the current article. o Enhanced the ':' command to operate on non-selected thread/articles (use ::cmd) or to operate on just the current thread (use :.cmd or ::.cmd). o Added a 't' modifier for the search command to start the search at the top of the group. E.g.: /subject string/t:+ The default for searches in the selector has always been the top of the group, so this only affects the command while reading articles. o Improved the catchup command to allow you to specify an article count to leave unread in the group. o Added a new sort order for the article selector -- by 'n'umber. o Enhanced the %( arg = regex ? : ) syntax to allow a % expansion in the regex portion of the comparison. o Enhanced xref code to not go sub-optimal on C news's single-group xref lines. o Added support for generic authentification (available in the reference NNTP 1.5.12 and INN 1.5). o Fixed an elusive crash bug that would not let some people run trn from cron. o Other misc. bug fixes. Changes from trn 3.4.1 to trn 3.5: o Enhanced the -p option to allow you to select how you'd like your postings to be selected. The default (which works the same as before) is -p (select your posting and its replies). Also available is -p+ (select all postings in the thread) and -pp (select the *parent* article and its replies). o Added the forward (^F) command to forward an article via mail. o Improved the tab command (skip cited text) to skip empty lines and choose the quotation character more intelligently. o You can now junk an article in just the current group via a search command using 'x'. E.g. /subject/:x o Included some code from Olaf Titz that allows you to tell trn to transform high-bit characters into their 7-bit ascii equivilents. See the _C command and the -Q option. o Added the back-scroll command: 'B'. o Added the -J option to allow you to join truncated subjects into a common thread. The default for -J is 30 chars, not counting the Re: portion. I have the lines "&-J27" and "X&+J" in my Babylon 5 kill file to join all the Genie posts that get truncated into the proper thread. o Added the -K option to keep a group static (no new articles) while you read it. Useful for people who have a really slow kill file for a group -- use "&-K" and "X&+K" in such a kill file to make only that group stop growing until you exit the group and re-enter. o Added optional mouse support in an xterm window. o Added a new intrp (%q) to get the value of the last quoted input (%"). Useful for using elm for forwarding articles as it needs the answer to the question "To?" on the command line: -EFORWARDPOSTER="elm -i %h -s '%'[subject]' %q" (though you may wish to redefine the FORWARDHEADER variable too). o Fixed bugs in the handling of thread kills in partially-threaded groups (Tj now works even if the -a option wasn't specified) and and in the handling of the subject-kill command (Aj). o New files, HINTS.TRN is like HINTS but in a better format. (I renamed it to avoid conflicts with the directory "hints"). o Portability enhancements to Configure and the support scripts. o Some fairly major and minor bugs fixed. Changes from trn 3.3 to trn 3.4.1: o Made the kill-file handling more consistent in how it deals with the THRU line. It now only ignores the THRU line for all selection commands. You can turn off this exception (and thus make all commands obey the THRU line) by turning off the -k option (using +k). You can also specify the 'I' modifier or the 'N' modifier to killfile searches to have them either ignore or not-ignore the THRU line, respectively. o Changed the %'s interp to not supply the leading and trailing single quotes to make it more useful. o The NNTP trn attempts to reconnect to the news server after it times out. If it is successful, trn continues. o The command /subj/M no longer assumes you meant /subj/r:M. o Automatically-generated killfile commands (e.g. the 'K' command) now escape a '/' that occurs in the subject string. o The _+ command (select whole thread) now starts reading the thread from the first unread article. o The -p self-matching code was improved to match a user's name more exactly so that people with short login names don't get improper matches. o Pnews was enhanced to deal with Followup-To: poster better when you choose to post anyway -- it now puts the user's address into the Cc: header and automatically corrects the Newsgroups: line. o Pnews returns to the prompt after an inews error instead of aborting. You can still choose to 'a'bort, if you like. o Speller now passes the ispell_options from Configure to spell, and Configure has been enhanced to prompt you for the options if either ispell or (new!) vspell can't be found. o Added the file Policy.sh.SH that puts your policy choices from the config.sh file into hints/Policy.sh. This allows you to use the Policy.sh file to prime the Configure choices on multiple machines or in the mthreads package without machine-specific selections getting in the way. o Upgraded Configure to the latest metaconfig release. o Fixed some MIME bugs and made the handling a little smarter. o Nested comment warnings removed from various include files. o Fixed several crash bugs and several nusiance bugs, including: * the problem with the NNTP trn mangling the date of new news groups (which caused either groups to get missed or groups to be re-offered). * the problem with the NNTP trn that caused it to think certain lines that begin with a '.' are the end of the list marker and quit when it got confused. * and lots more... Changes from trn 3.2 to trn 3.3: o Newsetup now looks for NEWSLIB/subscriptions for a default list of groups to subscribe the user to. If the file doesn't exist the NNTP version will attempt to grab it via the LIST SUBSCRIPTIONS command (available in INN and some nntp patches). [HINT: if you want your subscription file to default to all groups in the active file, link your subscription file to your active file -- trn will strip the info past the first space when it processes the .newsrc.] o The file newsnews will now default to a simple version update message instead of a welcome-to trn message (which is now provided by the newsetup file when creating a new .newsrc for a user). I still encourage you to install your own custom newsnews when trn is updated (and trn still doesn't install newsnews automatically -- you have to make this decision for your self). o Redirected and disabled groups (marked by '=' or 'x' in the active file) are now handled better, allowing you to read any remaining articles after a group gets redirected or disabled and warning you to either start using the new group name or that the group will not be receiving any new news. o Mime support is now prompted for in Configure and your system's display/store commands are remembered. We also handle a continued Content-Type header correctly now. o The tick (') interp modifier will generate a tick-quoted string with all ticks inside the string quoted. For example, %'s might generate (INCLUDING the "'"s) 'Ticks aren\'t a problem.' o If the environment variable FAST_PNEWS == y Pnews skips the "Are you sure?" question and the "include file" prompt. You can put -EFAST_PNEWS=y in the global INIT file, if you so desire. o Various bug fixes. Changes from trn 3.1 to trn 3.2: o Configure and the include file structure has been improved to install easier on more systems. o Fixed some non-portable test statements in Pnews. o Added the -Z option to allow you to turn on/off support for the two different database formats (-Zt, -Zo, -Zot, or +Z). o Configure now allows you set the default database support to thread files, overview files, both or none. o Trn and its support scripts handle ~/dirs in the newslib or rnlib better. o Various bug fixes. Changes from trn 3.0 to trn 3.1: o Added a new header-searching syntax: /string/Hheader:cmd. This allows you to match a string on any header that trn knows about and is faster on the NNTP side than full-header matching. Example: /: .../Hlines:j would junk all postings longer than 99 lines. o Two new commands can be put into a group's kill file: *j (kill all articles from THRU to the end of the group) and *X (kill all unselected articles from THRU to the end of the group). o Pnews (using artcheck) now checks the active file as well as the newsgroups file for each group on the Newsgroups line. This lets you know whenever you specify a group that doesn't exist. Also, the NNTP version has been fixed to provide full checking. o Pnews/Rnmail have been modified to allow you to type either upper or lower case for the response letters, and use "sp*ell" in addition to "c*heck" for the spelling checker ("s*end" is unchanged). o Pnews/Rnmail each have a pre-edit signature appending option. If you use the file DOTDIR/.news_sig INSTEAD of .signature Pnews will append your signature before you edit the file. Rnmail uses the file DOTDIR/.mail_sig. Note also that the names may be customized with the environment variables NEWSSIGNATURE and MAILSIGNATURE, if desired (allowing a per-group signature). o Added the modifiers '>' and ')' to interpreted strings. For example: %>f gives you the address only on the From line, while %)f gives you the real name portion. o Added the 'O' command (newsgroup level) that works just like 'o' except that it does not visit empty groups. o The killfile is fully saved before manual editing with Ctrl-K (message-id commands used to be saved only on exit from the group). o The screen is now refreshed when the window size changes. o Trn now makes use of stdlib.h, unistd.h (if available) and does a more intelligent inclusion of time.h & sys/time.h (as needed). o Trn doesn't loop through the current thread if unread articles still exist in it. Also fixed various problems with '>' & 'P'. o The 'D' command in the selector no longer jumps back to the top. o The NNTP version has a different new-article aquisition strategy. Your nntp server must send up-to-date information in response to the GROUP command (INN's nnrpd does) for a group to expand while you're in it. Also, you can now set the minimum time for trn to wait (see -z) before it refetches the active file at the end of the newsgroups. The default is 5 minutes. o Optional metamail support pauses between the headers and the metamail-interpreted article. Also, the 'v' command displays the raw article without metamail processing. o Numerous portability changes and bug fixes. Changes from trn 2.5 to trn 3.0: o Trn is now capable of reading more news database formats. It currently supports news overview (.overview files), mthreads (.thread files), and direct threading of the articles. The NNTP version supports the XTHREAD and XOVER NNTP extensions. If you compile trn with support for both formats it will figure out which groups (or which server) has which type of data and act accordingly. o Mthreads is now a separate package from trn since not everyone will need to use it. Look for it in the same place you found trn. o Trn attempts to build some useful default macros for your terminal's arrow keys. On the article level they move around in the thread; in the selector they change pages (left/right) and switch selections (up/down); on the newsgroup level they move by group (up/down) and enter a group (right). If you don't like this, turn it off with the +A option. o There's a new search scope -- the from line. For example: use /author/f+ to search for and select 'author's articles. o The thread selector has been extended to be a subject and article selector. Use the 'S'et selector command to change modes or use '=' to toggle between the article selector and the subject/thread selector (whichever was last in use). o The selector can now be sorted in a variety of ways: by date, subject, author, article count (in the subject/thread selector), or a combination of subject and date (in the article selector). The default is date order of the oldest unread article in a thread. Use the 'O'rder command to pick a new one or use 'R' to reverse the sort. See also the "-O" option to set your favorite mode and order. You can even put a "&-Oas" command (for example) into a group's kill file to set a per-group default. o The selector allows you to exclude all the non-selected articles from the display (i.e. narrow it) -- use 'E' to toggle this mode. o The selector has two new selection commands: '*' is used to select (or deselect) the current item and all other items with the same subject (useful in the article selector); '#' is used to make an overriding selection that immediately reads the current item ignoring all other selections. o You can now type 'M' in the selector to mark the current item's articles as read-but-returning and press 'Y' to yank back and select these articles before exiting the group. o Selections via searches are article-oriented (/subj/+) or thread-/subject-oriented (/subj/++). The article selector's default command is "+", while the thread/subject selector's is "++". In other words doing a "/subj" search with no specified command selects whatever type of object you're looking at in the selector. o If you specify the "-p" option, your postings and any replies to them are auto-selected whenever trn encounters them. o The '+' command in a non-threaded group visits the subject selector. You can also use "_a", "_s", "_t" or "_T" to force the article, subject, thread, or thread-but-I'll-settle-for-subject selector. o The selector displays subjects/threads that are partially-selected with a '*'. Fully-selected items are marked with a '+', as before. Use the article selector (possibly with 'E'xclusive set) to see which articles are selected in a partially-selected group (or just read them). o The selector remembers which subjects you selected (and didn't kill) and marks any newly-arriving articles in these subjects as selected until you exit the group. o The medium display mode of the thread selector has been improved to make it more readable. o The selector will leave out the middle portion of a subject that is too long to display the last two words of the subject. If you don't like this, use the -u option to leave them unbroken. o 'T'hread KILL commands now use message-ids to either junk or select articles. The 'T' command has been extended to be more flexible on the article level and has been added to the selector. o Another new command 'A'dds selection or kill commands to the KILL file, and works from both the article level and the selector. o The tree display has been updated to display unread-but-not- selected articles as . Other unread articles are still [x], while read articles still display as (x). o Trn can enter a group without thread information available and thread it in the background while you read. Articles that have references that may or may not exist show up as "(?)". If you visit an article like this and wait there, the screen will update when we know for sure one way or the other (after processing more of the group). o The -a option is used to tell trn to thread all of the articles on entry to the group. If you don't specify this option a group may have a few (or many) articles that get threaded in the background and won't show up on the tree display until trn processes them. o Pressing "_+" on the article level will select the entire thread associated with the current article -- useful if you've selected individual articles and wish to read the rest of the discussion. Use "_-" to deselect the current thread. o The commands _N and _P move to the next and previous article in numberical (arrival) order (article level). Thus, you can use the command "._P" on the newsgroup level to start reading a group from the very last article to arrive. o The 't' command on the newsgroup level now turns OFF reading a group with threads (this setting is stored in the .newsrc file, so it is remembered from session to session). Trn 2.x used this to force threading to be turned ON, but it wasn't usually needed for normal operation. To temporarily turn threads on once inside a non-threaded group, use the 't' or "_t" commands (article level) or the "St" command (selector). o Trn now checks for the environment variable TRNMACRO on startup (which defaults to DOTDIR/.trnmac) before checking for the usual RNMACRO (DOTDIR/.rnmac) file. If you're running trn in its rn- compatible mode, only RNMACRO is tried. o The threaded and non-threaded data in a group has been unified, resulting in more cached information on the non-threaded side (such as the from line) and more efficient handling of missing articles, just to name two benefits. o The newsgroup information is freed when we enter a new group, not when we exit the current group. This means that if you quit out of a group (even accidentally), you can go back in and everything is still there except the selections, which get cleared on group exit. o KILL file processing will now ignore the THRU line as much as possible without slowing down the handling of KILL files. If you have really slow searches (header or article searches) or you use trn without a database it will use the THRU line to only search an article one time (as it would in rn). This means that if you have subject-oriented selection commands you don't have to worry about missing articles if you don't read all of them the first time you enter a group after they arrive. This also means that you won't have to edit your local kill file to remove the THRU line to force a re-scan -- this is now unnecessary. o Header parsing is now done in-memory, making threading and caching of articles much faster. This especially helps out NNTP users because trn used to write a tmp file for every header parse. o Several new mode letters (accessed by %m in macros) were added. The most significant are 'f' for the end (Finis) of the newsgroup selection level (instead of 'n') and 'e' for the end of the article reading level (instead of 'a'). o A new % modifier has been introduced: "%:FMTx". This allows you to apply a printf-style column format to a regular %x expansion. For example, %:-50.50s would left-justify the subject into 50 characters, exactly. o The -f option will make trn go a little faster by getting rid of the delay/prompt after kill file processing, printing the "skipping article" message, and printing the "Depositing KILL command" message. This is the default if -t (terse) is specified, but can be overriden by specifying +f after the -t option. o A new option for the gadget-conscious (-B) displays a spinner when trn is processing articles in the background. o Added the -G option to make the newsgroup 'g'o command look for near matches (for those typing mistakes). o New newsgroups that are left unsubscribed are not appended to the .newsrc unless you use the -I option or you're running an NNTP version that does not use the NEWGROUPS code. o Support for metamail's mime handling is now built into the code -- see the METAMAIL define in common.h. o Pnews does more checking of your article before posting, has a spelling-check option, and allows the Cc: header to be used to send mail while posting the article. Changes from trn 1.0.3 to trn 2.5: o Added the '(' and ')' commands (article level) to move to an article's previous/next sibling, including "cousin" siblings. o The 'A'bandon command (newsgroup level) forgets all changes to the current newsgroup since you first started trn. o The thread selector now allows you type type 'c'/'y' to catchup the group without chasing cross-references. o Added 'z' and 'Z' commands (article level) supersede the current article ('Z' also including the original text). o The g command (newsgroup level) will go to a newsgroup by number where the the number corresponds to that shown in the 'L'ist command. o Added the "-o" option to use the old method of junking articles in the thread commands (they didn't chase cross-references in trn 1.x) o Added the "-b" option to read articles in a breadth-first descent of the article tree. You can specify this command at runtime to switch from breadth-first (type "&-b") to depth-first ("&+b). o Added the "-j" option to tell trn to pass control characters through the pager unharmed. trn-4.0-test77/HelpFiles/hints0000644000000000000000000001566207113133243014743 0ustar rootrootYou can specify trn environment variables in an initialization file with all the options. See the first hint for details on that. Certain environment variables, like RNINIT, must be set in your shell (how else does trn know to look there for the rest?). Others, like REPLYTO may be useful in other programs, so you could consider putting that in your shell startup file too. But most of them are specific, like MAILHEADER, so you might as well set them in the init file with the -E option. That's why most of them look like -EMAILHEADER=xx rather than MAILHEADER=xx -- to remind you. Helpful hints: + Two useful flags are the -p and -G flags. -p means to automatically select any thread that you have posted a message to. This makes it very easy to track replies to your insightful comments :-) (to automatically select other threads, try the 'T' command). -G tells the 'g' command to use a loose match algorithm on groups it can't find. So if you type "g news.softwre.raeders.genrl", it will assume that you meant to type news.software.readers and take you there. It is usually easier to type "/reader" than using a 'g' command, but use -G too. + The 'X' command in the selector (kinda like catchup/yes) can be made the default action on the last page of selections by using the command-line option: -XX. This lets you browse all the pages, making selections as you go, and then mark everything that was not selected as read when you press space at the last page. + If you like to select a little, read a little, and then select a little more, try using the command-line option: -XDD. This makes the 'D' command the default on all pages of the selector. Thus you can select the things you want to read on the current page, press space, and you will read the selected articles immediately (if there were any). All other articles on the current page will be marked as read. + To use mush to send all R)eplies, put this in your .trn/trnrc file: [environment] MAILPOSTER="mush -h %h" + To use elm to send all R)eplies: [environment] MAILHEADER="\\000" MAILPOSTER="elm -i %h -s '%'s' %t" + To tailor the time field: A good example is LOCALTIMEFMT="%A, %B %e %Y, %r %Z" which becomes: Date: Saturday, June 12 1993, 08:15:32 PM PDT. (The default is "%a %b %e %X %Z %Y", which is like the date(1) command). See the strftime(3C) man page for more details on other % characters. + To tailor the "In article , joe@user (Joe User)" line: person's name only: %)f person's address only: %>f For example: ATTRIBUTION='According to %)f <%>f>:' + A better kill file scheme than the default (which creates obtuse directories all over your ~/News directory) is to use the following: KILLGLOBAL="%p/Kill/Global" KILLLOCAL="%p/Kill/%C" Where %p is your news directory, usually ~/News, and %C is the full name of the group, like sci.bio. This means the kill file for sci.bio is in ~/News/Kill/sci.bio instead of ~/News/sci/bio/KILL. Think about this next time you subscribe to alt.swedish.chef.bork.bork.bork :-) Note that you need a file system that allows long file names to make this work. + If you're tired of endless subscription notices to newsgroups you could care less about, look at the AUTOUNSUBSCRIBE variable. There's a companion AUTOSUBSCRIBE function too. For example, to automatically unsubscribe all NEW alt groups, use ^alt.*. More dramatic, to unsubscribe to everything BUT certain groups, use "*,!^local.*,!^comp.*", for example. If you're REALLY tired of them, use -q to tell trn to not even check for new groups at all. + If you want replies to your postings to go to a machine other than the machine you're posting from, set the REPLYTO environment variable. For example, you might set this if you were trn's author: REPLYTO="wayne@clari.net (Wayne Davison)" + You might like to redefine the 'u' keys in the following way: 'u' visits the "Set unread?" prompt (except in the thread selector), 'U' goes directly to the already-read article selector, and Ctrl-U unsubscribes from the group (even while in the thread selector). Put the following 3 macros in your .trn/macros file to accomplish this: u %(%g=r?U:u) U %(%g=r?U+:U) ^U %(%g=s?+u:u) + If you like to be able to move forward/backward by article number more often than you need to search by subject, redefine ^N and ^P to be _N and _P by putting these lines into your macro file: ^P %(%m=[aep]?_P:^P) ^N %(%m=[aep]?_N:^N) + If you like the way that 'q' worked in the thread selector in trn 1.x, put the following macro in your macro file: q %(%g=s?+:q) + If you would like the 'f' command to always answer yes to the "Are you starting an unreleated topic?" question, put this line into your macro file: f %(%m=[ap]?fy^m:f) + If you want to be able to save your shar headers in a file as they are extracted and you have access to "unshar" (a program that can extract shar files while saving the headers in UNSHAR.HDR), twiddle the external extract command to send the entire article: EXSAVER="%e <%A" and then point the UNSHAR variable at unshar instead of /bin/sh: UNSHAR="unshar -s" Note that this assumes that any other commands you execute with "e dir|command" can also handle the entire article as input (uuencoded and shipped files are not affected). + The following lines represent trn's default headers for posting an article. If you want to customize your header copy this to your .trn/trnrc file and modify it (and _don't_ use Pnews directly to post, use trn -- the ":post" command from newsgroup's selector works well for this). [environment] NEWSHEADER='\ %(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n ))Newsgroups: %(%F=^$?%C:%F) Subject: %(%S=^$?%"^J^JSubject: ":Re: %S) Summary: Expires: %(%R=^$?:References: %R )Sender: Followup-To: %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} )Distribution: %(%i=^$?%"Distribution: ":%D) Organization: %o Keywords: %[keywords] Cc: %(%F=poster?%t:%(%F!=@?:%F)) ^J' At one point in life, trn's author had the following .trninit file: ---------------------------------------------------------------------- -+ -XX -B -N -f -x11ms "-F> " -pp -G -u -mu -a -hunrecognized -ELOCALTIMEFMT="%A, %B %e %Y, %r %Z" -ESELECTCHARS="abdefgijlmorstuvwxyz1234567890BCFGHIKVW" -EKILLLOCAL="%p/Kill/%C" -EKILLGLOBAL="%p/Kill/Global" -EMAILPOSTER="elm -i %h -s '%'s' %t" -EFORWARDPOSTER="elm -i %h -s '%'[subject]' %\"^J^JTo: \"" -EFORWARDHEADER='^J' -EATTRIBUTION="According to %)f <%>f>:" ---------------------------------------------------------------------- and the following .trn/macros file: ---------------------------------------------------------------------- ; %(%m=p?|less^J:;) u %(%g=r?U:u) U %(%g=r?U+:U) ^U %(%g=s?+u:u) ~T s ~/Mail/trn ~M | /bin/mail davison ^[^[ ^[ ---------------------------------------------------------------------- trn-4.0-test77/HelpFiles/config/index0000644000000000000000000000037207113133243016162 0ustar rootroot"Environment Variables" &environment "Options" &options "Macros" ¯os "Sample Config Files" :../samples/index "Your Config Files" ->yours >:yours "Your trnrc file" &%+/trnrc "Your access file" &%+/access "Your macros file" &%{TRNMACRO-%+/macros} trn-4.0-test77/HelpFiles/config/macros0000644000000000000000000000521607113133243016341 0ustar rootrootAUTOMATIC MACROS On startup trn attempts to build a set of macros that map your keypad arrow keys to useful functions. These default actions are mentioned in the prior description of each level's commands. If you don't like this (or trn gets it wrong), you can disable the automatic macros by using the -A option. CUSTOM MACROS When trn starts up it looks for a file containing macro definitions (see environment variables TRNMACRO and RNMACRO). Any sequence of commands may be bound to any sequence of keys, so you could re-map your entire keyboard if you desire. Blank lines or lines beginning with # in the macro file are considered comments; otherwise trn looks for two fields separated by white space. The first field gives the sequence of keystrokes that trigger the macro, and the second field gives the sequence of commands to execute. Both fields are subject to % interpolation, which will also translate backslash and caret sequences. (The keystroke field is interpreted at startup time, but the command field is interpreted at macro execution time so that you may refer to % values in a macro.) For example, if you want to reverse the roles of carriage return and space in trn ^J \040 ^M \040 \040 ^J will do just that. By default, all characters in the command field are interpreted as the canonical trn characters, i.e. no macro expansion is done. Otherwise the above pair of macros would cause an infinite loop. To force macro expansion in the command field, enclose the macro call with ^( ... ^) thusly: @s |mysavescript @w w^(@s^) You can use the %() conditional construct to construct macros that work differently under different circumstances. In particular, the current mode (%m) of trn could be used to make a command that only works at a particular level. This is particularly vital for the selector which uses most of the lower-case letters to select the associated item in its display. For example, a %(%m=t?a:s art.hold\n) will return the original letter (a) in the selector, and the command "s art.hold\n" everywhere else. %(%{TERM}=vt100?^[[O) /^J will do the binding only if the terminal type is vt100, though if you have many of these it would be better to have separate files for each terminal. If you want to bind a macro to a function key that puts a common garbage character after the sequence (such as the carriage return on the end of Televideo 920 function sequences), DO NOT put the carriage return into all the sequences or you will waste a CONSIDERABLE amount of internal storage. Instead of "^AF^M", put "^AF+1", which indicates to trn that it should gobble up one character after the F. trn-4.0-test77/HelpFiles/config/options0000644000000000000000000005265007113133243016554 0ustar rootrootOPTIONS Trn has a nice set of options to allow you to tailor the interaction to your liking. (You might like to know that the author swears by "-x6ms +e -mu -S -XX -N -B -p".) These options may be set on the command line, via the TRNINIT environment variable, via a file pointed to by the TRNINIT variable, or from within trn via the & command. Options may generally be unset by typing "+switch". Most of these options can also (and more easily) be set using the Option Selector. The corresponding items from the Option Selector are listed next to the options below. You can set and change options from within trn with the "&" command followed by the option. For example, to enable debug output during a trn session, use "&-D". Options include: -a (Background Threading) causes trn to always thread the unread articles on entry to a group. Without this option trn may enter a group in a partially-threaded state and process the unthreaded articles in the background. The down side of this is that the tree display may not be complete when it is first displayed and you may start out at an odd position in the first thread's article tree. -A (Auto Arrow Macros) tells trn to attempt to create some default macros that will map your arrow keys to useful trn functions (this is the default). Use +A to turn this behavior off. -b (Read Breadth First) will force trn to read each thread in a breadth-first order, rather than depth-first. -B (Background Spinner) will turn on a spinner that twirls when trn is doing background article-processing. A gizmo for those interested in what's going on behind the scenes. -c checks for news without reading news. If a list of newsgroups is given on the command line, only those newsgroups will be checked; otherwise all subscribed-to newsgroups are checked. Whenever the -c switch is specified, a non-zero exit status from trn means that there is unread news in one of the checked newsgroups. The -c switch does not disable the printing of newsgroups with unread news; this is controlled by the -s switch. (The -c switch is not meaningful when given via the & command.) -C (Checkpoint Newsrc Frequency) tells trn how often to checkpoint the .newsrc, in articles read. Actually, this number says when to start thinking about doing a checkpoint if the situation is right. If a reasonable check-pointing situation doesn't arise within 10 more articles, the .newsrc is check-pointed willy-nilly. -d (Save Dir) sets your private news directory to something other than ~/News. The directory name will be globbed (via csh) if necessary (and if possible). The value of SAVEDIR (where articles are saved) is initially set to this directory, but is often manipulated via the -/ option or by manipulating SAVEDIR directly (perhaps via the memorized commands (the KILL file) for a group. Any KILL files (see the K command in the Article Selection section) also reside in this directory and its subdirectories, by default. In addition, shell escapes leave you in this directory. -D enables debugging output. See common.h for the full list of flag values. Most commonly useful is -D16, which produces an NNTP protocol trace. Using no flag enables general debug output. Shut off debug output with +D. Using any debug output requires that trn be compiled with debug support. Warning: normally trn attempts to restore your .newsrc when an unexpected signal or internal error occurs. This is disabled when any debugging flags are set. -e causes each page within an article to be started at the top of the screen, not just the first page. (It is similar to the -c switch of more(1).) You never have to read scrolling text with this switch. This is helpful especially at certain baud rates because you can start reading the top of the next page without waiting for the whole page to be printed. It works nicely in conjunction with the -m switch, especially if you use half-intensity for your highlight mode. See also the -L switch. -E= sets the environment variable to the value specified. Within trn, "&-ESAVENAME=%t" is similar to "setenv SAVENAME '%t'" in csh, or "SAVENAME='%t'; export SAVENAME" in sh. Any environment variables set with -E will be inherited by subprocesses of trn. -f (Novice Delays) will make trn avoid various sleep calls and the prompt after the processing of the memorized commands that are intended to allow you time to read a message before the screen clears. This allows the advanced user to cruise along a little faster at the expense of readability. The -t (terse) option turns on -f by default, but you can override this by specifying +f after the -t option. -F (Cited Text String) sets the prefix string for the 'F' follow-up command to use in prefixing each line of the quoted article. For example, "-F" inserts a tab on the front of each line (which will cause long lines to wrap around, unfortunately), "-F>>>>" inserts ">>>>" on every line, and "-F" by itself causes nothing to be inserted, in case you want to reformat the text, for instance. The initial default prefix is ">". -g (Line Num for Goto) tells trn which line of the screen you want searched-for strings to show up on when you search with the 'g' command within an article. The lines are numbered starting with 1. The initial default is "-g1", meaning the first line of the screen. Setting the line to less than 1 or more than the number of lines on the screen will set it to the last line of the screen. -G (Fuzzy Newsgroup Names) selects the "fuzzy" processing on the go command when you don't type in a valid group name. With this option on trn will attempt to find the group you probably meant to type, but it can be a little slow about it, so it's not on by default. -h (Header Hiding) hides (disables the printing of) all header lines beginning with string. For instance, -hx- will disable the printing of all "X-Foo:" headers. Case is not significant. The default for unrecognized headers can be set with the -hunrecognized option. Alternately you could use -h (no string) to disable all headers except the Subject line and then use +h to select only those lines you want to see. You may wish to use the baud-rate switch modifier below to hide more lines at lower baud rates. -H (Header Magic) works just like -h except that instead of setting the hiding flag for a header line, it sets the magic flag for that header line. Certain header lines have magic behavior that can be controlled this way. At present, the following actions are caused by the flag for the particular line: the Date line prints the date in local time if the group is threaded; the From line will only print the commented portion of the user name; the Newsgroups line will only print when there are multiple newsgroups; the Subject line will be underlined and (when threaded) the keyword 'Subject:' is replaced by its subject number (e.g. [1]); and the Expires line will always be suppressed if there is nothing on it. In fact, all of these actions are the default, and you must use +H to undo them. -i= (Initial Article Lines) specifies how long (in lines) to consider the initial page of an article -- normally this is determined automatically depending on baud rate. (Note that an entire article header will always be printed regardless of the specified initial page length. If you are working at low baud rate and wish to reduce the size of the headers, you may hide certain header lines with the -h switch.) -I (Append Unsubscribed Groups) tells trn to append all new, unsubscribed groups to the end of the .newsrc. -j (Filter Control Characters) forces trn to leave control characters unmolested in messages. -J{} (Join Subject Lines) causes trn to join similar subjects into a common thread if they are the same up to the indicated number of characters (the default is 30). You can turn this on and off for specific groups by putting the following lines into your kill file for the group(s): &-J30 X&+J -k (Ignore THRU on Select) tells trn to ignore the THRU line when processing selection searches (i.e. searches with a command por- tion that starts with a '+' or a '.') in the memorized commands (aka kill files). This is turned on by default, so use +k if you want to turn it off. -K (Auto-Grow Groups) is used to keep a trn from checking for new news while you're in the group. Use this when your kill-file processing is so slow that you don't want the group to expand while you're reading. If you only want specific groups to be affected, put these lines into your kill file for the group(s): &-K X&+K -l (Erase Screen) disables the clearing of the screen at the beginning of each article, in case you have a bizarre terminal. -L (Erase Each Line) tells trn to leave information on the screen as long as possible by not blanking the screen between pages, and by using clear to end-of-line. (The more(1) program does this.) This feature works only if you have the requisite termcap capabilities. The switch has no effect unless the -e switch is set. -m= (Pager Line-Marking) enables the marking of the last line of the previous page printed, to help the user see where to continue reading. This is most helpful when less than a full page is going to be displayed. It may also be used in conjunction with the -e switch, in which case the page is erased, and the first line (which is the last line of the previous page) is highlighted. If -m=s is specified, the standout mode will be used, but if -m=u is specified, underlining will be used. If neither =s or =u is specified, standout is the default. Use +m to disable highlighting. -M (Default Savefile Type) forces mailbox format in creating new save files. Ordinarily you are asked which format you want. -N (Default Savefile Type) forces normal (non-mailbox) format in creating new save files. Ordinarily you are asked which format you want. -o will act like old versions of trn and not junk cross- referenced articles when using thread commands to junk articles in the current group (such as the selector's 'X' command). -O{} (News Selector Mode / News Selector Order) specifies the selector's mode and (optionally) the sort order. The modes are 'a'rticle, 's'ubject, or 't'hread. The orders are 'd'ate, 's'ubject, 'a'uthor, article 'c'ount per group, 'n'umeric, subject-date 'g'roups, or 'p'oints. The order can be capitalized to reverse the indicated order. For example, to choose the article selector in subject order specify "-Oas". -p{opt} (Select My Postings) tells trn to auto-select your postings and their replies as it encounters them in the various groups you read. The optional parameter is either a '.', 'p', or '+' (it defaults to '.' if omitted) and affects what command trn should execute when it encounters your postings. The default is to execute the command "T." on each of your postings which tells trn to memorize the auto-selection of this article and all its replies. Using -pp tells trn to use the same command, but start the selection with the parent article, so that you see any other replies to the same article. Using -p+ tells trn to select the whole thread that contains your reply. -q (Check for New Groups) bypasses the automatic check for new newsgroups when starting trn. -Q (Charset) defines the set of available charset conversions. This can be useful to restrict the available conversions to those your terminal can handle and/or to specify an alternate default. The first element of this set is taken as default for each article. -r (Restart At Last Group) causes trn to restart in the last newsgroup read during a previous session with trn. It is equivalent to starting up normally and then getting to the newsgroup with a g command. -s (Initial Group List) with no argument suppresses the initial listing of newsgroups with unread news, whether -c is specified or not. Thus -c and -s can be used together to test "silently" the status of news from within your .login file. If -s is followed by a number, the initial listing is suppressed after that many lines have been listed. Presuming that you have your .newsrc sorted into order of interest, -s5 will tell you the 5 most interesting newsgroups that have unread news. This is also a nice feature to use in your .login file, since it not only tells you whether there is unread news, but also how important the unread news is, without having to wade through the entire list of unread newsgroups. If no -s switch is given -s5 is assumed, so just putting "rn -c" into your .login file is fine. -S causes trn to enter subject search mode (^N) automatically whenever an unthreaded newsgroup is started up with unread articles or more. Additionally, it causes any 'n' typed while in subject search mode to be interpreted as '^N' instead. (To get back out of subject search mode, the best command is probably '^'.) If is omitted, 3 is assumed. -t (Terse Output) puts trn into terse mode. This is more cryptic but useful for low baud rates. (Note that your system administrator may have compiled trn with either verbose or terse messages only to save memory.) You may wish to use the baud-rate switch modifier below to enable terse mode only at lower baud rates. -T (Eat Type-Ahead) allows you to type ahead of trn. Ordinarily trn will eat typeahead to prevent your autorepeating space bar from doing a very frustrating thing when you accidentally hold it down. If you don't have a repeating space bar, or you are working at low baud rate, you can set this switch to prevent this behavior. You may wish to use the baud-rate switch modifier below to disable typeahead only at lower baud rates. -u (Compress Subjects) sets the unbroken-subject-line mode in the selector, which simply truncates subjects that are too long instead of dumping the middle portion prior to the last two words of the subject. -U tells trn to not write the .newsrc file out after visiting each group. While this is "unsafe" it can be faster if you have a really huge .newsrc. -v (Verify Input) sets verification mode for commands. When set, the command being executed is displayed to give some feedback that the key has actually been typed. Useful when the system is heavily loaded and you give a command that takes a while to start up. -V will output trn's version number and quit. -x{}{} (Use Threads / Article Tree Lines / Newsgroup Selector Display Styles) Enable the extended (threaded) features of trn beyond the rn compatibility mode (this may be the default on your system, use +x if you yearn for the good ol' days). The is the maximum number of article-tree lines (from 0 to 11) you want displayed in your header. Use the to choose which thread selector styles you like ('s'hort, 'm'edium, or 'l'ong), and in what order they are selected with the 'L' command. For example, use -xms to start with the medium display mode and only switch between it and the short mode. You can omit either or both of the parameters, in which case a default of -x6lms is assumed. -X{}{} (News Selector Commands) If you like using the selector, you'll probably want to use this option to make the selector command (+) the default when a newsgroup is started up with at least unread articles. (Your installer may have chosen to make -X0 the default on your system.) It is also used to select which commands you want to be the defaults while using the thread selector. For example, -X2XD will make the thread selector the default command for entering a newsgroup with at least 2 unread articles, and set the default command for the LAST page of the thread selector to be the X command and the default command for all other pages to be the D command. Either or both parameters can be omitted, as well as the second default command (e.g. -XX would change the default newsgroup entry to use the selector and the default command for the last page of the selector to be 'X'). The default is -X0Z> if just -X is specified. To set the default selector commands without having '+' be the default entry into a newsgroup, specify a high number, like 9999. -z (Default Refetch Time) sets the minimum number of minutes that must elapse before the active file is refetched to look for new articles. A value of 0 or using +z turns this off. -Z (Old Mthreads Database) is used to select what style of database you want trn to access. Use -Zt for thread files, -Zo for overview files, and +Z for none. The default is whatever your newsadmin compiled into trn, and can be -Zot to try to access either one. -/ (Save Dir / Auto Savename) sets SAVEDIR to "%p/%c" and SAVENAME to "%a", which means that by default articles are saved in a subdirectory of your private news directory corresponding to the name of the the current newsgroup, with the filename being the article number. +/ sets SAVEDIR to "%p" and SAVENAME to "%^C", which by default saves articles directly to your private news directory, with the filename being the name of the current newsgroup, first letter capitalized. (Either +/ or -/ may be default on your system, depending on the feelings of your news administrator when he, she or it installed trn.) You may, of course, explicitly set SAVEDIR and SAVENAME to other values -- see discussion in the environment section. Any switch may be selectively applied according to the current baud-rate. Simply prefix the switch with +speed to apply the switch at that speed or greater, and -speed to apply the switch at that speed or less. Examples: -1200-hposted suppresses the Posted line at 1200 baud or less; +9600-m enables marking at 9600 baud or more. You can apply the modifier recursively to itself also: +300-1200-t sets terse mode from 300 to 1200 baud. Similarly, switches may be selected based on terminal type: -=vt100+T set +T on vt100 -=tvi920-ETERM=mytvi get a special termcap entry -=tvi920-ERNMACRO=%./.rnmac.tvi set up special key-mappings +=paper-v set verify mode if not hardcopy Some switch arguments, such as environment variable values, may require spaces in them. Such spaces should be quoted via ", ', or \ in the conventional fashion, even when passed via TRNINIT or the & command. trn-4.0-test77/HelpFiles/samples/index0000644000000000000000000000013707113133243016360 0ustar rootroot"access -- define multiple news servers" &access "trnrc -- main trn configuration file" &trnrc trn-4.0-test77/HelpFiles/samples/access0000644000000000000000000001114607113133243016514 0ustar rootroot# This is a sample ~/.trn/access file. # This section defines the available news sources and gives them an # easy-to-use [ID] name. The always-present ID, "default", can also be # overriden here, if you so desire. # When defining an NNTP server, set "NNTP Server" to the domain name of # your server. If you need to use a non-standard port number, append ";119" # (for example) to the name. If you want to cache a local copy of the # active file, set "Active File" to the appropriate name. If you want # the file refetched after certain intervals, set "Active File Refetch" # to the appropriate interval (e.g. "1 day, 5 hours, 3 minutes"). # When defining a local news source (even NFS mounted), set "Active File" # and "Spool Dir". # Specify "none" for "Overview Dir" and/or "Thread Dir" to disable support # for that database type, omit or leave blank for the default access (either # "Spool Dir" for local or XOVER/XTHREAD for remote), or enter a custom # hierarchy path name. # Specify "none" for "Active Times" if the host has no new-group support, # leave it blank for the default (either active + ".times" for local access # or the NEWGROUPS command for remote), or enter a custom file name for your # active.times file. # Specify "none" for "Group Desc" if you don't have a file with newsgroup # descriptions, leave it blank for the default (NEWSLIB/newsgroups for local # access or the XGTITLE/LIST_NEWSGROUPS command for remote), or enter a # custom file name for your newsgroups file. If this is an NNTP source # you can also set "Group Desc Refetch" to a time value when the file # expires (something like "7 days" might be good). # Set Auth User and Auth Password if you need to specify them to use a # news server. On the other hand, if you're using the "generic # authentication" system, use the Auth Command setting instead. # If the server is poorly behaved and doesn't ask you for your # authentication, you can set Force Auth to "yes" to have trn force the # info onto the server. # Add "XHDR Broken = y" if your server doesn't properly respond to # XHDR requests (articles vanish unexpectedly). #[ID] #NNTP Server = #Active File = #Active File Refetch = #Spool Dir = #Thread Dir = #Overview Dir = #Overview Format File = #Active Times = #Group Desc = #Group Desc Refetch = #Auth User = #Auth Password = #Auth Command = #Force Auth = #XHDR Broken = [local1] Active File = /usr/lib/news/active Spool Dir = /usr/spool/news [local2] Active File = /usr/lib/news/active Spool Dir = /usr/spool/news Thread Dir = none [local3] Active File = /usr/lib/news/active Spool Dir = /usr/spool/news Thread Dir = none Overview Dir = /usr/spool/news/.nov Overview Format File = /usr/lib/news/overview.fmt Active Times = /usr/lib/news/acttimes.custom Group Desc = /usr/lib/news/newsgroups.custom [remote1] NNTP Server = remote.com Auth User = joesmoe Auth Password = OpenSezMe [remote2] NNTP Server = remote.com;2119 # use custom port number 2119 Overview Dir = none [rem3] NNTP Server = rem.ote.com Spool Dir = /usr/tmp Active File = /client/lib/active Active File Refetch = 12 hours Thread Dir = /client/threads Overview Dir = none Active Times = /client/lib/active.times Group Desc = /client/lib/newsgroups Group Desc Refetch = 7 days # This illustrates how to set different default news sources via the # machine's hostname. Always put the unqualified version last. [default] %H = nfshost.domain.com NNTP Server = server.domain.com Active File = /nfs/lib/news/active [default] %H = server.domain.com Active File = /usr/lib/news/active Spool Dir = /usr/spool/news [default] NNTP Server = server.domain.com # This section tells trn what newsrc groups you want to use. If you # define multiple entries with the same group number, all of the .newsrcs # are combined and used together. The first entry encountered will be # the first group that trn will open. Otherwise the numbers dictate the # presentation order (see Ctrl-N and Ctrl-P). You can have multiple # .newsrc's from the same server or different servers, however you like. # The ID name "default" always exists with your compiled-in defaults. # If you leave off the newsrc name, the default is supplied. If you # don't want any groups added to an rc, specify "no" in the "Add Groups" # field. If you don't want new-group checking but want to manually # add groups, specify "manual". The default is "yes". #[Group 1] #ID = default #Newsrc = ~/.newsrc #Add Groups = yes/no/manual [Group 1] ID = default [Group 2] ID = rem Newsrc = ~/.smallrc Add Groups = manual [Group 2] ID = rem2 Newsrc = ~/.newsrc2 [Group 3] ID = local3 Newsrc = ~/.mynewsrc [Group 4] ID = rem Newsrc = ~/.remrc Add Groups = no trn-4.0-test77/HelpFiles/samples/trnrc0000644000000000000000000001474407113133243016412 0ustar rootroot# This is a sample .trn/trnrc file. [environment] LOCALTIMEFMT="%A, %B %e %Y, %r %Z" KILLLOCAL=%+/Kill/%C KILLGLOBAL=%+/Kill/Global #KILLTHREADS=%+/Kill/Threads XTERMMOUSE=%(%{TERM}=xterm?y:n) MAILPOSTER="elm -i %h -s '%'s' '%'t'" MAILHEADER="\\0" FORWARDPOSTER="elm -i %h -s '%'[subject]' %\"^J^JTo: \"" FORWARDHEADER=^J ATTRIBUTION="According to %)f <%>f>:" SELECTCHARS=abcdefgijlmorstuvwxyz1234567890BCFGHIKVW [options] # ==Display Options======== Terse Output = #default of no Pager Line-Marking = underline Erase Screen = #default of no Erase Each Line = #default of no Muck Up Clear = #default of no Background Spinner = yes Charset = #default of patm Filter Control Characters = #default of yes # ==Selector Options======== Use Newsrc Selector = yes Newsrc Selector Commands = #default of Z> Use Addgroup Selector = yes Addgroup Selector Commands = #default of Z> Use Newsgroup Selector = yes Newsgroup Selector Order = #default of natural Newsgroup Selector Commands = #default of Z> Newsgroup Selector Display Styles = #default of slm Use News Selector = #default of yes News Selector Mode = #default of threads News Selector Order = #default of date News Selector Commands = X> News Selector Display Styles = ms Option Selector Commands = #default of Z> # ==Newsreading Options======== Use Threads = #default of yes Select My Postings = parent Initial Article Lines = #default of $LINES Article Tree Lines = 11 Auto-Grow Groups = #default of yes Compress Subjects = #default of yes Join Subject Lines = #default of no Line Num for Goto = #default of 1 Ignore THRU on Select = #default of yes Read Breadth First = #default of no Background Threading = #default of yes Scan Mode Count = #default of 0 Header Magic = #default of Header Hiding = #default of # ==Posting Options======== Cited Text String = "> " # ==Save Options======== Save Dir = #default of %./News Auto Savename = #default of no Default Savefile Type = norm # ==Mouse Options======== Use XTerm Mouse = #default of no Mouse Modes = #default of acjlptwK Newsrc Selector Mousebar = #default of [Top]^ [PgUp]< [PgDn]> [ OK ]^j [Quit]q [Help]? Addgroup Selector Mousebar = #default of [Top]^ [Bot]$ [PgUp]< [PgDn]> [ OK ]Z [Quit]q [Help]? Newsgroup Selector Mousebar = #default of [Top]^ [PgUp]< [PgDn]> [ OK ]Z [Quit]q [Help]? News Selector Mousebar = #default of [Top]^ [Bot]$ [PgUp]< [PgDn]> [KillPg]D [ OK ]Z [Quit]q [Help]? Option Selector Mousebar = #default of [Find]/ [FindNext]/^j [Top]^ [Bot]$ [PgUp]< [PgDn]> [Use]^i [Save]S [Abandon]q [Help]? Article Pager Mousebar = #default of [Next]n [Sel]+ [Quit]q [Help]h # ==MIME Options======== Multipart Separator = #default of -=-=-=-=-=- Auto-View Inline = #default of no # ==Misc Options======== Check for New Groups = #default of yes Restriction Includes Empty Groups = #default of yes Append Unsubscribed Groups = #default of no Initial Group List = no Restart At Last Group = #default of no Eat Type-Ahead = #default of yes Verify Input = #default of no Fuzzy Newsgroup Names = yes Auto Arrow Macros = #default of yes Checkpoint Newsrc Frequency = #default of 20 Default Refetch Time = #default of 4 hours Novice Delays = no Old Mthreads Database = #default of no # Make Use XTerm Mouse default to Yes if this is an xterm or similar [options] %{TERM}=^xterm\\|^vs100 Use XTerm Mouse = Yes Auto-View Inline = Yes # The [attribute] section attaches highlight attributes (standout, # underline, or normal) to various screen objects. If you have a # color terminal, you can specify colors instead (see below). If # you specify '-' it means don't change the attribute from its # current setting. [attribute] # object = attr ngname = standout plus = - minus = - star = - header = normal subject = underline tree = normal tree marker = standout more = standout heading = standout command = standout mouse bar = standout notice = standout score = standout art heading = normal mime separator = standout mime description= underline cited text = - # The [termcap] section is needed if you want to specify colors in # the [attribute] section (and must precede it). Any color names # can be used (up to around 48 names), but they must be in the form # "fg color" and "bg color" and fg/bg default must be defined. [termcap] %{TERM}=color-xterm\\|color-vs100 # fg/bg color = string fg default = \033[m fg black = \033[30m fg red = \033[31m fg green = \033[32m fg yellow = \033[33m fg blue = \033[34m fg magenta = \033[35m fg cyan = \033[36m fg white = \033[37m bg default = #\033[m bg black = \033[40m bg red = \033[41m bg green = \033[42m bg yellow = \033[43m bg blue = \033[44m bg magenta = \033[45m bg cyan = \033[46m bg white = \033[47m # Here's a color version of the [attribute] that will override the # above section if the TERM variable matches the indicated value. # The attribute is normally set to '-' as mixing colors and attributes # rarely produces any usable result. The attribute standout might be # useful if a terminal only supports foreground colors, i.e. perhaps # "normal white blue" can be written as "standout blue -". [attribute] %{TERM}=color-xterm\\|color-vs100 # object = attr fg bg ngname = - white blue plus = - black green minus = - white red star = - white blue header = - blue - subject = - blue yellow tree = - red - tree marker = - white red more = - yellow black heading = - white blue command = - yellow black mouse bar = - white blue notice = - blue - score = - blue - art heading = - white blue mime separator = - blue - mime description= - blue yellow cited text = - blue - # Here's a sample vt100 [termcap] and [attribute] section that defines # the "bold" capability [termcap] %{TERM}=vt100 fg default = \033[m fg bold = \033[1m bg default = #\033[m [attribute] %{TERM}=vt100 # object = attr fg bg tree = - bold - mime separator = - bold - mime description= - bold - cited text = - bold - # These trailing sections will override the above sections. # These examples are conditional on various parameters. [environment] %H=local.host.com # check the host name NETSPEED=fast [options] %j = 2400 # check the line speed Initial Article Lines = 8 [environment] %{DEBUG}=yes # check an environment variable NEWSHEADER='\ %(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n ))Newsgroups: %(%F=^$?%C:%F) Subject: %(%S=^$?%"^J^JSubject: ":Re: %S) Summary: Expires: %(%R=^$?:References: %R )Sender: Followup-To: %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} )Distribution: %(%i=^$?%"Distribution: ":%D) Organization: %o Keywords: %[keywords] Cc: %(%F=poster?%t:%(%F!=@?:%F)) ^J' trn-4.0-test77/hints/aix_rs.sh0000644000000000000000000000007207113133243014753 0ustar rootrootcppstdin='/lib/cpp -D_AIX -D_IBMR2 -U__STR__' cppminus='' trn-4.0-test77/hints/altos486.sh0000644000000000000000000000015707113133243015056 0ustar rootroot: have heard of problems with -lc_s on Altos 486 set `echo " $libswanted " | sed "s/ c_s / /"` libswanted="$*" trn-4.0-test77/hints/convexos.sh0000644000000000000000000000021307113133243015327 0ustar rootrooti_sgtty=undef i_termios=define ccflags="-D__STDC__ -ext -tm c1" d_voidsig='define' signal_t='void' d_strchr='undef' libc='/usr/lib/libc.a' trn-4.0-test77/hints/dec_osf1.sh0000644000000000000000000000031607115342257015163 0ustar rootrootlibpth="$libpth /usr/shlib" # Use the shared libraries if possible libc='/usr/shlib/libc.so' # The archive version is /lib/libc.a d_berknames='define' # Digital/Tru64 does use Berkeley format GCOS trn-4.0-test77/hints/dgux.sh0000644000000000000000000000007407113133243014437 0ustar rootrootcppstdin='/lib/cpp' libs='-ldgc' d_strchr='define' cc='gcc' trn-4.0-test77/hints/domainos.sh0000644000000000000000000000005007113133243015273 0ustar rootrootccflags='-A nansi' d_ignoreorg='define' trn-4.0-test77/hints/dynix.sh0000644000000000000000000000010507113133243014616 0ustar rootrootlibswanted=`echo $libswanted | sed -e 's/socket /socket seq inet /'` trn-4.0-test77/hints/freebsd.sh0000644000000000000000000000003707125211641015103 0ustar rootrooti_sysfilio='undef' usenm=false trn-4.0-test77/hints/hp9000_800.sh0000644000000000000000000000010207113133243014767 0ustar rootrootlibswanted=`echo $libswanted | sed -e 's/malloc //' -e 's/BSD //` trn-4.0-test77/hints/hpux.sh0000644000000000000000000000016207113133243014452 0ustar rootrootcase `(uname -r) 2>/dev/null` in *2.1*) libswanted=`echo $libswanted | sed 's/malloc //'` ;; esac d_strchr=define trn-4.0-test77/hints/i386.sh0000644000000000000000000000003007113133243014151 0ustar rootrootldflags='-L/usr/ucblib' trn-4.0-test77/hints/irix.sh0000644000000000000000000000007407113133243014443 0ustar rootrootd_voidsig=define d_vfork=undef d_sigblock=undef nm_opt='-B' trn-4.0-test77/hints/isc_2_2_1.sh0000644000000000000000000000001607113133243015124 0ustar rootrootd_ftime=undef trn-4.0-test77/hints/isc_3_2_2.sh0000644000000000000000000000012607113133243015130 0ustar rootrootset `echo $libswanted | sed -e 's/ x//' -e 's/malloc //'` libswanted="inet malloc $*" trn-4.0-test77/hints/isc_3_2_3.sh0000644000000000000000000000010507113133243015126 0ustar rootrootset `echo $libswanted | sed -e 's/ socket / inet /'` libswanted="$*" trn-4.0-test77/hints/linux.sh0000644000000000000000000000002307116414237014631 0ustar rootrootd_berknames=define trn-4.0-test77/hints/mc6000.sh0000644000000000000000000000012707113133243014374 0ustar rootroot# defaults for the masscomp (concurrent) 6000 series running RTU 5.0 cppstdin=/lib/cpp trn-4.0-test77/hints/mips.sh0000644000000000000000000000002207113133243014431 0ustar rootrootcc=cc nm_opt='-B' trn-4.0-test77/hints/next.sh0000644000000000000000000000002307113133243014440 0ustar rootrootlibswanted='sys_s' trn-4.0-test77/hints/sco_3.sh0000644000000000000000000000026307113133243014476 0ustar rootroot#libswanted=`echo $libswanted | sed 's/ x//'` mailer='/usr/lib/mail/execmail' xxx=`uname -X 2>/dev/null | sed -n 's/^Release.*=//p` case "$xxx" in ''|3.2v2) d_rename=undef;; esac trn-4.0-test77/hints/sco_3_2_4.sh0000644000000000000000000000007607113133243015144 0ustar rootrootxlibpth='' mailer='/usr/lib/mail/execmail' libc='/lib/libc.a' trn-4.0-test77/hints/sco_xenix.sh0000644000000000000000000000007407113133243015467 0ustar rootrooti_dirent=undef libswanted=`echo $libswanted | sed 's/ x//'` trn-4.0-test77/hints/sgi.sh0000644000000000000000000000006007113133243014245 0ustar rootrootd_voidsig=define d_vfork=undef d_sigblock=undef trn-4.0-test77/hints/solaris_2.sh0000644000000000000000000000014107113133243015360 0ustar rootrootd_sigblock='undef' d_getcwd='define' d_getwd='undef' libs='-lmalloc -lsocket -lnls -lnsl -lintl' trn-4.0-test77/hints/sunos.sh0000644000000000000000000000002207113133243014630 0ustar rootroot# no hints needed trn-4.0-test77/hints/svr4.sh0000644000000000000000000000017507113133243014370 0ustar rootrootcc='/bin/cc' test -f $cc || cc='/usr/ccs/bin/cc' mansrc='/usr/share/man/man1' libswanted='malloc socket nsl' d_strchr=define