# to unbundle, sh this file (in an empty directory)
# or use ftp://netlib.bell-labs.com/netlib/access/unshar.c.gz
echo bin/Depend 1>&2
sed >bin/Depend <<'-------cut here----- bin/Depend' 's/^X//'
X#!/bin/sh
Xcase $1 in
X
X
X-c)	#  creates ".depend" file for local directory, of the form
X	#	F file.c
X	#	D entrypoint1
X	#	R external1
X	#	R external2
X	> .depend
X	for FILE in *.[cefr];do
X		echo "F $FILE"
X		OBJ=`echo $FILE|sed 's/\..$/.o/'`
X		make $OBJ 1>&2
X		nm $OBJ |	# the following works for SGI IRIX 4.0.5
X		sed -n 's/^\([^ ]*\) *|[^|]*|\(.*\)/\2| \1/
X			/|ref=[0-9].*|Text/d
X			s/^Proc *|.*|Undefined|/R/p
X			s/^Proc *|end.*|Text *|/D/p' |
X		skip_posix | sort -u
X		rm $OBJ
X		sed -n 's/^#[ 	]*include[ 	]*"\(.*\)" *$/R \1/p' $FILE
X		echo ""
X	done >> .depend
X	ls *.h* 2>/dev/null |
X		while read FILE;do echo "F $FILE";echo "";done >> .depend
X	;;
X
X
X-t)	# prints filenames covering routines in $* for libraries in $LIBS
X	shift # get rid of -t
X	case $* in
X	all)	# *** return all files ***
X		for i in $LIBS;do
X			sed -n 's;^F ;'$i'/;p' $i/.depend
X		done | tr '\012' ' '
X		echo;;
X	*)	# ***cat .depend files for all LIBS, then search ***
X		# subtlety: have to add dir to F, hence make D explicit first.
X		U=
X		for i in $*; do U="$U $i $i"_; done
X		(for i in $LIBS; do
X			(cd $i
X			if test -r .depend; then
X				sed 's;^F \(.*\);F '$i'/\1\
XD \1;
X				  s;^R \([^/]*\)\.h$;R '$i'/\1.h;' .depend
X			else
X				ls | sed '/index/d
X					/readme/d
X					/disclaimer/d
X					/depend/d
X					s;^;F '$i'/;'
X			fi)
X		done) | getdepend $U | tr '\012' ' '
X		echo;;
X	esac
X	;;
X
X*)	echo $0 -c   ... to build .depend file for local directory
X	echo 'LIBS={directories} $0 -t {symbols}   ... for transitive closure'
X	exit 1
X	;;
X
Xesac
Xexit 0
-------cut here----- bin/Depend
chmod +x bin/Depend
echo bin/bibexpand 1>&2
sed >bin/bibexpand <<'-------cut here----- bin/bibexpand' 's/^X//'
X#!/bin/sh
X
X# tool to convert bibnet format (strings) to simple BibTeX (no strings)
X# Eric Grosse <ehg@research.bell-labs.com> 14 Dec 1994
X
Xtmp=/tmp/bib$$
Xsed '/^%/d
X	s/^  */ /' $* | combline > $tmp
X< $tmp sed 's/^  */ /' | tr -d '\015' |
X	sed -n 's/\\/\\\\/g
X		s/.*%.*@String/@String/
X		s/@String *{\([^ ]*\) *= *\(.*\) *}/s`\1`\2`/p' > $tmp.sed
X< $tmp sed -f $tmp.sed $tmp |
X	sed '/^@String/d
X		s/" # "//g' |
X	uncombline
Xrm -f $tmp $tmp.sed
-------cut here----- bin/bibexpand
chmod +x bin/bibexpand
echo bin/disclaim 1>&2
sed >bin/disclaim <<'-------cut here----- bin/disclaim' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
X#  add disclaimer messages to front of return mail
Xfor i in $1
X  do
X  if /bin/test -r $i/disclaimer
X    then
X    cat $i/disclaimer
X    fi
X  done
Xcase $1 in
X  "1127") exit 0;;
Xesac
Xcat admin/mess/disclaimer
Xecho "*** from netlib, `date` ***"
-------cut here----- bin/disclaim
chmod +x bin/disclaim
echo bin/index_chk 1>&2
sed >bin/index_chk <<'-------cut here----- bin/index_chk' 's/^X//'
X#!/bin/sh
X# expects list of index filenames on stdin, perhaps from "lsr|grep 'index$'"
X
X# *** this is an interactive script, not automatic! ***
X
X# gdiff is an SGI program that's handy for merging changes;  if you don't
X# have it, use your favorite alternative.  I hear that there is a similar
X# dxdiff on DEC systems.
X
Xcd /netlib      # file names are supposed to be relative to this
Xwhile read j; do
X        echo ===== $j =====
X        plauger_chk < $j
X        index_chk.x $j > $j.FIX
X        cmp -s $j $j.FIX || \
X                (ls -l $j; \
X                gdiff -s 1000,900 $j.FIX $j; \
X                ls -l $j )
X        rm $j.FIX
Xdone
-------cut here----- bin/index_chk
chmod +x bin/index_chk
echo bin/ldMx 1>&2
sed >bin/ldMx <<'-------cut here----- bin/ldMx' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
X# $1 = libraries, e.g.  "linpack blas"
X# $2 = routines, e.g.   "sgeco"
X# $3 = exclusions, e.g. "sgefa"
XLIBS=`echo $1|sed 's/-l//g'`
XPATH=admin/bin:$PATH
Xexport LIBS PATH
Xcase "$3" in
X  "") Depend -t "$2";;
X  *) Depend -t "$3" | tr ' ' '\012' | sort >/tmp/nl$$
X     Depend -t "$2" |tr ' ' '\012'|sort|comm -23 - /tmp/nl$$|tr '\012' ' '
X     rm /tmp/nl$$;;
Xesac
-------cut here----- bin/ldMx
chmod +x bin/ldMx
echo bin/mgrep 1>&2
sed >bin/mgrep <<'-------cut here----- bin/mgrep' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
X#  find lines containing keywords
X#  "$1" = files to be searched
X#  $2 $3 ... = patterns
Xcd list
X#... For portability to Berkeley systems, changed egrep to grep.
X#... Greg Astfalk convinced me simple string matching is better
X#... b='(^|[^a-zA-Z0-9])'
X#... e='($|[^a-zA-Z0-9])'
X# in System V and Eighth Edition Unix,
X#   egrep accepts a -i flag to ignore case distinctions
X#... cat $1 2>/dev/null | egrep -i "$2" | egrep -i "$b$2$e" >/tmp/w$$
Xcat $1 2>/dev/null | grep -i "$2" >/tmp/w$$
Xshift; shift
Xfor i
X  do
X#...   egrep -i "$b$i$e" </tmp/w$$ >/tmp/x$$
X  grep -i "$i" </tmp/w$$ >/tmp/x$$
X  mv /tmp/x$$ /tmp/w$$
X  done
Xif test -s /tmp/w$$
X  then
X    tr '\015' '\012' </tmp/w$$     # restore newlines hidden by combline
X  else
X    cat /netlib/admin/mess/sorry_mgrep
Xfi
Xrm /tmp/w$$
-------cut here----- bin/mgrep
chmod +x bin/mgrep
echo bin/netlibd 1>&2
sed >bin/netlibd <<'-------cut here----- bin/netlibd' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:/usr/local/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
X# reply to requests (improperly) sent to netlibd instead of netlib
Xcd /netlib
Xn=/tmp/net$$
Xr=/tmp/netlibd
Xcat >$n
Xecho "-----------------------------------------" >>$r
Xecho "....The following was sent to netlibd...." >>$r
Xu=`admin/bin/retadd <$n`
Xif (echo $u |egrep -s -i "(^$)|(mailer)|(uucp)|(postmaster)|(postoffice)|(trouble)|(server)|(netlib)|(!root)|(mail_system)|(!smtp )|(falsmtp)|(mmdf)|(error)|(mail.*daemon)|(unknown-user)|(washington.edu)")
X  then
X  echo "....and ignored...." >>$r
X else
X  mail $u <<!
XTo prevent infinite mail loops from vacation daemons, the response
Xfrom netlib appears to be coming from "netlibd".  But don't just
Xuse the reply feature of your mailer to submit more requests; send
Xthem to netlib, not netlibd.
X
XThis message has been generated by a program.  To talk to a human, send
Xmail to the address in the From line above.
X!
Xfi
Xsed '70q
X	/Your message begins/,$d' $n >>$r
Xecho "-----------------------------------------" >>$r
Xrm -f $n
Xchmod 666 $r
Xexit 0
-------cut here----- bin/netlibd
chmod +x bin/netlibd
echo bin/netlibnews 1>&2
sed >bin/netlibnews <<'-------cut here----- bin/netlibnews' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
X# reply to requests (improperly) sent to netlibnews
Xcd /netlib
Xn=/tmp/net$$
Xr=/tmp/netlibd
Xcat >$n
Xecho "-----------------------------------------" >>$r
Xecho "....The following was sent to netlibnews...." >>$r
Xu=`admin/bin/retadd <$n`
Xif (echo $u |egrep -s -i "(mailer)|(uucp)|(postmaster)|(trouble)|(server)|(netlib)|(!root)|(mail_system)|(!smtp )|(falsmtp)|(mmdf)|(error)|(maildaemon)|(unknown-user)")
X  then
X  echo "....and ignored...." >>$r
X else
X  mail $u <<!
X"netlibnews" is a dummy address used only for delivering notices of
Xchanged files in netlib directories to which you have subscribed.
XIf you wish to make a netlib request, send to netlib, not netlibnews.
X
XThis message has been generated by a program.  To talk to a human, send
Xmail to the address in the From line above.
X!
Xfi
Xsed '70q
X	/Your message begins/,$d' $n >>$r
Xecho "-----------------------------------------" >>$r
Xrm -f $n
Xchmod 666 $r
Xexit 0
-------cut here----- bin/netlibnews
chmod +x bin/netlibnews
echo bin/skip_posix 1>&2
sed >bin/skip_posix <<'-------cut here----- bin/skip_posix' 's/^X//'
Xsed '/^R _/d
X/^R assert.h$/d
X/^R ctype.h$/d
X/^R fcntl.h$/d
X/^R float.h$/d
X/^R grp.h$/d
X/^R limits.h$/d
X/^R locale.h$/d
X/^R math.h$/d
X/^R pwd.h$/d
X/^R setjmp.h$/d
X/^R signal.h$/d
X/^R stdarg.h$/d
X/^R stddef.h$/d
X/^R stdio.h$/d
X/^R stdlib.h$/d
X/^R string.h$/d
X/^R sys\/times.h$/d
X/^R sys\/types.h$/d
X/^R sys\/utsname.h$/d
X/^R sys\/wait.h$/d
X/^R time.h$/d
X/^R unistd.h$/d
X/^R utime.h$/d
X/^R abs$/d
X/^R acos$/d
X/^R alarm$/d
X/^R asctime$/d
X/^R asin$/d
X/^R atan$/d
X/^R atan2$/d
X/^R atexit$/d
X/^R atof$/d
X/^R atoi$/d
X/^R atol$/d
X/^R bsearch$/d
X/^R ceil$/d
X/^R chdir$/d
X/^R chmod$/d
X/^R clearerr$/d
X/^R clock$/d
X/^R close$/d
X/^R closedir$/d
X/^R cos$/d
X/^R cosh$/d
X/^R ctermid$/d
X/^R cuserid$/d
X/^R difftime$/d
X/^R dup$/d
X/^R exit$/d
X/^R exp$/d
X/^R fabs$/d
X/^R fclose$/d
X/^R fdopen$/d
X/^R feof$/d
X/^R ferror$/d
X/^R fflush$/d
X/^R fgetc$/d
X/^R fgets$/d
X/^R fileno$/d
X/^R floor$/d
X/^R fmod$/d
X/^R fopen$/d
X/^R fprintf$/d
X/^R fputc$/d
X/^R fputs$/d
X/^R fread$/d
X/^R free$/d
X/^R freopen$/d
X/^R fscanf$/d
X/^R fseek$/d
X/^R fstat$/d
X/^R ftell$/d
X/^R fwrite$/d
X/^R getc$/d
X/^R getchar$/d
X/^R getenv$/d
X/^R getopt$/d
X/^R getpid$/d
X/^R gets$/d
X/^R gmtime$/d
X/^R isdigit$/d
X/^R isspace$/d
X/^R isupper$/d
X/^R labs$/d
X/^R localtime$/d
X/^R log$/d
X/^R log10$/d
X/^R lseek$/d
X/^R malloc$/d
X/^R memcpy$/d
X/^R memmove$/d
X/^R memset$/d
X/^R mkdir$/d
X/^R mkfifo$/d
X/^R mknod$/d
X/^R mktime$/d
X/^R modf$/d
X/^R opendir$/d
X/^R pause$/d
X/^R pclose$/d
X/^R perror$/d
X/^R pipe$/d
X/^R popen$/d
X/^R pow$/d
X/^R printf$/d
X/^R putc$/d
X/^R putchar$/d
X/^R puts$/d
X/^R qsort$/d
X/^R read$/d
X/^R readdir$/d
X/^R realloc$/d
X/^R remove$/d
X/^R rename$/d
X/^R rewind$/d
X/^R rewinddir$/d
X/^R scanf$/d
X/^R seekdir$/d
X/^R setbuf$/d
X/^R setvbuf$/d
X/^R sin$/d
X/^R sinh$/d
X/^R sleep$/d
X/^R sprintf$/d
X/^R sqrt$/d
X/^R sscanf$/d
X/^R strcat$/d
X/^R strchr$/d
X/^R strcmp$/d
X/^R strcpy$/d
X/^R strftime$/d
X/^R strlen$/d
X/^R strncmp$/d
X/^R strrchr$/d
X/^R strtod$/d
X/^R system$/d
X/^R tan$/d
X/^R tanh$/d
X/^R telldir$/d
X/^R time$/d
X/^R tmpfile$/d
X/^R tmpnam$/d
X/^R tolower$/d
X/^R tzset$/d
X/^R umask$/d
X/^R ungetc$/d
X/^R utime$/d
X/^R vfprintf$/d
X/^R vprintf$/d
X/^R vsprintf$/d
X/^R write$/d'
-------cut here----- bin/skip_posix
chmod +x bin/skip_posix
echo bin/sortnames 1>&2
sed >bin/sortnames <<'-------cut here----- bin/sortnames' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
X#  remove duplicates from names file
X# cd /netlib/admin
Xn=names
Xsort -u -o $n $n
-------cut here----- bin/sortnames
chmod +x bin/sortnames
echo bin/to_subscribers2 1>&2
sed >bin/to_subscribers2 <<'-------cut here----- bin/to_subscribers2' 's/^X//'
X#!/bin/sh
X# invoked by to_subscribers
Xcd /tmp/to_subscribers || exit 1
Xecho begin mailing `date`
Xls -l
Xls | while read i;do
X	sort -u $i |
X		( cat /netlib/admin/mess/netlibnews
X		echo "	path $i"
X		echo "	unsubscribe unwanteddirectory"
X		echo "------------------------------------------"
X		cat - )| mail $i
X	rm $i
X	sleep 5
Xdone
Xecho end mailing `date`
Xcd
Xrmdir /tmp/to_subscribers
-------cut here----- bin/to_subscribers2
chmod +x bin/to_subscribers2
echo bin/uncombline 1>&2
sed >bin/uncombline <<'-------cut here----- bin/uncombline' 's/^X//'
X#!/bin/sh
XPATH=`pwd`/admin/bin:$PATH:/bin:/usr/bin:/usr/bsd:.
Xtr '\015' '\012'
-------cut here----- bin/uncombline
chmod +x bin/uncombline
echo bin/pgpsigned 1>&2
sed >bin/pgpsigned <<'-------cut here----- bin/pgpsigned' 's/^X//'
X#!/bin/sh
Xecho ok $1 $2
-------cut here----- bin/pgpsigned
chmod +x bin/pgpsigned
echo mess/big 1>&2
sed >mess/big <<'-------cut here----- mess/big' 's/^X//'
XThe reply to your request is quite large and will be sent in pieces.
XIf sometime in the future you should want to check sizes before
Xsubmitting the actual request, use the syntax
X     send list of rg from eispack
Xin place of
X     send rg from eispack.
XThis also lists file names, in case all you want to know is what
Xdepends on what.
X
-------cut here----- mess/big
echo mess/collided 1>&2
sed >mess/collided <<'-------cut here----- mess/collided' 's/^X//'
XUffda!  There was a database update collision while we tried to
Xcarry out your instructions.  Please try again later.
-------cut here----- mess/collided
echo mess/disclaimer 1>&2
sed >mess/disclaimer <<'-------cut here----- mess/disclaimer' 's/^X//'
XCareful! Anything free comes with no guarantee.
-------cut here----- mess/disclaimer
echo mess/netlibnews 1>&2
sed >mess/netlibnews <<'-------cut here----- mess/netlibnews' 's/^X//'
XSubject: new or changed files
X
XThe following files changed in directories that you asked to monitor.
XTo retrieve file "wantedfile",
X	mail netlib@research.bell-labs.com
X	send wantedfile
XTo turn off notification in "unwanteddirectory",
X	mail netlib@research.bell-labs.com
-------cut here----- mess/netlibnews
echo mess/no-topic 1>&2
sed >mess/no-topic <<'-------cut here----- mess/no-topic' 's/^X//'
XSorry, no such topic found.
-------cut here----- mess/no-topic
echo mess/siam 1>&2
sed >mess/siam <<'-------cut here----- mess/siam' 's/^X//'
XThis data from the SIAM MEMBERSHIP DIRECTORY is copyright 1996 by
XSociety for Industrial and Applied Mathematics.  All Rights Reserved.
XTo have your name added, join up!    email  siam@siam.org
X
XAn independent "nalist" is maintained at ORNL; ask na.help@na-net.ornl.gov
Xfor details.  In particular,
X   mail na.whois@na-net.ornl.gov
X   whois Rosener
Xwill look up that name in the "whitepages" database.
X
X
X
-------cut here----- mess/siam
echo mess/sorry 1>&2
sed >mess/sorry <<'-------cut here----- mess/sorry' 's/^X//'
XSorry, no such file was found.  Recheck the index.
XIf that doesn't explain the problem, send a complaint to research!ehg
Xor ehg@research.bell-labs.com.
X
XHere are some example requests, in case syntax is the problem:
X          send index
X          send index for eispack
X          send rg from eispack  
X          who is eric grosse
-------cut here----- mess/sorry
echo mess/sorry_cmd 1>&2
sed >mess/sorry_cmd <<'-------cut here----- mess/sorry_cmd' 's/^X//'
X
XSorry, netlib doesn't recognize or can't execute that command.
-------cut here----- mess/sorry_cmd
echo mess/sorry_enemy 1>&2
sed >mess/sorry_enemy <<'-------cut here----- mess/sorry_enemy' 's/^X//'
X
XOne of the machines in your return address is prohibited from
Xnetlib access.  This is usually because gateway administrators
Xhave asked that netlib not forward through their machines.  More
Xgenerally, such prohibitions result from someone irresponsibly
Xasking for large transmissions (with communication costs paid for
Xby third parties) and not stopping when asked.
X
XThere are also user-id's, like "root", "uucp" and "mailer", that are disabled
Xto protect against mail disasters.
X
XOf course, you are probably not the offending individual and it
Xis unfortunate that you must suffer for others' sins.  Perhaps you
Xcan find another, more direct, route that avoids the excommunicated
Xmachines.
-------cut here----- mess/sorry_enemy
echo mess/sorry_f2c 1>&2
sed >mess/sorry_f2c <<'-------cut here----- mess/sorry_f2c' 's/^X//'
-------cut here----- mess/sorry_f2c
echo mess/sorry_find 1>&2
sed >mess/sorry_find <<'-------cut here----- mess/sorry_find' 's/^X//'
X
XYour search resulted in too large a match.  Please
Xrephrase and try again.  The long reply would have started...
-------cut here----- mess/sorry_find
echo mess/sorry_indiv 1>&2
sed >mess/sorry_indiv <<'-------cut here----- mess/sorry_indiv' 's/^X//'
X
XWere you asking for an entire library?
XTo discourage huge requests and to comply with the requirements
Xof certain copyright holders, our policy is to require that you
Xask for the items in a library that you are specifically interested
Xin.  In case it helps, here's the index for the library:
X
-------cut here----- mess/sorry_indiv
echo mess/sorry_lib 1>&2
sed >mess/sorry_lib <<'-------cut here----- mess/sorry_lib' 's/^X//'
XSorry, no such library is available.  Recheck the general index.
XHere are some example requests, in case syntax is the problem:
X                   
X          send index
X          send index for eispack
X          send rg from eispack
X          who is eric grosse         
-------cut here----- mess/sorry_lib
echo mess/sorry_many 1>&2
sed >mess/sorry_many <<'-------cut here----- mess/sorry_many' 's/^X//'
X
XWe set a limit of 30 requests and 1.5 megabytes.  Since that has now
Xbeen exceeded, the remainder of your message has been discarded.
XYou may resubmit, but remember that someone is paying for communications
Xcosts! To get complete libraries you should probably be using ftp anyway,
Xsince that transfers compressed files and therefore wastes less bandwith.
X
XIf you want to check sizes before submitting the actual request, use the
Xsyntax
X     send list of rg from eispack
Xin place of
X     send rg from eispack.
XThis also lists file names, in case all you want to know is what
Xdepends on what.
-------cut here----- mess/sorry_many
echo mess/sorry_req 1>&2
sed >mess/sorry_req <<'-------cut here----- mess/sorry_req' 's/^X//'
XSubject: netlib syntax error
X
XSorry, the netlib program couldn't understand your request.  
XHere are some example requests, in case syntax is the problem:
X                   
X          send index
X          send index for eispack
X          send rg from eispack
X          who is Gene Golub
X          find fft
X
XDue to the stupid, one-pass nature of the parsing program, if you
Xput the request in the header Subject: line and some mailer puts
Xthat line before any address line, netlib won't know who you are.
XSorry, that will be fixed someday;  in the meantime, put the
Xrequest in the message body proper.
X
XYour mail, as received here, was...
-------cut here----- mess/sorry_req
echo mess/sorry_unsafe 1>&2
sed >mess/sorry_unsafe <<'-------cut here----- mess/sorry_unsafe' 's/^X//'
XSorry, your request included a shell magic character
X     "'`$\n;&|^<>()
Xprohibited for security reasons.  Please try again.
-------cut here----- mess/sorry_unsafe
echo mess/sorry_mgrep 1>&2
sed >mess/sorry_mgrep <<'-------cut here----- mess/sorry_mgrep' 's/^X//'
XSorry, nothing found.
X
XIf you're looking for material that used to be under /netlib/att, see
Xhttp://cm.bell-labs.com/        or  http://www.research.att.com/
XBell Labs (Lucent Technologies)     AT&T Research Labs
X
-------cut here----- mess/sorry_mgrep
echo LIBS 1>&2
sed >LIBS <<'-------cut here----- LIBS' 's/^X//'
X1127 => research
X127 => research
XDEFAULT => eispack linpack quadpack fn go blas fmm fishpack
Xappolo => apollo
Xatt/cs/v7 => att/cs/v7man
Xatt/cs/prog => research
Xawkbookcode => research/awkbookcode
Xbenchmarks => benchmark
Xblas/gemm-based => blas/gemm_based
Xblas1 => blas
Xblas2 => blas
Xblas3 => blas
Xc++/answers => c++/answerbook
Xc++/lapack++ => lapack++
Xc/meshark => c/meschach
Xcacm => toms
Xcalgo => toms
Xcascade => ieeecss/cascade
Xck => cheney-kincaid
Xclapack => clapack clapack/complex clapack/complex16 clapack/double clapack/single clapack/util
Xclapack/complex => clapack/complex clapack/single clapack/util
Xclapack/complex16 => clapack/complex16 clapack/double clapack/util
Xclapack/double => clapack/double clapack/util
Xclapack/single => clapack/single clapack/util
Xcomputers => machines
Xconf => confdb
Xconf/icsr3 => confdb/icsr3
Xcor => blas
Xcore => blas
Xd0min0 => domino
Xd0mino => domino
Xdeboor => pppack
Xdelaunay => voronoi
Xdodepack => odepack
Xdomin0 => domino
Xeispac => eispack
Xeispac/ex => eispack/ex
Xeispak => eispack
Xeispak/ex => eispack/ex
Xeispk => eispack
Xeratta => errata
Xerratta => errata
Xfft => fftpack
Xfishpak => fishpack
Xfitpak => fitpack
Xfnlib => fn
Xfraley => uncon/data
Xgcvpack => gcv
Xgolden => go
Xgoldies => go
Xgraphic => graphics
Xgraphix => graphics
Xhence/demo => hence/demos
Xhompack => hompack blas
Xhpff => hpf
Xitpak => itpack
Xlanczs => lanczos
Xlapac => lapack
Xlapack => lapack lapack/complex16 lapack/complex lapack/double lapack/single lapack/util
Xlapack/single => lapack/single lapack/util
Xlapack/double => lapack/double lapack/util
Xlapack/complex => lapack/complex lapack/single lapack/util
Xlapack/complex16 => lapack/complex16 lapack/double lapack/util
Xlapack/lawn => lapack/lawns
Xlapacker => lapacker blas
Xlapackers => lapacker blas
Xlapak => lapack
Xlapck => lapack
Xlapk => lapack
Xlatex => typesetting
Xleval => lanczos
Xlinpac => linpack  blas
Xlinpack => linpack  blas
Xlinpack => linpack blas
Xlinpackb => linpack blas
Xlinpak => linpack  blas
Xlinpak => linpack blas
Xlinpk => linpack  blas
Xmachine => machines
Xmeschach => c/meschach
Xmeshark => c/meschach
Xmgghat => pdes/mgghat
Xminpak => minpack
Xminpak/ex => minpack/ex
Xmiscelaneous => misc
Xmiscellaneous => misc
Xmonmacs => parmacs
Xmpi1 => mpi
Xna => numeralgo
Xode => ode blas
Xother => misc
Xparmac => parmacs
Xpbwg => parkbench
Xpchip => slatec/pchip slatec/src
Xclawpack => pdes/claw
Xperformance => benchmark
Xpolyhedron => polyhedra
Xportex => port/ex
Xportp => port
Xportp/chk => port/chk
Xportp/ex => port/ex
Xportpex => port/ex
Xppack => pppack
Xpvm => pvm3/pvm2.4
Xpvm/demo => pvm3/pvm2.4/demos
Xpvm/demos => pvm3/pvm2.4/demos
Xpvm3/example => pvm3/ex
Xpvm3/examples => pvm3/ex
Xquadpak => quadpack
Xrksuite => ode/rksuite
Xsblas => blas
Xschedule => sched
Xsciport => scilib
Xscpack => conformal
Xseispac => seispack
Xseispac/ex => seispack/ex
Xseispak => seispack
Xseispak/ex => seispack/ex
Xseispk => seispack
Xsfft => fishpack
Xsfftpack => fishpack
Xsfftpak => fishpack
Xsfnlib => fn
Xsiam => typesetting
Xsiam/typesetting => typesetting
Xsinpack => sminpack
Xsitpack => itpack
Xsitpak => itpack
Xslatec => slatec slatec/src slatec/fishfft slatec/pchip slatec/fnlib slatec/lin
Xslatec/fishfft => slatec/fishfft slatec/fnlib slatec/lin slatec/src
Xslatec/fnlib => slatec/fnlib slatec/src
Xslatec/lin => slatec/lin slatec/src
Xslatec/pchip => slatec/pchip slatec/src
Xslatec/src => slatec/src slatec/fishfft slatec/pchip slatec/fnlib slatec/lin
Xslinpac => linpack blas
Xslinpack => linpack blas
Xslinpack/chk => linpack/chk
Xslinpack/ex => linpack/ex
Xslinpak => linpack blas
Xslinpk => linpack blas
Xsminpak => sminpack
Xsminpak/ex => sminpack/ex
Xsparse1.3 => sparse
Xsparsepack => sparspak
Xsparsepak => sparspak
Xsparspack => sparspak
Xsppack => pppack
Xspppack => pppack
Xstat => att/stat
Xstat/data => att/stat/data
Xstat/doc => att/stat/doc
Xstat/prog => att/stat/prog
Xsvdpac => svdpack
Xsvdpak => svdpack
Xtemplate => templates
Xtool => sched
Xtools => sched
Xtools => sched
Xtypeset => typesetting
Xtypeseting => typesetting
Xucbtest => fp/ucbtest
Xuncon/dat => uncon/data
Xvfftpak => vfftpack
Xvfftpk => vfftpack
Xvfftpk => vfftpack
-------cut here----- LIBS
echo src/F2cargs.c 1>&2
sed >src/F2cargs.c <<'-------cut here----- src/F2cargs.c' 's/^X//'
X#include <stdio.h>
X
X/* For netlib's pipe f2c: check first line of input for flags:
X * If the line has the form
X *	c$f2c -flag -flag...
X * (with "c$f2c " starting in column 1) and no funny characters
X * in the flags (all of which start with -), then echo "-flag -flag..."
X * on stdout; otherwise echo nothing.
X */
X
Xmain(argc,argv)
X char **argv;
X{
X	FILE *f;
X	char buf[128];
X	static char good[256];
X	int i;
X	register char *s, *s1;
X
X	if (argc <= 1) {
X		fprintf(stderr, "%s: expected one arg: a file name\n", *argv);
X		return 1;
X		}
X	if (!(f = fopen(argv[1],"r"))) {
X		fprintf(stderr, "%s: can't open %s\n", argv[0], argv[1]);
X		return 1;
X		}
X	if (fgets(buf, sizeof(buf), f)) {
X		if (strncmp(buf, "c$f2c ", 6))
X			goto done;
X		for(i = 'a'; i <= 'z'; i++)
X			good[i] = good[i+'A'-'a'] = 1;
X		for(i = '0'; i <= '9'; i++)
X			good[i] = 1;
X		for(s = "\t -+=,.!%_\n"; *s; s++)
X			good[*s] = 1;
X		for(s = buf+6; good[*(unsigned char *)s]; s++);
X		if (*s)
X			goto done;
X		for(s = buf+5, s1 = 0;;) {
X			while(*++s == ' ');
X			if (*s != '-') {
X				if (!*s)
X					break;
X				goto done;
X				}
X			if (!s1)
X				s1 = s;
X			while(*++s != ' ')
X				if (!*s)
X					goto for_done;
X			}
X for_done:
X		if (s1)
X			printf("%s", s1);
X		}
X done:
X	fclose(f);
X	return 0;
X	}
-------cut here----- src/F2cargs.c
echo src/Makefile 1>&2
sed >src/Makefile <<'-------cut here----- src/Makefile' 's/^X//'
XCFLAGS=-O
XCMDS=	../bin/reply \
X	../bin/combline \
X	../bin/getdepend \
X	../bin/index_chk.x \
X	../bin/mk.list \
X	../bin/plauger_chk \
X	../bin/retadd \
X	../bin/seq \
X	../bin/to_subscribers
X
Xdebug: reply.o bigmail.o mimemail.o hash.o getfrom.o lsr.o subscribe.o Malloc.o
X	$(CC) -g -o nreply reply.o bigmail.o mimemail.o hash.o getfrom.o \
X		lsr.o subscribe.o Malloc.o
X	date >>../stderr
X	cd /netlib;\
X	(echo "From ehg";echo "send 1127/dup.shar")|\
X		DEBUG=on admin/src/nreply
X	tail -20 ../stderr
X
Xinstall: all
Xall: $(CMDS)
X	touch ../names ../log
X	date >../stderr
X	chmod go+w ../stderr ../names ../log
X../bin/reply: reply.c bigmail.c mimemail.c hash.c getfrom.c lsr.c subscribe.c Malloc.c
X	$(CC) -s -o $@ $(CFLAGS) reply.c bigmail.c mimemail.c hash.c getfrom.c \
X		lsr.c subscribe.c Malloc.c
X	date >>../stderr
X	cat /dev/null > ../../core
X	chmod a+w ../stderr ../../core
X../bin/getdepend: getdepend.c Malloc.c hash.c
X	$(CC) -s -o $@ $(CFLAGS) getdepend.c Malloc.c hash.c
X../bin/combline: combline.c
X	$(CC) -s -o $@ $(CFLAGS) combline.c
X../bin/seq: seq.c
X	$(CC) -s -o $@ seq.c
X../bin/retadd: getfrom.c
X	$(CC) -O -DSTANDALONE -o $@ getfrom.c
X../bin/plauger_chk: plauger_chk.c
X	$(CC) -s -o $@ $(CFLAGS) plauger_chk.c
X../bin/index_chk.x: index_chk.c Malloc.c hash.c
X	$(CC) -s -o $@ $(CFLAGS) index_chk.c Malloc.c hash.c
X../bin/to_subscribers: to_subscribers.c
X	$(CC) -s -o $@ to_subscribers.c
X../bin/mk.list: mk.list.c
X	$(CC) -s -o $@ mk.list.c
X	chmod u+s $@
X
Xbigmail.x: bigmail.c
X	$(CC) -o $@ $(CFLAGS) -DSTANDALONE bigmail.c
X	rm -f bigmail.o # to be sure reply doesn't get wrong version
X
Xoldindex_chk.c: index_chk.c
X	proto -fs index_chk.c > $@
X
Xbundle:
X	cd /netlib/admin; stree `cat bundlefiles; lsr src | grep -v Old` > /netlib/misc/netlib
Xclean:
X	rm -f *.o *.x retadd nreply core
X
-------cut here----- src/Makefile
echo src/Malloc.c 1>&2
sed >src/Malloc.c <<'-------cut here----- src/Malloc.c' 's/^X//'
X#include <stdlib.h>
X#include <string.h>
X#include <stdio.h>
X#include <stdarg.h>
X#include "depend.h"
X
Xint Error_rc = 1;  /* rarely, caller of Error() may want to set this */
Xextern int errno;
X
Xint
XError(char *fmt, ...)
X{
X	va_list ap;
X	va_start(ap,fmt);
X	if(vfprintf(stderr,fmt,ap)<0)
X		fprintf(stderr,"<vfprintf error>");
X	fputc('\n',stderr);
X	va_end(ap);
X	if(errno) perror("<");	/* may or may not be relevant */
X	exit(Error_rc);
X	/* NOTREACHED */
X	return(0);  /* so caller can use  crunch() || Error("bad"); */
X}
X
Xvoid *
XMalloc(size_t n)
X{
X	void	*p;
X	if(n==0)
X		n=1;
X	if (!(p = malloc((size_t)n)))
X		Error("unable to allocate %d bytes",n);
X	return p;
X}
X
Xvoid *
XRealloc(void *ReallocP, int ReallocN)
X{
X	if(ReallocN == 0)
X		ReallocN = 1;
X	if(!ReallocP)
X		ReallocP = Malloc(ReallocN);
X	else if(!(ReallocP = realloc(ReallocP, ReallocN)))
X		Error("unable to allocate %d bytes",ReallocN);
X	return(ReallocP);
X}
X
Xchar*
XStrdup(char* s)
X{
X	int l;
X	if (!s)
X		return 0;
X	l = strlen(s)+1;
X	return strcpy((char *)Malloc((size_t)l), s);
X}
X
X/* returns pointer to start of null-terminated line, or NULL if EOF */
Xchar*
XFgets(char* buf, int bufl, FILE* fp)
X{
X        char* s;
X        int l;
X
X        s = fgets(buf, bufl, fp);
X        if (s==NULL)
X                return NULL;
X        if (ferror(fp))
X                Error("read error");
X        l = strlen(buf)-1;
X        if (buf[l] == '\n')
X                buf[l] = '\0';
X        else if (l == bufl)
X                Error("line longer than %d characters", bufl);
X        return s;
X}
X
-------cut here----- src/Malloc.c
echo src/README 1>&2
sed >src/README <<'-------cut here----- src/README' 's/^X//'
XInstallation instructions:
X
X    Caution.    You'll find that the most common problems are with
X    corrupted mail addresses, network errors, and so on.  You ought
X    to be reasonably expert with e-mail before installing this.
X
XEdit the call to chdir in bin/reply.c to point to the place where the source
Xcode is stored on your system.  /netlib is assumed for illustration here.
XAll the files associated with running the distribution service are in
X/netlib/admin;  contents of the collection are in /netlib/eispack, etc.
X
X	Special suggestions for SunOS 4.1.4 users from mcmahan@cs.utk.edu:
X	#define atexit on_exit
X	#define memmove(a,b,c) bcopy(b,a,c)
X	#define _IFMT           0170000 /* type of file */
X	#define _IFLNK          0120000 /* symbolic link */
X	#define S_ISLNK(m)      (((m)&_IFMT) == _IFLNK)
X
XEdit /netlib/master/index to reflect what you are distributing (or, as we
Xdo, create that file automatically from ".master" files in each directory.)
XEdit the various disclaimers in /netlib/admin/mess.  You may also wish to
Xadd disclaimer files in the source directories.
X
XTo activate mail processing:
X*  if you run 4.1BSD, put
X          netlib: "|/netlib/admin/bin/reply"
X          netlibd: "|/netlib/admin/bin/netlibd"
X   into /usr/lib/aliases and execute newaliases;
X*  else if your system has no equivalent mechanism, try the
X   daemon in /netlib/admin/bin/Old-mail-sys.
XThe script admin/bin/netlibd contains (on line 3) "cd /netlib", which you
Xmay need to change.  Since Berkeley's alias facility provides no way to
Xset the userid, you probably ought to put your name and address in the
Xmessage so people know who is actually sending the mail.
X
XTo try the system out, "echo send index | mail netlib" and expect return
Xmail in a couple minutes.  A line should be added to /netlib/admin/log and
Xadmin/stderr should remain empty.
X
XOnce the basics are working, you can polish things a bit.
XSet up a nightly process to run /netlib/admin/bin/mkdirectory.
XUsing "Depend -c"  you can create ".depend" files in each directory to
Xrepresent the relationship between source files there.  Not only is this
Xfile itself useful to browsers, but netlib responds to a requests for
X"rg from eispack" by sending not just rg.f, but also balbak.f, hqr2.f...
X
XYou should permanently save /netlib/admin/log so that bug fixes can be
Xdistributed, traffic measurements made, and annual summaries sent to code
Xauthors.  The format of the log is:  date time [address] bytes-sent
Xlibrary/item possibly followed by: L = list, F = find.  The [address] is
Xfollowed by "l" if the address was recognized as local.  In contrast, the
Xcopy of incoming messages kept in /tmp/netreq is for debugging mail headers
Xand monitoring illegal request syntax.  Discard when convenient (perhaps by
Xan "rm" in /etc/rc) or, if you prefer, comment out the line in
Xreply.c:handle() that writes the file.
X
XSend suggestions to ehg@research.bell-labs.com.
X
XHappy Hacking
XEric Grosse
X
X
X
Xindex to files in /netlib/admin/
X
XLIBS		maps request library name into /netlib directory names
Xindex		top level description of contents of /netlib
X  *.lcl		describes restricted parts of the collection
Xlog		record of requests
Xnames		aid to identify e-mail addresses and human names
Xstderr		error logfile
Xgroups/		lists of machine names
X enemies	... from which requests are ignored
X local		... authorized for restricted parts of /netlib
Xmess/		various error messages
X
Xbin/
X Old-mail-sys	alternative to Version 9 Unix "Pipe to cmd" facility
X bigmail.c	break up large messages into smaller chunks
X combline.c	convert paragraph to single line, for searching by mgrep
X disclaim	script invoked by reply to add disclaimer to message
X getfrom.c	code for parsing RFC822 Internet standard From: address
X ldMx,ldM,lo	identify dependencies between files
X mgrep		script to (sequentially) search a database
X mvlower	utility for installing source files
X netlibd	another mailbox daemon;  "use netlib, not netlibd"
X reply.c	netlib server
X retadd.c	standalone driver for getfrom.c
X sortnames	compress admin/names
X
X
X
X
X
X
Xchecklist for adding new directories
X
X1. cd /netlib;mkdir foo
X
X3. ed master/index
X
X5. cp files foo;  be sure they are publicly readable
X
X6. ed foo/index, foo/.master, foo/changes, and possibly foo/disclaimer
X
X
Xchecklist for adding new file to old directory
X
X1. cd /netlib/foo
X
X2. cp files /netlib/foo/;  be sure they are publicly readable
X
X3. ed /netlib/foo/index, foo/changes
X
-------cut here----- src/README
echo src/addr_canon 1>&2
sed >src/addr_canon <<'-------cut here----- src/addr_canon' 's/^X//'
X#!/bin/sh
X# for canonicalizing /netlib/xxxx/.list
Xsed '/\(.*\)@\(.*\)/s//\2!\1/' |
Xsort -u
-------cut here----- src/addr_canon
chmod +x src/addr_canon
echo src/bigmail.c 1>&2
sed >src/bigmail.c <<'-------cut here----- src/bigmail.c' 's/^X//'
X/*
X *  bigmail - split files into self-recreating chunks and mail
X *
X * 1 Apr 1991 by Eric Grosse  ehg@research.bell-labs.com
X * Copyright (c) 1991,1992 by AT&T.
X * Permission to use, copy, modify, and distribute this software for any
X * purpose without fee is hereby granted, provided that this entire notice
X * is included in all copies of any software which is or includes a copy
X * or modification of this software and in all copies of the supporting
X * documentation for such software.
X * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
X * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
X * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
X * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
X *
X * The purpose of this function (or program, if compiled -DSTANDALONE)
X * is to mail to argv[1] with subject argv[2] a list of files in argv[3]
X * up to argv[n].  The files are to be formated as (several) shell
X * archives, each of size<maxbyte.
X * The archives should, even if executed out of order, reconstruct
X * the original files.  Files may be larger than maxbyte and hence
X * broken across archives.
X *
X * No checksum is used, because none is universally available and we
X * don't want to have to distribute checksum code.  Netlib tries to
X * store only files that survive the ravages of mail;  e.g. no long lines,
X * no significant trailing blanks, etc.  (Some authors do give us
X * trailing blanks, which would lead to spurious checksum warnings.)
X *
X * Plauger argues that for maximum portability MAXLINE should be 511.
X * A number of files in netlib violate this and I got tired of
X * haranguing authors, in the absence of an actual reported linelimit
X * problem.  So I compromised at 1000.  If this number were increased
X * too far, I would start to worry about excessive slop in the "pieces"
X * count.  There's no way to count exactly without making an extra
X * pass over the file.
X *
X * 9 Jan 92   Too many people were confused by error messages from
X * non-ANSI C compilers, so I've given in and filtered the source through
X * ghostscript's ansi2knr.  I'd would like to encourage people to use
X * function prototypes, but on the other hand I don't want my
X * mailbox to fill up with complaints, either!
X *
X */
X#define _POSIX_SOURCE
X#define _POSIX2_SOURCE
X#include <stdio.h>
X#include <string.h>	/* strrchr(), known to BSD folks as rindex() */
Xextern FILE    *popen(const char *, const char *);
X
X#define MAXLINE 1000	/* max bytes per line, counting newline */
X#define HDR_SIZE 600	/* bytes that might be needed by mail header */
X			/* From:, Received:, Subject:, WhoKnowsWhatElse: */
Xstatic char cutstring[] = "bigmail CUT HERE............";
Xstatic int cutsize;
Xstatic char disclaimer[] = "Anything free comes with no guarantee!";
Xstatic char piece_space[100][12]; /* constructed filenames */
X
X/* open filename for reading; return handle and size */
Xstatic
Xvoid
Xfopensize(filename, inp, filesizep)
X   char* filename; FILE** inp; long* filesizep;
X{
X	if( (*inp = fopen(filename,"r")) == NULL){
X		fprintf(stderr,"%s:\n",filename);
X		quit(1,"can't open");
X	}	/* would love to zopen(), but then couldn't fseek */
X	if( fseek(*inp,0L,2) == -1 ){
X		fprintf(stderr,"%s:\n",filename);
X		quit(1,"can't seek to end");
X	}
X	*filesizep = ftell(*inp);
X	if( fseek(*inp,0L,0) == -1 ){
X		fprintf(stderr,"%s:\n",filename);
X		quit(1,"can't seek to begin");
X	}
X}
X
X
X/* begin mail message */
Xstatic
Xint
Xbeginmess(mcnt, address, maxbyte, subject, outp)
X   int mcnt; char* address; long maxbyte; char* subject; FILE** outp;
X{
X	char cmd[512];
X	static int first = 0;
X	if(address){
X		if(strlen(address)>=500)
X			quit(strlen(address)+100,"address too long");
X		sprintf(cmd,"/bin/mail %s",address);
X		if( (*outp = popen(cmd,"w")) == NULL){	/* POSIX.2 */
X			fprintf(stderr,"%s:\n",cmd);
X			quit(1,"can't popen");
X		}
X	}else{
X		*outp = stdout;
X	}
X	if(!subject)
X		subject = "Subject: bigmail";
X	if(mcnt>=0)
X		fprintf(*outp,"%s %2d\n\n#!/bin/sh\n",subject,mcnt);
X	else
X		fprintf(*outp,"%s\n\n#!/bin/sh\n",subject);
X	fprintf(*outp,"# to unpack, sh this message in an empty directory\n");
X	fprintf(*outp,"PATH=/bin:/usr/bin\n");
X	if(first++==0)
X		fprintf(*outp,"echo \"%s\"\n",disclaimer);
X	return( maxbyte-HDR_SIZE-strlen(subject));
X}
X
X
X/* end mail message */
Xstatic
Xvoid
Xendmess(outp,last)
X   FILE** outp;
X   int last;  /* is this the last message? */
X{
X	int rc;
X	static int mess_cnt = 0;
X	if(last)
X		fprintf(*outp,"#define END\n");
X		/* this rather odd syntax is designed to allow Fortranners to compile
X		    messages directly, without unbundling */
X	fflush(*outp);
X	if(*outp==stdout){
X		fputs("#----------------------------------------\n",*outp);
X	}else{
X		if((rc=pclose(*outp)) != 0)	/* POSIX.2 */
X			fprintf(stderr,"bad pclose %d\n",rc);
X		*outp = (FILE*)0;
X	}
X	if(mess_cnt++>2)
X		sleep(1);  /* avoid saturating the mail system */
X}
X
X
X/* begin file (possibly several per message) */
Xstatic
Xint
Xbeginfile(out, filename)
X   FILE* out; char* filename;
X{
X	int bytes_used=0;
X	char *f=filename;
X	while(f=strchr(f,'/')){
X		/* This could be made smarter, remembering which directories
X			have already been checked in this message. */
X		*f = '\0';
X		if(out)
X			fprintf(out,"test ! -d %s && mkdir %s\n",
X				filename,filename);
X		bytes_used += 21+2*strlen(filename);
X		*f++ = '/';
X	}
X	if(out)
X		fprintf(out,"cat > %s <<'%s'\n",filename,cutstring);
X	bytes_used += 11+strlen(filename)+cutsize;
X	return(bytes_used);
X}
X
X
Xstatic
Xint
Xendfile(out)
X   FILE* out;
X{
X	fprintf(out,"%s\n",cutstring);
X	return(cutsize+1);
X}
X
X
X/* make up temp file names if file too big for single message */
Xstatic
Xint
Xpiece_names(filename, out, room, filesize, maxbyte, subjectlen, pieces)
X   char* filename; FILE* out; long room; long filesize;
X	long maxbyte; int subjectlen; char* pieces[];
X{
X	/* construct filenames of form  12345678P99 */
X	int npieces;		/* how many messages will file be split over? */
X	static int pid = 0;	/* so filenames unique across processes */
X	static job_cnt = 0;	/* so filenames unique in this process */
X	int beg_end = beginfile((FILE *)0,filename)+(cutsize+1);
X	long per_piece;
X	int i;
X	if(!pid)
X		pid = getpid();
X	pieces[0] = filename;
X	npieces = 1;
X	room -= beg_end;	/* space for beginfile/endfile */
X	room -= MAXLINE;	/* because catfile has no line lookahead */
X	if(filesize>room){
X		/* can't fit in remainder of this message. */
X		filesize -= room;  /* how much left? */
X		per_piece = maxbyte - HDR_SIZE - subjectlen - beg_end - MAXLINE;
X		while( filesize>0 ){
X			npieces += 1;
X			filesize -= per_piece;
X		}
X		for(i=2; i<=npieces; i++){
X			if(job_cnt>99){
X				fprintf(out,"too many messages: %s\n",filename);
X				fprintf(stderr,"%s:\n",filename);
X				quit(1,"too many messages");
X			}
X			sprintf(pieces[i-1]=piece_space[job_cnt],
X				"%.5dP%.2d",pid,job_cnt);
X			job_cnt += 1;
X		}
X	}
X	return(npieces);
X}
X
X
X/* return non-zero if file not exhausted */
Xstatic
Xint
Xcatfile(filename, in, out, proom)
X   char* filename; FILE *in; FILE *out; long* proom;
X{
X	char line[MAXLINE]; /* see discussion at top of file */
X	register int linesize, lastchar;
X	register long room = *proom;
X	static int errmess = 0;
X	lastchar = '\n';
X	while(room>MAXLINE && fgets(line,sizeof(line),in)!=NULL &&
X			(linesize=strlen(line))>0 ){
X		if(linesize>=sizeof(line)-1){
X			fprintf(out,"line too long in %s\n",filename);
X			fprintf(stderr,"%s:\n",filename);
X			quit(1,"line too long");
X		}
X		if(fputs(line,out)==EOF){
X			quit(1,"write error");
X		}
X		room -= linesize;
X		lastchar = line[linesize-1];	/* usually \n */
X		if( linesize==2 && *line=='.' && lastchar=='\n' ){
X			if(!errmess++)
X				fprintf(stderr,"%s has a . line\n",filename);
X			/* exit(1); /* this was too radical */
X			line[1]=' ';
X			line[2]='\n';
X			linesize=3;
X		}
X		if(*line==*cutstring && strncmp(line,cutstring,cutsize)==0 ){
X			fprintf(stderr,"%s has a %s line\n",filename,cutstring);
X			quit(1,"bad cutstring");
X		}
X	}
X	if(lastchar!='\n')
X		fputs("\n",out);
X	*proom = room;
X	return(room<=MAXLINE);
X}
X
X
Xstatic
Xint
Xifdone(out, pieces, npieces)
X   FILE* out; char* pieces[]; int npieces;
X{
X	int i;
X	if(npieces>1){
X		fprintf(out,"test -w %s &&\n",pieces[0]);
X		for(i=1; i<npieces; i++)
X			fprintf(out,"test -r %s &&\n",pieces[i]);
X		fprintf(out,"(\n");
X		for(i=1; i<npieces; i++)
X			fprintf(out," cat %s >> %s; rm %s\n",
X				pieces[i],pieces[0],pieces[i]);
X		fprintf(out,")\n");
X		return(16 + (npieces+2)*strlen(pieces[0])+
X			npieces*(2*strlen(pieces[1])+27));
X	}else{
X		return(0);
X	}
X}
X
X
X/* return length of common directory prefix */
Xstatic
Xint
Xchopdir(file, nfile)
X	char** file;	/* filenames */
X	int nfile;	/* number of filenames */
X{
X	int ntry = 1;	/* number of filenames yet to check */
X	char *s;	/* end of a candidate common prefix */
X	int c;		/* prefix length */
X	if(nfile<=0)
X		return(0);
X	s = file[0]+strlen(file[0]);  /* terminating '\0' */
X	while(ntry>0){
X		s--;
X		while(s>=file[0] && *s!='/') s--;  /* next last '/' */
X		c = s-file[0]+1;
X		if(c<=0) return(0);	/* in case strncmp() is buggy */
X		for(ntry=nfile-1; ntry>0; ntry--){
X			if(strncmp(file[0],file[ntry],c)!=0)
X				break;
X		}
X	}
X	return(c);
X}
X	
X
X/*
X * It is assumed that the caller has already verified that address
X * is safe to give to the shell, i.e. contains no backquotes, spaces, etc.
X */
Xvoid
Xbigmail(address, maxbyte, subject, file, nfile)
X  
X	char* address;	/* address to send to, or stdout if 0 */
X	long maxbyte; 	/* max number of bytes per mail message */
X	char* subject;	/* subject line */
X	char** file;	/* filenames */
X	int nfile;	/* number of filenames */
X{
X	long room;	/* how many bytes can we still send in this message? */
X	long filesize;	/* how many bytes in this file? */
X	int npieces;	/* how many pieces might this file require? */
X	int piece;	/*      make sure we send exactly that many */
X	char* pieces[101];  /* filenames for pieces */
X	int mcnt = 0;	/* so users can check for missing pieces */
X	FILE *in;	/* this file */
X	FILE *out = 0;	/* pipe to mail process for this message */
X	int commprefix = chopdir(file,nfile);
X	if(maxbyte<=3000+HDR_SIZE){
X		room = beginmess(-1,address,maxbyte,subject,&out);
X		fprintf(out,"Minimum mailsize is %d;  you asked for %ld\n",
X			3000+HDR_SIZE+1,maxbyte);
X		endmess(&out,0);
X		return;
X	} /* else worry about fragmentation */
X	cutsize = strlen(cutstring);
X	while(nfile>0){
X		fopensize(*file,&in,&filesize);
X		if( !out )
X			room = beginmess(++mcnt,address,maxbyte,subject,&out);
X		npieces = piece_names(*file,out,room,filesize,
X				maxbyte,strlen(subject),pieces);
X		pieces[0] = file[0]+commprefix;
X#ifdef SEMICRAZY
X		/* for administrators who want to hide original filename */
X		if(nfile>1) quit(1,"can't send several anonymous files");
X		pieces[0] = "anon";
X#endif
X		piece = 1;
X		room -= beginfile(out,pieces[piece-1]);
X		while(catfile(pieces[piece-1],in,out,&room)){
X			room -= endfile(out);
X			piece++;
X			room -= ifdone(out,pieces,npieces);
X			endmess(&out,0);
X			if(piece>npieces) quit(666,"INTERNAL ERROR, npieces");
X			room = beginmess(++mcnt,address,maxbyte,subject,&out);
X			room -= beginfile(out,pieces[piece-1]);
X		}
X		fclose(in);
X		room -= endfile(out);
X		if(npieces>1){
X			while(++piece<=npieces){
X				/* put out empty pieces as necessary */
X				if(room<1000){
X					endmess(&out,0);
X					room = beginmess(++mcnt,address,maxbyte,subject,&out);
X				}
X				room -= beginfile(out,pieces[piece-1]);
X				room -= endfile(out);
X			}
X			room -= ifdone(out,pieces,npieces);
X		}
X		file++;
X		nfile--;
X	}
X	if(out && out!=stdout)
X		endmess(&out,1);
X}
X
X
X#ifdef STANDALONE
X#include <stdlib.h>
Xquit(rc, mess)
Xint	rc;
Xchar	*mess;
X{
X	fprintf(stderr," %s\n", mess);
X	exit(rc);
X}
X
Xmain(argc, argv)
X   int argc; char* argv[];
X{
X	char *s;
X	long maxbyte = 100000;
X	if(s=getenv("N"))
X		maxbyte = atoi(s);
X	if(argc<4)
X		quit(1,"N=100000 bigmail address subject files...");
X	bigmail(argv[1],maxbyte,argv[2],argv+3,argc-3);
X	exit(0);
X}
X#endif
-------cut here----- src/bigmail.c
echo src/changes 1>&2
sed >src/changes <<'-------cut here----- src/changes' 's/^X//'
XSubject: changes in netlib processor
X
XFrom grigg!sue Mon Mar 24 10:54 EST 1986
XNetlib was released on 3/21/86
X
X27Jun93 ehg
X  /netlib/admin/bin/ldM	Added feature so "send all from x" sends all files mentioned
X	in "dependencies".  This gets around problems with sloppy directories that
X	have a variety of tar files, subdirectories, etc.
X#  Eric Grosse   ehg@research.att.com   20 Jun 1991
X#  This is a replacement for the older version of ldM (used 1985-1991 in
X#  netlib), which had to be replaced because
X#     1) SGI's loader map was buggy.
X#     2) some netlib's didn't have Fortran compilers.
X#     3) some netlib's run on gateways, for which object libraries
X#        are not needed for other purposes.
X#  The obscure sed script is merely glue to make this compatible with
X#  the previous version of ldM and with the syntax of "lo".
X# From dmg Tue Feb  2 18:06 EST 1993
X# I adjusted /netlib/admin/bin/ldM to keep sed from dropping core
X# when presented with a line over 4000 characters long.  The script,
X# though shorter, takes two more processes to do the same job, but
X# without danger to sed.
X
X26Sep93 ehg
X  added ".research.att.com" as one more suffix to strip off if(upas)
X
XFrom dmg Fri Nov 19 08:25 EST 1993
XAfter temporarily turning on group write permissions for
X/netlib/admin/bin, I moved F2C, F2cargs, F2cargs.c, Netlib.f2c
Xand F2c.sh to /netlib/admin/bin and adjusted reply.c to invoke
Xadmin/bin/F2C rather than f2c/F2C.
X
X28Mar94 ehg
X  moved src back to its own directory.
X  added plauger_chk, index_chk.
X
X17Apr94 ehg
X  substantial clean-up of code, not much intentional change to function.
X
X20Apr94 ehg
X  eliminate potential overwriting bug
X
X4May94 ehg
X  fixed bug in Depend -t regarding .h files
X
X22Jun94 ehg
X  make "send all from x" work even if there is no .depend file.
X  turn "send x" into "send index for x".
X
X6Jul94 ehg
X  add "subscribe" and "unsubscribe"
X
X11Jul94 ehg
X  Reply-To: takes precedence
X
X12Aug94 ehg
X  more AT&T nonsense
X
X1Sep94 ehg
X  Depend -t has to make D explicit before rewriting F
X
X18Sep94 ehg
X  Why did "send x/y" parse set depend=0?  Removed it.
X
X13Jan95 ehg
X  bigmail.c should say "in an empty directory".
X
X25Jan95 ehg
X  subscribe.c, to_subscribers.c, mk.list.c
X
X27Feb95 ehg
X  reply.c:  don't "send x" => "send index from x" when cmd==FIND
X  reply.c, Malloc.c:  only call perror if errno set
X
X19Mar95 ehg
X  reply.c:  -DFORWARD
X	Also took .list files out of my ftp archive, to discourage
X	mirroring of them elsewhere, and to eliminate possible subscriber
X	confusion because of the delay copying from pyxis to bootes.
X
X8Apr95 ehg
X  reply.c:  FS0, to work around apparent bug in gcc.
X
X28May95 ehg
X  bigmail.c: change cutstring to avoid collision with numeralgo/na7.
X    (We don't want to use the "sed s/^X//" trick, because we want to
X    produce shar files that require very little Unix knowledge.)
X
X31May95 ehg
X  getfrom.c, reply.c:  watch out for empty Reply-To:
X
X2Jun95 ehg
X  reply.c  NOTE: IMPORTANT SECURITY CHANGE.   In the past, only directories
Xexplicitly listed could be accessed.  Now, the LIBS file is used only to
Xsupply aliases, and anything under the chroot'd directory is accessible.
X
X17Jun96 ehg
X  subscribe.c:  create .newlist in /tmp to avoid permission problems.
X30Jun96 ehg
X  reply.c:  add bell-labs.com and lucent.com
X
X7Nov97 ehg
X  subscribe.c:  change / to _ in tmpname
X14Mar99 ehg
X  mimemail.c   new code for emailing binary files.   Yes, some netlib
X	users say they still prefer email, some say they have only email
X	not ftp or http!
X  reply.c  include mimemail as alternative to bigmail
X
X27Oct99 ehg
X  mimemail.c, bigmail.c   changed command "mail" to "/bin/mail"
X	to work better on SunOS 4.1.4
-------cut here----- src/changes
echo src/combline.c 1>&2
sed >src/combline.c <<'-------cut here----- src/combline.c' 's/^X//'
X/*  Joins lines together, with items separated on input by empty lines. */
X/*  Designed to replace refer by egrep. */
X
X#include <stdio.h>
X#define MAXITEM 8000
X#define LM '\015'
X/* line marker, so item can be reconstructed by  tr '\015' '\012' */
X
Xchar	item[MAXITEM];
Xchar	*l;
Xint	n, item_no;
Xint	maxlen, maxno;
X
X
X/* like gets(), but safe */
Xchar *
Xgetsn(s,m)
X	char *s;
X	int m;
X{
X	if(fgets(s,m,stdin)==NULL)
X		return(NULL);
X	m = strlen(s)-1;
X	if(s[m]=='\n')
X		s[m] = '\0';
X	return(s);
X}
X
X
Xmain()
X{
X	int	j;
X	item_no = 0;
X	maxno = 0;
X	maxlen = 0;
X	initi();
X	while (getsn(l, MAXITEM - 1 - n) != NULL) {
X		j = strlen(l);
X		if (j == 0) {
X			flushi();
X		} else {
X			l += j;
X			*l++ = LM;
X			*l = '\0';
X			n += j + 1;
X		}
X	}
X	if (n > 0) {
X		flushi();
X	}
X	exit(0);
X}
X
X
Xiniti()
X{
X	item[0] = '\0';
X	n = 0;
X	l = item;
X}
X
X
Xflushi()
X{
X	item_no++;
X	if (n > maxlen) {
X		maxlen = n;
X		maxno = item_no;
X	}
X	fputs(item, stdout);
X	putc('\n', stdout);
X	initi();
X}
-------cut here----- src/combline.c
echo src/depend.h 1>&2
sed >src/depend.h <<'-------cut here----- src/depend.h' 's/^X//'
Xstruct HashEntry {char* Key; int Code;};
Xtypedef struct HashEntry *HashTable;
Xextern char*	Fgets(char*,int,FILE*);
Xextern void	hashdel(HashTable*,int);
Xextern int	hashins(HashTable*,int,char*,int);
Xextern int	hashasu(char*,int);
Xextern int	hashpjw(char*,int);
Xextern int	hashsrch(HashTable*,char*);
Xextern void*	Malloc(size_t);
Xextern void*	Realloc(void*,int);
Xextern char*	Strdup(char*);
-------cut here----- src/depend.h
echo src/getdepend.c 1>&2
sed >src/getdepend.c <<'-------cut here----- src/getdepend.c' 's/^X//'
X/* read .depend file, compute transitive closure for request */
X/* Eric Grosse  9 Dec 1993;  cleaned for export 21 Mar 1994 */
X
X/* This reads from stdin a .depend file and builds 1) an array of
Xfiles, each with a list of symbols referenced, and 2) a hash table
Xmapping defined symbols into files.  Now, to compute the transitive
Xclosure of a list of symbols given as arguments on the command line,
Xkeep a stack of symbols yet to be resolved, a stack of files yet to be
Xexpanded, and a list of files processed.  */
X
X#include <string.h>
X#include <stdio.h>
X#include "depend.h"
X
Xtypedef struct{
X	char *name;
X	char **ref;	/* array of symbols referenced by this file */
X	int nref;
X	int done;	/* has this already been added to hit? */
X} File;
X
Xtypedef struct{
X	File *file;
X	int nfile;
X	HashTable Def;	/* symbol -> index of file defining it */
X} Dependencies;
X
Xstatic void
Xsave_refs(File *f, int nref, char **ref)
X{
X	f->ref = (char**)Malloc(nref*sizeof(*f->ref));
X	memcpy(f->ref,ref,nref*sizeof(*f->ref));
X	f->nref = nref;
X}
X
Xvoid
Xgetdepend(Dependencies *d)
X{
X	int i;	/* HashTable index */
X	int nf = 0;	/* index of file being read */
X	int maxf = 1000; /* guess at an upper bound for nf */
X	int nref, maxref = 5000; /* guess at an upper bound for nref */
X	int linelen;
X	char *name;
X	char line[1000];  /* surely 100 would be enough, but what the heck */
X	char **ref = Malloc(maxref*sizeof(*ref));
X	File *f = Malloc(maxf*sizeof(*f));
X	HashTable D = 0;
X
X	while(Fgets(line,sizeof(line),stdin)!=NULL){
X		if(line[0]==0 || line[0]=='#') continue;
X		linelen = strlen(line);
X		if(line[linelen-1]=='\n')
X			line[--linelen] = '\0';
X		name = Strdup(line+2);
X		switch(line[0]){
X		  case 'F':
X			if(nf>0)
X				save_refs(&f[nf],nref,ref);
X			nref = 0;
X			nf++;
X			if(nf>=maxf){
X				maxf *= 2;
X				f=Realloc(f,maxf*sizeof(*f));
X			}
X			f[nf].name = name;
X			f[nf].done = 0;
X			/* FALL THROUGH (file implicitly defines own name) */
X		  case 'D': i = hashsrch(&D,name);
X			if(i<0) hashins(&D,i,name,nf);
X			break;
X		  case 'R':
X			nref++;
X			if(nref>=maxref){
X				maxref *= 2;
X				ref=Realloc(ref,maxref*sizeof(*ref));
X			}
X			ref[nref-1] = name;
X			break;
X		  /* ignore other lines */
X		}
X	}
X	if(nf>0 && nref>0)
X		save_refs(&f[nf],nref,ref);
X	free(ref);
X	d->file = f;
X	d->nfile = nf;
X	d->Def = D;
X}
X
X
X
X
Xtypedef struct{
X	char **p;
X	int np, maxp;
X} stack;
X
Xstatic void
Xnew_stack(stack *s)
X{
X	s->maxp = 1000;
X	s->p = (char**)Malloc(s->maxp*sizeof(char*));
X	s->np = 0;
X}
X
Xstatic void
Xpush(char *p, stack *s)
X{
X	if(s->np==s->maxp){
X		s->maxp *= 2;
X		s->p = (char**)Realloc(s->p,s->maxp*sizeof(char*));
X	}
X	s->p[s->np++] = p;
X}
X
Xstatic char*
Xpop(stack *s)
X{
X	if(s->np==0) return 0;
X	return(s->p[--s->np]);
X}
X
X
X
Xstatic void
Xresolve(Dependencies *d, stack *hit, stack *need)
X{
X	int i, k;
X	char *r;
X	File *f;
X
X	while(r = pop(need)){
X		i = hashsrch(&d->Def,r);
X		if(i>0){
X			f = &d->file[d->Def[i].Code];
X			if(!f->done){
X				f->done = 1;
X				push(f->name,hit);
X				for(k = 0; k<f->nref; k++)
X					push(f->ref[k],need);
X			}
X		}  /* else unsatisfied external */
X	}
X}
X
Xstatic int
Xcmp(const void*a, const void*b)
X{
X	return(strcmp(*(char **)a,*(char **)b));
X}
X
Xvoid
Xmain(int argc, char**argv)
X{
X	Dependencies D;
X	stack hit, need;
X	int i;
X
X	new_stack(&hit);
X	new_stack(&need);
X	while(argc>1)
X		push(argv[--argc],&need);
X	getdepend(&D);
X	resolve(&D,&hit,&need);
X	/* hit isn't really a stack; now we prove it. */
X	qsort(hit.p,hit.np,sizeof(char*),cmp);
X	for(i = 0; i<hit.np; i++)
X		printf("%s\n",hit.p[i]);
X	exit(0);
X}
-------cut here----- src/getdepend.c
echo src/getdepend.mbox 1>&2
sed >src/getdepend.mbox <<'-------cut here----- src/getdepend.mbox' 's/^X//'
XFrom ehg Tue Apr 9 07:45:18 EDT 1996
XFrom: Eric Grosse <ehg@netlib.bell-labs.com>
XX-URL: http://netlib.bell-labs.com/who/ehg/
XTo: cs.utk.edu!jhorner
XSubject: Re: Getdepend 
X
XThe .depend file records symbols defined and referenced by the
Xcorresponding object files.  Thus it lists only "first order"
Xdependencies.
X
XThe job of getdepend is to take a list of symbols, given as
Xcommand line arguments, read the .depend database from its
Xstandard input, compute the transitive closure of the named
Xsymbols relative to that database, and finally print a list
Xof files that together satisfy as many of the dependencies
Xas possible.  Thus it works very much like a linker in the
Xoperating system.
X
XSymbols are currently derived by looking at object files
Xgenerated by compiling the source file.  In the case of Fortran,
Xthis means a number of system-specific Fortran runtime symbols
Xare listed, and then ignored by getdepend.  d_cnjg is an
Xexample.  For a while I used to screen these out, but this
Xgets rather tricky when there are systems like f2c around.
XSo for safety I leave them all in.
X
-------cut here----- src/getdepend.mbox
echo src/getfrom.c 1>&2
sed >src/getfrom.c <<'-------cut here----- src/getfrom.c' 's/^X//'
X#include <stdio.h>
X#include <string.h>
X
Xint
Xunsafe(char *s)
X{
X	char c;
X	/* check for characters interpreted by the shell (by which nefarious
X           users might otherwise break into the system) */
X	for (c = (*s); c != '\0'; c = (*++s)) {
X		if ( strchr("\"'`$\n;&|^<>()\\", c) || (c & 0200) ){
X			fprintf(stderr,"unsafe(%s) saw %c\n",s,c);
X			return(1);
X		}
X	}
X	return(0);
X}
X
X
X/*
X * EXTRACT SENDER'S ADDRESS OUT OF RFC822 "FROM" LINE
X *  The sender is either the next first whitespace delimited token or
X *  the first thing enclosed in "<" ">".
X *  (leading "From: " is already deleted before entry)
X *  adapted from /n/bowell/src/cmd/upas (Dave Presotto)
X *  modified by Eric Grosse to stop after comma and to allow nested parens
X */
Xint   /* 0 on success, 1 if address is unparseable or unsafe */
Xgetfrom(char *line, char *sender)
X{
X	char	*lp, *sp;
X	int	comment = 0;
X	int	anticomment = 0;
X
X	sp = sender;
X	for (lp = line; *lp; lp++) {
X		if (comment) {
X			if (*lp == ')') {
X				comment--;
X			} else if (*lp == '(') {
X				comment++;
X			}
X			continue;
X		}
X		if (anticomment) {
X			if (*lp == '>')
X				break;
X		}
X		switch (*lp) {
X		case '\t':
X		case '\n':
X			break;
X		case ' ':
X			if (strncmp(lp, " at ", sizeof(" at ") - 1) == 0 ||
X				strncmp(lp, " AT ", sizeof(" AT ") - 1) == 0 ) {
X				*sp++ = '@';
X				lp += sizeof(" at ") - 2;
X			}
X			break;
X		case '<':
X			anticomment = 1;
X			sp = sender;
X			break;
X		case '(':
X			comment++;
X			break;
X		case ',':  /* looks like multiple address; chop */
X			*sp++ = '\0';
X		default:
X			*sp++ = *lp;
X			break;
X		}
X	}
X	*sp = '\0';
X	if(!*sender || unsafe(sender)) return 1;
X	return 0;
X}
X
X
X#ifdef STANDALONE
X#define LINESIZE 1026
Xchar	line[LINESIZE]; /* raw input line */
Xchar	from[LINESIZE]; /* unfolded from line */
Xchar	address[LINESIZE]; /* return address */
X
X/* like gets(), but safe */
Xchar *
Xgetsn(s,m)
X	char *s;
X	int m;
X{
X	if(fgets(s,m,stdin)==NULL)
X		return(NULL);
X	m = strlen(s)-1;
X	if(s[m]=='\n')
X		s[m] = '\0';
X	return(s);
X}
X
X/* case independent prefix compare */
Xint
Xcicmp(char *s1, char *s2)
X{
X        int c1, c2;
X
X        for(; *s1; s1++, s2++){
X                c1 = *s1;
X                c2 = isupper(*s2) ? tolower(*s2) : *s2;
X                if (c1 != c2)
X                        return -1;
X        }
X	return(0);
X}
X
Xvoid
Xmain(int argc, char**argv)
X{
X	int	inside = 0, replyto = 0;
X	char	c, *p;
X
X	while(getsn(line, LINESIZE) != NULL){
X		c = line[0];
X		if(inside){ /* unfolding */
X			if( c==' ' || c=='\t' ){
X				if( strlen(line) + strlen(from) >= LINESIZE ){
X					fprintf(stderr,"From line too long\n");
X					exit(1);
X				}
X				strcat(from, line);
X				continue;
X			}else{ /* not a continuation */
X				inside = 0;
X			}
X		}
X		if ( c == '\0' ) {
X			break;	/* end of mail header */
X		}else if(!replyto && strncmp("From ",line,5)==0){
X			strcpy(from, line + 5);
X			if(p = strchr(from,' ')) *p = '\0';  /* chop date */
X		}else if(strlen(line)>9 && cicmp("reply-to:",line)==0){
X			strcpy(from, line + 9);
X			inside = 1;
X			replyto = 1;
X		}else if(!replyto && strlen(line)>5 && cicmp("from:",line)==0){
X			strcpy(from, line + 5);
X			inside = 1;
X		}
X	}
X	if(getfrom(from, address))
X		exit(1);
X	puts(address);
X	exit(0);
X}
X#endif
-------cut here----- src/getfrom.c
echo src/hash.c 1>&2
sed >src/hash.c <<'-------cut here----- src/hash.c' 's/^X//'
X/* version with Brent reorganization.    Eric Grosse <ehg@research.bell-labs.com>  */
X/* see Gonnet and Baeza-Yates, Handbook of Algorithms and Data Structures, 1991, p.63 */
X
X/*
XLet {\tt char* s} be a key,
Xlet {\tt int c} be a value to be associated with {\tt s},
Xlet {\tt HashTable H=0} be a symbol table,
Xand let {\tt int i=hashsrch(\&H,s)}.
XIf {\tt i<0}, then {\tt s}$\not\in${\tt H}, and the pair $(s,c)$
Xmay be inserted by
X{\tt hashins(\&H,i,Strdup(s),c)}. 
XOtherwise, {\tt s}$\in${\tt H} and
Xthe associated value is {\tt H[i].Code}.
XThe pair may be removed by {\tt hashdel(\&H,i)}.
XUse {\tt free(H)} to release the space allocated for {\tt H}
Xitself;  this does not free the individual keys.
XThe implementation uses open hashing, allocating a larger table
Xand rehashing when too many collisions occur.
X*/
X
X#include <string.h>
X#include <stdio.h>
X#include "depend.h"
X
X#define LENGTH (H[0].Code)
X#define DEAD ((char*)H)
X/* H[0] just holds the length n.  The hash table proper is H[1],...,H[n]. */
X
Xint 
Xhashpjw(char* key, int n)
X{
X	unsigned int g, h = 0;
X	while((unsigned char)*key!=0){
X		h = (h<<4)+(unsigned char)*key++;
X		if(g = h&15<<28){
X			h = h^g>>28;
X			h = h^g;
X		}
X	}
X	return h%n;
X}
X
Xint 
Xhashasu(char* key, int n)
X{
X	unsigned int h = 0;
X	while((unsigned char)*key!=0)
X		h = 65599*h+(unsigned char)*key++;
X	return h%n;
X}
X
Xstatic int	
Xrehash(HashTable*Hp)
X{
X	int i, j;
X	char* key;
X	static int level;	/* don't rehash in the middle of a rehash */
X	int m;	/* m and m-2 must both be prime! */
X	HashTable N, H = *Hp;
X	int n;
X
X	if(!H){
X		n = 211;
X		*Hp = H = (struct HashEntry*)Malloc((n+1)*sizeof(*H));
X		LENGTH = n;
X		for(j=1;j<=n;j++)
X			H[j].Key = 0;
X		level = 0;
X		return(1);
X	}
X	level++==0 || Error("rehash: recursive rehash"); /* maybe return(0)? */
X	n = LENGTH;
X	if(n == 211) { m = 1019;
X	} else if(n == 1019) { m = 5099;
X	} else if(n == 5099) { m = 25033;
X	} else if(n == 25033) { m = 100153;
X	} else if(n == 100153) { m = 500809;
X	} else if(n == 500809) { m = 2503183;
X	} else { m = 5007181; Error("HashTable overflowed");
X	}
X	N = (struct HashEntry*)Malloc((m+1)*sizeof(*N));
X	N[0].Code = m;  /* LENGTH */
X	for(j=1;j<=m;j++)
X		N[j].Key = 0;
X	for(i = 1; i <= n; i += 1  ) {
X		if((key = H[i].Key)!=0 && key!=DEAD) {
X			j = hashsrch(&N, key);
X			j<0 || Error("%s already in N in rehash!?",key);
X			hashins(&N, j, key, H[i].Code);
X		}
X	}
X	free(H);
X	*Hp = N;
X	level -= 1;
X	return(1);
X}
X
Xint 
Xhashsrch(HashTable*Hp, char* key)
X{
X	int i, inc, last, m;
X	char *HiKey;
X	HashTable H = *Hp;
X
X	key || Error("hashsrch: null key");
X	H || rehash(Hp); H = *Hp;
X	m = LENGTH;
X	i = hashpjw(key,m)+1;
X	HiKey = H[i].Key;
X	if(HiKey==0)
X		return(-i);
X	if(HiKey!=DEAD && HiKey[0]==key[0] && strcmp(HiKey+1,key+1)==0)
X		return(i);
X	inc = hashasu(key,m-2)+1;
X	last = (i-1+(m-1)*inc)%m+1;
X	while(1){
X		i = (i-1+inc)%m+1;
X		if(i==last) return(-i);
X		HiKey = H[i].Key;
X		if(HiKey==0) return(-i);
X		if(HiKey==DEAD) continue;
X		if(HiKey[0]==key[0] && strcmp(HiKey+1,key+1)==0) return(i);
X	}
X}
X
Xint	
Xhashins(HashTable*Hp, int j, char* key, int code)
X{
X	HashTable H = *Hp;
X	int init, inc, i, ii, jj, m = LENGTH;
X	char *HiKey;
X
X	j<0 || Error("hashins: Insert should follow unsuccessful Search");
X	/* search anyway, in case we can fill a DEAD slot */
X
X	init = hashpjw(key,m)+1;
X	HiKey = H[init].Key;
X	if( HiKey==0 || HiKey==DEAD){
X		H[init].Key = key;
X		H[init].Code = code;
X		return(init);
X	}
X
X	inc = hashasu(key,m-2)+1;	/* n-2 so probe offset is not zero */
X	for(i = 1; i<m; i++){
X	    j = i;
X		jj = (init-1+inc*i)%m + 1;
X		if(H[jj].Key==0 || H[jj].Key==DEAD){
X			H[jj].Key = key;   H[jj].Code = code;
X			goto finish;
X		}
X	    for(j = i-1; j>=0; j--){
X		jj = (init-1+inc*j)%m + 1;
X		ii = (jj-1+(hashasu(H[jj].Key,m-2)+1)*(i-j))%m+1;
X		if(H[ii].Key==0 || H[ii].Key==DEAD){ /* move record forward */
X			H[ii].Key = H[jj].Key;   H[ii].Code = H[jj].Code;
X			H[jj].Key = key;   H[jj].Code = code;
X			goto finish;
X		}
X	    }
X	}
X  finish:
X	/* if there were a lot of probes, rehash to speed future searches */
X	if((j>=5||(i-j)>5)&&m<1020 || (j>9||(i-j)>9)&&m<2500000){
X		rehash(Hp) || Error("hashsrch: new algorithm needed!");   H = *Hp;
X		jj = hashsrch(&H,key);
X		jj>0 || Error("hashins:can't");
X	}
X	return(jj);
X}
X
Xvoid	
Xhashdel(HashTable*Hp, int j)
X{
X	HashTable H = *Hp;
X	j>0 || Error("hashdel must follow successful Search");
X	H[j].Key = DEAD;
X}
X
X
-------cut here----- src/hash.c
echo src/index_chk.c 1>&2
sed >src/index_chk.c <<'-------cut here----- src/index_chk.c' 's/^X//'
X/*  Check conformance of index to /netlib/bib/thesaurus.
X
Xarg[1]: index filename, relative to netlib root
Xstderr: warnings
Xstdout: possibly improved index file
X
XIt is assumed that the index already passes plauger_chk.
X
X* Each paragraph starts with file or lib.
X* Filenames should have path relative to /netlib.
X* Only one file or lib per paragraph.
X* Each listed lib or file should exist and be readable.
X* If file is bigger than 200kB, list size in index.
X* Each line is keyword-tab-value.
X* Filenames should be lowercase and not too long.
X* Warn about files not in index.
X* Files should not be in multiple entries.
X
XEric Grosse <ehg@research.bell-labs.com>
X26 Mar 1994 first cut
X 4 Apr 1994 allow .Z;  minor fixes
X 5 Apr 1994 don't index "dependencies".   "global comments deprecated".
X	People used inconsistent mess of blanks and tabs, so just
X	rewrite all white space between key and val as single tab.
X	Remove empty comment lines.   Remove trailing space.
X12 Jun 1994 use , instead of <tab> for continuation.  include "readme"
X*/
Xchar *exclude[] = {
X"index","index.html","index.FIX",".depend","disclaimer",
X"permission","Makefile","dependencies",".master",
X".cache",".names",".cap",".cache+",0};
X
X#define _POSIX_SOURCE
X#include <stdlib.h>
X#include <unistd.h>
X#include <stdio.h>
X#include <assert.h>
X#include <string.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include "depend.h"
Xextern int Error(char*,...);
X
Xtypedef struct{
X	int inside; /* inside a paragraph, i.e. have seen "file" line */
X	unsigned long size;  /* file size, if large enough to matter */
X} State;
X
X/* HashTable Code */
X#define File 1
X#define Lib 2
X
Xint NR = 0;	/* input line number, for error messages */
X
Xint /* 1 if chopping succeeds */
Xchop(char *s, char *suffix)
X{
X	int m, n;
X	if(!s || !suffix ) return(0);
X	n = strlen(s); m = strlen(suffix);
X	if(strcmp(s+n-m,suffix)!=0) return(0);
X	s[n-m] = 0;
X	return(1);
X}
X
XHashTable
Xactual(void)
X{
X	struct dirent *d;
X	struct stat s;
X	DIR *dir = opendir(".");
X	char *name;
X	HashTable H = 0;
X	int i;
X
X	dir!=NULL || Error("can't open %s");
X	while((d=readdir(dir))!=NULL){
X		name = d->d_name;   assert(name&&name[0]);
X		if(strcmp(name,".")==0 || strcmp(name,"..")==0) continue;
X		stat(name,&s)==0 || Error("can't stat %s",name);
X		i = hashsrch(&H,name);  assert(i<0);
X		if(S_ISREG(s.st_mode)){
X			hashins(&H,i,Strdup(name),File);
X		}else if(S_ISDIR(s.st_mode)){
X			hashins(&H,i,Strdup(name),Lib);
X		} /* symbolic links are deprecated (and ignored). */
X	}
X	closedir(dir);
X	return(H);
X}
X
Xprintsize(State *state)
X{
X	if(state->size){
X		if(state->size<1000*1000)
X			printf("size\t%d kB\n",state->size/1000);
X		else
X			printf("size\t%.1f MB\n",state->size/(1000*1000.));
X		state->size = 0;
X	}
X}
X
Xvoid
Xrules(char *key, char *val, State *state, HashTable *pH, char *path)
X{
X	char *local, *l;  /* local filename (with directory prefix chopped) */
X	int file_or_lib = 0;
X	int i;
X	struct stat s;
X	static int globalcomm = 0;
X
X	if(!*key && !*val){ /* empty input line */
X		if(state->inside){
X			printsize(state);
X			printf("\n");
X			state->inside = 0;
X		}
X		return;	/* collapse multiple blank lines */
X	}
X
X	if(strcmp(key,"file")==0) file_or_lib = File;
X	else if(strcmp(key,"lib")==0) file_or_lib = Lib;
X
X	/* Each paragraph starts with file or lib. */
X	if(!state->inside && !file_or_lib && *key!='#'){
X		fprintf(stderr,"expected file or lib at line %d\n",NR);
X		printf("file	%s/???\n",path);
X		state->inside = 1;
X	}
X
X	if(file_or_lib){
X		int pathlen = strlen(path);
X		local = val+pathlen+1;  /* chop "path/" prefix */
X
X		/* Only one file or lib per paragraph. */
X		if(state->inside){
X			fprintf(stderr,"only one file per paragraph!  %d",NR);
X			printf("\n");  /* start new paragraph */
X		}
X
X		/* Filenames should have path relative to /netlib. */
X		if(strncmp(val,path,pathlen)!=0 || val[pathlen]!='/'){
X			fprintf(stderr,"use full path at line %d\n",NR);
X			!strchr(val,'/') || Error("lost in / at line %d",NR);
X			/* rewrite "lcl" as "path/lcl" */
X			memmove(local,val,strlen(val)+1);
X			strcpy(val,path);
X			val[pathlen] = '/';
X		}
X
X		/* Files should not be in multiple entries. */
X		if((i = hashsrch(pH,local))>0)
X			fprintf(stderr,"duplicate %s at %d\n",local,NR);
X		else
X			hashins(pH,i,Strdup(local),file_or_lib);
X
X		/* Each listed lib or file should exist and be readable. */
X		if(stat(local,&s)!=0){
X			fprintf(stderr,"%s at %d does not exist\n",local,NR);
X		}else{
X			if(S_ISREG(s.st_mode)){
X				if(file_or_lib!=File){
X					fprintf(stderr,"lib should be file at %d",NR);
X					if(val<key+5)
X						memmove(key+5,val,strlen(val)+1);
X					strcpy(key,"file");
X				}
X			}else if(S_ISDIR(s.st_mode)){
X				if(file_or_lib!=Lib){
X					fprintf(stderr,"file should be lib at %d",NR);
X					strcpy(key,"lib");
X				}
X			}
X			if(!(S_IROTH&s.st_mode))
X				fprintf(stderr,"%s unreadable?\n",local);
X
X			/* If file is bigger than 200kB, list size in index. */
X			if(s.st_size>200*1000)
X				state->size = s.st_size;
X		} /* stat */
X
X		/* Filenames should be lowercase and not too long. */
X		if(strlen(local)>23)
X			fprintf(stderr,"Shorten %s!\n",local);
X		for(l = local; *l; l++){
X			if(l[0]=='Z' && l[1]==0) break; /* .Z suffix is ok */
X			if(!islower(*l) && !isdigit(*l) &&
X					!strchr("-_+.,~?",*l)){
X				fprintf(stderr,"Use lowercase, simple name for %s!\n",local);
X				break;
X			}
X		}
X		state->inside = 1;
X	} /* file_or_lib */
X
X	/* Continuation lines have , as keyword. */
X	if(key[0]==0){
X		key = ",";
X	}else if(strcmp(key,"#")==0){
X		if(!*val)
X			return;	/* no empty comments */
X		if(state->inside)
X			; /* *key = 0 is heuristic for round of corrections */
X		else if(!globalcomm++)
X			fprintf(stderr,"global comments are deprecated\n");
X	}
X
X	if(strcmp(key,"size")==0)
X		printsize(state);
X	else /* Each line is keyword-tab-value. */
X		printf("%s\t%s\n",key,val);
X}
X
Xvoid
Xmissing(HashTable *pdoc, char *path)
X{
X	struct dirent *d;
X	struct stat s;
X	DIR *dir = opendir(".");
X	char *name, **excl;
X	int i;
X
X	for(excl = exclude; *excl; excl++){
X		i = hashsrch(pdoc,*excl);
X		if(i>=0) fprintf(stderr,"don't index %s\n",*excl);
X		 else hashins(pdoc,i,*excl,File);
X	}
X	dir!=NULL || Error("can't open %s");
X	while((d=readdir(dir))!=NULL){
X		name = d->d_name;   assert(name&&name[0]);
X		if(strcmp(name,".")==0 || strcmp(name,"..")==0) continue;
X		if(hashsrch(pdoc,name)<0){
X			stat(name,&s)==0 || Error("can't stat %s",name);
X			if(S_ISREG(s.st_mode)){
X				printf("file\t%s/%s\n\n",path,name);
X			}else if(S_ISDIR(s.st_mode)){
X				printf("lib\t%s/%s\n\n",path,name);
X			}
X		}
X	}
X	closedir(dir);
X}
X
Xvoid
Xmain(int argc, char**argv)
X{
X        char line[2048], *val, *path;
X	HashTable doc = 0, act = 0;
X	FILE *I; /* index file */
X	State state;
X
X        /* figure out where we should work and go there */
X	argc==2 || Error("%s a/index > index.FIX 2> err",argv[0]);
X	path = Strdup(argv[1]);
X	chop(path,"/index") || Error("arg1 should end in /index");
X	chdir(path)==0 || Error("can't cd %s",path);
X
X	/* what's the truth? */
X	act = actual();
X
X	/* what's documented? */
X	I = fopen("index","r");	I!=NULL || Error("can't read index");
X	state.inside = 0;  state.size = 0;
X	while(Fgets(line,sizeof(line)-100,I)!=NULL){
X		NR++;
X		val = line;
X		while(*val && *val!=' ' && *val!='\t') val++; /* past keyword */
X		if(*val){
X			*val++ = 0;  /* terminate keyword */
X			while(*val==' ' || *val=='\t') val++;
X		}
X		if(*val){ /* strip trailing space */
X			char *endval = &val[strlen(val)-1];
X			while(*endval==' '||*endval=='\t')
X				*endval-- = 0;
X		}
X		rules(line,val,&state,&doc,path);
X	}
X	rules("","",&state,&doc,path); /* for final printsize(), etc. */
X
X	/* Warn about files not in index, other than: index, etc.*/
X	missing(&doc,path);
X
X	exit(0);
X}
-------cut here----- src/index_chk.c
echo src/lsr.c 1>&2
sed >src/lsr.c <<'-------cut here----- src/lsr.c' 's/^X//'
X/* see copyright notice in /netlib/crc/lsr.c */
X
X#define _POSIX_SOURCE
X#include <stdlib.h>
X#include <unistd.h>
X#include <string.h>
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
Xextern char *Strdup(char*);
Xextern void *Malloc(int);
Xextern void *Realloc(void*,int);
Xtypedef unsigned char uchar;
X
X#ifdef NOSYMLINK
X#define lstat(f,s) stat(f,s)
X#endif
X
Xstatic uchar *path, **component, *dest;
Xstatic int ncomp;
X
Xstatic int
Xcmp(const void *a, const void *b)
X{
X	return strcmp(*(char**)a,*(char**)b);
X}
X
Xstatic char **
Xsorted_names(DIR *dir)
X{
X	int maxnames = 250;	/* starting guess at size of directory */
X	int nnames = 0;
X	char **names = (char **)Malloc(maxnames*sizeof(*names));
X	char *file, *s;
X	struct dirent *entry;
X
X	while( entry = readdir(dir) ){
X		file = entry->d_name;
X		if(strcmp(file,".")!=0 && strcmp(file,"..")!=0 ){
X			names[nnames++] = Strdup(file);
X			if(nnames>=maxnames){
X				maxnames += 200;
X				names = (char **)Realloc(names,maxnames*sizeof(char*));
X			}
X		}
X		for(s = file; *s; s++)
X			if(!isgraph(*s)){
X				strcpy((char*)component[ncomp-1],file);
X				break;
X			}
X	}
X	qsort((void*)names,nnames,sizeof(*names),cmp);
X	names[nnames] = 0;
X	return(names);
X}
X
Xstatic void
Xfree_names(char **names)
X{
X	char **name;
X	for(name = names; *name; name++)
X		free(*name);
X	free(names);
X}
X
Xstatic void
Xpr(void)
X{
X	struct stat s;
X	DIR *dir;
X	char buf[BUFSIZ];
X	int cc;
X	char **names, **name;
X
X	if(lstat((char*)path,&s)){
X		return;
X	}
X#ifndef NOSYMLINK
X	if(S_ISLNK(s.st_mode)){
X	}else
X#endif
X	     if(S_ISREG(s.st_mode)){
X		uchar *p = path;
X		if(p[0]=='.' && p[1]=='/')
X			p += 2;	/* chop off leading ./ */
X		strcpy(dest,p);   dest += strlen(dest);
X		*dest = ' ';   dest += 1;
X	}else if(S_ISDIR(s.st_mode)){
X		if(dir = opendir((char*)path)){
X			cc = strlen((char*)component[ncomp-1]);
X			component[ncomp] = cc+component[ncomp-1]+1;
X			component[ncomp][-1] = '/';
X			ncomp++;
X			names = sorted_names(dir);
X			for(name = names; *name; name++){
X				strcpy((char*)component[ncomp-1],*name);
X				pr();
X			}
X			free_names(names);
X			ncomp--;
X			component[ncomp][-1] = '\0';
X			closedir(dir);
X		}
X	}
X}
X
Xlsr(char *f, char *p)
X{
X	dest = f;
X	component = (uchar**)Malloc(1000*sizeof(uchar*));
X		/* should check against depth */
X	path = (uchar *)Malloc(4096);
X		/* should be _POSIX_PATH_MAX */
X	component[0] = path;
X	ncomp = 1;
X	strcpy(path,p);
X	pr();
X	free(path);
X	free(component);
X}
X
X#ifdef STANDALONE
Xmain(int argc, char**argv)
X{
X	char f[10000];
X	lsr(f,argc>1?argv[1]:".");
X	printf("%s\n",f);
X	exit(0);
X}
X#endif
-------cut here----- src/lsr.c
echo src/mimemail.c 1>&2
sed >src/mimemail.c <<'-------cut here----- src/mimemail.c' 's/^X//'
X/* netlib mimemail - bundle binary files as MIME attachments
X *
X * Eric Grosse <ehg@research.bell-labs.com>
X * Copyright (c) 1999 by Lucent Technologies.
X * Permission to use, copy, modify, and distribute this software for any
X * purpose without fee is hereby granted, provided that this entire notice
X * is included in all copies of any software which is or includes a copy
X * or modification of this software and in all copies of the supporting
X * documentation for such software.
X * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
X * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
X * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
X * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
X *
X * The purpose of this function (or program, if compiled -DSTANDALONE)
X * is to mail to argv[1] with subject argv[2] a list of files in argv[3]
X * up to argv[n].  This is a compatible substitute for netlib bigmail,
X * to be used when the files are binary and the recipient can handle MIME.
X */
X#define _POSIX_SOURCE
X#define _POSIX2_SOURCE
X#include <stdio.h>
X#include <string.h>
Xtypedef unsigned long ulong;
Xtypedef unsigned char uchar;
Xextern FILE    *popen(const char *, const char *);
X
Xstatic char cutstring[] = "--------12X12E144";
X
X/* rfc1521 base64 (3 octets = 4 printables) */
Xstatic char t64e[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
X
Xstatic int
Xenc64(char *out, uchar *in, int n)
X{
X	int i;
X	ulong b24;
X	char *start = out;
X
X	for(i = n/3; i > 0; i--){
X		b24 = (*in++)<<16;
X		b24 |= (*in++)<<8;
X		b24 |= *in++;
X		*out++ = t64e[(b24>>18)];
X		*out++ = t64e[(b24>>12)&0x3f];
X		*out++ = t64e[(b24>>6)&0x3f];
X		*out++ = t64e[(b24)&0x3f];
X	}
X
X	switch(n%3){
X	case 2:
X		b24 = (*in++)<<16;
X		b24 |= (*in)<<8;
X		*out++ = t64e[(b24>>18)];
X		*out++ = t64e[(b24>>12)&0x3f];
X		*out++ = t64e[(b24>>6)&0x3f];
X		break;
X	case 1:
X		b24 = (*in)<<16;
X		*out++ = t64e[(b24>>18)];
X		*out++ = t64e[(b24>>12)&0x3f];
X		*out++ = '=';
X		break;
X	}
X	*out++ = '=';
X	*out = 0;
X	return out - start;
X}
X
X
X/* begin mail message */
Xstatic void
Xbeginmess(char* address, char* subject, FILE** outp)
X{
X	char cmd[512];
X
X	if(address){
X		if(strlen(address)>=500)
X			quit(strlen(address)+100,"address too long");
X		sprintf(cmd,"/bin/mail %s",address);
X		if( (*outp = popen(cmd,"w")) == NULL){	/* POSIX.2 */
X			fprintf(stderr,"%s:\n",cmd);
X			quit(1,"can't popen");
X		}
X	}else{
X		*outp = stdout;
X	}
X	if(!subject)
X		subject = "Subject: netlib mimemail";
X	fprintf(*outp,"MIME-Version: 1.0\n");
X	fprintf(*outp,"%s\n",subject);
X	fprintf(*outp,"Content-Type: multipart/mixed;\n");
X	fprintf(*outp," boundary=\"%s\"\n\n",cutstring);
X	fprintf(*outp,"This is a multi-part message in MIME format.\n");
X	fprintf(*outp,"--%s\n",cutstring);
X	fprintf(*outp,"Content-Type: text/plain; charset=us-ascii\n");
X	fprintf(*outp,"Anything free comes with no guarantee!\n");
X}
X
X/* end mail message */
Xstatic void
Xendmess(FILE** outp)
X{
X	int rc;
X
X	fprintf(*outp,"--%s--\n",cutstring);
X	fflush(*outp);
X	if(*outp==stdout){
X		fputs("#----------------------------------------\n",*outp);
X	}else{
X		if((rc=pclose(*outp)) != 0)	/* POSIX.2 */
X			fprintf(stderr,"bad pclose %d\n",rc);
X		*outp = (FILE*)0;
X	}
X}
X
X/* begin file (possibly several per message) */
Xstatic void
Xbeginfile(FILE* out, char* filename)
X{
X	fprintf(out,"--%s\n",cutstring);
X	fprintf(out,"Content-Type: application/octet-stream;\n");
X	fprintf(out," name=\"%s\"\n",filename);
X	fprintf(out,"Content-Transfer-Encoding: base64\n");
X	fprintf(out,"Content-Disposition: attachment;\n");
X	fprintf(out," filename=\"%s\"\n\n",filename);
X}
X
Xstatic void
Xendfile(FILE* out)
X{
X}
X
Xstatic void
Xcatfile(char* filename, FILE *in, FILE *out)
X{
X	/* write 72 base64 chars per line, from 54 original octets */
X	char line[80], buf[54], *bp, *be;
X	int n, linesize;
X	while( (n=fread(buf,1,sizeof(buf),in)) > 0){
X		linesize = enc64(line,buf,n);
X		if(n==sizeof(buf))
X			linesize--;  /* chop = */
X		line[linesize++] = '\n';
X		if(fwrite(line,1,linesize,out)==EOF){
X			quit(1,"write error");
X		}
X	}
X}
X
X/*
X * It is assumed that the caller has already verified that address
X * is safe to give to the shell, i.e. contains no backquotes, spaces, etc.
X *	address = address to send to, or stdout if 0
X *	subject = subject line
X *	file = filenames
X *	nfile = number of filenames
X */
Xvoid
Xmimemail(char *address, char *subject, char **file, int nfile)
X{
X	FILE *in;	/* this file */
X	FILE *out;	/* pipe to mail process for this message */
X
X	beginmess(address,subject,&out);
X	while(nfile>0){
X		if((in = fopen(*file,"r"))==NULL){
X			fprintf(stderr,"%s:\n",*file);
X			quit(1,"can't open");
X		}
X		beginfile(out,*file);
X		catfile(*file,in,out);
X		fclose(in);
X		endfile(out);
X		file++;
X		nfile--;
X	}
X	endmess(&out);
X}
X
X
X#ifdef STANDALONE
X#include <stdlib.h>
Xquit(int rc, char *mess)
X{
X	fprintf(stderr," %s\n", mess);
X	exit(rc);
X}
X
Xmain(int argc, char* argv[])
X{
X	char *s;
X	if(argc<4)
X		quit(1,"mimemail address subject files...");
X	mimemail(argv[1],argv[2],argv+3,argc-3);
X	exit(0);
X}
X#endif
X
-------cut here----- src/mimemail.c
chmod +x src/mimemail.c
echo src/mk.list.c 1>&2
sed >src/mk.list.c <<'-------cut here----- src/mk.list.c' 's/^X//'
X/* setuid program, so netlibd can create subscriber list */
X#define _POSIX_SOURCE
X#include <stdlib.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <unistd.h>
X#include <strings.h>
X#include <sys/param.h>
X
Xgid_t groups[] = {0,2,3,4,20,49,50,482,995,997,998};
X
Xvoid
Xmain(int argc, char**argv)
X{
X	char path[300], *dir;
X	setuid(100);
X	setgroups(sizeof(groups)/sizeof(groups[0]),groups);
X	if(argc!=2)
X		exit(1);
X	if(strlen(argv[1])>250)
X		exit(2);
X	sprintf(path,"/netlib/%s/.list",argv[1]);
X	for( dir = path; dir = strchr(dir,'/'); dir++)
X		if(strncmp(dir,"/../",4)==0)
X			exit(3);
X	umask(0);
X	if(access(path,F_OK)!=0)
X		creat(path,0666);
X	else
X		chmod(path,0666);
X	exit(0);
X}
-------cut here----- src/mk.list.c
echo src/plauger_chk.c 1>&2
sed >src/plauger_chk.c <<'-------cut here----- src/plauger_chk.c' 's/^X//'
X/* check file for adherence to Plauger's rules of portable i/o */
X/* see: C Users Journal 7:6 (Aug89) 17-25 */
X/* Eric Grosse <ehg@research.bell-labs.com> Sep 1989, tweaked Mar 1994 */
X
X#include <stdlib.h>
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X
Xvoid
Xmain(int argc, char**argv)
X{
X	char line[512], *s;
X	unsigned char c;
X	int i, n, NR = 0, nonpr, cnt[256];
X	int dot = 0, err = 0, crlf = 0;
X
X	if(argc!=1){
X		fprintf(stderr,"%s < file > errors\n",argv[0]);
X		exit(++err);
X	}
X	for(i = 0; i<256; i++)
X		cnt[i] = 0;
X	while(fgets(line,sizeof(line),stdin)!=NULL){
X		NR++;
X		n = strlen(line);
X		if(n>510){
X			printf("%d long",NR);	err++;
X		}else{
X			n--;
X			if(line[n]!='\n'){
X				printf("%d no newline\n",NR);	err++;
X			}
X			line[n] = '\0';  /* strip newline */
X		}
X		if(!crlf && line[n-1]=='\r'){
X			crlf = 1;
X			printf("%d CR LF\n",NR);	err++;
X		}else if(isspace(line[n-1])){
X			printf("%d trailing space\n",NR);	err++;
X		}
X		if(dot==0 && n==1 && line[0]=='.'){
X			printf("%d line . is dangerous for mail\n",NR);
X			dot = 1;
X		}
X		nonpr = 0;
X		for(s = line; *s; s++){
X			c = *s;
X			if(!isprint(c) && c!='\t'){
X				cnt[c]++;
X				if(!nonpr){
X					nonpr = 1;
X					printf("%d nonprinting\n",NR);	err++;
X				}
X			}
X		}
X	}
X	if(NR==0){
X		printf("empty files may be discarded\n");	err++;
X	}
X	for(i=0; i<256; i++)
X		if(cnt[i]!=0){
X			printf("%d occurrences of '\\0%o'\n",cnt[i],i);	err++;
X		}
X	exit(err);
X}
-------cut here----- src/plauger_chk.c
echo src/reply.c 1>&2
sed >src/reply.c <<'-------cut here----- src/reply.c' 's/^X//'
X/*
X * The author of this software is Eric Grosse. Copyright (c)1985,1994 by AT&T.
X * Permission to use, copy, modify, and distribute this software for any
X * purpose without fee is hereby granted, provided that this entire notice
X * is included in all copies of any software which is or includes a copy
X * or modification of this software and in all copies of the supporting
X * documentation for such software.
X * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
X * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
X * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
X * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
X */
X
X/*  The following program reads an electronic mail message, searches
X    its database, and tries to reply.
X
X    To explore the program, set DEBUG=on in the shell environment.
X    Instead of executing shell commands they will be sent to admin/stderr.
X
X    The home directory, /netlib, contains various source directories,
X    such as /netlib/eispack.  The netlib administrative directory
X    /netlib/admin contains indices, permission tables, and logfiles.
X
X    For a revision log, see /netlib/admin/src/changes.
X    Send bug reports to ehg@research.bell-labs.com.
X
X	This code has grown in steps over time and is in serious
X	need of a complete rewrite.
X*/
X
X#define _POSIX_SOURCE
X#include <stdlib.h>
X#include <stdio.h>
X#include <assert.h>
X#include <ctype.h>
X#include <unistd.h>
X#include <string.h>
X#include <time.h>   /* on some systems, this is <sys/times.h> */
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>   /* POSIX;  used only in allfiles() */
X#ifndef S_ISDIR
X#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
X#endif
X#include "depend.h"
X
Xextern FILE    *popen(const char *, const char *);
X
XFILE	*Fopen();
Xchar	*get_date(void);
Xint	unsafe(char*);
Xint	getfrom(char*,char*);
Xextern int subscribe(char*,char*);
Xextern int unsubscribe(char*,char*);
Xextern void lsr(char*,char*);
X
X#define LINESIZE 10000
X#define FIELDMAX 400
Xchar	FS0[] = " \t\";,";  /* work around apparent bug in gcc */
Xchar    *FS = FS0, *field[FIELDMAX];
Xint	NR, NF;
Xextern int Error_rc, errno;
X
Xint	DEBUG;
Xint	in_header = 1;
Xint	group;	/* 0=general, 1=local, 10=enemy */
Xint	depend;	/* send all routines request depends on? */
Xint	upas;	/* does this system run Presotto's mailer? (used in parse) */
Xchar	reply[20];	/* temp file for constructing reply */
Xint	siam;	/* =1 if searching the SIAM membership list */
Xint	dir800;	/* =1 if searching the 800 directory */
Xchar	usermail[LINESIZE]; /* for use in "sorry" */
Xlong	maxbyte; 	/* max number of bytes per mail message */
Xint	mime = 0;	/* should reply be sent as one MIME multipart message? */
Xint	SEND,LIST,INDEX,FIND,EXEC,SUBSCRIBE,UNSUBSCRIBE,MIME; /* command codes */
X#define SKIP 0
X#define DONE 999
X
Xchar  *suffix[] =     /* for filename expansion in only() */
X	{"", ".r", ".f", ".c", ".C", ".h", ".H",
X		 ".tex", ".bib", ".shar", ".ps", ".html", 0 };
X
Xvoid
Xrmtemp(void)
X{
X	unlink(reply);
X}
X
Xmain()
X{
X	char	address[LINESIZE]; /* return address */
X	int	cmd;  /* code for type of request in current input line */
X	int	k;  /* field index */
X	int	item[2], excl[2], lib[2], thru[2]; /* first and last fields */
X				/* for routines, exclusions, libraries */
X	char	zlib[500];   /* strcat of field[lib[0]] ... field[lib[1]] */
X	char	xlib[1000];  /* zlib after expansion through LIBS file */
X	char	zitem[500], zexcl[500];
X	char	files[10*LINESIZE];  /* filenames to be sent as reply */
X	int	nreq = 0;   /* number of recognized requests */
X	int	kbreq = 0;  /* number of kilobytes sent so far */
X	char	buf[LINESIZE];
X	long	size, fsiz();
X	FILE	*F;
X	char	*date;
X	char	*debugs;
X	char	*sp;
X	char	subject[261];
X	char	execlog[50];
X
X	if(debugs=getenv("DEBUG"))
X		DEBUG = strcmp(debugs,"on")==0;
X	upas = (access("/v/lib/upas", 0) == 0);
X	if(upas && group>2)
X		chdir("/netlib2/pub");
X	else
X		chdir("/netlib");	/* MODIFY AS DESCRIBED IN README. */
X	if(freopen("admin/stderr","a",stderr)==NULL) exit(0);
X	date = get_date();
X	strcpy(reply, "/tmp/netlibXXXXXX");
X	mktemp(reply);
X	atexit(rmtemp);
X	Error_rc = 0;  /*Some mailers demand 0 return from alias script. */
X	errno = 0;
X	address[0] = '\0';
X
X	while (awk() != EOF){ /* read next line of message */
X		cmd=parse(address,item,excl,lib,thru);
X		if(cmd==DONE)
X			break;
X		if( (address[0] == '\0') ||
X		    (cmd == SKIP) ||
X		    duplicate(cmd, item, excl, lib) )
X			continue;
X
X		/* translate library names */
X		if(cmd != EXEC){
X			if(!lib[0]){
X				k = item[0];
X				if(k==item[1] && cmd!=FIND && isdir(field[k])){
X					/* "send x" => "send index from x" */
X					/* BUG: doesn't work for "send x/y" */
X					field[item[0] = item[1] = 1] = "index";
X					lib[0] = lib[1] = k;
X				} else
X					field[lib[0] = lib[1] = 1] = "DEFAULT";
X			}
X			gather(lib, zlib, sizeof(zlib));
X			if(cmd==SEND || cmd==LIST ||
X				cmd==SUBSCRIBE || cmd==UNSUBSCRIBE){
X				expand(lib, xlib);
X			}else{ /* FIND */
X				strcpy(xlib, zlib);
X				if((group == 1) && (strcmp(xlib, "DEFAULT") == 0))
X					strcat(xlib, " DEFAULT.lcl");
X			}
X			gather(item, zitem, sizeof(zitem));
X			gather(excl, zexcl, sizeof(zexcl));
X		}
X		if(DEBUG){
X			fprintf(stderr,"address=%s ", address);
X			fprintf(stderr,"group=%d ",group);
X			fprintf(stderr,"cmd=%d\n", cmd);
X			fprintf(stderr,"zitem=%s ", zitem);
X			fprintf(stderr,"zexcl=%s\n", zexcl);
X			fprintf(stderr,"xlib=%s\n", xlib);
X			fprintf(stderr,"item=%d,%d ", item[0],item[1]);
X			fprintf(stderr,"excl=%d,%d ", excl[0],excl[1]);
X			fprintf(stderr,"lib=%d,%d\n", lib[0],lib[1]);
X		}
X		sprintf(subject,"To: %.180s\nSubject: Re: %.60s",address,field[0]);
X		F = Fopen(reply, "w");
X		fputs(subject,F);
X		fputs("\n\n",F);
X		fclose(F);
X
X		/* execute command */
X		if( group == 10 ){
X			quit(666,"enemy");  /* fogel@oak.GEOG.UCSB.EDU loop */
X			sprintf(buf, "cat admin/mess/sorry_enemy >>%s", reply);
X			shell(buf);
X		}else if( cmd == EXEC ){
X			/* I considered making an easily edited file of
X			  commands, like LIBS or /usr/lib/uucp/Permissions.
X			  But then I decided that naive administrators would
X			  unknowingly open security holes. Be careful! */
X			if( strcmp(field[item[0]],"f2c")==0 ){
X				strcpy(execlog,"F2C");
X				while(in_header && awk()!=EOF ){
X					cmd=parse(address,item,excl,lib);
X				}
X				strcpy(files, "/tmp/f2cXXXXXX");
X				mktemp(files);	/* removed by F2C */
X				F = Fopen(files,"w");
X				while(fgets(buf,sizeof(buf),stdin)!=NULL)
X					fputs(buf,F);
X				fclose(F);
X				sprintf(buf, "admin/bin/F2C %s %s >> %s",
X					address, files, reply);
X				shell(buf);
X			}else if( strcmp(field[item[0]],"pgpsigned")==0 ){
X				strcpy(execlog,"pgpsigned");
X				while(in_header && awk()!=EOF ){
X					cmd=parse(address,item,excl,lib);
X				}
X				strcpy(files, "/tmp/pgpXXXXXX");
X				mktemp(files);	/* removed by pgpsigned */
X				F = Fopen(files,"w");
X				while(fgets(buf,sizeof(buf),stdin)!=NULL)
X					fputs(buf,F);
X				fclose(F);
X				sprintf(buf, "admin/bin/pgpsigned %s %s >> %s",
X					address, files, reply);
X				shell(buf);
X			}else if( strcmp(field[item[0]],"auth")==0 ){
X				strcpy(execlog,"auth inf");
X				sprintf(buf, "admin/bin/auth inf %s >> %s",
X					address, reply);
X				shell(buf);
X			}else{
X				strcpy(execlog,"sorry_cmd");
X				sprintf(buf, "cat admin/mess/sorry_cmd >>%s", reply);
X				shell(buf);
X			}
X		}else if(xlib[0] == '\0'){
X			sprintf(buf, "cat admin/mess/sorry_lib >>%s", reply);
X			shell(buf);
X		}else if(unsafe(xlib) || unsafe(zitem) || strchr(zitem,'/') || unsafe(zexcl) ){
X			sprintf(buf, "cat admin/mess/sorry_unsafe >>%s", reply);
X			shell(buf);
X		}else if( cmd == FIND ){
X			if(strcmp(xlib,"800")==0) dir800 = 1;
X			if(siam){
X				sprintf(buf, "cat admin/mess/siam >>%s", reply);
X				shell(buf);
X			}else if(dir800){
X				sprintf(buf, "cat admin/mess/dir800 >>%s", reply);
X				shell(buf);
X			}
X			sprintf(buf, "admin/bin/mgrep '%s' %s >>%s", xlib, zitem, reply);
X			shell(buf);
X		}else if( cmd==SUBSCRIBE || cmd==UNSUBSCRIBE ){
X#ifdef FORWARD
X			F = Fopen(reply,"a");
X			fprintf(F,"path %s\n",address);
X			fprintf(F,"%s %s\n",(cmd==SUBSCRIBE)?"subscribe":"unsubscribe",xlib);
X			fclose(F);
X			strcpy(address,FORWARD);
X#else
X			switch((cmd==SUBSCRIBE)?subscribe(address,xlib):
X						unsubscribe(address,xlib)){
X			   case 0:
X				sprintf(buf,"echo Done. >>%s",reply);
X				shell(buf);
X				break;
X			   case 1:
X				sprintf(buf,"cat admin/mess/no-topic >>%s",reply);
X				shell(buf);
X				break;
X			   case 2:
X				sprintf(buf,"cat admin/mess/collided >>%s",reply);
X				shell(buf);
X				break;
X			   case 3:
X				fprintf(stderr,"[%s]%s: \n",address,xlib);
X				fflush(stderr);
X				perror("SUBSCRIBE error");
X				break;
X			}
X#endif
X		}else if( cmd==SEND || cmd==LIST ){
X			if(depend){
X				sprintf(buf,"admin/bin/ldMx '%s' '%s' '%s'",xlib,zitem,zexcl);
X				if(DEBUG) fprintf(stderr,"[%s]\n",buf);
X				F = popen(buf,"r");
X				if(fgets(files,LINESIZE,F)==NULL)
X					files[0] = '\0';
X				pclose(F);
X				nlchop(files);
X				if(!*files){  /* failed; try only() */
X					depend = 0;
X				}
X			}
X			if(!depend)
X				only(xlib,item,files);
X			nlchop(files);
X			if(strncmp("admin/mess/sorry",files,16) != 0){
X				sprintf(buf,"admin/bin/disclaim %s >>%s",xlib,reply);
X				shell(buf);
X			}
X			sprintf(buf,"cat %s >>%s",files,reply);
X			shell(buf);
X			if( cmd == LIST ){
X				size = fsiz(reply);
X				F = Fopen(reply,"w");
X				fprintf(F,"Subject: Re: %s\n\n",field[0]);
X				fprintf(F,"The following files total %ld bytes\n",size);
X				if(size > 345600.)
X					fprintf(F," = %.1f hours per link at 9600bps.\n",size / 3456000.);
X				for( sp=files; sp=strchr(sp,' '); sp++)
X					*sp = '\n';   /* for easier reading */
X				fprintf(F,"\n%s\n",files);
X				fclose(F);
X			}
X		}
X
X		/* is user being too greedy? */
X		size = fsiz(reply);
X		kbreq += size / 1000;
X		if( (siam||dir800) && size > 5000 || cmd==FIND && size > 50000 ){
X			sprintf(buf,
X			    "sed 200q %s | cat admin/mess/sorry_find - | /bin/mail %s",
X			    reply, address);
X			shell(buf);
X			sprintf(buf,"big find: %.40s",field[0]);
X			quit(5, buf);
X		}else if( ((nreq++ > 50) || (kbreq > 2000)) && (group == 0) ){
X			sprintf(buf, "cat admin/mess/sorry_many | /bin/mail %s", address);
X			shell(buf);
X			quit(5, "too many requests");
X		}
X
X		/* ship it out */
X		if( cmd == SEND && xlib[0] != '\0' ){
X			/* new scheme */
X			send(address,maxbyte,subject,files,xlib);
X		}else{
X			sprintf(buf, "<%s /bin/mail %s", reply, address);
X			shell(buf);
X		}
X
X		/* append summary to log */
X		if((F = fopen("admin/log", "a")) != NULL){
X			if( group == 10 ){
X				fprintf(F, "%s [%s] %ld ENEMY\n", date, address,
X				    size);
X				exit(0);
X			}else if(cmd == EXEC){
X				fprintf(F, "%s [%s]%s %ld %s\n", date, address,
X				    group==1 ? "l" : "", size, execlog);
X			}else if(cmd==SUBSCRIBE){
X				fprintf(F, "%s [%s]%s %ld %s SUBSCRIBE\n", date, address,
X				    group==1 ? "l" : "", size, xlib);
X			}else if(cmd==UNSUBSCRIBE){
X				fprintf(F, "%s [%s]%s %ld %s UNSUBSCRIBE\n", date, address,
X				    group==1 ? "l" : "", size, xlib);
X			}else{
X				fprintf(F, "%s [%s]%s %ld %s / %s", date, address,
X				    group==1 ? "l" : "", size, zlib, zitem);
X				if(excl[0])
X					fprintf(F, "~%s", zexcl);
X				if(cmd == LIST)
X					fprintf(F, " L\n");
X				else if(cmd == FIND)
X					fprintf(F, " F\n");
X				else if(DEBUG)
X					fprintf(F, " DEBUG\n");
X				else
X					fprintf(F, "\n");
X			}
X			fclose(F);
X		}
X		siam = 0;
X		/* continue to next request */
X	}
X
X	if(nreq == 0){
X		F = Fopen(reply, "w");
X		fprintf(F, "%s\n", usermail);
X		fclose(F);
X		sprintf(buf, "cat admin/mess/sorry_req %s| /bin/mail %s",
X			reply, address);
X		shell(buf);
X		quit(3, "no requests");
X	}
X
X	quit(0, "");
X}
X
X
Xshell(s)
Xchar	*s;
X{
X	if(DEBUG){
X		fprintf(stderr,"[%s]\n", s);
X	}else{
X		system(s);
X	}
X}
X
X
Xint
Xallfiles(files,dirname)
Xchar	*files;
Xchar	*dirname;
X{
X	/* append all files in "dirname" to the string "files" */
X	DIR *dir = opendir(dirname);
X	struct dirent *d;
X	char buf[LINESIZE];
X	int nfiles = 0;
X
X	if(!dir) return(nfiles);
X	while(d = readdir(dir))
X		if(strcmp(d->d_name,".")!=0 && strcmp(d->d_name,"..")!=0){
X			sprintf(buf," %s/%s",dirname,d->d_name);
X			strcat(files,buf);
X			nfiles++;
X		}
X	closedir(dir);
X	return(nfiles);
X}
X
Xint
Xshar(xlib, item, files)
Xchar	*xlib;
Xint	item[2];
Xchar	*files;
X{
X	/* At this point, netlib was about to conclude that the requested
X	files do not exist.  Let's check for the case "send xxx.shar" and
X	see if directory xxx exists. */
X	int n;
X	char *it, buf[LINESIZE];
X
X	if(item[0]!=item[1])
X		return(0);
X	it = field[item[0]];
X	n = strlen(it);
X	if(DEBUG)
X		fprintf(stderr,"shar(%s,%s)\n",xlib,it);
X	if(n<6 || strcmp(".shar",it+n-5)!=0)
X		return(0);
X	it[n-5] = '\0';  /* chop .shar */
X	sprintf(buf,"%s%s%s",xlib,xlib?"/":"",it);
X	if( access(buf,F_OK|R_OK|X_OK)!=0 || !isdir(buf) )
X		return(0);
X	lsr(files,buf);
X	if(DEBUG)
X		fprintf(stderr,"lsr(%s)=%s\n",buf,files);
X	return(1);
X}
X
Xonly(xlib, item, files)
Xchar	*xlib;
Xint	item[2];
Xchar	*files;
X{
X	/* list all existing files of the form xlib/item */
X	int	nlib, nfiles, i, j, k;
X	char	buf[LINESIZE];
X	char	*lib[FIELDMAX];
X
X	nlib = split(xlib, lib, " \t");
X	nfiles = 0;
X	*files = '\0';
X	for (i = 0; i < nlib; i++){
X		if(item[0]==item[1] && strcmp("all",field[item[0]])==0){
X			nfiles += allfiles(files,lib[i]);
X			continue;
X		}
X		for (j = item[0]; j <= item[1]; j++){
X			for (k = 0; suffix[k]; k++){
X				sprintf(buf, "%s/%s%s", lib[i], field[j], suffix[k]);
X				if( access(buf,F_OK|R_OK)==0 && !isdir(buf) ){
X					if(nfiles)
X						strcat(files, " ");
X					strcat(files, buf);
X					nfiles++;
X					break; /* skip remaining suffixes */
X				}
X			}
X		}
X	}
X	if(nfiles == 0){
X		if( (group != 1) && (strcmp(xlib, "portP") == 0) ){
X			strcpy(files, "admin/mess/sorry_port");
X		}else if(shar(xlib,item,files)){
X		}else{
X			strcpy(files, "admin/mess/sorry");
X			sprintf(buf, "%s/index", field[item[0]]);
X			if(access(buf, 4) == 0){
X				strcat(files, " admin/mess/sorry_indiv ");
X				strcat(files, buf);
X			}
X		}
X	}
X}
X
X
X/* Gentle Reader:  I realize that hashing is overkill for this
X	application.  But the code was already lying around and you
X	might find it useful elsewhere.  */
Xvoid
Xinsert(HashTable*kt, char*key, int code)
X{
X	int i = hashsrch(kt,key);
X	i<0 || Error("duplicate keyword %s",key);
X	hashins(kt,i,key,code);
X}
X
Xint	/* non-zero if case changed to lower */
Xcasech(s)
Xregister char	*s;
X{
X	register int changed = 0;
X	while (*s){
X		if(isupper(*s)){
X			*s = tolower(*s);
X			changed = 1;
X		}
X		s++;
X	}
X	return(changed);
X}
X
Xint
Xlookup(char*key, HashTable*kt)
X{
X	/* strips trailing punctuation and may convert to lower case */
X	/* also strips surrounding brackets */
X	int i;
X	register char	*r = key + strlen(key)-1;   /* end of string */
X	register char	*t;
X	if(	((*key == '<') && (*r == '>')) ||
X	    ((*key == '[') && (*r == ']')) ||
X	    ((*key == '{') && (*r == '}')) ){
X		*r = '\0';
X		for (t = key; t < r; t++)
X			t[0] = t[1];
X	}
X	for (; (r >= key) && strchr(",?;\"'`.", *r); r--){}
X	r[1] = '\0';
X	i = hashsrch(kt,key);
X	if(i<0 && casech(key))
X		i = hashsrch(kt,key);
X	if(i<0)
X		return(0);
X	else
X		return((*kt)[i].Code);
X}
X
X/* case independent prefix compare */
Xint
Xcicmp(char *s1, char *s2)
X{
X        int c1, c2;
X
X        for(; *s1; s1++, s2++){
X                c1 = *s1;
X                c2 = isupper(*s2) ? tolower(*s2) : *s2;
X                if (c1 != c2)
X                        return -1;
X        }
X	return(0);
X}
X
Xint
Xparse( address, item, excl, lib, thru )
Xchar	address[];
Xint	item[2], excl[2], lib[2], thru[2];
X{
X	/* analyze field[] */
X	int	cmd, i, k;
X	char	c, *p;
X	static char	buf[LINESIZE];
X	static int	in_From = 0, replyto = 0, first = 1;
X	static HashTable keyword_table = 0, *kt;
X	static int	ONLY, OF, WHO, IS, WHOIS, BUT, NOT;
X	static int	THRU, EOL, IGNORE, MAXMAIL, QUIT, PATH;
X	int	code[FIELDMAX+2];  /* = lookup(field[],kt); */
X	FILE	*F;
X
X	if(first){
X		first = 0;
X		k = SKIP;
X		EOL = ++k;
X		kt = &keyword_table;
X		insert(kt,"path",PATH = ++k);
X		insert(kt,"send", SEND = ++k);
X		insert(kt,"list", LIST = ++k);
X		insert(kt,"only", ONLY = ++k);
X		insert(kt,"of", OF = ++k);
X		insert(kt,"whois", WHOIS = ++k);
X		insert(kt,"who", WHO = ++k);
X		insert(kt,"is", IS = ++k);
X		insert(kt,"find", FIND = ++k);
X		insert(kt,"execute", EXEC = ++k);
X		insert(kt,"Subject:", IGNORE = ++k);
X		insert(kt,"index", INDEX = ++k);
X		insert(kt,"but", BUT = ++k);
X		insert(kt,"not", NOT = ++k);
X		insert(kt,"|", THRU = ++k);
X		insert(kt,"mailsize",MAXMAIL = ++k);
X		insert(kt,"quit",QUIT = ++k);
X		insert(kt,"subscribe",SUBSCRIBE = ++k);
X		insert(kt,"unsubscribe",UNSUBSCRIBE = ++k);
X		insert(kt,"mime",MIME = ++k);
X		insert(kt,"please", IGNORE);
X		insert(kt,"echo", IGNORE);
X		insert(kt,"sned", SEND);
X		insert(kt,"mail", SEND);
X		insert(kt,"request", SEND);
X		insert(kt,"get", SEND);
X		insert(kt,"from", OF);
X		insert(kt,"to", OF);
X		insert(kt,"<", OF);
X		insert(kt,"for", OF);
X		insert(kt,"in", OF);
X		insert(kt,"where", WHO);
X		insert(kt,"directory", INDEX);
X		/*only affects "send directory", not "send directory for x"*/
X		insert(kt,"information", INDEX);
X		insert(kt,"info", INDEX);
X		insert(kt,"help", INDEX);
X		insert(kt,"readme", INDEX);
X		insert(kt,"filter", EXEC);
X		insert(kt,"pipe", EXEC);
X		insert(kt,"through", THRU);
X		insert(kt,"chunk", MAXMAIL);
X		insert(kt,"maxbyte", MAXMAIL);
X		insert(kt,"size", MAXMAIL);
X		insert(kt,"limit", MAXMAIL);
X		insert(kt,"maillimit", MAXMAIL);
X	}
X
X	if(in_From){ /* continuation of From: line */
X		c = *field[0];
X		if( c == ' ' || c == '\t' ){
X			if( strlen(field[0]) + strlen(buf) >= LINESIZE )
X				quit(2, "long line");
X			strcat(buf, field[0]);
X			return(SKIP);
X		}else{ /* not a continuation */
X			in_From = 0;
X			if(strchr(buf, '(') || strchr(buf, '<') || strchr(buf, '[')){
X				/* address appears to contain actual as well as electronic name */
X				if((F = fopen("admin/names", "a")) != NULL){
X					fprintf(F, "%s\n", buf);   /* log it */
X					fclose(F);
X				}
X			}
X			if(getfrom(buf,address)) /* extract RFC822 address */
X				quit(3, "bad address");
X			chkaddr(address);
X		}
X	}
X
X	if(NF == 0){ /* blank line */
X		in_header = 0;
X		return(SKIP);
X	}
X	code[1] = lookup(field[1],kt);
X
X	/* look for (start of) return address */
X	if( in_header && (NF>1) ){
X			/* ignore bogus From: lines in message text (IBM) */
X		if(!replyto && strncmp("From ",field[0],5)==0){
X			strcpy(address, field[2]);
X			chkaddr(address);
X			return(SKIP);
X		}else if(strlen(field[0])>9 && cicmp("reply-to:",field[0])==0){
X			strcpy(buf, 1 + strlen(field[1]) + field[0]);
X			in_From = 1;
X			replyto = 1;
X			return(SKIP);
X		}else if(!replyto && strlen(field[0])>5 &&
X				cicmp("from:",field[0])==0){
X			strcpy(buf, 1 + strlen(field[1]) + field[0]);
X			in_From = 1;
X			return(SKIP);
X		}
X	}
X	if( code[1] == PATH ){
X		if(NF!=2 || getfrom(field[2],address))
X				quit(3, "bad address");
X		chkaddr(address);
X		return(SKIP);
X	}
X
X	if( in_header && (c = *field[0])!=' ' && c!='\t' &&
X			strchr(field[0],':')==0 ){
X		in_header = 0;
X	}
X
X	/* ignore anything after ". " or " #" */
X	for (k = 1; k <= NF; k++){
X		p = field[k] + strlen(field[k]) - 1;
X		if( *p == '.' ){
X			*p = '\0';
X			NF = k;
X		}else if( *field[k] == '#' ){
X			NF = k - 1;
X		}
X	}
X
X	/* finish decoding */
X	for (k = 2; k <= NF; k++)
X		code[k] = lookup(field[k],kt);
X	code[NF+1] = EOL;
X	for (k = 1; code[k] == IGNORE; ++k ){}
X
X	/* now look for a request */
X	depend = 1;
X	item[0] = excl[0] = lib[0] = thru[0] =  0;
X	item[1] = excl[1] = lib[1] = thru[1] = -1;
X	cmd = code[k];
X	if( cmd == INDEX ){
X		cmd = SEND;
X		k--;
X	}else if( (cmd == SEND) || (cmd == LIST) ){
X		if( code[k+1] == ONLY ){
X			k++;
X			depend = 0;
X		}
X		if( (code[k+1] == LIST) && (code[k+2] == OF) ){
X			k += 2;
X			cmd = LIST;
X		}
X	}else if( (cmd == FIND) || (cmd == WHOIS) ){
X		depend = 0;
X	}else if( cmd == MAXMAIL ){
X		if( k+1<=NF ){
X			i=atoi(field[k+1]);
X			for( p = field[k+1]; isdigit(*p); p++){}
X			if(*p=='\0' && k+2<=NF)
X				p = field[k+2];
X			if(*p=='k' || *p=='K')
X				i *= 1000;
X			if(*p=='m' || *p=='M')
X				i *= 1000000;
X			maxbyte = i;
X		}
X		return(SKIP);
X	}else if( cmd == WHO ){
X		if( code[k+1] == IS ){
X			k += 1;
X			cmd = WHOIS;
X			depend = 0;
X		}else{
X			return(SKIP);
X		}
X	}else if( cmd == SUBSCRIBE || cmd == UNSUBSCRIBE ){
X		item[0] = item[1] = k;
X		lib[0] = lib[1] = k+1;
X		depend = 0;
X		return(cmd);
X	}else if( cmd == MIME ){
X		mime = 1;
X		return(SKIP);
X	}else if( cmd == QUIT ){
X		return(DONE);
X	}else if( cmd != INDEX && cmd != EXEC ){
X		return(SKIP);
X	}
X
X	/* get items, exclusions, libraries, final filter */
X	while( code[++k]==IGNORE );
X	item[0] = k;
X	while( code[k]==0 || code[k]==INDEX ) k++;
X	item[1] = k-1;
X	if( code[k]==BUT && code[k+1]==NOT ){
X		k++;
X		while( code[++k]==IGNORE );
X		excl[0] = k;
X		while( code[k]==0 ) k++;
X		excl[1] = k-1;
X	}
X	if( code[k]==OF ){
X		while( code[++k]==IGNORE );
X		lib[0] = k;
X		while( code[k]==0 ) k++;
X		lib[1] = k-1;
X	}
X	if( code[k]==THRU ){
X		while( code[++k]==IGNORE );
X		thru[0] = k;
X		while( code[k]!=EOL ) k++;
X		thru[1] = k-1;
X	}
X	if( code[k]!=EOL ){
X		if(strlen(field[k])>30) field[k][29] = '\0';
X		fprintf(stderr,"junk at end, field %d:%s\n",k,field[k]);
X		return(SKIP);
X	}
X
X	/* allow "send y/x" notation */
X	if( !lib[0] && NF<FIELDMAX-1){
X		for(i=item[0]; i<=item[1]; i++){
X			if(p=strrchr(field[i],'/')){
X				*p++ = '\0';
X				if(!lib[0]){  /* first time */
X					field[FIELDMAX-1] = field[i];
X					lib[0] = lib[1] = FIELDMAX-1;
X				}else{
X					if(strcmp(field[i],field[FIELDMAX-1])!=0)
X						return(SKIP);
X				}
X				field[i] = p;
X			}
X		}
X	}
X
X	/* fix up special cases */
X	if( (cmd == SEND) || (cmd == LIST) ){
X		if(item[0]==item[1] && code[item[0]]==INDEX){
X			k = item[0];
X			if(depend && strcmp(field[k],"index")==0){
X				field[k] = "readme";
X				field[k+1] = "index";
X				item[1] = k+1;
X				if(group==1 && !lib[0])
X					field[++item[1]] = "index.lcl";
X			}else if(strcmp(field[k],"help")==0){
X				field[k] = "readme";
X			}
X			if(!lib[0])
X				field[ lib[0]=lib[1]=FIELDMAX-2 ] = "master";
X			depend = 0;
X		}
X	}else if( cmd == WHOIS ){
X		cmd = FIND;
X		if(lib[0]==0){
X			assert(item[1] < FIELDMAX - 3);
X			k = FIELDMAX - 3;
X			field[k] = "siam-Secret";
X			field[k+1] = "golub";
X			lib[0] = k;
X			lib[1] = k + 1;
X			siam = 1;
X		}
X	}
X
X	if( (item[0]>item[1]) && (cmd==SEND||cmd==LIST||cmd==FIND) )
X			return(SKIP);
X
X	return(cmd);
X}
X
X
Xint
Xduplicate(cmd, item, excl, lib)
Xint	cmd, item[2], excl[2], lib[2];
X{
X	/* Is the current request the same as the previous one? */
X	static int	execnt = 0;
X	static int	c, i[2], e[2], l[2];
X	static char	line[LINESIZE], *f[FIELDMAX];
X	char	*next, *cpfld();
X
X	if( (execnt != 0) && (c == cmd) &&
X	    (cmpfld(i, f, item, field) == 0) &&
X	    (cmpfld(e, f, excl, field) == 0) &&
X	    (cmpfld(l, f, lib, field) == 0) ){
X		return(1);
X	}
X	execnt++;
X
X	/* save current request */
X	c = cmd;
X	i[0] = item[0];
X	i[1] = item[1];
X	e[0] = excl[0];
X	e[1] = excl[1];
X	l[0] = lib[0];
X	l[1] = lib[1];
X	next = cpfld(line, i, f);
X	next = cpfld(next, e, f);
X	next = cpfld(next, l, f);
X	return(0);
X}
X
X
Xint
Xcmpfld(i, f, j, g)
Xint	i[2], j[2];
Xchar	*f[], *g[];
X{
X	int	k = i[1]-i[0];
X	if( k != (j[1] - j[0]) )
X		return(1);
X	while (k >= 0){
X		if(strcmp(f[i[0]+k], g[j[0]+k]) != 0)
X			return(1);
X		k--;
X	}
X	return(0);
X}
X
X
Xchar	*
Xcpfld(p, i, f)
Xchar	*p;  /* next position to put chars */
Xint	i[2];
Xchar	*f[];
X{
X	int	k;
X	for (k = i[0]; k <= i[1]; k++){
X		f[k] = p;
X		strcpy(p, field[k]);
X		p += strlen(p) + 1;
X	}
X	return(p);
X}
X
X
X/********* expand library names ***********/
X
Xvoid
Xmsdos2unix(s)
Xchar *s;
X{
X	while(*s){
X		if(*s=='\\')
X			*s='/';
X		s++;
X	}
X}
X
X
Xexpand(lib, xlib)
Xint	lib[2];
Xchar	*xlib;
X{
X	int	j, k;
X	xlib[0] = '\0';
X	for (k = lib[0]; k <= lib[1]; k++){
X		msdos2unix(field[k]);
X		if( group==1 && search("admin/LIBS.lcl",field[k],xlib) )
X			;
X		else
X			search("admin/LIBS",field[k],xlib);
X	}
X	if(!xlib[0] && lib[0]==lib[1] && isdir(field[lib[0]]))
X		strcpy(xlib,field[lib[0]]);
X}
X
X
Xint	/* returns 1 if found, 0 if not */
Xsearch(filename, pat, xlib)
Xchar	*filename;
Xchar	*pat;
Xchar	*xlib;
X{
X	/* find first line in file starting with pat */
X	FILE * f;
X	int	len, i, c, matches = 0;
X	char	**p, line[400], *s;
X
X	if(*filename == '\0')
X		return(0);
X	if( (f = fopen(filename, "r")) == NULL )
X		return(0);
X	len = strlen(pat);
X	while (fgets(line, 400, f) != NULL){
X		nlchop(line);
X		if( (strncmp(line, pat, len) == 0) && (line[len] == ' ') ){
X			found(line, xlib);
X			matches++;
X			break;
X		}
X	}
X	fclose(f);
X	return(matches);
X}
X
X
Xfound(s, xlib)
Xchar	*s;
Xchar	*xlib;
X{
X	char	*r;
X	r = strchr(s, '>');   /* strip "^.*=> *" */
X	r || Error("corrupted LIBS? s=%s",s);
X	r++;
X	while (isspace(*r))
X		r++;
X	if(xlib[0] != '\0')
X		strcat(xlib, " ");
X	strcat(xlib, r);
X}
X
X
X/* make a quick guess;  does the file contain unmailable bytes? */
Xint
Xis_binary(file)
X	char *file;
X{
X	char *suffix;
X
X	suffix = strrchr(file,'.');
X	if(suffix){
X		suffix++;  /* skip . */
X		if(suffix[1]==0 && (suffix[0]=='f'||suffix[0]=='c'))
X			return 0;
X		if(strcmp(suffix,"tgz")==0 ||
X			strcmp(suffix,"gz") ||
X			strcmp(suffix,"Z") ||
X			strcmp(suffix,"zip") ||
X			strcmp(suffix,"exe") ||
X			strcmp(suffix,"pdf") )
X			return 1;
X	}
X	return 0;
X}
X
X
Xsend(address,maxbyte,subject,files,xlib)
X	char *address, *subject, *files, *xlib;
X	long maxbyte;
X{
X	/* convert "files" to form needed by bigmail() */
X	char *f[FIELDMAX];
X	int nf = 0;
X	int i, n;
X	char *s[FIELDMAX];
X	char buf[LINESIZE];
X
X	n = split(xlib, s, " \t");
X	for(i=0; i<n; i++){
X		sprintf(buf,"%s/disclaimer",s[i]);
X		if(access(buf,4)==0){
X			f[nf] = malloc(strlen(buf)+1);
X			strcpy(f[nf],buf);
X			nf++;
X			assert(nf<FIELDMAX);
X		}
X	}
X	n = split(files, s, " \t");
X	for(i=0; i<n; i++){
X		f[nf] = malloc(strlen(s[i])+1);
X		strcpy(f[nf],s[i]);
X		nf++;
X		assert(nf<FIELDMAX);
X		if(mime==0 && is_binary(s[i]))
X			mime = 1;
X	}
X	if(DEBUG){
X		fprintf(stderr,"%s %d\n",(mime?"mimemail":"bigmail"),nf);
X		for(i=0; i<n; i++){
X			fprintf(stderr,"	%s\n",f[i]);
X		}
X	}else{
X		if(mime)
X			mimemail(address,subject,f,nf);
X		else
X			bigmail(address,maxbyte,subject,f,nf);
X	}
X	while(nf)
X		free(f[--nf]);
X}
X
X
X/*************** utilities modeled after awk ****************/
Xstatic char	line[LINESIZE], line2[LINESIZE];
Xstatic char	isFS[256], isntFS[256], *oFS;
X
Xint
Xawk()
X{
X	if(fgets(field[0] = line2, LINESIZE - 1, stdin) == NULL)
X		return(EOF);
X	nlchop(line2);
X	++NR;
X	handle(line2);
X	strcpy(line, field[0]);
X	if(!FS)
X		FS = " \t";
X	NF = split(line, field + 1, FS);
X	return(1);
X}
X
X
Xint
Xsplit(ss, a, r)
Xchar	*ss, *a[], *r;
X{
X	register char	*s = ss;
X	register int	i = 0;
X	FS = r;
X	if(!oFS || strcmp(FS, oFS))
X		FSinit();
X	while (i < FIELDMAX - 1){
X		while (isFS[*s])
X			s++;
X		a[i++] = s;
X		while (isntFS[*s])
X			s++;
X		if(*s == '\0')
X			break;
X		*s++ = '\0';
X	}
X	if(!*a[i-1])
X		i--;
X	return(i);
X}
X
X
XFSinit()
X{
X	register int	i;
X	register char	*s;
X	for (i = 0; i < (sizeof isFS); i++){
X		isFS[i] = 0;
X		isntFS[i] = 1;
X	}
X	for (s = FS; *s; s++){
X		isFS[*s] = 1;
X		isntFS[*s] = 0;
X	}
X	isntFS[0] = 0;
X	oFS = FS;
X}
X
X
X/************* return address nonsense *****************/
X
Xchkaddr(address)
Xchar	address[];
X{
X	static int k = 0;
X	int	groupid();
X	char	*p, addr2[LINESIZE];
X
X	if( !*address || unsafe(address)){
X		Error("address=%s", address);
X		quit(99, "unsafe address");
X	}
X	if( (group = groupid(address)) >= 10)
X		return;
X
X	if(strncmp("attmail!", address, 8) == 0){
X		strcat(address, "/COD");
X	}
X
X	/* replace this heuristic with something better if you know one */
X	if( upas && group==1 )
X		maxbyte = 1000000;
X	else
X		maxbyte = 100000;
X}
X
X
X/*  This returns the "security clearance" of address.
X    Assumes address has (a special case of) the form
X	@boguspath:uucpname!...!user%another@inter.net.address
X    and, in particular, does not understand X.400.
X  return codes:
X    0 = general address
X    1 = local  (all machines in address appear in file "local")
X   10 = enemy  (some machine is in file "enemies")
X*/
X
Xint
Xgroupid(a)
Xchar	*a;
X{
X	char	*m[100];	/* machines in address */
X	int	n;		/* size of m */
X	char	*p;		/* a copy of a, for cutting with '\0' */
X	char	*q, *pp[2];
X	int	cmp();
X	int	i, len;
X
X	p = malloc(strlen(a) + 1);
X	assert(p);
X	strcpy(p, a);
X				/* check for full address */
X	pp[0] = p;
X	n = 1;
X	if(del(pp, &n, "admin/groups/enemies"))
X		return(10);
X				/* ok, let's get machine names... */
X	n = 0;
X				/* strip off leading @boguspath: */
X	while( p[0]=='@' && (q=strchr(p,':')) ){
X		*q = '\0';
X		m[n] = p+1;
X		if(++n>99) Error("too many hops in %s", a);
X		p = q+1;
X	}
X				/* strip off trailing @inter.net.address */
X	while( (q=strrchr(p,'@')) ){
X		*q = '\0';
X		m[n] = q+1;
X		if(++n>99) Error("too many hops in %s", a);
X	}
X				/* strip off leading uucpname! */
X	while( (q=strchr(p,'!')) ){
X		*q = '\0';
X		m[n] = p;
X		if(++n>99) Error("too many hops in %s", a);
X		p = q+1;
X	}
X				/* strip off trailing %another */
X	/* yes, this could be merged with search for @.  But
X	we've built the machine list in path order, which might
X	be handy for someone else's security policy. */
X	while( (q=strrchr(p,'%')) ){
X		*q = '\0';
X		m[n] = q+1;
X		if(++n>99) Error("too many hops in %s", a);
X	}
X	/* at this point, some sites might */
X	/* also want to check for :: or # or other magic, but my
X	/* mailer will treat those as invalid local addresses */
X				/* what's left must be userid */
X	pp[0] = p;
X	i = 1;
X				/* filter out uucp, root, unknown-user, ... */
X	if(del(pp, &i, "admin/groups/badusers"))
X		return(10);
X	if(n == 0)
X		return(1);
X	/* the following is intended only for netlib@research.bell-labs.com,
X				   but probably harmless elsewhere */
X	if(upas){
X		for(i=0; i<n; i++){
X			len = strlen(m[i]);
X			if( strcmp(m[i],"bell-labs.com") == 0 )
X				m[i] = "lucent";
X			else if( strcmp(m[i],"lucent.com") == 0 )
X				m[i] = "lucent";
X			else if( len>14 && strcmp(m[i]+len-14,".bell-labs.com") == 0 )
X				m[i] = "lucent";
X			else if( len>11 && strcmp(m[i]+len-11,".lucent.com") == 0 )
X				m[i] = "lucent";
X		}
X	}
X	qsort(m, n, sizeof(char *), cmp);
X	if(del(m, &n, "admin/groups/enemies"))
X		return(10);
X	del(m, &n, "admin/groups/local");
X	if(n == 0)
X		return(1);
X	return(0);
X	/* won't be many addresses, so don't bother to free */
X}
X
X
Xint
Xcmp ( p, q )
Xchar	**p, **q;
X{
X	/* needed for qsort */
X	return(strcmp(*p, *q));
X}
X
X
Xint		/* returns number of deletions */
Xdel ( m, nn, file )
Xchar	*m[];	/* machine names; matches will be deleted */
Xint	*nn;	/* number of entries in m (updated if there are deletions) */
Xchar	*file;	/* where to look for matches, to be deleted from m */
X{
X	/* file and m are assumed to be sorted. */
X	register int	n = *nn;
X	register int	i;  /* walks through m */
X	int	j;  /* continue place to put item in m;  if j==i, no need to copy */
X	int	ndel = 0;  /* number of deletions */
X	int	c;  /* result of string comparison */
X	char	*s, a[100]; /* machine name from file */
X	FILE * f = fopen(file, "r");
X	if(f == NULL)
X		return(0);
X	i = j = ndel = 0;
X	if(fgets(a, 100, f) == NULL)
X		return(0);
X	nlchop(a);
X	while (1){
X		c = strcmp(a, m[i]);
X		if(c < 0){
X			/* advance a */
X			if(fgets(a, 100, f) == NULL)
X				break;
X			nlchop(a);
X		}else if(c > 0){
X			if(i > j)
X				m[j] = m[i];
X			j++;
X			i++;
X			if(i >= n)
X				break;             /* advance m */
X			continue;
X		}else{ /* match */
X			ndel++;
X			i++;
X			if(i >= n)
X				break;             /* advance m */
X			continue;
X		}
X	}
X	fclose(f);
X	if(i > j)
X		while (i < n)
X			m[j++] = m[i++];
X	*nn = n - ndel;
X	return(ndel);
X}
X
X
X/******* other utilities *********/
X
XFILE*
XFopen(name,mode)		/* fopen() with abort on failure */
Xchar *name;
Xchar *mode;
X{
X	FILE *fp;
X	
X	fp = fopen(name,mode);
X	fp!=NULL || Error("Can't open %s for mode %m",name,mode);
X	return (fp);			/* fopen() succeeded */
X}
X
X
Xstatic char datestr[30];
X
Xchar	*
Xget_date(void)
X{
X	struct tm *tp, *localtime();
X	long	clock, time();
X	clock = time(0);
X	tp = localtime(&clock);
X	sprintf(datestr, "%d %2d %2d %02d%02d", tp->tm_year + 1900, tp->tm_mon + 1,
X	    tp->tm_mday, tp->tm_hour, tp->tm_min );
X	return(datestr);
X}
X
X
Xquit(rc, mess)
Xint	rc;
Xchar	*mess;
X{
X	rc==0 || Error("%s %s\n",datestr,mess);
X	exit(0);
X}
X
X
Xnlchop(s)
Xregister char	*s;
X{
X	/* chop trailing newline */
X	if(!*s)
X		return;
X	s += strlen(s) - 1;
X	if(*s == '\n')
X		*s = '\0';
X}
X
X
Xgather(f, z, n)
Xint	f[2];
Xchar	z[];
Xint	n;  /* sizeof(z) */
X{
X	/* catenate fields f[0]...f[1] */
X	char *s = z, *e = z+n-2, *t;
X	int	k;
X	for (k = f[0]; k <= f[1]; k++){
X		if(k > f[0]){
X			if(s==e){
X				Error("gather overflow %d",n);
X				quit(9,"gather");
X			}
X			*s++ = ' ';
X		}
X		t = field[k];
X		while(*t){
X			if(s==e){
X				Error("gather overflow %d",n);
X				quit(9,"gather");
X			}
X			*s++ = *t++;
X		}
X	}
X	*s = '\0';
X}
X
X
Xlong
Xfsiz ( s )
Xchar	*s;
X{
X	int	fd;
X	long	t;
X	long	lseek();
X	if( (fd = open(s, 0)) == -1 ){
X		Error("can't open %s", s);
X		quit(7, "open");
X	}
X	if( (t = lseek(fd, 0L, 2)) == -1 ){
X		Error("can't seek on %s", s);
X		quit(8, "seek");
X	}
X	close(fd);
X	return(t);
X}
X
X
Xint isdir ( s )
Xchar	*s;
X{
X	struct stat buf;
X	char *dot = s;
X	if(strcmp(s,"admin")==0) return 0;  /* disallow "send admin/log" */
X	while(dot = strchr(dot,'.')){       /* disallow ".." */
X		if(dot[1]=='.' &&
X			(dot[2]=='/' || dot[2]=='\0') &&
X			(dot[-1]=='/' || dot==s) )
X			return 0;
X		dot++;
X	}
X	stat(s,&buf);
X	return( S_ISDIR(buf.st_mode) );
X}
X
X
Xhandle(line)
Xchar	*line;
X{
X	/* save message, toss escapes */
X	register char	*s; /* next char to examine */
X	register char	*t; /* next place to copy char */
X	int userlen = strlen(usermail), linelen = strlen(line);
X	/****if you don't want to keep a log, remove the next 15 lines *****/
X	static int	execnt = 0;
X	static FILE *save;
X	if( execnt++ == 0 ){
X		save = fopen("/tmp/netreq", "a");
X#ifdef S_IRWXU
X		chmod("/tmp/netreq",(mode_t)S_IRWXU|S_IRWXG|S_IRWXO);
X#endif
X	}
X	/* closed automatically when program quits */
X	/* This won't work if several netlib processes are
X	   running simultaneously;  but this log isn't too
X	   important, so we won't worry about that. */
X	if(save!=NULL)
X		fprintf(save, "%s\n", line);
X	/***************************************************************/
X
X	/* append to usermail[] */
X	if(userlen+linelen+2<sizeof(userlen)){
X		strcpy(usermail+userlen,line);
X		strcpy(usermail+userlen+linelen,"\n");
X	}
X	/* delete nonprinting characters; interpret backspace and tab */
X	s = line;
X	t = line;
X	for (; *s; s++){
X		if( (*s == '\b') && (t > line) ){
X			t--;  /* might not be right for ESC BS, but tough */
X		}else if(*s == '\t'){
X			*t++ = ' ';
X		}else if(!iscntrl(*s)){
X			*t++ = *s;
X		}
X	}
X}
-------cut here----- src/reply.c
echo src/seq.c 1>&2
sed >src/seq.c <<'-------cut here----- src/seq.c' 's/^X//'
X#include <stdio.h>
Xmain(argc, argv)
Xchar	**argv;
X{
X	int	i, n = atoi(argv[1]);
X	for (i = 1; i <= n; i += 1)
X		printf(" %d", i);
X	printf("\n");
X	exit(0);
X}
X
X
-------cut here----- src/seq.c
echo src/subscribe.c 1>&2
sed >src/subscribe.c <<'-------cut here----- src/subscribe.c' 's/^X//'
X#define _POSIX_SOURCE
X#include <stdlib.h>
X#include <stdio.h>
X#include <assert.h>
X#include <ctype.h>
X#include <unistd.h>
X#include <string.h>
X#include <time.h>   /* on some systems, this is <sys/times.h> */
X#include <sys/types.h>
X#include <sys/stat.h>
X#ifndef S_ISDIR
X#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
X#endif
X
Xextern int isdir(char*);
X#ifdef EHGTEST
Xchar*addr_canon(char*a);
Xvoid*Malloc(int n){return malloc(n);}
Xint isdir(char*s){return 0;}
Xvoid shell(char*s){return;}
Xmain(){printf("%s\n",addr_canon("ehg@bell-labs.com"));}
X#endif
X
Xchar*
Xaddr_canon(char*addr)
X{
X	/* subscribe isn't called often, so ignore memory leak */
X	char *addrc;
X	char *c = strchr(addr,'@');
X	if(c){
X		addrc = (char*)Malloc(strlen(addr)+1);
X		strcpy(addrc,c+1);
X		strcat(addrc,"!");
X		strncat(addrc,addr,c-addr);
X	}else{
X		return addr;
X	}
X}
X
X
X/* RETURN CODES
X	0 = success
X	1 = couldn't find topic
X	2 = database update collision; try again later
X	3 = internal error
X*/
X
Xint
Xsubscribe(char*addr,char*topic)
X{
X	FILE *addrlist;
X	char *addrlistname, *cmd, *s;
X	char *addrc = addr_canon(addr);
X	
X	if(s = strchr(topic,' '))
X		*s = '\0';  /* for linpack => linpack blas */
X	if(strncmp(topic,"-l",2)==0)
X		topic += 2;
X	if(!isdir(topic)) return(1);
X	addrlistname = (char*)Malloc(strlen(topic)+6);
X	sprintf(addrlistname,"%s/.list",topic);
X	if(access(addrlistname,F_OK)!=0){
X		cmd = (char*)Malloc(strlen(topic)+strlen("mk.list ")+1);
X		sprintf(cmd,"/netlib/admin/bin/mk.list %s",topic);
X		system(cmd);
X		free(cmd);
X	}
X	addrlist = fopen(addrlistname,"a");
X	if(!addrlist) return(3);
X	fprintf(addrlist,"%s\n",addrc);
X	fclose(addrlist);
X	free(addrlistname);
X	return(0);
X}
X
Xint
Xunsubscribe(char*addr,char*topic)
X{
X	FILE *addrlist, *tmp;
X	char *addrlistname, *t, *tmpname, *addrn, line[1024], *cmd;
X	char *addrc = addr_canon(addr);
X
X	struct stat before, after;
X	if(!isdir(topic)) return(1);
X	addrlistname = (char*)Malloc(strlen(topic)+6);
X	sprintf(addrlistname,"%s/.list",topic);
X	stat(addrlistname,&before);
X	addrlist = fopen(addrlistname,"r");
X	if(!addrlist) return(1);
X	tmpname = (char*)Malloc(strlen(topic)+13);
X	sprintf(tmpname,"/tmp/%s.newlist",topic);
X	for(t = tmpname+5; *t; t++)
X		if(*t=='/') *t = '_';
X	if(fopen(tmpname,"r")) return(2);
X	tmp = fopen(tmpname,"w+");
X	if(!tmp){
X		fprintf(stderr,"couldn't open %s for writing\n",tmpname);
X		return(3);
X	}
X	addrn = (char*)Malloc(strlen(addr)+2);
X	sprintf(addrn,"%s\n",addr);
X	while(fgets(line,sizeof(line),addrlist)!=NULL){
X		if(strcmp(addrn,line)==0) continue;
X		fputs(line,tmp);
X	}
X	fclose(addrlist);
X	fclose(tmp);
X	chmod(tmpname,0666);
X	stat(addrlistname,&after);
X	if(before.st_mtime!=after.st_mtime){
X		unlink(tmpname);
X		return(2);
X	}
X	cmd = (char*)Malloc(2*strlen(topic)+20);
X	sprintf(cmd,"cp %s %s",tmpname,addrlistname);
X	shell(cmd);
X	unlink(tmpname);
X	free(cmd); free(addrn); free(tmpname); free(addrlistname);
X	return(0);
X}
-------cut here----- src/subscribe.c
echo src/to_subscribers.c 1>&2
sed >src/to_subscribers.c <<'-------cut here----- src/to_subscribers.c' 's/^X//'
X/* Send notification to people who have subscribed to directories.
X stdin: list of changed files
X env:   upasname
X
X Typically, this is invoked from imastercrc.
X I was tempted to do it all in that shell script, but worried
X about a security bug being introduced if someone (other than netlib)
X added nasty addresses to the .list files.
X Eric Grosse <ehg@research.bell-labs.com> 25 Jan 1995
X
X*/
X#define _POSIX_SOURCE
X#include <stdlib.h>
X#include <unistd.h>
X#include <string.h>
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#ifndef PATH_MAX
X#define PATH_MAX 1024
X#endif
X#ifndef NAME_MAX
X#define NAME_MAX 1024
X#endif
X#define FCNT 100	/* files to batch;  only an optimization */
X
Xchar *exclude[] = { ".list", "index.html", "att_crc" };
X
X
X/* as in /netlib/admin/src/getfrom.c, but now also rule out slash */
Xint
Xunsafe(char *s)
X{
X	char c;
X	/* check for characters interpreted by the shell (by which nefarious
X           users might otherwise break into the system) */
X	for (c = (*s); c != '\0'; c = (*++s)) {
X		if ( strchr("/\"'`$\n;&|^<>()\\", c) || (c & 0200) ){
X			fprintf(stderr,"unsafe(%s) saw %c\n",s,c);
X			return(1);
X		}
X	}
X	return(0);
X}
X
Xunsigned long
Xfsize(char *path)
X{
X	struct stat s;
X	if(stat((char*)path,&s)){
X		fprintf(stderr,"%s: ",path);
X		perror("can't stat");
X		return 0;
X	}
X	return (unsigned long)s.st_size;
X}
X
Xint
Xsearch(char*s,char**t)
X{
X	while(*t){
X		if(strcmp(s,*t)==0)
X			return(1);
X		t++;
X	}
X	return(0);
X}
X
Xint
Xrecord_files(int nf, char*dir, char files[][NAME_MAX+1], unsigned long*fsizes)
X{
X	char wdir[PATH_MAX], *slash, subscriber[NAME_MAX+21];
X	char cmd[PATH_MAX+NAME_MAX+10], list[PATH_MAX+1];
X	FILE *L, *S;
X	int m, n, nrecords = 0;
X
X	if(nf==0)
X		return(0);
X	strcpy(wdir,dir);
X	strcpy(subscriber,"/tmp/to_subscribers/");
X	n = strlen("/tmp/to_subscribers/");
X	while(1){ /**** for each subscriber list ****/
X		sprintf(list,"%s/.list",wdir);
X		if(L = fopen(list,"r")){
X			while(fgets(subscriber+n,sizeof(subscriber)-n,L)){
X						/**** for each subscriber ****/
X				m = strlen(subscriber);
X				if(m==n){
X					fprintf(stderr,"empty addr %s\n",list);
X					continue;
X				}
X				if(m>=sizeof(subscriber)-2){
X					fprintf(stderr,"long %s %s\n",subscriber+n,list);
X					continue;
X				}
X				if(subscriber[m-1]=='\n')
X					subscriber[--m] = '\0';	/* chop NL */
X				if(unsafe(subscriber+n))
X					continue;
X				S = fopen(subscriber,"a");
X				if(!S){
X					fprintf(stderr,"can't open %s\n",subscriber);
X					continue;
X				}
X				nrecords += nf;
X				for(m = 0; m<nf; m++)
X					fprintf(S,"%s/%s\t(%dk)\n",dir,files[m],(fsizes[m]+999)/1000);
X				fclose(S);
X			}
X			fclose(L);
X		}
X		if(!(slash = strrchr(wdir,'/')))
X			break;
X		*slash = '\0';  /* move up to parent */
X	}
X	return(nrecords);
X}
X
Xint
Xvalid_file(char *path, char *dir, char **pfile)
X{
X	char *file;
X	int m;
X
X	if(!*path || strchr(path,' ')){
X		fprintf(stderr,"bad file: %s",path);
X		return 0;
X	}
X	m = strlen(path);
X	if(m>=PATH_MAX-2){
X		fprintf(stderr,"long file %s",path);
X		return 0;
X	}
X	if(path[m-1]=='\n')
X		path[--m] = '\0';	/* chop NL */
X	strcpy(dir,path);
X	file = strrchr(dir,'/');
X	if(!file){
X		fprintf(stderr,"no dir?  %s\n",dir);
X		return 0;
X	}
X	*file++ = '\0';		/* terminate dir */
X	if(strlen(file)>NAME_MAX){
X		fprintf(stderr,"%s longer than NAME_MAX\n",file);
X		return 0;
X	}
X	if( strlen(dir)+strlen("/.list") > PATH_MAX ){
X		fprintf(stderr,"long dir  %s\n",dir);
X		return 0;
X	}
X	*pfile = file;
X	return 1;
X}
X
Xint
Xmain(int argc, char**argv)
X{
X	char path[PATH_MAX+2], dir[PATH_MAX+1], old_dir[PATH_MAX+1];
X	char *file, files[FCNT][NAME_MAX+1];
X	unsigned long fsizes[FCNT];
X	int n, filecnt, nrecords = 0;
X
X	system("rm -rf /tmp/to_subscribers;mkdir /tmp/to_subscribers");
X	old_dir[0] = '\0';
X	filecnt = 0;
X
X	/* accumulate notifications for each address */
X	while(fgets(path,sizeof(path),stdin)){ /**** for each changed file ****/
X		if(!valid_file(path,dir,&file))
X			continue;
X		if(search(file,exclude))
X			continue;
X		if(filecnt>=FCNT || strcmp(dir,old_dir)!=0){
X			nrecords += record_files(filecnt,old_dir,files,fsizes);
X			filecnt = 0;
X		}
X		strcpy(files[filecnt],file);
X		fsizes[filecnt] = fsize(path);
X		strcpy(old_dir,dir);
X		filecnt++;
X	}
X	nrecords += record_files(filecnt,old_dir,files,fsizes);
X
X	/* trickle out the mail */
X	if(nrecords)
X		system("to_subscribers2");
X	exit(0);
X}
-------cut here----- src/to_subscribers.c