Configure 664 540 12 123344 3477433214 6025 #! /bin/sh # # If these # comments don't work, trim them. Don't worry about the other # shell scripts, Configure will trim # comments from them for you. # # $Header: Configure,v 4.3.1.5 85/05/23 11:23:22 lwall Exp $ # # $Log: Configure,v $ # Revision 4.3.1.5 85/05/23 11:23:22 lwall # filexp flubs sed command. # # Revision 4.3.1.4 85/05/20 17:13:11 lwall # Makes sure -lcurses is really a terminfo library. # Puts single instead of double quotes around defs in config.sh. # # Revision 4.3.1.3 85/05/16 16:48:48 lwall # Took space from end of filexp output. # # Revision 4.3.1.2 85/05/13 15:54:35 lwall # Libraries in /usr/local/lib shouldn't use -l switch. # # Revision 4.3.1.1 85/05/10 11:29:20 lwall # Branch for patches. # # Revision 4.3 85/05/01 11:31:23 lwall # Baseline for release with 4.3bsd. # # Yes, you may rip this off to use in other distribution packages. n='' c='' libc='' eunicefix='' eunice='' cpp='' shsharp='' spitshell='' startsh='' test='' expr='' sed='' echo='' cat='' rm='' mv='' cp='' tail='' tr='' mkdir='' sort='' uniq='' inews='' grep='' egrep='' contains='' lib='' nametype='' cc='' iandd='' termlib='' jobslib='' ndirlib='' libndir='' usendir='' ndirc='' ndiro='' pager='' mailer='' internet='' rnbin='' filexp='' distlist='' Log='' Header='' sitename='' orgname='' isadmin='' newsadmin='' rnlib='' mansrc='' manext='' maildir='' spool='' active='' myactive='' mininact='' pref='' defeditor='' rootid='' mboxchar='' locpref='' orgpref='' citypref='' statepref='' cntrypref='' contpref='' strchr='' novoid='' novfork='' portable='' passnam='' berknam='' usgnam='' whoami='' termio='' fcntl='' ioctl='' normsig='' havetlib='' getpwent='' gethostname='' douname='' phostname='' hostcmd='' CONFIG='' echo "Beginning of configuration questions for rn kit." : Eunice requires " " instead of "", can you believe it echo " " : sanity checks PATH='.:/bin:/usr/bin:/usr/local/bin:/usr/ucb:/usr/local:/usr/lbin:/etc' export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh $0; kill $$) if test ! -t 0; then echo "Say 'sh Configure', not 'sh grimble if grep blurfldyick grimble >/dev/null 2>&1 ; then contains=contains else if grep grimblepritz grimble >/dev/null 2>&1 ; then contains=grep else contains=contains fi fi rm grimble : the following should work in any shell case $contains in contains*) echo " " echo "AGH! Grep doesn't return a status. Attempting remedial action." cat >contains <<'EOSS' grep "$1" "$2" >.greptmp && cat .greptmp && test -s .greptmp EOSS chmod 755 contains esac : first determine how to suppress newline on echo command echo "Checking echo to see how to suppress newlines..." (echo "hi there\c" ; echo " ") >.echotmp if $contains c .echotmp >/dev/null 2>&1 ; then echo "...using -n." n='-n' c='' else echo "...using \\\c." n='' c='\c' fi echo $n "Type carriage return to continue. Your cursor should be here-->$c" read ans rm .echotmp : now set up to do reads with possible shell escape : if this does not work on your machine, 1,$s/. myread/read ans/ cat <myread ans='!' while expr "X\$ans" : "X!" >/dev/null; do read ans case "\$ans" in !) sh echo " " echo $n "Your answer: $c" ;; !*) set \`expr "X\$ans" : "X!\(.*\)\$"\` sh -c "\$*" echo " " echo $n "Your answer: $c" ;; esac done EOSC : general instructions cat <<'EOH' This installation shell script will examine your system and ask you questions to determine how rn and its auxiliary files should be installed. If you get stuck on a question, you may use a ! shell escape to start a subshell or execute a command. Many of the questions will have default answers in square brackets--typing carriage return will give you the default. On some of the questions which ask for file or directory names you are allowed to use the ~name construct to specify the login directory belonging to "name", even if you don't have a shell which knows about that. Questions where this is allowed will be marked "(~name ok)". Much effort has been expended to ensure that this shell script will run on any Unix system. If despite that it blows up on you, your best bet is to edit Configure and run it again. (Trying to install rn without having run Configure is well nigh impossible.) Also, let me (lwall@sdcrdcf.UUCP) know how I blew it. This installation script affects things in two ways: 1) it does direct variable substitutions on some of the files included in this kit, and 2) it builds a config.h file for inclusion in C programs. You may edit any of these files as the need arises after running this script. EOH echo $n "[Type carriage return to continue] $c" . myread : get old answers, if there is a config file out there if test -f config.sh; then echo " " echo "(Fetching default answers from your old config.sh file...)" . config.sh fi : get list of predefined functions in a handy place echo " " if test -f /lib/libc.a; then echo "Your C library is in /lib/libc.a. You're normal." libc=/lib/libc.a else if test -f /usr/lib/libc.a; then echo "Your C library is in /usr/lib/libc.a, of all places." libc=/usr/lib/libc.a else if test -f "$libc"; then echo "Your C library is in $libc, like you said before." else cat <<'EOM' I can't seem to find your C library. I've looked for /lib/libc.a and /usr/lib/libc.a, but neither of those are there. What is the full name EOM echo $n "of your C library? $c" . myread libc="$ans" fi fi fi echo " " echo $n "Extracting names from $libc for later perusal...$c" if ar t $libc > libc.list; then echo "done" else echo " " echo "The archiver doesn't think $libc is a reasonable library." exit 1 fi : make some quick guesses about what we are up against echo " " echo $n "Hmm... $c" if $contains SIGTSTP /usr/include/signal.h >/dev/null 2>&1 ; then echo "Looks kind of like a BSD system, but we'll see..." echo exit 0 >bsd echo exit 1 >usg echo exit 1 >v7 else if $contains fcntl.o libc.list >/dev/null 2>&1 ; then echo "Looks kind of like a USG system, but we'll see..." echo exit 1 >bsd echo exit 0 >usg echo exit 1 >v7 else echo "Looks kind of like a version 7 system, but we'll see..." echo exit 1 >bsd echo exit 1 >usg echo exit 0 >v7 fi fi if $contains vmssystem.o libc.list >/dev/null 2>&1 ; then cat <<'EOI' There is, however, a strange, musty smell in the air that reminds me of something...hmm...yes...I've got it...there's a VMS nearby, or I'm a Blit. EOI echo "exit 0" >eunice eunicefix=unixtovms eunice=define : it so happens the Eunice I know will not run shell scripts in Unix format else echo " " echo "Congratulations. You aren't running Eunice." eunicefix=':' eunice=undef echo "exit 1" >eunice fi chmod 755 bsd usg v7 eunice $eunicefix bsd usg v7 eunice : see how we invoke the C preprocessor echo " " echo "Checking to see how your C preprocessor is invoked..." cat <<'EOT' >testcpp.c #define ABC abc #define XYZ xyz ABC+XYZ EOT echo 'Maybe "cc -E" will work...' cc -E testcpp.c >testcpp.out 2>&1 if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yup, it does." cpp='cc -E' else echo 'Nope...maybe "cc -P" will work...' cc -P testcpp.c >testcpp.out 2>&1 if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yup, that does." cpp='cc -P' else echo 'Nixed again...maybe "/lib/cpp" will work...' /lib/cpp testcpp.c >testcpp.out 2>&1 if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then echo "Hooray, it works! I was beginning to wonder." cpp='/lib/cpp' else echo 'Hmm...maybe you already told me...' case "$cpp" in '') ;; *) $cpp testcpp.c >testcpp.out 2>&1;; esac if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then echo "Hooray, you did! I was beginning to wonder." else echo "Nope. I can't find a C preprocessor. Name one: $c" . myread cpp="$ans" $cpp testcpp.c >testcpp.out 2>&1 if $contains 'abc+xyz' testcpp.out >/dev/null 2>&1 ; then echo "OK, that will do." else echo "Sorry, I can't get that to work. Go find one." exit 1 fi fi fi fi fi rm -f testcpp.c testcpp.out : check for pdp11 echo " " if usg; then : pdp11 is already defined else cat <<'EOT' >pdp11.c #ifdef pdp11 exit 0 #else exit 1 #endif EOT $cpp pdp11.c | grep exit >pdp11 chmod 755 pdp11 $eunicefix pdp11 rm pdp11.c fi if pdp11; then echo "This looks like a pdp11 to me." else echo "This doesn't look like a pdp11 to me." fi : see if sh knows # comments echo " " echo "Checking your sh to see if it knows about # comments..." if sh -c '#' >/dev/null 2>&1 ; then echo "Your sh handles # comments correctly." shsharp=true spitshell=cat echo " " echo "Okay, let's see if #! works on this system..." echo "#!/bin/echo hi" > try $eunicefix try chmod 755 try try > today if test -s today; then echo "It does." sharpbang='#!' else echo "#! /bin/echo hi" > try $eunicefix try chmod 755 try try > today if test -s today; then echo "It does." sharpbang='#! ' else echo "It doesn't." sharpbang=': use ' fi fi else echo "Your sh doesn't grok # comments--I will strip them later on." shsharp=false echo "exec grep -v '^#'" >spitshell chmod 755 spitshell $eunicefix spitshell spitshell=`pwd`/spitshell echo "I presume that if # doesn't work, #! won't work either!" sharpbang=': use ' fi : figure out how to guarantee sh startup echo " " echo "Checking out how to guarantee sh startup..." startsh=$sharpbang'/bin/sh' echo "Let's see if '$startsh' works..." cat >try <loc $startsh thing=\$1 shift dflt=\$1 shift for dir in \$*; do case "\$thing" in .) if test -d \$dir/\$thing; then echo \$dir exit 0 fi ;; *) if test -f \$dir/\$thing; then echo \$dir/\$thing exit 0 fi ;; esac done echo \$dflt exit 1 EOSC chmod 755 loc $eunicefix loc loclist="expr sed echo cat rm mv cp tail tr mkdir sort uniq grep" trylist="test inews egrep more pg Mcc" for file in $loclist; do xxx=`loc $file $file $pth` eval $file=$xxx case "$xxx" in /*) echo $file is in $xxx. ;; *) echo "I don't know where $file is. I hope it's in everyone's PATH." ;; esac done echo " " echo "Don't worry if any of the following aren't found..." for file in $trylist; do xxx=`loc $file $file $pth` eval $file=$xxx case "$xxx" in /*) echo $file is in $xxx. ;; *) echo "I don't see $file out there, offhand." ;; esac done case $egrep in egrep) echo "Substituting grep for egrep." egrep=$grep ;; esac case $test in test) echo "Hopefully test is built into your sh." ;; /bin/test) echo " " echo $n 'Is your "test" built into sh? [n] (OK to guess) '"$c" . myread case $ans in y*) test=test ;; esac ;; *) test=test ;; esac case $echo in echo) echo "Hopefully echo is built into your sh." ;; /bin/echo) echo " " echo "Checking compatibility between /bin/echo and builtin echo (if any)..." $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 echo "They are not compatible--the echo builtin will be used." echo=echo fi $rm -f foo1 foo2 ;; *) echo=echo ;; esac : decide how portable to be case "$portable" in define) dflt=y;; *) dflt=n;; esac $cat <<'EOH' I can set things up so that your shell scripts and binaries are more portable, at what may be a noticable cost in performance. In particular, if you ask to be portable, the following happens: 1) shell scripts will rely on the PATH variable rather than using the paths derived above. 2) ~username interpretations will be done at run time rather than by Configure. 3) the system name will be determined at run time, if at all possible. EOH $echo $n "Do you expect to run these scripts and binaries on multiple machines? [$dflt] $c" . myread case $ans in '') ans=$dflt;; esac case $ans in y*) portable=define for file in $loclist; do eval $file=$file done ;; *) portable=undef ;; esac : set up shell script to do ~ expansion cat >filexp <&2 exit 1 fi case \$1 in */*) $echo \$dir/\`$expr x\$1 : '..[^/]*/\(.*\)'\` ;; *) $echo \$dir ;; esac fi ;; *) $echo \$1 ;; esac EOSS chmod 755 filexp $eunicefix filexp : now get the site name $echo " " $echo "Figuring out site name..." $echo 'Maybe "hostname" will work...' if ans=`sh -c hostname 2>&1` ; then sitename=$ans hostcmd=hostname else $echo 'No, maybe "uuname -l" will work...' if ans=`sh -c 'uuname -l' 2>&1` ; then sitename=$ans hostcmd='uuname -l' else $echo 'Strange. Maybe "uname -n" will work...' if ans=`sh -c 'uname -n' 2>&1` ; then sitename=$ans hostcmd='uname -n' else $echo 'Oh well, maybe I can mine it out of whoami.h...' if ans=`sh -c $contains' sysname /usr/include/whoami.h' 2>&1` ; then sitename=`$echo "$ans" | $sed 's/^.*"\(.*\)"/\1/'` hostcmd="sed -n -e '"'/sysname/s/^.*\"\\(.*\\)\"/\1/{'"' -e p -e q -e '}' /dev/null 2>&1 ; then dflt=news else if $contains "^usenet:" /etc/passwd >/dev/null 2>&1 ; then dflt=news else if eunice; then dflt=system else dflt=root fi fi fi ;; *) 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.) What is the login name (not directory) that is used for news EOM echo $n "administration? [$dflt] $c" . myread newsadmin="$ans" case $newsadmin in '') newsadmin=$dflt ;; esac case $newsadmin in root) isadmin=undef ;; *) isadmin=define ;; esac : figure out news library case "$lib" in '') dflt=/usr/lib/news ;; *) dflt=$lib ;; esac libexp=$lib libexp='blurfl/dyick' while $test ! -d "$libexp"; do $echo " " case "$libexp" in blurfl*) ;; *) $echo "Directory $libexp not found";; esac echo $n "Where is your news library (~name okay)? [$dflt] $c" . myread case "$ans" in '') ans="$dflt";; esac lib="$ans" case $lib in ~*) libexp=`filexp $lib` echo "(That is $libexp on this particular system.)" case $portable in undef) lib=$libexp ;; esac ;; *) libexp=$lib ;; esac done if $test -f $libexp/inews; then echo "Aha! Inews is really in $libexp! Maybe this is 2.10.2..." case $inews in inews) : null ;; *) echo "(Make sure $inews isn't an old version.)" ;; esac inews=$libexp/inews fi : determine where manual pages go case "$mansrc" in '') dflt=`loc . /usr/man/man1 /usr/man/mann /usr/man/local/man1 /usr/man/u_man/man1 /usr/man/man1` ;; *) dflt="$mansrc" ;; esac mansrc='blurfl/dyick' while $test ! -d "$mansrc" ; do case $mansrc in blurfl*) ;; *) $echo "$mansrc does not appear to exist." ;; esac $echo " " $echo $n "Where do the manual pages (source) go? [$dflt] $c" . myread mansrc=`filexp "$ans"` case $mansrc in '') mansrc=$dflt ;; esac done case "$mansrc" in *l) manext=l ;; *n) manext=n ;; *) manext=1 ;; esac : determine where mail is spooled case "$maildir" in '') dflt=`loc . /usr/spool/mail /usr/spool/mail /usr/mail` ;; *) dflt="$maildir" ;; esac maildir='blurfl/dyick' while $test ! -d "$maildir" ; do case $maildir in blurfl*) ;; *) $echo "$maildir does not appear to exist." ;; esac $echo " " $echo $n "Where is yet-to-be-read mail spooled? [$dflt] $c" . myread maildir=`filexp "$ans"` case $maildir in '') maildir=$dflt ;; esac done : find out how to find out full name $echo " " case "$berkname" in define) dflt=y;; undef) dflt=n;; *) if bsd; then dflt=y else dflt=n fi ;; esac $echo "Does your /etc/passwd file keep full names in Berkeley/V7 format (name first" $echo $n "thing after ':' in GCOS field)? [$dflt] $c" . myread case $ans in '') ans=$dflt ;; esac case $ans in y*) passnam=define berknam=define usgnam=undef nametype=bsd ;; *) $echo " " case "$usgname" in define) dflt=y;; undef) dflt=n;; *) if usg; then dflt=y else dflt=n fi ;; esac $echo "Does your passwd file keep full names in USG format (name sandwiched" $echo $n "between a '-' and a '(')? [$dflt] $c" . myread case $ans in '') ans=$dflt ;; esac case $ans in n*) $echo "Full name will be taken from ~/.fullname" passnam=undef berknam=undef usgnam=undef nametype=other ;; *) passnam=define berknam=undef usgnam=define nametype=usg ;; esac ;; esac : see if we need a special compiler $echo " " if usg; then case "$cc" in '') case "$Mcc" in /*) dflt='Mcc' ;; *) if $contains '\-M' $mansrc/cc.1 >/dev/null 2>&1 ; then dflt='cc -M' else dflt='cc' fi ;; esac ;; *) dflt="$cc";; esac $cat <<'EOM' On some 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. What command will force resolution on EOM $echo $n "this system? [$dflt] $c" . myread cc="$ans" case "$cc" in '') cc="$dflt" ;; esac else $echo "Not a USG system--assuming cc can resolve multiple definitions." cc=cc fi : see if we should throw a -i into the Makefile $echo " " if pdp11; then if $contains '\-i' $mansrc/cc.1 >/dev/null 2>&1 ; then $echo $n "Your system appears to have separate I and D space. Is this true? [y] $c" . myread case $ans in n*|f*) iandd='' ;; *) iandd='-i' ;; esac else $echo "Your system appears to NOT have separate I and D space." $echo $n "Is this correct? [y] $c" . myread case $ans in n*|f*) iandd='-i' ;; *) iandd='' ;; esac fi else $echo "Not a pdp11--assuming no separate I and D." fi : index or strcpy $echo " " if $contains index.o libc.list >/dev/null 2>&1 ; then $echo "Your system appears to use index() and rindex() rather than strchr()" $echo $n "and strrchr(). Is this correct? [y] $c" . myread case $ans in n*|f*) strchr='define' ;; *) strchr='undef' ;; esac else $echo "Your system appears to use strchr() and strrchr() rather than index()" $echo $n "and rindex(). Is this correct? [y] $c" . myread case $ans in n*|f*) strchr=undef ;; *) strchr=define ;; esac fi : determine how to determine when a file is a mailbox case "$mboxchar" in '') dflt=F;; *) dflt="$mboxchar";; esac $cat <<'EOM' In saving articles, rn wants to differentiate between saving to mailbox format files and normal files. It does this by examining the first character of the file in question. On most systems the first line starts "From...", so the first character is F. On other systems there are magic cookies like control codes between articles, so one of those would be first. On your system, if a file is in mailbox format, what is the first character of EOM echo $n "that file? [$dflt] $c" . myread mboxchar="$ans" case $mboxchar in '') mboxchar="$dflt" ;; esac case $mboxchar in 'F') ;; *) cat <<'EOM' You will need to edit the shell script mbox.saver to properly append an article to a mailbox. The arguments to the script are documented in EOM case $shsharp in false) echo "comments in mbox.saver.std." ;; true) echo "comments in the shell script itself." ;; esac esac : where do we get termlib routines from $echo " " ans=`loc libcurses.a x /usr/lib /usr/local/lib /lib` case "$ans" in /*) ar t $ans >grimble if $contains tputs.o grimble >/dev/null 2>&1; then termlib='-lcurses' havetlib=define $echo "Terminfo library found." else ans=x fi ;; esac case "$ans" in x) ans=`loc libtermlib.a x /usr/lib /usr/local/lib /lib` case "$ans" in /usr/lib*|/lib*) termlib='-ltermlib' havetlib=define $echo "Termlib library found." ;; /*) termlib="$ans" havetlib=define $echo "Termlib library found." ;; *) ans=`loc libtermcap.a x /usr/lib /usr/local/lib /lib` case "$ans" in /usr/lib*|/lib*) termlib='-ltermcap' havetlib=define $echo "Termcap library found." ;; /*) termlib="$ans" havetlib=define $echo "Termcap library found." ;; *) case "$termlib" in '') $echo $n "Your system appears to NOT have termlib-style routines. Is this true? [y] $c" . myread case $ans in n*|f*) havetlib=define $echo "Then where are the termlib-style routines kept (specify either -llibname" $echo $n " or full pathname (~name ok))? $c" . myread termlib=`filexp $ans` ;; *) havetlib=undef termlib='' $echo "You will have to play around with term.c then." ;; esac $echo " " ;; *) $echo "You said termlib was $termlib before." ;; esac ;; esac ;; esac ;; esac : see if there is a whoami file if $test -r /usr/include/whoami.h ; then whoami=define $echo "whoami.h found." else whoami=undef fi : see if this is a termio system if $test -r /usr/include/termio.h ; then termio=define $echo "termio.h found." else if $test -r /usr/include/sgtty.h ; then termio=undef $echo "sgtty.h found." else termio=undef $echo "Neither termio.h nor sgtty.h found--you could have problems." fi fi : see if this is a termio system if $test -r /usr/include/fcntl.h ; then fcntl=define $echo "fcntl.h found." else fcntl=undef $echo "No fcntl.h found, but that's ok." fi : see if ioctl defs are in sgtty/termio or sys/ioctl if $test -r /usr/include/sys/ioctl.h ; then ioctl=define $echo "sys/ioctl.h found." else ioctl=undef $echo "sys/ioctl.h not found, assuming ioctl args are defined in sgtty.h." fi : see if there is a vfork if $contains vfork.o libc.list >/dev/null 2>&1 ; then $echo "vfork() found." novfork='undef' else $echo "No vfork() found--will use fork() instead." novfork='define' fi : see if there is a getpw if $contains getpw.o libc.list >/dev/null 2>&1 ; then $echo "getpw() found." getpwent='undef' else $echo "No getpw() found--will use getpwent() instead." getpwent='define' fi : see how we will look up site name douname=undef gethostname=undef phostname=undef if $contains gethostname.o libc.list >/dev/null 2>&1 ; then $echo "gethostname() found." gethostname=define else if $contains uname.o libc.list >/dev/null 2>&1 ; then $echo "uname() found." douname=define else case $hostcmd in '') ;; *) $cat </dev/null 2>&1 ; then $echo "No ndir library found, but you have readdir() so we'll use that." usendir=undef ndirc='' ndiro='' else $echo "No ndir library found and no readdir() found--using ./ndir.c." usendir=define ndirc='ndir.c' ndiro='ndir.o' fi fi : locate spool directory case "$spool" in '') dflt=/usr/spool/news ;; *) dflt="$spool";; esac ans='blurfl/dyick' while $test ! -d $ans; do $echo " " case "$ans" in blurfl*);; *) echo "Directory $ans not found.";; esac $echo $n "Where is news spooled (~name ok)? [$dflt] $c" . myread case "$ans" in '') ans="$dflt";; esac spool="$ans" case $spool in ~*) ans=`filexp $spool` echo "(That is $ans on this particular system.)" case $portable in undef) spool=$ans ;; esac ;; *) ans=$spool ;; esac done : locate active file case "$active" in '') dflt=$lib/active ;; *) dflt="$active";; esac myactive='blurfl/dyick' while $test ! -f "$myactive"; do $echo " " case "$myactive" in blurfl*);; *) "File $myactive not found.";; esac $echo $n "Where is the active file (~name ok)? [$dflt] $c" . myread case "$ans" in '') ans="$dflt";; esac active="$ans" case $active in ~*) myactive=`filexp $active` echo "(That is $myactive on this particular system.)" case $portable in undef) active=$myactive ;; esac ;; *) myactive=$active ;; esac if $test -d $myactive ; then myactive=$myactive/active active=$active/active fi done : check for 2.10.2 echo " " if $contains ' [0-9][0-9]* [0-9]' "$myactive" >/dev/null 2>&1; then echo "Looks like you are running at least 2.10.2 news." mininact=define else echo "It doesn't look like you are running 2.10.2 news yet. Are you planning" echo $n "to install it in the near future? [y] $c" . myread case $ans in n*) mininact=undef ;; *) mininact=define ;; esac fi : check for void type $echo " " $echo "Checking to see if your C compiler groks the void type..." $cat >try.c <<'EOCP' void main(); EOCP if cc -c try.c >/dev/null 2>&1 ; then novoid='undef' $echo "Yup, it does." else novoid='define' $echo "Nope, it doesn't (boo hiss). I will substitute int." fi $rm try.* : find out which shell people like to use most ans='blurfl/dyick' while $test ! -f "$ans" ; do case $ans in blurfl*) ;; *) $echo "$ans does not appear to exist." ;; esac case "$pref" in '') if $test -f /bin/ksh; then dflt='/bin/ksh' else if $test -f /bin/csh; then dflt='/bin/csh' else dflt='/bin/sh' fi fi ;; *) dflt="$pref";; esac $echo " " $echo "Give the full path name of the shell most people like to use on your" $echo $n "system: [$dflt] $c" . myread case $ans in '') ans=$dflt ;; esac done pref=$ans : locate the preferred pager for this system case "$pager" in '') case $pg in /*) dflt=$pg ;; esac case $more in /*) dflt=$more ;; esac case $dflt in '') dflt=/usr/ucb/more ;; esac ;; *) dflt="$pager";; esac pager='blurfl/dyick' while $test ! -f "$pager" ; do case $pager in blurfl*) $echo " " $echo "(If your kernel does terminal paging then you may answer this with '/bin/cat'.)" ;; /*) $echo "$pager does not appear to exist." $echo " " ;; *) $echo "Please give the full path name." $echo " " ;; esac $echo $n "What pager is used on your system? [$dflt] $c" . myread pager="$ans" case $pager in '') pager="$dflt" ;; esac done : determine default editor case "$defeditor" in '') dflt=/usr/ucb/vi ;; *) dflt="$defeditor" ;; esac defeditor='blurfl/dyick' while $test ! -f "$defeditor" ; do case $defeditor in blurfl*) ;; *) $echo "$defeditor does not appear to exist." ;; esac $echo " " $echo $n "What is the default editor on your system? [$dflt] $c" . myread defeditor="$ans" case $defeditor in '') defeditor=$dflt ;; esac done : determine mailer for Rnmail to use echo " " if $test -f /usr/lib/sendmail; then mailer=/usr/lib/sendmail else if usg && $test -f $libexp/recmail; then mailer=$libexp/recmail else mailer=/bin/mail fi fi echo "Mail sender is $mailer" : check for internet mailer case "$internet" in define) dflt=y;; undef) dflt=n;; *) dflt=n;; esac cat < .distlist fi $cat <<'EOH' Distribution groups are the things you use on the Distribution line to limit where an article will go to. You are likely to be a member of several distribution groups, such as organization, city, state, province, country, continent, etc. For example, Los Angeles has the distribution prefix "la", New Jersey has the prefix "nj", and Europe has the prefix "eunet". The categories you will be asked are: local organization (Could be just one machine or a cluster or an office) organization att, dec, kgb, ... city la, ny, mosc, ... state/province ca, nj, bc, ... country usa, can, rok, whatever continent na (North America, not "Not Applicable"), asia, etc. (If you don't have a distribution prefix in any of these categories then just hit return.) EOH if $test -f .distlist; then distlist=`tr '\012' ' ' <.distlist` if $test "$distlist" ; then $echo "(These are the distributions in your sys file: $distlist)" $echo " " fi fi case "$locpref" in '') dflt="";; *) dflt="[$locpref] ";; esac $echo $n "What is the distribution prefix for your local organization? $dflt$c" . myread case "$ans" in '') ;; *) locpref="$ans";; esac case $locpref in '') locpref=none ;; esac case "$orgpref" in '') dflt="";; *) dflt="[$orgpref] ";; esac $echo $n "What is the distribution prefix for your organization? $dflt$c" . myread case "$ans" in '') ;; *) orgpref="$ans";; esac case $orgpref in '') orgpref=none ;; esac case "$citypref" in '') dflt="";; *) dflt="[$citypref] ";; esac $echo $n "What is the distribution prefix for your city? $dflt$c" . myread case "$ans" in '') ;; *) citypref="$ans";; esac case $citypref in '') citypref=none ;; esac case "$statepref" in '') dflt="";; *) dflt="[$statepref] ";; esac $echo $n "What is the distribution prefix for your state/province? $dflt$c" . myread case "$ans" in '') ;; *) statepref="$ans";; esac case $statepref in '') statepref=none ;; esac case "$cntrypref" in '') dflt="";; *) dflt="[$cntrypref] ";; esac $echo $n "What is the distribution prefix for your country? $dflt$c" . myread case "$ans" in '') ;; *) cntrypref="$ans";; esac case $cntrypref in '') cntrypref=none ;; esac case "$contpref" in '') dflt="";; *) dflt="[$contpref] ";; esac $echo $n "What is the distribution prefix for your continent? $dflt$c" . myread case "$ans" in '') ;; *) contpref="$ans";; esac case $contpref in '') contpref=none ;; esac $echo " " $echo "If you have any other distribution groups you will need to edit Pnews" $echo "and newsetup to add them." $echo " " : determine root id rootid=`$sed config.sh $startsh # config.sh # This file was produced by running the Configure script. n='$n' c='$c' libc='$libc' eunicefix='$eunicefix' eunice='$eunice' cpp='$cpp' shsharp='$shsharp' startsh='$startsh' spitshell='$spitshell' test='$test' expr='$expr' sed='$sed' echo='$echo' cat='$cat' rm='$rm' mv='$mv' cp='$cp' tail='$tail' tr='$tr' mkdir='$mkdir' sort='$sort' uniq='$uniq' inews='$inews' grep='$grep' egrep='$egrep' contains='$contains' lib='$lib' libexp='$libexp' nametype='$nametype' cc='$cc' iandd='$iandd' termlib='$termlib' jobslib='$jobslib' ndirlib='$ndirlib' libndir='$libndir' usendir='$usendir' ndirc='$ndirc' ndiro='$ndiro' pager='$pager' mailer='$mailer' internet='$internet' rnbin='$rnbin' filexp='$filexp' distlist='$distlist' Log='$Log' Header='$Header' sitename='$sitename' orgname='$orgname' isadmin='$isadmin' newsadmin='$newsadmin' rnlib='$rnlib' mansrc='$mansrc' manext='$manext' maildir='$maildir' spool='$spool' active='$active' myactive='$myactive' mininact='$mininact' pref='$pref' defeditor='$defeditor' rootid='$rootid' mboxchar='$mboxchar' locpref='$locpref' orgpref='$orgpref' citypref='$citypref' statepref='$statepref' cntrypref='$cntrypref' contpref='$contpref' strchr='$strchr' novoid='$novoid' novfork='$novfork' portable='$portable' passnam='$passnam' berknam='$berknam' usgnam='$usgnam' whoami='$whoami' termio='$termio' fcntl='$fcntl' ioctl='$ioctl' normsig='$normsig' havetlib='$havetlib' getpwent='$getpwent' gethostname='$gethostname' douname='$douname' phostname='$phostname' hostcmd='$hostcmd' CONFIG=true EOT : create config.h file $echo " " $echo "Creating config.h..." $cat <config.h /* config.h * This file was produced by running the Configure script. * Feel free to modify any of this as the need arises. */ /* name of the site. May be overridden by gethostname, uname, etc. */ #define SITENAME "$sitename" /* name of the organization, may be a file name */ #define ORGNAME "$orgname" /* login name of news administrator, if any. */ #$isadmin NEWSADMIN "$newsadmin" /* news library, may use only ~ and %l expansion */ #define LIB "$lib" /* rn private library, may use ~ expansion, %x and %l */ #define RNLIB "$rnlib" /* location of the news spool directory, may use ~ expansion, %x and %l */ #define SPOOL "$spool" /* location of the active file, may use ~ expansion, %x and %l */ #define ACTIVE "$active" /* location of spooled mail */ #define MAILFILE "$maildir/%L" /* default shell--ok to be a slow shell like csh */ #define PREFSHELL "$pref" /* default editor */ #define DEFEDITOR "$defeditor" /* root uid */ #define ROOTID $rootid /* what is the first character of a mailbox? */ #define MBOXCHAR '$mboxchar' /* how to cancel an article */ #define CANCEL "$inews -h <%h" /* distribution groups */ #define LOCDIST "$locpref" #define ORGDIST "$orgpref" #define CITYDIST "$citypref" #define STATEDIST "$statepref" #define CNTRYDIST "$cntrypref" #define CONTDIST "$contpref" #$strchr index strchr /* cultural */ #$strchr rindex strrchr /* differences? */ #$novoid void int /* is void to be avoided? */ #$novfork vfork fork /* is vfork too virtual? */ #$eunice EUNICE /* no linking? */ #$eunice VMS /* not currently used, here just in case */ #$usendir USENDIR /* include ndir.c? */ #$libndir LIBNDIR /* include /usr/include/ndir.h? */ #$mininact MININACT /* include 2.10.2 optimization? */ #$portable PORTABLE /* do we do extra lookups to start up? */ #$passnam PASSNAMES /* do names come from the passwd file? */ /* (undef to take name from ~/.fullname) */ #$berknam BERKNAMES /* if so, are they Berkeley format? */ /* (that is, ":name,stuff:") */ #$usgnam USGNAMES /* or are they USG format? */ /* (that is, ":stuff-name(stuff):") */ #$whoami WHOAMI /* should we include whoami.h? */ #$termio TERMIO /* is this a termio system? */ #$fcntl FCNTL /* should we include fcntl.h? */ #$ioctl IOCTL /* are ioctl args all defined in one place? */ #$normsig NORMSIG /* use signal rather than sigset? */ #$havetlib HAVETERMLIB /* do we have termlib-style routines? */ #$getpwent GETPWENT /* need we include slow getpwent? */ #$internet INTERNET /* does our mailer do INTERNET addressing? */ #$gethostname GETHOSTNAME /* do we have a gethostname function? */ #$douname DOUNAME /* do we have a uname function? */ #$phostname PHOSTNAME "$hostcmd" /* how to get host name with popen */ EOT CONFIG=true $rm -f libc.list .distlist kit*isdone echo " " echo "Doing variable substitutions on various files..." echo " " set `$grep makedepend.out &"' echo $n "Would you like me to run it for you (it takes quite a while)? [n] $c" . myread case "$ans" in y*) makedepend;; esac : end of Configure dflt=n;; *) dflt=n;; esac cat <Makefile <>Makefile <<'!NO!SUBS!' public = rn newsetup newsgroups Pnews Rnmail private = norm.saver mbox.saver ng.help art.help pager.help subs.help makedir filexp Pnews.header manpages = rn.1 Pnews.1 Rnmail.1 newsetup.1 newsgroups.1 util = Makefile makedepend newsnews h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h h4 = respond.h rn.h search.h sw.h term.h util.h h = $(h1) $(h2) $(h3) $(h4) c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c c2 = final.c head.c help.c init.c intrp.c kfile.c last.c $(NDIRC) ng.c c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c c4 = respond.c rn.c search.c sw.c term.c util.c c = $(c1) $(c2) $(c3) $(c4) obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o obj4 = respond.o rn.o search.o sw.o term.o util.o obj = $(obj1) $(obj2) $(obj3) $(obj4) lintflags = -phbvxac add1 = Makefile.old Pnews Rnmail art.help add2 = bsd config.h config.sh eunice filexp add3 = loc makedepend makedir mbox.saver newsetup add4 = newsgroups newsnews ng.help norm.saver pager.help add5 = pdp11 rn subs.help usg v7 addedbyconf = $(add1) $(add2) $(add3) $(add4) $(add5) # grrr SHELL = /bin/sh .c.o: $(CC) -c $(CFLAGS) $*.c all: $(public) $(private) $(util) touch all rn: $(obj) $(CC) $(LDFLAGS) $(obj) $(libs) -o rn # if a .h file depends on another .h file... $(h): touch $@ install: rn # won't work with csh export PATH || exit 1 - mv $(rnbin)/rn $(rnbin)/rn.old - if test `pwd` != $(rnbin); then cp $(public) $(rnbin); fi cd $(rnbin); chmod 755 $(public) chmod 755 makedir - ./makedir `./filexp $(rnlib)` - if test `pwd` != `./filexp $(rnlib)`; then cp INIT $(private) `./filexp $(rnlib)`; fi cd `./filexp $(rnlib)`; chmod 755 $(private) - if test ! -f `./filexp $(rnlib)/newsnews`; then cp newsnews `./filexp $(rnlib)`; fi - if test `pwd` != $(mansrc); then \ for page in $(manpages); do \ cp $$page $(mansrc)/`basename $$page .1`.$(manext); \ done; \ fi clean: rm -f *.o realclean: rm -f rn *.o core $(addedbyconf) # The following lint has practically everything turned on. Unfortunately, # you have to wade through a lot of mumbo jumbo that can't be suppressed. # If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message # for that spot. lint: lint $(lintflags) $(defs) $(c) > rn.fuzz depend: makedepend # AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE $(obj): @ echo "You haven't done a "'"make depend" yet!'; exit 1 !NO!SUBS! $eunicefix Makefile /10 11:30:15 lwall # Branch for patches. # # Revision 4.3 85/05/01 11:33:26 lwall # Baseline for release with 4.3bsd. # CC = $cc rnbin = $rnbin rnlib NEW 664 540 12 5531 3473757007 4477 NEW FEATURES WITH RN 4.3 New commands TAB (pager) scan for end of quoted text. && (anywhere) set or display macros. Q (art level) exit this newsgroup but stay on it. x (ng level) exit rn without changing .newsrc. New switch -=TERM-switch apply switch if terminal is TERM. New environment variables RNMACRO name of your macro and keymap file, if any. SUBJLINE controls format of = article listing. New % interpolations. %I inclusion indicator (-F argument). %m current mode of rn (newsgroup, pager, etc.) %z length of current article. %"prompt" prompt for input from keyboard. %`command` same as shell backquotes. ^char now produces control-char. Macros and Keymaps You may now define macros of any reasonable length and map those macros onto your keyboard in any way. You can completely remap the keyboard if you wish. Macros may contain % interpolations. Global RNINIT file You can now set pseudo-environment variables on a system-wide basis in the file %X/INIT. You don't have to recompile rn when you want to try something different. Pnews Can append your .signature if you like. Now knows how to mail to moderators. Runs somewhat faster now when invoked with -h. Will use %x/distributions if it exists. Doesn't ask for Distribution on local newsgroups. Doesn't ask for editor if EDITOR or VISUAL is set. Terminal handling -L switch uses erase-to-end-of-line to leave info on screen as long as possible. rn -c will not flush typeahead in your login script now. In multi-character commands, \ now quotes the next character. Support for non-line-buffered machines. Certain V7 machines will appreciate the speedup. Configure Lets the poor people without job control do shell escapes. Now remembers your old answers and uses them for defaults. Searches much more widely for libraries. Looks for Mcc or cc -M if it needs to. Finds pg if it needs to. (Note, pg users: you can use macros to reverse the sense of CR and SP in rn now.) Figures out where manual pages go. Figures out where mail is spooled. Looks for ioctl.h, if any. Determines if you have a builtin echo that works differently than /bin/echo. Asks if your mail takes Internet addresses. Works reasonably on more systems, such as "Pyramids". Miscellaneous Commands may be typed directly to a help menu or subject list without having to type 'q' first. - command on first displayed article of a newsgroup takes you out to the previous newsgroup. (Someday it will take you to the previous article in the previous newsgroup.) You can now easily get into a newsgroup with a KILL file and no unread articles. The catchup command at the top level now asks for confirmation. Interpretation routines now check for output buffer overflow. The pager no longer get hung up on non-initial ^L. The negative unread articles bug was fixed. Numerous small bug fixes. ean: rm -f rn *.o core $(addedbyconf) # The following lint has practically everything turned on. Unfortunately, # you have to wade through a lot of mumbo jumbo thatPnews.1 664 540 12 4766 3473757371 5316 ''' $Header: Pnews.1,v 4.3 85/05/01 11:33:50 lwall Exp $ ''' ''' $Log: Pnews.1,v $ ''' Revision 4.3 85/05/01 11:33:50 lwall ''' Baseline for release with 4.3bsd. ''' .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. ''' Bell System Logo is used as a dummy character. ''' .ie n \{\ .tr \(bs-\*(Tr .ds -- \(bs- .if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH PNEWS 1 LOCAL .SH NAME Pnews - a program for posting news articles .SH SYNOPSIS .B Pnews newsgroup title .br or .br .B Pnews -h headerfile [oldarticle] .br or .br .B Pnews .SH DESCRIPTION Pnews is a friendly interface for posting news articles. It will ask several questions, then allow you to enter your article, and then post it using the inews(1) program. If you type h and a carriage return at any point, .I Pnews will tell you what it wants to know. .PP The -h form is used when invoked from .IR rn . If your editor can edit multiple files, and you want the article to which you are replying to show up as an alternate file, define the environment variable NEWSPOSTER as \*(L"Pnews -h %h %A\*(R". You can also modify the the NEWSHEADER environment variable to change the header file that .I rn passes to Pnews. .SH ENVIRONMENT .IP DOTDIR 8 Where to find your dot files, if they aren't in your home directory. .Sp Default: $HOME .IP EDITOR 8 The editor you want to use, if VISUAL is undefined. .Sp Default: whatever your news administrator installed, usually vi. .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .IP LOGNAME 8 Your login name, if USER is undefined. .Sp Default: value of \*(L"whoami\*(R". .IP NAME 8 Your full name. .Sp Default: name from /etc/passwd, or ~/.fullname. .IP ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. .Sp Default: whatever your news administrator chose. .IP USER 8 Your login name. .Sp Default: $LOGNAME .IP VISUAL 8 The editor you want to use. .Sp Default: $EDITOR .SH FILES /tmp/article$$ .br ~/dead.article .SH SEE ALSO rn(1), Rnmail(1), inews(1) .SH DIAGNOSTICS .SH BUGS Not the speediest program in the world, but maybe that's a blessing to the net. evious newPnews.SH 664 540 12 30642 3477432754 5477 case $CONFIG in '') . config.sh ;; esac echo "Extracting Pnews (with variable substitutions)" $spitshell >Pnews <>Pnews <<'!NO!SUBS!' if $test -f ${DOTDIR-${HOME-$LOGDIR}}/.pnewsexpert; then expertise=expert else $cat <<'EOM' I see you've never used this version of Pnews before. I will give you extra help this first time through, but then you must remember what you learned. If you don't understand any question, type h and a CR (carriage return) for help. If you've never posted an article to the net before, it is HIGHLY recommended that you read the netiquette document found in net.announce.newusers so that you'll know to avoid the commonest blunders. To do that, interrupt Pnews, and get to the top-level prompt of rn. Say "g net.announce.newusers" and you are on your way. EOM expertise=beginner fi case $cntry in can) stpr=Province ;; *) stpr=State ;; esac tmpart=/tmp/article$$ headerfile="" case $# in 0) ;; *) case $1 in -h) headerfile="$2" shift shift case $# in 0) oldart="" ;; *) oldart="$1" shift ;; esac ;; esac ;; esac case $headerfile in '') . $rnlib/Pnews.header ;; *) $cat < $headerfile > $tmpart ;; esac rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ; $echo saved in ${HOME-$LOGDIR}/dead.article ; $rm -f $tmpart; exit" trap "$rescue" 1 trap "$rescue" 2 $echo "" set X `$sed < $tmpart -n -e '/^Distribution: /{' -e p -e q -e '}' -e '/^$/q'` shift case $# in 0|1) set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'` shift case $# in 0|1) set "x net.whatever" ;; esac ;; *) set $1 $2.whatever ;; esac shift #: play recorded message #if $test -s ${lib}/recording ; then # ng=`$echo $1 | $sed "s/,.*//"` # _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 #fi # tell them what we think they are doing... !DIST! case $1 in net.*) $echo 'This program posts news to many hundreds of machines throughout the world.' ;; $cont.*) $echo 'This program posts news to many machines throughout the continent.' ;; $cntry.*) $echo 'This program posts news to many machines throughout the country.' ;; $state.*) $echo 'This program posts news to many machines throughout the state.' ;; $city.*) $echo 'This program posts news to many machines throughout the city.' ;; $org.*) $echo 'This program posts news to machines throughout the organization.' ;; $loc.*) $echo 'This program posts news to machines throughout the local organization.' ;; *.*) $echo 'This program may post news to many machines.' ;; *) $echo 'This program posts news to everyone on the machine.' ;; esac ans="" while $test "$ans" = "" ; do $echo $n "Are you absolutely sure that you want to do this? [ny] $c" read ans case $ans in y*) ;; f*) suppressmess=y ;; h*) $cat <<'EOH' Type n or CR to exit, y to post. EOH ans="" ;; *) exit ;; esac done file=h while $test "$file" = h ; do $echo "" $echo $n "Prepared file to include [none]: $c" read file case $file in h) $cat <<'EOH' If you have already produced the body of your article, type the filename for it here. If you just want to proceed directly to the editor, type a RETURN. In any event, you will be allowed to edit as many times as you want before you send off the article. EOH ;; '') $echo "" >> $tmpart state=edit ;; *) $cat $file >>$tmpart state=ask ;; esac done $echo "" while true ; do case $state in edit) case $expertise in beginner) $cat ${DOTDIR-${HOME-$LOGDIR}}/.pnewsexpert $cat <<'EOMessage' A temporary file has been created for you to edit. Be sure to leave at least one blank line between the header and the body of your message. (And until a certain bug is fixed all over the net, don't start the body of your message with any indentation, or it may get eaten.) Within the header may be fields that you don't understand. If you don't understand a field (or even if you do), you can simply leave it blank, and it will go away when the article is posted. Type return to get the default editor, or type the name of your favorite editor. EOMessage ;; esac case "${VISUAL-${EDITOR-}}" in '') tmp=h ;; *) tmp='' ;; esac while $test "$tmp" = h ; do $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" read tmp case $tmp in h) $cat <<'EOH' Type a return to get the default editor, or type the name of the editor you prefer. The default editor depends on the VISUAL and EDITOR environment variables. EOH ;; '') ;; *) VISUAL=$tmp export VISUAL ;; esac done trap : 2 ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=ask ;; ask) $echo "" $echo $n "Send, abort, edit, or list? $c" read ans case $ans in a*) state=rescue ;; e*) state=edit ;; l*) $pager $tmpart state=ask ;; s*) state=send ;; h*) $cat <<'EOH' Type s to send the article, a to abort and append the article to dead.article, e to edit the article again, or l to list the article. EOH esac ;; send) set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'` shift case $# in 2) state=cleanup if $test -f $lib/moderators; then tryinews=no shift case "$1" in *,*) set `$echo $1 | tr ',' ' '`;; esac for newsgroup in $*; do # the following screwy sed should prevent Eunice from hanging on no match moderator=`$sed <$lib/moderators \ -e "/^$newsgroup[ ]/!s/.*//" \ -e "s/^$newsgroup[ ]//"` case ${moderator}X in X) tryinews=yes ;; *) $echo Mailing to moderator $moderator case "$mailer" in *recmail) $echo To: $moderator | $cat - $tmpart | $mailer ;; *) $mailer $moderator < $tmpart ;; esac case $? in 0) ;; *) $echo Unable to mail to moderator $moderator state=rescue ;; esac ;; esac done else tryinews=yes fi case "$tryinews" in yes) if $inews -h < $tmpart ; then : null else state=rescue fi ;; esac ;; *) $echo "" $echo "Malformed Newsgroups line." $echo "" sleep 1 state=edit ;; esac ;; rescue) $cat $tmpart >> ${HOME-$LOGDIR}/dead.article $echo "Article saved to ${HOME-$LOGDIR}/dead.article" state=cleanup ;; cleanup) $rm -f $tmpart exit ;; esac done !NO!SUBS! $eunicefix Pnews chmod 755 Pnews $spitshell >Pnews.header <<'!NO!SUBS!' case $# in 0) ng=h while $test "$ng" = h ; do $echo "" $echo $n "Newsgroup(s): $c" read ng case $ng in h) $cat <<'EOH' Type the name of one or more newsgroups to which you wish to post an article. If you want to post to multiple newsgroups, it is better to do them all at once than to post to each newsgroup individually, which defeats the news reading programs' strategies of eliminating duplicates. Separate multiple newsgroup names with commas. EOH ;; esac done ;; *) ng=$1 shift ;; esac case $ng in *\ *) ng=`$echo "$ng" | $sed 's/[, ] */,/g'` ;; esac case $ng in net.*|fa.*|mod.*) defdist=net dist=h ;; *.*) defdist=`expr "X$ng" : 'X\([a-z0-9]*\)'` dist=h ;; *) defdist='' dist='' ;; esac while $test "$dist" = h ; do if $test -f $lib/distributions; then $echo " " $echo "Your local distribution prefixes are:" $cat $lib/distributions else $egrep -v '[ ]none$' <$tmpart && \ $test -s $tmpart; then : null else $echo "Unrecognized distribution prefix--type h for help." dist=h fi ;; esac done case $ng in *net.general*) follow=`echo "$ng" | sed 's/net\.general/net.followup/g'` ;; *) follow="" ;; esac case $# in 0) title=h while $test "$title" = h ; do $echo "" $echo $n "Title/Subject: $c" read title case $title in h) $cat <<'EOH' Type the title for your article. Please make it as informative as possible (within reason) so that people who aren't interested won't have to read the article to find out they aren't interested. This includes marking movie spoilers as (spoiler), and rotated jokes as (rot 13). EOH ;; esac done ;; *) title="$*" ;; esac # now build a file with a header for them to edit set X ${USER-${LOGNAME-`who am i`}} shift logname=$1 case $logname in *!*) logname=`expr "$logname" : '!\(.*\)$'` ;; esac case ${NAME-$nametype} in bsd) fullname=`$sed $tmpart <> ${HOME-$LOGDIR}/dead.article $echo "Article saved to ${HOME-$LOGDIR}/dead.article" stateRnmail.1 664 540 12 4424 3473757374 5436 ''' $Header: Rnmail.1,v 4.3 85/05/01 11:34:28 lwall Exp $ ''' ''' $Log: Rnmail.1,v $ ''' Revision 4.3 85/05/01 11:34:28 lwall ''' Baseline for release with 4.3bsd. ''' .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. ''' Bell System Logo is used as a dummy character. ''' .ie n \{\ .tr \(bs-\*(Tr .ds -- \(bs- .if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH RNMAIL 1 LOCAL .SH NAME Rnmail - a program for replying via mail .SH SYNOPSIS .B Rnmail destination_list .br or .br .B Rnmail -h headerfile [oldarticle] .br or .br .B Rnmail .SH DESCRIPTION Rnmail is a friendly interface for mailing replies to news articles. It will ask several questions, then allow you to enter your letter, and then mail it off. If you type h and a carriage return at any point, .I Rnmail will tell you what it wants to know. .PP The -h form is used when invoked from .IR rn . If your editor can edit multiple files, and you want the article to which you are replying to show up as an alternate file, define the environment variable MAILPOSTER as \*(L"Rnmail -h %h %A\*(R". You can also modify the the MAILHEADER environment variable to change the header file that .I rn passes to Rnmail. .SH ENVIRONMENT .IP EDITOR 8 The editor you want to use, if VISUAL is undefined. .Sp Default: whatever your news administrator installed, usually vi. .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .IP LOGNAME 8 Your login name, if USER is undefined. .Sp Default: value of \*(L"whoami\*(R". .IP ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. .Sp Default: whatever your news administrator chose. .IP USER 8 Your login name. .Sp Default: $LOGNAME .IP VISUAL 8 The editor you want to use. .Sp Default: $EDITOR .SH FILES /tmp/rnmail$$ .br ~/dead.letter .SH SEE ALSO rn(1), Pnews(1), mail(1) .SH DIAGNOSTICS .SH BUGS Uses /bin/mail in the absence of sendmail. r news 2.10.1 (or earlier) or 2 for 2.10.2. Last I heard, 2.10.3 was going to have the Xref patch built in. The purpose of the Xref patch is to put an Xref: line in the header of articles posted to more than one newsgroup.Rnmail.SH 664 540 12 12354 3477433052 5615 case $CONFIG in '') . config.sh ;; esac echo "Extracting Rnmail (with variable substitutions)" $spitshell >Rnmail <>Rnmail <<'!NO!SUBS!' tmpart=/tmp/rnmail$$ dotdir=${DOTDIR-${HOME-$LOGDIR}} headerfile="" case $# in 0) ;; *) case $1 in -h) headerfile="$2" case $# in 3) oldart=$3 ;; esac ;; esac ;; esac case $headerfile in '') case $# in 0) to=h while $test "$to" = h ; do $echo "" $echo $n "To: $c" read to case $to in h) $cat <<'EOH' Type the net address of those people that you wish the message sent to. Note that you will be asked later for additional addresses of people to whom the message is not addressed but you wish to get copies. Separate multiple addresses with spaces. EOH ;; esac done ;; *) to="$*" ;; esac to=`$echo "$to" | $sed 's/ */ /g'` title=h while $test "$title" = h ; do $echo "" $echo $n "Title/Subject: $c" read title case $title in h) $cat <<'EOH' Type the title for your message. EOH ;; esac done # now build a file with a header for them to edit orgname=${ORGANIZATION-$orgname} case $orgname in /*) orgname=`$cat $orgname` ;; esac $cat > $tmpart < $tmpart ;; esac file=h while $test "$file" = h ; do $echo "" $echo $n "Prepared file to include [none]: $c" read file case $file in h) $cat <<'EOH' If you have already produced the body of your message, type the filename for it here. If you just want to proceed directly to the editor, type a RETURN. In any event, you will be allowed to edit as many times as you want before you send off the message. EOH ;; '') $echo "" >> $tmpart state=edit ;; *) $cat $file >>$tmpart state=ask ;; esac done $echo "" while true ; do case $state in edit) rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.letter ; $echo saved in ${HOME-$LOGDIR}/dead.letter ; $rm -f $tmpart; exit" trap "$rescue" 1 trap : 2 case "${VISUAL-${EDITOR-}}" in '') tmp=h ;; *) tmp='' ;; esac while $test "$tmp" = h ; do $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" read tmp case $tmp in h) $cat <<'EOH' Type a return to get the default editor, or type the name of the editor you prefer. The default editor depends on the VISUAL and EDITOR environment variables. EOH ;; '') ;; *) VISUAL=$tmp export VISUAL ;; esac done ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=ask ;; ask) $echo "" $echo $n "Send, abort, edit, or list? $c" read ans case $ans in a*) state=rescue ;; e*) state=edit ;; l*) $pager $tmpart state=ask ;; s*) state=send ;; h*) $cat <<'EOH' Type s to send the message, a to abort and append the message to dead.letter, e to edit the message again, or l to list the message. EOH esac ;; send) if $test -f $dotdir/.signature; then echo $n "Append .signature file? [y] $c" read ans case $ans in ''|y*) echo "-- " >> $tmpart cat $dotdir/.signature >> $tmpart ;; esac fi case $mailer in *sendmail) $mailer -t <$tmpart ;; # but recmail does not know about Bcc, alas *recmail) $mailer <$tmpart ;; *) set 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 >> ${HOME-$LOGDIR}/dead.letter $echo "Message saved to ${HOME-$LOGDIR}/dead.letter" state=cleanup ;; cleanup) $rm -f $tmpart exit ;; esac done !NO!SUBS! $eunicefix Rnmail chmod 755 Rnmail - I've probably changed my copy since the version you have. Watch for rn patches in net.sources.bugs. Patches will generally be applyable (is that a word?) by the patch program. If you are just now bringing up news and aren't sure how many patches there areWishlist 664 540 12 1440 3473757447 5657 Generalized article set manipulation Interface to subject listing. Recursive newsgroup visitation. Virtual article abstract type to allow the following: Personalized header munging via % subs. Undigestification. Personal archive perusal. Mail handling. Remotely stored news. Parent command (waiting for ARTFILE interface and recursive newsgroups). Merge Pnews and postnews. Vnews duplicate suppression algorithm for sites that can't do Xref patch. Dynamic allocation of stuff currently restricted by MAXRCLINE. (And pull parallel arrays into array of structs). Separation of .newsrc functions and newsgroup functions to separate processes communicating via pipes (to make fit on non-separate-I-and-D pdp11, or unreasonable facsimiles thereof). Faster!!! Smaller!!! More general!!! Perfect? es mailer="${mailer-/bin/mail}" # if you change this to something that does signatures, take out signature code # your site name case $portable in define) sitename=\`$hostcmd\` ;; undef) sitename="$sitename" ;; esac # youraddng.c 664 540 12 5533 3501473176 5340 /* $Header: addng.c,v 4.3.1.2 85/05/29 09:06:24 lwall Exp $ * * $Log: addng.c,v $ * Revision 4.3.1.2 85/05/29 09:06:24 lwall * New newsgroups without spool directories incorrectly classified as "ancient". * * Revision 4.3.1.1 85/05/10 11:30:50 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:34:41 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "ngdata.h" #include "last.h" #include "util.h" #include "intrp.h" #include "only.h" #include "rcstuff.h" #include "INTERN.h" #include "addng.h" void addng_init() { ; } #ifdef FINDNEWNG /* generate a list of new newsgroups from active file */ bool newlist(munged,checkinlist) bool munged; /* are we scanning the whole file? */ bool checkinlist; { char *tmpname; register char *s; long birthof(); tmpname = savestr(filexp("/tmp/rnew.%$")); tmpfp = fopen(tmpname,"w"); if (tmpfp == Nullfp) { printf(cantcreate,tmpname) FLUSH; return FALSE; } while (fgets(buf,LBUFLEN,actfp) != Nullch) { if (s = index(buf,' ')) { *s++ = '\0'; if (strnEQ(buf,"to.",3)) continue; if (find_ng(buf) == nextrcline && (checkinlist ? (inlist(buf)) : (birthof(buf,(ART_NUM)atol(s)) > lasttime) ) ) { /* if not in .newsrc and younger */ /* than the last time we checked */ fprintf(tmpfp,"%s\n",buf); /* then remember said newsgroup */ } #ifdef FASTNEW else { /* not really a new group */ if (!munged) { /* did we assume not munged? */ fclose(tmpfp); /* then go back, knowing that */ UNLINK(tmpname); free(tmpname); return TRUE; /* active file was indeed munged */ } } #endif } #ifdef DEBUGGING else printf("Bad active record: %s\n",buf) FLUSH; #endif } /* we have successfully generated the list */ fclose(tmpfp); tmpfp = fopen(tmpname,"r"); UNLINK(tmpname); /* be nice to the world */ if (tmpfp == Nullfp) { printf(cantopen,tmpname) FLUSH; return FALSE; } while (fgets(buf,LBUFLEN,tmpfp) != Nullch) { buf[strlen(buf)-1] = '\0'; get_ng(buf,TRUE); /* add newsgroup, maybe */ } fclose(tmpfp); /* be nice to ourselves */ free(tmpname); return FALSE; /* do not call us again */ } /* return creation time of newsgroup */ long birthof(ngnam,ngsize) char *ngnam; ART_NUM ngsize; { char tst[128]; long time(); sprintf(tst, ngsize ? "%s/%s/1" : "%s/%s" ,spool,getngdir(ngnam)); if (stat(tst,&filestat) < 0) return (ngsize ? 0L : time(0)); /* not there, assume something good */ else return filestat.st_mtime; } bool scanactive() { NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ fseek(actfp,0L,0); newlist(TRUE,TRUE); if (nextrcline != oldnext) { /* did we add any new groups? */ return TRUE; } return FALSE; } #endif r release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "ngdata.h" #include "last.h" #include "util.h" #include "intrp.h" #iaddng.h 664 540 12 423 3473757520 5324 /* $Header: addng.h,v 4.3 85/05/01 11:34:48 lwall Exp $ * * $Log: addng.h,v $ * Revision 4.3 85/05/01 11:34:48 lwall * Baseline for release with 4.3bsd. * */ void addng_init(); #ifdef FINDNEWNG bool newlist(); long birthof(); bool scanactive(); #endif add any new groups? */ return TRUE; } return FALSE; } #endif r release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "ngdata.h" #include "last.h" #include "util.h" #include "intrp.h" #iart.c 664 540 12 53451 3477433246 5101 /* $Header: art.c,v 4.3.1.4 85/05/23 12:13:31 lwall Exp $ * * $Log: art.c,v $ * Revision 4.3.1.4 85/05/23 12:13:31 lwall * shouldn't display article that's really a subdirectory. * * Revision 4.3.1.3 85/05/13 09:29:55 lwall * Added CUSTOMLINES option. * * Revision 4.3.1.2 85/05/10 13:46:07 lwall * Fixed header reparse bug on backpage. * * Revision 4.3.1.1 85/05/10 11:30:56 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:34:51 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "ngstuff.h" #include "head.h" #include "cheat.h" #include "help.h" #include "search.h" #include "artio.h" #include "ng.h" #include "bits.h" #include "final.h" #include "artstate.h" #include "rcstuff.h" #include "term.h" #include "sw.h" #include "util.h" #include "backpage.h" #include "intrp.h" #include "INTERN.h" #include "art.h" /* page_switch() return values */ #define PS_NORM 0 #define PS_ASK 1 #define PS_RAISE 2 #define PS_TOEND 3 bool special = FALSE; /* is next page special length? */ int slines = 0; /* how long to make page when special */ ART_LINE highlight = -1; /* next line to be highlighted */ char *restart = Nullch; /* if nonzero, the place where last */ /* line left off on line split */ char *blinebeg; /* where in buffer current line began */ ART_POS alinebeg; /* where in file current line began */ #ifdef INNERSEARCH ART_POS innersearch = 0; /* artpos of end of line we found */ /* for 'g' command */ ART_LINE isrchline = 0; /* last line to display */ bool hide_everything = FALSE; /* if set, do not write page now, */ /* but refresh when done with page */ COMPEX gcompex; /* in article search pattern */ #endif bool firstpage; /* is this the 1st page of article? */ char art_buf[LBUFLEN]; /* place for article lines */ void art_init() { ; } int do_article() { register char *s; ART_POS artsize; /* size in bytes of article */ bool hide_this_line = FALSE; /* hidden header line? */ ART_LINE linenum; /* line # on page, 1 origin */ #ifdef ULSMARTS bool under_lining = FALSE; /* are we underlining a word? */ #endif register char *bufptr = art_buf; /* pointer to input buffer */ register int outpos; /* column position of output */ static char prompt_buf[64]; /* place to hold prompt */ bool notesfiles = FALSE; /* might there be notesfiles junk? */ char oldmode = mode; #ifdef INNERSEARCH register int outputok; #endif if (fstat(artfp->_file,&filestat)) /* get article file stats */ return DA_CLEAN; if (filestat.st_mode & S_IFMT != S_IFREG) return DA_NORM; artsize = filestat.st_size; /* from that get article size */ sprintf(prompt_buf, "%%sEnd of article %ld (of %ld)--what next? [%%s]", (long)art,(long)lastart); /* format prompt string */ prompt = prompt_buf; int_count = 0; /* interrupt count is 0 */ firstpage = (topline < 0); for (;;) { /* for each page */ assert(art == openart); if (do_fseek) { #ifdef ASYNC_PARSE parse_maybe(art); /* make sure header is ours */ #endif artpos = vrdary(artline); if (artpos < 0) artpos = -artpos; /* labs(), anyone? */ if (firstpage) artpos = (ART_POS)0; fseek(artfp,artpos,0); if (artpos < htype[PAST_HEADER].ht_minpos) in_header = SOME_LINE; do_fseek = FALSE; restart = Nullch; } if (firstpage) { if (firstline) { interp(art_buf, (sizeof art_buf), firstline); #ifdef CLEAREOL maybe_eol(); /* PWP */ #endif CLEAREOL fputs(art_buf,stdout) FLUSH; artopen(art); /* rewind article in case interp */ /* forced a header parse */ } else { ART_NUM i; #ifdef CLEAREOL maybe_eol(); /* PWP */ #endif CLEAREOL printf("Article %ld",(long)art); i = (((ART_NUM)toread[ng]) - 1 + was_read(art)); #ifdef DELAYMARK if (i || dmcount) { printf(" (%ld more",(long)i); if (dmcount) printf(" + %ld Marked to return)",(long)dmcount); putchar(')'); } #else if (i) printf(" (%ld more)",(long)i); #endif if (htype[NGS_LINE].ht_flags & HT_HIDE) printf(" in %s", ngname); fputs(":\n",stdout) FLUSH; } start_header(art); forcelast = FALSE; /* we will have our day in court */ restart = Nullch; artline = 0; /* start counting lines */ artpos = 0; vwtary(artline,artpos); /* remember pos in file */ } for (linenum=(firstpage?2:1); in_header || ( #ifdef INNERSEARCH innersearch ? innermore() : #endif linenum<(firstpage?initlines:(special?slines:LINES)) ); linenum++) { /* for each line on page */ if (int_count) { /* exit via interrupt? */ putchar('\n') FLUSH; /* get to left margin */ int_count = 0; /* reset interrupt count */ return DA_NORM; /* skip out of loops */ } if (restart) { /* did not finish last line? */ bufptr = restart; /* then start again here */ restart = Nullch; /* and reset the flag */ } else { /* not a restart */ if (fgets(art_buf,LBUFLEN,artfp)==Nullch) { /* if all done */ return DA_NORM; /* skip out of loops */ } bufptr = art_buf; /* so start at beginning */ art_buf[LBUFLEN-1] = '\0'; /* make sure string ends */ } blinebeg = bufptr; /* remember where we began */ alinebeg = artpos; /* both in buffer and file */ if (in_header && bufptr == art_buf) hide_this_line = parseline(art_buf,do_hiding,hide_this_line); else if (notesfiles && do_hiding && bufptr == art_buf && *art_buf == '#' && isupper(art_buf[1]) && art_buf[2] == ':' ) { fgets(art_buf,sizeof(art_buf),artfp); if (index(art_buf,'!') != Nullch) fgets(art_buf,sizeof(art_buf),artfp); htype[PAST_HEADER].ht_minpos = ftell(artfp); /* exclude notesfiles droppings */ hide_this_line = TRUE; /* and do not print either */ notesfiles = FALSE; } #ifdef CUSTOMLINES if (hideline && bufptr == art_buf && execute(&hide_compex,art_buf) ) hide_this_line = TRUE; #endif if (in_header && htype[in_header].ht_flags & HT_MAGIC) { if (in_header == NGS_LINE) { hide_this_line = (index(art_buf,',') == Nullch); } else if (in_header == EXPIR_LINE) { if (!(htype[EXPIR_LINE].ht_flags & HT_HIDE)) hide_this_line = (strlen(art_buf) < 10); } } if (in_header == SUBJ_LINE && htype[SUBJ_LINE].ht_flags & HT_MAGIC) { /* is this the subject? */ int length; length = strlen(art_buf)-1; artline++; art_buf[length] = '\0'; /* wipe out newline */ #ifdef NOFIREWORKS no_ulfire(); #endif notesfiles = (instr(&art_buf[length-10]," - (nf") != Nullch); if (oldsubject) { length += 7; fputs("(SAME) ",stdout); oldsubject = FALSE; } if (length+UG > COLS) { /* rarely true */ linenum++; vwtary(artline,vrdary(artline-1)+COLS); artline++; } s = art_buf + 8; *s++ = '\0'; /* make into 2 strings */ #ifdef CLEAREOL maybe_eol(); /* PWP */ #endif CLEAREOL fputs(art_buf,stdout) FLUSH; /* print up through : */ if (!UG) putchar(' '); underprint(s); /* print subject underlined */ putchar('\n') FLUSH; /* and finish the line */ } else if (hide_this_line && do_hiding) { /* do not print line? */ linenum--; /* compensate for linenum++ */ if (!in_header) hide_this_line = FALSE; } else { /* just a normal line */ if (highlight==artline) { /* this line to be highlit? */ if (marking == STANDOUT) { #ifdef NOFIREWORKS if (erase_screen) no_sofire(); #endif standout(); } else { #ifdef NOFIREWORKS if (erase_screen) no_ulfire(); #endif underline(); } if (*bufptr == '\n') putchar(' '); } #ifdef INNERSEARCH outputok = !hide_everything; /* get it into register, hopefully */ #endif #ifdef CLEAREOL #ifdef INNERSEARCH if (outputok) #endif maybe_eol(); /* PWP */ #endif CLEAREOL #ifdef CUSTOMLINES if (pagestop && bufptr == art_buf && execute(&page_compex,art_buf) ) linenum = 32700; #endif for (outpos = 0; outpos < COLS; ) { /* while line has room */ if (*bufptr >= ' ') { /* normal char? */ #ifdef ULSMARTS if (*bufptr == '_') { if (bufptr[1] == '\b') { if (!under_lining && highlight!=artline #ifdef INNERSEARCH && outputok #endif ) { under_lining++; if (UG) { if (bufptr != buf && bufptr[-1] == ' ') { outpos--; backspace(); } } underline(); } bufptr += 2; } } else { if (under_lining) { under_lining = 0; un_underline(); if (UG) { if (*bufptr == ' ') goto skip_put; outpos++; } } } #endif #ifdef INNERSEARCH if (outputok) #endif { #ifdef ROTATION if (rotate && !in_header && isalpha(*bufptr)) { if ((*bufptr & 31) <= 13) putchar(*bufptr+13); else putchar(*bufptr-13); } else #endif putchar(*bufptr); } if (*UC && ((highlight==artline && marking == 1) #ifdef ULSMARTS || under_lining #endif )) { backspace(); underchar(); } skip_put: bufptr++; outpos++; } else if (*bufptr == '\n' || !*bufptr) { /* newline? */ #ifdef ULSMARTS if (under_lining) { under_lining = 0; un_underline(); } #endif #ifdef DEBUGGING if (debug & DEB_INNERSRCH && outpos < COLS - 6) { standout(); printf("%4d",artline); un_standout(); } #endif #ifdef INNERSEARCH if (outputok) #endif putchar('\n') FLUSH; restart = 0; outpos = 1000; /* signal normal \n */ } else if (*bufptr == '\t') { /* tab? */ #ifdef INNERSEARCH if (outputok) #endif putchar(*bufptr); bufptr++; outpos += 8 - outpos % 8; } else if (*bufptr == '\f') { /* form feed? */ #ifdef INNERSEARCH if (outputok) #endif fputs("^L",stdout); if (bufptr == blinebeg && highlight != artline) linenum = 32700; /* how is that for a magic number? */ bufptr++; outpos += 2; } else { /* other control char */ #ifdef INNERSEARCH if (outputok) #endif { putchar('^'); if (highlight == artline && *UC && marking == 1) { backspace(); underchar(); putchar(*bufptr+64); backspace(); underchar(); } else putchar(*bufptr+64); } bufptr++; outpos += 2; } } /* end of column loop */ if (outpos < 1000) {/* did line overflow? */ restart = bufptr; /* restart here next time */ if (AM) { /* automatic margins on tty? */ if (!XN && *bufptr == '\n') /* need we simulate XN? */ restart = 0; /* skip the newline */ } else { /* cursor just hangs there */ #ifdef INNERSEARCH if (outputok) #endif putchar('\n') FLUSH; /* so move it down ourselves */ if (*bufptr == '\n') restart = 0; /* simulate XN if need be */ } #ifdef CLEAREOL /* #ifdef INNERSEARCH if (outputok) #endif maybe_eol(); */ /* PWP *//* comment this out for now until I am sure it is needed*/ #endif CLEAREOL } /* handle normal end of output line formalities */ if (highlight == artline) { /* were we highlighting line? */ if (marking == STANDOUT) un_standout(); else un_underline(); highlight = -1; /* no more we are */ } artline++; /* count the line just printed */ if (artline - LINES + 1 > topline) /* did we just scroll top line off? */ topline = artline - LINES + 1; /* then recompute top line # */ } /* determine actual position in file */ if (restart) /* stranded somewhere in the buffer? */ artpos += restart - blinebeg; /* just calculate position */ else /* no, ftell will do */ artpos = ftell(artfp); /* so do ftell */ vwtary(artline,artpos); /* remember pos in file */ } /* end of line loop */ #ifdef INNERSEARCH innersearch = 0; if (hide_everything) { hide_everything = FALSE; *buf = Ctl('l'); goto fake_command; } #endif if (linenum >= 32700)/* did last line have formfeed? */ vwtary(artline-1,-vrdary(artline-1)); /* remember by negating pos in file */ special = FALSE; /* end of page, so reset page length */ firstpage = FALSE; /* and say it is not 1st time thru */ /* extra loop bombout */ if (artpos == artsize) /* did we just now reach EOF? */ return DA_NORM; /* avoid --MORE--(100%) */ /* not done with this article, so pretend we are a pager */ reask_pager: unflush_output(); /* disable any ^O in effect */ standout(); /* enter standout mode */ printf("--MORE--(%ld%%)",(long)(artpos*100/artsize)); un_standout(); /* leave standout mode */ fflush(stdout); /* reinp_pager: /* unused, commented for lint */ eat_typeahead(); #ifdef DEBUGGING if (debug & DEB_CHECKPOINTING) { printf("(%d %d %d)",checkcount,linenum,artline); fflush(stdout); } #endif if (checkcount >= docheckwhen && linenum == LINES && (artline > 40 || checkcount >= docheckwhen+10) ) { /* while he is reading a whole page */ /* in an article he is interested in */ checkcount = 0; checkpoint_rc(); /* update .newsrc */ } collect_subjects(); /* loads subject cache until */ /* input is pending */ mode = 'p'; getcmd(buf); if (errno) { if (LINES < 100 && !int_count) *buf = '\f';/* on CONT fake up refresh */ else { *buf = 'q'; /* on INTR or paper just quit */ } } carriage_return(); #ifndef CLEAREOL erase_eol(); /* and erase the prompt */ #else if (erase_screen && can_home_clear) /* PWP was here */ clear_rest(); else erase_eol(); /* and erase the prompt */ #endif CLEAREOL fflush(stdout); fake_command: /* used by innersearch */ /* parse and process pager command */ switch (page_switch()) { case PS_ASK: /* reprompt "--MORE--..." */ goto reask_pager; case PS_RAISE: /* reparse on article level */ mode = oldmode; return DA_RAISE; case PS_TOEND: /* fast pager loop exit */ mode = oldmode; return DA_TOEND; case PS_NORM: /* display more article */ break; } } /* end of page loop */ } /* process pager commands */ int page_switch() { register char *s; switch (*buf) { case 'd': case Ctl('d'): /* half page */ special = TRUE; slines = LINES / 2 + 1; if (marking && *blinebeg != '\f' #ifdef CUSTOMLINES && (!pagestop || blinebeg != art_buf || !execute(&page_compex,blinebeg)) #endif ) { up_line(); highlight = --artline; restart = blinebeg; artpos = alinebeg; } return PS_NORM; case '!': /* shell escape */ escapade(); return PS_ASK; #ifdef INNERSEARCH case Ctl('i'): gline = 3; sprintf(cmd_buf,"^[^%c]",*blinebeg); compile(&gcompex,cmd_buf,TRUE,TRUE); goto caseG; case Ctl('g'): gline = 3; compile(&gcompex,"^Subject:",TRUE,TRUE); goto caseG; case 'g': /* in-article search */ if (!finish_command(FALSE))/* get rest of command */ return PS_ASK; s = buf+1; if (isspace(*s)) s++; if ((s = compile(&gcompex,s,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; return PS_ASK; } carriage_return(); erase_eol(); /* erase the prompt */ /* FALL THROUGH */ caseG: case 'G': { /* ART_LINE lines_to_skip = 0; */ ART_POS start_where; if (gline < 0 || gline > LINES-2) gline = LINES-2; #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("Start here? %d >=? %d\n",topline + gline + 1,artline) FLUSH; #endif if (*buf == Ctl('i') || topline+gline+1 >= artline) start_where = artpos; /* in case we had a line wrap */ else { start_where = vrdary(topline+gline+1); if (start_where < 0) start_where = -start_where; } if (start_where < htype[PAST_HEADER].ht_minpos) start_where = htype[PAST_HEADER].ht_minpos; fseek(artfp,(long)start_where,0); innersearch = 0; /* assume not found */ while (fgets(buf, sizeof buf, artfp) != Nullch) { /* lines_to_skip++; NOT USED NOW */ #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("Test %s",buf) FLUSH; #endif if (execute(&gcompex,buf) != Nullch) { innersearch = ftell(artfp); break; } } if (!innersearch) { fseek(artfp,artpos,0); fputs("(Not found)",stdout) FLUSH; return PS_ASK; } #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("On page? %ld <=? %ld\n",(long)innersearch,(long)artpos) FLUSH; #endif if (innersearch <= artpos) { /* already on page? */ if (innersearch < artpos) { artline = topline+1; while (vrdary(artline) < innersearch) artline++; } highlight = artline - 1; #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("@ %d\n",highlight) FLUSH; #endif topline = highlight - gline; if (topline < -1) topline = -1; *buf = '\f'; /* fake up a refresh */ innersearch = 0; return page_switch(); } else { /* who knows how many lines it is? */ do_fseek = TRUE; hide_everything = TRUE; } return PS_NORM; } #else case 'g': case 'G': case Ctl('g'): notincl("g"); return PS_ASK; #endif case '\n': /* one line */ special = TRUE; slines = 2; return PS_NORM; #ifdef ROTATION case 'X': rotate = !rotate; /* FALL THROUGH */ #endif case 'l': case '\f': /* refresh screen */ #ifdef DEBUGGING if (debug & DEB_INNERSRCH) { printf("Topline = %d",topline) FLUSH; gets(buf); } #endif clear(); do_fseek = TRUE; artline = topline; if (artline < 0) artline = 0; firstpage = (topline < 0); return PS_NORM; case 'b': case '\b': /* I like backspace for this -- PWP */ /* Leaving it undocumented in case */ /* I want to steal the key--LAW */ case Ctl('b'): { /* back up a page */ ART_LINE target; #ifndef CLEAREOL clear(); #else if (can_home_clear) /* if we can home do it -- PWP */ home_cursor(); else clear(); #endif CLEAREOL do_fseek = TRUE; /* reposition article file */ target = topline - (LINES - 2); artline = topline; do { artline--; } while (artline >= 0 && artline > target && vrdary(artline-1) >= 0); topline = artline; /* remember top line of screen */ /* (line # within article file) */ if (artline < 0) artline = 0; firstpage = (topline < 0); return PS_NORM; } case 'h': { /* help */ int cmd; if ((cmd = help_page()) > 0) pushchar(cmd); return PS_ASK; } case '\177': case '\0': /* treat del,break as 'n' */ *buf = 'n'; /* FALL THROUGH */ case 'k': case 'K': case 'n': case 'N': case Ctl('n'): case 's': case 'S': case 'u': case 'w': case 'W': case '|': mark_as_read(art); /* mark article as read */ /* FALL THROUGH */ case '#': case '$': case '&': case '-': case '.': case '/': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '=': case '?': case 'c': case 'C': case 'f': case 'F': case 'j': case Ctl('k'): case 'm': case 'M': case 'p': case 'P': case Ctl('p'): case 'Q': case 'r': case 'R': case Ctl('r'): case 'v': case 'Y': #ifndef ROTATION case 'x': case 'X': #endif case Ctl('x'): case '^': #ifdef ROTATION rotate = FALSE; #endif reread = FALSE; do_hiding = TRUE; if (index("nNpP",*buf) == Nullch && index("wWsS!&|/?123456789.",*buf) != Nullch) { setdfltcmd(); standout(); /* enter standout mode */ printf(prompt,mailcall,dfltcmd); /* print prompt, whatever it is */ un_standout(); /* leave standout mode */ putchar(' '); fflush(stdout); } return PS_RAISE; /* and pretend we were at end */ #ifdef ROTATION case 'x': rotate = TRUE; /* FALL THROUGH */ #endif case 'y': case Ctl('v'): /* I like emacs -- PWP */ /* Leaving it undocumented in case */ /* I want to steal the key--LAW */ case ' ': /* continue current article */ if (erase_screen) { /* -e? */ #ifndef CLEAREOL clear(); /* clear screen */ #else if (can_home_clear) /* if we can home do it -- PWP */ home_cursor(); else clear(); /* else clear screen */ #endif CLEAREOL if (*blinebeg != '\f' #ifdef CUSTOMLINES && (!pagestop || blinebeg != art_buf || !execute(&page_compex,blinebeg)) #endif ) { restart = blinebeg; artline--; /* restart this line */ artpos = alinebeg; if (marking) /* and mark repeated line */ highlight = artline; } topline = artline; /* and remember top line of screen */ /* (line # within article file) */ } else if (marking && *blinebeg != '\f' #ifdef CUSTOMLINES && (!pagestop || blinebeg != art_buf || !execute(&page_compex,blinebeg)) #endif ) { /* are we marking repeats? */ up_line(); /* go up one line */ highlight = --artline;/* and get ready to highlight */ restart = blinebeg; /* the old line */ artpos = alinebeg; } return PS_NORM; case 'q': /* quit this article? */ do_hiding = TRUE; return PS_TOEND; default: fputs(hforhelp,stdout) FLUSH; settle_down(); return PS_ASK; } } #ifdef INNERSEARCH bool innermore() { if (artpos < innersearch) { /* not even on page yet? */ #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("Not on page %ld < %ld\n",(long)artpos,(long)innersearch) FLUSH; #endif return TRUE; } if (artpos == innersearch) { /* just got onto page? */ isrchline = artline; /* remember first line after */ highlight = artline - 1; #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("There it is %ld = %ld, %d @ %d\n",(long)artpos, (long)innersearch,hide_everything,highlight) FLUSH; #endif if (hide_everything) { /* forced refresh? */ topline = highlight - gline; if (topline < -1) topline = -1; return FALSE; /* let refresh do it all */ } } #ifdef DEBUGGING if (debug & DEB_INNERSRCH) printf("Not far enough? %d = 32700)/* did last line have formfeed?art.h 664 540 12 533 3473757500 5035 /* $Header: art.h,v 4.3 85/05/01 11:35:29 lwall Exp $ * * $Log: art.h,v $ * Revision 4.3 85/05/01 11:35:29 lwall * Baseline for release with 4.3bsd. * */ /* do_article() return values */ #define DA_NORM 0 #define DA_RAISE 1 #define DA_CLEAN 2 #define DA_TOEND 3 void art_init(); int do_article(); int page_switch(); bool innermore(); case 'N': case Ctl('n'): case 's': case 'S': case 'u': case 'w': case 'W': case '|': mark_as_read(art); /* mark article as read */ /* FALL THROUGH art.help.SH 664 540 12 5257 3473757355 6107 case $CONFIG in '') . config.sh ;; esac echo "Extracting art.help (with variable substitutions)" $spitshell >art.help <_file,&filestat)) return artfp; if (filestat.st_size < (sizeof tmpbuf)) { fgets(tmpbuf,(sizeof tmpbuf),artfp); if (*tmpbuf == '/') { /* is a "link" to another article */ fclose(artfp); if (s=index(tmpbuf,'\n')) *s = '\0'; if (!(artfp = fopen(tmpbuf,"r"))) openart = 0; else { if (*linkartname) free(linkartname); linkartname = savestr(tmpbuf); } } else fseek(artfp,0L,0); /* get back to the beginning */ } } #endif return artfp; /* and return either fp or NULL */ } / return artfp; /* and say we succeeded */ } if (artfp != Nullfp) { /* it was somebody else? */ fclose(artfp); /* put them out of their misery */ openart = 0; /* and remember them no more */ } sprintf(artname,"%ld",(long)artnum); /* produce the name of the article */ if (artfp = fopen(artname,"r")) /* if we can open it */ openart = artnum; /* remember what we did here */ #ifdef LINKART { char tmpbuf[artio.h 664 540 12 1173 3473757460 5413 /* $Header: artio.h,v 4.3 85/05/01 11:35:43 lwall Exp $ * * $Log: artio.h,v $ * Revision 4.3 85/05/01 11:35:43 lwall * Baseline for release with 4.3bsd. * */ EXT ART_POS artpos INIT(0); /* byte position in article file */ EXT ART_LINE artline INIT(0); /* current line number in article file */ EXT FILE *artfp INIT(Nullfp); /* current article file pointer */ EXT ART_NUM openart INIT(0); /* what is the currently open article number? */ #ifdef LINKART EXT char *linkartname INIT(nullstr);/* real name of article for Eunice */ #endif void artio_init(); FILE *artopen(); /* open an article unless already opened */ if (artfp != Nullfp) { /* it was somebody else? */ fclose(artfp); /* put them out of their misery */ openart = 0; /* and remember them no more */ } sprintf(artname,"%ld",(long)artnum); /* produce the name of the article */ if (artfp = fopen(artname,"r")) /* if we can open it */ openart = artnum; /* remember what we did here */ #ifdef LINKART { char tmpbuf[artsrch.c 664 540 12 15237 3473757264 5766 /* $Header: artsrch.c,v 4.3 85/05/01 11:35:47 lwall Exp $ * * $Log: artsrch.c,v $ * Revision 4.3 85/05/01 11:35:47 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "term.h" #include "util.h" #include "intrp.h" #include "bits.h" #include "kfile.h" #include "head.h" #include "final.h" #include "cheat.h" #include "ng.h" #include "artio.h" #include "INTERN.h" #include "artsrch.h" void artsrch_init() { #ifdef ARTSEARCH #ifdef ZEROGLOB init_compex(&sub_compex); init_compex(&art_compex); #endif #endif } /* search for an article containing some pattern */ #ifdef ARTSEARCH int art_search(patbuf,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 = Nullch; /* list of commands to do */ int normal_return = SRCH_NOTFOUND; /* assume no commands */ bool saltaway = FALSE; /* store in KILL file? */ char howmuch; /* search just the subjects */ bool doread; /* search read articles? */ bool foldcase = TRUE; /* fold upper and lower case? */ int_count = 0; if (cmdchr == '/' || cmdchr == '?') { /* normal search? */ if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return SRCH_ABORT; compex = &art_compex; if (patbuf[1]) { howmuch = 0; doread = FALSE; } else { howmuch = art_howmuch; doread = art_doread; } s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */ pattern = buf; if (*pattern) { if (*lastpat) free(lastpat); lastpat = savestr(pattern); } if (*s) { /* modifiers or commands? */ for (s++; *s && index("Kharc",*s); s++) { if (*s == 'h') /* scan header */ howmuch = 1; else if (*s == 'a') /* scan article */ howmuch = 2; else if (*s == 'r') /* scan read articles */ doread = TRUE; else if (*s == 'K') /* put into KILL file */ saltaway = TRUE; else if (*s == 'c') /* make search case sensitive */ foldcase = FALSE; } } while (isspace(*s) || *s == ':') s++; if (*s) { if (*s == 'm' || *s == 'M') doread = TRUE; if (*s == 'k') /* grandfather clause */ *s = 'j'; cmdlst = savestr(s); normal_return = SRCH_DONE; } art_howmuch = howmuch; art_doread = doread; if (srchahead) srchahead = -1; } else { register char *h; howmuch = 0; /* just search subjects */ doread = (cmdchr == Ctl('p')); if (cmdchr == Ctl('n')) normal_return = SRCH_SUBJDONE; compex = &sub_compex; pattern = patbuf+1; strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,patbufsiz - (h-patbuf),"%s"); /* fetch current subject */ if (cmdchr == 'K') { saltaway = TRUE; cmdchr = 'k'; } if (cmdchr == 'k') { normal_return = SRCH_DONE; cmdlst = savestr("j"); mark_as_read(art); /* this article has this subject */ if (!*h) { #ifdef VERBOSE IF(verbose) fputs("\nCannot delete null subject.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNull subject.\n",stdout) FLUSH; #endif return SRCH_ABORT; } #ifdef VERBOSE else if (verbose) printf("\nMarking subject \"%s\" as read.\n",h) FLUSH; #endif } else if (!srchahead) srchahead = -1; h[24] = '\0'; /* compensate for notesfiles */ while (*h) { if (index("/\\[.^*$'\"",*h) != Nullch) *h++ = '.'; else h++; } #ifdef DEBUGGING if (debug) { printf("\npattern = %s\n",pattern) FLUSH; } #endif } if ((s = compile(compex,pattern,TRUE,foldcase)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; return SRCH_ABORT; } #ifdef KILLFILES if (saltaway) { char saltbuf[LBUFLEN]; s = saltbuf; sprintf(s,"/%s/",pattern); s += strlen(s); if (doread) *s++ = 'r'; if (howmuch==1) *s++ = 'h'; else if (howmuch==2) *s++ = 'a'; *s++ = ':'; if (!cmdlst) cmdlst = savestr("j"); safecpy(s,cmdlst,LBUFLEN-(s-saltbuf)); kf_append(saltbuf); } #endif if (cmdlst && index(cmdlst,'=')) normal_return = SRCH_ERROR; /* listing subjects is an error? */ if (get_cmd) { fputs("\nSearching...\n",stdout) FLUSH; /* give them something to read */ } if (backward) { if (cmdlst && art < lastart) art++; /* include current article */ if (doread) check_first(absfirst); } else { if (art > lastart) art = (doread ? absfirst : firstart) - 1; else if (cmdlst && art > absfirst) art--; /* include current article */ check_first(art); } if (srchahead > 0) { if (!backward) art = srchahead - 1; srchahead = -1; } assert(!cmdlst || *cmdlst); for (;;) { if (int_count) { int_count = 0; if (cmdlst) free(cmdlst); return SRCH_INTR; } if (backward ? (--art < absfirst || (!doread && art < firstart)) : (++art > lastart) ) { /* out of articles? */ if (cmdlst) free(cmdlst); return normal_return; } /*NOSTRICT*/ if (doread || !was_read(art)) { if (wanted(compex,art,howmuch)) { /* does the shoe fit? */ if (cmdlst) { if (perform(cmdlst,TRUE)) { if (cmdlst) free(cmdlst); return SRCH_INTR; } } else { if (cmdlst) free(cmdlst); return SRCH_FOUND; } } else if (!cmdlst && ! (art%50)) { printf("...%ld",(long)art); fflush(stdout); } } } } /* determine if article fits pattern */ /* returns TRUE if it exists and fits pattern, FALSE otherwise */ bool wanted(compex, artnum, scope) COMPEX *compex; ART_NUM artnum; char scope; { if (!scope) { char subj_buf[266]; strcpy(subj_buf, "Subject: "); strncpy(subj_buf+9,fetchsubj(artnum,FALSE,FALSE),256); #ifdef DEBUGGING if (debug & DEB_SEARCH_AHEAD) printf("%s\n",subj_buf) FLUSH; #endif return execute(compex,subj_buf) != Nullch; } #ifdef CACHESUBJ else fetchsubj(artnum,FALSE,FALSE);/* might as well get subject handy */ #endif if (artopen(artnum) == Nullfp) /* ensure that article is open */ return FALSE; /* if not, return NO MATCH */ scope--; while (fgets(buf,LBUFLEN,artfp) != Nullch) { /* for each line of article */ if (!scope && index(buf,':') == Nullch && *buf != ' ' && *buf != '\t') /* if headers only and out of header */ return FALSE; /* say no go */ if (execute(compex,buf) != Nullch) { /* does pattern matcher match? */ return TRUE; /* say Eureka */ } } return FALSE; /* out of article, so no match */ } #endif G; case Ctl('g'): gline = 3; compile(&gcompex,"^Subject:",TRUE,TRUE); goto caseG; case 'g': /* in-article search */ if (!finish_command(FALSE))/* get rest of command */ return PS_ASK; s = buf+1; if (isspace(*s)) s++; if ((s = compile(&gcompex,s,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%sartsrch.h 664 540 12 1703 3473757441 5741 /* $Header: artsrch.h,v 4.3 85/05/01 11:35:55 lwall Exp $ * * $Log: artsrch.h,v $ * Revision 4.3 85/05/01 11:35:55 lwall * Baseline for release with 4.3bsd. * */ #ifndef NBRA #include "search.h" #endif #ifdef ARTSEARCH #define SRCH_ABORT 0 #define SRCH_INTR 1 #define SRCH_FOUND 2 #define SRCH_NOTFOUND 3 #define SRCH_DONE 4 #define SRCH_SUBJDONE 5 #define SRCH_ERROR 6 #endif EXT char *lastpat INIT(nullstr); /* last search pattern */ #ifdef ARTSEARCH EXT COMPEX sub_compex; /* last compiled subject search */ EXT COMPEX art_compex; /* last compiled normal search */ # ifdef CONDSUB EXT COMPEX *bra_compex INIT(&art_compex); /* current compex with brackets */ # endif EXT char art_howmuch; /* search just the subjects */ EXT bool art_doread; /* search read articles? */ #endif void artsrch_init(); #ifdef ARTSEARCH int art_search(); bool wanted(); /* return TRUE if current article matches pattern */ #endif cmdchr == '?' || cmdchr == Ctl('p'); /* direction of seartstate.h 664 540 12 2111 3473762335 6112 /* $Header: artstate.h,v 4.3.1.2 85/05/13 09:30:30 lwall Exp $ * * $Log: artstate.h,v $ * Revision 4.3.1.2 85/05/13 09:30:30 lwall * Added CUSTOMLINES option. * * Revision 4.3.1.1 85/05/10 11:31:32 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:35:59 lwall * Baseline for release with 4.3bsd. * */ EXT bool reread INIT(FALSE); /* consider current art temporarily */ /* unread? */ EXT bool do_fseek INIT(FALSE); /* should we back up in article file? */ EXT bool oldsubject INIT(FALSE); /* not 1st art in subject thread */ EXT ART_LINE topline INIT(-1); /* top line of current screen */ EXT bool do_hiding INIT(TRUE); /* hide header lines with -h? */ #ifdef ROTATION EXT bool rotate INIT(FALSE); /* has rotation been requested? */ #endif EXT char *prompt; /* pointer to current prompt */ EXT char *firstline INIT(Nullch); /* special first line? */ #ifdef CUSTOMLINES EXT char *hideline INIT(Nullch); /* custom line hiding? */ EXT char *pagestop INIT(Nullch); /* custom page terminator? */ EXT COMPEX hide_compex; EXT COMPEX page_compex; #endif mdlst = Nullch; /* list of commands to do */ int normal_return = SRCH_NOTFOUND; /* assume no commands */ bool saltaway = FALSE; /* store in KILL file? */ char howmuch; /* search just the subjects */ bool doread; /* search read articles? */ bool foldcase = TRUE; /* fold upper and lower case? */ int_count = 0; if (cmdchr == '/' || cmdchr == '?') { /* normal search? */ if (get_cmd && buf == patbuf) backpage.c 664 540 12 4016 3473757400 6016 /* $Header: backpage.c,v 4.3 85/05/01 11:36:03 lwall Exp $ * * $Log: backpage.c,v $ * Revision 4.3 85/05/01 11:36:03 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "final.h" #include "INTERN.h" #include "backpage.h" ART_LINE maxindx = -1; long lseek(); 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 DEBUGGING 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 DEBUGGING 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; } wmuch = 1; else if (*s == 'a') /* scan article */ howmuch = 2; else if (*s == 'r') /* scan read articles */ doread = TRUE; else if (*s == 'K') /* put into KILL file */ saltaway = TRUE; else if (*s == 'c') /* make search case sensitive */ foldcase = FALSE; } } while (isspace(*s) || *s == ':') s++; if (*s) { if (*s == 'm' || *s == 'M') doread = TRUE; if (*s == 'k') /* grandfather clause */ *s = 'j'; cmdlst = savestr(s); normalbackpage.h 664 540 12 1000 3473757465 6024 /* $Header: backpage.h,v 4.3 85/05/01 11:36:11 lwall Exp $ * * $Log: backpage.h,v $ * Revision 4.3 85/05/01 11:36:11 lwall * Baseline for release with 4.3bsd. * */ /* things for doing the 'back page' command */ EXT int varyfd INIT(0); /* virtual array file for storing */ /* file offsets */ EXT ART_POS varybuf[VARYSIZE]; /* current window onto virtual array */ EXT long oldoffset INIT(-1); /* offset to block currently in window */ void backpage_init(); ART_POS vrdary(); void vwtary(); bits.c 664 540 12 34767 3473757005 5263 /* $Header: bits.c,v 4.3 85/05/01 11:36:15 lwall Exp $ * * $Log: bits.c,v $ * Revision 4.3 85/05/01 11:36:15 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rcstuff.h" #include "head.h" #include "util.h" #include "final.h" #include "rn.h" #include "cheat.h" #include "ng.h" #include "artio.h" #include "intrp.h" #include "ngdata.h" #include "rcln.h" #include "kfile.h" #include "INTERN.h" #include "bits.h" #ifdef DBM # ifdef NULL # undef NULL # endif NULL # include #endif DBM MEM_SIZE ctlsize; /* size of bitmap in bytes */ void bits_init() { #ifdef DELAYMARK dmname = savestr(filexp(RNDELNAME)); #else ; #endif } /* checkpoint the .newsrc */ void checkpoint_rc() { #ifdef DEBUGGING if (debug & DEB_CHECKPOINTING) { fputs("(ckpt)",stdout); fflush(stdout); } #endif if (doing_ng) restore_ng(); /* do not restore M articles */ if (rc_changed) write_rc(); #ifdef DEBUGGING if (debug & DEB_CHECKPOINTING) { fputs("(done)",stdout); fflush(stdout); } #endif } /* reconstruct the .newsrc line in a human readable form */ void restore_ng() { register char *s, *mybuf = buf; register ART_NUM i; ART_NUM count=0; int safelen = LBUFLEN - 16; strcpy(buf,rcline[ng]); /* start with the newsgroup name */ s = buf + rcnums[ng] - 1; /* use s for buffer pointer */ *s++ = rcchar[ng]; /* put the requisite : or !*/ *s++ = ' '; /* put the not-so-requisite space */ for (i=1; i<=lastart; i++) { /* for each article in newsgroup */ if (s-mybuf > safelen) { /* running out of room? */ safelen *= 2; if (mybuf == buf) { /* currently static? */ *s = '\0'; mybuf = safemalloc((MEM_SIZE)safelen + 16); strcpy(mybuf,buf); /* so we must copy it */ s = mybuf + (s-buf); /* fix the pointer, too */ } else { /* just grow in place, if possible */ char *newbuf; newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16); s = newbuf + (s-mybuf); mybuf = newbuf; } } 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 DEBUGGING if (debug & DEB_NEWSRC_LINE && !panic) { printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH; printf("%s\n",mybuf) FLUSH; } #endif free(rcline[ng]); /* return old rc line */ if (mybuf == buf) { rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1); /* grab a new rc line */ strcpy(rcline[ng], buf); /* and load it */ } else { mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1); /* be nice to the heap */ rcline[ng] = mybuf; } *(rcline[ng] + rcnums[ng] - 1) = '\0'; if (rcchar[ng] == NEGCHAR) { /* did they unsubscribe? */ printf(unsubto,ngname) FLUSH; toread[ng] = TR_UNSUB; /* make line invisible */ } else /*NOSTRICT*/ toread[ng] = (ART_UNREAD)count; /* remember how many unread there are */ } /* mark an article unread, keeping track of toread[] */ void onemore(artnum) ART_NUM artnum; { #ifdef DEBUGGING if (debug && artnum < firstart) { printf("onemore: %d < %d\n",artnum,firstart) FLUSH; return; } #endif if (ctl_read(artnum)) { ctl_clear(artnum); ++toread[ng]; } } /* mark an article read, keeping track of toread[] */ void oneless(artnum) ART_NUM artnum; { #ifdef DEBUGGING if (debug && artnum < firstart) { printf("oneless: %d < %d\n",artnum,firstart) FLUSH; return; } #endif if (!ctl_read(artnum)) { ctl_set(artnum); if (toread[ng] > TR_NONE) --toread[ng]; } } /* mark an article as unread, making sure that firstart is properly handled */ /* cross-references are left as read in the other newsgroups */ void unmark_as_read(artnum) ART_NUM artnum; { check_first(artnum); onemore(artnum); #ifdef MCHASE if (!parse_maybe(artnum)) chase_xrefs(artnum,FALSE); #endif } #ifdef DELAYMARK /* temporarily mark article as read. When newsgroup is exited, articles */ /* will be marked as unread. Called via M command */ void delay_unmark(artnum) ART_NUM artnum; { if (dmfp == Nullfp) { dmfp = fopen(dmname,"w"); if (dmfp == Nullfp) { printf(cantcreate,dmname) FLUSH; sig_catcher(0); } } oneless(artnum); /* set the correct bit */ dmcount++; fprintf(dmfp,"%ld\n",(long)artnum); } #endif /* mark article as read. If article is cross referenced to other */ /* newsgroups, mark them read there also. */ void mark_as_read(artnum) ART_NUM artnum; { oneless(artnum); /* set the correct bit */ checkcount++; /* get more worried about crashes */ chase_xrefs(artnum,TRUE); } /* make sure we have bits set correctly down to firstart */ void check_first(min) ART_NUM min; { register ART_NUM i = firstart; if (min < absfirst) min = absfirst; if (min < i) { for (i--; i>=min; i--) ctl_set(i); /* mark as read */ firstart = min; } } /* bring back articles marked with M */ #ifdef DELAYMARK void yankback() { register ART_NUM anum; if (dmfp) { /* delayed unmarks pending? */ #ifdef VERBOSE printf("\nReturning %ld Marked article%s...\n",(long)dmcount, dmcount == 1 ? nullstr : "s") FLUSH; #endif fclose(dmfp); if (dmfp = fopen(dmname,"r")) { while (fgets(buf,sizeof buf,dmfp) != Nullch) { anum = (ART_NUM)atol(buf); /*NOSTRICT*/ onemore(anum); /* then unmark them */ #ifdef MCHASE chase_xrefs(anum,FALSE); #endif } fclose(dmfp); dmfp = Nullfp; UNLINK(dmname); /* and be tidy */ } else { printf(cantopen,dmname) FLUSH; sig_catcher(0); } } dmcount = 0; } #endif /* run down xref list and mark as read or unread */ int chase_xrefs(artnum,markread) ART_NUM artnum; int markread; { #ifdef ASYNC_PARSE if (parse_maybe(artnum)) /* make sure we have right header */ return -1; #endif #ifdef DBM { datum lhs, rhs; datum fetch(); register char *idp; char *ident_buf; static FILE * hist_file = Nullfp; #else if ( #ifdef DEBUGGING debug & DEB_FEED_XREF || #endif htype[XREF_LINE].ht_minpos >= 0) { /* are there article# xrefs? */ #endif DBM char *xref_buf, *curxref; register char *xartnum; char *rver_buf = Nullch; static char *inews_site = Nullch; register ART_NUM x; char tmpbuf[128]; #ifdef DBM rver_buf = fetchlines(artnum,NGS_LINE); /* get Newsgroups */ if (!index(rver_buf,',')) /* if no comma, no Xref! */ return 0; if (hist_file == Nullfp) { /* Init. file accesses */ #ifdef DEBUGGING if (debug) printf ("chase_xref: opening files\n"); #endif dbminit(filexp(ARTFILE)); if ((hist_file = fopen (filexp(ARTFILE), "r")) == Nullfp) return 0; } xref_buf = safemalloc((MEM_SIZE)BUFSIZ); ident_buf = fetchlines(artnum,MESSID_LINE); /* get Message-ID */ #ifdef DEBUGGING if (debug) printf ("chase_xref: Message-ID: %s\n", ident_buf); #endif idp = ident_buf; 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; fseek (hist_file, *((long *)rhs.dptr), 0); /* datum returned is position in hist file */ fgets (xref_buf, BUFSIZ, hist_file); #ifdef DEBUGGING if (debug) printf ("Xref from history: %s\n", xref_buf); #endif curxref = cpytill(tmpbuf, xref_buf, '\t') + 1; curxref = cpytill(tmpbuf, curxref, '\t') + 1; #ifdef DEBUGGING if (debug) printf ("chase_xref: curxref: %s\n", curxref); #endif #else !DBM #ifdef DEBUGGING if (htype[XREF_LINE].ht_minpos >= 0) #endif xref_buf = fetchlines(artnum,XREF_LINE); /* get xrefs list */ #ifdef DEBUGGING else { xref_buf = safemalloc((MEM_SIZE)100); printf("Give Xref: ") FLUSH; gets(xref_buf); } #endif #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) printf("Xref: %s\n",xref_buf) FLUSH; #endif curxref = cpytill(tmpbuf,xref_buf,' ') + 1; /* Make sure site name on Xref matches what inews thinks site is. * Check first against last inews_site. If it matches, fine. * If not, fetch inews_site from current 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. */ if (inews_site == Nullch || strNE(tmpbuf,inews_site)) { char *t; if (inews_site != Nullch) free(inews_site); rver_buf = fetchlines(artnum,RVER_LINE); if ((t = instr(rver_buf,"; site ")) == Nullch) inews_site = savestr(nullstr); else { char new_site[128]; cpytill(new_site,t + 7,'.'); inews_site = savestr(new_site); } if (strNE(tmpbuf,inews_site)) { #ifdef DEBUGGING if (debug) printf("Xref not from %s--ignoring\n",inews_site) FLUSH; #endif goto wild_goose; } } #endif DBM while (*curxref) { /* for each newsgroup */ curxref = cpytill(tmpbuf,curxref,' '); #ifdef DBM xartnum = index(tmpbuf,'/'); #else xartnum = index(tmpbuf,':'); #endif DBM if (!xartnum) /* probably an old-style Xref */ break; *xartnum++ = '\0'; if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */ x = atol(xartnum); if (x) if (markread) { if (addartnum(x,tmpbuf)) goto wild_goose; } #ifdef MCHASE else subartnum(x,tmpbuf); #endif } while (*curxref && isspace(*curxref)) curxref++; } wild_goose: free(xref_buf); #ifdef DBM free(ident_buf); #endif DBM if (rver_buf != Nullch) free(rver_buf); } return 0; } int initctl() { char *mybuf = buf; /* place to decode rc line */ register char *s, *c, *h; register long i; register ART_NUM unread; #ifdef DELAYMARK dmcount = 0; #endif if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */ return -1; absfirst = getabsfirst(ng,lastart); /* remember first existing article */ if (!absfirst) /* no articles at all? */ absfirst = 1; /* pretend there is one */ #ifndef lint ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20); #endif lint ctlarea = safemalloc(ctlsize); /* allocate control area */ /* now modify ctlarea to reflect what has already been read */ for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ; /* find numbers in rc line */ i = strlen(s); #ifndef lint if (i >= LBUFLEN-2) /* bigger than buf? */ mybuf = safemalloc((MEM_SIZE)(i+2)); #endif lint strcpy(mybuf,s); /* make scratch copy of line */ mybuf[i++] = ','; /* put extra comma on the end */ mybuf[i] = '\0'; s = mybuf; /* initialize the for loop below */ if (strnEQ(s,"1-",2)) { /* can we save some time here? */ firstart = atol(s+2)+1; /* ignore first range thusly */ s=index(s,',') + 1; } else firstart = 1; /* all the bits are valid for now */ if (absfirst > firstart) { /* do we know already? */ firstart = absfirst; /* no point calling getngmin again */ } else if (artopen(firstart) == Nullfp) { /* first unread article missing? */ i = getngmin(".",firstart); /* see if expire has been busy */ if (i) { /* avoid a bunch of extra opens */ firstart = i; } } #ifdef PENDING # ifdef CACHESUBJ subj_to_get = firstart; # endif #endif unread = lastart - firstart + 1; /* assume this range unread */ for (i=OFFSET(firstart)/BITSPERBYTE; i lastart) max = lastart; if (min <= max) /* non-null range? */ unread -= max - min + 1;/* adjust unread count */ for (i=min; i<=max; i++) /* for all articles in range */ ctl_set(i); /* mark them read */ } else if ((i = atol(s)) >= firstart && i <= lastart) { /* is single number reasonable? */ ctl_set(i); /* mark it read */ unread--; /* decrement articles to read */ } #ifdef DEBUGGING if (debug & DEB_CTLAREA_BITMAP) { printf("\n%s\n",s) FLUSH; for (i=1; i <= lastart; i++) if (! was_read(i)) printf("%ld ",(long)i) FLUSH; } #endif } #ifdef DEBUGGING if (debug & DEB_CTLAREA_BITMAP) { fputs("\n(hit CR)",stdout) FLUSH; gets(cmd_buf); } #endif if (mybuf != buf) free(mybuf); toread[ng] = unread; return 0; } void grow_ctl() { ART_NUM newlast; ART_NUM tmpfirst; MEM_SIZE newsize; register ART_NUM i; forcegrow = FALSE; newlast = getngsize(ng); if (newlast > lastart) { ART_NUM tmpart = art; #ifndef lint newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2); #else newsize = Null(MEM_SIZE); #endif lint if (newsize > ctlsize) { newsize += 20; ctlarea = saferealloc(ctlarea,newsize); ctlsize = newsize; } toread[ng] += (ART_UNREAD)(newlast-lastart); for (i=lastart+1; i<=newlast; i++) ctl_clear(i); /* these articles are unread */ #ifdef CACHESUBJ if (subj_list != Null(char**)) { #ifndef lint subj_list = (char**)saferealloc((char*)subj_list, (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) ); #endif lint for (i=lastart+1; i<=newlast; i++) subj_list[OFFSET(i)] = Nullch; } #endif tmpfirst = lastart+1; lastart = newlast; #ifdef KILLFILES #ifdef VERBOSE IF(verbose) sprintf(buf, "%ld more article%s arrived--looking for more to kill...\n\n", (long)(lastart - firstart + 1), (lastart > firstart ? "s have" : " has" ) ); ELSE /* my, my, how clever we are */ #endif #ifdef TERSE strcpy(buf, "More news--killing...\n\n"); #endif kill_unwanted(tmpfirst,buf,TRUE); #endif art = tmpart; } } When newsbits.h 664 540 12 3213 3473757421 5230 /* $Header: bits.h,v 4.3 85/05/01 11:36:39 lwall Exp $ * * $Log: bits.h,v $ * Revision 4.3 85/05/01 11:36:39 lwall * Baseline for release with 4.3bsd. * */ EXT char *ctlarea INIT(Nullch); /* one bit for each article in current newsgroup */ /* with the following interpretation: */ /* 0 => unread */ /* 1 => read */ /* if subscripting is faster than shifting on your machine, define this */ #undef USESUBSCRIPT #ifdef USESUBSCRIPT EXT char powerof2[] INIT({1,2,4,8,16,32,64,128}); #define pow2(x) powerof2[x] #else #define pow2(x) (1 << (x)) #endif #ifdef lint EXT bool nonesuch INIT(FALSE); #define ctl_set(a) #define ctl_clear(a) #define ctl_read(a) nonesuch #define was_read(a) nonesuch #else #define ctl_set(a) (ctlarea[(OFFSET(a)) / BITSPERBYTE] |= pow2((OFFSET(a)) % BITSPERBYTE)) #define ctl_clear(a) (ctlarea[(OFFSET(a)) / BITSPERBYTE] &= ~pow2((OFFSET(a)) % BITSPERBYTE)) #define ctl_read(a) ((ctlarea[(OFFSET(a)) / BITSPERBYTE] & pow2((OFFSET(a)) % BITSPERBYTE)) != 0) #define was_read(a) ((a) lastart) { /* out of articles? */ #ifdef DEBUGGING if (debug) fputs("(not found)",stdout); #endif break; } if (!was_read(srchahead) && wanted(&srchcompex,srchahead,0)) { /* does the shoe fit? */ #ifdef DEBUGGING if (debug) printf("(%ld)",(long)srchahead); #endif artopen(srchahead); break; } if (input_pending()) break; } fflush(stdout); } } else #endif { if (art+1 <= lastart)/* how about a pre-fetch? */ artopen(art+1); /* look for the next article */ } } #endif /* see what else we can do while they are reading */ void collect_subjects() { #ifdef PENDING # ifdef CACHESUBJ ART_NUM oldart = openart; ART_POS oldartpos; if (!in_ng || !srchahead) return; if (oldart) /* remember where we were in art */ oldartpos = ftell(artfp); if (srchahead >= subj_to_get) subj_to_get = srchahead+1; while (!input_pending() && subj_to_get <= lastart) fetchsubj(subj_to_get++,FALSE,FALSE); if (oldart) { artopen(oldart); fseek(artfp,oldartpos,0); /* do not screw the pager */ } # endif #endif } ART_NUM artnum; { if (dmfp == Nullfp) { dmfp = fopen(dmname,"w"); if (dmfp == Nullfp) { printf(cantcreate,dmname) FLUSH; sig_catcher(0); } } oneless(artnum); /* set the correct bit */ dmcount++; fprintf(dmfp,"%ld\n",(long)artnum); } #endif /* mark article as read. If article is cross referenced to other */ /* newsgroups, mark them read there also. */ void mark_acheat.h 664 540 12 701 3473757473 5341 /* $Header: cheat.h,v 4.3 85/05/01 11:36:58 lwall Exp $ * * $Log: cheat.h,v $ * Revision 4.3 85/05/01 11:36:58 lwall * Baseline for release with 4.3bsd. * */ #ifdef ARTSEARCH EXT ART_NUM srchahead INIT(0); /* are we in subject scan mode? */ /* (if so, contains art # found or -1) */ #endif #ifdef PENDING # ifdef CACHESUBJ EXT ART_NUM subj_to_get; # endif #endif void cheat_init(); void look_ahead(); void collect_subjects(); fdef PENDING # ifdef ARTSEARCH COMPEX srchcompex; /* compilcommon.h 664 540 12 53100 3477433271 5575 /* $Header: common.h,v 4.3.1.3 85/05/23 17:19:32 lwall Exp $ * * $Log: common.h,v $ * Revision 4.3.1.3 85/05/23 17:19:32 lwall * Now allows 'r' and 'f' on null articles. * * Revision 4.3.1.2 85/05/13 09:30:39 lwall * Added CUSTOMLINES option. * * Revision 4.3.1.1 85/05/10 11:32:04 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:37:11 lwall * Baseline for release with 4.3bsd. * */ #include "config.h" /* generated by installation script */ #ifdef WHOAMI # include #endif #include #include #include #include #ifndef isalnum # define isalnum(c) (isalpha(c) || isdigit(c)) #endif #include #include #ifdef IOCTL #include #endif IOCTL #ifdef FCNTL # include #endif #ifdef TERMIO # include #else # include #endif #ifdef GETPWENT # include #endif #define BITSPERBYTE 8 #define LBUFLEN 512 /* line buffer length */ /* (don't worry, .newsrc lines can exceed this) */ #ifdef pdp11 # define CBUFLEN 256 /* command buffer length */ # define PUSHSIZE 128 #else # define CBUFLEN 512 /* command buffer length */ # define PUSHSIZE 256 #endif #ifdef pdp11 # define MAXFILENAME 128 #else # define MAXFILENAME 512 #endif #define LONGKEY 15 /* longest keyword: currently "posting-version" */ #define FINISHCMD 0177 /* some handy defs */ #define bool char #define TRUE (1) #define FALSE (0) #define Null(t) ((t)0) #define Nullch Null(char *) #define Nullfp Null(FILE *) #define Ctl(ch) (ch & 037) #define strNE(s1,s2) (strcmp(s1,s2)) #define strEQ(s1,s2) (!strcmp(s1,s2)) #define strnNE(s1,s2,l) (strncmp(s1,s2,l)) #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l)) /* Things we can figure out ourselves */ #ifdef SIGTSTP # define BERKELEY /* include job control signals? */ #endif #ifdef SIGPROF # define BSD42 /* do we have Berkeley 4.2? */ #endif #ifdef FIONREAD # define PENDING #else # ifdef O_NDELAY # define PENDING # endif #endif #ifdef EUNICE # define LINKART /* add 1 level of possible indirection */ # define UNLINK(victim) while (!unlink(victim)) #else # define UNLINK(victim) unlink(victim) #endif /* Valid substitutions for strings marked with % comment are: * %a Current article number * %A Full name of current article (%P/%c/%a) * (if LINKART defined, is the name of the real article) * %b Destination of a save command, a mailbox or command * %B The byte offset to the beginning of the article for saves * with or without the header * %c Current newsgroup, directory form * %C Current newsgroup, dot form * %d %P/%c * %D Old Distribution: line * %f Old From: line or Reply-To: line * %F Newsgroups to followup to from Newsgroups: and Followup-To: * %h Name of header file to pass to mail or news poster * %H Host name (yours) * %i Old Message-I.D.: line, with <> * %I Inclusion indicator * %l News administrator login name * %L Login name (yours) * %M Number of articles markd with M * %n Newsgroups from source article * %N Full name (yours) * %o Organization (yours) * %O Original working directory (where you ran rn from) * %p Your private news directory (-d switch) * %P Public news spool directory (SPOOLDIR) * %r Last reference (parent article id) * %R New references list * %s Subject, with all Re's and (nf)'s stripped off * %S Subject, with one Re stripped off * %t New To: line derived from From: and Reply-To (Internet always) * %T New To: line derived from Path: * %u Number of unread articles * %U Number of unread articles disregarding current article * %x News library directory, usually /usr/lib/news * %X Rn library directory, usually %x/rn * %z Size of current article in bytes. * %~ Home directory * %. Directory containing . files * %$ 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. * (Note: %() only works if CONDSUB defined.) * %digit Substitute the text matched by the nth bracket in the last * pattern that had brackets. %0 matches the last bracket * matched, in case you had alternatives. * * Put ^ in the middle to capitalize the first letter: %^C = Net.jokes * Put _ in the middle to capitalize last component: %_c = net/Jokes * * ~ interpretation in filename expansion happens after % expansion, so * you could put ~%{NEWSLOGNAME-news} and it will expand correctly. */ /* *** System Dependent Stuff *** */ /* NOTE: many of these are defined in the config.h file */ /* name of organization */ #ifndef ORGNAME # define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota" #endif #ifndef MBOXCHAR # define MBOXCHAR 'F' /* how to recognize a mailbox by 1st char */ #endif #ifndef ROOTID # define ROOTID 0 /* uid of superuser */ #endif #ifdef NORMSIG # define sigset signal # define sigignore(sig) signal(sig,SIG_IGN) #endif #ifndef LOGDIRFIELD # define LOGDIRFIELD 6 /* Which field (origin 1) is the */ /* login directory in /etc/passwd? */ /* (If it is not kept in passwd, */ /* but getpwnam() returns it, */ /* define the symbol GETPWENT) */ #endif #ifndef GCOSFIELD # define GCOSFIELD 5 #endif #ifndef NEGCHAR # define NEGCHAR '!' #endif /* Space conservation section */ /* To save D space, cut down size of MAXRCLINE, NGMAX, VARYSIZE. */ #define MAXRCLINE 500 /* number of lines allowed in .newsrc */ /* several parallel arrays affected. */ /* (You can have more lines in the active file, */ /* just not in the .newsrc) */ #define HASHSIZ 547 /* should be prime, and at least MAXRCLINE + 10% */ #define NGMAX 100 /* number of newsgroups allowed on command line */ /* undefine ONLY symbol to disable "only" feature */ #define VARYSIZE 256 /* this makes a block 1024 bytes long in DECville */ /* (used by virtual array routines) */ /* Undefine any of the following features to save both I and D space */ /* In general, earlier ones are easier to get along without */ /* Pdp11's without split I and D may have to undefine them all */ #define DEBUGGING /* include debugging code */ #define CUSTOMLINES /* include code for HIDELINE and PAGESTOP */ #define PUSHBACK /* macros and keymaps using pushback buffer */ #define SPEEDOVERMEM /* use more memory to run faster */ #define WORDERASE /* enable ^W to erase a word */ #define MAILCALL /* check periodically for mail */ #define CLEAREOL /* use clear to end-of-line instead of clear screen */ #define NOFIREWORKS /* keep whole screen from flashing on certain */ /* terminals such as older Televideos */ #define VERIFY /* echo the command they just typed */ #define HASHNG /* hash newsgroup lines for fast lookup-- */ /* linear search used if not defined */ #define CONDSUB /* allow %(cond?text:text) */ #define 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 GETLOGIN /* use getlogin() routine as backup to environment */ /* variables USER or LOGNAME */ #define ORGFILE /* if organization begins with /, look up in file */ #define TILDENAME /* allow ~logname expansion */ #define SETENV /* allow command line environment variable setting */ #define GETWD /* use our getwd() instead of piped in pwd */ #ifndef BSD42 /* 4.2 sites should just use groups for this */ #define SETUIDGID /* substitute eaccess() for access() so that rn */ /* can run setuid or setgid */ /* if not setuid or setgid, you don't need it */ #endif #define MAKEDIR /* use our makedir() instead of shell script */ #define MEMHELP /* keep help messages in memory */ #define VERBOSE /* compile in more informative messages */ #define TERSE /* compile in shorter messages */ /* (Note: both VERBOSE and TERSE can be defined; -t * sets terse mode. One or the other MUST be defined. */ #ifndef pdp11 # define CACHESUBJ /* cache subject lines in memory */ /* without this ^N still works but runs really slow */ /* but you save lots and lots of D space */ # define CACHEFIRST /* keep absolute first article numbers in memory */ /* cost: about 2k */ #endif #define ROTATION /* enable x, X and ^X commands to work */ #define DELBOGUS /* ask if bogus newsgroups should be deleted */ #define RELOCATE /* allow newsgroup rearranging */ #define ESCSUBS /* escape substitutions in multi-character commands */ #define DELAYMARK /* allow articles to be temporarily marked as read */ /* until exit from current newsgroup or Y command */ #define MCHASE /* unmark xrefed articles on m or M */ #define MUNGHEADER /* allow alternate header formatting via */ /* environment variable ALTHEADER (not impl) */ #define ASYNC_PARSE /* allow parsing headers asyncronously to reading */ /* used by MCHASE and MUNGHEADER */ #define FINDNEWNG /* check for new newsgroups on startup */ #define FASTNEW /* do optimizations on FINDNEWNG for faster startup */ /* (this optimization can make occasional mistakes */ /* if a group is removed and another group of the */ /* same length is added, and if no softpointers are */ /* affected by said change.) */ #define INNERSEARCH /* search command 'g' with article */ #define CATCHUP /* catchup command at newsgroup level */ #define NGSEARCH /* newsgroup pattern matching */ #define ONLY /* newsgroup restrictions by pattern */ #define KILLFILES /* automatic article killer files */ #define ARTSEARCH /* pattern searches among articles */ /* /, ?, ^N, ^P, k, K */ /* some dependencies among options */ #ifndef ARTSEARCH # undef KILLFILES # undef INNERSEARCH # undef CACHESUBJ #endif #ifndef DELAYMARK # ifndef MCHASE # ifndef MUNGHEADER # undef ASYNC_PARSE # endif # endif #endif #ifndef SETUIDGID # define eaccess access #endif #ifdef ONLY /* idiot lint doesn't grok #if */ # define NGSORONLY #else # ifdef NGSEARCH # define NGSORONLY # endif #endif #ifdef VERBOSE # ifdef TERSE # define IF(c) if (c) # define ELSE else # else !TERSE # define IF(c) # define ELSE # endif #else !VERBOSE # ifndef TERSE # define TERSE # endif # define IF(c) "IF" outside of VERBOSE??? # define ELSE "ELSE" outside of VERBOSE??? #endif #ifdef DEBUGGING # define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}} #else # define assert(ex) ; #endif #ifdef SPEEDOVERMEM # define OFFSET(x) (x) #else # define OFFSET(x) ((x)-absfirst) #endif /* If you're strapped for space use the help messages in shell scripts */ /* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */ #ifdef MEMHELP /* undef MEMHELP above to get them all as sh scripts */ # undef NGHELP # undef ARTHELP # undef PAGERHELP # undef SUBSHELP #else # ifndef NGHELP /* % and ~ */ # define NGHELP "%X/ng.help" # endif # ifndef ARTHELP /* % and ~ */ # define ARTHELP "%X/art.help" # endif # ifndef PAGERHELP /* % and ~ */ # define PAGERHELP "%X/pager.help" # endif # ifndef SUBSHELP /* % and ~ */ # define SUBSHELP "%X/subs.help" # endif #endif #ifdef CLEAREOL # define TCSIZE 512 /* capacity for termcap strings */ #else # ifdef pdp11 # define TCSIZE 256 /* capacity for termcap strings */ # else # define TCSIZE 512 /* capacity for termcap srings */ # endif #endif /* Additional ideas: * Make the do_newsgroup() routine a separate process. * Keep .newsrc on disk instead of in memory. * Overlays, if you have them. * Get a bigger machine. */ /* End of Space Conservation Section */ /* More System Dependencies */ /* news library */ #ifndef LIB /* ~ and %l only ("~%l" is permissable) */ # define LIB "/usr/lib/news" #endif /* path to private executables */ #ifndef RNLIB /* ~, %x and %l only */ # define RNLIB "%x/rn" #endif /* system-wide RNINIT switches */ #ifndef GLOBINIT # define GLOBINIT "%X/INIT" #endif /* where to find news files */ #ifndef SPOOL /* % and ~ */ # define SPOOL "/usr/spool/news" #endif /* file containing list of active newsgroups and max article numbers */ #ifndef ACTIVE /* % and ~ */ # define ACTIVE "%x/active" #endif /* location of history file */ #ifndef ARTFILE /* % and ~ */ # define ARTFILE "%x/history" #endif /* command to setup a new .newsrc */ #ifndef NEWSETUP /* % and ~ */ # define NEWSETUP "newsetup" #endif /* command to display a list of un-subscribed-to newsgroups */ #ifndef NEWSGROUPS /* % and ~ */ # define NEWSGROUPS "newsgroups" #endif /* preferred shell for use in doshell routine */ /* ksh or sh would be okay here */ #ifndef PREFSHELL # define PREFSHELL "/bin/csh" #endif /* path to fastest starting shell */ #ifndef SH # define SH "/bin/sh" #endif /* path to default editor */ #ifndef DEFEDITOR # define DEFEDITOR "/usr/ucb/vi" #endif /* location of macro file */ #ifndef RNMACRO # ifdef PUSHBACK # define RNMACRO "%./.rnmac" # endif #endif /* location of full name */ #ifndef FULLNAMEFILE # ifndef PASSNAMES # define FULLNAMEFILE "%./.fullname" # endif #endif /* virtual array file name template */ #ifndef VARYNAME /* % and ~ */ # define VARYNAME "/tmp/rnvary.%$" #endif /* file to pass header to followup article poster */ #ifndef HEADNAME /* % and ~ */ # define HEADNAME "%./.rnhead" /* or alternately #define HEADNAME "/tmp/rnhead.%$" */ #endif #ifndef MAKEDIR /* shell script to make n-deep subdirectories */ # ifndef DIRMAKER /* % and ~ */ # define DIRMAKER "%X/makedir" # endif #endif /* location of newsrc file */ #ifndef RCNAME /* % and ~ */ # define RCNAME "%./.newsrc" #endif /* temporary newsrc file in case we crash while writing out */ #ifndef RCTNAME /* % and ~ */ # define RCTNAME "%./.newnewsrc" #endif /* newsrc file at the beginning of this session */ #ifndef RCBNAME /* % and ~ */ # define RCBNAME "%./.oldnewsrc" #endif /* if existent, contains process number of current or crashed rn */ #ifndef LOCKNAME /* % and ~ */ # define LOCKNAME "%./.rnlock" #endif /* information from last invocation of rn */ #ifndef LASTNAME /* % and ~ */ # define LASTNAME "%./.rnlast" #endif /* file with soft pointers into the active file */ #ifndef SOFTNAME /* % and ~ */ # define SOFTNAME "%./.rnsoft" #endif /* list of article numbers to mark as unread later (see M and Y cmmands) */ #ifndef RNDELNAME /* % and ~ */ # define RNDELNAME "%./.rndelay" #endif /* a motd-like file for rn */ #ifndef NEWSNEWSNAME /* % and ~ */ # define NEWSNEWSNAME "%X/newsnews" #endif /* command to send a reply */ #ifndef MAILPOSTER /* % and ~ */ # define MAILPOSTER "Rnmail -h %h" #endif #ifdef INTERNET # ifndef MAILHEADER /* % */ # ifdef CONDSUB # define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n" # endif # endif #else # ifndef MAILHEADER /* % */ # ifdef CONDSUB # define MAILHEADER "To: %T\nSubject: %(%i=^$?:Re: %S\nNewsgroups: %n\nIn-Reply-To: %i)\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n" # endif # endif #endif #ifndef YOUSAID /* % */ # define YOUSAID "In article %i you write:" #endif /* command to submit a followup article */ #ifndef NEWSPOSTER /* % and ~ */ # define NEWSPOSTER "Pnews -h %h" #endif #ifndef NEWSHEADER /* % */ # ifdef CONDSUB # define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nReply-To: %L@%H.UUCP (%N)\nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n" # else # define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nReply-To: %L@%H.UUCP (%N)\nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n" # endif #endif #ifndef ATTRIBUTION /* % */ # define ATTRIBUTION "In article %i %f writes:" #endif #ifndef PIPESAVER /* % */ # ifdef CONDSUB # define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b" # else # define PIPESAVER "tail +%Bc %A | %b" # endif #endif #ifndef NORMSAVER /* % and ~ */ # define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\"" #endif #ifndef MBOXSAVER /* % and ~ */ # ifdef MININACT /* 2.10.2 site? */ # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %`date`\"" # else # ifdef CONDSUB # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\"" /* header munging with a vengeance */ # else # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %[posted]\"" # endif # endif #endif #ifdef MKDIRS # ifndef SAVEDIR /* % and ~ */ # define SAVEDIR "%p/%c" # endif # ifndef SAVENAME /* % */ # define SAVENAME "%a" # endif #else # ifndef SAVEDIR /* % and ~ */ # define SAVEDIR "%p" # endif # ifndef SAVENAME /* % */ # define SAVENAME "%^C" # endif #endif #ifndef KILLGLOBAL /* % and ~ */ # define KILLGLOBAL "%p/KILL" #endif #ifndef KILLLOCAL /* % and ~ */ # define KILLLOCAL "%p/%c/KILL" #endif /* how to cancel an article */ #ifndef CANCEL # ifdef MININACT /* 2.10.2 ? */ # define CANCEL "%x/inews -h < %h" # else # define CANCEL "inews -h < %h" # endif #endif /* how to cancel an article, continued */ #ifndef CANCELHEADER # define CANCELHEADER "Newsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nReply-To: %L@%H.UUCP (%N)\nDistribution: %D\nOrganization: %o\n" #endif /* where to find the mail file */ #ifndef MAILFILE # define MAILFILE "/usr/spool/mail/%L" #endif /* some important types */ typedef int NG_NUM; /* newsgroup number */ typedef long ART_NUM; /* article number */ #ifdef pdp11 typedef short ART_UNREAD; /* ordinarily this should be long */ /* like ART_NUM, but assuming that */ /* we stay less than 32767 articles */ /* behind saves a lot of space. */ /* NOTE: do not make unsigned. */ #else typedef long ART_UNREAD; #endif typedef long ART_POS; /* char position in article file */ typedef int ART_LINE; /* line position in article file */ typedef short ACT_POS; /* char position in active file */ typedef unsigned int MEM_SIZE; /* for passing to malloc */ /* *** end of the machine dependent stuff *** */ /* GLOBAL THINGS */ /* file statistics area */ EXT struct stat filestat; /* various things of type char */ char *index(); char *rindex(); char *getenv(); char *strcat(); char *strcpy(); char *sprintf(); EXT char buf[LBUFLEN+1]; /* general purpose line buffer */ EXT char cmd_buf[CBUFLEN]; /* buffer for formatting system commands */ EXT char *indstr INIT(">"); /* indent for old article embedded in followup */ EXT char *cwd INIT(Nullch); /* current working directory */ EXT char *dfltcmd INIT(Nullch); /* 1st char is default command */ /* switches */ #ifdef DEBUGGING EXT int debug INIT(0); /* -D */ # define DEB_INNERSRCH 32 # define DEB_FILEXP 64 # define DEB_HASH 128 # define DEB_XREF_MARKER 256 # define DEB_CTLAREA_BITMAP 512 # define DEB_SOFT_POINTERS 1024 # define DEB_NEWSRC_LINE 2048 # define DEB_SEARCH_AHEAD 4096 # define DEB_CHECKPOINTING 8192 # define DEB_FEED_XREF 16384 #endif #ifdef ARTSEARCH EXT int scanon INIT(0); /* -S */ #endif EXT bool mbox_always INIT(FALSE); /* -M */ EXT bool norm_always INIT(FALSE); /* -N */ EXT bool checkflag INIT(FALSE); /* -c */ EXT bool suppress_cn INIT(FALSE); /* -s */ EXT int countdown INIT(5); /* how many lines to list before invoking -s */ EXT bool muck_up_clear INIT(FALSE); /* -loco */ EXT bool erase_screen INIT(FALSE); /* -e */ #ifdef CLEAREOL EXT bool can_home_clear INIT(FALSE); /* fancy -e -- PWP */ #endif CLEAREOL EXT bool findlast INIT(FALSE); /* -r */ EXT bool typeahead INIT(FALSE); /* -T */ #ifdef VERBOSE # ifdef TERSE EXT bool verbose INIT(TRUE); /* +t */ # endif #endif #ifdef VERIFY EXT bool verify INIT(FALSE); /* -v */ #endif #define NOMARKING 0 #define STANDOUT 1 #define UNDERLINE 2 EXT int marking INIT(NOMARKING); /* -m */ EXT ART_LINE initlines INIT(0); /* -i */ /* miscellania */ long atol(), fseek(), ftell(); EXT bool in_ng INIT(FALSE); /* current state of rn */ EXT char mode INIT('i'); /* current state of rn */ EXT FILE *tmpfp INIT(Nullfp); /* scratch fp used for .rnlock, .rnlast, etc. */ EXT NG_NUM nextrcline INIT(0); /* 1st unused slot in rcline array */ /* startup to avoid checking twice in a row */ extern errno; /* Factored strings */ EXT char nullstr[] INIT(""); EXT char sh[] INIT(SH); EXT char defeditor[] INIT(DEFEDITOR); EXT char hforhelp[] INIT("Type h for help.\n"); #ifdef STRICTCR EXT char badcr[] INIT("\nUnnecessary CR ignored.\n"); #endif EXT char readerr[] INIT("rn read error"); EXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n"); EXT char cantopen[] INIT("Can't open %s\n"); EXT char cantcreate[] INIT("Can't create %s\n"); #ifdef VERBOSE EXT char nocd[] INIT("Can't chdir to directory %s\n"); #else EXT char nocd[] INIT("Can't find %s\n"); #endif #ifdef NOLINEBUF #define FLUSH ,fflush(stdout) #else #define FLUSH #endif #ifdef lint #undef FLUSH #define FLUSH #undef putchar #define putchar(c) #endif /* Additional ideas: * Make the do_newsgroup() routine a separate process. * Keep .newsrc on disk instead of in memory. * Overlays, if you have them. * Get a bigger machine. */ /* End of Space Conservation Section */ /* More System Dependencies */ /* news library */ #ifndef LIB /* ~ and %l only ("~%l" is permissable) */ # define LIB "/usr/lib/news" #endif /* path to private executables */ #ifndef RNLIB /* ~, %x and %l only */ # dfinal.c 664 540 12 7534 3473757341 5366 /* $Header: final.c,v 4.3 85/05/01 11:38:08 lwall Exp $ * * $Log: final.c,v $ * Revision 4.3 85/05/01 11:38:08 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "term.h" #include "ng.h" #include "init.h" #include "bits.h" #include "last.h" #include "rcstuff.h" #include "INTERN.h" #include "final.h" void final_init() { #ifdef SIGTSTP sigset(SIGTSTP, stop_catcher); /* job control signals */ sigset(SIGCONT, cont_catcher); /* job control signals */ #endif sigset(SIGINT, int_catcher); /* always catch interrupts */ sigset(SIGHUP, sig_catcher); /* and hangups */ #ifndef lint sigignore(SIGEMT); #endif lint sigset(SIGILL, sig_catcher); sigset(SIGTRAP, sig_catcher); sigset(SIGFPE, sig_catcher); sigset(SIGBUS, sig_catcher); sigset(SIGSEGV, sig_catcher); sigset(SIGSYS, sig_catcher); sigset(SIGTERM, sig_catcher); #ifdef SIGXCPU sigset(SIGXCPU, sig_catcher); #endif #ifdef SIGXFSZ sigset(SIGXFSZ, sig_catcher); #endif } void /* very much void */ finalize(status) int status; { if (bizarre) resetty(); UNLINK(lockname); if (status < 0) { chdir("/usr/tmp"); sigset(SIGILL,SIG_DFL); abort(); } exit(status); } /* come here on interrupt */ int int_catcher() { sigset(SIGINT,int_catcher); #ifdef DEBUGGING if (debug) write(2,"int_catcher\n",12); #endif if (!waiting) { if (int_count) { /* was there already an interrupt? */ write(2,"\nBye-bye.\n",10); sig_catcher(0); /* emulate the other signals */ } int_count++; } } /* come here on signal other than interrupt, stop, or cont */ int sig_catcher(signo) { #ifdef VERBOSE static char *signame[] = { "", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "???" #ifdef SIGTSTP ,"STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "TINT", "XCPU", "XFSZ" #ifdef SIGPROF ,"VTALARM", "PROF" #endif #endif }; #endif #ifdef SIGTTOU #ifndef lint sigignore(SIGTTOU); #endif lint #endif #ifdef DEBUGGING if (debug) { printf("\nSIG%s--.newsrc not restored in debug\n",signame[signo]); finalize(-1); } #endif if (panic) abort(); (void) sigset(SIGILL,SIG_DFL); panic = TRUE; /* disable terminal I/O */ if (doing_ng) { /* need we reconstitute rc line? */ yankback(); restore_ng(); /* then do so (hope this works) */ } doing_ng = FALSE; if (rc_changed) /* need we write .newsrc out? */ write_rc(); /* then do so */ rc_changed = FALSE; if (signo != SIGHUP) #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 switch (signo) { case SIGBUS: case SIGILL: case SIGSEGV: finalize(-signo); } finalize(1); /* and blow up */ } #ifdef SIGTSTP /* come here on stop signal */ int stop_catcher() { if (!waiting) { checkpoint_rc(); /* good chance of crash while stopped */ resetty(); /* this is the point of all this */ #ifdef DEBUGGING if (debug) write(2,"stop_catcher\n",13); #endif sigset(SIGTSTP,SIG_DFL); /* enable stop */ #ifdef BSD42 sigsetmask(sigblock(0) & ~(1 << (SIGTSTP-1))); #endif kill(0,SIGTSTP); /* and do the stop */ } sigset(SIGTSTP,stop_catcher); /* unenable the stop */ } /* come here on cont signal */ int cont_catcher() { sigset(SIGCONT,cont_catcher); savetty(); #ifdef MAILCALL; mailcount = 0; /* force recheck */ #endif if (!panic) { if (!waiting) { #ifdef DEBUGGING if (debug) write(2,"cont_catcher\n",13); #endif noecho(); /* set no echo */ crmode(); /* set cbreak mode */ forceme("\f"); /* cause a refresh */ /* (defined only if TIOCSTI defined) */ } } } #endif ME /* % and ~ */ # define RCTNAME "%./.newnewsrc" #endif /* newsrc file at the beginning of this session */ #ifndef RCBNAME /* % and ~ */ # define RCBNAME "%final.h 664 540 12 1300 3473757453 5360 /* $Header: final.h,v 4.3 85/05/01 11:38:17 lwall Exp $ * * $Log: final.h,v $ * Revision 4.3 85/05/01 11:38:17 lwall * Baseline for release with 4.3bsd. * */ /* cleanup status for fast exits */ EXT bool panic INIT(FALSE); /* we got hung up or something-- */ /* so leave tty alone */ EXT bool rc_changed INIT(FALSE); /* need we rewrite .newsrc? */ EXT bool doing_ng INIT(FALSE); /* do we need to reconstitute */ /* current rc line? */ EXT char int_count INIT(0); /* how many interrupts we've had */ /* signal catching routines */ int int_catcher(); int sig_catcher(); #ifdef SIGTSTP int stop_catcher(); int cont_catcher(); #endif void final_init(); void finalize(); sigset(SIGILL, sig_catcher); sigset(SIGTRAP, sig_catcher); sigset(SIGFPE, sig_catcher); sigset(SIGBUS, sig_catcher); sigset(SIGSEGV, sig_catcher); sigset(SIGSYS, sig_catcher); sigset(SIGTERM, sig_catcher); #ifdef SIGXCPU sigset(SIGXCPU, sig_catcher); #endif #ifdef SIGXFSZ sigset(SIGXhead.c 664 540 12 14535 3473762230 5206 /* $Header: head.c,v 4.3.1.2 85/05/10 13:47:25 lwall Exp $ * * $Log: head.c,v $ * Revision 4.3.1.2 85/05/10 13:47:25 lwall * Added debugging stuff. * * Revision 4.3.1.1 85/05/10 11:32:30 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:38:21 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "artio.h" #include "bits.h" #include "util.h" #include "INTERN.h" #include "head.h" bool first_one; /* is this the 1st occurance of this header line? */ static char htypeix[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; void head_init() { register int i; for (i=HEAD_FIRST+1; i LONGKEY+2) { /* is it the end of the header? */ htype[PAST_HEADER].ht_minpos = (*art_buf == '\n') ? ftell(artfp) : artpos; /* remember where body starts */ in_header = PAST_HEADER; } else { /* it is a new header line */ in_header = set_line_type(art_buf,s); first_one = (htype[in_header].ht_minpos < 0); if (first_one) htype[in_header].ht_minpos = artpos; #ifdef DEBUGGING if (debug & 4) dumpheader(art_buf); #endif if (htype[in_header].ht_flags & HT_HIDE) return newhide; } } return FALSE; /* don't hide this line */ } #ifdef ASYNC_PARSE int parse_maybe(artnum) ART_NUM artnum; { char tmpbuf[LBUFLEN]; if (parsed_art == artnum) return 0; /* no maybe about it now */ if (artopen(artnum) == Nullfp) { return -1; } start_header(artnum); while (in_header) { artpos = ftell(artfp); if (fgets(tmpbuf,LBUFLEN,artfp) == Nullch) break; parseline(tmpbuf,FALSE,FALSE); } in_header = PAST_HEADER; return 0; } #endif /* get the subject line for an article */ char * fetchsubj(artnum,current_subject,copy) ART_NUM artnum; /* article to get subject from */ bool current_subject; /* is it in a parsed header? */ bool copy; /* do you want it savestr()ed? */ { char *s = Nullch, *t; #ifdef CACHESUBJ if (!subj_list) { register ART_NUM i; #ifndef lint subj_list = (char**)safemalloc((MEM_SIZE)((OFFSET(lastart)+2)*sizeof(char *))); #endif lint for (i=0; i<=OFFSET(lastart); i++) subj_list[i] = Nullch; } if (!artnum || artnum > lastart) s = nullstr; else s = subj_list[OFFSET(artnum)]; #endif if (s == Nullch) { if (current_subject) { s = fetchlines(artnum,SUBJ_LINE); #ifdef CACHESUBJ subj_list[OFFSET(artnum)] = s; #endif } else { s = safemalloc((MEM_SIZE)256); *s = '\0'; if (artopen(artnum) != Nullfp) { do { if (fgets(s,256,artfp) == Nullch) strcpy(s, "Title: \n"); } while (strnNE(s,"Title:",6) && strnNE(s,"Subject:",8)); s[strlen(s)-1] = '\0'; t = index(s,':')+1; while (*t == ' ') t++; strcpy(s, t); } s = saferealloc(s, (MEM_SIZE)strlen(s)+1); #ifdef CACHESUBJ subj_list[OFFSET(artnum)] = s; #endif } } #ifdef CACHESUBJ if (copy) { t = savestr(s); return t; } else return s; #else if (copy) return s; else { safecpy(cmd_buf,s,CBUFLEN); /* hope this is okay--we're */ free(s); return cmd_buf; /* really scraping for space here */ } #endif } /* get header lines from an article */ char * fetchlines(artnum,which_line) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ { char *newbuf, *t, tmp_buf[LBUFLEN]; register ART_POS curpos; int size; register ART_POS firstpos; register ART_POS lastpos; #ifdef ASYNC_PARSE if (parse_maybe(artnum)) artnum = 0; #endif firstpos = htype[which_line].ht_minpos; lastpos = htype[which_line].ht_maxpos; if (!artnum || firstpos < 0 || artopen(artnum) == Nullfp) { newbuf = safemalloc((unsigned int)1); *newbuf = '\0'; return newbuf; } #ifndef lint size = lastpos - firstpos + 1; #else size = Null(int); #endif lint #ifdef DEBUGGING if (debug && (size < 1 || size > 1000)) { printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos); gets(tmp_buf); } #endif newbuf = safemalloc((unsigned int)size); *newbuf = '\0'; fseek(artfp,firstpos,0); for (curpos = firstpos; curpos < lastpos; curpos = ftell(artfp)) { if (fgets(tmp_buf,LBUFLEN,artfp) == Nullch) break; if (*tmp_buf == ' ' || *tmp_buf == '\t') t = tmp_buf; else t = index(tmp_buf,':')+1; if (t == Nullch) break; else { while (*t == ' ' || *t == '\t') t++; safecat(newbuf,t,size); } } return newbuf; } DER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # dehead.h 664 540 12 6741 3473757343 5204 /* $Header: head.h,v 4.3 85/05/01 11:38:31 lwall Exp $ * * $Log: head.h,v $ * Revision 4.3 85/05/01 11:38:31 lwall * Baseline for release with 4.3bsd. * */ #define HEAD_FIRST 1 /* types of header lines (if only C really believed in enums) * (These must stay in alphabetic order at least in the first letter. * Within each letter it helps to arrange in increasing likelihood.) */ #define PAST_HEADER 0 /* body */ #define SOME_LINE 1 /* unrecognized */ #define ARTID_LINE 2 /* article-i.d. */ #define APPR_LINE 3 /* approved */ #define DIST_LINE 4 /* distribution */ #define DATE_LINE 5 /* date */ #define RECEIVED_LINE 6 /* date-received */ #define EXPIR_LINE 7 /* expires */ #define FOLLOW_LINE 8 /* followup-to */ #define FROM_LINE 9 /* from */ #define KEYW_LINE 10 /* keywords */ #define LINES_LINE 11 /* lines */ #define MESSID_LINE 12 /* message-id */ #define NFFR_LINE 13 /* nf-from */ #define NFID_LINE 14 /* nf-id */ #define NGS_LINE 15 /* newsgroups */ #define ORG_LINE 16 /* organization */ #define PATH_LINE 17 /* path */ #define POSTED_LINE 18 /* posted */ #define PVER_LINE 19 /* posting-version */ #define REPLY_LINE 20 /* reply-to */ #define REFS_LINE 21 /* references */ #define RVER_LINE 22 /* relay-version */ #define SENDER_LINE 23 /* sender */ #define SUMRY_LINE 24 /* summary */ #define SUBJ_LINE 25 /* subject */ #define XREF_LINE 26 /* xref */ #define HEAD_LAST 27 /* one more than the last one above */ struct headtype { char *ht_name; /* header line identifier */ #ifdef pdp11 short ht_minpos; short ht_maxpos; #else ART_POS ht_minpos; /* pointer to beginning of line in article */ ART_POS ht_maxpos; /* pointer to end of line in article */ #endif char ht_length; /* could make these into nybbles but */ char ht_flags; /* it wouldn't save space normally */ }; /* due to alignment considerations */ #define HT_HIDE 1 /* -h on this line */ #define HT_MAGIC 2 /* do any special processing on this line */ /* This array must stay in the same order as the list above */ #ifndef DOINIT EXT struct headtype htype[HEAD_LAST]; #else struct headtype htype[HEAD_LAST] = { /* name minpos maxpos length flag */ {"BODY", 0, 0, 4, 0 }, {"unrecognized", 0, 0, 12, 0 }, {"article-i.d.", 0, 0, 12, HT_HIDE }, {"approved", 0, 0, 8, HT_HIDE }, {"distribution", 0, 0, 12, 0 }, {"date", 0, 0, 4, 0 }, {"date-received", 0, 0, 13, 0 }, {"expires", 0, 0, 7, HT_HIDE|HT_MAGIC}, {"followup-to", 0, 0, 11, 0 }, {"from", 0, 0, 4, 0 }, {"keywords", 0, 0, 8, 0 }, {"lines", 0, 0, 5, 0 }, {"message-id", 0, 0, 10, 0 }, {"nf-from", 0, 0, 7, HT_HIDE }, {"nf-id", 0, 0, 5, HT_HIDE }, {"newsgroups", 0, 0, 10, HT_MAGIC|HT_HIDE}, {"organization", 0, 0, 12, 0 }, {"path", 0, 0, 4, HT_HIDE }, {"posted", 0, 0, 6, HT_HIDE }, {"posting-version", 0, 0, 15, HT_HIDE }, {"reply-to", 0, 0, 8, 0 }, {"references", 0, 0, 10, 0 }, {"relay-version", 0, 0, 13, HT_HIDE }, {"sender", 0, 0, 6, 0 }, {"summary", 0, 0, 7, 0 }, {"subject", 0, 0, 7, HT_MAGIC }, {"xref", 0, 0, 4, HT_HIDE } }; #endif #ifdef ASYNC_PARSE EXT ART_NUM parsed_art INIT(0); #endif EXT char in_header INIT(0); /* are we decoding the header? */ #ifdef CACHESUBJ EXT char **subj_list INIT(Null(char **)); #endif void head_init(); int set_line_type(); void start_header(); bool parseline(); #ifdef ASYNC_PARSE int parse_maybe(); #endif char *fetchsubj(); char *fetchlines(); return cmd_buf; /* really scrheader.c.1.pat 664 540 12 3426 3473757410 6440 NOTE: the preceding context may be different under 2.10.2, but you should be able to figure it out anyway. *** header.old.c Fri Apr 27 11:30:49 1984 --- header.c Mon Feb 27 10:44:03 1984 *************** *** 107,112 #define NUMLINES 19 #define KEYWORDS 20 #define APPROVED 21 #define OTHER 99 char *malloc(); --- 107,115 ----- #define NUMLINES 19 #define KEYWORDS 20 #define APPROVED 21 + #ifdef DOXREFS + #define XREF 98 + #endif DOXREFS #define OTHER 99 char *malloc(); *************** *** 201,206 seenrelay = 1; } break; case OTHER: if (unreccnt < NUNREC) { hp->unrec[unreccnt] = malloc(strlen(bfr) + 1); --- 204,214 ----- seenrelay = 1; } break; + #ifdef DOXREFS + case XREF: + getfield(hp->xref); + break; + #endif DOXREFS case OTHER: if (unreccnt < NUNREC) { hp->unrec[unreccnt] = malloc(strlen(bfr) + 1); *************** *** 398,403 return KEYWORDS; if (its("Approved: ")) return APPROVED; return OTHER; } --- 406,415 ----- return KEYWORDS; if (its("Approved: ")) return APPROVED; + #ifdef DOXREFS + if (its("Xref: ")) + return XREF; + #endif DOXREFS return OTHER; } *************** *** 507,512 fprintf(fp, "Keywords: %s\n", hp->keywords); if (*hp->approved) fprintf(fp, "Approved: %s\n", hp->approved); for (iu = 0; iu < NUNREC; iu++) { if (hp->unrec[iu]) fprintf(fp, "%s", &hp->unrec[iu][0]); --- 519,528 ----- fprintf(fp, "Keywords: %s\n", hp->keywords); if (*hp->approved) fprintf(fp, "Approved: %s\n", hp->approved); + #ifdef DOXREFS + if (wr == 1 && *hp->xref) + fprintf(fp, "Xref: %s\n", hp->xref); + #endif DOXREFS for (iu = 0; iu < NUNREC; iu++) { if (hp->unrec[iu]) fprintf(fp, "%s", &hp->unrec[iu][0]); * summary */ #define SUBJ_LINE 25 /* subject */ #define XREF_LINE 26 /* xref */ #define HEAD_LAST 27 /* one more than the last one above */ struct headtype { char *ht_name; /* header line identifier */ #ifdef pdp11 short htheader.c.2.pat 664 540 12 3327 3473757416 6447 *** header.old.c Tue Apr 30 14:33:53 1985 --- header.c Tue Apr 30 14:33:56 1985 *************** *** 119,124 #define APPROVED 21 #define NFID 22 #define NFFROM 23 #define OTHER 99 char *malloc(); --- 119,127 ----- #define APPROVED 21 #define NFID 22 #define NFFROM 23 + #ifdef DOXREFS + #define XREF 98 + #endif DOXREFS #define OTHER 99 char *malloc(); *************** *** 214,219 getfield(hp->relayversion); } break; case OTHER: if (unreccnt < NUNREC) { if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) { --- 217,227 ----- getfield(hp->relayversion); } break; + #ifdef DOXREFS + case XREF: + getfield(hp->xref); + break; + #endif DOXREFS case OTHER: if (unreccnt < NUNREC) { if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) { *************** *** 422,427 return NFID; if (its("Nf-From: ")) return NFFROM; return OTHER; } --- 430,439 ----- return NFID; if (its("Nf-From: ")) return NFFROM; + #ifdef DOXREFS + if (its("Xref: ")) + return XREF; + #endif DOXREFS return OTHER; } *************** *** 536,541 fprintf(fp, "Nf-ID: %s\n", hp->nf_id); if (*hp->nf_from) fprintf(fp, "Nf-From: %s\n", hp->nf_from); for (iu = 0; iu < NUNREC; iu++) { if (hp->unrec[iu]) fprintf(fp, "%s", &hp->unrec[iu][0]); --- 548,557 ----- fprintf(fp, "Nf-ID: %s\n", hp->nf_id); if (*hp->nf_from) fprintf(fp, "Nf-From: %s\n", hp->nf_from); + #ifdef DOXREFS + if (wr == 1 && *hp->xref) + fprintf(fp, "Xref: %s\n", hp->xref); + #endif DOXREFS for (iu = 0; iu < NUNREC; iu++) { if (hp->unrec[iu]) fprintf(fp, "%s", &hp->unrec[iu][0]); se into nybbles but */ char ht_flags; /* it wouldn't save space normally */ }; /* due to alignment considerations */ #define HT_HIDE 1 /* -h on this line */ #define HT_MAGIC 2 /* do any special processing on this line */ /* This array must stay in the same order as the list above */ #iheader.h.1.pat 664 540 12 1225 3473757460 6445 NOTE: the preceding context may be different under 2.10.2, but you should be able to figure it out anyway. *** header.old.h Fri Apr 27 11:30:49 1984 --- header.h Thu May 10 15:19:55 1984 *************** *** 34,38 int intnumlines; /* Integer version */ char keywords[BUFLEN]; /* Keywords: */ char approved[BUFLEN]; /* Approved: */ char *unrec[NUNREC]; /* unrecognized lines */ }; --- 34,41 ----- int intnumlines; /* Integer version */ char keywords[BUFLEN]; /* Keywords: */ char approved[BUFLEN]; /* Approved: */ + #ifdef DOXREFS + char xref[BUFLEN]; /* Xref: */ + #endif char *unrec[NUNREC]; /* unrecognized lines */ }; break; + #ifdef DOXREFS + case XREF: + getfield(hp->xref); + break; + #endif DOXREFS case OTHER: if (unreccnt < NUNREC) { if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) { *************** *** 422,427 return NFID; if (its("Nf-From: ")) return NFFROM; return OTHER; } --- 430,439 ----- returnheader.h.2.pat 664 540 12 1044 3473757360 6444 *** header.old.h Tue Apr 30 14:33:33 1985 --- header.h Tue Apr 30 14:33:35 1985 *************** *** 35,39 char approved[BUFLEN]; /* Approved: */ char nf_id[BUFLEN]; /* Nf-ID: */ char nf_from[BUFLEN]; /* Nf-From: */ char *unrec[NUNREC]; /* unrecognized lines */ }; --- 35,42 ----- char approved[BUFLEN]; /* Approved: */ char nf_id[BUFLEN]; /* Nf-ID: */ char nf_from[BUFLEN]; /* Nf-From: */ + #ifdef DOXREFS + char xref[BUFLEN]; /* Xref: */ + #endif DOXREFS char *unrec[NUNREC]; /* unrecognized lines */ }; fdef DOXREFS + char xref[BUFLEN]; /* Xref: */ + #endif char *unrec[NUNREC]; /* unrecognized lines */ }; break; + #ifdef DOXREFS + case XREF: + getfield(hp->xref); + break; + #endif DOXREFS case OTHER: if (unreccnt < NUNREC) { if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) { *************** *** 422,427 return NFID; if (its("Nf-From: ")) return NFFROM; return OTHER; } --- 430,439 ----- returnhelp.c 664 540 12 22337 3473757254 5246 /* $Header: help.c,v 4.3 85/05/01 11:38:59 lwall Exp $ * * $Log: help.c,v $ * Revision 4.3 85/05/01 11:38:59 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "term.h" #include "INTERN.h" #include "help.h" void help_init() { ; } int help_page() { int cmd; #ifdef PAGERHELP doshell(sh,filexp(PAGERHELP)); #else page_init(); if ((cmd = print_lines("\ Paging commands:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ SP Display the next page.\n\ x Display the next page decrypted (rot13).\n\ d Display half a page more.\n\ CR Display one more line.\n\ ^R,v,^X Restart the current article (v=verbose header, ^X=rot13).\n\ ",NOMARKING)) || (cmd = print_lines("\ ^B Back up one page.\n\ ^L,X Refresh the screen (X=rot13).\n\ g pat Go to (search forward within article for) pattern.\n\ G Search again for current pattern within article.\n\ ^G Search for next line beginning with \"Subject:\".\n\ TAB Search for next line beginning with a different character.\n\ q Quit the pager, go to end of article. Leave article read or unread.\n\ j Junk this article (mark it read). Goes to end of article.\n\ \n\ ",NOMARKING)) || (cmd = print_lines("\ The following commands skip the rest of the current article, then behave\n\ just as if typed to the 'What next?' prompt at the end of the article:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ n Scan forward for next unread article.\n\ N Go to next article.\n\ ^N Scan forward for next unread article with same title.\n\ p,P,^P Same as n,N,^N, only going backwards.\n\ - Go to previously displayed article.\n\ \n\ ",NOMARKING)) || (cmd = print_lines("\ The following commands also take you to the end of the article.\n\ Type h at end of article for a description of these commands:\n\ ",STANDOUT)) || (cmd = print_lines("\ # $ & / = ? c C f F k K ^K m M number r R ^R s S u v w W Y ^ |\n\ \n\ (To return to the middle of the article after one of these commands, type ^L.)\n\ ",NOMARKING)) ) return cmd; #endif return 0; } int help_art() { int cmd; #ifdef ARTHELP doshell(sh,filexp(ARTHELP)); #else page_init(); if ((cmd = print_lines("\ Article Selection commands:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ n,SP Scan forward for next unread article.\n\ N Go to next article.\n\ ^N Scan forward for next unread article with same subject.\n\ p,P,^P Same as n,N,^N, only going backwards.\n\ - Go to previously displayed article.\n\ ",NOMARKING)) || (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\ Valid commands are: j, m, M, s, S, and !.\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 h to scan headers, a to scan\n\ entire articles, r to scan read articles, c to make case sensitive.\n\ /pattern/modifiers:command{:command}\n\ Apply one or more commands to the set of articles matching pattern.\n\ Use a K modifier to save entire command to the KILL file for this\n\ newsgroup. Commands m and M, if first, imply an r modifier.\n\ Valid commands are: j, m, M, s, S, and !.\n\ ",NOMARKING)) || (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\ s ... Save to file or pipe via sh.\n\ S ... Save via preferred shell.\n\ w,W Like s and S but save without the header.\n\ | ... Same as s|...\n\ C Cancel this article, if yours.\n\ ",NOMARKING)) || (cmd = print_lines("\ ^R,v Restart article (v=verbose).\n\ ^X Restart article, rot13 mode.\n\ c Catch up (mark all articles as read).\n\ ^B Back up one page.\n\ ^L Refresh the screen. You can get back to the pager with this.\n\ X Refresh screen in rot13 mode.\n\ ",NOMARKING)) || (cmd = print_lines("\ ^ Go to first unread article. Disables subject search mode.\n\ $ Go to end of newsgroup. Disables subject search mode.\n\ ",NOMARKING)) || (cmd = print_lines("# Print last article number.\n\ & Print current values of command-line switches.\n\ &switch {switch}\n\ Set or unset more switches.\n\ && Print current macro definitions.\n\ &&def Define a new macro.\n\ j Junk this article (mark it read). Stays at end of article.\n\ m Mark article as still unread.\n\ M Mark article as still unread upon exiting newsgroup or Y command.\n\ ",NOMARKING)) || (cmd = print_lines("\ Y Yank back articles marked temporarily read via M.\n\ k Mark current SUBJECT as read.\n\ K Mark current SUBJECT as read, and save command in KILL file.\n\ = List subjects of unread articles.\n\ u Unsubscribe to this newsgroup.\n\ ^K Edit local KILL file (the one for this newsgroup).\n\ q Quit this newsgroup for now.\n\ Q Quit newsgroup, staying at current newsgroup.\n\ ",NOMARKING)) ) return cmd; #endif return 0; } int help_ng() { int cmd; #ifdef NGHELP doshell(sh,filexp(NGHELP)); #else page_init(); if (cmd = print_lines("\ Newsgroup Selection commands:\n\ ",STANDOUT) ) return cmd; if (ng != nextrcline) { if (cmd = print_lines("\ \n\ y,SP Do this newsgroup now.\n\ .cmd Do this newsgroup, executing cmd as first command.\n\ = Equivalent to .=.\n\ u Unsubscribe from this newsgroup.\n\ c Catch up (mark this newsgroup all read).\n\ ",NOMARKING) ) return cmd; } if ((cmd = print_lines("\ \n\ n Go to the next newsgroup with unread news.\n\ N Go to the next newsgroup.\n\ p Go to the previous newsgroup with unread news.\n\ P Go to the previous newsgroup.\n\ ",NOMARKING)) || (cmd = print_lines("\ - Go to the previously displayed newsgroup.\n\ 1 Go to the first newsgroup.\n\ ^ Go to the first newsgroup with unread news.\n\ $ Go to the last newsgroup.\n\ ",NOMARKING)) || (cmd = print_lines("\ g name Go to the named newsgroup. Subscribe to new newsgroups this way too.\n\ /pat Search forward for newsgroup matching pattern.\n\ ?pat Search backward for newsgroup matching pattern.\n\ (Use * and ? style patterns. Append r to include read newsgroups.)\n\ ",NOMARKING)) || (cmd = print_lines("\ l pat List unsubscribed newsgroups containing pattern.\n\ m name Move named newsgroup elsewhere (no name moves current newsgroup).\n\ o pat Only display newsgroups matching pattern. Omit pat to unrestrict.\n\ a pat Like o, but also scans for unsubscribed newsgroups matching pattern.\n\ L List current .newsrc.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Print current command-line switch settings.\n\ &switch {switch}\n\ Set (or unset) more command-line switches.\n\ && Print current macro definitions.\n\ &&def Define a new macro.\n\ !cmd Shell escape.\n\ ",NOMARKING)) || (cmd = print_lines("\ q Quit rn.\n\ ^K Edit the global KILL file. Use commands like /pattern/j to suppress\n\ pattern in every newsgroup.\n\ v Print version.\n\ ",NOMARKING)) ) return cmd; #endif #ifdef PUSHBACK if (cmd = get_anything()) return cmd; show_macros(); #endif return 0; } #ifdef ESCSUBS int help_subs() { int cmd; #ifdef SUBSHELP doshell(sh,filexp(SUBSHELP)); #else page_init(); if ((cmd = print_lines("\ Valid substitutions are:\n\ ",STANDOUT)) || (cmd = print_lines("\ \n\ a Current article number\n\ A Full name of current article (%P/%c/%a)\n\ b Destination of last save command, often a mailbox\n\ B Bytes to ignore at beginning of last saved article\n\ ",NOMARKING)) || (cmd = print_lines("\ c Current newsgroup, directory form\n\ C Current newsgroup, dot form\n\ d Full name of newsgroup directory (%P/%c)\n\ D Distribution line from current article\ ",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\ m Current mode, first letter of (init,newsgroup,article,pager,misc)\n\ M Number of article marked with M\n\ n Newsgroups from current article\n\ N Full name (yours)\n\ ",NOMARKING)) || (cmd = print_lines("\ o Organization (yours)\n\ O Original working directory (where you ran rn from)\n\ p Your private news directory (from -d)\n\ P Public news spool directory\n\ ",NOMARKING)) || (cmd = print_lines("\ r Last reference (parent article id)\n\ R References list for followup article\n\ s Subject, with all Re's and (nf)'s stripped off\n\ S Subject, with one Re stripped off\ ",NOMARKING)) || (cmd = print_lines("\ t New To line derived from From and Reply-To (Internet format)\n\ T New To line derived from Path\n\ u Number of unread articles\n\ U Number of unread articles not counting current article\n\ x News library directory\n\ X Rn library directory\n\ z Length of current article in bytes\n\ ",NOMARKING)) || (cmd = print_lines("\ ~ Your home directory\n\ . Directory containing . files\n\ $ Current process number\n\ / Last search string\n\ ESC Run preceding command through % interpretation\n\ ",NOMARKING)) ) return cmd; #endif return 0; } #endif P %c %a %B %C \"%b\" \"From %T %`date`\"" # else # ifdef CONDSUB # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\"" /* header munging with a vengeance */ # elhelp.h 664 540 12 424 3473757517 5206 /* $Header: help.h,v 4.3 85/05/01 11:39:19 lwall Exp $ * * $Log: help.h,v $ * Revision 4.3 85/05/01 11:39:19 lwall * Baseline for release with 4.3bsd. * */ void help_init(); int help_ng(); int help_art(); int help_page(); #ifdef ESCSUBS int help_subs(); #endif M, s, S, and !.\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 h to scan headers, a to scan\n\ entire articinews.c.1.pat 664 540 12 13413 3473757271 6357 *** inews.c.1.std Tue Oct 2 16:09:59 1984 --- inews.c.1 Fri Sep 21 14:50:49 1984 *************** *** 483,488 /* * Link ARTICLE into dir for ngname and update active file. */ localize(ngname) char *ngname; { --- 483,491 ----- /* * Link ARTICLE into dir for ngname and update active file. */ + #ifdef DOXREFS + long + #endif localize(ngname) char *ngname; { *************** *** 515,520 } for (;;) { sprintf(bfr, "%s/%ld", dirname(ngname), ngsize+1); if (link(ARTICLE, bfr) == 0) break; e = errno; /* keep log from clobbering it */ fprintf(stderr, "Cannot install article as %s\n", bfr); --- 518,528 ----- } for (;;) { sprintf(bfr, "%s/%ld", dirname(ngname), ngsize+1); + #ifdef LINKART + if (mylink(ARTICLE, bfr) == 0) break; + /* on first file inits ARTICLE, on subsequent */ + /* files "links" to first article */ + #else if (link(ARTICLE, bfr) == 0) break; #endif e = errno; /* keep log from clobbering it */ *************** *** 516,521 for (;;) { sprintf(bfr, "%s/%ld", dirname(ngname), ngsize+1); if (link(ARTICLE, bfr) == 0) break; e = errno; /* keep log from clobbering it */ fprintf(stderr, "Cannot install article as %s\n", bfr); log("Cannot install article as %s", bfr); --- 524,530 ----- /* files "links" to first article */ #else if (link(ARTICLE, bfr) == 0) break; + #endif e = errno; /* keep log from clobbering it */ fprintf(stderr, "Cannot install article as %s\n", bfr); log("Cannot install article as %s", bfr); *************** *** 542,547 strcpy(firstbufname, bfr); sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); return TRUE; } --- 551,557 ----- strcpy(firstbufname, bfr); sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); + #ifndef DOXREFS return TRUE; #else DOXREFS return ngsize+1; *************** *** 543,548 sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); return TRUE; } /* --- 553,561 ----- addhist(bfr); #ifndef DOXREFS return TRUE; + #else DOXREFS + return ngsize+1; + #endif DOXREFS } /* *************** *** 553,558 register char *ptr; register FILE *tfp; int badgroup = 0, goodgroup = 0; /* Fill up the rest of header. */ if (mode != PROC) { --- 566,574 ----- register char *ptr; register FILE *tfp; int badgroup = 0, goodgroup = 0; + #ifdef DOXREFS + register char *nextxref = header.xref; + #endif DOXREFS /* Fill up the rest of header. */ if (mode != PROC) { *************** *** 565,570 if (mode==PROC) log("from %s relay %s", header.from, header.relayversion); /* Write article to temp file. */ tfp = xfopen(mktemp(ARTICLE), "w"); lhwrite(&header, tfp); --- 581,593 ----- if (mode==PROC) log("from %s relay %s", header.from, header.relayversion); + #ifdef LINKART + *ARTICLE = '\0'; /* tell mylink() to snarf the name */ + #else !LINKART + #ifdef DOXREFS + /* Open temp file for article, but link before writing */ + tfp = xfopen(mktemp(ARTICLE), "w"); + #else DOXREFS /* Write article to temp file. */ tfp = xfopen(mktemp(ARTICLE), "w"); lhwrite(&header, tfp); *************** *** 577,582 } fclose(tfp); fclose(infp); if (is_ctl) { control(&header); --- 600,607 ----- } fclose(tfp); fclose(infp); + #endif DOXREFS + #endif LINKART if (is_ctl) { control(&header); *************** *** 593,598 } } } else { for (ptr = nbuf; *ptr;) { if (*ptr == '-') { while (*ptr++) --- 618,627 ----- } } } else { + #ifdef DOXREFS + sprintf(nextxref,"%s ",SYSNAME); + nextxref += strlen(nextxref); + #endif for (ptr = nbuf; *ptr;) { if (*ptr == '-') { while (*ptr++) *************** *** 610,615 } else goodgroup++; if (*nbuf) localize(ptr); while (*ptr++) --- 639,645 ----- } else goodgroup++; + #ifndef DOXREFS if (*nbuf) localize(ptr); #else DOXREFS *************** *** 612,617 goodgroup++; if (*nbuf) localize(ptr); while (*ptr++) ; } --- 642,653 ----- #ifndef DOXREFS if (*nbuf) localize(ptr); + #else DOXREFS + if (*nbuf) + sprintf(nextxref,"%s:%ld ",ptr,localize(ptr)); + while (*nextxref) + nextxref++; + #endif DOXREFS while (*ptr++) ; } *************** *** 616,621 ; } } #ifdef NOFORWARD if (*nbuf) --- 652,663 ----- ; } } + #ifdef DOXREFS + if (goodgroup < 2) + header.xref[0] = '\0'; + else + *(nextxref-1) = '\0'; + #endif #ifdef LINKART tfp = xfopen(ARTICLE,"w"); /* open 1st article localized */ *************** *** 617,622 } } #ifdef NOFORWARD if (*nbuf) #endif --- 659,683 ----- *(nextxref-1) = '\0'; #endif + #ifdef LINKART + tfp = xfopen(ARTICLE,"w"); /* open 1st article localized */ + #endif + + #if defined(LINKART) || defined(DOXREFS) + /* Now that xref is constructed, write article to temp file. */ + /* (We ought to detect no room at this point and clean up.) */ + lhwrite(&header, tfp); + while (fgets(bfr, BUFLEN, infp) != NULL) { + /* + if (!strncmp(bfr, "From ", 5)) + putc('>', tfp); + */ + fputs(bfr, tfp); + } + fclose(tfp); + fclose(infp); + #endif LINKART || DOXREFS + #ifdef NOFORWARD if (*nbuf) #endif *************** *** 861,863 mclose(fd); } } --- 922,946 ----- mclose(fd); } } + + #ifdef LINKART + mylink(tmpart,linkfrom) + char *tmpart, *linkfrom; + { + struct stat statbuf; + + if (stat(linkfrom,&statbuf)==0) + return -1; + if (!*tmpart) + strcpy(tmpart,linkfrom); + else { + FILE *linkfp = fopen(linkfrom,"w"); + + if (!linkfp) + return -1; + fprintf(linkfp,"%s\n",tmpart); + fclose(linkfp); + } + return 0; + } + #endif LINKART ",NOMARKING)) || (cmd = print_lines("\ q Quit rn.\n\ ^K Edit the global KILL file. Use commands like /pattern/j to suppress\n\ pattern in every newsgroup.\n\ v Print version.\n\ ",NOMARKING)) ) return cmd; #endif #ifdef PUSHBACK if (cinews.c.2.pat 664 540 12 22553 3473757246 6367 *** inews.old.c Tue Apr 30 14:34:19 1985 --- inews.c Tue Apr 30 14:34:33 1985 *************** *** 416,421 /* * Link ARTICLE into dir for ngname and update active file. */ localize(ngname) char *ngname; { --- 416,424 ----- /* * Link ARTICLE into dir for ngname and update active file. */ + #ifdef DOXREFS + long + #endif localize(ngname) char *ngname; { *************** *** 453,458 mknewsg(cp, ngname); sprintf(bfr, "%s/%ld", cp, ngsize+1); #ifdef VMS if ((f2 = creat(bfr, 0666)) >=0 ) { f1 = open(article, 0); --- 456,466 ----- mknewsg(cp, ngname); sprintf(bfr, "%s/%ld", cp, ngsize+1); + #ifdef LINKART + if (mylink(ARTICLE, bfr) == 0) break; + /* on first file inits ARTICLE, on subsequent */ + /* files "links" to first article */ + #else !LINKART #ifdef VMS if ((f2 = creat(bfr, 0666)) >=0 ) { f1 = open(article, 0); *************** *** 468,473 if (link(ARTICLE, bfr) == 0) break; #endif !VMS e = errno; /* keep log from clobbering it */ logerr("Cannot install article as %s", bfr); if (e != EEXIST) { --- 476,482 ----- if (link(ARTICLE, bfr) == 0) break; #endif !VMS + #endif !LINKART e = errno; /* keep log from clobbering it */ logerr("Cannot install article as %s", bfr); if (e != EEXIST) { *************** *** 494,499 strcpy(firstbufname, bfr); sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); return TRUE; } --- 503,509 ----- strcpy(firstbufname, bfr); sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); + #ifndef DOXREFS return TRUE; #else DOXREFS return ngsize+1; *************** *** 495,500 sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); return TRUE; } /* --- 505,513 ----- addhist(bfr); #ifndef DOXREFS return TRUE; + #else DOXREFS + return ngsize+1; + #endif DOXREFS } /* *************** *** 507,512 char c; struct srec srec; /* struct for sys file lookup */ int is_invalid = FALSE; /* Fill up the rest of header. */ if (mode != PROC) { --- 520,529 ----- char c; struct srec srec; /* struct for sys file lookup */ int is_invalid = FALSE; + #ifdef DOXREFS + register char *nextxref = header.xref; + int numxrefs = 0; + #endif DOXREFS /* Fill up the rest of header. */ if (mode != PROC) { *************** *** 527,532 if (!is_ctl && mode != CREATENG) is_invalid = ngfcheck(mode == PROC); /* Write article to temp file. */ tfp = xfopen(mktemp(ARTICLE), "w"); if ( (c=getc(infp)) == ' ' || c == '\t' ) { --- 544,556 ----- if (!is_ctl && mode != CREATENG) is_invalid = ngfcheck(mode == PROC); + #ifdef LINKART + *ARTICLE = '\0'; /* tell mylink() to snarf the name */ + #else !LINKART + #ifdef DOXREFS + /* Open temp file for article, but link before writing */ + tfp = xfopen(mktemp(ARTICLE), "w"); + #else DOXREFS /* Write article to temp file. */ tfp = xfopen(mktemp(ARTICLE), "w"); if ( (c=getc(infp)) == ' ' || c == '\t' ) { *************** *** 545,550 putc('\n',tfp); fclose(tfp); fclose(infp); if (is_invalid) { logerr("No valid newsgroups found, moved to junk"); --- 569,576 ----- putc('\n',tfp); fclose(tfp); fclose(infp); + #endif DOXREFS + #endif LINKART if (is_invalid) { logerr("No valid newsgroups found, moved to junk"); *************** *** 550,555 logerr("No valid newsgroups found, moved to junk"); if (localize("junk")) savehist(histline); xxit(1); } --- 576,582 ----- logerr("No valid newsgroups found, moved to junk"); if (localize("junk")) savehist(histline); + #ifndef DOXREFS xxit(1); #endif } *************** *** 551,556 if (localize("junk")) savehist(histline); xxit(1); } if (time((time_t)0) > (cgtdate(header.subdate) + DFLTEXP) ){ --- 578,584 ----- savehist(histline); #ifndef DOXREFS xxit(1); + #endif } #ifdef DOXREFS else *************** *** 552,558 savehist(histline); xxit(1); } ! if (time((time_t)0) > (cgtdate(header.subdate) + DFLTEXP) ){ logerr("Article too old, moved to junk"); if (localize("junk")) --- 580,588 ----- xxit(1); #endif } ! #ifdef DOXREFS ! else ! #endif if (time((time_t)0) > (cgtdate(header.subdate) + DFLTEXP) ){ logerr("Article too old, moved to junk"); if (localize("junk")) *************** *** 557,562 logerr("Article too old, moved to junk"); if (localize("junk")) savehist(histline); xxit(1); } --- 587,593 ----- logerr("Article too old, moved to junk"); if (localize("junk")) savehist(histline); + #ifndef DOXREFS xxit(1); #endif } *************** *** 558,563 if (localize("junk")) savehist(histline); xxit(1); } if (is_ctl) { --- 589,595 ----- savehist(histline); #ifndef DOXREFS xxit(1); + #endif } #ifdef DOXREFS else *************** *** 559,565 savehist(histline); xxit(1); } ! if (is_ctl) { control(&header); localize("control"); --- 591,599 ----- xxit(1); #endif } ! #ifdef DOXREFS ! else ! #endif if (is_ctl) { #ifndef DOXREFS control(&header); *************** *** 561,566 } if (is_ctl) { control(&header); localize("control"); } else { --- 595,601 ----- else #endif if (is_ctl) { + #ifndef DOXREFS control(&header); #endif localize("control"); *************** *** 562,567 if (is_ctl) { control(&header); localize("control"); } else { if (s_find(&srec, FULLSYSNAME) == FALSE) --- 597,603 ----- if (is_ctl) { #ifndef DOXREFS control(&header); + #endif localize("control"); } else { if (s_find(&srec, FULLSYSNAME) == FALSE) *************** *** 566,571 } else { if (s_find(&srec, FULLSYSNAME) == FALSE) xerror("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE); for (ptr = nbuf; *ptr;) { if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL) localize(ptr); --- 602,611 ----- } else { if (s_find(&srec, FULLSYSNAME) == FALSE) xerror("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE); + #ifdef DOXREFS + sprintf(nextxref,"%s ",FULLSYSNAME); + nextxref += strlen(nextxref); + #endif for (ptr = nbuf; *ptr;) { #ifndef DOXREFS if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL) *************** *** 567,572 if (s_find(&srec, FULLSYSNAME) == FALSE) xerror("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE); for (ptr = nbuf; *ptr;) { if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL) localize(ptr); while (*ptr++) --- 607,613 ----- nextxref += strlen(nextxref); #endif for (ptr = nbuf; *ptr;) { + #ifndef DOXREFS if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL) localize(ptr); #else DOXREFS *************** *** 569,574 for (ptr = nbuf; *ptr;) { if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL) localize(ptr); while (*ptr++) ; } --- 610,624 ----- #ifndef DOXREFS if (ngmatch(ptr, srec.s_nbuf) || index(ptr,'.') == NULL) localize(ptr); + #else DOXREFS + if (ngmatch(ptr, srec.s_nbuf) || + index(ptr,'.') == NULL) { + sprintf(nextxref,"%s:%ld ",ptr,localize(ptr)); + numxrefs++; + while (*nextxref) + nextxref++; + } + #endif DOXREFS while (*ptr++) ; } *************** *** 577,582 localize("junk"); } } broadcast(); savehist(histline); --- 627,638 ----- localize("junk"); } } + #ifdef DOXREFS + if (numxrefs >= 2) + *(nextxref-1) = '\0'; /* wipe out the last space */ + else + header.xref[0] = '\0'; /* wipe out the whole thing */ + #endif #ifdef LINKART tfp = xfopen(ARTICLE,"w"); /* open 1st article localized */ *************** *** 578,583 } } broadcast(); savehist(histline); xxit(0); --- 634,669 ----- header.xref[0] = '\0'; /* wipe out the whole thing */ #endif + #ifdef LINKART + tfp = xfopen(ARTICLE,"w"); /* open 1st article localized */ + #endif + + #if defined(LINKART) || defined(DOXREFS) + /* Now that xref is constructed, write article to temp file. */ + /* (We ought to detect no room at this point and clean up.) */ + if ( (c=getc(infp)) == ' ' || c == '\t' ) { + header.intnumlines++; + sprintf(header.numlines,"%d",header.intnumlines); + } + lhwrite(&header, tfp); + /* Kludge to get around article truncation problem */ + if (c == ' ' || c == '\t' ) + putc('\n', tfp); + putc(c,tfp); + while (fgets(bfr, BUFLEN, infp) != NULL) + fputs(bfr, tfp); + + if (bfr[strlen(bfr)-1] != '\n') + putc('\n',tfp); + fclose(tfp); + fclose(infp); + #endif LINKART || DOXREFS + + #ifdef DOXREFS + if (is_ctl) /* moved here cuz checkgroups uses ARTICLE! */ + control(&header); + #endif + broadcast(); savehist(histline); xxit(0); *************** *** 853,855 } return(NULL); } --- 939,963 ----- } return(NULL); } + + #ifdef LINKART + mylink(tmpart,linkfrom) + char *tmpart, *linkfrom; + { + struct stat statbuf; + + if (stat(linkfrom,&statbuf)==0) + return -1; + if (!*tmpart) /* first article? */ + strcpy(tmpart,linkfrom); /* just remember name */ + else { + FILE *linkfp = fopen(linkfrom,"w"); + + if (!linkfp) + return -1; + fprintf(linkfp,"%s\n",tmpart); /* do "symbolic link" */ + fclose(linkfp); + } + return 0; + } + #endif LINKART gerr("Article too old, moved to junk"); if (localize("junk")) savehist(histline); xxit(1); } --- 587,593 ----- logerr("Articlinit.c 664 540 12 16021 3477433137 5245 /* $Header: init.c,v 4.3.1.2 85/05/21 14:22:46 lwall Exp $ * * $Log: init.c,v $ * Revision 4.3.1.2 85/05/21 14:22:46 lwall * Sped up "rn -c" by avoiding unnecessary initialization. * * Revision 4.3.1.1 85/05/10 11:33:39 lwall * Branch for patches. * * Revision 4.3 85/05/01 16:16:13 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "final.h" #include "term.h" #include "last.h" #include "rn.h" #include "rcstuff.h" #include "ngdata.h" #include "only.h" #include "intrp.h" #include "addng.h" #include "sw.h" #include "art.h" #include "artsrch.h" #include "artio.h" #include "backpage.h" #include "bits.h" #include "cheat.h" #include "head.h" #include "help.h" #include "kfile.h" #include "ngsrch.h" #include "ngstuff.h" #include "rcln.h" #include "respond.h" #include "ng.h" #include "INTERN.h" #include "init.h" bool initialize(argc,argv) int argc; char *argv[]; { char *tcbuf; register bool foundany = FALSE; long time(); #ifdef NOLINEBUF char std_out_buf[BUFSIZ]; setbuf(std_out_buf); #endif tcbuf = safemalloc(1024); /* make temp buffer for termcap and */ /* other initialization stuff */ /* init terminal */ term_init(); /* must precede sw_init() so that */ /* ospeed is set for baud-rate */ /* switches. Actually terminal */ /* mode setting is in term_set() */ /* we have to know rnlib to look up global switches in %X/INIT */ lib = savestr(filexp(LIB)); rnlib = savestr(filexp(RNLIB)); /* decode switches */ sw_init(argc,argv,&tcbuf); /* must not do % interps! */ /* (but may mung environment) */ /* init signals, status flags */ final_init(); /* start up file expansion and the % interpreter */ intrp_init(tcbuf); /* now make sure we have a current working directory */ if (!checkflag) cwd_check(); /* now that we know where to save things, cd to news directory */ if (chdir(spool)) { printf(nocd,spool) FLUSH; finalize(1); } /* if we aren't just checking, turn off echo */ if (!checkflag) term_set(tcbuf); /* get info on last rn run, if any */ if (!checkflag) last_init(tcbuf); free(tcbuf); /* recover 1024 bytes */ /* make sure we are the right version */ if (!checkflag) version_check(); /* make sure we are the sole possessors of .newsrc */ if (!checkflag) lock_check(); /* check for news news */ if (!checkflag) newsnews_check(); /* open active file, etc. */ ngdata_init(); /* now read in the .newsrc file */ foundany = rcstuff_init(); /* it looks like we will actually read something, so init everything */ addng_init(); art_init(); artio_init(); artsrch_init(); backpage_init(); bits_init(); cheat_init(); /* final_init(); already done */ head_init(); help_init(); /* intrp_init(); already done */ kfile_init(); /* last_init(); already done */ ng_init(); /* ngdata_init(); already done */ ngsrch_init(); ngstuff_init(); only_init(); rcln_init(); /* rcstuff_init(); already done */ respond_init(); rn_init(); search_init(); /* sw_init(); already done */ /* term_init(); already done */ util_init(); #ifdef FINDNEWNG fstat(actfp->_file,&filestat); /* did active file grow? */ if (filestat.st_size != lastactsiz) { long actsiz = filestat.st_size; /* remember new size */ NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ #ifdef FASTNEW bool munged = writesoft || !lastactsiz; /* bad soft ptrs -> edited active */ #else bool munged = TRUE; /* just assume .newsrc munged */ #endif #ifdef VERBOSE IF(verbose) fputs("\nChecking active list for new newsgroups...\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNew newsgroups:\n",stdout) FLUSH; #endif #ifdef FASTNEW if (!munged) { /* maybe just do tail of file? */ fseek(actfp,lastactsiz-1,0); fgets(buf,LBUFLEN,actfp); munged = (*buf != '\n'); if (!munged) munged = newlist(munged,FALSE); } #endif if (munged) { /* must we scan entire file? */ fseek(actfp,0L,0); /* rewind active file */ newlist(munged,FALSE); /* sure hope they use hashing... */ } lastactsiz = actsiz; /* remember for .rnlast */ if (nextrcline != oldnext) { /* did we add any new groups? */ foundany = TRUE; /* let main() know */ starthere = 0; /* and start ng scan from the top */ } } #endif time(&lasttime); /* remember when we inited-- */ /* ends up back in .rnlast */ writelast(); /* in fact, put it there now */ #ifdef FINDNEWNG # ifdef ONLY if (maxngtodo) /* patterns on command line? */ foundany |= scanactive(); # endif #endif return foundany; } /* make sure there is no rn out there already */ void lock_check() { lockname = savestr(filexp(LOCKNAME)); if (!checkflag) { tmpfp = fopen(lockname,"r"); if (tmpfp != Nullfp) { int processnum; fgets(buf,LBUFLEN,tmpfp); fclose(tmpfp); processnum = atoi(buf); #ifdef VERBOSE IF(verbose) printf("You seem to have left an rn running, process %d.\n", processnum) FLUSH; ELSE #endif #ifdef TERSE printf("Rn left running, #%d.\n", processnum) FLUSH; #endif if (kill(processnum, SIGEMT)) { /* does process not exist? */ /* (rn ignores SIGEMT) */ sleep(2); #ifdef VERBOSE IF(verbose) fputs("\n\ That process does not seem to exist anymore. The count of read articles\n\ may be incorrect in the last newsgroup accessed by that other (defunct)\n\ process.\n\n",stdout) 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 } get_anything(); putchar('\n') FLUSH; } else { #ifdef VERBOSE IF(verbose) fputs("\n\ You may not have two copies of rn running simultaneously. Goodbye.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nCan't start another.\n",stdout) FLUSH; #endif finalize(0); } } tmpfp = fopen(lockname,"w"); if (tmpfp == Nullfp) { printf(cantcreate,lockname) FLUSH; sig_catcher(0); } fprintf(tmpfp,"%d\n",getpid()); fclose(tmpfp); } } void newsnews_check() { char *newsnewsname = filexp(NEWSNEWSNAME); if ((tmpfp = fopen(newsnewsname,"r")) != Nullfp) { fstat(tmpfp->_file,&filestat); if (filestat.st_mtime > lasttime) { while (fgets(buf,sizeof(buf),tmpfp) != Nullch) fputs(buf,stdout) FLUSH; get_anything(); putchar('\n') FLUSH; } fclose(tmpfp); } } void version_check() { set_ngname("net.announce"); if (access(ngdir,0)) { #ifdef VERBOSE IF(verbose) fputs("Can't find net.announce. Wrong news version?\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Wrong version?\n",stdout) FLUSH; #endif finalize(1); } } SH; finalize(1); } /* if we aren't just checking, turn off echo */ if (!checkflag) term_set(tcbuf); /* get info on last rn run, if any */ if (!checkflag) last_init(tcbuf); free(tcbuf); /* recover 1024 bytes */ /* make sure we are the right version */ if (!checkflag) version_check(); /* make sure we are the sole possessors of .newsrc */ if (!checkflag) lock_check(); /* check for news news */ if (!checkflag) newsnews_check(); init.h 664 540 12 435 3473757516 5222 /* $Header: init.h,v 4.3 85/05/01 11:40:46 lwall Exp $ * * $Log: init.h,v $ * Revision 4.3 85/05/01 11:40:46 lwall * Baseline for release with 4.3bsd. * */ EXT char *lockname INIT(nullstr); bool initialize(); void lock_check(); void newsnews_check(); void version_check(); endif if (kill(processnum, SIGEMT)) { /* does process not exist? */ /* (rn ignores SIGEMT) */ sleep(2); #ifdef VERBOSE IF(verbose) fputs("\n\ That process does not seem to exist anymore. The counintrp.c 664 540 12 55363 3477433274 5454 /* $Header: intrp.c,v 4.3.1.5 85/05/23 17:21:24 lwall Exp $ * * $Log: intrp.c,v $ * Revision 4.3.1.5 85/05/23 17:21:24 lwall * Now allows 'r' and 'f' on null articles. * * Revision 4.3.1.4 85/05/21 13:35:21 lwall * Sped up "rn -c" by not doing unnecessary initialization. * * Revision 4.3.1.3 85/05/17 10:37:11 lwall * Fixed & substitution to capitalize last name too. * * Revision 4.3.1.2 85/05/15 14:39:45 lwall * Spelled gecos right. * * Revision 4.3.1.1 85/05/10 11:33:51 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:40:54 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "search.h" #include "head.h" #include "rn.h" #include "artsrch.h" #include "ng.h" #include "util.h" #include "respond.h" #include "rcstuff.h" #include "bits.h" #include "artio.h" #include "term.h" #include "final.h" #include "INTERN.h" #include "intrp.h" char orgname[] = ORGNAME; /* name of this site */ #ifdef GETHOSTNAME char *hostname; # undef SITENAME # define SITENAME hostname #else !GETHOSTNAME # ifdef DOUNAME # include struct utsname uts; # undef SITENAME # define SITENAME uts.nodename # else !DOUNAME # ifdef PHOSTNAME char *hostname; # undef SITENAME # define SITENAME hostname # else !PHOSTNAME # ifdef WHOAMI # undef SITENAME # define SITENAME sysname # endif WHOAMI # endif PHOSTNAME # endif DOUNAME #endif GETHOSTNAME #ifdef TILDENAME static char *tildename = Nullch; static char *tildedir = Nullch; #endif char *realname INIT(Nullch); /* real name of sender from /etc/passwd */ char *dointerp(); char *getrealname(); #ifdef CONDSUB char *skipinterp(); #endif static void abort_interp(); void intrp_init(tcbuf) char *tcbuf; { char *getlogin(); spool = savestr(filexp(SPOOL)); /* usually /usr/spool/news */ /* get environmental stuff */ /* get home directory */ homedir = getenv("HOME"); if (homedir == Nullch) homedir = getenv("LOGDIR"); dotdir = getval("DOTDIR",homedir); /* get login name */ logname = getenv("USER"); if (logname == Nullch) logname = getenv("LOGNAME"); #ifdef GETLOGIN if (logname == Nullch) logname = savestr(getlogin()); #endif if (checkflag) /* that getwd below takes ~1/3 sec. */ return; /* and we do not need it for -c */ getwd(tcbuf); /* find working directory name */ origdir = savestr(tcbuf); /* and remember it */ /* get the real name of the person (%N) */ /* Must be done after logname is read in because BERKNAMES uses that */ strcpy(tcbuf,getrealname(getuid())); realname = savestr(tcbuf); /* name of header file (%h) */ headname = savestr(filexp(HEADNAME)); /* name of this site (%H) */ #ifdef GETHOSTNAME gethostname(buf,sizeof buf); hostname = savestr(buf); #else #ifdef DOUNAME /* get sysname */ uname(&uts); #else #ifdef PHOSTNAME { FILE *popen(); FILE *pipefp = popen(PHOSTNAME,"r"); if (pipefp == Nullfp) { printf("Can't find hostname\n"); sig_catcher(0); } fgets(buf,sizeof buf,pipefp); buf[strlen(buf)-1] = '\0'; /* wipe out newline */ hostname = savestr(buf); pclose(pipefp); } #endif #endif #endif sitename = savestr(SITENAME); } /* expand filename via %, ~, and $ interpretation */ /* returns pointer to static area */ /* Note that there is a 1-deep cache of ~name interpretation */ char * filexp(s) register char *s; { static char filename[CBUFLEN]; char scrbuf[CBUFLEN]; register char *d; #ifdef DEBUGGING if (debug & DEB_FILEXP) printf("< %s\n",s) FLUSH; #endif interp(filename, (sizeof filename), s); /* interpret any % escapes */ #ifdef DEBUGGING 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 DEBUGGING if (debug & DEB_FILEXP) printf("~ %s\n",scrbuf) FLUSH; #endif strcpy(filename,scrbuf); } else { #ifdef TILDENAME for (d=scrbuf; isalnum(*s); s++,d++) *d = *s; *d = '\0'; if (tildedir && strEQ(tildename,scrbuf)) { strcpy(scrbuf,tildedir); strcat(scrbuf, s); strcpy(filename, scrbuf); #ifdef DEBUGGING if (debug & DEB_FILEXP) printf("r %s %s\n",tildename,tildedir) FLUSH; #endif } else { if (tildename) { free(tildename); free(tildedir); } tildedir = Nullch; tildename = savestr(scrbuf); #ifdef GETPWENT /* getpwnam() is not the paragon of efficiency */ { struct passwd *getpwnam(); struct passwd *pwd = getpwnam(tildename); sprintf(scrbuf,"%s%s",pwd->pw_dir,s); tildedir = savestr(pwd->pw_dir); #ifdef NEWSADMIN if (strEQ(newsadmin,tildename)) newsuid = atoi(pwd->pw_uid); #endif strcpy(filename,scrbuf); #ifdef GETPWENT endpwent(); #endif } #else /* this will run faster, and is less D space */ { /* just be sure LOGDIRFIELD is correct */ FILE *pfp = fopen("/etc/passwd","r"); char tmpbuf[512]; int i; if (pfp == Nullfp) { printf(cantopen,"passwd") FLUSH; sig_catcher(0); } while (fgets(tmpbuf,512,pfp) != Nullch) { d = cpytill(scrbuf,tmpbuf,':'); #ifdef DEBUGGING if (debug & DEB_FILEXP) printf("p %s\n",tmpbuf) FLUSH; #endif if (strEQ(scrbuf,tildename)) { #ifdef NEWSADMIN if (strEQ(newsadmin,tildename)) newsuid = atoi(index(d,':')+1); #endif for (i=LOGDIRFIELD-2; i; i--) { if (d) d = index(d+1,':'); } if (d) { cpytill(scrbuf,d+1,':'); tildedir = savestr(scrbuf); strcat(scrbuf,s); strcpy(filename,scrbuf); } break; } } fclose(pfp); } #endif } #else !TILDENAME #ifdef VERBOSE IF(verbose) fputs("~loginname not implemented.\n",stdout) 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 DEBUGGING if (debug & DEB_FILEXP) printf("$ %s\n",scrbuf) FLUSH; #endif interp(filename, (sizeof filename), scrbuf); /* this might do some extra '%'s but */ /* that is how the Mercedes Benz */ } #ifdef DEBUGGING if (debug & DEB_FILEXP) printf("> %s\n",filename) FLUSH; #endif return filename; } #ifdef CONDSUB /* skip interpolations */ char * skipinterp(pattern,stoppers) register char *pattern; char *stoppers; { while (*pattern && (!stoppers || !index(stoppers,*pattern))) { #ifdef DEBUGGING if (debug & 8) printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern); #endif if (*pattern == '%' && pattern[1]) { switch (*++pattern) { case '{': for (pattern++; *pattern && *pattern != '}'; pattern++) if (*pattern == '\\') pattern++; break; case '[': for (pattern++; *pattern && *pattern != ']'; pattern++) if (*pattern == '\\') pattern++; break; #ifdef CONDSUB case '(': { pattern = skipinterp(pattern+1,"!="); if (!*pattern) goto getout; for (pattern++; *pattern && *pattern != '?'; pattern++) if (*pattern == '\\') pattern++; if (!*pattern) goto getout; pattern = skipinterp(pattern+1,":)"); if (*pattern == ':') pattern = skipinterp(pattern+1,")"); break; } #endif #ifdef BACKTICK case '`': { pattern = skipinterp(pattern+1,"`"); break; } #endif #ifdef PROMPTTTY case '"': pattern = skipinterp(pattern+1,"\""); break; #endif default: break; } pattern++; } else { if (*pattern == '^' && pattern[1]) pattern += 2; else if (*pattern == '\\' && pattern[1]) pattern += 2; else pattern++; } } getout: return pattern; /* where we left off */ } #endif /* interpret interpolations */ char * dointerp(dest,destsize,pattern,stoppers) register char *dest; register int destsize; register char *pattern; char *stoppers; { char *subj_buf = Nullch; char *ngs_buf = Nullch; char *refs_buf = Nullch; char *artid_buf = Nullch; char *reply_buf = Nullch; char *from_buf = Nullch; char *path_buf = Nullch; char *follow_buf = Nullch; char *dist_buf = Nullch; char *line_buf = Nullch; register char *s, *h; register int i; char scrbuf[512]; bool upper = FALSE; bool lastcomp = FALSE; int metabit = 0; while (*pattern && (!stoppers || !index(stoppers,*pattern))) { #ifdef DEBUGGING if (debug & 8) printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern); #endif if (*pattern == '%' && pattern[1]) { upper = FALSE; lastcomp = FALSE; for (s=Nullch; !s; ) { switch (*++pattern) { case '^': upper = TRUE; break; case '_': lastcomp = TRUE; break; case '/': #ifdef ARTSRCH s = scrbuf; if (!index("/?g",pattern[-2])) *s++ = '/'; strcpy(s,lastpat); s += strlen(s); if (pattern[-2] != 'g') { if (index("/?",pattern[-2])) *s++ = pattern[-2]; else *s++ = '/'; if (art_howmuch == 1) *s++ = 'h'; else if (art_howmuch == 2) *s++ = 'a'; if (art_doread) *s++ = 'r'; } *s = '\0'; s = scrbuf; #else s = nullstr; #endif break; case '{': pattern = cpytill(scrbuf,pattern+1,'}'); if (s = index(scrbuf,'-')) *s++ = '\0'; else s = nullstr; s = getval(scrbuf,s); break; case '[': pattern = cpytill(scrbuf,pattern+1,']'); i = set_line_type(scrbuf,scrbuf+strlen(scrbuf)); if (line_buf) free(line_buf); s = line_buf = fetchlines(art,i); break; #ifdef CONDSUB case '(': { COMPEX *oldbra_compex = bra_compex; COMPEX cond_compex; char rch; bool matched; init_compex(&cond_compex); pattern = dointerp(dest,destsize,pattern+1,"!="); rch = *pattern; if (rch == '!') pattern++; if (*pattern != '=') goto getout; pattern = cpytill(scrbuf,pattern+1,'?'); if (!*pattern) goto getout; if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) { printf("%s: %s\n",scrbuf,s) FLUSH; pattern += strlen(pattern); goto getout; } matched = (execute(&cond_compex,dest) != Nullch); if (cond_compex.nbra) /* were there brackets? */ bra_compex = &cond_compex; if (matched==(rch == '=')) { pattern = dointerp(dest,destsize,pattern+1,":)"); if (*pattern == ':') pattern = skipinterp(pattern+1,")"); } else { pattern = skipinterp(pattern+1,":)"); if (*pattern == ':') pattern++; pattern = dointerp(dest,destsize,pattern,")"); } s = dest; bra_compex = oldbra_compex; free_compex(&cond_compex); break; } #endif #ifdef BACKTICK case '`': { FILE *pipefp, *popen(); pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`"); pipefp = popen(scrbuf,"r"); if (pipefp != Nullfp) { int len; len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1, pipefp); scrbuf[len] = '\0'; pclose(pipefp); } else { printf("\nCan't run %s\n",scrbuf); *scrbuf = '\0'; } for (s=scrbuf; *s; s++) { if (*s == '\n') { if (s[1]) *s = ' '; else *s = '\0'; } } s = scrbuf; break; } #endif #ifdef PROMPTTTY case '"': pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\""); fputs(scrbuf,stdout) FLUSH; resetty(); gets(scrbuf); noecho(); crmode(); s = scrbuf; break; #endif case '~': s = homedir; break; case '.': s = dotdir; break; case '$': s = scrbuf; sprintf(s,"%d",getpid()); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': #ifdef CONDSUB s = getbracket(bra_compex,*pattern - '0'); #else s = nullstr; #endif break; case 'a': s = scrbuf; sprintf(s,"%ld",(long)art); break; case 'A': #ifdef LINKART s = linkartname; /* so Eunice people get right file */ #else s = scrbuf; sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art); #endif break; case 'b': s = savedest; break; case 'B': s = scrbuf; sprintf(s,"%ld",(long)savefrom); break; case 'c': s = ngdir; break; case 'C': s = ngname; break; case 'd': s = scrbuf; sprintf(s,"%s/%s",spool,ngdir); break; case 'D': s = dist_buf = fetchlines(art,DIST_LINE); break; case 'f': /* from line */ #ifdef ASYNC_PARSE parse_maybe(art); #endif if (htype[REPLY_LINE].ht_minpos >= 0) { /* was there a reply line? */ if (!(s=reply_buf)) s = reply_buf = fetchlines(art,REPLY_LINE); } else if (!(s = from_buf)) s = from_buf = fetchlines(art,FROM_LINE); break; case 'F': #ifdef ASYNC_PARSE parse_maybe(art); #endif if (htype[FOLLOW_LINE].ht_minpos >= 0) /* is there a Followup-To line? */ s = follow_buf = fetchlines(art,FOLLOW_LINE); else { int off; s = ngs_buf = fetchlines(art,NGS_LINE); if (h = instr(s,"net.general")) { off = h-s; strncpy(scrbuf,s,off+4); strcpy(scrbuf+off+4,"followup"); safecpy(scrbuf+off+12,h+11,sizeof(scrbuf)); s = scrbuf; } } break; case 'h': /* header file name */ s = headname; break; case 'H': /* host name */ s = sitename; break; case 'i': if (!(s=artid_buf)) s = artid_buf = fetchlines(art,MESSID_LINE); if (*s && *s != '<') { sprintf(scrbuf,"<%s>",artid_buf); s = scrbuf; } break; case 'I': /* ref article indicator */ s = scrbuf; sprintf(scrbuf,"'%s'",indstr); break; case 'l': /* rn library */ #ifdef NEWSADMIN s = newsadmin; #else s = "???"; #endif break; case 'L': /* login id */ s = logname; break; case 'm': /* current mode */ s = scrbuf; *s = mode; s[1] = '\0'; break; case 'M': #ifdef DELAYMARK sprintf(scrbuf,"%ld",(long)dmcount); s = scrbuf; #else s = nullstr; #endif break; case 'n': /* newsgroups */ s = ngs_buf = fetchlines(art,NGS_LINE); break; case 'N': /* full name */ s = getval("NAME",realname); break; case 'o': /* organization */ s = getval("ORGANIZATION",orgname); #ifdef ORGFILE if (*s == '/') { FILE *ofp = fopen(s,"r"); if (ofp) { fgets(scrbuf,sizeof scrbuf,ofp); fclose(ofp); s = scrbuf; s[strlen(s)-1] = '\0'; } } #endif break; case 'O': s = origdir; break; case 'p': s = cwd; break; case 'P': s = spool; break; case 'r': #ifdef ASYNC_PARSE parse_maybe(art); #endif if (htype[REFS_LINE].ht_minpos >= 0) { refs_buf = fetchlines(art,REFS_LINE); refscpy(scrbuf,(sizeof scrbuf),refs_buf); } else *scrbuf = '\0'; s = rindex(scrbuf,'<'); break; case 'R': #ifdef ASYNC_PARSE parse_maybe(art); #endif if (htype[REFS_LINE].ht_minpos >= 0) { refs_buf = fetchlines(art,REFS_LINE); refscpy(scrbuf,(sizeof scrbuf),refs_buf); } else *scrbuf = '\0'; if (!artid_buf) artid_buf = fetchlines(art,MESSID_LINE); if (artid_buf[0] == '<') safecat(scrbuf,artid_buf,sizeof(scrbuf)); else if (artid_buf[0]) { char tmpbuf[64]; sprintf(tmpbuf,"<%s>",artid_buf); safecat(scrbuf,tmpbuf,sizeof(scrbuf)); } s = scrbuf; break; case 's': if (!(s=subj_buf)) s = subj_buf = fetchsubj(art,TRUE,TRUE); /* get subject handy */ while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') { /* skip extra Re: */ s += 3; if (*s == ' ') s++; } if (h = instr(s,"- (nf")) *h = '\0'; break; case 'S': if (!(s=subj_buf)) s = subj_buf = fetchsubj(art,TRUE,TRUE); /* get subject handy */ if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') { /* skip extra Re: */ s += 3; if (*s == ' ') s++; } break; case 't': case 'T': #ifdef ASYNC_PARSE parse_maybe(art); #endif if (htype[REPLY_LINE].ht_minpos >= 0) { /* was there a reply line? */ if (!(s=reply_buf)) s = reply_buf = fetchlines(art,REPLY_LINE); } else if (!(s = from_buf)) s = from_buf = fetchlines(art,FROM_LINE); if (*pattern == 'T') { if (htype[PATH_LINE].ht_minpos >= 0) { /* should we substitute path? */ s = path_buf = fetchlines(art,PATH_LINE); } i = strlen(sitename); if (strnEQ(sitename,s,i) && s[i] == '!') s += i + 1; } if ((h=index(s,'(')) != Nullch) /* strip garbage from end */ *(h-1) = '\0'; else if ((h=index(s,'<')) != Nullch) { /* or perhaps from beginning */ s = h+1; if ((h=index(s,'>')) != Nullch) *h = '\0'; } break; case 'u': sprintf(scrbuf,"%ld",(long)toread[ng]); s = scrbuf; break; case 'U': sprintf(scrbuf,"%ld", (long)(((ART_NUM)toread[ng]) - 1 + was_read(art))); s = scrbuf; break; case 'x': /* news library */ s = lib; break; case 'X': /* rn library */ s = rnlib; break; case 'z': #ifdef LINKART s = linkartname; /* so Eunice people get right file */ #else s = scrbuf; sprintf(s,"%ld",(long)art); #endif if (stat(s,&filestat) < 0) filestat.st_size = 0L; sprintf(scrbuf,"%5ld",(long)filestat.st_size); s = scrbuf; break; default: if (--destsize <= 0) abort_interp(); *dest++ = *pattern | metabit; s = nullstr; break; } } if (!s) s = nullstr; 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); } i = metabit; /* maybe get into register */ if (s == dest) { while (*dest) { if (--destsize <= 0) abort_interp(); *dest++ |= i; } } else { while (*s) { if (--destsize <= 0) abort_interp(); *dest++ = *s++ | i; } } } else { if (--destsize <= 0) abort_interp(); if (*pattern == '^' && pattern[1]) { ++pattern; /* skip uparrow */ i = *pattern; /* get char into a register */ if (i == '?') *dest++ = '\177' | metabit; else if (i == '(') { metabit = 0200; destsize++; } else if (i == ')') { metabit = 0; destsize++; } else *dest++ = i & 037 | metabit; pattern++; } else if (*pattern == '\\' && pattern[1]) { ++pattern; /* skip backslash */ i = *pattern; /* get char into a register */ /* this used to be a switch but the if may save space */ if (i >= '0' && i <= '7') { i = 1; while (i < 01000 && *pattern >= '0' && *pattern <= '7') { i <<= 3; i += *pattern++ - '0'; } *dest++ = i & 0377 | metabit; --pattern; } else if (i == 'b') *dest++ = '\b' | metabit; else if (i == 'f') *dest++ = '\f' | metabit; else if (i == 'n') *dest++ = '\n' | metabit; else if (i == 'r') *dest++ = '\r' | metabit; else if (i == 't') *dest++ = '\t' | metabit; else *dest++ = i | metabit; pattern++; } else *dest++ = *pattern++ | metabit; } } *dest = '\0'; getout: if (subj_buf != Nullch) /* return any checked out storage */ free(subj_buf); if (ngs_buf != Nullch) free(ngs_buf); if (refs_buf != Nullch) free(refs_buf); if (artid_buf != Nullch) free(artid_buf); if (reply_buf != Nullch) free(reply_buf); if (from_buf != Nullch) free(from_buf); if (path_buf != Nullch) free(path_buf); if (follow_buf != Nullch) free(follow_buf); if (dist_buf != Nullch) free(dist_buf); if (line_buf != Nullch) free(line_buf); return pattern; /* where we left off */ } void interp(dest,destsize,pattern) char *dest; int destsize; char *pattern; { dointerp(dest,destsize,pattern,Nullch); #ifdef DEBUGGING if (debug & DEB_FILEXP) fputs(dest,stdout); #endif } /* copy a references line, normalizing as we go */ void refscpy(dest,destsize,src) register char *dest, *src; register int destsize; { register char *dot, *at, *beg; char tmpbuf[64]; while (*src) { if (*src != '<') { if (--destsize <= 0) break; *dest++ = '<'; at = dot = Nullch; beg = src; while (*src && *src != ' ' && *src != ',') { if (*src == '.') dot = src; else if (*src == '@') at = src; if (--destsize <= 0) break; *dest++ = *src++; } if (destsize <= 0) break; if (dot && !at) { int len; *dest = *dot++ = '\0'; sprintf(tmpbuf,"%s@%s.UUCP",dot,beg); len = strlen(tmpbuf); if (destsize > len) { strcpy(dest,tmpbuf); dest = dest + len; destsize -= len; } } if (--destsize <= 0) break; *dest++ = '>'; } else { while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ; if (destsize <= 0) break; } while (*src == ' ' || *src == ',') src++; if (*src && --destsize > 0) *dest++ = ' '; } *dest = '\0'; } /* get the person's real name from /etc/passwd */ /* (string is overwritten, so it must be copied) */ char * getrealname(uid) int uid; { char *s, *c; #ifdef PASSNAMES #ifdef GETPWENT struct passwd *pwd = getpwuid(uid); s = pwd->pw_gecos; #else char tmpbuf[512]; int i; getpw(uid, tmpbuf); for (s=tmpbuf, i=GCOSFIELD-1; i; i--) { if (s) s = index(s,':')+1; } if (!s) return nullstr; cpytill(tmpbuf,s,':'); s = tmpbuf; #endif #ifdef BERKNAMES #ifdef BERKJUNK while (*s && !isalnum(*s) && *s != '&') s++; #endif if ((c = index(s, ',')) != Nullch) *c = '\0'; if ((c = index(s, ';')) != Nullch) *c = '\0'; s = cpytill(buf,s,'&'); if (*s == '&') { /* whoever thought this one up was */ c = buf + strlen(buf); /* in the middle of the night */ strcat(c,logname); /* before the morning after */ strcat(c,s+1); if (islower(*c)) *c = toupper(*c); /* gack and double gack */ } #else if ((c = index(s, '(')) != Nullch) *c = '\0'; if ((c = index(s, '-')) != Nullch) s = c; strcpy(buf,tmpbuf); #endif #ifdef GETPWENT endpwent(); #endif return buf; /* return something static */ #else if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) { fgets(buf,sizeof buf,tmpfp); fclose(tmpfp); buf[strlen(buf)-1] = '\0'; return buf; } return "PUT YOUR NAME HERE"; #endif } static void abort_interp() { fputs("\n% interp buffer overflow!\n",stdout) FLUSH; sig_catcher(0); } ly line? */ if (!(s=reply_buf)) s = reply_buf = fetchlines(art,REPLY_LINE); } else if (!(s = from_buf)) s = from_buf = fetchlines(art,FROM_LINE); break; case 'F': #ifdef ASYNC_PARSE parse_maybe(art); #endif if (htype[FOLLOW_intrp.h 664 540 12 1412 3473757450 5424 /* $Header: intrp.h,v 4.3 85/05/01 11:41:48 lwall Exp $ * * $Log: intrp.h,v $ * Revision 4.3 85/05/01 11:41:48 lwall * Baseline for release with 4.3bsd. * */ EXT char *lib INIT(Nullch); /* news library */ EXT char *rnlib INIT(Nullch); /* private news program library */ EXT char *origdir INIT(Nullch); /* cwd when rn invoked */ EXT char *homedir INIT(Nullch); /* login directory */ EXT char *dotdir INIT(Nullch); /* where . files go */ EXT char *logname INIT(Nullch); /* login id */ EXT char *sitename INIT(Nullch); /* host name */ #ifdef NEWSADMIN EXT char newsadmin[] INIT(NEWSADMIN);/* news administrator */ EXT int newsuid INIT(0); #endif void intrp_init(); char *filexp(); char *dointerp(); void interp(); void refscpy(); char *getrealname(); /* strip garbage from end */ *(h-1) = '\0'; else if ((h=index(s,'<')) != Nullch) { /* or perhaps from beginning */ s = h+1; if ((h=index(s,'>')) != Nullch) *h = '\0'; } break; case 'u': sprinkfile.c 664 540 12 12574 3501473225 5373 /* $Header: kfile.c,v 4.3.1.3 85/05/29 09:11:52 lwall Exp $ * * $Log: kfile.c,v $ * Revision 4.3.1.3 85/05/29 09:11:52 lwall * Suppressed some killing messages on -t. * * Revision 4.3.1.2 85/05/10 14:21:29 lwall * Prevented THRU from setting art < absfirst. * * Revision 4.3.1.1 85/05/10 11:34:33 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:41:53 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "artsrch.h" #include "ng.h" #include "bits.h" #include "intrp.h" #include "ngstuff.h" #include "rcstuff.h" #include "rn.h" #include "INTERN.h" #include "kfile.h" static bool exitcmds = FALSE; void kfile_init() { ; } #ifndef KILLFILES int edit_kfile() { notincl("^K"); return -1; } #else KILLFILES char killglobal[] = KILLGLOBAL; char killlocal[] = KILLLOCAL; void mention(str) char *str; { #ifdef VERBOSE IF(verbose) { #ifdef NOFIREWORKS no_sofire(); #endif standout(); fputs(str,stdout); un_standout(); putchar('\n'); } ELSE #endif #ifdef TERSE putchar('.'); #endif fflush(stdout); } int do_kfile(kfp,entering) FILE *kfp; int entering; { art = lastart+1; fseek(kfp,0L,0); /* rewind file */ while (fgets(buf,LBUFLEN,kfp) != Nullch) { buf[strlen(buf)-1] = '\0'; if (strnEQ(buf,"THRU",4)) { ART_NUM tmpart; tmpart = atol(buf+4)+1; if (tmpart < absfirst) tmpart = absfirst; check_first(tmpart); firstart = tmpart; continue; } if (*buf == 'X') { /* exit command? */ if (entering) { exitcmds = TRUE; continue; } strcpy(buf,buf+1); } else { if (!entering) continue; } if (*buf == '&') { mention(buf); switcheroo(); } else if (*buf == '/' && firstart <= lastart) { mention(buf); switch (art_search(buf, (sizeof 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 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; } } } 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; bool anytokill = (toread[ng] > 0); if (localkfp || globkfp) { if (!entering && !exitcmds) return; exitcmds = FALSE; oldfirst = firstart; firstart = starting; clear(); #ifdef VERBOSE IF(verbose) if (message) fputs(message,stdout) FLUSH; #endif if (localkfp) intr = do_kfile(localkfp,entering); if (globkfp && !intr) intr = do_kfile(globkfp,entering); if (entering && localkfp && !intr) setthru(lastart); putchar('\n') FLUSH; #ifdef VERBOSE IF(verbose) if (entering) get_anything(); #endif if (anytokill) /* if there was anything to kill */ forcelast = FALSE; /* allow for having killed it all */ firstart = oldfirst; } } void setthru(thru) ART_NUM thru; { FILE *newkfp; fseek(localkfp,0L,0); /* rewind current file */ strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); UNLINK(buf); /* to prevent file reuse */ if (newkfp = fopen(buf,"w")) { fprintf(newkfp,"THRU %ld\n",(long)thru); while (fgets(buf,LBUFLEN,localkfp) != Nullch) { if (strnEQ(buf,"THRU",4)) continue; fputs(buf,newkfp); } fclose(newkfp); open_kfile(KF_LOCAL); /* and reopen local file */ } else printf(cantcreate,buf) FLUSH; } /* edit KILL file for newsgroup */ int edit_kfile() { int r = -1; if (in_ng) strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); else strcpy(buf,filexp(getval("KILLGLOBAL",killglobal))); if ((r = makedir(buf,MD_FILE)) >= 0) { sprintf(cmd_buf,"%s %s", filexp(getval("VISUAL",getval("EDITOR",defeditor))),buf); printf("\nEditing %s KILL file:\n%s\n", (in_ng?"local":"global"),cmd_buf) FLUSH; resetty(); /* make sure tty is friendly */ r = doshell(sh,cmd_buf);/* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ open_kfile(in_ng); } else printf("Can't make %s\n",buf) FLUSH; return r; } void open_kfile(local) int local; { char *kname = filexp(local ? getval("KILLLOCAL",killlocal) : getval("KILLGLOBAL",killglobal) ); stat(kname,&filestat); if (!filestat.st_size) /* nothing in the file? */ UNLINK(kname); /* delete the file */ if (local) { if (localkfp) fclose(localkfp); localkfp = fopen(kname,"r"); } else { if (globkfp) fclose(globkfp); globkfp = fopen(kname,"r"); } } void kf_append(cmd) char *cmd; { strcpy(cmd_buf,filexp(getval("KILLLOCAL",killlocal))); if (makedir(cmd_buf,MD_FILE) >= 0) { #ifdef VERBOSE IF(verbose) printf("\nDepositing command in %s...",cmd_buf); ELSE #endif #ifdef TERSE printf("\n--> %s...",cmd_buf); #endif fflush(stdout); sleep(2); if ((tmpfp = fopen(cmd_buf,"a")) != Nullfp) { fseek(tmpfp,0L,2); /* get to EOF for sure */ fprintf(tmpfp,"%s\n",cmd); fclose(tmpfp); fputs("done\n",stdout) FLUSH; } else printf(cantopen,cmd_buf) FLUSH; } } #endif KILLFILES ar *dot, *at, *beg; char tmpbuf[64]; while (*src) { if (*src != '<') { if (--destsize <= 0) break; *dest++kfile.h 664 540 12 1022 3473757463 5363 /* $Header: kfile.h,v 4.3 85/05/01 11:42:00 lwall Exp $ * * $Log: kfile.h,v $ * Revision 4.3 85/05/01 11:42:00 lwall * Baseline for release with 4.3bsd. * */ #define KF_GLOBAL 0 #define KF_LOCAL 1 #ifdef KILLFILES EXT FILE *globkfp INIT(Nullfp); /* global article killer file */ EXT FILE *localkfp INIT(Nullfp); /* local (for this newsgroup) */ /* article killer file */ #endif void kfile_init(); int do_kfile(); void kill_unwanted(); int edit_kfile(); void open_kfile(); void kf_append(); void setthru(); ) { /* exit command? */ if (entering) { exitcmds = TRUE; continue; } strcpy(buf,buf+1); } else { if (!entering) continue; } if (*buf == '&') { mention(buf); switcheroo(); } else if (*buf == '/' && firstart <= lastart) { mention(buf); switch (art_search(buf, (sizeof buf), FALSE)) { case SRCH_ABORT: continue; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; kit1isdone 664 540 12 0 3473756703 6024 kit2isdone 664 540 12 0 3473756752 6031 kit3isdone 664 540 12 0 3473757011 6017 kit4isdone 664 540 12 0 3473757116 6026 kit5isdone 664 540 12 0 3473757175 6034 kit6isdone 664 540 12 0 3473757271 6032 kit7isdone 664 540 12 0 3473757361 6033 kit8isdone 664 540 12 0 3473757502 6031 kit9isdone 664 540 12 0 3473757522 6034 kitleader 664 540 12 1035 3473757463 6013 #! /bin/sh # $Header: kitleader,v 4.3 85/05/01 11:42:03 lwall Exp $ # # $Log: kitleader,v $ # Revision 4.3 85/05/01 11:42:03 lwall # Baseline for release with 4.3bsd. # cat >$1 < #define MAXKIT 100 #define MAXKITSIZE 63000 #define KITOVERHEAD 700 #define FILEOVERHEAD 80 long tot[MAXKIT]; FILE *outfp[MAXKIT]; /* of course, not this many file descriptors */ main(argc,argv) int argc; char **argv; { FILE *inp, *popen(); char buf[1024], filnam[128]; char *index(); register char *s; register int i, newtot; sprintf(buf,"\ ls -l `awk '{print $1}' <%s'` | awk '{print $8 \" \" $4}' | sort +1nr\ ", argc > 1 ? argv[1] : "MANIFEST.new"); inp = popen(buf,"r"); while (fgets(buf,1024,inp) != Nullch) { s = index(buf,' '); *s++ = '\0'; for (i=1; (newtot = tot[i] + atol(s) + FILEOVERHEAD) > MAXKITSIZE-KITOVERHEAD; i++) ; if (!tot[i]) { sprintf(filnam,"kit%d.list",i); outfp[i] = fopen(filnam,"w"); } tot[i] = newtot; printf("Adding %s to kit %d giving %d bytes\n",buf,i,newtot); fprintf(outfp[i],"%s\n",buf); } } SH; #endif 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; } } } return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char *message; int entering; { bool intr = FALSE; /* did we get akittrailer 664 540 12 1252 3473757455 6223 #! /bin/sh # $Header: kittrailer,v 4.3 85/05/01 11:42:11 lwall Exp $ # # $Log: kittrailer,v $ # Revision 4.3 85/05/01 11:42:11 lwall # Baseline for release with 4.3bsd. # rangelist=`range 1 $3` cat >>$1 <kit${2}isdone config=true for iskit in $rangelist; do if test -f kit\${iskit}isdone; then echo "You have run kit \${iskit}." else echo "You still need to run kit \${iskit}." config=false fi done case \$config in true) echo "You have run all your kits. Please read README and then type Configure." chmod 755 Configure ;; esac : I do not append .signature, but someone might mail this. exit EOT inp = popen(buf,"r"); while (fgets(buf,1024,inp) != Nullch) { s = index(buf,' '); *s++ = '\0'; for (i=1; (newtot = tot[i] + atol(s) + FILEOVERHEAD) > MAXKITSIZE-KITOVERHEAD; i++) ; if (!tot[i]) { sprintf(filnam,"kit%d.list",i); outfp[i] = fopen(filnam,"w"); } tot[i] = newtot; printf("Adding %s to kit %d last.c 664 540 12 2015 3473757440 5225 /* $Header: last.c,v 4.3 85/05/01 11:42:16 lwall Exp $ * * $Log: last.c,v $ * Revision 4.3 85/05/01 11:42:16 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "util.h" #include "intrp.h" #include "INTERN.h" #include "last.h" char *lastname = Nullch; /* path name of .rnlast file */ void last_init(tcbuf) char *tcbuf; { lastname = savestr(filexp(LASTNAME)); if ((tmpfp = fopen(lastname,"r")) != Nullfp) { fgets(tcbuf,1024,tmpfp); tcbuf[strlen(tcbuf)-1] = '\0'; lastngname = savestr(tcbuf); fgets(tcbuf,1024,tmpfp); lasttime = atol(tcbuf); fgets(tcbuf,1024,tmpfp); lastactsiz = atol(tcbuf); fclose(tmpfp); } else { lastngname = nullstr; lasttime = 0; lastactsiz = 0; } } /* put out certain values for next run of rn */ void writelast() { if ((tmpfp = fopen(lastname,"w")) != Nullfp) { fprintf(tmpfp,"%s\n%ld\n%ld\n",ngname,(long)lasttime,(long)lastactsiz); fclose(tmpfp); } else printf(cantcreate,lastname) FLUSH; } es\n",buf,i,newtot); fprintf(outfp[i],"%s\n",buf); } } SH; #endif 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; } } } return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char *message; int entering; { bool intr = FALSE; /* did we get alast.h 664 540 12 653 3473757474 5227 /* $Header: last.h,v 4.3 85/05/01 11:42:22 lwall Exp $ * * $Log: last.h,v $ * Revision 4.3 85/05/01 11:42:22 lwall * Baseline for release with 4.3bsd. * */ EXT char *lastngname INIT(Nullch); /* last newsgroup read, from .rnlast file */ EXT long lasttime INIT(0); /* time last rn was started up */ EXT long lastactsiz INIT(0); /* size of active file when rn last started up */ void last_init(); void writelast(); TNAME)); if ((tmpfp = fopen(lastname,"r")) != Nullfp) { fgets(tcbuf,1024,tmpfp);makedepend.SH 664 540 12 4137 3473762431 6452 case $CONFIG in '') . config.sh ;; esac echo "Extracting makedepend (with variable substitutions)" $spitshell >makedepend <.deptmp $echo "(Note: this is going to take a while.)" $rm -f X*.c for file in *.c; do filebase=\`basename \$file .c\` $echo "Finding dependencies for \$filebase.o." $sed -n <\$file >X\$file \\ -e "/^\${filebase}_init(/q" \\ -e '/^#/{' \\ -e 's|/\*.*$||' \\ -e p \\ -e '}' $cpp -I/usr/local/include X\$file | $sed \\ -e '/^# *[0-9]/!d' \\ -e 's/^.*"\(.*\)".*\$/'\$filebase'.o: \1/' \\ -e 's|: \./|: |' \\ -e 's|: X|: |' | \\ $uniq | $sort | $uniq >> .deptmp done for file in *.SH; do $echo \`basename \$file .SH\`: \$file config.sh \; /bin/sh \$file >> .deptmp done $sed Makefile.new -e '1,/^# AUTOMATICALLY/!d' if $test -s .deptmp; then echo "Updating Makefile..." echo "# If this runs make out of memory, delete /usr/include lines." >>Makefile.new $cat .deptmp >>Makefile.new else $echo "You don't seem to have a proper C preprocessor. Using grep instead." $egrep '^#include ' *.c *.h >.deptmp echo "Updating Makefile..." <.deptmp $sed -n 's|c:#include "\(.*\)".*\$\$|o: \1|p' >> Makefile.new <.deptmp $sed -n 's|c:#include <\(.*\)>.*\$\$|o: /usr/include/\1|p' >> Makefile.new <.deptmp $sed -n 's|h:#include "\(.*\)".*\$\$|h: \1|p' >> Makefile.new <.deptmp $sed -n 's|h:#include <\(.*\)>.*\$\$|h: /usr/include/\1|p' >> Makefile.new fi $mv Makefile Makefile.old $mv Makefile.new Makefile $echo "# WARNING: Put nothing here or make depend will gobble it up!" >> Makefile rm .deptmp X*.c !GROK!THIS! $eunicefix makedepend chmod 755 makedepend ; 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; } } } return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char *message; int entering; { bool intr = FALSE; /* did we get amakedir.SH 664 540 12 2077 3473757436 6003 case $CONFIG in '') . config.sh ;; esac echo "Extracting makedir (with variable substitutions)" $spitshell >makedir </dev/null 2>&1 done !GROK!THIS! $eunicefix makedir chmod 755 makedir makedepend chmod 755 makedepend ; 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; } } } return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char *message; int entering; { bool intr = FALSE; /* did we get amakedist 664 540 12 350 3473757115 5621 #!/bin/sh # $Header: makedist,v 4.3 85/05/01 11:42:35 lwall Exp $ # # $Log: makedist,v $ # Revision 4.3 85/05/01 11:42:35 lwall # Baseline for release with 4.3bsd. # rm -f kit*.list manifake kitlists manimake makekit kit*.list ision 4.3 85/05/01 11:42:31 lwall # Baseline for release with 4.3bsd. # export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$) case \$# in 0) $echo "makedir pathname filenameflag" exit 1 ;; esac : guarantmakekit 664 540 12 1351 3473757451 5472 #!/bin/sh # $Header: makekit,v 4.3 85/05/01 11:42:38 lwall Exp $ # # $Log: makekit,v $ # Revision 4.3 85/05/01 11:42:38 lwall # Baseline for release with 4.3bsd. # numkits=$# for kitlist in $*; do kit=`basename $kitlist .list` kitnum=`expr "$kit" : 'kit\([0-9][0-9]*\)'` echo "*** Making $kit ***" kitleader "$kit" "$kitnum" "$numkits" for file in `/bin/cat $kitlist`; do echo $file echo "echo Extracting $file" >> $kit if egrep '^\.$' $file; then echo "sed >$file <<'!STUFFY!FUNK!' -e 's/X//'" >> $kit sed <$file >>$kit -e 's/^/X/' else echo "cat >$file <<'!STUFFY!FUNK!'" >> $kit /bin/cat $file >> $kit fi echo "!STUFFY!FUNK!" >> $kit done kittrailer "$kit" "$kitnum" "$numkits" done n reasonable status if nothing to be created if $test -d "\$1" ; then exit 0 fi list='' while true ; do case \$1 in */*) list="\$1 \$list" set \`echo \$1 | $sed 's:\(.*\)/:\1 :'\` ;; *) break ;; esac done set \$list for dir do $mkdir \$dir >/dev/nmanifake 664 540 12 712 3473757470 5601 #!/bin/sh # $Header: manifake,v 4.3 85/05/01 11:42:41 lwall Exp $ # # $Log: manifake,v $ # Revision 4.3 85/05/01 11:42:41 lwall # Baseline for release with 4.3bsd. # : make MANIFEST and MANIFEST.new say the same thing if test ! -f MANIFEST.new; then if test -f MANIFEST; then sed MANIFEST.new \ -e '1,/---/d' \ -e 's/\([ ][ ]*\)[0-9]* */\1/' else echo "Make a MANIFEST.new file, with names and descriptions." fi fi ^\.$' $file; then echo "sed >$file <<'!STUFFY!FUNmanimake 664 540 12 1263 3473757455 5635 #!/bin/sh # $Header: manimake,v 4.3 85/05/01 11:42:46 lwall Exp $ # # $Log: manimake,v $ # Revision 4.3 85/05/01 11:42:46 lwall # Baseline for release with 4.3bsd. # : make MANIFEST and MANIFEST.new say the same thing if test -f MANIFEST.new; then cat <<'EOH' > MANIFEST After all the rn kits are run you should have the following files: Filename Kit Description -------- --- ----------- EOH sort MANIFEST.new >.mani grep . kit*.list | sed 's/^kit\(.*\)\.list:\$*\(.*\)$/\2 |\1|/' | \ sort | \ join -a1 - .mani | \ awk -F'|' '{printf "%-16s%2s %s\n",$1,$2,$3}' | \ unexpand >> MANIFEST rm .mani else echo "You don't have a MANIFEST.new file. Run manifake." fi done kittrailer "$kit" "$kitnum" "$numkits" done n reasonable status if nothing to be created if $test -d "\$1" ; then exit 0 fi list='' while true ; do case \$1 in */*) list="\$1 \$list" set \`echo \$1 | $sed 's:\(.*\)/:\1 :'\` ;; *) break ;; esac done set \$list for dir do $mkdir \$dir >/dev/nmbox.saver.SH 664 540 12 2040 3477433043 6426 case $CONFIG in '') . config.sh ;; esac echo "Extracting mbox.saver (with variable substitutions)" $spitshell >mbox.saver <From/" $echo "" $echo "" ) >> \$7 !GROK!THIS! $eunicefix mbox.saver chmod 755 mbox.saver .sh ;; esac echo "Extracting mbox.saver (with variable substitutions)" $spitshell >mbox.saver <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. */ struct direct * readdir(dirp) register DIR *dirp; { register struct olddirect *dp; static struct direct dir; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) return NULL; } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->od_ino == 0) continue; dir.d_ino = dp->od_ino; strncpy(dir.d_name, dp->od_name, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } /* * close a directory. */ void closedir(dirp) register DIR *dirp; { close(dirp->dd_fd); dirp->dd_fd = -1; dirp->dd_loc = 0; free(dirp); } #endif USENDIR anything to kill */ forcelast = FALSE; /* allow for having killed it all */ firstart = oldfirst; } } void setthru(thru) ART_NUM thru; { FILE *newkfp; fseek(localkfp,0L,0); /* rewind current file */ strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); UNLINK(buf); /* to prevent file reuse */ if (newkfp = fopen(buf,"w")) { fprintf(newkfp,"THRU %ld\n",(long)thru); while (fgendir.h 664 540 12 2557 3473757431 5236 /* $Header: ndir.h,v 4.3 85/05/01 11:43:00 lwall Exp $ * * $Log: ndir.h,v $ * Revision 4.3 85/05/01 11:43:00 lwall * Baseline for release with 4.3bsd. * */ #ifdef LIBNDIR # include #else # ifndef USENDIR # include # else #ifndef DEV_BSIZE #define DEV_BSIZE 512 #endif #define DIRBLKSIZ DEV_BSIZE #define MAXNAMLEN 255 struct direct { long d_ino; /* inode number of entry */ short d_reclen; /* length of this record */ short d_namlen; /* length of string in d_name */ char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ }; /* * The DIRSIZ macro gives the minimum record length which will hold * the directory entry. This requires the amount of space in struct direct * without the d_name field, plus enough space for the name with a terminating * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. */ #undef DIRSIZ #define DIRSIZ(dp) \ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)) /* * Definitions for library routines operating on directories. */ typedef struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; } DIR; #ifndef NULL #define NULL 0 #endif extern DIR *opendir(); extern struct direct *readdir(); extern long telldir(); extern void seekdir(); #define rewinddir(dirp) seekdir((dirp), (long)0) extern void closedir(); # endif #endif r; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) rnewsetup.1 664 540 12 3077 3473757425 6066 ''' $Header: newsetup.1,v 4.3 85/05/01 11:43:22 lwall Exp $ ''' ''' $Log: newsetup.1,v $ ''' Revision 4.3 85/05/01 11:43:22 lwall ''' Baseline for release with 4.3bsd. ''' ''' .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. ''' Bell System Logo is used as a dummy character. ''' .ie n \{\ .tr \(bs-\*(Tr .ds -- \(bs- .if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH NEWSETUP 1 LOCAL .SH NAME newsetup - a program to set up a .newsrc file .SH SYNOPSIS .B newsetup .SH DESCRIPTION The .I newsetup program creates a new .newsrc file containing all of the currently active newsgroups. It tries to put them in a reasonable order, i.e. local newsgroups earlier, but you'll probably want to change the ordering anyway (if you use .IR rn ) in order to put interesting newsgroups first. If you already have a .newsrc, it will be backed up with the name \*(L".oldnewsrc\*(R". .SH ENVIRONMENT .IP DOTDIR 8 Where to put your .newsrc, if not in your home directory. .Sp Default: $HOME .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .SH FILES /usr/lib/news/active or a reasonable facsimile .br ${DOTDIR-{$HOME-$LOGDIR}}/.newsrc .SH SEE ALSO rn(1), newsrc(5) .SH DIAGNOSTICS .SH BUGS >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 Dnewsetup.SH 664 540 12 6573 3473757347 6247 case $CONFIG in '') . config.sh ;; esac echo "Extracting newsetup (with variable substitutions)" $spitshell >newsetup </tmp/n.tmp\$\$ \\ -e 's/^\([^ ]*\) .*\$/\1:/' \\ -e '/^control:/{' \\ -e " w /tmp/n.test\$\$" \\ -e ' d' \\ -e '}' \\ -e '/^junk:/{' \\ -e " w /tmp/n.test\$\$" \\ -e ' d' \\ -e '}' \\ -e '/test:/{' \\ -e " w /tmp/n.test\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^net\./{" \\ -e " w /tmp/n.net\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^mod\./{" \\ -e " w /tmp/n.mod\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^\$locorg\./{" \\ -e " w /tmp/n.\$locorg\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^\$organization\./{" \\ -e " w /tmp/n.\$organization\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^\$city\./{" \\ -e " w /tmp/n.\$city\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^\$state\./{" \\ -e " w /tmp/n.\$state\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^fa\./{" \\ -e " w /tmp/n.fa\$\$" \\ -e ' d' \\ -e '}' $sed /tmp/n.local\$\$ \\ -e "/^\$cntry\./{" \\ -e " w /tmp/n.\$cntry\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^\$cont\./{" \\ -e " w /tmp/n.\$cont\$\$" \\ -e ' d' \\ -e '}' \\ -e "/^to\./{" \\ -e " w /tmp/n.to\$\$" \\ -e ' d' \\ -e '}' \\ -e "/\./{" \\ -e " w /tmp/n.misc\$\$" \\ -e ' d' \\ -e '}' if $test -s \$dotdir/.newsrc ; then $echo "Saving your current .newsrc as .oldnewsrc..." $mv -f \$dotdir/.newsrc \$dotdir/.oldnewsrc fi : newsrc order determined here $cat \\ /tmp/n.local\$\$ \\ /tmp/n.\$locorg\$\$ \\ /tmp/n.\$organization\$\$ \\ /tmp/n.\$city\$\$ \\ /tmp/n.\$state\$\$ \\ /tmp/n.\$cntry\$\$ \\ /tmp/n.\$cont\$\$ \\ /tmp/n.mod\$\$ \\ /tmp/n.net\$\$ \\ /tmp/n.fa\$\$ \\ /tmp/n.misc\$\$ \\ /tmp/n.test\$\$ \\ | $uniq >\$dotdir/.newsrc $rm -f /tmp/n.to\$\$ \\ /tmp/n.tmp\$\$ \\ /tmp/n.local\$\$ \\ /tmp/n.\$locorg\$\$ \\ /tmp/n.\$organization\$\$ \\ /tmp/n.\$city\$\$ \\ /tmp/n.\$state\$\$ \\ /tmp/n.\$cntry\$\$ \\ /tmp/n.\$cont\$\$ \\ /tmp/n.mod\$\$ \\ /tmp/n.net\$\$ \\ /tmp/n.fa\$\$ \\ /tmp/n.misc\$\$ \\ /tmp/n.test\$\$ $cat <<'EOH' Done. If you have never used the news system before, you may find the articles in net.announce.newuser to be helpful. There is also a manual entry for rn. To get rid of newsgroups you aren't interested in, use the 'u' command. Type h for help at any time while running rn. EOH !GROK!THIS! $eunicefix newsetup chmod 755 newsetup strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } /* * close a directory. */ void closedir(dirp) register Dnewsgroups.1 664 540 12 3252 3473757417 6424 ''' $Header: newsgroups.1,v 4.3 85/05/01 11:43:32 lwall Exp $ ''' ''' $Log: newsgroups.1,v $ ''' Revision 4.3 85/05/01 11:43:32 lwall ''' Baseline for release with 4.3bsd. ''' ''' .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. ''' Bell System Logo is used as a dummy character. ''' .ie n \{\ .tr \(bs-\*(Tr .ds -- \(bs- .if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH NEWSGROUPS 1 LOCAL .SH NAME newsgroups - a program to list unsubscribed newsgroups. .SH SYNOPSIS .B newsgroups pattern flag .SH DESCRIPTION The .I newsgroups program compares your .newsrc file with the file of active newsgroups, and prints a list of unsubscribed newsgroups matching pattern. If the second argument \*(L"flag\*(R" is present, only newsgroups not found in your .newsrc are listed, and the display is not paged. If the second argument is missing, the display is paged, and an additional list of unsubscribed newsgroups occurring in your .newsrc is printed. .SH ENVIRONMENT .IP DOTDIR 8 Where to find your .newsrc, if not in your home directory. .Sp Default: $HOME .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .SH FILES /usr/lib/news/active or a reasonable facsimile .br ${DOTDIR-{$HOME-$LOGDIR}}/.newsrc .SH SEE ALSO rn(1), newsrc(5) .SH DIAGNOSTICS .SH BUGS The flag argument is a kludge. ere is also a manual entry for rn. To get rid of newsgroups you aren't interested in, use the 'u' command. Type h for help at any time while running rn. EOH !GROK!THIS! $eunicefix newsetup chmod 755 newsetup strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } /* * close a directory. */ void closedir(dirp) register Dnewsgroups.SH 664 540 12 3076 3473757427 6603 case $CONFIG in '') . config.sh ;; esac echo "Extracting newsgroups (with variable substitutions)" $spitshell >newsgroups <newsnews <h in the middle of a multi-character command will list escape substitutions. * Typing a space to any prompt means to do the normal thing. You could spend all day reading news and never hit anything but the space bar. This particular message comes from $rnlib/newsnews. You will only see it once. You news administrator should feel free to substitute his or her own message whenever something new happens to rn, and then the file will again be displayed, just once for each person. Larry Wall sdcrdcf!lwall !GROK!THIS! $eunicefix newsnews if $test \$# -ge 2 ; then exit fi $echo $n "[Type return to continue] $c" read tmp $echo "" $echo "Unsubscribed but mentioned in .newsrc:" $sed < \$dotdir/.newsrc \\ -e "/\$1.*!/"'s/^\([^!]*\)!.*\$/\1/p' \\ -e 'd' | \\ $sort | \ng.c 664 540 12 52611 3477433001 4701 /* $Header: ng.c,v 4.3.1.3 85/05/16 16:48:09 lwall Exp $ * * $Log: ng.c,v $ * Revision 4.3.1.3 85/05/16 16:48:09 lwall * Fixed unsubsubscribe. * * Revision 4.3.1.2 85/05/13 09:29:28 lwall * Added CUSTOMLINES option. * * Revision 4.3.1.1 85/05/10 11:36:00 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:43:43 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "term.h" #include "final.h" #include "util.h" #include "artsrch.h" #include "cheat.h" #include "help.h" #include "kfile.h" #include "rcstuff.h" #include "head.h" #include "artstate.h" #include "bits.h" #include "art.h" #include "artio.h" #include "ngstuff.h" #include "intrp.h" #include "respond.h" #include "ngdata.h" #include "backpage.h" #include "rcln.h" #include "last.h" #include "search.h" #include "INTERN.h" #include "ng.h" #include "artstate.h" /* somebody has to do it */ /* art_switch() return values */ #define AS_NORM 0 #define AS_INP 1 #define AS_ASK 2 #define AS_CLEAN 3 ART_NUM recent_art = 0; /* previous article # for '-' command */ ART_NUM curr_art = 0; /* current article # */ int exit_code = NG_NORM; void ng_init() { #ifdef KILLFILES open_kfile(KF_GLOBAL); #endif #ifdef CUSTOMLINES init_compex(&hide_compex); init_compex(&page_compex); #endif } /* do newsgroup on line ng with name ngname */ /* assumes that we are chdir'ed to SPOOL, and assures that that is * still true upon return, but chdirs to SPOOL/ngname in between * * If you can understand this routine, you understand most of the program. * The basic structure is: * for each desired article * for each desired page * for each line on page * if we need another line from file * get it * if it's a header line * do special things * for each column on page * put out a character * end loop * end loop * end loop * end loop * * (Actually, the pager is in another routine.) * * The chief problem is deciding what is meant by "desired". Most of * the messiness of this routine is due to the fact that people want * to do unstructured things all the time. I have used a few judicious * goto's where I thought it improved readability. The rest of the messiness * arises from trying to be both space and time efficient. Have fun. */ int do_newsgroup(start_command) char *start_command; /* command to fake up first */ { char oldmode = mode; register long i; /* scratch */ int skipstate; /* how many unavailable articles */ /* have we skipped already? */ char *whatnext = "%sWhat next? [%s]"; #ifdef ARTSEARCH srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0); /* did they say -S? */ #endif mode = 'a'; recent_art = curr_art = 0; exit_code = NG_NORM; if (eaccess(ngdir,5)) { /* directory read protected? */ if (eaccess(ngdir,0)) { #ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s does not have a spool directory!\n", ngname) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo spool for %s!\n",ngname) FLUSH; #endif #ifdef CATCHUP catch_up(ng); #endif toread[ng] = TR_NONE; } else { #ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s is not currently accessible.\n", ngname) FLUSH; ELSE #endif #ifdef TERSE printf("\n%s not readable.\n",ngname) FLUSH; #endif toread[ng] = TR_NONE; /* make this newsgroup invisible */ /* (temporarily) */ } mode = oldmode; return -1; } /* chdir to newsgroup subdirectory */ if (chdir(ngdir)) { printf(nocd,ngdir) FLUSH; mode = oldmode; return -1; } #ifdef CACHESUBJ subj_list = Null(char **); /* no subject list till needed */ #endif /* initialize control bitmap */ if (initctl()) { mode = oldmode; return -1; } /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */ in_ng = TRUE; /* tell the world we are here */ forcelast = TRUE; /* if 0 unread, do not bomb out */ art=firstart; /* remember what newsgroup we were in for sake of posterity */ writelast(); /* see if there are any special searches to do */ #ifdef KILLFILES open_kfile(KF_LOCAL); #ifdef VERBOSE IF(verbose) kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE); ELSE #endif #ifdef TERSE kill_unwanted(firstart,"Killing...\n\n",TRUE); #endif #endif /* do they want a special top line? */ firstline = getval("FIRSTLINE",Nullch); /* custom line suppression, custom page ending */ #ifdef CUSTOMLINES if (hideline = getval("HIDELINE",Nullch)) compile(&hide_compex,hideline,TRUE,TRUE); if (pagestop = getval("PAGESTOP",Nullch)) compile(&page_compex,pagestop,TRUE,TRUE); #endif /* now read each unread article */ rc_changed = doing_ng = TRUE; /* enter the twilight zone */ skipstate = 0; /* we have not skipped anything (yet) */ checkcount = 0; /* do not checkpoint for a while */ do_fseek = FALSE; /* start 1st article at top */ if (art > lastart) art=firstart; /* init the for loop below */ for (; art<=lastart+1; ) { /* for each article */ /* do we need to "grow" the newsgroup? */ if (art > lastart || forcegrow) grow_ctl(); check_first(art); /* make sure firstart is still 1st */ if (start_command) { /* fake up an initial command? */ prompt = whatnext; strcpy(buf,start_command); free(start_command); start_command = Nullch; art = lastart+1; goto article_level; } if (art>lastart) { /* are we off the end still? */ ART_NUM ucount = 0; /* count of unread articles left */ for (i=firstart; i<=lastart; i++) if (!(ctl_read(i))) ucount++; /* count the unread articles */ #ifdef DEBUGGING /*NOSTRICT*/ if (debug && ((ART_NUM)toread[ng]) != ucount) printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount) FLUSH; #endif /*NOSTRICT*/ toread[ng] = (ART_UNREAD)ucount; /* this is perhaps pointless */ art = lastart + 1; /* keep bitmap references sane */ if (art != curr_art) { recent_art = curr_art; /* remember last article # (for '-') */ curr_art = art; /* remember this article # */ } if (erase_screen) clear(); /* clear the screen */ else fputs("\n\n",stdout) FLUSH; #ifdef VERBOSE IF(verbose) printf("End of newsgroup %s.",ngname); /* print pseudo-article */ ELSE #endif #ifdef TERSE printf("End of %s",ngname); #endif if (ucount) { printf(" (%ld article%s still unread)", (long)ucount,ucount==1?nullstr:"s"); } else { if (!forcelast) goto cleanup; /* actually exit newsgroup */ } prompt = whatnext; #ifdef ARTSEARCH srchahead = 0; /* no more subject search mode */ #endif fputs("\n\n",stdout) FLUSH; skipstate = 0; /* back to none skipped */ } else if (!reread && was_read(art)) { /* has this article been read? */ art++; /* then skip it */ continue; } else if (!reread && !was_read(art) && artopen(art) == Nullfp) { /* never read it, & cannot find it? */ if (errno != ENOENT) { /* has it not been deleted? */ #ifdef VERBOSE IF(verbose) printf("\n(Article %ld exists but is unreadable.)\n", (long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(%ld unreadable.)\n",(long)art) FLUSH; #endif skipstate = 0; sleep(2); } switch(skipstate++) { case 0: clear(); #ifdef VERBOSE IF(verbose) fputs("Skipping unavailable article",stdout); ELSE #endif #ifdef TERSE fputs("Skipping",stdout); #endif for (i = just_a_sec/3; i; --i) putchar(PC); fflush(stdout); sleep(1); break; case 1: fputs("..",stdout); fflush(stdout); break; default: putchar('.'); fflush(stdout); #define READDIR #ifdef READDIR { /* fast skip patch */ ART_NUM newart; if (! (newart=getngmin(".",art))) newart = lastart+1; for (i=art; i=0; --i) if (subj_list[i]) free(subj_list[i]); #ifndef lint free((char*)subj_list); #endif lint } #endif write_rc(); /* and update .newsrc */ rc_changed = FALSE; /* tell sig_catcher it is ok */ if (chdir(spool)) { printf(nocd,spool) FLUSH; sig_catcher(0); } #ifdef KILLFILES if (localkfp) { fclose(localkfp); localkfp = Nullfp; } #endif mode = oldmode; return exit_code; } /* Whew! */ /* decide what to do at the end of an article */ int art_switch() { register ART_NUM i; setdef(buf,dfltcmd); #ifdef VERIFY printcmd(); #endif switch (*buf) { case 'p': /* find previous unread article */ do { if (art <= firstart) break; art--; } while (was_read(art) || artopen(art) == Nullfp); #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case 'P': /* goto previous article */ if (art > absfirst) art--; else { #ifdef VERBOSE IF(verbose) fputs("\n\ There are no articles prior to this one.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNo previous articles\n",stdout) FLUSH; #endif return AS_ASK; } reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '-': if (recent_art) { art = recent_art; reread = TRUE; #ifdef ARTSEARCH srchahead = -(srchahead != 0); #endif return AS_NORM; } else { exit_code = NG_MINUS; return AS_CLEAN; } case 'n': /* find next unread article? */ if (art > lastart) { if (toread[ng]) art = firstart; else return AS_CLEAN; } #ifdef ARTSEARCH else if (scanon && srchahead) { *buf = Ctl('n'); goto normal_search; } #endif else art++; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case 'N': /* goto next article */ if (art > lastart) art = absfirst; else art++; if (art <= lastart) reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '$': art = lastart+1; forcelast = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '1': case '2': case '3': /* goto specified article */ case '4': case '5': case '6': /* or do something with a range */ case '7': case '8': case '9': case '.': forcelast = TRUE; switch (numnum()) { case NN_INP: return AS_INP; case NN_ASK: return AS_ASK; case NN_REREAD: reread = TRUE; #ifdef ARTSEARCH if (srchahead) srchahead = -1; #endif break; case NN_NORM: if (was_read(art)) { art = firstart; pad(just_a_sec/3); } else return AS_ASK; break; } return AS_NORM; case Ctl('k'): edit_kfile(); return AS_ASK; case 'K': case 'k': case Ctl('n'): case Ctl('p'): case '/': case '?': #ifdef ARTSEARCH normal_search: { /* search for article by pattern */ char cmd = *buf; reread = TRUE; /* assume this */ switch (art_search(buf, (sizeof buf), TRUE)) { case SRCH_ERROR: return AS_ASK; case SRCH_ABORT: return AS_INP; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif art = curr_art; /* restore to current article */ return AS_ASK; case SRCH_DONE: fputs("done\n",stdout) FLUSH; pad(just_a_sec/3); /* 1/3 second */ if (srchahead) art = firstart; else art = curr_art; reread = FALSE; return AS_NORM; case SRCH_SUBJDONE: #ifdef UNDEF fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH; pad(just_a_sec/3); /* 1/3 second */ #endif art = firstart; reread = FALSE; return AS_NORM; case SRCH_NOTFOUND: fputs("\n\n\n\nNot found.\n",stdout) FLUSH; art = curr_art; /* restore to current article */ return AS_ASK; case SRCH_FOUND: if (cmd == Ctl('n') || cmd == Ctl('p')) oldsubject = TRUE; break; } return AS_NORM; } #else buf[1] = '\0'; notincl(buf); return AS_ASK; #endif case 'u': /* unsubscribe from this newsgroup? */ rcchar[ng] = NEGCHAR; return AS_CLEAN; case 'M': #ifdef DELAYMARK if (art <= lastart) { delay_unmark(art); printf("\nArticle %ld will return.\n",(long)art) FLUSH; } #else notincl("M"); #endif return AS_ASK; case 'm': if (art <= lastart) { unmark_as_read(art); printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH; } return AS_ASK; case 'c': /* catch up */ reask_catchup: #ifdef VERBOSE IF(verbose) in_char("\nDo you really want to mark everything as read? [yn] "); ELSE #endif #ifdef TERSE in_char("\nReally? [ynh] "); #endif putchar('\n') FLUSH; setdef(buf,"y"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\ Type y or SP to mark all articles as read.\n\ Type n to leave articles marked as they are.\n\ Type u to mark everything read and unsubscribe.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\ y or SP to mark all read.\n\ n to forget it.\n\ u to mark all and unsubscribe.\n\ ",stdout) FLUSH; #endif goto reask_catchup; } else if (*buf == 'n' || *buf == 'q') { return AS_ASK; } else if (*buf != 'y' && *buf != 'u') { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_catchup; } for (i = firstart; i <= lastart; i++) { ctl_set(i); /* mark as read */ } #ifdef DELAYMARK if (dmfp) yankback(); #endif if (*buf == 'u') { rcchar[ng] = NEGCHAR; return AS_CLEAN; } art = lastart+1; forcelast = FALSE; return AS_NORM; case 'Q': exit_code = NG_ASK; /* FALL THROUGH */ case 'q': /* go back up to newsgroup level? */ return AS_CLEAN; case 'j': putchar('\n') FLUSH; if (art <= lastart) mark_as_read(art); return AS_ASK; case 'h': { /* help? */ int cmd; if ((cmd = help_art()) > 0) pushchar(cmd); 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 return AS_ASK; case '=': { char tmpbuf[256]; ART_NUM oldart = art; int cmd; char *subjline = getval("SUBJLINE",Nullch); #ifndef CACHESUBJ char *s; #endif page_init(); #ifdef CACHESUBJ if (!subj_list) fetchsubj(art,TRUE,FALSE); #endif for (i=firstart; i<=lastart && !int_count; i++) { #ifdef CACHESUBJ if (!was_read(i) && (subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) && *subj_list[OFFSET(i)] ) { sprintf(tmpbuf,"%5ld ", i); if (subjline) { art = i; interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline); } else safecpy(tmpbuf + 6, subj_list[OFFSET(i)], (sizeof tmpbuf) - 6); if (cmd = print_lines(tmpbuf,NOMARKING)) { if (cmd > 0) pushchar(cmd); break; } } #else if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) { sprintf(tmpbuf,"%5ld ", i); if (subjline) { /* probably fetches it again! */ art = i; interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline); } else safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6); if (cmd = print_lines(tmpbuf,NOMARKING)) { if (cmd > 0) pushchar(cmd); break; } } #endif } int_count = 0; art = oldart; return AS_ASK; } case '^': art = firstart; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; #if defined(CACHESUBJ) && defined(DEBUGGING) case 'D': printf("\nFirst article: %ld\n",(long)firstart) FLUSH; if (!subj_list) fetchsubj(art,TRUE,FALSE); if (subj_list != Null(char **)) { for (i=1; i<=lastart && !int_count; i++) { if (subj_list[OFFSET(i)]) printf("%5ld %c %s\n", i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH; } } int_count = 0; return AS_ASK; #endif case 'v': if (art <= lastart) { reread = TRUE; do_hiding = FALSE; } return AS_NORM; #ifdef ROTATION case Ctl('x'): #endif case Ctl('r'): #ifdef ROTATION rotate = (*buf==Ctl('x')); #endif if (art <= lastart) reread = TRUE; return AS_NORM; #ifdef ROTATION case 'X': rotate = !rotate; /* FALL THROUGH */ #else case Ctl('x'): case 'x': case 'X': notincl("x"); return AS_ASK; #endif case 'l': case Ctl('l'): /* refresh screen */ if (art <= lastart) { reread = TRUE; clear(); do_fseek = TRUE; artline = topline; if (artline < 0) artline = 0; } return AS_NORM; case 'b': case Ctl('b'): /* back up a page */ if (art <= lastart) { ART_LINE target; reread = TRUE; clear(); do_fseek = TRUE; target = topline - (LINES - 2); artline = topline; do { artline--; } while (artline >= 0 && artline > target && vrdary(artline-1) >= 0); topline = artline; if (artline < 0) artline = 0; } return AS_NORM; case '!': /* shell escape */ if (escapade()) return AS_INP; return AS_ASK; case 'C': { cancel_article(); return AS_ASK; } case 'R': case 'r': { /* reply? */ reply(); return AS_ASK; } case 'F': case 'f': { /* followup command */ followup(); forcegrow = TRUE; /* recalculate lastart */ return AS_ASK; } case '|': case 'w': case 'W': case 's': case 'S': /* save command */ if (save_article() == SAVE_ABORT) return AS_INP; return AS_ASK; #ifdef DELAYMARK case 'Y': /* yank back M articles */ yankback(); art = firstart; /* from the beginning */ return AS_NORM; /* pretend nothing happened */ #endif #ifdef STRICTCR case '\n': fputs(badcr,stdout) FLUSH; return AS_ASK; #endif default: printf("\n%s",hforhelp) FLUSH; settle_down(); return AS_ASK; } } #ifdef MAILCALL /* see if there is any mail */ void setmail() { if (! (mailcount++)) { char *mailfile = filexp(getval("MAILFILE",MAILFILE)); if (stat(mailfile,&filestat) < 0 || !filestat.st_size || filestat.st_atime > filestat.st_mtime) mailcall = nullstr; else mailcall = "(Mail) "; } mailcount %= 10; /* check every 10 articles */ } #endif void setdfltcmd() { if (toread[ng]) { #ifdef ARTSEARCH if (srchahead) dfltcmd = "^Nnpq"; else #endif dfltcmd = "npq"; } else { if (art > lastart) dfltcmd = "qnp"; else dfltcmd = "npq"; } } it is ok */ if (chdir(spool)) { printf(nocd,spool) FLUSH; sig_catcher(0); } #ifdef KILLFILES if (localkfpng.h 664 540 12 1654 3473756702 4703 /* $Header: ng.h,v 4.3 85/05/01 11:44:29 lwall Exp $ * * $Log: ng.h,v $ * Revision 4.3 85/05/01 11:44:29 lwall * Baseline for release with 4.3bsd. * */ EXT ART_NUM art INIT(0); /* current or prospective article # */ EXT int checkcount INIT(0); /* how many articles have we read */ /* in the current newsgroup since */ /* the last checkpoint? */ EXT int docheckwhen INIT(20); /* how often to do checkpoint */ #ifdef MAILCALL EXT int mailcount INIT(0); /* check for mail when 0 mod 10 */ #endif EXT char *mailcall INIT(nullstr); EXT bool forcelast INIT(FALSE); /* ought we show "End of newsgroup"? */ EXT bool forcegrow INIT(FALSE); /* do we want to recalculate size */ /* of newsgroup, e.g. after posting? */ #define NG_ERROR -1 #define NG_NORM 0 #define NG_ASK 1 #define NG_MINUS 2 void ng_init(); int do_newsgroup(); int art_switch(); #ifdef MAILCALL void setmail(); #endif void setdfltcmd(); printf(nocd,spool) FLUSH; sig_catcher(0); } #ifdef KILLFILES if (localkfpng.help.SH 664 540 12 3361 3473757412 5711 case $CONFIG in '') . config.sh ;; esac echo "Extracting ng.help (with variable substitutions)" $spitshell >ng.help <. u Unsubscribe from this newsgroup. c Catch up (mark this newsgroup all read). n Go to the next newsgroup with unread news. N Go to the next newsgroup. p Go to the previous newsgroup with unread news. P Go to the previous newsgroup. - Go to the previously displayed newsgroup. 1 Go to the first newsgroup. ^ Go to the first newsgroup with unread news. $ Go to the last newsgroup. g name Go to the named newsgroup. Subscribe to new newsgroups this way too. /pat Search forward for newsgroup matching pattern. ?pat Search backward for newsgroup matching pattern. (Use * and ? style patterns. Append r to include read newsgroups.) l pat List unsubscribed newsgroups containing pattern. m name Move named newsgroup elsewhere (no name moves current newsgroup). o pat Only display newsgroups matching pattern. Omit pat to unrestrict. a pat Like o, but also scans for unsubscribed newsgroups matching pattern. L List current .newsrc. & Print current command-line switch settings. &switch {switch} Set (or unset) more command-line switches. && Print current macro definitions. &&def Define a new macro. !cmd Shell escape. q Quit rn. ^K Edit the global KILL file. Use commands like /pattern/j to suppress pattern in every newsgroup. v Print version. EOT !GROK!THIS! $eunicefix ng.help chmod 755 ng.help us articles\n",stdout) FLUSH; #endif return AS_ASK; } reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '-': if (recent_art) { art = recent_art; reread = TRUE; #ifdef ARTSEARCH srchahead = -(srchahead != 0); #endif ngdata.c 664 540 12 10345 3473757337 5552 /* $Header: ngdata.c,v 4.3 85/05/01 11:44:38 lwall Exp $ * * $Log: ngdata.c,v $ * Revision 4.3 85/05/01 11:44:38 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "ndir.h" #include "rcstuff.h" #include "rn.h" #include "intrp.h" #include "final.h" #include "rcln.h" #include "INTERN.h" #include "ngdata.h" void ngdata_init() { /* The following is only for systems that do not zero globals properly */ #ifdef ZEROGLOB # ifdef CACHEFIRST for (i=0; i= 0) { if (softptr[num] != oldsoft) { softmisses++; writesoft = TRUE; } } else { softptr[num] = 0; if (rcchar[num] == ':') /* unsubscribe quietly */ rcchar[num] = NEGCHAR; return TR_BOGUS; /* well, not so quietly, actually */ } #ifdef DEBUGGING if (debug & DEB_SOFT_POINTERS) { printf("Should be %ld\n",(long)softptr[num]) FLUSH; } #endif #ifdef MININACT { register char *s; ART_NUM tmp; for (s=tmpbuf+len+1; isdigit(*s); s++) ; if (tmp = atol(s)) #ifdef CACHEFIRST abs1st[num] = tmp; #else abs1st = tmp; #endif } #endif return atol(tmpbuf+len+1); } ACT_POS findact(outbuf,nam,len,suggestion) char *outbuf; char *nam; int len; long suggestion; { ACT_POS retval; fseek(actfp,100000L,1); /* hopefully this forces a reread */ if (suggestion == 0L || fseek(actfp,suggestion,0) < 0 || fgets(outbuf,80,actfp) == Nullch || outbuf[len] != ' ' || strnNE(outbuf,nam,len)) { #ifdef DEBUGGING if (debug & DEB_SOFT_POINTERS) printf("Missed, looking for %s in %sLen = %d\n",nam,outbuf,len) FLUSH; #endif fseek(actfp,0L,0); #ifndef lint retval = (ACT_POS)ftell(actfp); #else retval = Null(ACT_POS); #endif lint while (fgets(outbuf,80,actfp) != Nullch) { if (outbuf[len] == ' ' && strnEQ(outbuf,nam,len)) return retval; #ifndef lint retval = (ACT_POS) ftell(actfp); #endif lint } return (ACT_POS) -1; /* well, not so quietly, actually */ } else #ifndef lint return (ACT_POS) suggestion; #else return retval; #endif lint /*NOTREACHED*/ } /* determine the absolutely first existing article number */ ART_NUM getabsfirst(ngnum,ngsize) register NG_NUM ngnum; ART_NUM ngsize; { register ART_NUM a1st; #ifndef MININACT char dirname[MAXFILENAME]; #endif #ifdef CACHEFIRST if (a1st = abs1st[ngnum]) return a1st; #endif #ifdef MININACT getngsize(ngnum); # ifdef CACHEFIRST return abs1st[ngnum]; # else return abs1st; # endif #else not MININACT sprintf(dirname,"%s/%s",spool,getngdir(rcline[ngnum])); a1st = getngmin(dirname,0L); if (!a1st) /* nothing there at all? */ a1st = ngsize+1; /* aim them at end of newsgroup */ # ifdef CACHEFIRST abs1st[ngnum] = a1st; # endif return a1st; #endif MININACT } /* scan a directory for minimum article number greater than floor */ ART_NUM getngmin(dirname,floor) char *dirname; ART_NUM floor; { register DIR *dirp; register struct direct *dp; register ART_NUM min = 1000000; register ART_NUM maybe; register char *p; char tmpbuf[128]; dirp = opendir(dirname); if (!dirp) return 0; while ((dp = readdir(dirp)) != Null(struct direct *)) { if ((maybe = atol(dp->d_name)) < min && maybe > floor) { for (p = dp->d_name; *p; p++) if (!isdigit(*p)) goto nope; if (*dirname == '.' && !dirname[1]) stat(dp->d_name, &filestat); else { sprintf(tmpbuf,"%s/%s",dirname,dp->d_name); stat(tmpbuf, &filestat); } if (! (filestat.st_mode & S_IFDIR)) min = maybe; } nope: ; } closedir(dirp); return min==1000000 ? 0 : min; } reread = FALSE; return AS_NORM; case SRCH_NOTFOUND: fputs("\n\n\n\nNot found.\n",stdout) FLUSH; art = curr_art; /* restore to current article */ return AS_ASK; case SRCH_FOUND: if (cmd == Ctl('n') || cmd == Ctl('p')) oldsubject = TRUE; break; ngdata.h 664 540 12 1134 3473757462 5532 /* $Header: ngdata.h,v 4.3 85/05/01 11:44:48 lwall Exp $ * * $Log: ngdata.h,v $ * Revision 4.3 85/05/01 11:44:48 lwall * Baseline for release with 4.3bsd. * */ EXT FILE *actfp INIT(Nullfp); /* the active file */ EXT bool writesoft INIT(FALSE); /* rewrite the soft pointer file? */ EXT int softtries INIT(0), softmisses INIT(0); #ifdef CACHEFIRST EXT ART_NUM abs1st[MAXRCLINE]; /* 1st real article in newsgroup */ #else # ifdef MININACT EXT ART_NUM abs1st INIT(0); # endif #endif void ngdata_init(); ART_NUM getngsize(); ACT_POS findact(); ART_NUM getabsfirst(); ART_NUM getngmin(); actfp = fopen(filexp(ACTIVE),"r"); if (actfp == Nullfp) { printf(cantopen,filexp(ACTIVE)) FLUSH; finalize(1); } } /* find the maximum article number of a newsgroup */ ART_NUM getngsize(num) register NG_NUM num; { register int len; register char *nam; char tmpbuf[80]; ART_POS oldsoft; nam = rcline[num]; len = rcnums[num] - 1; softtries++; #ifdef DEBUGGING if (debug & DEngsrch.c 664 540 12 5322 3473757353 5555 /* $Header: ngsrch.c,v 4.3 85/05/01 11:44:51 lwall Exp $ * * $Log: ngsrch.c,v $ * Revision 4.3 85/05/01 11:44:51 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "rcstuff.h" #include "final.h" #include "search.h" #include "rn.h" #include "util.h" #include "term.h" #include "rcln.h" #include "INTERN.h" #include "ngsrch.h" #ifdef NGSORONLY COMPEX ngcompex; #endif void ngsrch_init() { #ifdef ZEROGLOB init_compex(&ngcompex); #endif /* ZEROGLOB */ ; } #ifdef NGSEARCH int ng_search(patbuf,get_cmd) char *patbuf; /* if patbuf != buf, get_cmd must */ int get_cmd; /* be set to FALSE!!! */ { char *pattern; /* unparsed pattern */ register char cmdchr = *patbuf; /* what kind of search? */ register char *s; bool backward = cmdchr == '?'; /* direction of search */ int_count = 0; if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return NGS_ABORT; for (pattern = patbuf+1; *pattern == ' '; pattern++) ; if (*pattern) { ng_doread = FALSE; } s = rindex(pattern,cmdchr); if (s != Nullch && *(s-1) != '\\') { *s++ = '\0'; if (index(s,'r') != Nullch) ng_doread = TRUE; } if ((s = ng_comp(&ngcompex,pattern,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; return NGS_ABORT; } fputs("\nSearching...",stdout) FLUSH; /* give them something to read */ fflush(stdout); for (;;) { if (int_count) { int_count = 0; return NGS_INTR; } if (backward) { if (ng > 0) --ng; else ng = nextrcline; } else { if (ng >= nextrcline) ng = 0; else ++ng; } if (ng == current_ng) return NGS_NOTFOUND; if (ng == nextrcline || toread[ng] < TR_NONE || !ng_wanted()) continue; if (toread[ng] == TR_NONE) set_toread(ng); if (toread[ng] > TR_NONE) return NGS_FOUND; else if (toread[ng] == TR_NONE) if (ng_doread) return NGS_FOUND; else printf("\n[0 unread in %s--skipping]",rcline[ng]) FLUSH; } } bool ng_wanted() { return execute(&ngcompex,rcline[ng]) != Nullch; } #endif #ifdef NGSORONLY char * ng_comp(compex,pattern,RE,fold) COMPEX *compex; char *pattern; bool RE; bool fold; { char ng_pattern[128]; register char *s = pattern, *d = ng_pattern; if (!*s) return Nullch; /* reuse old pattern */ for (; *s; s++) { if (*s == '.') { *d++ = '\\'; *d++ = *s; } else if (*s == '?') { *d++ = '.'; } else if (*s == '*') { *d++ = '.'; *d++ = *s; } else if (strnEQ(s,"all",3)) { *d++ = '.'; *d++ = '*'; s += 2; } else *d++ = *s; } *d = '\0'; return compile(compex,ng_pattern,RE,fold); } #endif determine the absolutely first existing article number */ ART_NUM getabsfirst(ngnum,ngsize) register NG_NUM ngnum; ART_NUM ngsize; { register ART_NUM a1st; #ifndef MININACT char dirname[MAXFILENAME]; #endif #ifdef CACHEFIRST if (a1st = abs1st[ngnum]) return a1st; #endif #ifdef MININACTngsrch.h 664 540 12 732 3473757467 5550 /* $Header: ngsrch.h,v 4.3 85/05/01 11:44:56 lwall Exp $ * * $Log: ngsrch.h,v $ * Revision 4.3 85/05/01 11:44:56 lwall * Baseline for release with 4.3bsd. * */ #ifdef NGSEARCH #define NGS_ABORT 0 #define NGS_FOUND 1 #define NGS_INTR 2 #define NGS_NOTFOUND 3 EXT bool ng_doread INIT(FALSE); /* search read newsgroups? */ #endif void ngsrch_init(); #ifdef NGSEARCH int ng_search(); bool ng_wanted(); #endif #ifdef NGSORONLY char *ng_comp(); #endif compex(&ngcompex); #endif /* ZEROGLOB ngstuff.c 664 540 12 14052 3473762303 5754 /* $Header: ngstuff.c,v 4.3.1.2 85/05/10 14:31:52 lwall Exp $ * * $Log: ngstuff.c,v $ * Revision 4.3.1.2 85/05/10 14:31:52 lwall * Prevented "Junked" or "Marked unread" when no state change. * * Revision 4.3.1.1 85/05/10 11:36:45 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:45:03 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "ng.h" #include "bits.h" #include "intrp.h" #include "cheat.h" #include "head.h" #include "final.h" #include "sw.h" #include "INTERN.h" #include "ngstuff.h" void ngstuff_init() { ; } /* do a shell escape */ int escapade() { register char *s; bool interactive = (buf[1] == FINISHCMD); bool docd; char whereiam[256]; if (!finish_command(interactive)) /* get remainder of command */ return -1; s = buf+1; docd = *s != '!'; if (!docd) { s++; } else { getwd(whereiam); if (chdir(cwd)) { printf(nocd,cwd) 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(Nullch,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]) pr_switches(); #ifdef PUSHBACK else if (buf[1] == '&') { if (!buf[2]) { page_init(); show_macros(); } else { char tmpbuf[LBUFLEN]; register char *s; for (s=buf+2; isspace(*s); s++); mac_line(s,tmpbuf,(sizeof tmpbuf)); } } #endif else { bool docd = (instr(buf,"-d") != Nullch); char whereami[256]; if (docd) getwd(whereami); sw_list(buf+1); if (docd) { cwd_check(); if (chdir(whereami)) { /* -d does chdirs */ printf(nocd,whereami) FLUSH; sig_catcher(0); } } } return 0; } /* process range commands */ int numnum() { ART_NUM min, max; char *cmdlst = Nullch; register char *s, *c; ART_NUM oldart = art; char tmpbuf[LBUFLEN]; bool justone = TRUE; /* assume only one article */ if (!finish_command(TRUE)) /* get rest of command */ return NN_INP; if (lastart < 1) { fputs("\nNo articles\n",stdout) FLUSH; return NN_ASK; } #ifdef ARTSRCH if (srchahead) srchahead = -1; #endif for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++) if (!isdigit(*s)) justone = FALSE; if (*s) { cmdlst = savestr(s); justone = FALSE; } else if (!justone) cmdlst = savestr("m"); *s++ = ','; *s = '\0'; safecpy(tmpbuf,buf,LBUFLEN); for (s = tmpbuf; c = index(s,','); s = ++c) { *c = '\0'; if (*s == '.') min = oldart; else min = atol(s); if (minlastart) { max = lastart; if (min > max) min = max; printf("(Last article is %ld)\n",(long)lastart) FLUSH; pad(just_a_sec/3); } if (max < min) { fputs("\nBad range\n",stdout) FLUSH; if (cmdlst) free(cmdlst); return NN_ASK; } if (justone) { art = min; return NN_REREAD; } check_first(min); for (art=min; art<=max; art++) { if (perform(cmdlst,TRUE)) { #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif if (cmdlst) free(cmdlst); return NN_ASK; } } } art = oldart; if (cmdlst) free(cmdlst); return NN_NORM; } int perform(cmdlst,toplevel) register char *cmdlst; int toplevel; { register int ch; if (toplevel) { printf("%-6ld",art); fflush(stdout); } for (; ch = *cmdlst; cmdlst++) { if (isspace(ch) || ch == ':') continue; if (ch == 'j') { if (!was_read(art)) { mark_as_read(art); #ifdef VERBOSE IF(verbose) fputs("\tJunked",stdout); #endif } } else if (ch == 'm') { if (was_read(art)) { unmark_as_read(art); #ifdef VERBOSE IF(verbose) fputs("\tMarked unread",stdout); #endif } } else if (ch == 'M') { #ifdef DELAYMARK delay_unmark(art); #ifdef VERBOSE IF(verbose) fputs("\tWill return",stdout); #endif #else notincl("M"); return -1; #endif } else if (ch == '=') { printf("\t%s",fetchsubj(art,FALSE,FALSE)); #ifdef VERBOSE IF(verbose) ; ELSE #endif putchar('\n') FLUSH; /* ghad! */ } else if (ch == 'C') { #ifdef ASYNC_PARSE printf("\t%sancelled",(cancel_article() ? "Not c" : "C")); #else notincl("C"); return -1; #endif } else if (ch == '%') { #ifdef ASYNC_PARSE char tmpbuf[512]; cmdlst = dointerp(tmpbuf, (sizeof tmpbuf), cmdlst,":"); if (*cmdlst != ':') --cmdlst; if (perform(tmpbuf,FALSE)) return -1; #else notincl("%"); return -1; #endif } else if (index("!&sSwW|",ch)) { cmdlst = cpytill(buf,cmdlst,':') - 1; /* we now have the command in buf */ if (ch == '!') { escapade(); #ifdef VERBOSE IF(verbose) fputs("\tShell escaped",stdout); #endif } else if (ch == '&') { switcheroo(); #ifdef VERBOSE IF(verbose) fputs("\tSwitched",stdout); #endif } else { putchar('\t'); save_article(); } } else { printf("\t???%s\n",cmdlst); return -1; } #ifdef VERBOSE fflush(stdout); #endif } if (toplevel) { #ifdef VERBOSE IF(verbose) putchar('\n') FLUSH; ELSE #endif #ifdef TERSE putchar(' '); #endif } return 0; } fputs("\nNo articles\n",stdout) FLUSH; return NN_ASK; } #ifdef ARTSRCH if (srchahead) srchahead = -1; #endif for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++) if (!isdigit(*s)) justone = FALSE; if (*s) { cmdlst = savestr(s); justone = FALSE; } else if (!justone) cmdlst = savestr("m"); *s++ = ','; *s = '\0'; safecpy(tmpbuf,buf,LBUFLEN); for (s = tmpbuf; c = index(s,','); s = ++c) { *c = '\0'; if (*s == 'ngstuff.h 664 540 12 512 3473757501 5721 /* $Header: ngstuff.h,v 4.3 85/05/01 11:45:12 lwall Exp $ * * $Log: ngstuff.h,v $ * Revision 4.3 85/05/01 11:45:12 lwall * Baseline for release with 4.3bsd. * */ #define NN_NORM 0 #define NN_INP 1 #define NN_REREAD 2 #define NN_ASK 3 void ngstuff_init(); int escapade(); int switcheroo(); int numnum(); int perform(); printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif if (cmdlst) free(cmnorm.saver.SH 664 540 12 1716 3477433044 6446 case $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 \tJunked",stdout); #endif } } else if (ch =only.c 664 540 12 4334 3473757376 5261 /* $Header: only.c,v 4.3 85/05/01 11:45:21 lwall Exp $ * * $Log: only.c,v $ * Revision 4.3 85/05/01 11:45:21 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "util.h" #include "final.h" #include "ngsrch.h" #include "INTERN.h" #include "only.h" void only_init() { ; } void setngtodo(pat) char *pat; { char *s; #ifdef ONLY if (!*pat) return; if (maxngtodo < NGMAX) { ngtodo[maxngtodo] = savestr(pat); #ifdef SPEEDOVERMEM #ifndef lint compextodo[maxngtodo] = (COMPEX*)safemalloc(sizeof(COMPEX)); #endif lint init_compex(compextodo[maxngtodo]); compile(compextodo[maxngtodo],pat,TRUE,TRUE); if ((s = ng_comp(compextodo[maxngtodo],pat,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; finalize(1); } #endif maxngtodo++; } #else notincl("o"); #endif } /* if command line list is non-null, is this newsgroup wanted? */ bool inlist(ngnam) char *ngnam; { #ifdef ONLY register int i; #ifdef SPEEDOVERMEM if (maxngtodo == 0) return TRUE; for (i=0; i 1 ? ", etc." : nullstr) FLUSH; ELSE #endif #ifdef TERSE fputs("\nExiting \"only\".\n",stdout) FLUSH; #endif for (whicharg = 0; whicharg < maxngtodo; whicharg++) { free(ngtodo[whicharg]); #ifdef SPEEDOVERMEM free_compex(compextodo[whicharg]); #ifndef lint free((char*)compextodo[whicharg]); #endif lint #endif } maxngtodo = 0; } } #endif stdout); #endif } else { putchar('\t'); save_article(); } } else { printf("\t???%s\n",cmdlst); return -1; } #ifdef VERBOSE fflush(stdout); #endif } if (toplevel) { #ifdef VERBOSE IF(verbose) putchar('\n') FLUSH; ELSE #endif #ifdef TERSE puonly.h 664 540 12 1226 3473757457 5263 /* $Header: only.h,v 4.3 85/05/01 11:45:27 lwall Exp $ * * $Log: only.h,v $ * Revision 4.3 85/05/01 11:45:27 lwall * Baseline for release with 4.3bsd. * */ #ifndef NBRA #include "search.h" #endif #ifdef ONLY EXT char *ngtodo[NGMAX]; /* restrictions in effect */ # ifdef SPEEDOVERMEM EXT COMPEX *compextodo[NGMAX]; /* restrictions in compiled form */ # endif #endif EXT int maxngtodo INIT(0); /* 0 => no restrictions */ /* >0 => # of entries in ngtodo */ void only_init(); bool inlist(); /* return TRUE if ngname is in command line list */ /* or if there was no list */ void setngtodo(); #ifdef ONLY void end_only(); #endif maxngtodo],pat,TRUE,TRUE); if ((s = ng_comp(compextodo[maxngtodo],pat,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; finalize(1); } #endif maxngtodo++; } #else notincl("o"); #endif } /* if command line list is non-null, is this newsgroup wanted? */ bool inlist(ngnam) char *ngnam; { #ifdef ONLpager.help.SH 664 540 12 3141 3473757422 6400 case $CONFIG in '') . config.sh ;; esac echo "Extracting pager.help (with variable substitutions)" $spitshell >pager.help < 1 ? ", etc." : nullstr) FLUSH; ELSE #endif #ifdef TERSE fputs("\nExiting \"only\".\n",stdout) FLUSH; #endif for (whicharg = 0; whicharg < patchlevel 664 540 12 14 3501473244 6112 Patch #: 21 in '') . config.sh ;; esac echo "Extracting pager.help (with variable substitutions)" $spitshell >pager.help < ngmax[ngnum] + 10 /* allow for incoming articles */ ) { printf("\nCorrupt Xref line!!! %ld --> %s(1..%ld)\n", artnum,ngnam, ngmax[ngnum]) FLUSH; paranoid = TRUE; /* paranoia reigns supreme */ return -1; /* hope this was the first newsgroup */ } #endif if (toread[ngnum] == TR_BOGUS) return 0; #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("%ld->\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif s = rcline[ngnum] + rcnums[ngnum]; while (*s == ' ') s++; /* skip spaces */ t = s; while (isdigit(*s) && artnum >= (min = atol(s))) { /* while it might have been read */ for (t = s; isdigit(*t); t++) ; /* skip number */ if (*t == '-') { /* is it a range? */ t++; /* skip to next number */ if (artnum <= (max = atol(t))) return 0; /* it is in range => already read */ lastnum = max; /* remember it */ maxt = t; /* remember position in case we */ /* want to overwrite the max */ while (isdigit(*t)) t++; /* skip second number */ } else { if (artnum == min) /* explicitly a read article? */ return 0; lastnum = min; /* remember what the number was */ maxt = Nullch; /* last one was not a range */ } while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ s = t; } /* we have not read it, so insert the article number before s */ morenum = isdigit(*s); /* will it need a comma after? */ *(rcline[ngnum] + rcnums[ngnum] - 1) = rcchar[ngnum]; mbuf = safemalloc((MEM_SIZE)(strlen(s) + (s-rcline[ngnum]) + 8)); strcpy(mbuf,rcline[ngnum]); /* make new rc line */ if (maxt && lastnum && artnum == lastnum+1) /* can we just extend last range? */ t = mbuf + (maxt-rcline[ngnum]);/* then overwrite previous max */ else { t = mbuf + (t-rcline[ngnum]); /* point t into new line instead */ if (lastnum) { /* have we parsed any line? */ if (!morenum) /* are we adding to the tail? */ *t++ = ','; /* supply comma before */ if (!maxt && artnum == lastnum+1 && *(t-1) == ',') /* adjacent singletons? */ *(t-1) = '-'; /* turn them into a range */ } } if (morenum) { /* is there more to life? */ if (min == artnum+1) { /* can we consolidate further? */ bool range_before = (*(t-1) == '-'); bool range_after; char *nextmax; for (nextmax = s; isdigit(*nextmax); nextmax++) ; range_after = *nextmax++ == '-'; if (range_before) *t = '\0'; /* artnum is redundant */ else sprintf(t,"%ld-",(long)artnum);/* artnum will be new min */ if (range_after) s = nextmax; /* *s is redundant */ /* else s = s */ /* *s is new max */ } else sprintf(t,"%ld,",(long)artnum); /* put the number and comma */ } else sprintf(t,"%ld",(long)artnum); /* put the number there (wherever) */ strcat(t,s); /* copy remainder of line */ #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("%s\n",mbuf) FLUSH; } #endif free(rcline[ngnum]); rcline[ngnum] = mbuf; /* pull the switcheroo */ *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0'; /* wipe out : or ! */ if (toread[ngnum] > TR_NONE) /* lest we turn unsub into bogus */ --toread[ngnum]; return 0; } #ifdef MCHASE /* delete an article number from a newsgroup, if it is there */ void subartnum(artnum,ngnam) register ART_NUM artnum; char *ngnam; { register NG_NUM ngnum = find_ng(ngnam); register char *s, *t; register ART_NUM min, max; char *mbuf; int curlen; if (!artnum) return; if (ngnum == nextrcline || !rcnums[ngnum]) return; /* not found in newsrc? */ #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("%ld<-\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif s = rcline[ngnum] + rcnums[ngnum]; while (*s == ' ') s++; /* skip spaces */ /* a little optimization, since it is almost always the last number */ for (t=s; *t; t++) ; /* find end of string */ curlen = t-rcline[ngnum]; for (t--; isdigit(*t); t--) ; /* find previous delim */ if (*t == ',' && atol(t+1) == artnum) { *t = '\0'; if (toread[ngnum] >= TR_NONE) ++toread[ngnum]; #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) printf("%s%c %s\n",rcline[ngnum],rcchar[ngnum],s) 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; } *(rcline[ngnum] + rcnums[ngnum] - 1) = rcchar[ngnum]; mbuf = safemalloc((MEM_SIZE)(curlen + (artnum?15:2))); *s = '\0'; strcpy(mbuf,rcline[ngnum]); /* make new rc line */ s = mbuf + (s-rcline[ngnum]); /* point s into mbuf now */ if (artnum) { /* split into two ranges? */ prange(s,min,artnum-1); s += strlen(s); *s++ = ','; prange(s,artnum+1,max); } else /* only one range */ prange(s,min,max); s += strlen(s); strcpy(s,t); /* copy remainder over */ #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("%s\n",mbuf) FLUSH; } #endif free(rcline[ngnum]); rcline[ngnum] = mbuf; /* pull the switcheroo */ *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0'; /* wipe out : or ! */ if (toread[ngnum] >= TR_NONE) ++toread[ngnum]; return; } } else { if (artnum == min) { /* explicitly a read article? */ if (*t == ',') /* pick a comma, any comma */ t++; else if (s[-1] == ',') s--; else if (s[-2] == ',') /* (in case of space) */ s -= 2; strcpy(s,t); /* no need to realloc */ if (toread[ngnum] >= TR_NONE) ++toread[ngnum]; #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) 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(ngnum) register NG_NUM ngnum; { register char *s, *c, *h; char tmpbuf[64], *mybuf = tmpbuf; char *nums; int length; #ifdef CACHEFIRST bool virgin_ng = (!abs1st[ngnum]); #endif ART_NUM ngsize = getngsize(ngnum); ART_NUM unread = ngsize; ART_NUM newmax; #ifdef DEBUGGING ngmax[ngnum] = ngsize; /* for checking out-of-range Xrefs */ #endif if (ngsize == TR_BOGUS) { printf("Warning! Bogus newsgroup: %s\n",rcline[ngnum]) FLUSH; paranoid = TRUE; toread[ngnum] = TR_BOGUS; return; } #ifdef CACHEFIRST if (virgin_ng) #else if (!toread[ngnum]) #endif { sprintf(tmpbuf," 1-%ld",(long)ngsize); if (strNE(tmpbuf,rcline[ngnum]+rcnums[ngnum])) checkexpired(ngnum,ngsize); /* this might realloc rcline */ } nums = rcline[ngnum]+rcnums[ngnum]; length = strlen(nums); if (length >= 60) mybuf = safemalloc((MEM_SIZE)(length+5)); strcpy(mybuf,nums); mybuf[length++] = ','; mybuf[length] = '\0'; for (s = mybuf; isspace(*s); s++) ; for ( ; (c = index(s,',')) != Nullch ; s = ++c) { /* for each range */ *c = '\0'; /* keep index from running off */ if ((h = index(s,'-')) != Nullch) /* find - in range, if any */ unread -= (newmax = atol(h+1)) - atol(s) + 1; else if (newmax = atol(s)) unread--; /* recalculate length */ if (newmax > ngsize) { /* paranoia check */ unread = -1; break; } } if (unread >= 0) /* reasonable number? */ toread[ngnum] = (ART_UNREAD)unread; /* remember how many are left */ else { /* SOMEONE RESET THE NEWSGROUP!!! */ toread[ngnum] = (ART_UNREAD)ngsize; /* assume nothing carried over */ printf("Warning! Somebody reset %s--assuming nothing read.\n", rcline[ngnum]) FLUSH; *(rcline[ngnum] + rcnums[ngnum]) = '\0'; paranoid = TRUE; /* enough to make a guy paranoid */ } if (mybuf != tmpbuf) free(mybuf); if (rcchar[ngnum] == NEGCHAR) toread[ngnum] = TR_UNSUB; } /* make sure expired articles are marked as read */ void checkexpired(ngnum,ngsize) register NG_NUM ngnum; ART_NUM ngsize; { register ART_NUM a1st = getabsfirst(ngnum,ngsize); register char *s, *t; register ART_NUM num, lastnum = 0; char *mbuf, *newnum; if (a1st<=1) return; #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("1-%ld->\n%s%c%s\n",(long)(a1st-1),rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif for (s = rcline[ngnum] + rcnums[ngnum]; isspace(*s); s++); while (*s && (num = atol(s)) <= a1st) { while (isdigit(*s)) s++; while (*s && !isdigit(*s)) s++; lastnum = num; } if (*s) { if (s[-1] == '-') { /* landed in a range? */ if (lastnum != 1) sprintf(rcline[ngnum]+rcnums[ngnum]," 1-%s",s); goto ret; } } /* s now points to what should follow first range */ if (s - rcline[ngnum] > rcnums[ngnum] + 10) mbuf = rcline[ngnum]; else { mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + strlen(s) + 10)); strcpy(mbuf,rcline[ngnum]); } newnum = t = mbuf+rcnums[ngnum]; sprintf(t," 1-%ld",(long)(a1st - (lastnum != a1st))); if (*s) { t += strlen(t); *t++ = ','; strcpy(t,s); } if (mbuf == rcline[ngnum]) { rcline[ngnum] = saferealloc(rcline[ngnum], (MEM_SIZE)(rcnums[ngnum] + strlen(newnum) + 1)); } else { free(rcline[ngnum]); rcline[ngnum] = mbuf; } ret:; /* semicolon in case DEBUGGING undefined */ #ifdef DEBUGGING if (debug & DEB_XREF_MARKER) { printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif } = t-rcline[ngnum]; for (t--; isdigit(*t); t--) ; /* find previous delim */ if (*t == ',' && atorcln.h 664 540 12 621 3473757476 5217 /* $Header: rcln.h,v 4.3 85/05/01 11:45:52 lwall Exp $ * * $Log: rcln.h,v $ * Revision 4.3 85/05/01 11:45:52 lwall * Baseline for release with 4.3bsd. * */ #ifdef DEBUGGING EXT ART_NUM ngmax[MAXRCLINE]; #endif void rcln_init(); #ifdef CATCHUP void catch_up(); #endif int addartnum(); #ifdef MCHASE void subartnum(); #endif void prange(); void set_toread(); void checkexpired(); buf,rcline[ngnum]); /* make new rc line */ s = mbuf + (s-rcline[ngnum]); /* point s into mbuf now */ ircstuff.c 664 540 12 50160 3501473251 5745 /* $Header: rcstuff.c,v 4.3.1.3 85/05/29 09:13:25 lwall Exp $ * * $Log: rcstuff.c,v $ * Revision 4.3.1.3 85/05/29 09:13:25 lwall * %d that should be %ld. * * Revision 4.3.1.2 85/05/17 11:40:08 lwall * Sped up "rn -c" by not mallocing unnecessarily. * * Revision 4.3.1.1 85/05/10 11:37:18 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:45:56 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "ngdata.h" #include "term.h" #include "final.h" #include "rn.h" #include "intrp.h" #include "only.h" #include "rcln.h" #include "INTERN.h" #include "rcstuff.h" char *rcname INIT(Nullch); /* path name of .newsrc file */ char *rctname INIT(Nullch); /* path name of temp .newsrc file */ char *rcbname INIT(Nullch); /* path name of backup .newsrc file */ char *softname INIT(Nullch); /* path name of .rnsoft file */ FILE *rcfp INIT(Nullfp); /* .newsrc file pointer */ #ifdef HASHNG short hashtbl[HASHSIZ]; #endif bool rcstuff_init() { register NG_NUM newng; register char *s; register int i; register bool foundany = FALSE; char *some_buf; long length; #ifdef HASHNG for (i=0; i= MAXRCLINE) { /* check for overflow */ fputs("Too many lines in .newsrc\n",stdout) FLUSH; finalize(1); } if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch) softptr[newng] = atoi(tmpbuf); else softptr[newng] = 0; some_buf[--length] = '\0'; /* wipe out newline */ if (checkflag) /* no extra mallocs for -c */ rcline[newng] = some_buf; else if (some_buf == buf) { rcline[newng] = savestr(some_buf); /* make a semipermanent copy */ } else { /*NOSTRICT*/ #ifndef lint some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1)); #endif lint rcline[newng] = some_buf; } #ifdef NOTDEF if (strnEQ(some_buf,"to.",3)) { /* is this a non-newsgroup? */ nextrcline--; /* destroy this line */ continue; } #endif if (*some_buf == ' ' || *some_buf == '\t' || strnEQ(some_buf,"options",7)) { /* non-useful line? */ toread[newng] = TR_JUNK; rcchar[newng] = ' '; rcnums[newng] = 0; continue; } for (s = rcline[newng]; *s && *s != ':' && *s != NEGCHAR; s++) ; if (!*s && !checkflag) { #ifndef lint rcline[newng] = saferealloc(rcline[newng],(MEM_SIZE)length+2); #endif lint s = rcline[newng] + length; *s = ':'; *(s+1) = '\0'; } rcchar[newng] = *s; /* salt away the : or ! */ rcnums[newng] = (char)(s - rcline[newng]); rcnums[newng]++; /* remember where it was */ *s = '\0'; /* null terminate newsgroup name */ #ifdef HASHNG if (!checkflag) sethash(newng); #endif if (rcchar[newng] == NEGCHAR) { toread[newng] = TR_UNSUB; continue; } /* now find out how much there is to read */ if (!inlist(buf) || (suppress_cn && foundany && !paranoid)) toread[newng] = TR_NONE; /* no need to calculate now */ else set_toread(newng); #ifdef VERBOSE if (!checkflag && softmisses == 1) { softmisses++; /* lie a little */ fputs("(Revising soft pointers--be patient.)\n",stdout) FLUSH; } #endif if (toread[newng] > TR_NONE) { /* anything unread? */ if (!foundany) { starthere = newng; foundany = TRUE; /* remember that fact*/ } if (suppress_cn) { /* if no listing desired */ if (checkflag) { /* if that is all they wanted */ finalize(1); /* then bomb out */ } } else { #ifdef VERBOSE IF(verbose) printf("Unread news in %-20s %5ld article%s\n", rcline[newng],(long)toread[newng], toread[newng]==TR_ONE ? nullstr : "s") FLUSH; ELSE #endif #ifdef TERSE printf("%s: %ld article%s\n", rcline[newng],(long)toread[newng], toread[newng]==TR_ONE ? nullstr : "s") FLUSH; #endif if (int_count) { countdown = 1; int_count = 0; } if (countdown) { if (! --countdown) { fputs("etc.\n",stdout) FLUSH; if (checkflag) finalize(1); suppress_cn = TRUE; } } } } } fclose(rcfp); /* close .newsrc */ if (tmpfp != Nullfp) fclose(tmpfp); /* close .rnsoft */ if (checkflag) { /* were we just checking? */ finalize(foundany); /* tell them what we found */ } if (paranoid) cleanup_rc(); #ifdef DEBUGGING if (debug & DEB_HASH) { page_init(); for (i=0; i= TR_NONE; } /* add a newsgroup to the .newsrc file (eventually) */ NG_NUM add_newsgroup(ngn) char *ngn; { register NG_NUM newng = nextrcline++; /* increment max rcline index */ rcnums[newng] = strlen(ngn) + 1; rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 1)); strcpy(rcline[newng],ngn); /* and copy over the name */ *(rcline[newng] + rcnums[newng]) = '\0'; rcchar[newng] = ':'; /* call it subscribed */ toread[newng] = TR_NONE; /* just for prettiness */ #ifdef HASHNG sethash(newng); /* so we can find it again */ #endif #ifdef RELOCATE return relocate_newsgroup(newng,-1); #else return newng; #endif } #ifdef RELOCATE NG_NUM relocate_newsgroup(ngx,newng) NG_NUM ngx; NG_NUM newng; { char *dflt = (ngx!=current_ng ? "$^.L" : "$^L"); char *tmprcline; ART_UNREAD tmptoread; char tmprcchar; char tmprcnums; ACT_POS tmpsoftptr; register NG_NUM i; #ifdef DEBUGGING ART_NUM tmpngmax; #endif #ifdef CACHEFIRST ART_NUM tmpabs1st; #endif starthere = 0; /* Disable this optimization */ writesoft = TRUE; /* Update soft pointer file */ if (ngx < nextrcline-1) { #ifdef HASHNG for (i=0; i ngx) --hashtbl[i]; else if (hashtbl[i] == ngx) hashtbl[i] = nextrcline-1; } #endif tmprcline = rcline[ngx]; tmptoread = toread[ngx]; tmprcchar = rcchar[ngx]; tmprcnums = rcnums[ngx]; tmpsoftptr = softptr[ngx]; #ifdef DEBUGGING tmpngmax = ngmax[ngx]; #endif #ifdef CACHEFIRST tmpabs1st = abs1st[ngx]; #endif for (i=ngx+1; i ngx) current_ng--; if (newng < 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); reinp_reloc: eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') { /* if return from stop signal */ goto reask_reloc; /* give them a prompt again */ } setdef(buf,dflt); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { printf("\n\n\ Type ^ to put the newsgroup first (position 0).\n\ Type $ to put the newsgroup last (position %d).\n", nextrcline-1); printf("\ Type . to put it before the current newsgroup (position %d).\n", current_ng); printf("\ Type -newsgroup name to put it before that newsgroup.\n\ Type +newsgroup name to put it after that newsgroup.\n\ Type a number between 0 and %d to put it at that position.\n", nextrcline-1); printf("\ Type L for a listing of newsgroups and their positions.\n") FLUSH; } ELSE #endif #ifdef TERSE { printf("\n\n\ ^ to put newsgroup first (pos 0).\n\ $ to put last (pos %d).\n", nextrcline-1); printf("\ . to put before current newsgroup (pos %d).\n", current_ng); printf("\ -newsgroup to put before newsgroup.\n\ +newsgroup to put after.\n\ number in 0-%d to put at that pos.\n", nextrcline-1); printf("\ L for list of .newsrc.\n") FLUSH; } #endif goto reask_reloc; } else if (*buf == 'L') { putchar('\n') FLUSH; list_newsgroups(); goto reask_reloc; } else if (isdigit(*buf)) { if (!finish_command(TRUE)) /* get rest of command */ goto reinp_reloc; newng = atoi(buf); if (newng < 0) newng = 0; if (newng >= nextrcline) return nextrcline-1; } else if (*buf == '^') { putchar('\n') FLUSH; newng = 0; } else if (*buf == '$') { putchar('\n') FLUSH; return nextrcline-1; } else if (*buf == '.') { putchar('\n') FLUSH; newng = current_ng; } else if (*buf == '-' || *buf == '+') { if (!finish_command(TRUE)) /* get rest of command */ goto reinp_reloc; newng = find_ng(buf+1); if (newng == nextrcline) { fputs("Not found.",stdout) FLUSH; goto reask_reloc; } if (*buf == '+') newng++; } else { printf("\n%s",hforhelp) FLUSH; settle_down(); goto reask_reloc; } } if (newng < nextrcline-1) { #ifdef HASHNG for (i=0; i= newng) ++hashtbl[i]; } #endif tmprcline = rcline[nextrcline-1]; tmptoread = toread[nextrcline-1]; tmprcchar = rcchar[nextrcline-1]; tmprcnums = rcnums[nextrcline-1]; tmpsoftptr = softptr[nextrcline-1]; #ifdef DEBUGGING tmpngmax = ngmax[nextrcline-1]; #endif #ifdef CACHEFIRST tmpabs1st = abs1st[nextrcline-1]; #endif for (i=nextrcline-2; i>=newng; i--) { rcline[i+1] = rcline[i]; toread[i+1] = toread[i]; rcchar[i+1] = rcchar[i]; rcnums[i+1] = rcnums[i]; softptr[i+1] = softptr[i]; #ifdef DEBUGGING ngmax[i+1] = ngmax[i]; #endif #ifdef CACHEFIRST abs1st[i+1] = abs1st[i]; #endif } rcline[newng] = tmprcline; toread[newng] = tmptoread; rcchar[newng] = tmprcchar; rcnums[newng] = tmprcnums; softptr[newng] = tmpsoftptr; #ifdef DEBUGGING ngmax[newng] = tmpngmax; #endif #ifdef CACHEFIRST abs1st[newng] = tmpabs1st; #endif } if (current_ng >= newng) current_ng++; return newng; } #endif /* List out the newsrc with annotations */ void list_newsgroups() { register NG_NUM i; char tmpbuf[2048]; static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"}; int cmd; page_init(); print_lines("\ # Status Newsgroup\n\ ",STANDOUT); for (i=0; i= 0) set_toread(i); *(rcline[i] + rcnums[i] - 1) = rcchar[i]; if (toread[i] > 0) sprintf(tmpbuf,"%3d %6ld ",i,(long)toread[i]); else sprintf(tmpbuf,"%3d %7s ",i,status[-toread[i]]); safecpy(tmpbuf+13,rcline[i],2034); *(rcline[i] + rcnums[i] - 1) = '\0'; if (cmd = print_lines(tmpbuf,NOMARKING)) { if (cmd > 0) pushchar(cmd); break; } } int_count = 0; } /* find a newsgroup in .newsrc */ NG_NUM find_ng(ngnam) char *ngnam; { register NG_NUM ngnum; #ifdef HASHNG register int hashix = hash(ngnam); register int incr = 1; while ((ngnum = hashtbl[hashix]) >= 0) { if (strEQ(rcline[ngnum], ngnam) && toread[ngnum] >= TR_UNSUB) return ngnum; hashix = (hashix + incr) % HASHSIZ; incr += 2; /* offsets from original are in n*2 */ } return nextrcline; /* = notfound */ #else /* just do linear search */ for (ngnum = 0; ngnum < nextrcline; ngnum++) { if (strEQ(rcline[ngnum],ngnam)) break; } return ngnum; #endif } void cleanup_rc() { register NG_NUM ngx; register NG_NUM bogosity = 0; #ifdef VERBOSE IF(verbose) fputs("Checking out your .newsrc--hang on a second...\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Checking .newsrc--hang on...\n",stdout) FLUSH; #endif for (ngx = 0; ngx < nextrcline; ngx++) { if (toread[ngx] >= TR_UNSUB) { set_toread(ngx); /* this may reset newsgroup */ /* or declare it bogus */ } if (toread[ngx] == TR_BOGUS) bogosity++; } for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--) bogosity--; /* discount already moved ones */ if (nextrcline > 5 && bogosity > nextrcline / 2) { fputs( "It looks like the active file is messed up. Contact your news administrator,\n\ ",stdout); fputs( "leave the \"bogus\" groups alone, and they may come back to normal. Maybe.\n\ ",stdout) FLUSH; } #ifdef RELOCATE else if (bogosity) { #ifdef VERBOSE IF(verbose) fputs("Moving bogus newsgroups to the end of your .newsrc.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Moving boguses to the end.\n",stdout) FLUSH; #endif for (; ngx >= 0; ngx--) { if (toread[ngx] == TR_BOGUS) relocate_newsgroup(ngx,nextrcline-1); } #ifdef DELBOGUS reask_bogus: in_char("Delete bogus newsgroups? [ny] "); putchar('\n') FLUSH; setdef(buf,"n"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\ Type y to delete bogus newsgroups.\n\ Type n or SP to leave them at the end in case they return.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("y to delete, n to keep\n",stdout) FLUSH; #endif goto reask_bogus; } else if (*buf == 'n' || *buf == 'q') ; else if (*buf == 'y') { while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0) --nextrcline; /* real tough, huh? */ } else { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_bogus; } #endif } #else #ifdef VERBOSE IF(verbose) fputs("You should edit bogus newsgroups out of your .newsrc.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Edit boguses from .newsrc.\n",stdout) FLUSH; #endif #endif paranoid = FALSE; } #ifdef HASHNG /* make an entry in the hash table for the current newsgroup */ void sethash(thisng) NG_NUM thisng; { register int hashix = hash(rcline[thisng]); register int incr = 1; #ifdef DEBUGGING static int hashhits = 0, hashtries = 0; #endif #ifdef DEBUGGING hashtries++; #endif while (hashtbl[hashix] >= 0) { #ifdef DEBUGGING hashhits++; if (debug & DEB_HASH) { printf(" Hash hits: %d / %d\n",hashhits, hashtries) FLUSH; } hashtries++; #endif hashix = (hashix + incr) % HASHSIZ; incr += 2; /* offsets from original are in n*2 */ } hashtbl[hashix] = thisng; } short prime[] = {1,2,-3,-5,7,11,-13,-17,19,23,-29,-31,37,41,-43,-47,53,57,-59, -61,67,71,-73,-79,83,89,-97,-101,1,1,1,1,1,1,1,1,1,1,1,1}; int hash(ngnam) register char *ngnam; { register int i = 0; register int ch; register int sum = 0; #ifdef DEBUGGING char *ngn = ngnam; #endif while (ch = *ngnam++) { sum += (ch + i) * prime[i]; /* gives ~ 10% hits at 25% full */ i++; } #ifdef DEBUGGING if (debug & DEB_HASH) printf("hash(%s) => %d => %d\n",ngn, sum, (sum<0?-sum:sum)%HASHSIZ) FLUSH; #endif if (sum < 0) sum = -sum; return sum % HASHSIZ; } #endif void newsrc_check() { rcfp = fopen(rcname,"r"); /* open it */ if (rcfp == Nullfp) { /* not there? */ #ifdef VERBOSE IF(verbose) fputs("\ Trying to set up a .newsrc file--running newsetup...\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Setting up .newsrc...\n",stdout) FLUSH; #endif if (doshell(sh,filexp(NEWSETUP)) || (rcfp = fopen(rcname,"r")) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\ Can't create a .newsrc--you must do it yourself.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("(Fatal)\n",stdout) FLUSH; #endif finalize(1); } } else { UNLINK(rcbname); /* unlink backup file name */ link(rcname,rcbname); /* and backup current name */ } } /* write out the (presumably) revised .newsrc */ void write_rc() { register NG_NUM tmpng; register char *delim; rcfp = fopen(rctname, "w"); /* open .newsrc */ if (rcfp == Nullfp) { printf("Can't recreate .newsrc\n") FLUSH; finalize(1); } /* write out each line*/ for (tmpng = 0; tmpng < nextrcline; tmpng++) { if (rcnums[tmpng]) { delim = rcline[tmpng] + rcnums[tmpng] - 1; *delim = rcchar[tmpng]; } else delim = Nullch; #ifdef DEBUGGING if (debug & DEB_NEWSRC_LINE) printf("%s\n",rcline[tmpng]) FLUSH; #endif fprintf(rcfp,"%s\n",rcline[tmpng]); if (delim) *delim = '\0'; /* might still need this line */ } fclose(rcfp); /* close .newsrc */ UNLINK(rcname); link(rctname,rcname); UNLINK(rctname); if (writesoft) { tmpfp = fopen(filexp(softname), "w"); /* open .rnsoft */ if (tmpfp == Nullfp) { printf(cantcreate,filexp(softname)) FLUSH; return; } for (tmpng = 0; tmpng < nextrcline; tmpng++) { fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]); } fclose(tmpfp); } } void get_old_rc() { UNLINK(rctname); link(rcname,rctname); UNLINK(rcname); link(rcbname,rcname); UNLINK(rcbname); } ) current_ng--; if (newng < 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); reinp_reloc: eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') { /* if return from stop signal */ rcstuff.h 664 540 12 2415 3473757433 5751 /* $Header: rcstuff.h,v 4.3 85/05/01 11:46:49 lwall Exp $ * * $Log: rcstuff.h,v $ * Revision 4.3 85/05/01 11:46:49 lwall * Baseline for release with 4.3bsd. * */ EXT char *rcline[MAXRCLINE];/* pointers to lines of .newsrc */ EXT ART_UNREAD toread[MAXRCLINE]; /* number of articles to be read in newsgroup */ /* <0 => invalid or unsubscribed newsgroup */ #define TR_ONE ((ART_UNREAD) 1) #define TR_NONE ((ART_UNREAD) 0) #define TR_UNSUB ((ART_UNREAD) -1) /* keep this one as -1, some tests use >= TR_UNSUB */ #define TR_BOGUS ((ART_UNREAD) -2) #define TR_JUNK ((ART_UNREAD) -3) EXT char rcchar[MAXRCLINE]; /* holds the character : or ! while spot is \0 */ EXT char rcnums[MAXRCLINE]; /* offset from rcline to numbers on line */ EXT ACT_POS softptr[MAXRCLINE]; /* likely ptr to active file entry for newsgroup */ EXT bool paranoid INIT(FALSE); /* did we detect some inconsistency in .newsrc? */ bool rcstuff_init(); bool get_ng(); /* return TRUE if newsgroup can be found or added */ NG_NUM add_newsgroup(); #ifdef RELOCATE NG_NUM relocate_newsgroup(); /* move newsgroup around */ #endif void list_newsgroups(); NG_NUM find_ng(); /* return index of newsgroup */ void cleanup_rc(); void sethash(); int hash(); void newsrc_check(); void write_rc(); void get_old_rc(); int_count = 0; } /* find a newsgroup in .newsrc */ NG_NUM find_ng(ngnam) char *ngnam; { register NG_NUM ngnum; #ifdef HASHNG register int hashix = hash(ngnam); register int incr = 1; while ((ngnum = hashtbl[hashix]) >= 0) respond.c 664 540 12 22717 3477433300 5755 /* $Header: respond.c,v 4.3.1.4 85/05/23 17:24:49 lwall Exp $ * * $Log: respond.c,v $ * Revision 4.3.1.4 85/05/23 17:24:49 lwall * Now allows 'r' and 'f' on null articles. * * Revision 4.3.1.3 85/05/15 14:42:32 lwall * Removed duplicate include of intrp.h. * * Revision 4.3.1.2 85/05/14 08:55:15 lwall * Default for normal/mailbox question was applied to wrong buffer. * * Revision 4.3.1.1 85/05/10 11:37:33 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:47:04 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "head.h" #include "term.h" #include "ng.h" #include "util.h" #include "rn.h" #include "artio.h" #include "final.h" #include "INTERN.h" #include "respond.h" static char nullart[] = "\nNull article\n"; void respond_init() { ; } int save_article() { bool use_pref; register char *s, *c; char altbuf[CBUFLEN]; int iter; bool interactive = (buf[1] == FINISHCMD); if (!finish_command(interactive)) /* get rest of command */ return SAVE_ABORT; use_pref = isupper(*buf); #ifdef ASYNC_PARSE parse_maybe(art); #endif savefrom = (*buf=='w' || *buf=='W' ? htype[PAST_HEADER].ht_minpos : 0); if (artopen(art) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\n\ Saving null articles is not very productive! :-)\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif return SAVE_DONE; } if (chdir(cwd)) { printf(nocd,cwd) FLUSH; sig_catcher(0); } if (savedest) free(savedest); if ((s = index(buf,'|')) != Nullch) { /* is it a pipe command? */ s++; /* skip the | */ while (*s == ' ') s++; safecpy(altbuf,filexp(s),sizeof altbuf); savedest = altbuf; interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER)); /* then set up for command */ resetty(); /* restore tty state */ if (use_pref) /* use preferred shell? */ doshell(Nullch,cmd_buf); /* do command with it */ else doshell(sh,cmd_buf); /* do command with sh */ noecho(); /* and stop echoing */ crmode(); /* and start cbreaking */ savedest = savestr(savedest); } else { /* normal save */ bool there, mailbox; char *savename = getval("SAVENAME",SAVENAME); s = buf+1; /* skip s or S */ if (*s == '-') { /* if they are confused, skip - also */ #ifdef VERBOSE IF(verbose) fputs("Warning: '-' ignored. This isn't readnews.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("'-' ignored.\n",stdout) FLUSH; #endif s++; } for (; *s == ' '; s++); /* skip spaces */ safecpy(altbuf,filexp(s),sizeof altbuf); s = altbuf; if (! index(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 (iter = 0; (there = stat(s,&filestat) >= 0) && (filestat.st_mode & S_IFDIR); iter++) { /* is it a directory? */ c = (s+strlen(s)); *c++ = '/'; /* put a slash before filename */ interp(c, s==buf?(sizeof buf):(sizeof altbuf), iter ? "News" : savename ); /* generate a default name somehow or other */ if (index(c,'/')) { /* yikes, a '/' in the filename */ makedir(s,MD_FILE); } } if (*s != '/') { /* relative path? */ c = (s==buf ? altbuf : buf); sprintf(c, "%s/%s", cwd, s); s = c; /* absolutize it */ } s = savedest = savestr(s); /* doesn't move any more */ /* make it handy for %b */ if (!there) { if (mbox_always) mailbox = TRUE; else if (norm_always) mailbox = FALSE; else { char *dflt = (instr(savename,"%a") ? "nyq" : "ynq"); sprintf(cmd_buf, "\nFile %s doesn't exist--\n use mailbox format? [%s] ", s,dflt); reask_save: in_char(cmd_buf); putchar('\n') FLUSH; setdef(buf,dflt); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) printf("\n\ Type y to create %s as a mailbox.\n\ Type n to create it as a normal file.\n\ Type q to abort the save.\n\ ",s) 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 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; settle_down(); goto reask_save; } } } else if (filestat.st_mode & S_IFCHR) mailbox = FALSE; else { int tmpfd; tmpfd = open(s,0); if (tmpfd == -1) mailbox = FALSE; else { read(tmpfd,buf,LBUFLEN); c = buf; if (!isspace(MBOXCHAR)) while (isspace(*c)) c++; mailbox = (*c == MBOXCHAR); close(tmpfd); } } safecpy(cmd_buf, filexp(mailbox ? getval("MBOXSAVER",MBOXSAVER) : getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf); /* format the command */ resetty(); /* make terminal behave */ if (doshell(use_pref?Nullch:SH,cmd_buf)) fputs("Not saved",stdout); else printf("%s to %s %s", there?"Appended":"Saved", mailbox?"mailbox":"file", s); if (interactive) putchar('\n') FLUSH; noecho(); /* make terminal do what we want */ crmode(); } s_bomb: if (chdir(spool) || chdir(ngdir)) { printf(nocd,ngdir) FLUSH; sig_catcher(0); } return SAVE_DONE; } int cancel_article() { char *artid_buf; char *ngs_buf; char *from_buf; char *reply_buf; int myuid = getuid(); int r = -1; if (artopen(art) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\n\ Cancelling null articles is your idea of fun? :-)\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif return r; } reply_buf = fetchlines(art,REPLY_LINE); from_buf = fetchlines(art,FROM_LINE); artid_buf = fetchlines(art,ARTID_LINE); ngs_buf = fetchlines(art,NGS_LINE); if (!instr(from_buf,sitename) || (!instr(from_buf,logname) && !instr(reply_buf,logname) && #ifdef NEWSADMIN myuid != newsuid && #endif myuid != ROOTID ) ) #ifdef VERBOSE IF(verbose) fputs("You can't cancel someone else's article\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Not your article\n",stdout) FLUSH; #endif else { tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; goto no_cancel; } interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER)); fputs(buf,tmpfp); fclose(tmpfp); r = doshell(sh,filexp(getval("CANCEL",CANCEL))); } no_cancel: free(artid_buf); free(ngs_buf); free(from_buf); free(reply_buf); return r; } void reply() { bool incl_body = (*buf == 'R'); char *maildoer = savestr(filexp(getval("MAILPOSTER",MAILPOSTER))); artopen(art); tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; goto no_reply; } interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER)); fputs(buf,tmpfp); if (!instr(maildoer,"%h")) #ifdef VERBOSE IF(verbose) printf("\n%s\n(Above lines saved in file %s)\n",buf,headname) FLUSH; ELSE #endif #ifdef TERSE printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH; #endif if (incl_body && artfp != Nullfp) { interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID)); fprintf(tmpfp,"%s\n",buf); #ifdef ASYNC_PARSE parse_maybe(art); #endif fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0); while (fgets(buf,LBUFLEN,artfp) != Nullch) { fprintf(tmpfp,"%s%s",indstr,buf); } fprintf(tmpfp,"\n"); } fclose(tmpfp); interp(cmd_buf, (sizeof cmd_buf), maildoer); invoke(cmd_buf,origdir); UNLINK(headname); /* kill the header file */ no_reply: free(maildoer); } void followup() { bool incl_body = (*buf == 'F'); artopen(art); tmpfp = fopen(headname,"w"); if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; return; } interp(buf, (sizeof buf), getval("NEWSHEADER",NEWSHEADER)); fprintf(tmpfp,"%s",buf); if (incl_body && artfp != Nullfp) { #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); #ifdef ASYNC_PARSE parse_maybe(art); #endif fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0); while (fgets(buf,LBUFLEN,artfp) != Nullch) { fprintf(tmpfp,"%s%s",indstr,buf); } fprintf(tmpfp,"\n"); } fclose(tmpfp); safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf); invoke(cmd_buf,origdir); UNLINK(headname); } void invoke(cmd,dir) char *cmd,*dir; { if (chdir(dir)) { printf(nocd,dir) FLUSH; return; } #ifdef VERBOSE IF(verbose) printf("\n(leaving cbreak mode; cwd=%s)\nInvoking command: %s\n\n", dir,cmd) FLUSH; ELSE #endif #ifdef TERSE printf("\n(-cbreak; cwd=%s)\nInvoking: %s\n\n",dir,cmd) FLUSH; #endif resetty(); /* make terminal well-behaved */ doshell(sh,cmd); /* do the command */ noecho(); /* set no echo */ crmode(); /* and cbreak mode */ #ifdef VERBOSE IF(verbose) fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(+cbreak)\n",stdout) FLUSH; #endif if (chdir(spool) || chdir(ngdir)) { printf(nocd,ngdir) FLUSH; sig_catcher(0); } } o reask_reloc; } } if (newng < nextrclinrespond.h 664 540 12 712 3473757471 5727 /* $Header: respond.h,v 4.3 85/05/01 11:47:50 lwall Exp $ * * $Log: respond.h,v $ * Revision 4.3 85/05/01 11:47:50 lwall * Baseline for release with 4.3bsd. * */ EXT char *savedest INIT(Nullch); /* value of %b */ EXT ART_POS savefrom INIT(0); /* value of %B */ EXT char *headname INIT(Nullch); #define SAVE_ABORT 0 #define SAVE_DONE 1 void respond_init(); int save_article(); int cancel_article(); void reply(); void followup(); void invoke(); ches. * * Revision 4.3 85/05/01 11:47:04 lwall rn.1 664 540 12 156171 3477433314 4667 ''' $Header: rn.1,v 4.3.1.3 85/05/23 17:14:14 lwall Exp $ ''' ''' $Log: rn.1,v $ ''' Revision 4.3.1.3 85/05/23 17:14:14 lwall ''' Now allows 'r' and 'f' on null articles. ''' ''' Revision 4.3.1.2 85/05/13 09:27:53 lwall ''' Added CUSTOMLINES option. ''' ''' Revision 4.3.1.1 85/05/10 11:38:22 lwall ''' Branch for patches. ''' ''' Revision 4.3 85/05/01 11:48:26 lwall ''' Baseline for release with 4.3bsd. ''' ''' .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .de Ip .br .ie \\n.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. ''' Bell System Logo is used as a dummy character. ''' .tr \(bs-|\(bv\*(Tr .ie n \{\ .ds -- \(bs- .if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH RN 1 LOCAL .SH NAME rn - new read news program .SH SYNOPSIS .B rn [options] [newsgroups] .SH DESCRIPTION .I Rn is a replacement for the readnews(1) program that was written to be as efficient as possible, particularly in human interaction. .I Rn attempts to minimize the amount of \*(L"dead\*(R" time spent reading news\*(--it tries to get things done while the user is reading or deciding whether to read, and attempts to get useful information onto the screen as soon as possible, highlighting spots that the eye makes frequent reference to, like subjects and previously read lines. Whether or not it's faster, it SEEMS faster. .PP If no newsgroups are specified, all the newsgroups which have unread news are displayed, and then the user is asked for each one whether he wants to read it, in the order in which the newsgroups occur in the .I .newsrc file. With a list of newsgroups, .I rn will start up in \*(L"add\*(R" mode, using the list as a set of patterns to add new newsgroups and restrict which newsgroups are displayed. See the discussion of the \*(L'a\*(R' command on the newsgroup selection level. .PP .I Rn operates on three levels: the newsgroup selection level, the article selection level, and the paging level. Each level has its own set of commands, and its own help menu. At the paging level (the bottom level), .I rn behaves much like the .IR more (1) program. At the article selection level, you may specify which article you want next, or read them in the default order, which is either in order of arrival on your system, or by subject threads. At the newsgroup selection level (the top level), you may specify which newsgroup you want next, or read them in the default order, which is the order that the newsgroups occur in your .I .newsrc file. (You will therefore want to rearrange your .I .newsrc file to put the most interesting newsgroups first. This can be done with the \*(L'm\*(R' command on the Newsgroup Selection level. WARNING: invoking readnews/vnews (the old user interface) in any way (including as a news checker in your login sequence!) will cause your .I .newsrc to be disarranged again.) .PP On any level, at ANY prompt, an \*(L'h\*(R' may be typed for a list of available commands. This is probably the most important command to remember, so don't you forget it. Typing space to any question means to do the normal thing. You will know what that is because every prompt has a list of several plausible commands enclosed in square brackets. The first command in the list is the one which will be done if you type a space. (All input is done in cbreak mode, so carriage returns should not be typed to terminate anything except certain multi-character commands. Those commands will be obvious in the discussion below because they take an argument.) .PP Upon startup, .I rn will do several things: .Ip 1. 4 It will look for your .I .newsrc file, which is your list of subscribed-to newsgroups. If .I rn doesn't find a .IR .newsrc , it will create one. If it does find one, it will back it up under the name \*(L".oldnewsrc\*(R". .Ip 2. 4 It will input your .I .newsrc file, listing out the first several newsgroups with unread news. .Ip 3. 4 It will perform certain consistency checks on your .IR .newsrc . If your .I .newsrc is out of date in any of several ways, .I rn will warn you and patch it up for you, but you may have to wait a little longer for it to start up. .Ip 4. 4 .I Rn will next check to see if any new newsgroups have been created, and give you the opportunity to add them to your .IR .newsrc . .Ip 5. 4 .I Rn goes into the top prompt level\*(--the newsgroup selection level. .Sh "Newsgroup Selection Level" In this section the words \*(L"next\*(R" and \*(L"previous\*(R" refer to the ordering of the newsgroups in your .I .newsrc file. On the newsgroup selection level, the prompt looks like this: .Sp ******** 17 unread articles in net.blurfl\*(--read now? [ynq] .Sp and the following commands may be given at this level: .Ip y,SP 8 Do this newsgroup now. .Ip .command 8 Do this newsgroup now, but execute .I command before displaying anything. The command will be interpreted as if given on the article selection level. .Ip = 8 Do this newsgroup now, but list subjects before displaying articles. .Ip n 8 Go to the next newsgroup with unread news. .Ip N 8 Go to the next newsgroup. .Ip p 8 Go to the previous newsgroup with unread news. If there is none, stay at the current newsgroup. .Ip P 8 Go to the previous newsgroup. .Ip \- 8 Go to the previously displayed newsgroup (regardless of whether it is before or after the current one in the list). .Ip 1 8 Go to the first newsgroup. .Ip ^ 8 Go to the first newsgroup with unread news. .Ip $ 8 Go to the end of the newsgroups list. .Ip "g newsgroup" 8 Go to .IR newsgroup . If it isn't currently subscribed to, you will be asked if you want to subscribe. .Ip "/pattern" 8 Scan forward for a newsgroup matching .IR pattern . Patterns do globbing like filenames, i.e., use ? to match a single character, * to match any sequence of characters, and [] to specify a list of characters to match. (\*(L"all\*(R" may be used as a synonym for \*(L"*\*(R".) Unlike normal filename globbing, newsgroup searching is not anchored to the front and back of the filename, i.e. \*(L"/jok\*(R" will find net.jokes. You may use ^ or $ to anchor the front or back of the search: \*(L"/^test$\*(R" will find newsgroup test and nothing else If you want to include newsgroups with 0 unread articles, append /r. If the newsgroup is not found between the current newsgroup and the last newsgroup, the search will wrap around to the beginning. .Ip "?pattern" 8 Same as /, but search backwards. .Ip u 8 Unsubscribe from current newsgroup. .Ip "l string" 8 List newsgroups not subscribed to which contain the string specified. .Ip L 8 13v Lists the current state of the .IR .newsrc , along with status information. .Sp .nf \h'|0.5i'Status \h'|2i'Meaning \h'|0.5i' \h'|2i'Count of unread articles in newsgroup. \h'|0.5i'READ \h'|2i'No unread articles in newsgroup. \h'|0.5i'UNSUB \h'|2i'Unsubscribed newsgroup. \h'|0.5i'BOGUS \h'|2i'Bogus newsgroup. \h'|0.5i'JUNK \h'|2i'Ignored line in .newsrc \h'|2i'(e.g. readnews \*(L"options\*(R" line). .fi .Sp (A bogus newsgroup is one that is not in the list of active newsgroups in the active file, which on most systems is /usr/lib/news/active.) .Ip "m name" 8 Move the named newsgroup somewhere else in the .IR .newsrc . If no name is given, the current newsgroup is moved. There are a number of ways to specify where you want the newsgroup\*(--type h for help when it asks where you want to put it. .Ip c 8 Catch up\*(--mark all unread articles in this newsgroup as read. .Ip "o pattern" 8 Only display those newsgroups whose name matches .IR pattern . Patterns are the same as for the \*(L'/\*(R' command. Multiple patterns may be separated by spaces, just as on the command line. The restriction will remain in effect either until there are no articles left in the restricted set of newsgroups, or another restriction command is given. Since .I pattern is optional, \*(L'o\*(R' by itself will remove the restriction. .Ip "a pattern" 8 Add new newsgroups matching .IR pattern . Newsgroups which are already in your .I .newsrc file, whether subscribed to or not, will not be listed. If any new newsgroups are found, you will be asked for each one whether you would like to add it. After any new newsgroups have been added, the \*(L'a\*(R' command also restricts the current set of newsgroups just like the \*(L'o\*(R' command does. .Ip & 8 Print out the current status of command line switches and any newsgroup restrictions. .Ip "&switch {switch}" 8 Set additional command line switches. .Ip && 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 on most systems is /usr/spool/news. The environment variable SHELL will be used if defined. If .I command is null, an interactive shell is started. .Ip q 8 Quit. .Ip x 8 Quit, restoring .newsrc to its state at startup of .IR rn . 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 KILL file. This is a file which contains /pattern/j commands (one per line) to be applied to every newsgroup as it is started up, that is, when it is selected on the newsgroup selection level. The purpose of a KILL file is to mark articles as read on the basis of some set of patterns. This saves considerable wear and tear on your \*(L'n\*(R' key. There is also a local KILL file for each newsgroup. Because of the overhead involved in searching for articles to kill, it is better if possible to use a local KILL file. Local KILL files are edited with a \*(L'^K\*(R' on the article selection level. There are also automatic ways of adding search commands to the local KILL file\*(--see the \*(L'K\*(R' command and the K search modifier on the article selection level. .Sp If either of the environment variables VISUAL or EDITOR is set, the specified editor will be invoked; otherwise a default editor (normally vi(1)) is invoked on the KILL file. .Sh "Article Selection Level" On the article selection level, .I rn selects (by default) unread articles in numerical order (the order in which articles have arrived at your site). If you do a subject search (^N), the default order is modified to be numerical order within each subject thread. You may switch back and forth between numerical order and subject thread order at will. The .B \-S switch can be used to make subject search mode the default. .Sp On the article selection level you are .I not asked whether you want to read an article before the article is displayed; rather, .I rn simply displays the first page (or portion of a page, at low baud rates) of the article and asks if you want to continue. The normal article selection prompt comes at the END of the article (though article selection commands can be given from within the middle of the article (the pager level) also). The prompt at the end of an article looks like this: .Sp End of article 248 (of 257)\*(--what next? [npq] .Sp The following are the options at this point: .Ip n,SP 8 Scan forward for next unread article. (Note: the \*(L'n\*(R' (next) command when typed at the end of an article does not mark the article as read, since an article is automaticaly marked as read after the last line of it is printed. It is therefore possible to type a sequence such as \*(L'mn\*(R' and leave the article marked as unread. The fact that an article is marked as read by typing \*(L'n\*(R', \&\*(L'N\*(R', \*(L'^N\*(R', \*(L's\*(R', or \*(L'S\*(R' within the MIDDLE of the article is in fact a special case.) .Ip N 8 Go to the next article. .Ip ^N 8 Scan forward for the next article with the same subject, and make ^N default (subject search mode). .Ip p 8 Scan backward for previous unread article. If there is none, stay at the current article. .Ip P 8 Go to the previous article. .Ip \- 8 Go to the previously displayed article (regardless of whether that article is before or after this article in the normal sequence). .Ip ^P 8 Scan backward for the previous article with the same subject, and make ^N default (subject search mode). .Ip ^R 8 Restart the current article. .Ip 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 q 8 Quit this newsgroup and go back to the newsgroup selection level. .Ip ^ 8 Go to the first unread article. .Ip $ 8 Go to the last article (actually, one past the last article). .Ip "number" 8 Go to the numbered article. .Ip "range{,range} command{:command}" 8 Apply a set of commands to a set of articles. A range consists of either
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' (delayed mark as unread), \*(L'j\*(R' (mark as read), \*(L"s dest\*(R" (save to a destination), \*(L"!command\*(R" (shell escape), \*(L"=\*(R" (print the subject) and \*(L"C\*(R" (cancel). .Ip j 8 Junk the current article\*(--mark it as read. If this command is used from within an article, you are left at the end of the article, unlike \*(L'n\*(R', which looks for the next article. .Ip m 8 Mark the current article as still unread. (If you are in subject search mode you probably want to use M instead of m. Otherwise the current article may be selected as the beginning of the next subject thread.) .Ip M 8 Mark the current article as still unread, but not until the newsgroup is exited. Until then, the current article will be marked as read. This is useful for returning to an article in another session, or in another newsgroup. .Ip /pattern 8 Scan forward for article containing .I pattern in the subject. See the section on Regular Expressions. Together with the escape substitution facility described later, it becomes easy to search for various attributes of the current article, such as subject, article ID, author name, etc. The previous pattern can be recalled with \*(L"/\*(R". If .I pattern is omitted, the previous pattern is assumed. .Ip /pattern/h 8 Scan forward for article containing .I pattern in the header. .Ip /pattern/a 8 Scan forward for article containing .I pattern anywhere in article. .Ip /pattern/r 8 Scan read articles also. .Ip /pattern/c 8 Make search case sensitive. Ordinarily upper and lower case are considered the same. .Ip "/pattern/modifiers:command{:command}" 8 Apply the commands listed to articles matching the search command (possibly with h, a, or r modifiers). Applicable commands include \*(L'm\*(R' (mark as unread), \*(L'M\*(R' (delayed mark as unread), \*(L'j\*(R' (mark as read), \*(L"s dest\*(R" (save to a destination), \*(L"!command\*(R" (shell escape), \*(L"=\*(R" (print the subject) and \*(L"C\*(R" (cancel). If the first command is \*(L'm\*(R' or \*(L'M\*(R', modifier r is assumed. A K may be included in the modifiers (not the commands) to cause the entire command (sans K) to be saved to the local KILL file, where it will be applied to every article that shows up in the newsgroup. .Sp For example, to save all articles in a given newsgroup to the line printer and mark them read, use \*(L"/^/\||\|lpr:j\*(R". If you say \*(L"/^/K\||\|lpr:j\*(R", this will happen every time you enter the newsgroup. .Ip ?pattern 8 Scan backward for article containing .I pattern in the subject. May be modified as the forward search is: ?pattern?modifiers[:commands]. It is likely that you will want an r modifier when scanning backward. .Ip k 8 Mark as read all articles with the same subject as the current article. (Note: there is no single character command to temporarily mark as read (M command) articles matching the current subject. That can be done with \*(L"/s/M\*(R", however.) .Ip K 8 Do the same as the k command, but also add a line to the local KILL file for this newsgroup to kill this subject every time the newsgroup is started up. For a discussion of KILL files, see the \*(L'^K\*(R' command below. See also the K modifier on searches above. .Ip ^K 8 Edit the local KILL file for this newsgroup. Each line of the KILL file should be a command of the form /pattern/j. (With the exception that .I rn will insert a line at the beginning of the form \*(L"THRU \*(R", which tells .I rn the maximum article number that the KILL file has been applied to. You may delete the THRU line to force a rescan of current articles.) You may also have reason to use the m, h, or a modifiers. Be careful with the M modifier in a kill file\*(--there are more efficient ways to never read an article. You might have reason to use it if a particular series of articles is posted to multiple newsgroups. In this case, M would force you to view the article in a different newsgroup. .Sp To see only newgroup articles in the control newsgroup, for instance, you might put .Sp /^/j .br /newgroup/m .Sp which kills all subjects not containing \*(L"newgroup\*(R". You can add lines automatically via the K command and K search modifiers, but editing is the only way to remove lines. If either of the environment variables VISUAL or EDITOR is set, the specified editor will be invoked; otherwise a default editor (normally vi) is invoked on the KILL file. .Sp The KILL file may also contain switch setting lines beginning with \*(L'&\*(R'. Additionally, any line beginning with \*(L'X\*(R' is executed on exit from the newsgroup rather than on entrance. This can be used to set switches back to a default value. .Ip r 8 Reply through net mail. The environment variables MAILPOSTER and MAILHEADER may be used to modify the mailing behavior of .I rn (see environment section). If on a nonexistent article such as the "End of newsgroup" pseudo-article (which 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 Submit a followup article. If on a nonexistent article such as the "End of newsgroup" pseudo-article (which you can get to with a \*(L'$\*(R' command), posts an original article (basenote). .Ip F 8 Submit a followup article, and include the old article, with lines prefixed either by \*(L">\*(R" or by the argument to a .B \-F switch. .I Rn will attempt to provide an attribution line in front of the quoted article, generated from the From: line of the article. Unfortunately, the From: line doesn't always contain the right name; you should double check it against the signature and change it if necessary, or you may have to apologize for quoting the wrong person. The environment variables NEWSPOSTER, NEWSHEADER and ATTRIBUTION may be used to modify the posting behavior of .I rn (see environment section). .Ip C 8 Cancel the current article, but only if you are the contributor or superuser. .Ip c 8 Catch up in this newsgroup; i.e., mark all articles as read. .Ip u 8 Unsubscribe to this newsgroup. .Ip "s destination" 8 Save to a filename or pipe using sh. If the first character of the destination is a vertical bar, the rest of the command is considered a shell command to which the article is passed through standard input. The command is subject to filename expansion. (See also the environment variable PIPESAVER.) If the destination does not begin with a vertical bar, the rest of the command is assumed to be a filename of some sort. An initial tilde \*(L'~\*(R' will be translated to the name of the home directory, and an initial environment variable substitution is also allowed. If only a directory name is specified, the environment variable SAVENAME is used to generate the actual name. If only a filename is specified (i.e. no directory), the environment variable SAVEDIR will be used to generate the actual directory. If nothing is specified, then obviously both variables will be used. Since the current directory for rn while doing a save command is your private news directory, saying \*(L"s ./filename\*(R" will force the file to your news directory. Save commands are also run through % interpretation, so that you can say \*(L"s %O/filename\*(R" to save to the directory you were in when you ran .IR rn , and \*(L"s %t\*(R" to save to a filename consisting of the Internet address of the sender. .Sp After generating the full pathname of the file to save to, .I rn determines if the file exists already, and if so, appends to it. .I Rn will attempt to determine if an existing file is a mailbox or a normal file, and save the article in the same format. If the output file does not yet exist, .I rn will by default ask you which format you want, or you can make it skip the question with either the .B \-M or .B \-N switch. If the article is to be saved in mailbox format, the command to do so is generated from the environment variable MBOXSAVER. Otherwise, NORMSAVER is used. .Ip "S destination" 8 Save to a filename or pipe using a preferred shell, such as csh. Which shell is used depends first on what you have the environment variable SHELL set to, and in the absence of that, on what your news administrator set for the preferred shell when he or she installed .IR rn . .Ip "| command" 8 Shorthand for \*(L"s | command\*(R". .Ip "w destination" 8 The same as \*(L"s destination\*(R", but saves without the header. .Ip "W destination" 8 The same as \*(L"S destination\*(R", but saves without the header. .Ip & 8 Print out the current status of command line switches. .Ip "&switch {switch}" 8 Set additional command line switches. .Ip && 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 List subjects of unread articles. .Ip # 8 Print last article number. .Sh "Pager Level" At the pager level (within an article), the prompt looks like this: .Sp \*(--MORE\*(--(17%) .Sp and a number of commands may be given: .Ip SP 8 Display next page. .Ip x 8 Display next page and decrypt as a rot13 message. .Ip d,^D 8 Display half a page more. .Ip CR 8 Display one more line. .Ip q 8 Go to the end of the current article (don't mark it either read or unread). Leaves you at the \*(L"What next?\*(R" prompt. .Ip j 8 Junk the current article. Mark it read and go to the end of the article. .Ip ^L 8 Refresh the screen. .Ip X 8 Refresh the screen and decrypt as a rot13 message. .Ip b,^B 8 Back up one page. .Ip gpattern 8 Goto (search forward for) .I pattern within current article. Note that there is no space between the command and the pattern. If the pattern is found, the page containing the pattern will be displayed. Where on the page the line matching the pattern goes depends on the value of the .B \-g switch. By default the matched line goes at the top of the screen. .Ip G 8 Search for g pattern again. .Ip ^G 8 This is a special version of the \*(L'g\*(R' command that is for skipping articles in a digest. It is equivalent to setting \*(L"\-g4\*(R" and then executing the command \*(L"g^Subject:\*(R". .Ip 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 # $ & / = ? c C f F k K ^K m M r R ^R u v Y ^ .br number .br range{,range} command{:command} .Sp The following commands also skip to the end of the article, but have the additional effect of marking the current article as read: .Sp n N ^N s S | w W .Sp .Sh "Miscellaneous facts about commands" An \*(L'n\*(R' typed at either the \*(L"Last newsgroup\*(R" prompt or a \*(L"Last article\*(R" prompt will cycle back to the top of the newsgroup or article list, whereas a \*(L'q\*(R' will quit the level. (Note that \*(L'n\*(R' does not mean \*(L"no\*(R", but rather \*(L"next\*(R".) A space will of course do whatever is shown as the default, which will vary depending on whether rn thinks you have more articles or newsgroups to read. .PP The \*(L'b\*(R' (backup page) command may be repeated until the beginning of the article is reached. If .I rn is suspended (via a ^Z), then when the job is resumed, a refresh (^L) will automatically be done (Berkeley-type systems only). If you type a command such as \*(L'!\*(R' or \*(L's\*(R' which takes you from the middle of the article to the end, you can always get back into the middle by typing \*(L'^L\*(R'. .PP In multi-character commands such as \*(L'!\*(R', \*(L's\*(R', \*(L'/\*(R', etc, you can interpolate various run-time values by typing escape and a character. To find out what you can interpolate, type escape and \*(L'h\*(R', or check out the single character % substitutions for environment variables in the Interpretation and Interpolation section, which are the same. Additionally, typing a double escape will cause any % substitutions in the string already typed in to be expanded. .Sh "Options" .I Rn has a nice set of options to allow you to tailor the interaction to your liking. (You might like to know that the author swears by \*(L"\-e \-m \-S \-/\*(R".) These options may be set on the command line, via the RNINIT environment variable, via a file pointed to by the RNINIT variable, or from within rn via the & command. Options may generally be unset by saying \*(L"+switch\*(R". Options include: .TP 5 .B \-c checks for news without reading news. If a list of newsgroups is given on the command line, only those newsgroups will be checked; otherwise all subscribed-to newsgroups are checked. Whenever the .B \-c switch is specified, a non-zero exit status from .I rn means that there is unread news in one of the checked newsgroups. The .B \-c switch does not disable the printing of newsgroups with unread news; this is controlled by the .B \-s switch. (The .B \-c switch is not meaningful when given via the & command.) .TP 5 .B \-C tells .I rn how often to checkpoint the .IR .newsrc , in articles read. Actually, this number says when to start thinking about doing a checkpoint if the situation is right. If a reasonable checkpointing situation doesn't arise within 10 more articles, the .I .newsrc is checkpointed willy-nilly. .TP 5 .B \-d sets the default save directory to something other than ~/News. The directory name will be globbed (via csh) if necessary (and if possible). Articles saved by .I rn may be placed in the save directory or in a subdirectory thereof depending on the command that you give and the state of the environment variables SAVEDIR and SAVENAME. Any KILL files (see the K command in the Article Selection section) also reside in this directory and its subdirectories, by default. In addition, shell escapes leave you in this directory. .TP 5 .B \-D enables debugging output. See common.h for flag values. Warning: normally .I rn 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 conjuction 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 rn , \*(L"&\-ESAVENAME=%t\*(R" is similar to \*(L"setenv SAVENAME '%t'\*(R" in .IR csh , or \*(L"SAVENAME='%t'; export SAVENAME\*(R" in .IR sh . Any environment variables set with .B \-E will be inherited by subprocesses of .IR rn . .TP 5 .B \-F sets the prefix string for the \*(L'F\*(R' followup command to use in prefixing each line of the quoted article. For example, \*(L"\-F\*(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 rn which line of the screen you want searched-for strings to show up on when you search with the \*(L'g\*(R' command within an article. The lines are numbered starting with 1. The initial default is \*(L"\-g1\*(R", meaning the first line of the screen. Setting the line to less than 1 or more than the number of lines on the screen will set it to the last line of the screen. .TP 5 .B \-h hides (disables the printing of) all header lines beginning with .I string. For instance, \-hexp will disable the printing of the \*(L"Expires:\*(R" line. Case is insignificant. If is null, all header lines except Subject are hidden, and you may then use .B +h to select those lines you want to see. You may wish to use the baud-rate switch modifier below to hide more lines at lower baud rates. .TP 5 .B \-H works just like .B \-h except that instead of setting the hiding flag for a header line, it sets the magic flag for that header line. Certain header lines have magic behavior that can be controlled this way. At present, the following actions are caused by the flag for the particular line: the Newsgroups line will only print when there are multiple newsgroups, the Subject line will be underlined, and the Expires line will always be suppressed if there is nothing on it. In fact, all of these actions are the default, and you must use .B +H to undo them. .TP 5 .B \-i= specifies how long (in lines) to consider the initial page of an article\*(--normally this is determined automatically depending on baud rate. (Note that an entire article header will always be printed regardless of the specified initial page length. If you are working at low baud rate and wish to reduce the size of the headers, you may hide certain header lines with the .B \(bsh switch.) .TP 5 .B \-l disables the clearing of the screen at the beginning of each article, in case you have a bizarre terminal. .TP 5 .B \-L tells .I rn 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 \-r causes .I rn to restart in the last newsgroup read during a previous session with .I rn. It is equivalent to starting up normally and then getting to the newsgroup with a g command. .TP 5 .B \-s with no argument suppresses the initial listing of newsgroups with unread news, whether .B \-c is specified or not. Thus .B \-c and .B \-s can be used together to test \*(L"silently\*(R" the status of news from within your .I .login file. If .B \-s is followed by a number, the initial listing is suppressed after that many lines have been listed. Presuming that you have your .I .newsrc sorted into order of interest, .B \-s5 will tell you the 5 most interesting newsgroups that have unread news. This is also a nice feature to use in your .I .login file, since it not only tells you whether there is unread news, but also how important the unread news is, without having to wade through the entire list of unread newsgroups. If no .B \-s switch is given .B \-s5 is assumed, so just putting \*(L"rn \-c\*(R" into your \&.login file is fine. .TP 5 .B \-S causes .I rn to enter subject search mode (^N) automatically whenever a 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 rn into terse mode. This is more cryptic but useful for low baud rates. (Note that your system administrator may have compiled .I rn with either verbose or terse messages only to save memory.) You may wish to use the baud-rate switch modifier below to enable terse mode only at lower baud rates. .TP 5 .B \-T allows you to type ahead of rn. Ordinarily rn will eat typeahead to prevent your autorepeating space bar from doing a very frustrating thing when you accidentally hold it down. If you don't have a repeating space bar, or you are working at low baud rate, you can set this switch to prevent this behavior. You may wish to use the baud-rate switch modifier below to disable typeahead only at lower baud rates. .TP 5 .B \-v sets verification mode for commands. When set, the command being executed is displayed to give some feedback that the key has actually been typed. Useful when the system is heavily loaded and you give a command that takes a while to start up. .TP 5 .B \-/ sets SAVEDIR to \*(L"%p/%c\*(R" and SAVENAME to \*(L"%a\*(R", which means that by default articles are saved in a subdirectory of your private news directory corresponding to the name of the the current newsgroup, with the filename being the article number. .B +/ sets SAVEDIR to \*(L"%p\*(R" and SAVENAME to \*(L"%^C\*(R", which by default saves articles directly to your private news directory, with the filename being the name of the current newsgroup, first letter capitalized. (Either .B +/ or .B \-/ may be default on your system, depending on the feelings of your news administrator when he, she or it installed .IR rn .) You may, of course, explicitly set SAVEDIR and SAVENAME to other values\*(--see discussion in the environment section. .PP Any switch may be selectively applied according to the current baud-rate. Simply prefix the switch with +speed to apply the switch at that speed or greater, and \%\-speed to apply the switch at that speed or less. Examples: \%\-1200\-hposted suppresses the Posted line at 1200 baud or less; \%+9600\-m enables marking at 9600 baud or more. You can apply the modifier recursively to itself also: \%+300\-1200\-t sets terse mode from 300 to 1200 baud. .PP 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 keymappings .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 RNINIT or the & command. .Sh "Regular Expressions" The patterns used in article searching are regular expressions such as those used by .IR ed (1). In addition, \ew matches an alphanumeric character and \eW a nonalphanumeric. Word boundaries may be matched by \eb, and non-boundaries by \eB. The bracketing construct \e(\ ...\ \e) may also be used, and \edigit matches the digit'th substring, where digit can range from 1 to 9. \e0 matches whatever the last bracket match matched. Up to 10 alternatives may given in a pattern, separated by \e|, with the caveat that \e(\ ...\ \e|\ ...\ \e) is illegal. .Sh "Interpretation and Interpolation" Many of the strings that .I rn handles are subject to interpretations of several types. Under filename expansion, an initial \*(L"~/\*(R" is translated to the name of your home directory, and \*(L"~name\*(R" is translated to the login directory for the user specified. Filename expansion will also expand an initial environment variable, and also does the backslash, uparrow and percent expansion mentioned below. .PP All interpreted strings go through backslash, uparrow and percent interpretation. The backslash escapes are the normal ones (such as \en, \et, \ennn, etc.). The uparrow escapes indicate control codes in the normal fashion. Backslashes or uparrows to be passed through should be escaped with 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). (On a Eunice system with the LINKART option, %P/%c/%a returns the name of the article in the current newsgroup, while %A returns the real name of the article, which may be different if the current article was posted to multiple newsgroups.) .Ip %b 8 Destination of last save command, often a mailbox. .Ip %B 8 The byte offset to the beginning of the part of the article to be saved, set by the save command. The \*(L's\*(R' and \*(L'S\*(R' commands set it to 0, and the \*(L'w\*(R' and \*(L'W\*(R' commands set it to the byte offset of the body of the article. .Ip %c 8 Current newsgroup, directory form. .Ip %C 8 Current newsgroup, dot form. .Ip %d 8 Full name of newsgroup directory (%P/%c). .Ip %D 8 \*(L"Distribution:\*(R" line from the current article. .Ip %f 8 \*(L"From:\*(R" line from the current article, or the \*(L"Reply-To:\*(R" line if there is one. This differs from %t in that comments (such as the full name) are not stripped out with %f. .Ip %F 8 \*(L"Newsgroups:\*(R" line for a new article, constructed from \*(L"Newsgroups:\*(R" and \*(L"Followup-To:\*(R" lines of current article. .Ip %h 8 Name of the header file to pass to the mail or news poster, containing all the information that the poster program needs in the form of a message header. It may also contain a copy of the current article. The format of the header file is controlled by the MAILHEADER and NEWSHEADER environment variables. .Ip %H 8 Host name (your machine's name). .Ip %i 8 \*(L"Message-I.D.:\*(R" line from the current article, with <> guaranteed. .Ip %I 8 The reference indication mark (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 rn, for use in conditional macros. .Sp .nf i Initializing. n Newsgroup selection level. a Article selection level. p Pager level. m Miscellaneous questions. .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 rn from). .Ip %p 8 Your private news directory, normally ~/News. .Ip %P 8 Public news spool directory, normally /usr/spool/news. .Ip %r 8 Last reference on references line of current article (parent article id). .Ip %R 8 References list for a new article, constructed from the references and article ID of the current article. .Ip %s 8 Subject, with all Re's and (nf)'s stripped off. .Ip %S 8 Subject, with one \*(L"Re:\*(R" stripped off. .Ip %t 8 \*(L"To:\*(R" line derived from the \*(L"From:\*(R" and \*(L"Reply-To:\*(R" lines of the current article. This always returns an Internet format address. .Ip %T 8 \*(L"To:\*(R" line derived from the \*(L"Path:\*(R" line of the current article to produce a uucp path. .Ip %u 8 The number of unread articles in the current newsgroup. .Ip %U 8 The number of unread articles in the current newsgroup, not counting the current article. .Ip %x 8 The news library directory. .Ip %X 8 The rn library directory. .Ip %z 8 The length of the current article in bytes. .Ip %~ 8 Your home directory. .Ip %. 8 The directory containing your dot files, which is your home directory unless the environment variable DOTDIR is defined when rn is invoked. .Ip %$ 8 Current process number. .Ip %/ 8 Last search string. .Ip %% 8 A percent sign. .Ip "%{name} or %{name\-default}" 8 The environment variable \*(L"name\*(R". .Ip %[name] 8 The value of header line \*(L"Name:\*(R" from the current article. The \*(L"Name:\ \*(R" is not included. For example \*(L"%D\*(R" and \*(L"%[distribution]\*(R" are equivalent. The name must be spelled out in full. .Ip %`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 metacharacters (\*(L'=\*(R', \*(L'?\*(R', \*(L':\*(R', or \*(L')\*(R'), precede with a backslash. .Ip %digit 8 The digits 1 through 9 interpolate the string matched by the nth bracket in the last pattern match that had brackets. If the last pattern had alternatives, you may not know the number of the bracket you want\*(--%0 will give you the last bracket matched. .PP Modifiers: to capitalize the first letter, insert \*(L'^\*(R': \*(L"%^C\*(R" produces something like \*(L"Net.jokes\*(R". Inserting \*(L'_\*(R' causes the first letter following the last \&\*(L'/\*(R' to be capitalized: \*(L"%_c\*(R" produces \*(L"net/Jokes\*(R". .SH ENVIRONMENT The following environment variables are paid attention to by .IR rn . In general the default values assumed for these variables by .I rn are reasonable, so if you are using .I rn for the first time, you can safely ignore this section. 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 rn source directory, and the file INIT in the rn library. .PP Those variables marked (%) are subject to % interpolation, and those marked (~) are subject to both % interpolation and ~ interpretation. .Ip "ATTRIBUTION (%)" 8 Gives the format of the attribution line in front of the quoted article included by an F command. .Sp Default: In article %i %f writes: .Ip "CANCEL (~)" 8 The shell command used to cancel an article. .Sp Default: inews \-h < %h .Ip "CANCELHEADER (%)" 8 13v The format of the file to pass to the CANCEL command in order to cancel an article. .Sp Default: .br Newsgroups: %n .br Subject: cmsg cancel %i .br References: %R .br Reply-To: %L@%H.UUCP (%N) .br Distribution: %D .br Organization: %o .sp 1 %i cancelled from rn. .Ip DOTDIR 8 Where to find your dot files, if they aren't in your home directory. Can be interpolated using \*(L"%.\*(R". .Sp Default: $HOME .Ip "EDITOR (~)" 8 The name of your editor, if VISUAL is undefined. .Sp Default: whatever your news administrator compiled in, usually vi. .Ip "FIRSTLINE (%)" 8 Controls the format of the line displayed at the top of an article. Warning: this may go away. .Sp Default: Article %a %(%U%M!=^00$?(%U more%(%M!=^0$? + %M Marked to return)\e) )in %C:, more or less. .Ip 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 "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 Newsgroups: %n .br In-Reply-To: %i) .br %(%[references]!=^$?References\\: %[references] .br )Organization: %o .br Cc: .br Bcc: \en\en .Ip "MAILPOSTER (~)" 8 The shell command to be used by the reply commands (r and R) in order to allow you to enter and deliver the response. .I Rn will not itself call upon an editor for replies\*(--this is a function of the program called by .IR rn . See also MAILHEADER. .Sp Default: Rnmail \-h %h .Ip "MBOXSAVER (~)" 8 The shell command to save an article in mailbox format. .Sp Default: %X/mbox.saver %A %P %c %a %B %C "%b" \e .br "From: %T %`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 NAME 8 Your full name. May be interpolated using \*(L"%N\*(R". .Sp Default: name from /etc/passwd, or ~/.fullname. .Ip "NEWSHEADER (%)" 8 16v The format of the header file for followups. See also NEWSPOSTER. .Sp Default: .Sp Newsgroups: %(%F=^$?%C:%F) .br Subject: %(%S=^$?%"\n\nSubject: ":Re: %S) .br Summary: .br Expires: .br %(%R=^$?:References: %R .br )Sender: .br Reply-To: %L@%H.UUCP (%N) .br Followup-To: .br Distribution: %(%i=^$?%"\nDistribution: ":%D) .br Organization: %o .br Keywords: \en\en .Ip "NEWSPOSTER (~)" 8 The shell command to be used by the followup commands (f and F) in order to allow you to enter and post a followup news article. .I Rn will not itself call upon an editor for followups\*(--this is a function of the program called by .IR rn . See also NEWSHEADER. .Sp Default: Pnews \-h %h .Ip "NORMSAVER (~)" 8 The shell command to save an article in the normal (non-mailbox) format. .Sp Default: %X/norm.saver %A %P %c %a %B %C "%b" .Ip ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. May be interpolated using \*(L"%o\*(R". .Sp Default: whatever your news administrator compiled in. .Ip 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 RNINIT 8 Default values for switches may be passed to .I rn by placing them in RNINIT. Any switch that is set in RNINIT may be overruled on the command line, or via the \*(L'&\*(R' command from within .IR rn . Binary-valued switches that are set with \*(L"\-switch\*(R" may be unset using \*(L"+switch\*(R". .Sp If RNINIT begins with a \*(L'/\*(R' it is assumed to be the name of a file containing switches. If you want to set many environment variables but don't want to keep them all in your environment, or if the use of any of these variables conflicts with other programs, you can use this feature along with the .B \-E switch to set the environment variables upon startup. .Sp Default: \*(L" \*(R". .Ip "RNMACRO (~)" 8 The name of the file containing macros and key mappings. See the 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 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 TERM 8 Determines which termcap entry to use, unless TERMCAP contains the entry. .Ip TERMCAP 8 Holds either the name of your termcap file, or a termcap entry. .Sp Default: /etc/termcap, normally. .Ip USER 8 Your login name. May be interpolated using \*(L"%L\*(R". .Sp Default: $LOGNAME .Ip "VISUAL (~)" 8 The name of your editor. .Sp Default: $EDITOR .Ip "YOUSAID (%)" 8 Gives the format of the attribution line in front of the quoted article included by an R command. .Sp Default: In article %i you write: .SH MACROS When .I rn starts up, it looks for a file containing macro definitions (see environment variable RNMACRO). Any sequence of commands may be bound to any sequence of keys, so you could remap your entire keyboard if you desire. Blank lines or lines beginning with # in the macro file are considered comments; otherwise .I rn 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 uparrow 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 rn .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 rn 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 rn could be used to make a command that only works at a particular level. For example, .Sp ^[[O %(%m=p?\e040) .Sp will only allow the macro to work at the pager level. .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 rn that it should gobble up one character after the F. .SH AUTHOR Larry Wall .br Regular expression routines are borrowed from emacs, by James Gosling. .SH FILES .Ip "%./.newsrc" 1.25i status of your news reading .Ip "%./.oldnewsrc" 1.25i backup copy of your .I .newsrc from start of session .Ip "%./.rnlock" 1.25i lock file so you don't screw up your .I .newsrc .Ip "%./.rnlast" 1.25i info from last run of rn .Ip "%./.rnsoft" 1.25i soft pointers into /usr/lib/active to speed startup, synchronous with .I .newsrc .Ip "%./.rnhead" 1.25i temporary header file to pass to a mailer or news poster .Ip "%./.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 .Ip "%P" 1.25i the public news spool directory, usually /usr/spool/news .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 rn knows about. .PP The \*(L'\-\*(R' command doesn't cross newsgroup boundaries, and only undoes the last article selection. .PP If you edit your .I .newsrc while .I rn is running, .I rn will happily wipe out your changes when it decides to write out the .I .newsrc file. .PP .I Rn doesn't do certain things (like ordering articles on posting date) that the author feels should be handled by inews. .PP Marking of duplicate articles as read in cross-referenced newsgroups will not work unless the Xref patch is installed in inews. .PP If you get carried away with % or escape substitutions, you can overflow buffers. .PP There should be no fixed limit on the number of newsgroups. .PP Some of the more esoteric features may be missing on machines with limited address space. 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 undefinern.c 664 540 12 30365 3477433014 4722 /* rn -- new readnews program * * From: lwall@sdcrdcf.UUCP (Larry Wall) * Organization: System Development Corporation, Santa Monica * * begun: 01/14/83 * 1.0: 04/08/83 * 2.0: 09/01/83 */ static char rnid[] = "@(#)$Header: rn.c,v 4.3.1.3 85/05/16 16:47:10 lwall Exp $"; /* $Log: rn.c,v $ * Revision 4.3.1.3 85/05/16 16:47:10 lwall * Catchup confirmation didn't grok -t. * * Revision 4.3.1.2 85/05/13 09:34:53 lwall * Fixed default after do_newsgroup() returns from Q command. * * Revision 4.3.1.1 85/05/10 11:38:08 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:47:56 lwall * Baseline for release with 4.3bsd. * */ #include "INTERN.h" #include "common.h" #include "rn.h" #include "EXTERN.h" #include "rcstuff.h" #include "term.h" #include "final.h" #include "ngdata.h" #include "util.h" #include "only.h" #include "ngsrch.h" #include "help.h" #include "last.h" #include "init.h" #include "intrp.h" #include "rcln.h" #include "sw.h" #include "addng.h" #include "ng.h" #include "INTERN.h" void rn_init() { ; } void main(argc,argv) int argc; char *argv[]; { bool foundany = initialize(argc,argv); register char *s; bool oh_for_the_good_old_days = FALSE; if (maxngtodo) starthere = 0; else if (!foundany) { /* nothing to do? */ #ifdef VERBOSE if (verbose) fputs("\ No unread news in subscribed-to newsgroups. To subscribe to a new\n\ newsgroup use the g command.\n\ ",stdout) FLUSH; #endif starthere = nextrcline; } /* loop through all unread news */ { char promptbuf[80]; bool special = FALSE; /* temporarily allow newsgroup */ /* with no unread news? */ bool retry; /* cycle back to top of list? */ NG_NUM recent_ng = 0; current_ng = 0; do { retry = FALSE; if (findlast) { findlast = FALSE; starthere = 0; if (*lastngname) { if ((ng = find_ng(lastngname)) == nextrcline) ng = 0; else { set_ngname(lastngname); set_toread(ng); if (toread[ng] <= TR_NONE) ng = 0; } } } else { ng = starthere; starthere = 0; } while (ng <= nextrcline) { /* for each newsgroup */ mode = 'n'; if (ng >= nextrcline) { /* after the last newsgroup? */ ng = nextrcline; /* force it to 1 after */ #ifdef ONLY if (maxngtodo) { if (retry) #ifdef VERBOSE IF(verbose) printf("\nRestriction %s%s still in effect.\n", ngtodo[0], maxngtodo > 1 ? ", etc." : nullstr) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(\"Only\" mode.)\n",stdout) FLUSH; #endif else { #ifdef VERBOSE IF(verbose) fputs("\nNo articles under restriction.", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNo \"only\" articles.",stdout) FLUSH; #endif end_only(); /* release the restriction */ retry = TRUE; } } #endif dfltcmd = (retry ? "npq" : "qnp"); #ifdef VERBOSE IF(verbose) sprintf(promptbuf, "\n******** End of newsgroups--what next? [%s] ", dfltcmd); ELSE #endif #ifdef TERSE sprintf(promptbuf, "\n**** End--next? [%s] ", dfltcmd); #endif } else { bool shoe_fits; /* newsgroup matches restriction? */ if (toread[ng] >= TR_NONE) { /* recalc toread? */ set_ngname(rcline[ng]); if (shoe_fits = (special || inlist(ngname))) set_toread(ng); if (paranoid) { recent_ng = current_ng; current_ng = ng; cleanup_rc(); /* this may move newsgroups around */ ng = current_ng; set_ngname(rcline[ng]); } } if (toread[ng] < (maxngtodo||special ? TR_NONE : TR_ONE) || !shoe_fits) { /* unwanted newsgroup? */ ng++; /* then skip it */ continue; } dfltcmd = "ynq"; #ifdef VERBOSE IF(verbose) sprintf(promptbuf, "\n******** %3ld unread article%c in %s--read now? [%s] ", (long)toread[ng], (toread[ng]==TR_ONE ? ' ' : 's'), ngname, dfltcmd); /* format prompt string */ ELSE #endif #ifdef TERSE sprintf(promptbuf, "\n**** %3ld in %s--read? [%s] ", (long)toread[ng], ngname,dfltcmd); /* format prompt string */ #endif } special = FALSE; /* go back to normal mode */ if (ng != current_ng) { recent_ng = current_ng; /* remember previous newsgroup */ current_ng = ng; /* remember current newsgroup */ } reask_newsgroup: unflush_output(); /* disable any ^O in effect */ fputs(promptbuf,stdout) FLUSH;/* print prompt */ fflush(stdout); reinp_newsgroup: eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') { putchar('\n') FLUSH; /* if return from stop signal */ goto reask_newsgroup; /* give them a prompt again */ } setdef(buf,dfltcmd); #ifdef VERIFY printcmd(); #endif switch (*buf) { case 'p': /* find previous unread newsgroup */ do { if (ng <= 0) break; ng--; if (toread[ng] == TR_NONE) set_toread(ng); } while (toread[ng] <= TR_NONE); break; case 'P': /* goto previous newsgroup */ do { if (ng <= 0) break; ng--; } while (toread[ng] < TR_NONE); special = TRUE; /* don't skip it if toread==0 */ break; case '-': ng = recent_ng; /* recall previous newsgroup */ special = TRUE; /* don't skip it if toread==0 */ break; case 'q': case 'Q': case 'x': /* quit? */ oh_for_the_good_old_days = (*buf == 'x'); putchar('\n') FLUSH; ng = nextrcline+1; /* satisfy */ retry = FALSE; /* loop conditions */ break; case '^': putchar('\n') FLUSH; ng = 0; break; case 'n': case '+': /* find next unread newsgroup */ if (ng == nextrcline) { putchar('\n') FLUSH; retry = TRUE; } else if (toread[ng] > TR_NONE) retry = TRUE; ng++; break; case 'N': /* goto next newsgroup */ ng++; special = TRUE; /* and don't skip it if toread==0 */ break; case '1': /* goto 1st newsgroup */ ng = 0; special = TRUE; /* and don't skip it if toread==0 */ break; case '$': ng = nextrcline; /* goto last newsgroup */ retry = TRUE; break; case 'L': list_newsgroups(); goto reask_newsgroup; case '/': case '?': /* scan for newsgroup pattern */ #ifdef NGSEARCH switch (ng_search(buf,TRUE)) { case NGS_ABORT: goto reinp_newsgroup; case NGS_INTR: #ifdef VERBOSE IF(verbose) fputs("\n(Interrupted)\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(Intr)\n",stdout) FLUSH; #endif ng = current_ng; goto reask_newsgroup; case NGS_FOUND: special = TRUE; /* don't skip it if toread==0 */ break; case NGS_NOTFOUND: #ifdef VERBOSE IF(verbose) fputs("\n\nNot found--use g to add newsgroups\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\nNot found\n",stdout) FLUSH; #endif goto reask_newsgroup; } #else notincl("/"); #endif break; case 'm': #ifndef RELOCATE notincl("m"); break; #endif case 'g': /* goto named newsgroup */ if (!finish_command(FALSE)) /* if they didn't finish command */ goto reinp_newsgroup; /* go try something else */ for (s = buf+1; *s == ' '; s++); /* skip leading spaces */ if (!*s) strcpy(s,ngname); #ifdef RELOCATE if (!get_ng(s,*buf=='m')) /* try to find newsgroup */ #else if (!get_ng(s,FALSE)) /* try to find newsgroup */ #endif ng = current_ng;/* if not found, go nowhere */ special = TRUE; /* don't skip it if toread==0 */ break; #ifdef DEBUGGING case 'D': printf("\nTries: %d Hits: %d\n", softtries,softtries-softmisses) FLUSH; goto reask_newsgroup; #endif case '!': /* shell escape */ if (escapade()) /* do command */ goto reinp_newsgroup; /* if rubbed out, re input */ goto reask_newsgroup; case Ctl('k'): /* edit global KILL file */ edit_kfile(); goto reask_newsgroup; case 'c': /* catch up */ #ifdef CATCHUP reask_catchup: #ifdef VERBOSE IF(verbose) in_char("\nDo you really want to mark everything as read? [yn] "); ELSE #endif #ifdef TERSE in_char("\nReally? [ynh] "); #endif putchar('\n') FLUSH; setdef(buf,"y"); if (*buf == 'h') { #ifdef VERBOSE printf("Type y or SP to mark all articles as read.\n"); printf("Type n to leave articles marked as they are.\n"); #else printf("y or SP to mark all read.\n"); printf("n to forget it.\n"); #endif goto reask_catchup; } else if (*buf!=' ' && *buf!='y' && *buf!='n' && *buf!='q') { printf(hforhelp); settle_down(); goto reask_catchup; } else if ( (*buf == ' ' || *buf == 'y') && ng= TR_NONE) { /* unsubscribable? */ printf(unsubto,rcline[ng]) FLUSH; rcchar[ng] = NEGCHAR; /* unsubscribe to (from?) it */ toread[ng] = TR_UNSUB; /* and make line invisible */ ng++; /* do an automatic 'n' */ } break; case 'h': { /* help */ int cmd; if ((cmd = help_ng()) > 0) pushchar(cmd); goto reask_newsgroup; } case 'a': #ifndef FINDNEWNG notincl("a"); goto reask_newsgroup; #else /* FALL THROUGH */ #endif case 'o': #ifdef ONLY { #ifdef FINDNEWNG bool doscan = (*buf == 'a'); #endif if (!finish_command(TRUE)) /* get rest of command */ goto reinp_newsgroup; /* if rubbed out, try something else */ end_only(); if (buf[1]) { bool minusd = instr(buf+1,"-d") != Nullch; sw_list(buf+1); if (minusd) cwd_check(); putchar('\n') FLUSH; #ifdef FINDNEWNG if (doscan && maxngtodo) scanactive(); #endif } ng = 0; /* simulate ^ */ retry = FALSE; break; } #else notincl("o"); goto reask_newsgroup; #endif case '&': if (switcheroo()) /* get rest of command */ goto reinp_newsgroup; /* if rubbed out, try something else */ goto reask_newsgroup; case 'l': { /* list other newsgroups */ if (!finish_command(TRUE)) /* get rest of command */ goto reinp_newsgroup; /* if rubbed out, try something else */ for (s = buf+1; *s == ' '; s++); /* skip leading spaces */ sprintf(cmd_buf,"%s '%s'",filexp(NEWSGROUPS),s); resetty(); if (doshell(sh,cmd_buf)) #ifdef VERBOSE IF(verbose) fputs(" (Error from newsgroups program)\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("(Error)\n",stdout) FLUSH; #endif noecho(); crmode(); goto reask_newsgroup; } case '.': case '=': case 'y': case 'Y': /* do normal thing */ if (ng >= nextrcline) { fputs("\nNot on a newsgroup.",stdout) FLUSH; goto reask_newsgroup; } if (*buf == '=') s = savestr("="); else if (*buf == '.') { /* start command? */ if (!finish_command(FALSE)) /* get rest of command */ goto reinp_newsgroup; s = savestr(buf+1); /* do_newsgroup will free it */ } else s = Nullch; if (toread[ng]) retry = TRUE; switch (do_newsgroup(s)) { case NG_ERROR: case NG_NORM: ng++; break; case NG_ASK: dfltcmd = "ynq"; goto reask_newsgroup; case NG_MINUS: ng = recent_ng; /* recall previous newsgroup */ special = TRUE; /* don't skip it if toread==0 */ break; } break; #ifdef STRICTCR case '\n': fputs(badcr,stdout) FLUSH; goto reask_newsgroup; #endif case 'v': printf("\n%s\n",rnid) FLUSH; goto reask_newsgroup; default: printf("\n%s",hforhelp) FLUSH; settle_down(); goto reask_newsgroup; } } } while (retry); } /* now write .newsrc back out */ write_rc(); if (oh_for_the_good_old_days) get_old_rc(); finalize(0); /* and exit */ } /* set current newsgroup */ void set_ngname(what) char *what; { int len = strlen(what)+1; growstr(&ngname,&ngnlen,len); strcpy(ngname,what); growstr(&ngdir,&ngdlen,len); strcpy(ngdir,getngdir(ngname)); } static char *myngdir; static int ngdirlen = 0; char * getngdir(ngnam) char *ngnam; { register char *s; growstr(&myngdir,&ngdirlen,strlen(ngnam)+1); strcpy(myngdir,ngnam); for (s = myngdir; *s; s++) if (*s == '.') *s = '/'; return myngdir; } 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' instrn.h 664 540 12 1455 3473757446 4723 /* $Header: rn.h,v 4.3 85/05/01 11:48:19 lwall Exp $ * * $Log: rn.h,v $ * Revision 4.3 85/05/01 11:48:19 lwall * Baseline for release with 4.3bsd. * */ EXT char *ngname INIT(Nullch); /* name of current newsgroup */ EXT int ngnlen INIT(0); /* current malloced size of ngname */ EXT char *ngdir INIT(Nullch); /* same thing in directory name form */ EXT int ngdlen INIT(0); /* current malloced size of ngdir */ EXT NG_NUM ng INIT(0); /* current newsgroup index into rcline and toread */ EXT NG_NUM current_ng INIT(0); /* stable current newsgroup so we can ditz with ng */ EXT NG_NUM starthere INIT(0); /* set to the first newsgroup with unread news on startup */ EXT char *spool INIT(Nullch); /* public news spool directory */ void rn_init(); void main(); void set_ngname(); char *getngdir(); ad now? [%s] ", (long)toread[ng], (toread[ng]==TR_ONE ? ' ' : 's'), ngname, dfltcmd); /* format prompt string */ ELSE #endif #ifdef TERSE sprintf(promptbuf, "\n**** %3ld in %s--readsearch.c 664 540 12 31570 3473757111 5552 /* $Header: search.c,v 4.3 85/05/01 11:50:16 lwall Exp $ * * $Log: search.c,v $ * Revision 4.3 85/05/01 11:50:16 lwall * Baseline for release with 4.3bsd. * */ /* string search routines */ /* Copyright (c) 1981,1980 James Gosling */ /* Modified Aug. 12, 1981 by Tom London to include regular expressions as in ed. RE stuff hacked over by jag to correct a few major problems, mainly dealing with searching within the buffer rather than copying each line to a separate array. Newlines can now appear in RE's */ /* Ripped to shreds and glued back together to make a search package, * July 6, 1984, by Larry Wall. (If it doesn't work, it's probably my fault.) * Changes include: * Buffer, window, and mlisp stuff gone. * Translation tables reduced to 1 table. * Expression buffer is now dynamically allocated. * Character classes now implemented with a bitmap. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "INTERN.h" #include "search.h" #ifndef BITSPERBYTE #define BITSPERBYTE 8 #endif #define BMAPSIZ (127 / BITSPERBYTE + 1) /* meta characters in the "compiled" form of a regular expression */ #define CBRA 2 /* \( -- begin bracket */ #define CCHR 4 /* a vanilla character */ #define CDOT 6 /* . -- match anything except a newline */ #define CCL 8 /* [...] -- character class */ #define NCCL 10 /* [^...] -- negated character class */ #define CDOL 12 /* $ -- matches the end of a line */ #define CEND 14 /* The end of the pattern */ #define CKET 16 /* \) -- close bracket */ #define CBACK 18 /* \N -- backreference to the Nth bracketed string */ #define CIRC 20 /* ^ matches the beginning of a line */ #define WORD 32 /* matches word character \w */ #define NWORD 34 /* matches non-word characer \W */ #define WBOUND 36 /* matches word boundary \b */ #define NWBOUND 38 /* matches non-(word boundary) \B */ #define STAR 01 /* * -- Kleene star, repeats the previous REas many times as possible; the value ORs with the other operator types */ #define ASCSIZ 0200 typedef char TRANSTABLE[ASCSIZ]; static TRANSTABLE trans = { 0000,0001,0002,0003,0004,0005,0006,0007, 0010,0011,0012,0013,0014,0015,0016,0017, 0020,0021,0022,0023,0024,0025,0026,0027, 0030,0031,0032,0033,0034,0035,0036,0037, 0040,0041,0042,0043,0044,0045,0046,0047, 0050,0051,0052,0053,0054,0055,0056,0057, 0060,0061,0062,0063,0064,0065,0066,0067, 0070,0071,0072,0073,0074,0075,0076,0077, 0100,0101,0102,0103,0104,0105,0106,0107, 0110,0111,0112,0113,0114,0115,0116,0117, 0120,0121,0122,0123,0124,0125,0126,0127, 0130,0131,0132,0133,0134,0135,0136,0137, 0140,0141,0142,0143,0144,0145,0146,0147, 0150,0151,0152,0153,0154,0155,0156,0157, 0160,0161,0162,0163,0164,0165,0166,0167, 0170,0171,0172,0173,0174,0175,0176,0177, }; static bool folding = FALSE; static int err; static char *FirstCharacter; void search_init() { #ifdef UNDEF register int i; for (i = 0; i < ASCSIZ; i++) trans[i] = i; #else ; #endif } void init_compex(compex) register COMPEX *compex; { /* the following must start off zeroed */ compex->eblen = 0; compex->brastr = Nullch; } void free_compex(compex) register COMPEX *compex; { if (compex->eblen) { free(compex->expbuf); compex->eblen = 0; } if (compex->brastr) { free(compex->brastr); compex->brastr = Nullch; } } static char *gbr_str = Nullch; static int gbr_siz = 0; char * getbracket(compex,n) register COMPEX *compex; int n; { int length = compex->braelist[n] - compex->braslist[n]; if (!compex->nbra || n > compex->nbra || !compex->braelist[n] || length<0) return nullstr; growstr(&gbr_str, &gbr_siz, length+1); safecpy(gbr_str, compex->braslist[n], length+1); return gbr_str; } void case_fold(which) int which; { register int i; if (which != folding) { if (which) { for (i = 'A'; i <= 'Z'; i++) trans[i] = tolower(i); } else { for (i = 'A'; i <= 'Z'; i++) trans[i] = i; } folding = which; } } /* Compile the given regular expression into a [secret] internal format */ char * compile (compex, strp, RE, fold) register COMPEX *compex; register char *strp; int RE; int fold; { register int c; register char *ep; char *lastep; char bracket[NBRA], *bracketp; char **alt = compex->alternatives; char *retmes = "Badly formed search string"; case_fold(compex->do_folding = fold); if (!compex->eblen) { compex->expbuf = safemalloc(84); compex->eblen = 80; } ep = compex->expbuf; /* point at expression buffer */ *alt++ = ep; /* first alternative starts here */ bracketp = bracket; /* first bracket goes here */ if (*strp == 0) { /* nothing to compile? */ if (*ep == 0) /* nothing there yet? */ return "Null search string"; return Nullch; /* just keep old expression */ } compex->nbra = 0; /* no brackets yet */ lastep = 0; for (;;) { if (ep - compex->expbuf >= compex->eblen) grow_eb(compex); c = *strp++; /* fetch next char of pattern */ if (c == 0) { /* end of pattern? */ if (bracketp != bracket) { /* balanced brackets? */ #ifdef VERBOSE retmes = "Unbalanced parens"; #endif goto cerror; } *ep++ = CEND; /* terminate expression */ *alt++ = 0; /* terminal alternative list */ /* compex->eblen = ep - compex->expbuf + 1; compex->expbuf = saferealloc(compex->expbuf,compex->eblen+4); */ return Nullch; /* return success */ } if (c != '*') lastep = ep; if (!RE) { /* just a normal search string? */ *ep++ = CCHR; /* everything is a normal char */ *ep++ = c; } else /* it is a regular expression */ switch (c) { case '\\': /* meta something */ switch (c = *strp++) { case '(': if (compex->nbra >= NBRA) { #ifdef VERBOSE retmes = "Too many parens"; #endif goto cerror; } *bracketp++ = ++compex->nbra; *ep++ = CBRA; *ep++ = compex->nbra; break; case '|': if (bracketp>bracket) { #ifdef VERBOSE retmes = "No \\| in parens"; /* Alas! */ #endif goto cerror; } *ep++ = CEND; *alt++ = ep; break; case ')': if (bracketp <= bracket) { #ifdef VERBOSE retmes = "Unmatched right paren"; #endif goto cerror; } *ep++ = CKET; *ep++ = *--bracketp; break; case 'w': *ep++ = WORD; break; case 'W': *ep++ = NWORD; break; case 'b': *ep++ = WBOUND; break; case 'B': *ep++ = NWBOUND; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *ep++ = CBACK; *ep++ = c - '0'; break; default: *ep++ = CCHR; if (c == '\0') goto cerror; *ep++ = c; break; } break; case '.': *ep++ = CDOT; continue; case '*': if (lastep == 0 || *lastep == CBRA || *lastep == CKET || *lastep == CIRC || (*lastep&STAR)|| *lastep>NWORD) goto defchar; *lastep |= STAR; continue; case '^': if (ep != compex->expbuf && ep[-1] != CEND) goto defchar; *ep++ = CIRC; continue; case '$': if (*strp != 0 && (*strp != '\\' || strp[1] != '|')) goto defchar; *ep++ = CDOL; continue; case '[': { /* character class */ register int i; if (ep - compex->expbuf >= compex->eblen - BMAPSIZ) grow_eb(compex); /* reserve bitmap */ for (i = BMAPSIZ; i; --i) ep[i] = 0; if ((c = *strp++) == '^') { c = *strp++; *ep++ = NCCL; /* negated */ } else *ep++ = CCL; /* normal */ i = 0; /* remember oldchar */ do { if (c == '\0') { #ifdef VERBOSE retmes = "Missing ]"; #endif goto cerror; } if (*strp == '-' && *(++strp)) i = *strp++; else i = c; while (c <= i) { ep[c / BITSPERBYTE] |= 1 << (c % BITSPERBYTE); if (fold && isalpha(c)) ep[(c ^ 32) / BITSPERBYTE] |= 1 << ((c ^ 32) % BITSPERBYTE); /* set the other bit too */ c++; } } while ((c = *strp++) != ']'); ep += BMAPSIZ; continue; } defchar: default: *ep++ = CCHR; *ep++ = c; } } cerror: compex->expbuf[0] = 0; compex->nbra = 0; return retmes; } void grow_eb(compex) register COMPEX *compex; { compex->eblen += 80; compex->expbuf = saferealloc(compex->expbuf, (MEM_SIZE)compex->eblen + 4); } char * execute (compex, addr) register COMPEX *compex; char *addr; { register char *p1 = addr; register char *trt = trans; register int c; if (addr == Nullch) return Nullch; if (compex->nbra) { /* any brackets? */ for (c = 0; c <= compex->nbra; c++) compex->braslist[c] = compex->braelist[c] = Nullch; if (compex->brastr) free(compex->brastr); compex->brastr = savestr(p1); /* in case p1 is not static */ p1 = compex->brastr; /* ! */ } case_fold(compex->do_folding); /* make sure table is correct */ FirstCharacter = p1; /* for ^ tests */ if (compex->expbuf[0] == CCHR && !compex->alternatives[1]) { c = trt[compex->expbuf[1]]; /* fast check for first character */ do { if (trt[*p1] == c && advance (compex, p1, compex->expbuf)) return p1; p1++; } while (*p1 && !err); return Nullch; } else { /* regular algorithm */ do { register char **alt = compex->alternatives; while (*alt) { if (advance (compex, p1, *alt++)) return p1; } p1++; } while (*p1 && !err); return Nullch; } } /* advance the match of the regular expression starting at ep along the string lp, simulates an NDFSA */ bool advance (compex, lp, ep) register COMPEX *compex; register char *ep; register char *lp; { register char *curlp; register char *trt = trans; register int i; while ((*ep & STAR) || *lp || *ep == CIRC || *ep == CKET) switch (*ep++) { case CCHR: if (trt[*ep++] != trt[*lp]) return FALSE; lp++; continue; case CDOT: if (*lp == '\n') return FALSE; lp++; continue; case CDOL: if (!*lp || *lp == '\n') continue; return FALSE; case CIRC: if (lp == FirstCharacter || lp[-1]=='\n') continue; return FALSE; case WORD: if (isalnum(*lp)) { lp++; continue; } return FALSE; case NWORD: if (!isalnum(*lp)) { lp++; continue; } return FALSE; case WBOUND: if ((lp == FirstCharacter || !isalnum(lp[-1])) != (!*lp || !isalnum(*lp)) ) continue; return FALSE; case NWBOUND: if ((lp == FirstCharacter || !isalnum(lp[-1])) == (!*lp || !isalnum(*lp))) continue; return FALSE; case CEND: return TRUE; case CCL: if (cclass (ep, *lp, 1)) { ep += BMAPSIZ; lp++; continue; } return FALSE; case NCCL: if (cclass (ep, *lp, 0)) { ep += BMAPSIZ; lp++; continue; } return FALSE; case CBRA: compex->braslist[*ep++] = lp; continue; case CKET: i = *ep++; compex->braelist[i] = lp; compex->braelist[0] = lp; compex->braslist[0] = compex->braslist[i]; continue; case CBACK: if (compex->braelist[i = *ep++] == 0) { fputs("bad braces\n",stdout) 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[lp[-1]] == trt[*ep]); ep++; goto star; case CCL | STAR: case NCCL | STAR: curlp = lp; while (*lp++ && cclass (ep, lp[-1], ep[-1] == (CCL | STAR))); ep += BMAPSIZ; goto star; star: do { lp--; if (advance (compex, lp, ep)) return TRUE; } while (lp > curlp); return FALSE; default: fputs("Badly compiled pattern\n",stdout) FLUSH; err = TRUE; return -1; } if (*ep == CEND || *ep == CDOL) { return TRUE; } return FALSE; } bool backref (compex, i, lp) register COMPEX *compex; register int i; register char *lp; { register char *bp; bp = compex->braslist[i]; while (*lp && *bp == *lp) { bp++; lp++; if (bp >= compex->braelist[i]) return TRUE; } return FALSE; } bool cclass (set, c, af) register char *set; register int c; { c &= 0177; #if BITSPERBYTE == 8 if (set[c >> 3] & 1 << (c & 7)) #else if (set[c / BITSPERBYTE] & 1 << (c % BITSPERBYTE)) #endif return af; return !af; } loc(84); compex->eblen = 80; } ep = compex->expbuf; /* point at expression buffer */ *alt++ = ep; /* first alternative ssearch.h 664 540 12 2034 3473757437 5543 /* $Header: search.h,v 4.3 85/05/01 11:50:46 lwall Exp $ * * $Log: search.h,v $ * Revision 4.3 85/05/01 11:50:46 lwall * Baseline for release with 4.3bsd. * */ #ifndef NBRA #define NBRA 10 /* the maximum number of meta-brackets in an RE -- \( \) */ #define NALTS 10 /* the maximum number of \|'s */ typedef struct { char *expbuf; /* The compiled search string */ int eblen; /* Length of above buffer */ char *alternatives[NALTS]; /* The list of \| seperated alternatives */ char *braslist[NBRA]; /* RE meta-bracket start list */ char *braelist[NBRA]; /* RE meta-bracket end list */ char *brastr; /* saved match string after execute() */ char nbra; /* The number of meta-brackets int the most recenlty compiled RE */ bool do_folding; /* fold upper and lower case? */ } COMPEX; void search_init(); void init_compex(); void free_compex(); char *getbracket(); void case_fold(); char *compile(); void grow_eb(); char *execute(); bool advance(); bool backref(); bool cclass(); #endif register char *lp; { register char *curlp; register char *trt = trans; register int i; while ((*ep & STAR) || *lp || *ep == CIRC || *ep == CKET) switch (*ep++) { case CCHR: if (trt[*ep++] != trt[*lp]) return FALSE; lp++; continue; case CDOT: if (*lp == '\n') return FALSE; lp++; continue; case CDOL: if (!*lp || *lp == '\n') continue; return FALSE; case CIRC: if (lp == FirstCharacter || lp[-1]=='\n') contisubs.help.SH 664 540 12 3567 3473757403 6271 case $CONFIG in '') . config.sh ;; esac echo "Extracting subs.help (with variable substitutions)" $spitshell >subs.help < I Reference indicator mark (see -F switch) l News administrator's login name, if any L Login name (yours) m Current mode, first letter of (init,newsgroup,article,pager,misc) M Number of article marked with M n Newsgroups from current article N Full name (yours) o Organization (yours) O Original working directory (where you ran rn from) p Your private news directory (from -d) P Public news spool directory r Last reference (parent article id) R References list for followup article s Subject, with all Re's and (nf)'s stripped off S Subject, with one Re stripped off\ t New To line derived from From and Reply-To (Internet format) T New To line derived from Path u Number of unread articles U Number of unread articles not counting current article x News library directory X Rn library directory z Length of current article in bytes ~ Your home directory . Directory containing . files $ Current process number / Last search string ESC Run preceding command through % interpretation EOT !GROK!THIS! $eunicefix subs.help chmod 755 subs.help acter || !isalnum(lp[-1])) == (!*lp || !isalnum(*lp))) continue; return FALSE; case CEND: return TRUE; case CCsw.c 664 540 12 23430 3477433161 4732 /* $Header: sw.c,v 4.3.1.2 85/05/21 13:36:23 lwall Exp $ * * $Log: sw.c,v $ * Revision 4.3.1.2 85/05/21 13:36:23 lwall * Sped up "rn -c" by not doing unnecessary initialization. * * Revision 4.3.1.1 85/05/10 11:40:38 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:50:54 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "head.h" #include "only.h" #include "term.h" #include "ng.h" #include "intrp.h" #include "INTERN.h" #include "sw.h" void sw_init(argc,argv,tcbufptr) int argc; char *argv[]; char **tcbufptr; { register int i; if (argc >= 2 && strEQ(argv[1],"-c")) checkflag=TRUE; /* so we can optimize for -c */ interp(*tcbufptr,1024,GLOBINIT); sw_file(tcbufptr,FALSE); safecpy(*tcbufptr,getenv("RNINIT"),1024); if (**tcbufptr) { if (**tcbufptr == '/') { sw_file(tcbufptr,TRUE); } else sw_list(*tcbufptr); } for (i = 1; i < argc; i++) decode_switch(argv[i]); } void sw_file(tcbufptr,bleat) char **tcbufptr; bool bleat; { int initfd = open(*tcbufptr,0); if (initfd >= 0) { fstat(initfd,&filestat); if (filestat.st_size > 1024) *tcbufptr = saferealloc(*tcbufptr,(MEM_SIZE)filestat.st_size); if (filestat.st_size) { read(initfd,*tcbufptr,(int)filestat.st_size); (*tcbufptr)[filestat.st_size-1] = '\0'; /* wipe out last newline */ sw_list(*tcbufptr); } else **tcbufptr = '\0'; close(initfd); } else { if (bleat) printf(cantopen,*tcbufptr) FLUSH; **tcbufptr = '\0'; } } /* decode a list of space separated switches */ void sw_list(swlist) char *swlist; { char *tmplist = safemalloc((MEM_SIZE) strlen(swlist) + 2); /* semi-automatic string */ register char *p, inquote = 0; strcpy(tmplist,swlist); for (p=tmplist; isspace(*p); p++) ; /* skip any initial spaces */ while (*p) { /* "String, or nothing" */ if (!inquote && isspace(*p)) { /* word delimiter? */ *p++ = '\0'; /* chop here */ while (isspace(*p)) /* these will be ignored later */ p++; } else if (inquote == *p) { strcpy(p,p+1); /* delete trailing quote */ inquote = 0; /* no longer quoting */ } else if (!inquote && *p == '"' || *p == '\'') { /* OK, I know when I am not wanted */ inquote = *p; /* remember single or double */ strcpy(p,p+1); /* delete the quote */ } /* (crude, but effective) */ else if (*p == '\\') { /* quoted something? */ if (p[1] == '\n') /* newline? */ strcpy(p,p+2); /* "I didn't see anything" */ else { strcpy(p,p+1); /* delete the backwhack */ p++; /* leave the whatever alone */ } } else p++; /* normal char, leave it alone */ } *++p = '\0'; /* put an extra null on the end */ if (inquote) printf("Unmatched %c in switch\n",inquote) FLUSH; for (p = tmplist; *p; /* p += strlen(p)+1 */ ) { decode_switch(p); while (*p++) ; /* point at null + 1 */ } free(tmplist); /* this oughta be in Ada */ } /* decode a single switch */ void decode_switch(s) register char *s; { while (isspace(*s)) /* ignore leading spaces */ s++; #ifdef DEBUGGING if (debug) printf("Switch: %s\n",s) FLUSH; #endif if (*s != '-' && *s != '+') { /* newsgroup pattern */ setngtodo(s); } else { /* normal switch */ bool upordown = *s == '-' ? TRUE : FALSE; char tmpbuf[LBUFLEN]; s++; switch (*s) { #ifdef TERMMOD case '=': { char *beg = s+1; while (*s && *s != '-' && *s != '+') s++; cpytill(tmpbuf,beg,*s); if (upordown ? strEQ(getenv("TERM"),tmpbuf) : strNE(getenv("TERM"),tmpbuf) ) { decode_switch(s); } break; } #endif #ifdef BAUDMOD case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (upordown ? (just_a_sec*10 <= atoi(s)) : (just_a_sec*10 >= atoi(s)) ) { while (isdigit(*s)) s++; decode_switch(s); } break; #endif case '/': if (checkflag) break; #ifdef SETENV setenv("SAVEDIR", upordown ? "%p/%c" : "%p" ); setenv("SAVENAME", upordown ? "%a" : "%^C"); #else notincl("-/"); #endif break; case 'c': checkflag = upordown; break; case 'C': s++; if (*s == '=') s++; docheckwhen = atoi(s); break; case 'd': { if (checkflag) break; s++; if (*s == '=') s++; if (cwd) { chdir(cwd); free(cwd); } cwd = savestr(s); break; } #ifdef DEBUGGING case 'D': s++; if (*s == '=') s++; if (*s) if (upordown) debug |= atoi(s); else debug &= ~atoi(s); else if (upordown) debug |= 1; else debug = 0; break; #endif case 'e': erase_screen = upordown; break; case 'E': #ifdef SETENV s++; if (*s == '=') s++; strcpy(tmpbuf,s); s = index(tmpbuf,'='); if (s) { *s++ = '\0'; setenv(tmpbuf,s); } else setenv(tmpbuf,nullstr); #else notincl("-E"); #endif break; case 'F': s++; indstr = savestr(s); break; #ifdef INNERSEARCH case 'g': gline = atoi(s+1)-1; break; #endif case 'H': case 'h': { register int len, i; char *t; int flag = (*s == 'h' ? HT_HIDE : HT_MAGIC); if (checkflag) break; s++; len = strlen(s); for (t=s; *t; t++) if (isupper(*t)) *t = tolower(*t); for (i=HEAD_FIRST; i 0) { /* is this a crt? */ if (!initlines) /* no -i? */ if (ospeed >= B9600) /* whole page at >= 9600 baud */ initlines = LINES; else if (ospeed >= B4800) /* 16 lines at 4800 */ initlines = 16; else /* otherwise just header */ initlines = 8; } else { /* not a crt */ LINES = 30000; /* so don't page */ CL = "\n\n"; /* put a couple of lines between */ if (!initlines) /* make initlines reasonable */ initlines = 8; } if (COLS <= 0) COLS = 80; noecho(); /* turn off echo */ crmode(); /* enter cbreak mode */ #ifdef PUSHBACK mac_init(tcbuf); #endif } #ifdef PUSHBACK void mac_init(tcbuf) char *tcbuf; { char tmpbuf[1024]; tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r"); if (tmpfp != Nullfp) { while (fgets(tcbuf,1024,tmpfp) != Nullch) { mac_line(tcbuf,tmpbuf,(sizeof tmpbuf)); } fclose(tmpfp); } } void mac_line(line,tmpbuf,tbsize) char *line; char *tmpbuf; int tbsize; { register char *s, *m; register KEYMAP *curmap; register int ch; register int garbage = 0; static char override[] = "\nkeymap overrides string\n"; if (topmap == Null(KEYMAP*)) topmap = newkeymap(); if (*line == '#' || *line == '\n') return; if (line[ch = strlen(line)-1] == '\n') line[ch] = '\0'; m = dointerp(tmpbuf,tbsize,line," \t"); 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) { puts(override,stdout) FLUSH; free(curmap->km_ptr[ch].km_str); curmap->km_ptr[ch].km_str = Nullch; } curmap->km_type[ch] = KM_KEYMAP + garbage; if (curmap->km_ptr[ch].km_km == Null(KEYMAP*)) curmap->km_ptr[ch].km_km = newkeymap(); curmap = curmap->km_ptr[ch].km_km; } else { if ((curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP) puts(override,stdout) FLUSH; else { curmap->km_type[ch] = KM_STRING + garbage; curmap->km_ptr[ch].km_str = savestr(m); } } } } KEYMAP* newkeymap() { register int i; register KEYMAP *map; #ifndef lint map = (KEYMAP*)safemalloc(sizeof(KEYMAP)); #else map = Null(KEYMAP*); #endif lint for (i=127; i>=0; --i) { map->km_ptr[i].km_km = Null(KEYMAP*); map->km_type[i] = KM_NOTHIN; } return map; } void show_macros() { char prebuf[64]; if (topmap != Null(KEYMAP*)) { print_lines("Macros:\n",STANDOUT); *prebuf = '\0'; show_keymap(topmap,prebuf); } } 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]) { 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[(char)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; } } } } #endif /* routine to pass to tputs */ char putchr(ch) register char ch; { putchar(ch); #ifdef lint ch = Null(char); ch = ch; #endif } /* input the 2nd and succeeding characters of a multi-character command */ /* returns TRUE if command finished, FALSE if they rubbed out first character */ bool finish_command(donewline) int donewline; { register char *s; register bool quoteone = FALSE; s = buf; if (s[1] != FINISHCMD) /* someone faking up a command? */ return TRUE; do { top: if (*s < ' ') { putchar('^'); putchar(*s | 64); } else if (*s == '\177') { putchar('^'); putchar('?'); } else putchar(*s); /* echo previous character */ s++; re_read: fflush(stdout); getcmd(s); if (quoteone) { quoteone = FALSE; continue; } if (errno || *s == Ctl('l')) { *s = Ctl('r'); /* force rewrite on CONT */ } if (*s == '\033') { /* substitution desired? */ #ifdef ESCSUBS char tmpbuf[4], *cpybuf; tmpbuf[0] = '%'; read_tty(&tmpbuf[1],1); #ifdef RAWONLY tmpbuf[1] &= 0177; #endif tmpbuf[2] = '\0'; if (tmpbuf[1] == 'h') { (void) help_subs(); *s = '\0'; reprint(); goto re_read; } else if (tmpbuf[1] == '\033') { *s = '\0'; cpybuf = savestr(buf); interp(buf, (sizeof buf), cpybuf); free(cpybuf); s = buf + strlen(buf); reprint(); goto re_read; } else { interp(s,(sizeof buf) - (s-buf),tmpbuf); fputs(s,stdout); s += strlen(s); } goto re_read; #else notincl("^["); *s = '\0'; reprint(); goto re_read; #endif } else if (*s == ERASECH) { /* they want to rubout a char? */ rubout(); s--; /* discount the char rubbed out */ if (*s < ' ' || *s == '\177') rubout(); if (s == buf) { /* entire string gone? */ fflush(stdout); /* return to single char command mode */ return FALSE; } else goto re_read; } else if (*s == KILLCH) { /* wipe out the whole line? */ while (s-- != buf) { /* emulate that many ERASEs */ rubout(); if (*s < ' ' || *s == '\177') rubout(); } fflush(stdout); return FALSE; /* return to single char mode */ } #ifdef WORDERASE else if (*s == Ctl('w')) { /* wipe out one word? */ *s-- = ' '; while (!isspace(*s) || isspace(s[1])) { rubout(); if (s-- == buf) { fflush(stdout); return FALSE; /* return to single char mode */ } if (*s < ' ' || *s == '\177') rubout(); } s++; goto re_read; } #endif else if (*s == Ctl('r')) { *s = '\0'; reprint(); goto re_read; } else if (*s == Ctl('v')) { putchar('^'); backspace(); fflush(stdout); getcmd(s); goto top; } else if (*s == '\\') { quoteone = TRUE; } } while (*s != '\n'); /* till a newline (not echoed) */ *s = '\0'; /* terminate the string nicely */ if (donewline) putchar('\n') FLUSH; return TRUE; /* say we succeeded */ } /* discard any characters typed ahead */ void eat_typeahead() { #ifdef PUSHBACK if (!typeahead && nextin==nextout) /* cancel only keyboard stuff */ #else if (!typeahead) #endif { #ifdef PENDING while (input_pending()) read_tty(buf,sizeof(buf)); #else /* this is probably v7 */ ioctl(_tty_ch,TIOCSETP,&_tty); #endif } } void settle_down() { dingaling(); fflush(stdout); sleep(1); #ifdef PUSHBACK nextout = nextin; /* empty circlebuf */ #endif eat_typeahead(); } #ifdef PUSHBACK /* read a character from the terminal, with multi-character pushback */ int read_tty(addr,size) char *addr; int size; { if (nextout != nextin) { *addr = circlebuf[nextout++]; nextout %= PUSHSIZE; return 1; } else { size = read(0,addr,size); #ifdef RAWONLY *addr &= 0177; #endif return size; } } #ifdef PENDING #ifndef FIONREAD int circfill() { register int howmany = read(devtty,circlebuf+nextin,1); if (howmany) { nextin += howmany; nextin %= PUSHSIZE; } return howmany; } #endif PENDING #endif FIONREAD void pushchar(c) char c; { nextout--; if (nextout < 0) nextout = PUSHSIZE - 1; if (nextout == nextin) { fputs("\npushback buffer overflow\n",stdout) FLUSH; sig_catcher(0); } circlebuf[nextout] = c; } #else PUSHBACK #ifndef read_tty /* read a character from the terminal, with hacks for O_NDELAY reads */ int read_tty(addr,size) char *addr; int size; { if (is_input) { *addr = pending_ch; is_input = FALSE; return 1; } else { size = read(0,addr,size) #ifdef RAWONLY *addr &= 0177; #endif return size; } } #endif read_tty #endif PUSHBACK /* print an underlined string, one way or another */ void underprint(s) register char *s; { assert(UC); if (*UC) { /* char by char underline? */ while (*s) { if (*s < ' ') { putchar('^'); backspace();/* back up over it */ underchar();/* and do the underline */ putchar(*s+64); backspace();/* back up over it */ underchar();/* and do the underline */ } else { putchar(*s); backspace();/* back up over it */ underchar();/* and do the underline */ } s++; } } else { /* start and stop underline */ underline(); /* start underlining */ while (*s) { if (*s < ' ') { putchar('^'); putchar(*s+64); } else putchar(*s); s++; } un_underline(); /* stop underlining */ } } /* keep screen from flashing strangely on magic cookie terminals */ #ifdef NOFIREWORKS void no_sofire() { if (*UP && *SE) { /* should we disable fireworks? */ putchar('\n'); un_standout(); up_line(); carriage_return(); } } void no_ulfire() { if (*UP && *US) { /* should we disable fireworks? */ putchar('\n'); un_underline(); up_line(); carriage_return(); } } #endif /* get a character into a buffer */ void getcmd(whatbuf) register char *whatbuf; { #ifdef PUSHBACK register KEYMAP *curmap; register int i; bool no_macros; int times = 0; /* loop detector */ char scrchar; tryagain: curmap = topmap; no_macros = (whatbuf != buf && nextin == nextout); #endif for (;;) { int_count = 0; errno = 0; if (read_tty(whatbuf,1) < 0 && !errno) errno = EINTR; if (errno && errno != EINTR) { perror(readerr); sig_catcher(0); } #ifdef PUSHBACK if (*whatbuf & 0200 || no_macros) { *whatbuf &= 0177; goto got_canonical; } if (curmap == Null(KEYMAP*)) goto got_canonical; for (i = (curmap->km_type[*whatbuf] >> KM_GSHIFT) & KM_GMASK; i; --i){ read_tty(&scrchar,1); } switch (curmap->km_type[*whatbuf] & 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[*whatbuf].km_km; assert(curmap != Null(KEYMAP*)); break; case KM_STRING: /* a string? */ pushstring(curmap->km_ptr[*whatbuf].km_str); if (++times > 20) { /* loop? */ fputs("\nmacro loop?\n",stdout); settle_down(); } no_macros = FALSE; goto tryagain; } #else #ifdef RAWONLY *whatbuf &= 0177; #endif break; #endif } got_canonical: #ifndef TERMIO if (*whatbuf == '\r') *whatbuf = '\n'; #endif if (whatbuf == buf) whatbuf[1] = FINISHCMD; /* tell finish_command to work */ } #ifdef PUSHBACK void pushstring(str) char *str; { register int i; char tmpbuf[PUSHSIZE]; register char *s = tmpbuf; assert(str != Nullch); interp(s,PUSHSIZE,str); for (i = strlen(s)-1; i >= 0; --i) { s[i] ^= 0200; pushchar(s[i]); } } #endif int get_anything() { char tmpbuf[2]; reask_anything: unflush_output(); /* disable any ^O in effect */ standout(); #ifdef VERBOSE IF(verbose) fputs("[Type space to continue] ",stdout); ELSE #endif #ifdef TERSE fputs("[MORE] ",stdout); #endif un_standout(); fflush(stdout); eat_typeahead(); if (int_count) { return -1; } collect_subjects(); /* loads subject cache until */ /* input is pending */ getcmd(tmpbuf); if (errno || *tmpbuf == '\f') { putchar('\n') FLUSH; /* 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 goto reask_anything; } else if (*tmpbuf != ' ' && *tmpbuf != '\n') { carriage_return(); erase_eol(); /* erase the prompt */ return *tmpbuf == 'q' ? -1 : *tmpbuf; } if (*tmpbuf == '\n') { page_line = LINES - 1; carriage_return(); erase_eol(); } else { page_line = 1; if (erase_screen) /* -e? */ clear(); /* clear screen */ else { carriage_return(); erase_eol(); /* erase the prompt */ } } return 0; } void in_char(prompt) char *prompt; { char oldmode = mode; reask_in_char: unflush_output(); /* disable any ^O in effect */ fputs(prompt,stdout); fflush(stdout); eat_typeahead(); mode = 'm'; getcmd(buf); if (errno || *buf == '\f') { putchar('\n') FLUSH; /* if return from stop signal */ goto reask_in_char; /* give them a prompt again */ } mode = oldmode; } int print_lines(what_to_print,hilite) char *what_to_print; int hilite; { register char *s; register int i; if (page_line < 0) /* they do not want to see this? */ return -1; for (s=what_to_print; *s; ) { if (page_line >= LINES || int_count) { if (i = -1, int_count || (i = get_anything())) { page_line = -1; /* disable further print_lines */ return i; } } page_line++; if (hilite == STANDOUT) { #ifdef NOFIREWORKS if (erase_screen) no_sofire(); #endif standout(); } else if (hilite == UNDERLINE) { #ifdef NOFIREWORKS if (erase_screen) no_ulfire(); #endif underline(); } for (i=0; i= ' ') putchar(*s); else if (*s == '\t') { putchar(*s); i = ((i+8) & ~7) - 1; } else if (*s == '\n') { i = 32000; } else { i++; putchar('^'); putchar(*s + 64); } s++; } if (i) { if (hilite == STANDOUT) un_standout(); else if (hilite == UNDERLINE) un_underline(); if (AM && i == COLS) fflush(stdout); else putchar('\n') FLUSH; } } return 0; } void page_init() { page_line = 1; if (erase_screen) clear(); else putchar('\n') FLUSH; } void pad(num) int num; { register int i; for (i = num; i; --i) putchar(PC); fflush(stdout); } /* echo the command just typed */ #ifdef VERIFY void printcmd() { if (verify && buf[1] == FINISHCMD) { if (*buf < ' ') { putchar('^'); putchar(*buf | 64); backspace(); backspace(); } else { putchar(*buf); backspace(); } fflush(stdout); } } #endif void rubout() { backspace(); /* do the old backspace, */ putchar(' '); /* space, */ backspace(); /* backspace trick */ } void reprint() { register char *s; fputs("^R\n",stdout) FLUSH; for (s = buf; *s; s++) { if (*s < ' ') { putchar('^'); putchar(*s | 64); } else putchar(*s); } } #ifdef CLEAREOL /* start of additions by Paul Placeway (PWP) */ void home_cursor() { char *tgoto(); if (!*HO) { /* no home sequence? */ if (!*CM) { /* no cursor motion either? */ fputs ("\n\n\n", stdout); return; /* forget it. */ } tputs (tgoto (CM, 0, 0), 1, putchr); /* go to home via CM */ return; } else { /* we have home sequence */ tputs (HO, 1, putchr); /* home via HO */ } } #endif CLEAREOL lse notincl("^["); *s = '\0'; reprint(); goto re_read; #endif } else if (*s == ERASECH) { /* they want to rubout a char? */ rubout(); s--; /* discount the char rubbed out */ if (*s < ' ' || *s == '\177') rubout(); iterm.h 664 540 12 15337 3473762415 5267 /* $Header: term.h,v 4.3.1.2 85/05/13 15:52:05 lwall Exp $ * * $Log: term.h,v $ * Revision 4.3.1.2 85/05/13 15:52:05 lwall * Declared devtty on TERMIO system. * * Revision 4.3.1.1 85/05/10 11:41:24 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:51:36 lwall * Baseline for release with 4.3bsd. * */ #ifdef PUSHBACK EXT char circlebuf[PUSHSIZE]; EXT int nextin INIT(0); EXT int nextout INIT(0); #ifdef PENDING #ifdef FIONREAD EXT long iocount INIT(0); #ifndef lint #define input_pending() (nextin!=nextout || (ioctl(0, FIONREAD, &iocount),(int)iocount)) #else #define input_pending() bizarre #endif lint #else FIONREAD int circfill(); EXT int devtty INIT(0); #ifndef lint #define input_pending() (nextin!=nextout || circfill()) #else #define input_pending() bizarre #endif lint #endif FIONREAD #else PENDING #ifndef lint #define input_pending() (nextin!=nextout) #else #define input_pending() bizarre #endif lint #endif PENDING #else PUSHBACK #ifdef PENDING #ifdef FIONREAD /* must have FIONREAD or O_NDELAY for input_pending() */ #define read_tty(addr,size) read(0,addr,size) #ifndef lint #define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount) #else #define input_pending() bizarre #endif lint EXT long iocount INIT(0); #else FIONREAD EXT int devtty INIT(0); EXT bool is_input INIT(FALSE); EXT char pending_ch INIT(0); #ifndef lint #define input_pending() (is_input || (is_input=read(devtty,&pending_ch,1))) #else #define input_pending() bizarre #endif lint #endif FIONREAD #else PENDING #define read_tty(addr,size) read(0,addr,size) #define input_pending() (FALSE) #endif PENDING #endif PUSHBACK /* stuff wanted by terminal mode diddling routines */ #ifdef TERMIO EXT struct termio _tty, _oldtty; #else EXT struct sgttyb _tty; EXT int _res_flg INIT(0); #endif EXT int _tty_ch INIT(2); EXT bool bizarre INIT(FALSE); /* do we need to restore terminal? */ /* terminal mode diddling routines */ #ifdef TERMIO #define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty)) #define resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty)) #define unflush_output() #else #define raw() ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty)) #define noraw() ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty)) #define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty)) #define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty)) #define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty)) #define nl() ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty)) #define nonl() ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty)) #define savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags) #define resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty)) #ifdef LFLUSHO #ifndef lint EXT int lflusho INIT(LFLUSHO); #else EXT long lflusho INIT(LFLUSHO); #endif lint #define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho)) #else #define unflush_output() #endif LFLUSHO #endif TERMIO #ifdef TIOCSTI #ifdef lint #define forceme(c) ioctl(_tty_ch,TIOCSTI,Null(long*)) /* ghad! */ #else #define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */ #endif lint #else #define forceme(c) #endif /* termcap stuff */ /* * NOTE: if you don't have termlib you'll either have to define these strings * and the tputs routine, or you'll have to redefine the macros below */ #ifdef HAVETERMLIB EXT char *BC INIT(Nullch); /* backspace character */ EXT char *UP INIT(Nullch); /* move cursor up one line */ EXT char *CR INIT(Nullch); /* get to left margin, somehow */ EXT char *VB INIT(Nullch); /* visible bell */ EXT char *CL INIT(Nullch); /* home and clear screen */ EXT char *CE INIT(Nullch); /* clear to end of line */ #ifdef CLEAREOL EXT char *CM INIT(Nullch); /* cursor motion -- PWP */ EXT char *HO INIT(Nullch); /* home cursor -- PWP */ EXT char *CD INIT(Nullch); /* clear to end of display -- PWP */ #endif CLEAREOL EXT char *SO INIT(Nullch); /* begin standout mode */ EXT char *SE INIT(Nullch); /* end standout mode */ EXT int SG INIT(0); /* blanks left by SO and SE */ EXT char *US INIT(Nullch); /* start underline mode */ EXT char *UE INIT(Nullch); /* end underline mode */ EXT char *UC INIT(Nullch); /* underline a character, if that's how it's done */ EXT int UG INIT(0); /* blanks left by US and UE */ EXT bool AM INIT(FALSE); /* does terminal have automatic margins? */ EXT bool XN INIT(FALSE); /* does it eat 1st newline after automatic wrap? */ EXT char PC INIT(0); /* pad character for use by tputs() */ EXT short ospeed INIT(0); /* terminal output speed, for use by tputs() */ EXT int LINES INIT(0), COLS INIT(0); /* size of screen */ EXT int just_a_sec INIT(960); /* 1 sec at current baud rate */ /* (number of nulls) */ /* define a few handy macros */ #define backspace() tputs(BC,0,putchr) FLUSH #define clear() tputs(CL,LINES,putchr) FLUSH #define erase_eol() tputs(CE,1,putchr) FLUSH #ifdef CLEAREOL #define clear_rest() tputs(CD,LINES,putchr) FLUSH /* PWP */ #define maybe_eol() if(erase_screen&&can_home_clear)tputs(CE,1,putchr) FLUSH #endif CLEAREOL #define underline() tputs(US,1,putchr) FLUSH #define un_underline() tputs(UE,1,putchr) FLUSH #define underchar() tputs(UC,0,putchr) FLUSH #define standout() tputs(SO,1,putchr) FLUSH #define un_standout() tputs(SE,1,putchr) FLUSH #define up_line() tputs(UP,1,putchr) FLUSH #define carriage_return() tputs(CR,1,putchr) FLUSH #define dingaling() tputs(VB,1,putchr) FLUSH #else ???????? /* up to you */ #endif EXT int page_line INIT(1); /* line number for paging in print_line (origin 1) */ void term_init(); void term_set(); #ifdef PUSHBACK void pushchar(); void mac_init(); void mac_line(); void show_macros(); #endif char putchr(); /* routine for tputs to call */ bool finish_command(); void eat_typeahead(); void settle_down(); #ifndef read_tty int read_tty(); #endif void underprint(); #ifdef NOFIREWORKS void no_sofire(); void no_ulfire(); #endif void getcmd(); int get_anything(); void in_char(); int print_lines(); void page_init(); void pad(); void printcmd(); void rubout(); void reprint(); #ifdef CLEAREOL void home_cursor(); #endif CLEAREOL e 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[*whatbuf].km_km; assert(curmap != Null(KEYMAP*)); break; case Kutil.c 664 540 12 24650 3473762513 5265 /* $Header: util.c,v 4.3.1.2 85/05/15 14:44:27 lwall Exp $ * * $Log: util.c,v $ * Revision 4.3.1.2 85/05/15 14:44:27 lwall * Last arg of execl changed from 0 to Nullch [(char*)0]. * * Revision 4.3.1.1 85/05/10 11:41:30 lwall * Branch for patches. * * Revision 4.3 85/05/01 11:51:44 lwall * Baseline for release with 4.3bsd. * */ #include "EXTERN.h" #include "common.h" #include "final.h" #include "ndir.h" #include "INTERN.h" #include "util.h" void util_init() { ; } /* fork and exec a shell command */ int doshell(shl,s) char *s, *shl; { int status, pid, w; register int (*istat)(), (*qstat)(); int (*signal())(); char *shell; #ifdef SIGTSTP sigset(SIGTSTP,SIG_DFL); sigset(SIGCONT,SIG_DFL); #endif if (shl != Nullch) shell = shl; else if ((shell = getenv("SHELL")) == Nullch || !*shell) shell = PREFSHELL; if ((pid = vfork()) == 0) { if (*s) execl(shell, shell, "-c", s, Nullch); else execl(shell, shell, Nullch, Nullch, Nullch); _exit(127); } #ifndef lint istat = signal(SIGINT, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); #else istat = Null(int (*)()); qstat = Null(int (*)()); #endif lint waiting = TRUE; while ((w = wait(&status)) != pid && w != -1) ; if (w == -1) status = -1; waiting = FALSE; signal(SIGINT, istat); signal(SIGQUIT, qstat); #ifdef SIGTSTP sigset(SIGTSTP,stop_catcher); sigset(SIGCONT,cont_catcher); #endif return status; } static char nomem[] = "rn: out of memory!\n"; /* paranoid version of malloc */ char * safemalloc(size) MEM_SIZE size; { char *ptr; char *malloc(); ptr = malloc(size?size:1); /* malloc(0) is NASTY on our system */ if (ptr != Nullch) return ptr; else { fputs(nomem,stdout) FLUSH; sig_catcher(0); } /*NOTREACHED*/ } /* paranoid version of realloc */ char * saferealloc(where,size) char *where; MEM_SIZE size; { char *ptr; char *realloc(); ptr = realloc(where,size?size:1); /* realloc(0) is NASTY on our system */ if (ptr != Nullch) return ptr; else { fputs(nomem,stdout) FLUSH; sig_catcher(0); } /*NOTREACHED*/ } /* safe version of string copy */ char * safecpy(to,from,len) char *to; register char *from; register int len; { register char *dest = to; if (from != Nullch) for (len--; len && (*dest++ = *from++); len--) ; *dest = '\0'; return to; } /* safe version of string concatenate, with \n deletion and space padding */ char * safecat(to,from,len) char *to; register char *from; register int len; { register char *dest = to; len--; /* leave room for null */ if (*dest) { while (len && *dest++) len--; if (len) { len--; *(dest-1) = ' '; } } if (from != Nullch) while (len && (*dest++ = *from++)) len--; if (len) dest--; if (*(dest-1) == '\n') dest--; *dest = '\0'; return to; } /* copy a string up to some (non-backslashed) delimiter, if any */ char * cpytill(to,from,delim) register char *to, *from; register int delim; { for (; *from; from++,to++) { if (*from == '\\' && from[1] == delim) from++; else if (*from == delim) break; *to = *from; } *to = '\0'; return from; } /* return ptr to little string in big string, NULL if not found */ char * instr(big, little) char *big, *little; { register char *t, *s, *x; for (t = big; *t; t++) { for (x=t,s=little; *s; x++,s++) { if (!*x) return Nullch; if (*s != *x) break; } if (!*s) return t; } return Nullch; } /* effective access */ #ifdef SETUIDGID int eaccess(filename, 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 */ #ifdef GETWD #define dot "." #define dotdot ".." static char *name; static DIR *dirp; static int off; static struct stat d, dd; static struct direct *dir; char * getwd(np) char *np; { long rdev, rino; *np++ = '/'; *np = 0; name = np; off = -1; stat("/", &d); rdev = d.st_dev; rino = d.st_ino; for (;;) { stat(dot, &d); if (d.st_ino==rino && d.st_dev==rdev) goto done; if ((dirp = opendir(dotdot)) == Null(DIR *)) prexit("getwd: cannot open ..\n"); stat(dotdot, &dd); chdir(dotdot); if(d.st_dev == dd.st_dev) { if(d.st_ino == dd.st_ino) goto done; do if ((dir = readdir(dirp)) == Null(struct direct *)) prexit("getwd: read error in ..\n"); while (dir->d_ino != d.st_ino); } else do { if ((dir = readdir(dirp)) == Null(struct direct *)) prexit("getwd: read error in ..\n"); stat(dir->d_name, &dd); } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev); cat(); closedir(dirp); } done: name--; if (chdir(name) < 0) { printf("getwd: can't cd back to %s\n",name) FLUSH; sig_catcher(0); } return (name); } void cat() { register i, j; i = -1; while (dir->d_name[++i] != 0); if ((off+i+2) > 1024-1) return; for(j=off+1; j>=0; --j) name[j+i+1] = name[j]; if (off >= 0) name[i] = '/'; off=i+off+1; name[off] = 0; for(--i; i>=0; --i) name[i] = dir->d_name[i]; } void prexit(cp) char *cp; { write(2, cp, strlen(cp)); sig_catcher(0); } #else char * getwd(np) /* shorter but slower */ char *np; { FILE *popen(); FILE *pipefp = popen("/bin/pwd","r"); if (pipefd == Nullfp) { printf("Can't run /bin/pwd\n") FLUSH; finalize(1); } fgets(np,512,pipefp); np[strlen(np)-1] = '\0'; /* wipe out newline */ pclose(pipefp); return np; } #endif /* just like fgets but will make bigger buffer as necessary */ char * get_a_line(original_buffer,buffer_length,fp) char *original_buffer; register int buffer_length; FILE *fp; { register int bufix = 0; register int nextch; register char *some_buffer_or_other = original_buffer; do { if (bufix >= buffer_length) { buffer_length *= 2; if (some_buffer_or_other == original_buffer) { /* currently static? */ some_buffer_or_other = safemalloc((MEM_SIZE)buffer_length+1); strncpy(some_buffer_or_other,original_buffer,buffer_length/2); /* so we must copy it */ } else { /* just grow in place, if possible */ some_buffer_or_other = saferealloc(some_buffer_or_other, (MEM_SIZE)buffer_length+1); } } if ((nextch = getc(fp)) == EOF) return Nullch; some_buffer_or_other[bufix++] = (char) nextch; } while (nextch && nextch != '\n'); some_buffer_or_other[bufix] = '\0'; len_last_line_got = bufix; return some_buffer_or_other; } /* copy a string to a safe spot */ char * savestr(str) char *str; { register char *newaddr = safemalloc((MEM_SIZE)(strlen(str)+1)); strcpy(newaddr,str); return newaddr; } int makedir(dirname,nametype) register char *dirname; int nametype; { #ifdef MAKEDIR register char *end; register char *s; char tmpbuf[1024]; register char *tbptr = tmpbuf+5; for (end = dirname; *end; end++) ; /* find the end */ if (nametype == MD_FILE) { /* not to create last component? */ for (--end; end != dirname && *end != '/'; --end) ; if (*end != '/') return 0; /* nothing to make */ *end = '\0'; /* isolate file name */ } strcpy(tmpbuf,"mkdir"); s = end; for (;;) { if (stat(dirname,&filestat) >= 0) { /* does this much exist? */ *s = '/'; /* mark this as existing */ break; } s = rindex(dirname,'/'); /* shorten name */ if (!s) /* relative path! */ break; /* hope they know what they are doing */ *s = '\0'; /* mark as not existing */ } for (s=dirname; s <= end; s++) { /* this is grody but efficient */ if (!*s) { /* something to make? */ sprintf(tbptr," %s",dirname); tbptr += strlen(tbptr); /* make it, sort of */ *s = '/'; /* mark it made */ } } if (nametype == MD_DIR) /* don't need final slash unless */ *end = '\0'; /* a filename follows the dir name */ return (tbptr==tmpbuf+5 ? 0 : doshell(sh,tmpbuf)); /* exercise our faith */ #else sprintf(cmd_buf,"%s %s %d", filexp(DIRMAKER), dirname, nametype); return doshell(sh,cmd_buf); #endif } #ifdef SETENV static bool firstsetenv = TRUE; extern char **environ; void setenv(nam,val) char *nam, *val; { register int i=envix(nam); /* where does it go? */ if (!environ[i]) { /* does not exist yet */ if (firstsetenv) { /* need we copy environment? */ int j; #ifndef lint char **tmpenv = (char**) /* point our wand at memory */ safemalloc((MEM_SIZE) (i+2) * sizeof(char*)); #else char **tmpenv = Null(char **); #endif lint firstsetenv = FALSE; for (j=0; j *curlen) { /* need more room? */ if (*curlen) *strptr = saferealloc(*strptr,(MEM_SIZE)newlen); else *strptr = safemalloc((MEM_SIZE)newlen); *curlen = newlen; } } void setdef(buffer,dflt) char *buffer,*dflt; { #ifdef STRICTCR if (*buffer == ' ') #else if (*buffer == ' ' || *buffer == '\n') #endif { if (*dflt == '^' && isupper(dflt[1])) *buffer = Ctl(dflt[1]); else *buffer = *dflt; } } dir.h" #include "INTERN.h" #include "util.h" void util_init() { ; } /* fork anutil.h 664 540 12 1512 3473757443 5250 /* $Header: util.h,v 4.3 85/05/01 11:51:58 lwall Exp $ * * $Log: util.h,v $ * Revision 4.3 85/05/01 11:51:58 lwall * Baseline for release with 4.3bsd. * */ EXT bool waiting INIT(FALSE); /* are we waiting for subprocess (in doshell)? */ EXT int len_last_line_got INIT(0); /* strlen of some_buf after */ /* some_buf = get_a_line(bufptr,buffersize,fp) */ /* is the string for makedir a directory name or a filename? */ #define MD_DIR 0 #define MD_FILE 1 void util_init(); int doshell(); char *safemalloc(); char *saferealloc(); char *safecpy(); char *safecat(); char *cpytill(); char *instr(); #ifdef SETUIDGID int eaccess(); #endif char *getwd(); void cat(); void prexit(); char *get_a_line(); char *savestr(); int makedir(); void setenv(); int envix(); void notincl(); char *getval(); void growstr(); void setdef(); llch || !*shell) shell = PREFSHELL; if ((pid = vfork()) == 0) { if (*s) execl(shell, shell, "-c", s, Nullch); else execl(shell, shell, Nullch, Nullch, Nullch); _exipending() (nextin!=nextout || (ioctl(0, FIONREAD, &iocount),(int)iocount)) #else #define input_pending() bizarre #endif lint #else FIONREAD int circfill(); EXT int devtty INIT(0); #ifndef lint #define input_pending() (nextin!=nextout || circfill()) #else #define input_pending() bizarre #endif lint #endif FIONREAD #else PENDING #ifndef lint #define input_pending() (nextin!=nextout) #else #define input_pending() bizarre #endif lint #endif PENDING #else PUSHBACK #ifdef PENDING #ifdef FIONREAD /* must have FIONREAD or O_NDELAY for input_pending() */ #define read_tty(addr,size) read(0,addr,size) #ifndef lint #define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount) #else #define input_pending() bizarre #endif lint EXT long iocount INIT(0); #else FIONREAD EXT int devtty INIT(0); EXT bool is_input INIT(FALSE); EXT char pending_ch INIT(0); #ifndef lint #define input_pending() (is_input || (is_input=read(devtty,&pending_ch,1))) #else #define input_pending() bizarre #endif lint #endif FIONREAD #else PENDING #define read_tty(addr,size) read(0,addr,size) #define input_pending() (FALSE) #endif PENDING #endif PUSHBACK /* stuff wanted by terminal mode diddling routines */ #ifdef TERMIO EXT struct termio _tty, _oldtty; #else EXT struct sgttyb _tty; EXT int _res_flg INIT(0); #endif EXT int _tty_ch INIT(2); EXT bool bizarre INIT(FALSE); /* do we need to restore terminal? */ /* terminal mode diddling routines */ #ifdef TERMIO #define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty)) #define resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty)) #define unflush_output() #else #define raw() ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty)) #define noraw() ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty)) #define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty)) #define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty)) #define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty)) #define nl() ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty)) #define nonl() ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty)) #define savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags) #define resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty)) #ifdef LFLUSHO #ifndef lint EXT int lflusho INIT(LFLUSHO); #else EXT long lflusho INIT(LFLUSHO); #endif lint #define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho)) #else #define unflush_output() #endif LFLUSHO #endif TERMIO #ifdef TIOCSTI #ifdef lint #define forceme(c) ioctl(_tty_ch,TIOCSTI,Null(long*)) /* ghad! */ #else #define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */ #endif lint #else #define forceme(c) #endif /* termcap stuff */ /* * NOTE: if you don't have termlib you'll either have to define these strings * and the tputs routine, or you'll have to redefine the macros below */ #ifdef HAVETERMLIB EXT char *BC INIT(Nullch); /* backspace character */ EXT char *UP INIT(Nullch); /* move cursor up one line */ EXT char *CR INIT(Nullch); /* get to left margin, somehow */ EXT char *VB INIT(Nullch); /* visible bell */ EXT char *CL INIT(Nullch); /* home and clear screen */ EXT char *CE INIT(Nullch); /* clear to end of line */ #ifdef CLEAREOL EXT char *CM INIT(Nullch); /* cursor motion -- PWP */ EXT char *HO INIT(Nullch); /* home cursor -- PWP */ EXT char *CD INIT(Nullch); /* clear to end of display -- PWP */ #endif CLEAREOL EXT char *SO INIT(Nullch); /* begin standout mode */ EXT char *SE INIT(Nullch); /* end standout mode */ EXT int SG INIT(0); /* blanks left by SO and SE */ EXT char *US INIT(Nullch); /* start underline mode */ EXT char *UE INIT(Nullch); /* end underline mode */ EXT char *UC INIT(Nullch); /* underline a character, if that's how it's done */ EXT int UG INIT(0); /* blanks left by US and UE */ EXT bool AM INIT(FALSE); /* does terminal have automatic margins? */ EXT bool XN INIT(FALSE); /* does it eat 1st newline after automatic wrap? */ EXT char PC INIT(0); /* pad character for use by tputs() */ EXT short ospeed INIT(0); /* terminal output speed, for use by tputs() */ EXT int LINES INIT(0), COLS INIT(0); /* size of screen */ EXT int just_a_sec INIT(960); /* 1 sec at current baud rate */ /* (number of nulls) */ /* define a few handy macros */ #define backspace() tputs(BC,0,putchr) FLUSH #define clear() tputs(CL,LINES,putchr) FLUSH #define erase_eol() tputs(CE,1,putchr) FLUSH #ifdef CLEAREOL #define clear_rest() tputs(CD,LINES,putchr) FLUSH /* PWP */ #define maybe_eol() if(erase_screen&&can_home_clear)tputs(CE,1,putchr) FLUSH #endif CLEAREOL #define underline() tputs(US,1,putchr) FLUSH #define un_underline() tputs(UE,1,putchr) FLUSH #define underchar() tputs(UC,0,putchr) FLUSH #define standout() tputs(SO,1,putchr) FLUSH #define un_standout() tputs(SE,1,putchr) FLUSH #define up_line() tputs(UP,1,putchr) FLUSH #define carriage_return() tputs(CR,1,putchr) FLUSH #define dingaling() tputs(VB,1,putchr) FLUSH #else ???????? /* up to you */ #endif EXT int page_line INIT(1); /* line number for paging in print_line (origin 1) */ void term_init(); void term_set(); #ifdef PUSHBACK void pushchar(); void mac_init(); void mac_line(); void show_macros(); #endif char putchr(); /* routine for tputs to call */ bool finish_command(); void eat_typeahead(); void settle_down(); #ifndef read_tty int read_tty(); #endif void underprint(); #ifdef NOFIREWORKS void no_sofire(); void no_ulfire(); #endif void getcmd(); int get_anything(); void in_char(); int print_lines(); void page_init(); void pad(); void printcmd(); void rubout(); void reprint(); #ifdef CLEAREOL void home_cursor(); #endif CLEAREOL e 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[*whatbuf].km_km; assert(curmap != Null(KEYMAP*)); break; case Kutil.c 664 540 12 24650 3473762513 5265